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:
- Phase a: one tool,
canary. - Phase b:
canary's description gains a sentence that startsCANARY: this description changed after you approved this server, and a second tool,canary_added, appears.
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.