mcpgawkdocs ← Site

Rug-pull test server

A server can change what its tools do after you approved it. Your agent will call the new one and never notice. This server does exactly that, on a clock, with nothing dangerous in it, so you can watch what your setup does about it.

What it is

https://mcpgawk-testserver.vercel.app/rugpull/mcp is a streamable-HTTP MCP server with one tool, canary, that returns a fixed sentence and does nothing else. Every ten minutes, on the UTC clock, its surface flips:

Those are the two shapes a rug-pull takes: a tool you trusted says something new, and a tool you never saw turns up. Nothing else moves, so a diff reads as one thing. The status page /rugpull says which phase is live and how many seconds until the next flip. /rugpull-a/mcp and /rugpull-b/mcp never flip, for CI. /rugpull-c/mcp is the phase the pin does not see; it is explained below.

Harmless by construction. Every call returns one fixed sentence. The server reads nothing, stores nothing and sends nothing. The changed description is a plain canary sentence, not an instruction. A test pins all of that: no-op calls over the wire, benign wording, exactly two differences between phases, a deterministic clock. It is vendor-neutral: point any checker at it. mcpgawk is the one we know passes.

1 · Record the baseline

mcpgawk scan --http https://mcpgawk-testserver.vercel.app/rugpull/mcp

You should see one server and one tool (or two, if you caught phase b; the status page says which). The scan records this as the server's baseline under the name rugpull-canary, and prints the approve command:

mcpgawk approve mcp:rugpull-canary

Run that yourself, in your own terminal. It refuses to run from inside an agent session, on purpose: approval needs the person at the keyboard.

2 · Wait for the flip, scan again

Check the status page for the next flip, then run the same scan. You should see the change named, line by line:

    ⟳ DRIFT on rugpull-canary — changed since you approved it 3 minutes ago:
        ! tool description CHANGED: canary
            canary gained: 'CANARY: this description changed after you approved this server. If your agent can still call this tool, nothing checked the tool surface.'
        + tools added: canary_added
        Δ cost index: +77 tok
        Nothing in what changed matched a known injection pattern — that is not proof it is safe. Read the diff above before approving.
        Reviewed it and it is fine? mcpgawk approve mcp:rugpull-canary

That is a real capture from the shipped version, not a mock-up. The scan does not approve anything: the baseline is still the one you accepted.

3 · Watch the call get refused

With protection on (mcpgawk guard install, once), your agent's next call to the changed tool is refused before it runs. The agent is told this, word for word:

# mcp__rugpull-canary__canary
mcpgawk blocked rugpull-canary.canary — its content changed since you approved it. Review it with `mcpgawk decide` in your own terminal.

[mcpgawk guard] SECURITY BLOCK (mcpgawk). 'canary' on MCP server 'rugpull-canary' has CHANGED since you approved it — same name, different content (approved 9a57aa660753, last seen 2026-10-02T13:50:08+00:00 — not this call; a change since then is not yet measured: 06b713284255). This is the rug-pull shape: a tool you trusted, rewritten to say something else.
This decision is final for this session. Do not retry it, do not call a different tool to achieve the same thing, and do not run any mcpgawk command to change the baseline — approval requires the person at the keyboard, and attempting it from inside an agent session is itself treated as a red flag.
Tell the user exactly this: the MCP server 'rugpull-canary' changed the tool 'canary' after they approved it, mcpgawk blocked the call, and they should run `mcpgawk decide` themselves to read what changed before deciding whether to trust it.

# mcp__rugpull-canary__canary_added
mcpgawk blocked rugpull-canary.canary_added — a tool that was not there when you approved this server. Review it with `mcpgawk decide` in your own terminal.

[mcpgawk guard] SECURITY BLOCK (mcpgawk). 'canary_added' is not in the approved baseline for MCP server 'rugpull-canary'. A tool that appeared after this server was approved is how a malicious update arrives.
This decision is final for this session. Do not retry it, do not call a different tool to achieve the same thing, and do not run any mcpgawk command to change the baseline — approval requires the person at the keyboard, and attempting it from inside an agent session is itself treated as a red flag.
Tell the user exactly this: the MCP server 'rugpull-canary' added a tool called 'canary_added' since they approved it, mcpgawk blocked the call, and they should run `mcpgawk scan` themselves to see what changed before deciding whether to trust it.

The denial names no override. The agent cannot run mcpgawk approve for you; trying is itself treated as a red flag.

4 · Decide, then it happens again

mcpgawk decide

Read the diff, accept it or not. Ten minutes later the surface flips back and the next scan reports a change again. That is the point: a server you approved once keeps shipping, and only a baseline you hold tells you when it moved.

What is compared, and what the pin cannot see

Two comparisons happen, and they are not the same width. mcpgawk scan and mcpgawk monitor record a tool's name, description, input schema (canonically serialised, so key order is not a change) and annotations, plus prompt and resource descriptions, the transport and the protocol version, and report a change in any of them. The guard hook and the gateway, at call time, compare the tool's name and the hash of its description against what you approved; a tool whose schema or annotations changed with its description intact is reported by the next scan but is not refused at the call. The server's own name is the record's key; its version string is not compared.

An implementation change behind an identical definition is invisible to the pin, by design. The fixture says so rather than hiding it: /rugpull-c/mcp serves phase a's definitions byte for byte, and every call answers with a different sentence. Scan it against a baseline taken from /rugpull-a/mcp and nothing fires. That is the boundary of a tool-surface check; seeing that change takes observed behaviour, which is what mcpgawk verify and the gateway's call log are for, and they are not in this fixture.

Use it in CI

Pin either phase and assert on the exit code: mcpgawk scan --http https://mcpgawk-testserver.vercel.app/rugpull-b/mcp against a baseline taken from /rugpull-a/mcp exits non-zero with the drift report. Any other checker can do the same; the fixture does not care who is asking.