<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sean McDonnell</title>
    <description>The latest articles on DEV Community by Sean McDonnell (@seaneboneslang).</description>
    <link>https://dev.to/seaneboneslang</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4177895%2F929c320c-cc5a-4d8d-b7b2-85a2d8082238.jpg</url>
      <title>DEV Community: Sean McDonnell</title>
      <link>https://dev.to/seaneboneslang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/seaneboneslang"/>
    <language>en</language>
    <item>
      <title>Put a permission gate between your agent and its tools</title>
      <dc:creator>Sean McDonnell</dc:creator>
      <pubDate>Sun, 11 Oct 2026 22:33:54 +0000</pubDate>
      <link>https://dev.to/seaneboneslang/put-a-permission-gate-between-your-agent-and-its-tools-p9a</link>
      <guid>https://dev.to/seaneboneslang/put-a-permission-gate-between-your-agent-and-its-tools-p9a</guid>
      <description>&lt;p&gt;An agent can understand a task and still attempt an action you did not authorize.&lt;br&gt;
The useful place to decide whether that action may run is the dispatcher that&lt;br&gt;
actually performs it. A prompt can explain the rules; a dispatcher can enforce&lt;br&gt;
them before a connected tool body starts.&lt;/p&gt;

&lt;p&gt;I built DeadBolt at NextEleven to provide that local admission point. It is free,&lt;br&gt;
open source under Apache-2.0, and usable as an embedded Rust library, local HTTP&lt;br&gt;
sidecar, or proxy in front of a trusted stdio MCP server. This walkthrough uses&lt;br&gt;
the MCP route, with disposable marker tools instead of real accounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authorship disclosure:&lt;/strong&gt; this article was drafted by an AI coding assistant.&lt;br&gt;
The implementation and instructions were checked against the real published&lt;br&gt;
bundle and disposable tool effects. Those checks do not establish that every&lt;br&gt;
agent, client or deployment is protected.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update — October 12, 2026:&lt;/strong&gt; &lt;a href="https://github.com/seanebones-lang/deadbolt/releases/tag/v2.0.0" rel="noopener noreferrer"&gt;stable 2.0.0 is published&lt;/a&gt;, with &lt;a href="https://seanebones-lang.github.io/deadbolt/editors-v2.html" rel="noopener noreferrer"&gt;its matching editor guide and helper&lt;/a&gt;. It includes fixed run deadlines, optional supervisor-only liveness, separated operator/workload credentials, exact-action approval and readable MCP denials. Conflicting recognized destinations are refused, and a damaged existing store cannot be silently rebuilt at startup. The worked tutorial below remains historical and pinned to bundle 1.1.0 / engine 1.0.3; use the current guide for 2.0.0 and do not mix helpers or configurations. Stable artifacts passed installed/protocol/effect checks across four native platforms. Earlier RC3 GUI checks are separately recorded; they are not certification or proof of coverage in your application.&lt;/p&gt;

&lt;p&gt;We are keeping 2.0.0 as the release baseline through Monday, October 19, collecting feedback and reviewing it weekly. The next iteration will focus on reproducible bugs, documentation and integration friction, with checks before publication. DeadBolt is Apache-2.0: you may use, fork, modify and integrate it, including commercially. Useful adapters and improvements are welcome for review and possible inclusion; further expansion will follow demonstrated user needs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Choose one route to protect
&lt;/h2&gt;

&lt;p&gt;This is a specific integration boundary. The proxy does not intercept built-in&lt;br&gt;
terminal commands, file editing, other MCP connections or arbitrary network&lt;br&gt;
traffic. The child server retains its operating-system permissions and must be&lt;br&gt;
trusted. Initialization and non-tool MCP methods are outside the admission gate.&lt;/p&gt;

&lt;p&gt;For this example, the path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Editor / MCP client
        ↓ tools/call
DeadBolt admission check ← trusted operator approval / kill
        ↓ only after admission
Trusted disposable MCP server
        ↓
