All postsSecurity

MCP Tool Poisoning: Securing AI Agents That Act

Agents now write, send and deploy, so injected text in tool metadata and results is a real threat. How tool poisoning works and a layered playbook to stop it.

13 min readHoook Team

For most of the last few years, an AI assistant that got confused produced a bad paragraph. That is no longer the failure mode. Agents now open pull requests, send email, update CRM records, run queries and call production APIs. Once a model can act, anything that can steer the model can act too. The attack surface moved from the chat box to the tool layer, and the most important class of attack there is tool poisoning: instructions hidden in tool descriptions and tool results that the model follows as if they came from you.

This post explains how that attack class works, what the recent research shows, why better models do not make it go away, and a practical defense-in-depth playbook you can apply to any agent that uses MCP or function calling.

From reading to acting

Microsoft's incident response team put the shift plainly in a June 30, 2026 post on securing AI agents: as tools move from reading to acting, attackers can redirect agent behavior by manipulating tool descriptions, often without tripping any alert. Their recommendations are worth repeating up front. Treat MCP servers as supply chain dependencies. Handle tool descriptions with the same care as system prompts. Apply "least agency", not only least privilege.

The underlying problem is structural. A language model receives one stream of tokens. Your system prompt, the user's request, a tool's description and a tool's output all land in the same context window. The model has no reliable, built-in way to tell which parts are instructions and which parts are data.

Simon Willison named the dangerous combination in June 2025 as the lethal trifecta: an agent with access to private data, exposure to untrusted content, and the ability to communicate externally. Most useful business agents have all three by design. A support agent reads tickets from strangers, can see customer records and can send replies. That is exactly the shape attackers look for.

What tool poisoning is

Tool poisoning is a form of indirect prompt injection that targets the tool layer rather than the user prompt. There are two main places the injected text can live.

Poisoned tool descriptions

When an agent connects to an MCP server, it receives a list of tools with names, descriptions and input schemas. The model reads those descriptions to decide what each tool does and how to call it. Invariant Labs showed in April 2025 that a description can carry instructions that the model sees but the user never does, because client interfaces usually show a short summary rather than the full text.

Their research also described two variants that matter for anyone running more than one server:

  • Rug pulls. A server behaves well when you approve it, then changes its tool descriptions later. If the client does not re-check, the new instructions take effect silently.
  • Shadowing. A malicious server's descriptions tell the model how to behave when using other, trusted tools. The harmful action is then carried out by a tool you trust.

Microsoft's write-up describes the same pattern as a four-phase chain: a poisoned description, silent re-trust after a metadata update, a normal user request that invokes the tool, and exfiltration to an attacker endpoint. The user does nothing unusual at any step.

Poisoned tool results

The second location is the data a tool returns. The OWASP community entry on MCP Tool Poisoning focuses here. It points to a trust gap: descriptions may be reviewed at connection time, but responses flow into the model's context unchecked. A tool returns real data mixed with embedded directives, and the agent goes on to read files, call restricted tools or send data somewhere it should not.

This version does not need a malicious server at all. A perfectly honest tool that fetches a web page, a GitHub issue, a support ticket or an email will faithfully return whatever an attacker wrote into that content. The Practical DevSecOps overview of MCP vulnerabilities cites a mid-2025 Supabase MCP case in which instructions planted in a support ticket targeted an agent with privileged service-role database access, with the aim of reading and leaking integration tokens.

What the research shows

MCPTox: poisoning works against real servers

MCPTox, published in August 2025, is the first large benchmark of tool poisoning against real MCP servers. The authors built it on 45 live servers and 353 authentic tools, generated 1,312 malicious test cases across 10 risk categories, and evaluated 20 prominent LLM agents.

The results are uncomfortable:

