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 (3)

Collapse
 
alexshev profile image
Alex Shev

Static analysis is a good fit for money movement bugs because the failure pattern is often structural. I would pair the finding with a regression fixture so the same class cannot come back.

Collapse
 
mbelckadi profile image
Mehdi Belckadi

Good call, fits the deterministic approach well, each rule already has internal fixtures, just not surfaced in the report as a "prevention" suggestion yet. Curious — are you working with MCP/agents in production yourself, or looking at this more from a general static-analysis angle?

Collapse
 
tercelyi profile image
tercel

IdempotencyMissing is a sharp rule because it matches how agents actually behave: retries + no human in the loop. I’d build a small checklist around it:

1) Side‑effecting tool? Require an idempotency token field.

2) Does the spec say how conflicts are handled? Make it explicit.

3) For each tool, ask: “what if this runs 3 times in a row?”