Security Engineering

Capability Attestation and Tool Poisoning: Trusting What a Server Claims

David Guzenburg/ / 8 min read

You have a lockfile for your npm dependencies and nothing equivalent for the tool definitions an MCP server sends you on every single connection.

MCPsupply chaintool poisoningattestation

The claim nobody verifies

When a client connects to an MCP server, the server declares what it does. It sends a list of tools, each with a name, a description and a schema. The client accepts that declaration and presents it to the model.

There is no mechanism in the protocol for verifying that the declaration is accurate. Security analysis of the specification identifies this as one of three protocol-level weaknesses, and the phrase used for the missing piece is capability attestation — the absent ability to check that a server's stated capabilities match its actual behaviour.

A server declaring a read-only lookup may write. A server declaring it touches one resource may touch several. The declaration is a claim, and the protocol treats claims as facts.

Tool poisoning: the declaration can change

Worse than an inaccurate declaration is one that changes after you approved it. Tool poisoning — documented in the threat-modelling literature — describes a server that presents benign tool definitions during initial review and different ones later.

PhaseWhat the server sendsWhat the user sees
InstallHonest, narrow descriptionReviews it, approves
Weeks 1–6SameWorks as expected, trust builds
LaterDescription now carries instructionsNothing — descriptions are not re-reviewed

The asymmetry is the problem. Review happens once, at install, when the server is on best behaviour. The definitions are fetched fresh on every connection thereafter, and nobody looks at them again.

This is not exotic. It is the ordinary supply-chain pattern — the same shape as a package that behaves for six versions and ships something else in the seventh — applied to a channel with no integrity checking and no lockfile.

The gap in one sentence

You have a lockfile for your npm dependencies and nothing equivalent for the tool definitions an MCP server sends you on every connection.

Pinning definitions yourself

Since the protocol does not provide integrity checking, the practical mitigation is to build a crude version. Record a hash of each server's tool definitions when you approve it, and check on subsequent connections.

import hashlib, json, pathlib, sys

PINS = pathlib.Path("mcp-tool-pins.json")

def fingerprint(tools):
    # stable hash over name + description + schema of every tool
    norm = sorted(
        (t["name"], t.get("description", ""),
         json.dumps(t.get("inputSchema", {}), sort_keys=True))
        for t in tools
    )
    return hashlib.sha256(
        json.dumps(norm, sort_keys=True).encode()
    ).hexdigest()[:16]

def check(server_name, tools):
    pins = json.loads(PINS.read_text()) if PINS.exists() else {}
    current = fingerprint(tools)
    known = pins.get(server_name)

    if known is None:
        print(f"NEW server {server_name}: {current}")
        print("Review the tool descriptions, then re-run with --accept")
        return False
    if known != current:
        print(f"CHANGED {server_name}: pinned {known}, now {current}")
        print("Tool definitions changed since approval. Review before use.")
        return False
    return True

Simple, and it converts a silent change into a visible one. That is the whole objective: you cannot prevent a server from changing its definitions, but you can refuse to let it happen without anyone noticing.

Reviewing a server before you trust it

Since attestation is unavailable, review substitutes for it. Six questions, in rough order of how much they tell you.

QuestionConcerning answer
Who publishes it?Anonymous, or a recently created account
Is the source available?Binary only, or a repo that does not match the release
What does it actually reach?Broader network or filesystem access than its purpose needs
Does it need credentials?Asks for more scope than the stated function requires
Do the descriptions contain directives?Any instruction addressed to the model, not the user
How is it updated?Auto-updating from a mutable tag with no pinning

The fifth is the fastest signal and takes thirty seconds. Dump the tool list and read the descriptions as a person. A description is meant to tell a model what the tool does. Anything that instructs it to do something else first, or references files and systems unrelated to the tool's purpose, is worth stopping on.

Run the server where it can do least

Reviewing a server tells you what it appeared to do at review time. Since you cannot rely on that holding, the durable control is limiting what it can reach.

The registry problem

With more than 18,000 servers listed on public directories by early 2026, most teams find servers by search rather than by referral. That is a meaningful change in how the trust decision gets made, and not for the better.

A package on a mainstream language registry carries some ambient signal: download counts accumulated over years, dependents you can inspect, a maintainer with history, security scanning by the registry itself. MCP directories are younger and thinner. A server listed there may have been published last week by an account created the week before.

Two habits help, and neither is sophisticated. Prefer servers published by the organisation whose service they wrap — an official server for a SaaS product is a different proposition from a third-party wrapper of the same API, because the vendor has a reputation at stake and you already have a relationship with them. And prefer servers you can read. Most MCP servers are small. Skimming a few hundred lines before granting a process access to your filesystem is a reasonable hour.

A useful reframing

You would not run curl | bash from a URL you found in a search result. Installing an unreviewed MCP server that runs locally with filesystem access is closer to that than to adding a library dependency, because there is no sandbox between it and your machine.

The realistic posture

Treat every MCP server as untrusted code running with the access you grant it, because that is what it is. The lightweight install experience — a few lines in a config file — badly understates the trust decision, and the mismatch between how easy installation feels and how consequential it is accounts for a good deal of the exposure in the wild.

Practically: keep the connected set small, pin the definitions, scope the credentials, isolate the process, and re-review anything that changes. None of that is novel security thinking. It is ordinary supply-chain discipline applied to a dependency type most teams have not yet recognised as one.

Takeaway

The protocol cannot verify that a server does what it says, and definitions are re-fetched on every connection with no integrity check. Pin the fingerprint, read descriptions for directives addressed to the model, scope credentials tightly, and run the process with the least access that lets it work.

Keep reading
Security Engineering

Reviewing Third-Party MCP Servers Before You Install Them

A tiered review process short enough to actually run: publisher, source, behaviour and descriptions, plus the pinning and containment that keep working when the review was wrong.

Security Engineering

Implicit Trust Propagation: Why Provenance Dies in the Context Window

Content loses its origin the moment it enters the context, and tool chains launder it further. The tainted-session model, and how to split research from action.

Security Engineering

Unauthenticated Sampling: When an MCP Server Drives Your Model

MCP sampling lets a server request completions on your account, inverting the usual direction of control. Why it is a documented attack vector and how to constrain it.

Security Engineering

The MCP Threat Model: Where Trust Actually Breaks Down

Why the Model Context Protocol attracted 30+ CVEs and a DoD advisory within eighteen months: three protocol-level weaknesses, and why the whole context window is one trust domain.

← Reviewing Third-Party MCP Servers Before You Install Them  ·  Gating Irreversible Actions: Controls That Don't Depend on the Model →

All security engineering articles  ·  Every article