Skip to content

Learn

Exchange & Broker MCP

Read-only vs trade permission in an MCP setup

CoTrading editorial team Updated: 2026-09-23

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

“Read-only” in an MCP setup can mean two completely different things: a label the server attaches to its own tools, or a permission the venue enforces on your key. Only one of them survives the server being wrong.

This distinction is easy to miss because both are called read-only, both appear in configuration, and in the normal case they agree. They come apart exactly when it matters.

The annotation is a hint, and the spec says so

MCP lets a server attach annotations to each tool it exposes. One of them is readOnlyHint. The schema defines it plainly:

readOnlyHint?: boolean — “If true, the tool does not modify its environment.” Default: false.

So far so good. Now the note directly above it in the same schema file:

“NOTE: all properties in ToolAnnotations are hints. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like title).

Clients should never make tool use decisions based on ToolAnnotations received from untrusted servers.”

And the tools specification repeats it normatively:

“For trust & safety and security, clients MUST consider tool annotations to be untrusted unless they come from trusted servers.”

The implication for trading is blunt. Nothing stops a server from declaring a place_order tool with readOnlyHint: true. The annotation is not verified by anything; it is the server describing itself. A client that decides what to auto-approve by reading annotations is asking the component it is supposed to be guarding for permission to stop guarding it.

This is not a flaw in MCP. Annotations exist to improve UI and model behaviour, and the spec is honest about what they are. It is a flaw in using them as a security boundary.

The defaults are better than people assume

Worth knowing, because it changes how you read a server that annotates nothing:

FieldDefaultWhat the default means
readOnlyHintfalseAssume the tool modifies state
destructiveHinttrueAssume the modification is destructive
idempotentHintfalseAssume calling twice does it twice
openWorldHinttrueAssume it touches external systems

An unannotated tool is, by the specification’s own defaults, treated as destructive and non-idempotent. That is the right default, and it means a server that annotates nothing is easier to reason about than one that annotates optimistically. Absence of annotations is not a red flag. Confident annotations from a server you have not audited are.

Note idempotentHint defaulting to false in particular. For an order-placement tool that default is correct and load-bearing: calling it twice places two orders.

Where read-only can actually be enforced

Three layers, in increasing order of how much they are worth.

1. The server’s own configuration

Some servers implement real read-only modes, and the good ones are quite thorough. Among the Bybit community servers, one ships with trading disabled by default, requires an explicit TRADING_ENABLED=true to enable mutating tools, offers a READONLY_MODE=true that hard-blocks mutations, and caps estimated order notional with a MAX_ORDER_SIZE_USDT setting. Another is read-only by construction and recommends only ever pointing a read-only API key at it.

This is genuinely good engineering and you should prefer servers that do it. But it is a control inside the component you are bounding. It protects you from the model; it does not protect you from the server being buggy, being updated, or being something other than what you thought you installed.

2. The venue’s key permissions

This is the layer that holds regardless of what the software does, because it is enforced on the other side of the network by a party with no stake in your configuration being right.

On a centralised exchange this is the API key’s permission set. The detail worth knowing on Binance: withdrawals cannot be enabled at all on a key without IP access restriction — the filter is a precondition, not an option alongside it. And a key with no IP whitelist has its spot trading permission automatically switched off after a period, by design, as a standing hygiene mechanism.

The practical shape: a key that can read and nothing else cannot be talked into placing an order by any prompt, any server bug, or any annotation. There is no argument to be had with the exchange.

Binance also exposes the key’s current state programmatically via GET /sapi/v1/account/apiRestrictions, which returns fields including ipRestrict, enableReading, enableWithdrawals and enableFutures. If you want to verify what a key can do rather than remember what you ticked, ask.

3. Local code between the model and the venue

The layer the venue cannot provide, because the venue does not know your rules. Per-trade size caps, a daily-loss breaker, a cooldown after consecutive losses — these are yours, and they only bind if they are evaluated as functions rather than read as instructions.

The case that breaks the mental model

Everything above assumes a key with permissions attached to it. Hyperliquid does not work that way, and it is worth knowing before you carry CEX advice across.

There is no API key. A master account approves an API wallet (also called an agent wallet) to sign on its behalf. The documentation is explicit that “API wallets are only used to sign”, and that to query account data you must pass the actual address of the master or sub-account — the agent’s own address owns nothing.

Can that agent wallet move funds? Hyperliquid’s documentation does not say either way; there is no prohibition to quote. Its official Python SDK signs withdrawals and transfers (withdraw3, usdSend, spotSend) with a different scheme from trading, and third-party integrations state that these require the master account’s key — but that is a third party describing the venue, not the venue committing to it.

So the honest answer is that it is unconfirmed. This page previously described it as a structural property of the signing scheme; that argument rested on a misreading of the SDK and was withdrawn on 2026-09-23. If your security model depends on it, test it on testnet with your own agent wallet before relying on it.

What to actually do

  • Treat readOnlyHint as a UI affordance. It is useful for making a client’s interface sensible. It is not a permission.
  • Set the permission at the venue, and prefer a key that cannot trade until you have decided you want it to.
  • Prefer servers with explicit default-off trading and a real read-only mode — then still set the key permission, because the two protect against different failures.
  • Re-check after updates. A server’s tool list can change under you; the protocol has a notifications/tools/list_changed message precisely because this is expected behaviour.

FAQ

If a tool says readOnlyHint: true, can it still place an order?

Yes. The annotation is a claim the server makes about its own tool, and the MCP schema states outright that annotations “are not guaranteed to provide a faithful description of tool behavior”. The specification further requires clients to treat annotations from untrusted servers as untrusted. Use the annotation to build a sensible interface; use the venue’s key permissions to decide what is actually possible.

Is a server that provides no annotations less safe?

No, and slightly the opposite. The schema defaults assume the worst — readOnlyHint false, destructiveHint true, idempotentHint false — so an unannotated tool is treated as destructive and non-idempotent. A server that annotates nothing gives you conservative defaults. A server that annotates confidently gives you claims you would have to audit to trust.

Can I use one API key for both reading and trading if I am careful?

You can, and the question is what “careful” is protecting against. A single key with trade permission means every path to that key — the server, the client, the model, anything that can read your environment variables — is a path to an order. Separate keys collapse that to one path. The operational cost is configuring two credentials once.

How do I check what a key is actually allowed to do?

Ask the venue rather than your memory. On Binance, GET /sapi/v1/account/apiRestrictions returns the current permission flags including ipRestrict, enableReading and enableWithdrawals. Other venues expose equivalents to varying degrees. Checking takes one request and catches the case where a key is not configured the way you believe.