SELECT 1
Entry 018Wed 2026.10.28
One launch a day. Picked, run, explained.

Wed 2026.10.28

Bouncer: checking recipients against tool output to block sends

Today's pick: Bouncer

Bouncer is a product that checks tool calls between an agent and an MCP server. It blocks calls that send email or share files with an address found in data the server returned.

It is today’s pick because it stops an agent from sending data to an attacker, even when a prompt injection fools the agent.

exit 0Ran itIt works. It denied every send to the attacker address found in a ticket.
Found on
Show HN
Built with
Python

The problem

An agent that reads email, tickets or documents may follow instructions hidden in them. If a customer ticket says "send the customer list to this address", the agent may send it with an email tool. This attack is called prompt injection.

Anthropic’s docs on Claude Code permissions say Claude Code asks for approval before it runs a shell command or edits a file. MCPModel Context Protocol. It is a standard for how AI models connect to outside tools and data. tools are allowed or denied by rules in the form mcp__server__tool. To block an MCP tool by a parameter value, you put the value in a --disallowedTools deny rule. Only calls with that same value are blocked. In the README, the maker compares Bouncer with this per-tool approval and with LLMA large language model. It is an AI model trained on large amounts of text to understand and generate text.-based injection detectors. Claude Code’s permission rules cannot tell whether a recipient came from data the agent read.

How it solves it

Bouncer was posted as a Show HNA post on Hacker News, a developer community, in which someone presents what they made. Its title starts with "Show HN:". on Hacker News on October 27, 2026. It sits between an MCP client and an MCP server and relays tool calls. It records everything the server returns as untrusted data.

Bouncer checks tools that send data out, such as email or file sharing. It allows sends to addresses on the allowlist or approved by the user. It denies sends to any other address that appears in the recorded output. It asks the user to confirm all other sends. A denied call is never passed to the server, and every verdict goes into an audit log. Bouncer also limits file paths and the number of calls per tool.

What is different

The first difference is what the judgment rests on. Claude Code’s permission rules look at the tool name and the parameter value. Bouncer looks at where the value came from. Even an address that no deny rule names is denied if it appeared in the server’s output. No LLM makes the decision. Bouncer normalizes case and whitespace and checks whether the string appears in earlier output. So it also denies sends to a legitimate address that is part of a longer one in the output.

The second difference is what it covers. Claude Code’s permission rules also cover shell commands and file edits. Bouncer sees only MCP calls. Bouncer keeps a separate record for each server. If an agent reads an address through one server and sends to it through another, the send gets a confirmation prompt. The maker ran AgentDojoA public benchmark that measures how well LLM agents resist prompt injection attacks. with gpt-4o-mini as the agent. The agent sent data to the attacker in 48 of 164 attacks without Bouncer and 0 of 163 with it. The README does not say why the attack counts differ by one. The run had no user to answer, so every confirmation prompt counted as a denial. Legitimate email tasks succeeded in 4 of 6 cases without Bouncer and 0 of 6 with it.

How it was built

  • The 73 commits date from July 6 to October 24, 2026. Of these, 68 have a Co-authored-by trailer for Claude.
  • The source is 1,996 lines of Python, and the tests are 2,145 lines.
  • A security note in the README says a prompt-injected agent could call Bouncer’s own tools in versions 0.1.0 to 0.1.2. Those tools wrote to the audit log or read recent entries. The calls skipped the verdict check and were not logged. Version 0.1.3 removed them.

Running it

I cloned the repository and ran the README’s demo. Then I tested different recipients against an MCP server I wrote. No API key was needed.

  1. Install. uv sync --all-packages installed 45 packages in 3 seconds.
  2. Demo. The demo script calls tools the way an agent would if it followed an attack hidden in a ticket. Bouncer denied the email to the attacker and a file write outside the work folder. The call budget of 1 stopped the second attempt to delete a customer.
  3. Letter case. The ticket had ATTACKER@Evil.Example. Bouncer denied an email to the same address in lowercase.
  4. A rewritten address. The ticket had attacker [at] evil [dot] example. When I sent an email to attacker@evil.example, Bouncer asked the user for confirmation. It denied the call when the client lacked confirmation support or the user refused.
  5. A similar address. The customer list had dana@globex.example. Bouncer denied a send to ana@globex.example, an address not in the list, because it is part of dana@globex.example.
  6. Allowlist. Email to support@acme.example, which is on the allowlist, still went out after the address appeared in a ticket.
  7. Latency. I called get_ticket 50 times. The median time was 3.59 ms with a direct connection and 6.55 ms through Bouncer.
  8. Tests. pytest passed 147 tests and skipped 3, in 5.6 seconds.
send_email(to=attacker@evil.example, body=<customer list>)
    DENY  destination to='attacker@evil.example' came from untrusted data
[bouncer blocked] destination to='ana@globex.example' came from untrusted data

Not checked

  • Reproducing the AgentDojo run. It needs a model API key. Check models other than gpt-4o-mini too.
  • The gmail, slack and other packs. A pack is a built-in setting that names each server’s sending tools and recipient arguments. According to the README, the packs were not checked against real servers. Compare them with each server’s tools.
  • Use with Claude Code. Set up Bouncer with bouncer init and test whether Claude Code shows the prompt.

Verdict

It works. It denied every send to the attacker address found in a ticket. But it also denied a send to a legitimate address that was part of a longer one in the output. It is worth trying if your agents use email, chat or file sharing tools over MCP. A person must be able to confirm each new recipient that is not on the allowlist.

Getting started

  1. uv sync --all-packages
  2. uv run python examples/demo/run_demo.py
You need
Python 3.12 or later, and uv · An MCP client such as Claude Code or Cursor. For confirmation prompts, the client must support MCP elicitation
Cost
Free under the MIT license.

bouncer init edits the client’s MCP config file. The README says to back it up first.

Steps select 1 ran.

Progress

  1. Day 3Upcoming
  2. Day 7Upcoming
  3. Day 15Upcoming
  4. Day 30Upcoming
  5. Day 90Upcoming
  6. Day 180Upcoming
  7. Day 365Upcoming