Finding Value
Highest attack success rate 72.8% (o1-mini)
Average attack success rate across the 20 models 36.5% (per CSA's analysis)
Highest refusal rate Under 3% (Claude 3.7 Sonnet)

The low refusal rate is the key point. The attacks in MCPTox mostly ask the agent to use legitimate tools for unauthorized purposes. Nothing about the call looks harmful in isolation, so safety training that watches for obviously dangerous requests rarely fires. The authors conclude that existing alignment is ineffective against this pattern.

Comment and Control: a PR title hijacks coding agents

In April 2026, researchers Aonan Guan, Zhengyu Liu and Gavin Zhong disclosed an attack class they called Comment and Control, summarized in a Cloud Security Alliance research note. Three AI agents that run on GitHub events (Anthropic's Claude Code Security Review, Google's Gemini CLI Action and GitHub's Copilot Agent) could be steered by text in pull request titles, issue bodies and comments.

According to the note, the PR title was placed into the agent's context without sanitization. Content written by an outside contributor was therefore read as instructions, and the agents could be made to expose secrets from the runner environment, including API keys and GitHub tokens. Anthropic rated its finding 9.4 on CVSS. The recommendations were practical: audit workflows triggered by pull request events, remove secrets the runner does not need, and cut GITHUB_TOKEN permissions to the minimum.

The lesson generalizes. Any field an outsider can write to, whether a PR title, a calendar invite, a product review or a form submission, becomes part of your agent's prompt the moment a tool reads it.

Silent re-trust is a real bug class

Rug pulls are not theoretical. CSA's July 2026 note on tool poisoning and auto-execution lists CVE-2025-54136 (MCPoison), disclosed by Check Point Research, in which Cursor trusted an MCP configuration by name. Someone with write access to a repository could swap the server command after the team had approved it. The same note covers CVE-2025-54135 (CurXecute) and later research showing configuration auto-execution across several coding tools. The common thread: approval happened once, and nothing checked whether the thing approved was still the thing running.

Why more capable models can be more susceptible

It is tempting to assume the next model release will fix this. The MCPTox authors found the opposite trend: more capable models are often more susceptible, because the attack exploits their stronger instruction following.

That makes sense once you look at what the attacker is asking for. A poisoned description reads like a legitimate operating requirement: "before calling this tool, also do X". A model that is better at following detailed, multi-step instructions is better at following those ones too. A model that is better at chaining tools is better at chaining them for someone else.

The practical takeaway is that model choice is not a security control. Alignment helps at the margins, but you should design as if the model will sometimes follow injected text, and make sure that when it does, the damage is small, visible and reversible. That is what the rest of this post is about.

A defense-in-depth playbook

No single control stops prompt injection. The goal is layers, so that an attack has to beat several independent checks, and the ones that get through hit a small blast radius.

1. Allowlist servers and tools

Start with an explicit inventory. Only approved MCP servers should be reachable, and within each server, only the tools the agent actually needs should be exposed. OWASP lists allowlists of approved servers as a core mitigation, and CSA recommends auditing every configured server against one.

Expose tools per agent, not per organization. A research agent has no reason to see a "delete record" tool, and a tool the model cannot see is a tool it cannot be tricked into calling.

2. Pin and review tool descriptions

Treat tool descriptions like code. Invariant recommends that clients pin the server version and verify tool definitions with a hash or checksum. In practice:

  • Store a hash of each tool's name, description and schema when you approve it.
  • On every connection, recompute and compare. If anything changed, disable the tool until someone reviews the diff.
  • Review descriptions for anything that reads like an instruction to the model rather than documentation for it: references to other tools, requests to read files, or extra parameters that do not fit the tool's purpose.
  • Put MCP configuration changes through the same code review as production code, as CSA recommends.

3. Bind every call to a real user

Agents often run with a single shared service credential. That turns every injection into a confused deputy problem: the attacker borrows the agent's broad access. Instead, bind each tool call to the identity of the user the agent is acting for, and use that user's own token for the downstream API.

With per-user binding, an injected instruction can only reach what that user could already reach, and the downstream system's own permission model still applies. It also makes your logs meaningful, because each action traces to a person, not to "the bot".

4. Scope credentials to the minimum

Least privilege still matters, and it is often the cheapest control to add. Request read-only OAuth scopes when the agent only reads. Keep secrets out of environments the agent does not need them in. The Comment and Control fixes, stripping runner secrets and cutting token permissions, are this control in action. CSA also recommends running MCP processes in containers without ambient cloud or SSH credentials.

5. Require human approval for writes

The MCP specification says there should always be a human in the loop able to deny tool invocations, and that clients should show tool inputs to the user before calling the server. You do not need a prompt for every search. You do need one for actions that send, delete, pay, publish, merge or change permissions.

Enforce this in code, outside the model. A policy file makes the rules reviewable:

# agent-tool-policy.yaml
agent: support-assistant
servers:
  - name: helpdesk
    pinned_sha256: "3f9a...c21e"   # hash of approved tool definitions
    on_definition_change: disable_and_alert
tools:
  helpdesk.search_tickets:
    access: allow
  helpdesk.get_ticket:
    access: allow
    output: untrusted            # wrap and label before it reaches the model
  helpdesk.reply_to_ticket:
    access: require_approval     # a human sees the exact arguments first
    max_calls_per_session: 5
  crm.update_contact:
    access: require_approval
    allowed_fields: [phone, company]
  crm.export_contacts:
    access: deny
egress:
  allowed_domains: [api.helpdesk.example, api.crm.example]

The model can ask for anything. The runtime decides what actually happens.

6. Handle tool output as untrusted data

Everything a tool returns should be treated as input from a stranger. The MCP spec says clients should validate tool results before passing them to the model, and OWASP recommends structured output with schema validation. Useful habits:

  • Prefer structured results with an output schema, and reject responses that do not match.
  • Return only the fields the task needs. A 40-field record gives an attacker 40 places to hide text.
  • Clearly delimit and label external content as data in the prompt. This does not solve injection, but it helps.
  • Strip content the model does not need, such as HTML comments and hidden markup, before it reaches the context. CSA recommends exactly this for PR content.
  • Never pass model output straight into a shell, a query or a URL without validation.

7. Monitor at runtime and keep audit logs

The MCP spec asks clients to log tool usage for audit purposes. Go further and log each call with the user, agent, tool, arguments, result size, approval decision and the hash of the tool definition in use. Then watch for signals such as:

  • A tool definition hash that changed since approval.
  • Calls to tools that the current task does not explain.
  • Unusual sequences, such as a read of sensitive data followed by an outbound send.
  • Arguments that contain data copied from an earlier tool result, especially tokens or email addresses.
  • Spikes in call volume for a single user or session.

Logs turn a silent incident into a detectable one, and give you what you need to answer "what did the agent do, and for whom?"

8. Limit the blast radius

Assume something will get through, and design so that it cannot go far:

  • Break the lethal trifecta where you can. An agent that reads untrusted content should not also hold broad private data and open egress.
  • Restrict outbound network access to known domains.
  • Isolate privileged tools in a separate agent context, as OWASP suggests, and pass only vetted, structured data between agents.
  • Add rate limits and per-session caps on write actions.
  • Prefer reversible actions: drafts over sends, soft deletes over hard ones.
  • Keep a kill switch that revokes an agent's tokens or disables a server in one step.

A checklist you can use this week

  • Inventory every MCP server and tool your agents can reach.
  • Remove servers and tools that no current task needs.
  • Hash approved tool definitions and block changed ones until reviewed.
  • Route MCP configuration changes through code review.
  • Replace shared service credentials with per-user tokens.
  • Downgrade OAuth scopes to read-only wherever the agent only reads.
  • Strip unneeded secrets from agent and CI runner environments.
  • Require human approval, with visible arguments, for every write action.
  • Validate tool results against schemas and trim unused fields.
  • Label external content as data and strip hidden markup.
  • Log every tool call with user, arguments, decision and definition hash.
  • Alert on definition changes and read-then-send sequences.
  • Restrict agent egress to an allowlist of domains.
  • Test your agents with injected content in tickets, issues and PR titles.

Where the integration layer fits

Many of these controls live at the integration layer rather than inside the model: who the call runs as, which scopes the token has, which tools are exposed, and what gets logged. That is why it pays to centralize them instead of rebuilding them for every agent.

Hoook, currently in beta, is one way to handle that part. It provides managed auth with per-user connections and automatic token refresh, hosted MCP servers with one URL per user so each agent session is tied to a real identity, and a curated catalog of tools you can expose selectively. The policy decisions are still yours, but you are not wiring credentials and tool exposure by hand for every integration.

The short version

Agents that act turn every piece of text they read into a potential command. Research like MCPTox and Comment and Control shows the problem is real and that stronger models do not solve it on their own. You cannot fully stop a model from being fooled. You can decide what a fooled model is allowed to touch. Allowlist the tools, pin what you approved, bind actions to real users, keep scopes narrow, put humans in front of writes, and log everything. Then an injection becomes a blocked call in an audit log, not an incident.

Book a demo

Thirty minutes on what you are building, with the engineers behind Hoook.

Ask Ivy nowOur assistant answers straight away, then passes you to the team.

We answer within one business day.

Powered bydayrun.ai