Local marker file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The editor can request an action. The operator owns policy and approval. DeadBolt&lt;br&gt;
checks the routed request before forwarding it. The marker file makes it possible&lt;br&gt;
to distinguish a denied response from a tool that ran anyway.&lt;/p&gt;
&lt;h2&gt;
  
  
  Start without a model account
&lt;/h2&gt;

&lt;p&gt;Download &lt;a href="https://github.com/seanebones-lang/deadbolt/releases/download/mcpb-v1.1.0/deadbolt-1.1.0.mcpb" rel="noopener noreferrer"&gt;DeadBolt MCP bundle 1.1.0&lt;/a&gt;&lt;br&gt;
and &lt;a href="https://seanebones-lang.github.io/deadbolt/assets/editor_setup.py" rel="noopener noreferrer"&gt;the configuration helper&lt;/a&gt;.&lt;br&gt;
Review the helper, save both in Downloads, and use Python 3.9+ with Node.js 20+.&lt;br&gt;
The bundle supports Apple Silicon/Intel Macs, Linux x64 with glibc and Windows x64.&lt;/p&gt;

&lt;p&gt;From Downloads on macOS/Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local/share/deadbolt/editor-1.1.0"&lt;/span&gt;
python3 editor_setup.py prepare &lt;span class="nt"&gt;--bundle&lt;/span&gt; deadbolt-1.1.0.mcpb &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
python3 editor_setup.py verify &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows users can follow the PowerShell equivalents in&lt;br&gt;
&lt;a href="https://seanebones-lang.github.io/deadbolt/editors.html" rel="noopener noreferrer"&gt;the editor installation guide&lt;/a&gt;.&lt;br&gt;
Use a new private directory outside any agent-editable workspace.&lt;/p&gt;

&lt;p&gt;The helper verifies the full bundle's published checksum, refuses to overwrite&lt;br&gt;
an installation, and exports configuration without modifying existing profiles.&lt;br&gt;
The verification starts a disposable server, tests real calls and removes its&lt;br&gt;
temporary state. It needs no model key, hosted operator endpoint or connected&lt;br&gt;
account. The bundle is version 1.1.0; its engine is stable 1.0.3.&lt;/p&gt;

&lt;p&gt;Expected results include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"allowed_marker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"body_ran"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"purpose_exceeded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"body_ran"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"needs_human"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"body_ran"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"killed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"body_ran"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete report also verifies one approved execution and that its approval&lt;br&gt;
is consumed. This is a protocol test with local effects, not a benchmark of a&lt;br&gt;
model's judgment or proof of enforcement in your application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Add it to an editor
&lt;/h2&gt;

&lt;p&gt;For Cursor, open the generated &lt;code&gt;install.html&lt;/code&gt; and review the local command in its&lt;br&gt;
installation prompt. For VS Code, merge the exported &lt;code&gt;vscode-mcp.json&lt;/code&gt; server&lt;br&gt;
entry into the configuration opened by &lt;strong&gt;MCP: Open User Configuration&lt;/strong&gt;. Preserve&lt;br&gt;
existing servers. A new portable workspace configuration is also exported.&lt;/p&gt;

&lt;p&gt;Initially, only two read-only setup tools are available: an integration guide and&lt;br&gt;
a checksum check. &lt;strong&gt;Connected setup is not active protection.&lt;/strong&gt; It starts no&lt;br&gt;
downstream server. Use this mode to check the runtime before wiring an action.&lt;br&gt;
An editor's AI chat may need its own account even though the local protocol test&lt;br&gt;
does not.&lt;/p&gt;
&lt;h2&gt;
  
  
  See denial, approval and revocation
&lt;/h2&gt;

