Manav.id
Developer · 5 min read

Scope-blind tool approvals: approving a server is not approving its actions

Scope-blind tool approvals: approving a server is not approving its actions

When you approve a tool server, what exactly did you approve? In most implementations the answer is the server's identity, and the server's capabilities are whatever it declares — now and later.

What is MCPoison and why does approving a tool server not bound its behaviour?

MCPoison, tracked as CVE-2025-54136 in Cursor, let an attacker silently change an MCP server configuration after a user had approved it, so commands ran on every subsequent project open with no new prompt. The approval was bound to a name, and the name kept pointing at changed behaviour.

Key takeaways
  • Approval is typically bound to a server identity. Tool definitions — what the server can be asked to do — are supplied by the server and may change after approval.
  • The vulnerability class reported against AI-enabled editors in 2025, tracked under identifiers including CVE-2025-54135, sits in this space: configuration trusted after an initial approval.
  • Binding approval to a schema hash makes a post-approval change fail closed rather than proceed silently.

What an approval dialog is actually asking

Approve serverby nameonceConfig modifieddifferent commandssilentProject reopenedno new prompttrust persistsCommands executeattacker'spersistent
The approval is bound to an identifier. Everything the identifier resolves to can change afterwards.

A developer adds a tool server to their AI-enabled editor. The editor presents a dialog: this server wants to provide tools, do you allow it?

The mental model the dialog invites is do I trust this server. The mental model the implementation uses is frequently record that this server identity is permitted, with the set of things it can do resolved separately, at invocation time, from whatever the server currently declares.

Those differ in an important way. Trust in a party is stable; capability declared by that party is not.

The trust-after-approval problem

Public vulnerability reporting against AI-enabled development environments during 2025, including entries tracked under identifiers such as CVE-2025-54135, described a pattern in which configuration or tool definitions were trusted after an initial user approval, such that later modification could lead to code execution without a fresh prompt.

Readers should consult the vendor advisories for the specifics of any given issue — details vary and are patched. What matters architecturally is the class, which is broader than any single CVE.

Bound at approval timeConsequence if it changes later
Server identity onlyAny capability the server later declares is covered
Server plus tool namesArgument schemas can widen; behaviour can change entirely
Server plus full tool schemasA change is detectable if the client compares
A hash of the schemas, stored with the approvalA change fails closed

Why names are the weakest possible binding

Tool names and descriptions are strings supplied by the server. They are not verified against behaviour and they are the primary input the model uses to decide which tool to call.

A tool named read_file is not obliged to read a file. A description saying it performs a safe, read-only lookup is not a constraint. Both are attacker-controlled in exactly the case that matters — a third-party server.

This is why classification for a friction ladder cannot be derived from the tool's self-description, and why it has to come from review recorded outside the server's control.

Schema binding, concretely

At approval, compute a hash over the canonicalised tool definitions and store it with the approval record. At invocation, recompute and compare.

on_approve(server):
    defs   = fetch_tool_definitions(server)
    digest = sha256(canonicalise(defs))          # RFC 8785 ordering
    store_approval(server.id, digest, approver, scope, expiry)

on_invoke(server, tool, args):
    defs   = fetch_tool_definitions(server)
    digest = sha256(canonicalise(defs))
    if digest != stored_digest(server.id):
        halt('tool definitions changed since approval')   # fail closed

Canonicalisation matters here for a mundane reason: JSON key ordering and whitespace differ between serialisations of the same logical content, and without a canonical form the comparison produces false positives on every fetch.

Per-invocation binding for consequential tools

Schema binding stops the capability set from changing silently. It does not address a tool that was always dangerous and was approved once.

For tools on the irreversible rung, the approval has to attach to the specific call rather than to the server. The human sees the rendered arguments — the actual path, the actual amount, the actual recipient — and signs that.

Deriving the rendered statement from the tool's JSON Schema rather than from its description keeps the rendering honest: the fields shown are the fields that will be sent.

