A duplicate-charge bug in an MCP tool, found by static analysis before it shipped
A few weeks ago I ran a small side project of mine — Nexum, a deterministic static scanner for OpenAPI/MCP specs — against a public MCP server: adotob-mcp by Fabian Williams.
It flagged something real.
The finding
The purchase_free_bundle tool had no idempotency protection. In plain terms: nothing in the spec stopped the same call from executing twice.
For a human-driven API, that's a bug ticket. For an MCP tool exposed to an AI agent, it's a different category of risk. Agents retry. A network blip, a timeout, a retry-loop in whatever framework is orchestrating the agent — any of these can trigger the same tool call twice, with no human in the loop to notice. No Idempotency-Key header, no request_id field in the body, nothing for the server to deduplicate against.
This is one of five deterministic rules Nexum runs (NEXUM-004 — IdempotencyMissing). No LLM in the core detection path — it's pattern matching against the spec, on purpose, so the output is reproducible and explainable.
What happened next
I opened a PR. Fabian merged it the same day:
"Credit where it is due @mbelckadi — your trust-manifest is a clean static-analysis schema."
That's the best outcome this kind of scanning can have — a real fix, shipped fast, by someone who didn't have to engage with a stranger's static-analysis output at all.
Why this matters beyond one bug
The interesting part isn't the individual finding — it's the threat model. Most security tooling for APIs (Snyk, dependency scanners, generic SAST) answers "is this code/dependency vulnerable." Nexum asks a narrower, more specific question: what could an AI agent with real tool-calling access do if something goes wrong here — not if it's attacked, just if a retry happens, a param is missing, or a schema is looser than intended.
That's a smaller question than "is this API secure," and I think that's exactly why it's tractable to check for deterministically, without an LLM guessing at intent.
The full write-up
Case study with the actual finding, evidence, and fix: getnexum.dev/blog/nexum-004-fabian-williams
Thanks to Fabian for merging fast and for being fine with this being written up publicly.
Nexum only analyzes the spec you send it — no access to your production infra or MCP environment required. If you're running MCP servers or exposing APIs to AI agents and want to know what a scan would flag, the free scanner is at getnexum.dev.
Top comments (0)