Skip to content
BITBRIEF

Institutional research · AI · Cybersecurity · Digital assets

Vol. 01 · No. 17

What a tool call exposes

An agent turns text that may be attacker-controlled into a privileged call. Authorisation has to hold after the agent has been steered.

In short

The security problem with tool-using agents fits in one sentence: they translate natural language, which may contain attacker-controlled text, into privileged tool calls. Everything else follows, including the conclusion that authorisation must hold even when the agent itself has been successfully steered.

A gap analysis across six official Model Context Protocol SDKs — Python, TypeScript, Go, Rust, C# and Swift — reported no pre-authentication tool discovery and no fine-grained per-tool authorisation in the base SDKs, which pushes the decision of which tools a caller may invoke outside the protocol layer.

Filtering what a caller can see and enforcing what a caller can invoke are separate mechanisms in most deployments, and they drift apart. Binding them to a single declarative annotation removes a category of bug rather than one instance of it.

Tool calls are also where latency is most visible and output easiest to predict. Draft models of around 300 million parameters reported 57% lower latency on function calling for one 2.6-billion-parameter target, because field names, brackets and separators are close to determined by what came before.

What is the actual attack surface?

One sentence covers it: a tool-using agent translates natural language, which may contain attacker-controlled text, into privileged tool calls. Every other property of the system follows from that, including the uncomfortable one — that authorisation has to hold even when the agent has been successfully steered. A design that assumes the agent will only ever ask for what the user wanted has no security property at all.

That is a different threat model from the one most API authorisation was built for. The caller is not malicious and not trusted either; it is a component that can be persuaded. The question is therefore not whether the agent can be steered but what it is able to do once it has been.

What do the protocol SDKs give you, and what do they not?

The Model Context Protocol is now the widely adopted interface at exactly that boundary, which makes what it does and does not provide a practical matter rather than a design debate. A systematic gap analysis across six official SDKs — Python, TypeScript, Go, Rust, C# and Swift — reported three structural shortcomings.

  • No pre-authentication tool discovery, so a client cannot learn what a server offers before holding credentials for it.
  • No fine-grained per-tool authorisation in the base SDKs, which pushes the decision of which tools a caller may invoke outside the protocol layer.
  • Authorisation primitives that assume the agent has not been steered by an attacker.

The remedies the authors propose are composable extensions rather than protocol changes: cross-header credential normalisation, cached token verification across different identity providers, an unauthenticated metadata endpoint for credential-free registry discovery, and permission-filtered tool visibility kept consistent with per-tool enforcement through a single declarative annotation.

Why is a filter not a boundary?

Because filtering what a caller can see and enforcing what a caller can invoke are two separate mechanisms in most deployments, and separate mechanisms drift. The visible list is updated when someone remembers; the enforcement path is updated when something breaks. After a few releases they disagree, and the disagreement is invisible until somebody calls a tool they could not see.

Binding both to one declaration is the proposal worth taking seriously, because it removes a category of bug rather than a specific instance of it. The same argument applies anywhere a system has a display rule and an enforcement rule for the same decision: if they are expressed twice, they will eventually say different things.

ConcernIn the base protocol SDKsWhere it has to live instead
Which tools existDiscoverable only with credentials in handAn unauthenticated metadata endpoint for registry discovery
Which tools a caller may invokeNot fine-grainedYour own layer, or a per-tool annotation bound to visibility
Identity across providersNot normalisedCross-header credential normalisation and cached token verification
Behaviour after the agent is steeredAssumed not to happenEnforcement that does not trust the caller's intent
Tool schema shapeConverged across vendors without a standardAdapters of a few dozen lines, not a subsystem
Error and streaming semanticsStill divergent between vendorsTested against each vendor; rarely documented
Tool-call concerns and where each is actually handled

What about portability?

Better than it looks, in one dimension and worse in another. Tool definitions have converged on a JSON Schema shape close enough across vendors that adapters are now a few dozen lines rather than a subsystem. Nobody published a standard; the shape won by being copied.

The divergence that remains is in error handling and in how partial or streamed tool calls are surfaced. Those are where portability actually breaks, and they are rarely covered in vendor documentation — which means they have to be established by testing rather than by reading.

Why do tool calls speed up more than prose?

Because structured output is the easiest thing in the world to guess. Field names, brackets, quotes and separators are close to determined by what came before, so a draft model's acceptance rate is high exactly where latency is most visible to a user waiting on a tool call. One published set of draft models, around 300 million parameters each with five decoder layers, reported 57% lower latency on function calling for a 2.6-billion-parameter target — a larger practical result than the throughput headline alongside it.

That gain is conditional, and the condition is worth knowing. Speculative decoding pays when verification is cheap relative to generation and the draft guesses well. On a memory-bandwidth-bound device running a model with one billion active parameters, the same architecture returned 1.18 times against 2.54 times on a datacentre accelerator. The technique nearly disappears where those conditions do not hold, so the only measurement worth acting on is one taken on your own hardware and traffic.

What should you check in your own stack?

  • Can a caller invoke a tool it cannot see, or see a tool it cannot invoke? If the two rules are written twice, they will diverge.
  • Does any authorisation decision depend on the agent having interpreted the request faithfully?
  • What happens to a partial or streamed tool call on each vendor you support, and was that tested or assumed?
  • Is the latency you care about on the tool-call path, and have you measured speculative decoding on your own traffic rather than a published mix?
  • Can a client discover what a server offers without first holding credentials for it, and should it be able to?

Questions

What is the core security problem with tool-using agents?
They translate natural language, which may contain attacker-controlled text, into privileged tool calls. The consequence is that authorisation has to hold even when the agent has been successfully steered; a design that assumes a faithful agent has no security property.
Does the Model Context Protocol handle per-tool authorisation?
Not in the base SDKs. A gap analysis across the six official SDKs — Python, TypeScript, Go, Rust, C# and Swift — found no fine-grained per-tool authorisation and no pre-authentication tool discovery, which pushes the decision of which tools a caller may invoke outside the protocol layer.
Why bind tool visibility and tool enforcement together?
Because they are separate mechanisms in most deployments and they drift apart, so a caller ends up able to invoke something it cannot see. Expressing both through a single declarative annotation removes a category of bug rather than one instance of it.
Are tool schemas portable between vendors?
The shape is. Tool definitions converged on a JSON Schema form close enough that adapters are a few dozen lines rather than a subsystem, without anyone publishing a standard. What stays divergent is error handling and how partial or streamed calls are surfaced, and those are rarely documented.

All guides