Volodymyr ShkarbanSoftware engineerLet’s talk
All writing

Writing · · 8 min read

What I learned reporting security issues to ten MCP vendors.

In April 2026, I sent coordinated security reports to ten companies that publish MCP servers. Three answered with a person. One fixed the issue and published a CVE. This is what I found, what came back, and what I changed in how I report.

Sent
21 and 22 April 2026
Window
90 days, closed 20 July 2026
Vendors
Anthropic, CircleCI, Sentry, MongoDB, Vercel, Salesforce and Heroku, Shopify, Qase, Railway, Upstash
Manuscript
Co-authored with Oleksandr Leontiev
10

vendors reported to, one email each, with a 90-day window

3

replies from a person: CircleCI, Sentry and Upstash

1

published advisory with a CVE, from CircleCI

Where the reports came from

The reports came out of a study of the tools that AI agents call. I built a scanner that connects to a tool server, reads what it says about each tool, and in some cases calls the tools in a sandbox. I ran it across six protocols. The manuscript, “Across the Protocol Stack”, covers 424 endpoints.

MCP scored lowest of the six. That made MCP servers from well-known companies the obvious place to look closer. I picked servers published by the vendors themselves, checked each finding by hand, and wrote one report per vendor.

Five kinds of problems

Most findings fell into five groups. I am naming the groups and how often they appeared, not which vendor had which unfixed issue.

1. Annotations that don’t match what the tool does (8 of 10 reports)

A tool marked readOnlyHint: true that can write, or a tool that deletes or deploys with no destructiveHint. Hosts use these hints to decide when to ask the user. This was by far the most common problem, and it is the one behind the CircleCI CVE.

2. Tool descriptions that give the model orders (4 of 10)

Text like “You MUST call this function before…” or a “Note to LLM” inside a description. The model reads the description in the same context as the user’s request, and cannot tell a vendor’s instruction from an attacker’s.

3. Chains across tools (4 of 10)

One tool reads something sensitive, like CI configuration or file contents. Another tool can store or send data somewhere. Each is fine alone. Together, a prompt injection can move data from one to the other with no confirmation in between.

4. Parameters the description doesn’t mention (2 of 10)

A parameter in the schema that the description never explains, so a user reading the tool list would not know it exists.

5. Loops (2 of 10)

Tools whose descriptions point the model at each other in a cycle, which can keep an agent calling them and burn through its context.

What came back

CircleCI: a fix and a CVE

CircleCI opened a ticket the same day and asked which commit I tested. In July they added destructiveHint to eight tools, fixed how annotations were registered, and published GHSA-8xjg-jpfh-5257 with CVE-2026-95854. Full story here.

Sentry: “go ahead”

An engineer replied 13 minutes after the email. The reply said I was free to disclose, that they would fix it, that the whole ecosystem is still immature, and that next time a normal bug report would be fine. That last part was a useful lesson about scale: not every finding needs a formal disclosure.

Upstash: a real reason

My Context7 report was about two descriptions that told the model it must call one tool before the other. An engineer explained why the wording is there: in their tests, agents rarely use a tool unless the description pushes hard, and users who install Context7 expect it to be used without typing “use context7” every time. They closed the ticket. I think their reason is fair, and it changed how I think about this class of finding (more below).

Anthropic and Vercel: wrong channel

Both sent automated replies pointing to their HackerOne programs. An email to a disclosure address does not start a review there. That is on me: their process was documented and I should have used it.

The other five

I have no reply on record from MongoDB, Shopify, Salesforce and Heroku, Qase or Railway.

Steering or injection?

The Upstash reply is the part I think about most. A vendor writing “you must call this first” is doing exactly what a prompt injection does: putting instructions where the model will follow them. The difference is intent, and the model cannot see intent.

But the vendor’s problem is real. Without strong words, models skip tools that would help the user. So this is less a bug in one server and more a gap in how tools describe themselves. A structured way to say “call this one first”, or hosts that show users the full description text, would let vendors guide the model without writing instructions that look like an attack.

I now report this class as a design question with a suggested alternative, not as a vulnerability.

What I got wrong

  • Severity came from the scanner. I checked every finding by hand, but words like HIGH and CRITICAL came from an early version of the scanner. In June I tested it against deliberately vulnerable services and clean ones. When it said CRITICAL, it was right about one time in three. In July the paper was postponed so every number can be re-checked before it goes out.
  • Some findings read a schema gap as a proven bug. A path parameter with no pattern in its schema is not a path traversal if the code checks the path. A report should say what the schema allows and what the code was or was not tested to do.
  • The subject lines were too loud. I used phrases like “annotation lies” and “behavioural hijacking”, plus weekly download counts. Plain words get read more charitably.
  • No commit hashes. CircleCI had to ask which commit I tested. Every report should name the exact version.
  • Wrong channel for two vendors. Read the security policy first, then send.

How I report now

The private reports I sent in October 2026 follow a different template. Each one goes to a single project, through the channel its security policy names.

What every report now contains
Project, exact version and commit
What the docs or policy say the code enforces
What the code does instead, in one paragraph
A reproduction script with fake secrets only
A small patch in the project's style, with tests
  that fail before the fix and pass after
What I did not test
Severity: left to the maintainers
Whether an AI assistant helped, and how

I still think the April work was worth doing. One CVE, one “go ahead”, and one honest disagreement taught me more than the scanner did. But the October reports are the ones I would want a maintainer to judge me by.

I am not publishing details of findings that vendors have not fixed. The finding counts above come from my April emails. The scanner accuracy numbers come from my own June test on local services and describe that early version only.

For a role, a project, or a technical conversation.

Building something
agents will touch?