MCP security checklist for enterprises

An MCP security checklist for enterprises confirms five things: which servers an agent can load, which server identities are allowed, which tokens and scopes a server holds, which tools need a person’s approval, and whether each call is logged. Each item has one owner, either the client policy or the MCP server.
Who enforces each control
Each checklist area has a primary owner, shown below. The items that follow say what to check in each.
| Checklist area | Owner | Items |
|---|---|---|
| Server approval and identity | Managed settings and your approved list | 1 to 7 |
| Token audience and scopes | Authorization server and MCP server | 8 to 10 |
| Per-tool rules and prompts | Client permission rules | 11 to 12 |
| Server controls and call record | MCP server and client telemetry | 16 to 18 |
The checklist
Work through these in order. Each item says what to verify, so an engineer can confirm it on a managed machine without relying on a vendor claim.
Inventory and approval
- Inventory every server a session can load. Reviewing a project’s
.mcp.jsonis not enough. Claude Code’s security page notes that servers at other scopes, claude.ai connectors and plugins also add servers. The post on finding MCP servers covers searching codebases and clusters for the ones you missed. - Don’t let a repository approve its own servers. A cloned repository can’t approve its own servers. The MCP docs say that
enableAllProjectMcpServersorenabledMcpjsonServerscommitted to.claude/settings.jsonis ignored in an untrusted folder. Confirm that each developer has accepted the workspace trust dialog before you rely on project approvals. - Write the approved list with exact values. For each server, record the owner, the purpose, the exact URL or the command with its arguments, and the tools it exposes. A name in a list is not enough, as the next section shows.
- Decide who may add servers, and write it down. Whether developers can add their own servers is a policy choice. Managed settings enforce it, and the per-tool and allowlist post walks through the file formats. Make the decision before rollout so the approved list and the policy agree.
Server identity and transport
- Allow by URL or command, never by name. A name is a label each user assigns, not a control. The allowlist post covers the URL and command match rules.
- Check the port and scheme in each URL pattern. A
*in the hostname matches every port, so the allowlist post is the place to check the pattern before you approve a server. - Mind the
envblock. Command entries do not compare it, so define a server inmanaged-mcp.jsonif you must control its environment values. The allowlist post lists the other match gaps.
Authentication and tokens
- Require PKCE with S256 on remote OAuth servers. The MCP authorization specification says clients MUST use the
S256code challenge method when technically capable. It also says clients MUST refuse to proceed if the authorization server metadata lackscode_challenge_methods_supported. All authorization server endpoints MUST be served over HTTPS. - Pin the scopes. Set
oauth.scopesto the subset your security team approved. The docs say it takes precedence over scopes the server advertises, so a server that advertises more gets only what you pin.
{
"mcpServers": {
"slack": {
"type": "http",
"url": "https://mcp.slack.com/mcp",
"oauth": {
"scopes": "channels:read chat:write search:read"
}
}
}
}
If a tool call returns 403 insufficient_scope, the docs say the call fails and the server shows as needing sign-in. A new scope is granted only after you add it to the pinned list and authenticate again.
10. Check that servers accept only their own tokens. The authorization specification says an MCP server MUST accept only tokens issued specifically for itself, MUST reject tokens that do not name it in the audience, and MUST NOT pass through the token it received from the client. Test this with a token issued for another resource. The call should be refused.
Tool-level control
- Block unreviewed MCP tools, and keep the block narrow enough to allow reviewed ones. In Claude Code a deny rule is checked before any allow rule, whatever the rule’s specificity. A blanket deny of
mcp__*therefore cannot be reopened by adding allow rules beneath it. Narrow it to the servers or tools you have not approved, then add allow rules for the named tools you have reviewed. The tool-level authorization post covers rule syntax and the order in which rules are checked.
{
"permissions": {
"deny": [
"mcp__*"
]
}
}
- Mark consent tools in the server. If you build a server, a tool whose prompt is itself the consent step can be marked so that Claude Code prompts on every call. The docs say this applies even in
acceptEdits,autoandbypassPermissionsmodes, and that allow rules don’t skip the prompt. Use it only for tools where auto-approval would mean no person agreed.
{
"name": "grant_access",
"description": "Requests access to a protected resource",
"_meta": {
"anthropic/requiresUserInteraction": true
}
}
Human in the loop
- Put a person in front of sensitive calls. The tools specification says there SHOULD always be a human in the loop with the ability to deny tool invocations. Applications SHOULD make clear which tools are exposed to the model, show a visible indicator when a tool runs, and show tool inputs to the user before calling the server.
- Treat tool annotations as untrusted. The specification says clients MUST consider tool annotations to be untrusted unless they come from trusted servers. Do not write allow rules from annotations alone. Read them as information about a tool, then decide the rule yourself.
- Re-review when the tool list changes. A server that declares the
listChangedcapability SHOULD send a notification when its tools change. Treat each notification as a change that needs review, because a new tool can widen what an agent may do without any change on your side.
{
"jsonrpc": "2.0",
"method": "notifications/tools/list_changed"
}
Server-side controls
- Check the server’s own controls. The tools specification requires servers to validate inputs, enforce access control, rate limit and sanitize outputs. Write the access check per tool, not only per server. The tool-level authorization post maps which checks sit with the server.
- Set timeouts and validate results. The Claude Code MCP docs say the per-server
timeoutis a hard wall-clock limit for each tool call, and progress notifications do not extend it. The idle window defaults to five minutes for HTTP, SSE and WebSocket servers. The tools specification says clients SHOULD validate tool results before passing them to the model and SHOULD implement timeouts.
Logging and rollout
- Log every call, including denials. The tools specification says clients SHOULD log tool usage for audit purposes. The audit post covers the OpenTelemetry setup. The Claude Code monitoring docs describe
OTEL_LOG_TOOL_DETAILS, which is off by default. Set it to1to add MCP server and tool names and tool input to tool events. - Tell users before a block lands. The managed MCP docs say a server that becomes blocked by policy disappears from
/mcpandclaude mcp listwith no signal that policy is the reason. Name the blocked servers to affected users when you roll out a new restriction.
Gaps the docs leave open
- Upgrades can start servers without notice. The allowlist post covers the gap where an upgrade starts loading a server that an allowlist was blocking.
- Reviewing one file is not an inventory. Project files, user settings, plugins and connectors each add servers, so the approved list must cover all of them.
- The specification leaves the access design open. It requires servers to implement access controls but does not say how. Your per-tool design is yours to write and test.
Sources checked on 2026-11-13: the MCP tools specification and authorization specification, and the Claude Code permissions, MCP, managed MCP, monitoring and security docs.
Next steps
Start with the inventory and the approved list, then set identity and scope rules, then turn on logging. For the individual controls, read how to restrict which MCP servers Claude Code can use, how to set tool-level authorization and how to audit MCP tool calls. The pillar page for this topic is MCP security. If your sign-in runs through an identity provider, read cross-app access for MCP for how the client and the provider share it.
Frequently asked questions
What should an enterprise check before it approves an MCP server?
Confirm who publishes the server and which tools it exposes, and record the exact URL or command you approve. Check which credentials it needs and whether it uses OAuth with scopes you can pin. Then add an allowlist entry and name an owner. A display name is not enough, because any user can label a server with any name.
Does the MCP specification require servers to enforce access control?
Yes. The tools specification says servers MUST validate all tool inputs, implement proper access controls, rate limit tool invocations and sanitize tool outputs. Clients should prompt for sensitive operations and log tool usage. Those server duties sit alongside client-side checks, not in place of them.
Can an MCP server pass a client's OAuth token on to another API?
No. The MCP authorization specification says an MCP server MUST NOT pass through the token it received from the MCP client. It must accept only tokens issued for itself. If it calls an upstream API, that API issues a separate token. Clients must send the resource parameter from RFC 8707 so tokens are bound to the server they are meant for.
Does a repository's .mcp.json show every MCP server a session can load?
No. Claude Code's security docs say servers at other scopes, claude.ai connectors and plugins also add servers, so a file review misses them. A cloned repository also cannot approve its own servers. Project approvals count only after the developer accepts the workspace trust dialog, so confirm trust before you rely on them.