A secure Model Context Protocol deployment starts with one rule: treat every MCP tool call as a real operation performed with the server’s identity and access. Reduce what the server can reach, validate requests at the server boundary, keep credentials out of model-visible data, and require approval for consequential actions. This checklist covers local and remote servers, which have different attack surfaces.
Protocol-specific statements in this article were checked against official Model Context Protocol documentation on 2026-07-23. Client interfaces and server implementations vary, so verify the controls in the exact client, SDK, and deployment you use.
1. Start with a concrete MCP threat model
Draw the system before choosing controls. Include the user, MCP client, model, server process, authorization server, downstream APIs, files, network destinations, and logs. Trace both data and authority across those boundaries.
At minimum, evaluate these failure paths:
- A compromised server: a local package can execute with client privileges, while a remote server can return hostile content or metadata.
- Prompt injection: instructions embedded in data may redirect the model from the user’s goal.
- Excess privilege: a narrow-sounding tool may hold broad filesystem, database, cloud, or administrative access.
- Credential theft: tokens may leak through prompts, results, arguments, URLs, errors, or traces.
- Cross-user or cross-session access: weak session binding can let one user receive another user’s events or invoke tools under another identity.
- Unintended network access: authorization discovery and general-purpose fetch tools can be abused to reach localhost, private networks, or cloud metadata services.
- A legitimate but mistaken request: the model or user can supply the wrong repository, account, recipient, environment, or record identifier.
For every tool, record the worst credible consequence: disclosure, external transmission, modification, deletion, financial action, privilege change, or code execution. The official MCP guidance covers confused-deputy attacks, token passthrough, SSRF, session hijacking, local compromise, unsafe authorization URLs, and excessive scopes, but application-specific threats still need review.
2. Make each tool least-privileged by construction
A tool should expose one business operation, not a general escape hatch. Prefer read_invoice(invoice_id) over run_sql(query), and prefer create_draft(recipient, subject, body) over a tool that can send arbitrary messages immediately. Validate identifiers against resources the authenticated user is allowed to access; possession of a syntactically valid ID is not authorization.
Apply least privilege at each layer:
- Tool surface: register only tools the workflow needs.
- Input schema: prefer bounded fields and enums to free-form commands.
- Authorization: check user, tenant, resource, and operation on every call.
- Credential: request only the scopes needed for the operation.
- Runtime: use a low-privilege account, narrow working directory, and network allowlist.
- Environment: separate development, staging, and production.
For local servers, process isolation matters as much as protocol authorization. Official guidance recommends sandboxing, restricted filesystem and network access, explicit grants for added access, and stdio where appropriate. Do not mount a home directory when the tool needs one project folder.
For remote servers, define scopes around actions and resources. The current authorization specification supports incremental scope requests; a read operation should not silently request write or administrative scope.
Tool annotations can present risk, but the specification says clients must treat them as untrusted unless they come from trusted servers. Enforce policy independently.
Reducing the number and size of exposed tools also lowers model context overhead. The techniques in How to Reduce MCP Token Usage are useful only after security boundaries are correct; token savings should not be achieved by merging unrelated privileged actions into one broad tool.
3. Keep secrets outside prompts and tool results
The model should never see a raw API key, token, password, private key, or session cookie. Inject credentials through an operating-system or managed secret store. Keep retrieval inside trusted code, not a model-constructed tool argument.
For local stdio, the authorization specification says credentials should come from the environment instead of the HTTP authorization flow. Limit inheritance and prevent diagnostics from dumping the environment.
For HTTP, the specification requires bearer tokens in the Authorization header and forbids access tokens in query strings. Servers must validate the intended audience and must not pass unrelated upstream tokens through to another API.
Use separate downstream credentials, prefer short-lived audience-bound tokens, and rotate or revoke them after suspected exposure. Never put secrets in:
- tool descriptions, examples, prompts, or resource contents;
- command-line arguments visible in process listings;
- URLs, including query parameters;
- model-visible error messages;
- source control or reusable client configuration;
- approval screens that do not need the full value.
Test with a recognizable fake secret and confirm it is rejected or redacted before reaching model context, logs, traces, or errors.
4. Treat prompt injection as untrusted data crossing a boundary
Prompt injection is not solved by telling the model to ignore it. Retrieved text can conflict with the user’s request. Treat it as data even when it claims to be a system message, approval, or emergency.
Build controls outside the model:
- Separate retrieved content from trusted policy and label its source.
- Do not let content select credentials, broaden scopes, or disable approval requirements.
- Validate tool arguments against the user’s original task and the server’s authorization policy.
- Constrain file paths to allowed roots after canonicalization; reject traversal and unexpected links.
- Restrict outbound destinations and re-check redirects to reduce SSRF and exfiltration paths.
- Validate and sanitize tool results before returning them to the model.
- Require a fresh authorization decision when a read-only workflow attempts a write.
Prompt injection is especially dangerous when one call can read sensitive data and transmit it. Split those capabilities, use different scopes, and require confirmation before transmission.
Model-produced code needs the same skepticism. Use the review boundaries in How to Review AI-Generated Code before executing changes that affect authentication, authorization, network access, or secret handling.
5. Log enough to investigate without leaking data
An audit trail should explain who requested what, which policy allowed it, what was targeted, and what happened without reproducing every prompt or credential.
Record a timestamp, correlation ID, principal, tenant, server and tool version, authorization decision, scopes, normalized resource identifier, approval, outcome, latency, and stable error category. For high-impact actions, link the event to the downstream change ID.
Redact before data enters the logging pipeline. Do not log authorization headers, cookies, secret environment variables, raw documents, full prompts, unrestricted results, or sensitive query parameters. Limit retention and access.
Distinguish validation failure, authentication failure, insufficient scope, policy denial, user denial, downstream rejection, timeout, and partial completion instead of recording one ambiguous success event.
Logs are evidence, not enforcement. A complete record cannot prevent an overprivileged call. Use deterministic policy checks before execution, following the same principle described in Machine Gates for AI Output.
6. Put approval gates at the point of consequence
The tools specification recommends a human in the loop who can deny invocations, with visible tools, invocation indicators, and confirmation prompts. The protocol does not mandate one interface, so verify the actual client.
Approval should be risk-based. Narrow, reversible reads may run automatically. Require confirmation for deletion, overwrite, external communication, purchases, production or permission changes, credential creation, code execution, and bulk operations.
Use a matrix like this as a starting point, then adjust it for the data, users, and recovery options in your system:
| Operation class | Default decision | Minimum controls |
|---|---|---|
| Bounded, reversible read | May run automatically | Resource-level authorization, output filtering, audit event |
| Sensitive or bulk read | Confirm or apply a separate policy gate | Exact scope and target preview, rate limit, export logging |
| Reversible write in development | Confirm on the first or materially changed call | Argument-bound approval, change ID, rollback path |
| External message or data transfer | Confirm every final destination or batch | Recipient and payload preview, destination allowlist, transmission log |
| Production, permission, credential, purchase, delete, or code-execution action | Require explicit confirmation at the point of consequence | Exact target and effect, short-lived approval, server-side policy, recovery or incident plan |
This is a policy baseline, not a protocol guarantee. A client may present confirmation UI, but the server still has to enforce identity, resource, scope, and operation boundaries.
A useful confirmation shows:
- the exact operation in plain language;
- the account, tenant, environment, and target;
- the fields or files that will leave the boundary;
- the expected side effects and whether they can be reversed;
- the credential or scope class being used, without displaying the secret;
- a clear approve or deny choice.
Bind approval to the tool name, normalized arguments, target, and expiry. Re-ask after material changes. Silence, timeout, approval for another target, and model-generated text are not consent. Server policy must still reject unauthorized actions.
For one-click local installation, official guidance requires consent before startup commands and display of the untruncated command. Review the source, publisher, pinned version, arguments, filesystem access, and network access.
7. Run this preflight checklist before enabling the server
Use this static checklist for each server and repeat it after a new tool, scope, credential, transport, or downstream integration is added.
Trust and inventory
- The server source, package, publisher, and pinned version have been reviewed.
- Every exposed tool has an owner, purpose, input schema, and worst-case consequence.
- Local startup commands and arguments are visible and explicitly approved.
- Tool annotations and tool descriptions are treated as untrusted metadata.
Permissions and isolation
- Unneeded tools are disabled.
- Each tool checks principal, tenant, resource, and action server-side.
- Credentials and OAuth scopes are limited to required operations.
- The process uses a low-privilege account and narrow filesystem roots.
- Network egress is restricted; redirects, localhost, private IP ranges, and cloud metadata endpoints are handled safely.
- Development, staging, and production are separated.
Secrets and authorization
- No secret enters model context, tool arguments, URLs, errors, or logs.
- Remote tokens are validated for issuer, audience, expiry, and required scope.
- The server does not pass client tokens through to downstream APIs.
- Rotation, revocation, and suspected-leak procedures have been tested.
- Sessions are unpredictable, expire, and remain bound to the authenticated user.
Inputs, outputs, and approvals
- Inputs are schema-validated, size-bounded, normalized, and authorized.
- File paths and network destinations are checked after canonicalization or redirects.
- Tool results are validated and sanitized before model use.
- Sensitive actions display exact targets and consequences before execution.
- Approval is bound to the final arguments and cannot be reused after a material change.
- Timeouts, rate limits, cancellation, and safe retry behavior are defined.
Audit and recovery
- Logs identify the principal, tool, target, decision, approval, and outcome without sensitive payloads.
- Alerts cover repeated denials, unusual scope requests, bulk actions, and outbound-destination changes.
- Operators can disable the server, revoke credentials, and identify affected actions quickly.
- Negative tests cover cross-tenant access, traversal, injected instructions, secret leakage, SSRF, and changed arguments after approval.
For a focused test plan, adapt the adversarial cases in How to Test Prompt Injection in RAG Systems. For local process containment, continue with How to Sandbox AI Coding Agents.
Frequently asked questions
Is MCP secure by default?
No deployment is secure merely because it uses MCP. The protocol defines messages and authorization behavior, but the client, server, tool implementation, credentials, runtime isolation, and downstream APIs determine the effective boundary. Review each server as code that can act with real authority.
Is a local stdio MCP server safer than a remote HTTP server?
stdio reduces exposure to unsolicited network clients, but the local server process can still inherit powerful filesystem, environment, and network access. Use a trusted, pinned package plus sandboxing and explicit resource grants. An HTTP server needs transport authorization, token audience validation, session controls, and network protections in addition to tool-level authorization.
Should every MCP tool call require human approval?
Not necessarily. Repeated confirmation for low-risk, bounded reads can train users to approve reflexively. Automate only calls whose targets and effects are narrow, authorized, observable, and reversible; require fresh confirmation when a call writes, transmits, deletes, spends, executes code, changes permissions, or materially differs from what was approved.
Can an MCP server ask for an API key through a prompt or form?
Do not collect passwords, API keys, or other secrets in model-visible prompts or form-mode elicitation. Keep secrets in trusted storage. When an interactive third-party login is required, URL mode can move the credential entry to an external browser flow, but the client must still identify the requesting server and handle the URL safely.
How should an MCP security review be tested?
Start with negative tests, not only successful calls. Attempt cross-user resource IDs, path traversal, redirects to private addresses, oversized inputs, injected instructions, expired or wrong-audience tokens, changed arguments after approval, cancellation, retries, and partial downstream failure. Confirm that policy blocks the action before execution and that logs preserve useful metadata without the sensitive payload.
Official primary sources
Checked on 2026-07-23:
- MCP Security Best Practices: official attack classes and mitigations for token passthrough, SSRF, sessions, local servers, authorization URLs, and scope minimization.
- MCP Authorization specification, 2025-11-25 revision: HTTP authorization, token audience validation, protected-resource discovery, scope handling, and token transport requirements.
- MCP Tools specification, 2025-11-25 revision: human-in-the-loop guidance, untrusted annotations, input and output validation, access control, confirmation, timeouts, and audit logging.
Added and checked on 2026-07-24:
- MCP Transports specification, 2025-11-25 revision: Origin validation, protocol-version headers, session identifiers, session expiry behavior, and explicit HTTP session termination.
- MCP Elicitation specification, 2025-11-25 revision: identity binding, user approval, safe URL handling, and the prohibition on requesting passwords or API keys through form mode.