Back to blog
Engineering12 min read

MCP Security: What You're Actually Granting

MCP security means knowing what a connection grants: full tool access, an OAuth scope, or local code execution. Verified against the spec and vendor docs, Sept. 2026.

NH
Nafiul Hasan
Founder, Prompt Architects

TL;DR: MCP security depends entirely on what you connect, not on the protocol itself. A remote server gets only the OAuth scopes you grant, revocable anytime. A local (stdio) server runs with your full user privileges and skips authorization by design. Read the tool list and the transport type before you approve either.

"Connect an MCP server" is a one-click action in most clients now, and that's exactly the problem. The button feels like installing a browser extension. What it actually does is closer to handing someone a set of keys and hoping they only use the ones you meant to give them. MCP security isn't a single property the protocol either has or lacks. The specification deliberately leaves large parts of it to whoever builds the client and whoever builds the server. This is what "MCP security" actually resolves to once you read the specification itself rather than a summary of it: what a connection grants, what's optional, and where the real risk sits. We ship an MCP server ourselves, so we'll hold it to the same bar at the end.

If you're brand new to MCP and just want it working, start with MCP for Beginners; this post assumes you already know what a connection is and want to know what it costs.

What does connecting an MCP server actually grant?

Three things happen the moment you approve a connection, and only one of them is usually visible on the consent screen.

First, every tool the server declares becomes callable by the model, not just the ones you'll ever use. A server built for a ticketing system might expose create_ticket, close_ticket, and delete_project, and all three become available the moment you connect, whether your workflow needs the third one or not.

Second, if the server runs locally over stdio (the default for anything installed as a command rather than a URL), there's no authorization layer between you and it at all. Authorization is optional in MCP generally, and for stdio specifically the specification is direct about it: "Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment." No scope, no token, no consent screen. The server runs with whatever your own user account can already do.

Third, if the server also uses MCP's older "roots" feature to advertise which folders are in scope, don't mistake that for enforcement.

None of this is a defect in MCP. It's a protocol describing several genuinely different trust postures (remote and OAuth-scoped, local and unscoped, or something in between), and it leaves the client to decide which one you're getting.

Is MCP secure by design, or does that depend on what you connect?

The specification's protections are almost all worded as SHOULD, not MUST, which is worth noticing if you're used to reading protocol documents as hard rules. On tool calls, the spec says: "there SHOULD always be a human in the loop with the ability to deny tool invocations." The identical requirement governs sampling, the lesser-known feature where a server can ask your client to run a model completion on its behalf, using your account and your tokens.

SHOULD is a request to implementers, not a protocol-enforced gate. Nothing in MCP itself stops a client from auto-approving every tool call. The specification is describing the client it wants built, not the one you necessarily have in front of you.

Tool metadata carries the same caveat. A server can attach annotations to a tool declaring it read-only, non-destructive, or safe to call repeatedly, but the tools specification treats these as hints, not guarantees, and says so in the same breath: "clients MUST consider tool annotations to be untrusted unless they come from trusted servers." The official schema goes further, warning that a tool's annotations "are not guaranteed to provide a faithful description of tool behavior" and that "Clients should never make tool use decisions based on ToolAnnotations received from untrusted servers." A tool can call itself read-only and still write to disk. Nothing in the protocol checks that claim for you. By default, the schema itself assumes the opposite: a tool with no destructiveHint set is treated as potentially destructive, not safe, unless the server says otherwise.

Local server vs. remote server: what's the actual difference?

Most of what "MCP security" boils down to in practice is which of two shapes you're connecting: a command that runs on your machine, or a URL that runs somewhere else.

A local server, using the stdio transport, is a program, not a service, and the specification's own security guidance treats it that way. It notes that local servers "may have direct access to the user's system" and lists arbitrary code execution first among the resulting risks: "Attackers can execute any command with MCP client privileges." A malicious startup command, or a legitimate server with an ordinary bug, gets exactly the access you have: your files, your shell, whatever the account running the server can already read. There's no default sandbox, and as covered above, the authorization spec doesn't even apply here by design.

A remote server, over HTTP, is different by construction. Authorization is optional under MCP too, but when a server does require it, the specification pushes implementations toward OAuth 2.1: bearer tokens, a defined scope, and a revoke button that actually works. Compromise a remote server's OAuth client and an attacker gets whatever scope you granted. Not your machine.

MCP's two transports imply two different trust models
FeatureLocal (stdio) serverRemote (HTTP) server
Where credentials liveYour local environment: files, env vars, whatever the process can readAn OAuth 2.1 authorization server, behind a browser consent screen
What the spec recommendsSkip the authorization spec; read credentials from the environmentFollow the authorization spec: OAuth 2.1, ideally with PKCE
Blast radius if it's maliciousWhatever your OS user account can doWhatever the granted token's scope allows, no more
How you revoke accessDelete the config entry, kill the process; no central switchRevoke the token at the authorization server, effective immediately

If you just need a server connected and want the setup mechanics rather than the risk model, see our Claude Desktop MCP setup walkthrough.

What do Claude Code and Cursor actually check before a tool runs?

The specification leaves this entirely to the client, so it's worth knowing what your specific tool actually does rather than what the protocol permits in theory.

Claude Code starts a session with read-only permissions in its default Manual mode. Its own security documentation states that when it needs to edit files, run tests, or execute commands, "it asks you first, and you choose whether to approve the action once or allow it from then on." Project-scoped servers (the ones defined in a .mcp.json file a teammate committed) need your explicit approval before Claude Code will connect to them at all, and an unfamiliar workspace won't auto-approve them even if the committed file says to (more on scopes and trust in our Claude Code MCP guide). The risk most worth internalizing here is prompt injection: content a connected tool fetches that quietly tries to redirect the model. Claude Code's own MCP docs put it bluntly: "Servers that fetch external content can expose you to prompt injection risk." (Our dedicated piece on defending against prompt injection covers the pattern beyond MCP specifically.)

