MCP Security

The Postmark MCP Backdoor Wasn't About the Code Review. It Was About What Happened After.

A malicious npm package spent fifteen releases looking exactly like a legitimate Postmark integration before it started blind-copying every email an agent sent. The lesson a year on isn't about catching malware — it's about what a one-time approval is worth once time has passed.

CheckedAgent

·

Key takeaways

  • A malicious actor published a fake postmark-mcp package on npm impersonating Postmark's own tooling; Postmark has stated it never published an MCP server to npm before the incident.

  • The backdoor was added in version 1.0.16, after fifteen preceding versions behaved cleanly — the kind of behaviour a one-time review would have found nothing wrong with.

  • The added code silently blind-copied every outgoing email to an address the attacker controlled; the tool's response to the calling agent still reported ordinary success.

  • The tool's self-reported outcome was not evidence of what it actually did — approval granted at version 15 said nothing about version 16.

  • Vetting an integration once, at install time, doesn't account for what a package can become after an update.

What happened

In September 2025, a package published to npm under the name postmark-mcp presented itself as an MCP (Model Context Protocol) server for sending email through Postmark. It was not built or published by Postmark. Postmark has since stated plainly: "we have not published our Postmark MCP server on npm prior to this incident." The package was the work of an unrelated author impersonating Postmark's name.

For its first fifteen published versions, the package did what it claimed: it sent email. Nothing in that behaviour would have raised a flag in a code review, and nothing about the package's name or description changed between releases.

Version 1.0.16, published on 17 September 2025, added one thing: every outgoing email was silently blind-copied to an address the attacker controlled. The tool's declared behaviour didn't change. Its description, its parameters, the way an agent was meant to call it — none of that moved. What changed was a single line, buried in code nobody outside the package's own repository was looking at.

The package was reported publicly on 29 September 2025 and its author later deleted it from npm.

What the tool told the agent

Here's the detail worth sitting with: when the backdoored version ran, the agent that called it got back an ordinary success response. The email had, after all, been sent — to the intended recipient and, silently, to the attacker too. Nothing about that response was malformed, anomalous, or worth flagging. A system built to inspect what tools return would have found a clean "sent" and moved on.

That's not a gap in response inspection specifically. It's a gap in what a tool's own report is worth as evidence. The tool said the job was done. It was — that just wasn't the whole job.



Fifteen versions bought nothing for version sixteen

A one-time approval — a security review, an allowlist entry, a developer's decision to install a package — answers one question: was this trustworthy at the moment it was checked. It doesn't answer whether it still is a week, a month, or fifteen releases later. Nothing about the mechanics of npm, or MCP, or how agents discover and call tools, requires that a package's behaviour stay constant after it's been approved.

That's the actual lesson from postmark-mcp, and it isn't really a story about malware sophistication — the change itself was, by every account, a small one. It's a story about the shelf life of trust. Version 15 earned nothing for version 16. The two shared the same name, the same install command, the same declared interface, and materially different code.



What this means for teams running agents against MCP tools

Treating an integration's security as a decision made once, at onboarding, is treating a moving target as fixed. The practical question isn't "did we vet this tool" — it's "when did we last check, and what's changed since." That's a different kind of control than a review gate: it's continuous, not a checkpoint, and it has to run for as long as the tool stays installed, not just on the day it was added.

Frequently asked questions

Was Postmark's own service compromised?
No. The malicious package was published by an unrelated third party impersonating Postmark's name on npm. Postmark has stated it did not publish an MCP server to npm before the incident and was not itself breached.

What did the backdoored version actually do?
Starting with version 1.0.16, the package silently added the attacker's address as a blind carbon copy recipient on every email sent through it, without altering the tool's declared parameters or description.

Would response inspection have caught this?
Not on its own. The tool's response to a successful send looked like any other successful send — nothing anomalous in the content returned to the calling agent. This wasn't a content-inspection gap; it was a gap in treating a tool's report of its own actions as sufficient evidence of what it did.

How long was the malicious version live before it was found?
Version 1.0.16 was published 17 September 2025; the incident was reported publicly on 29 September 2025.

What's the actual lesson for teams running MCP integrations?
That approval granted at one point in time doesn't extend automatically to a tool's next update. A package that behaved correctly for fifteen releases behaved differently on the sixteenth, with no change to what it claimed to do. The safeguard that matters is ongoing, not a one-time gate.

— Request a demo

Every Agent action.
Checked.

30-minute walkthrough with a security engineer — not a sales rep. We'll show the full pipeline, run your suspected attack patterns through it, and answer the questions your auditor is already asking.