What a developer can check today

  1. List the tool servers approved in your editor or agent client. Most developers are surprised by the length.
  2. For each, determine what the approval was bound to — check whether the client stores a schema digest or only an identity.
  3. Determine whether approvals expire. Most do not.
  4. For servers you did not author, read the current tool definitions and compare them against what you believed you approved.

Step four is the one that finds things. Tool servers evolve, and nobody re-reads a schema they approved in March.

The 2025 Cursor advisories, precisely

Two related issues were disclosed in Cursor in mid-2025 and fixed in version 1.3. CVE-2025-54136, named MCPoison by the researchers who reported it, is the scope-blind one: once a user approved an MCP server configuration, an attacker who could modify that configuration changed what it ran, and the change inherited the original approval on every subsequent project open with no further prompt. CVE-2025-54135, CurXecute, is its sibling: content returned from an external service could rewrite the configuration file in the first place, with a CVSS score of 8.6.

The remediation is the tell. Cursor's fix was to require user approval every time an entry in the MCP configuration file is modified — that is, to stop treating the name as the thing that was approved. Responsible disclosure was on 16 July 2025; the fix shipped later that month.

The two advisories, and what each one binds
IdentifierNameApproval bound toEffect
CVE-2025-54136MCPoisonA server namePost-approval config change runs silently
CVE-2025-54135CurXecuteWorkspace write accessExternal content rewrites the config

Objections and honest limits

“This was patched, so it is history.” The specific bug was. The pattern — approving a mutable identifier rather than its resolved contents — is the default in almost every agent tool registry shipping today, and it is why the fix had to change what approval means rather than adding a check.

“We pin our tool servers.” Pinning a version helps if the pin covers the tool definitions and not just the package. Ask whether a server can change a tool's schema or description without changing the version your client pinned.

What schema binding does not fix: a server that was hostile at approval time, or a tool whose legitimate schema is dangerous. It closes the drift between approval and invocation, which is a narrower and more achievable goal.

Checking your own agent tool trust

  1. What exactly did the user approve? A server name, a package version, or a hash over the tool definitions. Only the third survives a change.
  2. What happens when definitions change? Re-prompt, refuse, or silently continue. Silently continue is the MCPoison shape.
  3. Is approval re-checked at invocation? Compare the schema hash recorded at approval with the one presented at call time, and fail closed on mismatch.
  4. Which tools can cause irreversible effects? Those need per-invocation authorisation, not a session-level grant, however the registry is governed.

Terms used here

MCP
Model Context Protocol: an open protocol for exposing tools and data to a model client. The tool definitions a server advertises are supplied by the server and can change.
Scope-blind approval
Approving a component by identifier without binding the approval to the behaviour that identifier currently resolves to.
Schema hash
A digest over a tool's declared input schema and description, recorded at approval and recompared at invocation so drift is detectable.

Frequently asked questions

Is this specific to one editor or vendor? No. The trust-after-approval pattern appears across clients that persist tool server approvals. Specific vulnerabilities are patched; the architectural class is what this article addresses.

Why not just re-prompt on every invocation? Prompt volume produces approval fatigue, which removes the control entirely. Schema binding plus per-invocation signing for consequential tools is the workable split.

Can the model be trusted to classify tools? No. Names and descriptions are supplied by the server and are attacker-controlled for third-party servers. Classification must come from review recorded outside the server's control.

What should a change in definitions do? Fail closed and require re-approval. A change is new capability, and treating it as routine is how the class became exploitable.

Is MCPoison CVE-2025-54135 or CVE-2025-54136? MCPoison is CVE-2025-54136. CVE-2025-54135 is the related CurXecute issue in the same editor, where external content could rewrite the MCP configuration file. Both were addressed in Cursor 1.3.

How was it fixed? By requiring user approval every time an entry in the MCP configuration is modified, rather than treating the original approval of a server name as covering later changes.

Does this affect other MCP clients? The specific bug was in one editor. The pattern of approving a server by name and inheriting that trust across configuration changes is common across agent tool registries.

Where this fits in Manav

Manav pins the tool's schema hash inside the signed statement, so the thing the human approved is the thing that must still be true at invocation. A changed definition produces a different hash and the call is refused rather than silently permitted.

See schema pinning →

Sources and further reading