Cursor's model puts a checkpoint both earlier and later. "All MCP connections need your approval." After that, "each tool call still needs individual approval before running" unless you've pre-approved it on an allowlist. Cursor is honest about that allowlist's limits, too: its guardrails, including Run Modes and the auto-review classifier, are "best-effort guardrails rather than a hard security boundary." Not a sandbox.

Neither client changes what the protocol grants. Both are deciding, on their own, how much friction sits between a tool call and your approval: exactly the SHOULD-not-MUST gap the specification leaves open. If a server you've connected won't hold a connection at all, that's a separate problem; see MCP Not Working? 15 Fixes.

What attack patterns does the protocol's own spec name?

Two named attack classes are worth knowing before you build or install one.

The first is the confused deputy problem: a proxy server using a single static client ID against a third-party API, combined with a consent cookie the third-party authorization server sets after the first login, can let a malicious client obtain authorization codes without the user ever seeing a fresh consent screen. It's a plumbing bug, not a phishing attack: the server does the trusting on your behalf, and does it incorrectly.

The second is token passthrough: a server that accepts a token from the client and forwards it unchanged to a downstream API, without checking that the token was actually issued for it. The specification calls this "explicitly forbidden", for reasons that include breaking rate limits, destroying the audit trail, and letting a stolen token be laundered through the server. The mitigation is a flat MUST NOT: "MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server."

There's a subtler, third pattern that's a design tension rather than a named attack. The security-best-practices page's own scope-minimization guidance says that when a server's WWW-Authenticate challenge doesn't name a scope, clients fall back to requesting everything in scopes_supported: "Requesting all available scopes allows the authorization server and end-user to determine appropriate permissions during the consent process". That's the documented fallback, even though the separate authorization page recommends the opposite as the default posture: "clients SHOULD follow the principle of least privilege by requesting only the scopes necessary for their intended operations." Which behavior your client actually follows is worth checking. Claude Code, for one, changed away from requesting the full catalog specifically because it broke sign-in against identity providers that advertise admin-only scopes.

How do you actually limit what you're granting?

None of the mitigations above require trusting the server more. They mostly require doing less by default.

  • Read the tool list before you approve. Every client shows it somewhere: Claude Code's /mcp panel, Cursor's MCP settings. A ticketing server that exposes delete_project alongside create_ticket is a decision, not an implementation detail.
  • Pin your OAuth scopes instead of accepting the default. Claude Code supports this directly: set oauth.scopes in the server's config to the subset you actually need, and it takes precedence over whatever the server or its metadata advertises.
{
  "mcpServers": {
    "slack": {
      "type": "http",
      "url": "https://mcp.slack.com/mcp",
      "oauth": {
        "scopes": "channels:read chat:write search:read"
      }
    }
  }
}
  • Use a personal access token for headless jobs, not a standing OAuth session. A long-lived token you generate and can revoke on sight is easier to reason about in a CI pipeline than a browser session nobody's watching.
  • Treat a local server like the program it is. Read the source before running one from an unfamiliar repository. Apply the same caution you'd use for any other unfamiliar script you were about to execute. Cursor's own guidance agrees: "review the server's source code", and "Run sensitive servers locally with stdio transport" once you have, rather than exposing one to the network.
  • Don't trust a tool's own annotations. destructiveHint defaults to true and readOnlyHint defaults to false in the protocol's schema, so the cautious assumption is already the default. The spec doesn't let a client rely on a server's word for it anyway.

What does Prompt Architects' own MCP server grant?

We ask readers to check this everywhere else in this post, so here's ours, checked the same way, on 4 September 2026.

Our MCP server (mcp.prompt-architects.com/mcp) exposes exactly four tools: improve, refine, shorten, and enhance, each a narrow prompt-rewriting operation, not an account-management surface. Connecting uses OAuth 2.1 with PKCE by default; a personal access token is the alternative for headless setups, and either credential can be revoked from your dashboard, effective immediately. Our own integrations page states the boundary directly: "The MCP server never reads your chat history." What it does store is exactly what the equivalent action in the web app would: "Transformed prompts are stored in your account history so you can revisit them — same as the web app." Tokens are stored hashed, and the raw value is "shown exactly once at creation."

That's a narrower grant than a general-purpose local server by design: four tools, no filesystem roots, no ability to see what you're doing in your editor or chat window before you invoke one of them. It's also not nothing: approving it does hand a model the ability to call those four tools on its own judgment, which is exactly the kind of decision this whole post has been arguing you should look at before you click approve, on any server, including ours.

A pre-connection checklist

Five checks that take less time than the connection dialog itself:

  • What tools does the server expose, and does your workflow actually need all of them?
  • Is it local (stdio) or remote (OAuth)? A local server runs with your privileges; a remote one is bounded by whatever scope you grant.
  • If it's OAuth, can you pin the scope instead of accepting the default catalog?
  • If it's local, have you read the source, or does it come from a maintainer you'd trust with your .ssh folder?
  • Does the client you're using show tool calls before running them, or only log them after?

None of this requires distrust as a starting posture. It requires treating "connect" as a real permission grant, because per the specification itself, that's exactly what it is.

Free Chrome Extension

Stop rewriting prompts. Start shipping.

Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.

Create An Account

Frequently asked questions

Free Chrome Extension

Stop rewriting prompts. Start shipping.

Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.

Create An Account