Learn / Exchange & Broker MCP

Why MCP needs an approval layer

Figures on this page are as of 2026-09. Fees, limits and margin tiers change — check the venue's own docs before acting on a number.

The Model Context Protocol’s own specification contains this sentence:

“For trust & safety and security, there SHOULD always be a human in the loop with the ability to deny tool invocations.”

It is worth sitting with that for a second. The people who designed the protocol looked at what they had built — a standard for letting a language model discover and call tools on its own — and wrote down that someone should be able to stop it.

And then they wrote SHOULD, not MUST. Which is correct. A protocol that hard- required a confirmation dialog would be unusable for the many legitimate cases where tools are read-only and the interruption is pure friction. But it means the recommendation is advisory, and nothing in MCP enforces it.

For a weather tool, that is fine. For a tool that places orders against your exchange account, the gap between “recommended” and “enforced” is where your money lives.

The protocol is explicit that it does not decide this

The tools specification is unusually direct about the division of responsibility:

“Tools in MCP are designed to be model-controlled, meaning that the language model can discover and invoke tools automatically based on its contextual understanding and the user’s prompts.”

“However, implementations are free to expose tools through any interface pattern that suits their needs—the protocol itself does not mandate any specific user interaction model.”

Read together: the model decides what to call, and whether anyone asks you first is left entirely to whichever client you happen to be using. That is a reasonable architectural choice. It is also a fact you should know before connecting a broker.

What the spec asks clients to do

The same page lists what clients should do, and the list reads like a specification for a trade-approval screen:

Clients SHOULD:

  • Prompt for user confirmation on sensitive operations
  • Show tool inputs to the user before calling the server, to avoid malicious or accidental data exfiltration
  • Validate tool results before passing to LLM
  • Implement timeouts for tool calls
  • Log tool usage for audit purposes

“Show tool inputs to the user before calling the server” is the important one. Not a log you read afterwards. Not a summary of what happened. The actual arguments, before they go anywhere.

For an order, the tool inputs are the trade: symbol, side, quantity, price, order type. A client that shows you those before the call is showing you a trade plan. A client that does not is asking you to trust that the numbers the model generated are the numbers you would have chosen.

Three reasons this matters more for orders than for other tools

A tool call is not a decision. When a model emits a place_order call, nothing about that emission reflects deliberation. It is a token sequence that matched a pattern well enough to be produced. It may be the output of excellent reasoning about your chart. It may be the output of a misread axis. The exchange cannot tell the difference — both arrive as a signed API request from your key.

The parameters fail quietly. A quantity off by a decimal place, a side flipped, a price in the wrong units, a stop submitted as a limit. None of these look wrong in a tool-call log. You find out from your position, which is the most expensive place to find out.

Context is an input you do not fully control. If the model reads anything you did not write — a news page, a scraped forum, the output of another tool — that text enters the same context window as your instructions. A model holding order-placement tools and reading untrusted text is a combination that deserves more caution than it typically gets.

None of these are arguments against connecting a model to a market. They are arguments about where the boundary goes.

Why the boundary cannot be a prompt

The intuitive fix is to write the rules down for the model: never risk more than this, stop after that many losses, do not trade this instrument.

This does not work, and the reason is not that models are unreliable. It is that a prompt is an input to a process whose output is probabilistic. A limit expressed as text is a strong suggestion competing with every other consideration in the context window. Most of the time it wins. The times it does not are, by construction, the times you most needed it to.

A limit expressed as a function that rejects the order does not compete with anything. It returns false. It behaves the same on the tenth trade of a bad day as on the first, which is precisely when intention is weakest and determinism is worth most.

This is the whole argument, and it is narrow: the model is good at reading and drafting, and should do those things. The constraint should not be made of the same material as the thing it constrains.

What an approval layer actually is

Concretely, four things sitting between the model and the venue:

  1. A structured proposal rather than an action. The model’s output is a plan — direction, entry, stop, size, rationale — not an API call. Nothing is submitted by producing it.
  2. A human decision on that proposal. You see the parameters before they exist anywhere else. This is the spec’s “show tool inputs before calling the server”, made concrete.
  3. Limits enforced in code. Per-trade and per-account caps, a daily-loss breaker, a cooldown after consecutive losses — evaluated as deterministic local functions, not as instructions.
  4. A record. The spec asks clients to “log tool usage for audit purposes”. For trading this is not just forensics; it is the input to reviewing your own decisions later.

The cost is real: you cannot leave it running overnight. That is not an implementation gap, it is the trade being made. The question is whether unattended order placement is a feature you actually want, or one you have acquired by default because the protocol does not require otherwise.

The narrow claim

MCP is a good protocol and the exchange servers built on it are genuinely useful. Reading positions, summarising exposure, explaining why a setup looks the way it does — this is real value and it needs no approval step at all.

The claim here is only this: the specification’s authors recommended that a human be able to deny a tool invocation, they did not require it, and when the tool is an order, the difference between those two is the entire safety argument. Somebody has to implement the SHOULD. If the client you are using does not, it falls to you, and doing it by watching carefully is not the same as doing it structurally.

FAQ

Does MCP actually require a confirmation prompt before a tool runs?

No. The specification uses SHOULD, which in specification language is a strong recommendation that implementations may decline to follow. It states that “there SHOULD always be a human in the loop with the ability to deny tool invocations” and separately recommends that clients “prompt for user confirmation on sensitive operations” — but the protocol explicitly does not mandate any particular user interaction model. Whether you get an approval step depends entirely on the client.

Isn’t an approval step just friction that defeats the point of automation?

For read-only tools, yes, and they should not have one. The distinction worth drawing is between tools that change state and tools that do not. Reading balances, fetching candles and summarising positions are where most of the day- to-day value of connecting a model to an exchange actually lives, and none of it needs a dialog. The approval step belongs on the small number of calls that move money.

Can I get the same protection by writing strict rules in the system prompt?

Not reliably, and the failure mode is unfortunate. A prompt-expressed limit is a strong input to a probabilistic process, so it holds most of the time — which means you stop checking. The occasions it does not hold are unpredictable and correlated with unusual situations, which are exactly the situations where the limit mattered. A limit implemented as code that rejects the order has no such distribution.

What if the MCP server itself enforces limits?

That is better than nothing and some servers do it well — default-off trading, read-only modes, notional caps. But it is a control living inside the component you are trying to bound, configured by whoever wrote it. The permissions that cannot be self-reported are the ones enforced outside the server entirely: what your exchange API key is allowed to do, and what your own local code will let through.