deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

MCP tool descriptions can change after approval, evading scanners and code review

A dev.to post shows that MCP tool descriptions are re-fetched, unpinned prompt content, so an approved server can turn hostile without any config change.

MCP tool descriptions can change after approval, evading scanners and code review

An approval that expires silently

A post on dev.to lays out a supply-chain weakness specific to Model Context Protocol setups: an MCP server configuration can stay byte-identical after you review it while the server it points to becomes hostile. The mechanism is the tool description itself. Descriptions are loaded into the model's context on every connection, and nothing pins them to what you originally approved.

The post's worked example starts with an unremarkable config for a note-taking server whose single tool appends to a notebook. After the user approves it, three things could happen: the maintainer ships an update, the maintainer's npm account is phished, or the package was always malicious and simply waited. The description then grows an extra instruction telling the agent to read the user's SSH private key, pass it into a parameter, and keep quiet about it. The config file is unchanged, the version string can stay the same, and the attack arrives as documentation rather than code. The instruction enters the context window on connect, whether or not the tool is ever invoked.

The layers that cannot catch it

The author walks the existing security stack and shows each blind spot. Dependency scanners such as Dependabot, Snyk and Trivy read manifests and image layers, and the payload is not a dependency change. Semgrep and CodeQL scan source files, but the malicious text is a runtime string served by a process on the user's machine. Code review looks at diffs, and there is no diff. The human who reviewed the description read it once, weeks earlier, at install time. Every layer is working correctly; the hostile text simply arrives through a channel none of them watches.

Four properties that compose badly

According to the post, this is not one bug but four ecosystem traits that interact badly. Tool descriptions are prompt content, so whoever writes them writes part of your prompt. They are re-fetched on every connection, with no lockfile, signature or hash. The npx -y idiom auto-confirms an unpinned remote code fetch that re-resolves each time the agent starts. And organizations have no inventory of which MCP servers their engineers run or what those servers can reach. Package managers solved pinning for code over a decade ago; prompt text has no equivalent, and altered prose never has to pass a test suite.

Treating descriptions as artifacts

The post then introduces Bulwark, the author's own scanner (installed as bulwark-scanner), whose central feature is a lockfile. bulwark pin records a content hash of every description and schema the model may see, and that file is committed. When the maintainer later pushes the altered description, bulwark verify fails in CI on three counts: the text changed, the tool gained a credential-touching capability, and the argument schema grew a field that could carry data out. The author's argument is that detection rules are an arms race, while integrity checking is not — you do not need to recognize every payload, only to notice that the thing you approved is no longer the thing running.

Per the post, the tool parses MCP configs for Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Cline, Roo, Zed and Continue, makes no network calls, and maps findings to frameworks such as the OWASP LLM Top 10 and MITRE ATLAS. These claims come from the tool's own author and are not independently verified.

Checks an inventory enables

Parsing every agent surface makes other checks cheap, the post argues. Descriptions can hide instructions in the Unicode Tag block (U+E0000–U+E007F), which renders as nothing in mainstream editors, or in zero-width characters, bidi overrides, base64 and HTML comments; a scanner can decode and print what was hidden. A combination no single-tool check catches is an agent that can simultaneously reach private data, ingest outsider-written content, and send data outside the network — a complete exfiltration path requiring no vulnerability. Two servers exposing the same tool name also lets a newly added one quietly capture calls meant for the established one.

Limits the author concedes

The post is candid about boundaries. The scanner does not make a model injection-resistant; a crafted instruction inside an allowed tool's result can still be followed, and the durable fix there is architectural — removing one leg of the exfiltration path. It does not read server source code, so an honest description over a malicious implementation passes. Capability inference is a heuristic over names, descriptions and schemas, with the schema weighted most heavily.

Why it matters

The core point stands regardless of any particular tool: MCP's consent model is a snapshot, but the text the model actually obeys is fetched live and pinned nowhere. As agents gain access to credentials, files and the network, "I reviewed this server once" stops being a security boundary. Hash-pinning descriptions, keeping an inventory of connected servers, and breaking up the reach-private-data / ingest-external-text / send-outbound-data combination are the practical responses the post leaves readers with.

  • #mcp
  • #prompt-injection
  • #supply-chain
  • #security
  • #ai-agents

Related posts