Topic: mcp server authentication
MCP Server Authentication — Securing Tokens, Scopes, and Transport
Authentication in MCP servers has two distinct security surfaces: how the server authenticates to downstream APIs it calls on behalf of the user, and how the server exposes that authentication to the LLM's tool call arguments. The 101-server public corpus shows authentication failures on both surfaces — in 38% of repos for credential handling and in 43% for permission scope. This page covers the secure patterns for both.
Server-to-API authentication: the env var injection pattern
The standard pattern for MCP server authentication is environment variable injection: the operator or user sets a token (OPENAI_API_KEY, GITHUB_TOKEN, STRIPE_SECRET_KEY) in the shell environment, and the server reads it at startup via process.env or os.environ. This is correct. The failure modes are downstream of this step:
- The token is read at startup but echoed into a return value. A tool handler that includes the token in an error message, a log line returned to the model, or a debugging field in the response exposes it to the LLM's context. See the credential exposure page for detection patterns.
- The token is read per-call from args rather than from env. Some servers pass credentials as tool arguments so the user can supply different tokens for different calls. This is not inherently insecure, but it requires the tool description to document clearly that the argument contains a sensitive value, and the handler must never log it.
- The token is initialized with a scope that covers more than any single tool needs. This is the Permissions axis finding — covered below.
OAuth scope minimization
The most consequential authentication security issue in the corpus is not credential theft but over-scoped credentials. Several F-grade corpus servers — particularly those wrapping cloud platform APIs (Heroku, AWS, Azure, Auth0, MongoDB Atlas) — initialize a single API client with org-admin or account-owner scope and pass that client to all tool handlers. The blast radius of any SSRF, command-exec, or prompt-injection finding in any handler is then the full scope of that one credential.
The principle: provision the minimum token scope required by the most-restricted tool in the server. If one tool needs read-only access and another needs write access, consider whether they should be in separate servers with separately scoped tokens, or whether the write-capable tool should require the caller to supply the write token as an argument (which the model can source from a user-owned credential, not a server-stored one).
Concrete scope minimization by platform:
- GitHub: Create a fine-grained personal access token scoped to the specific repo and the specific permissions (e.g.,
contents:readfor a read-only code search tool, notrepo:*). - AWS: Create an IAM role with a policy attached to only the specific actions and resources the tools need. Pass the role ARN to the SDK rather than root credentials.
- Stripe: Use a restricted key with only the permissions your tools need (e.g.,
charges:readfor a read-only charge lookup tool, not a secret key with full write access). - OAuth generally: Request only the scopes listed in the tool's permission declaration. If your
mcp_config.jsondeclarescalendars:readand your handler also fires a write call when an edge case triggers, that's a scope declaration mismatch — fix the handler, not the declaration.
Transport security
Most MCP servers run over stdio transport — the server process is spawned by the client, and the communication happens over the process's stdin/stdout. This transport is not network-exposed and does not require TLS. The authentication concern here is startup-time: the server must validate that the env vars it expects are present before registering any tools, so that a tool call cannot arrive before credentials are initialized.
For HTTP-transport MCP servers (used for remote or multi-user deployments), transport security follows standard web API practices: TLS required, no credential transmission in query strings, token validation on every request not just at session start. The SkillAudit engine flags HTTP-transport servers that do not enforce HTTPS on all tool call paths.
What the SkillAudit Credentials and Permissions axes check
The SkillAudit rubric evaluates authentication security across two axes:
- Credentials axis — checks for env var echo (process.env values flowing to return statements), hardcoded tokens in source or git history, and tokens appearing in log calls that return to the model.
- Permissions axis — checks whether the declared permission scopes in
mcp_config.jsonmatch the actual API calls made by handlers, and whether the OAuth token provisioning pattern suggests minimum-scope discipline.
A server that handles authentication correctly across both axes earns a clean score on these two of six axes, contributing meaningfully to its overall grade. Authentication failures on either axis typically produce a D or F overall regardless of Security and Maintenance scores — team leads gate adoption on Credentials and Permissions first because the blast radius is proportional to the credential scope.