MCP security: govern which tools an agent may call

An MCP server turns a model into something that can act. Governance is the layer that decides which of those actions may happen, and leaves evidence behind either way.

The failure mode worth preventing

Most MCP incidents are not exotic. A tool that can delete, deploy or send gets discovered by an agent that was only asked a question, and it acts on an instruction that came from a document, a web page or a ticket. There is rarely a human in the loop and rarely a record of what was proposed.

The fix has three parts: know the tool inventory the runtime actually declares, decide per call rather than per server, and keep the decision.

What Descles does today

MCP servers are registered on the edge with credentials only it holds, and agents call them at /mcp/<connector>. Each call is checked on the edge: a rule can allow, deny or require approval, scoped to the tool or to specific argument values. A denied call never reaches the server; an approval holds it until a named person decides.

  • Inventory: the edge lists the tools each MCP server declares, and only declared names are reported.
  • Per-call decisions rather than a flat allowlist per connection.
  • Argument scoping for the keys that matter — paths, commands, URLs, queries.
  • Approvals bound to the exact parameters and consumed once.
  • A record of the decision on the edge, alongside the call.
MCP tools pointed at the gateway (/mcp/<connector>) are executed by the gateway with a credential only it holds, after policy and any approval. Tools a runtime calls some other way are outside it. The gateway supports tools/list and tools/call; MCP resources, prompts and subscriptions are not governed.

Start with the inventory, not a big policy

Load the starter policy, look at what your runtime declares, and mark only the tools that can destroy or publish. An inventory you have read is worth more than a policy nobody can explain — and unlisted tools are allowed, so the defaults matter.

defaults:
  tools:
    shell.rm: deny                 # nobody deletes files through an agent
    shell.kubectl.delete: require_approval
  require_approval:
    - github.create_issue

Questions

Does this work with a self-hosted MCP server?

The policy surface is runtime-independent: rules match the tool names your agent declares. A self-hosted MCP server behind an integrated runtime gets the same treatment as a hosted one.

Is a denied tool call enough to stop a determined agent?

It stops that call, because the runtime never receives it. An agent that can reach the same capability through another path is governed by the rule for that path — keep the inventory honest and put a sandbox behind anything you cannot afford to lose.

Are tool results verified?

No, and the difference is stated rather than blurred: a runtime-reported result is evidence that the runtime reported it, not independent proof the local action succeeded.

Related

  • Agent approvals — Autonomy is fine until the tool call is `kubectl delete pod checkout-api` in production. Approval puts one named person in that gap, and only for the calls that need it.
  • AI agent control plane — A control plane sits beside the data plane: your agents keep talking to their model providers, and the control plane decides who they are, what they may spend, which tool calls they may make, who reviews the risky ones, and what record survives afterwards.
  • Claude Code governance — A coding agent with a shell is the most useful and most dangerous agent most teams run. Descles keeps the Anthropic credential off laptops and puts a policy in front of the tool calls it proposes.
  • Documentation — endpoints, provider keys, and the full policy reference.

Try it: run the open-source edge, or apply as a design partner. Questions: outreach@descles.com. See also terms and privacy.