DEV Community

Mehdi Belckadi
Mehdi Belckadi

Posted on

I found a duplicate-charge bug in an MCP tool via static analysis — here's the finding and the fix (NEXUM-004)

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)