&lt;p&gt;Create a fresh disposable demo with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 editor_setup.py demo &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the returned directory's install link or configuration to add &lt;strong&gt;deadbolt-demo&lt;/strong&gt;.&lt;br&gt;
Its three tools append their names to a fixed local file. Set &lt;code&gt;DEMO&lt;/code&gt; to the&lt;br&gt;
returned directory path. There are no real emails, payments or account actions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;demo_write_marker({})&lt;/code&gt;. The allow-listed action adds one line.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;demo_off_list({})&lt;/code&gt;. The policy blocks it with &lt;code&gt;purpose_exceeded&lt;/code&gt;; no line
is added.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;demo_publish_marker({})&lt;/code&gt;. It is deliberately classified as irreversible
for the demonstration and returns &lt;code&gt;needs_human&lt;/code&gt;; no line is added.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In your trusted operator terminal, run:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 editor_setup.py operator &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--demo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEMO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; approve
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Call &lt;code&gt;demo_publish_marker({})&lt;/code&gt; once. It writes a line. A second attempt needs&lt;br&gt;
approval again.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Revoke the run from the operator terminal:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 editor_setup.py operator &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--demo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEMO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;kill&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The next marker call returns &lt;code&gt;killed&lt;/code&gt; and leaves the file unchanged.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Inspect the demo's &lt;code&gt;effects.txt&lt;/code&gt; and &lt;code&gt;events.jsonl&lt;/code&gt;. A tool's error response is&lt;br&gt;
useful; evidence that its body did not run is the stronger check. An editor's&lt;br&gt;
Allow/Run button permits an attempted call and does not grant operator approval.&lt;/p&gt;

&lt;p&gt;The default lease expires after 60 seconds without admission. If your setup or&lt;br&gt;
conversation takes longer, remove/stop the old demo connection and create a fresh&lt;br&gt;
one. Admission renews a live lease; reconnecting cannot revive an expired or&lt;br&gt;
killed ID. The automated verification handles this demonstration without a manual&lt;br&gt;
conversation delay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Carry the boundary into your own application
&lt;/h2&gt;

&lt;p&gt;Replace a trusted downstream server's direct launch with the DeadBolt proxy.&lt;br&gt;
Keep policy, approval and revocation in a separate trusted operator path. Remove&lt;br&gt;
the ungated direct connection, configure actual tool/destination conventions,&lt;br&gt;
and repeat the effect checks against one bounded action in your executor.&lt;/p&gt;

&lt;p&gt;Stable 1.0.3 approvals apply once to an agent/tool pair; they are not bound to&lt;br&gt;
exact arguments or recipients. Stable 2.0.0 includes&lt;br&gt;
&lt;a href="https://github.com/seanebones-lang/deadbolt/blob/v2.0.0/docs/ACTION-APPROVALS.md" rel="noopener noreferrer"&gt;exact-action APIs and their limits&lt;/a&gt;. Do not assume those features are in this older bundle; the MCP bundle’s ordinary one-shot approval does not provision exact-action grants.&lt;/p&gt;

&lt;p&gt;DeadBolt does not cancel an action already running, make an external action atomic&lt;br&gt;
with admission, or protect routes you have not connected. Same-user malicious code&lt;br&gt;
can also reach same-user state: private folders are useful hygiene, not an OS&lt;br&gt;
sandbox. A deployment's executor and operator separation must be trustworthy.&lt;/p&gt;

&lt;p&gt;The next useful test is small: one real route, an allowed action, a denied action&lt;br&gt;
whose body stays untouched, and a revoked run that cannot perform the next effect.&lt;br&gt;
Measure installation time and what confused the developer before widening scope.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/seanebones-lang/deadbolt" rel="noopener noreferrer"&gt;Source and installation guide&lt;/a&gt;&lt;br&gt;
· &lt;a href="https://seanebones-lang.github.io/deadbolt/#watch" rel="noopener noreferrer"&gt;Captioned overview and full walkthrough&lt;/a&gt;&lt;br&gt;
· &lt;a href="https://github.com/seanebones-lang/deadbolt/blob/main/docs/INTEGRATION.md" rel="noopener noreferrer"&gt;Integration boundaries&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, which step took the most work: installation, choosing the routed&lt;br&gt;
tools, or keeping operator authority separate from the agent?&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
