Security

MCP for Internal Tools: Permissions, Scopes, and Boring Success Criteria

MCP for Internal Tools: Permissions, Scopes, and Boring Success Criteria

Let’s get one thing straight: the Model Context Protocol (MCP) is not a security boundary. It never was.

When we first started wiring MCP into our internal developer workflows, the instinct was to treat it like a standard API gateway. We connected our LLMs to internal databases, document stores, and CI/CD pipelines, assuming that because the connection lived inside our network, the risk was manageable. That was a mistake.

MCP is an open standard for communication, not security. By default, any connected LLM has access to everything the server can touch. There is no built-in authentication or authorization layer in the protocol itself. If you rely on the protocol to keep your data safe, you are relying on a pipe, not a vault.

The “boring” success criterion for any internal MCP implementation is simple: If a token leaks, can the attacker do anything other than what was explicitly intended? If the answer is “yes, they can do everything,” you have failed.

The Boring Truth: MCP is a Pipe, Not a Vault

The core issue with MCP is that it lacks a native identity layer. When an MCP client connects to a server, it establishes a channel. Without external gatekeepers, that channel is wide open. As noted by PropelAuth, this means any rogue prompt or compromised model can access sensitive data like payroll records or production databases if the server isn’t locked down.

This creates an “all-or-nothing” risk. Cerbos highlights that without scopes, MCP access is binary: you either have access to the entire toolset or none of it. This is dangerous because a single compromise jeopardizes the entire toolset. If a developer’s API key is leaked, or if a prompt injection succeeds, the attacker doesn’t just get access to one function; they get access to the entire surface area defined by that server.

We see this play out in production failure modes constantly. Maxim AI identifies three specific ways this breaks:
1. Prompt injection escalation: Malicious instructions in documents coerce models into calling dangerous tools.
2. Cross-contamination: Customer-facing agents inadvertently reach internal tools because the scoping is too broad.
3. Credential blast radius: A single leaked key exposes the full tool surface.

The solution isn’t to abandon MCP. It’s to accept that security must be implemented around the protocol, not within it.

Why Broad Scopes Fail in Production

The most common mistake we see teams make is granting server-level access. They create an MCP server that exposes “Database Access” or “HR Tools” and give every user who connects to that server the same broad permissions. This is a failure of granularity.

The “All-or-Nothing” Trap

When you use coarse-grained access tokens, you create a massive blast radius. If a token is compromised, the attacker has the keys to the kingdom. Cerbos argues strongly for denying access by default and enabling tools selectively based on role and context. This means starting with no tools enabled and only granting access to specific functions as needed.

Friction vs. Security

There is also a user experience cost to broad scopes. Supabase developers have reported significant friction when their MCP clients request broad account-level OAuth permissions for simple tasks, such as reading documentation. Users are becoming more aware of what they are granting. If an agent needs to read a single document, asking for “full database write access” is not just a security risk; it’s a trust violation. Users will reject these permissions, or worse, grant them blindly, creating a false sense of security.

The Blast Radius of Leaked Keys

In our experience, API keys end up in logs, commit histories, and shared Slack channels. If a key grants broad access, a leaked key is a critical incident. Checkmarx advises separating read, write, delete, and execute capabilities into narrow, operation-specific tools. This reduces the blast radius significantly. If a key is leaked, it should only allow the specific action it was intended for, not the entire server.

Implementing Tool-Level Permissions (RBAC)

To secure internal MCP servers, we need to move from server-level scoping to tool-level permissions. This is essentially Role-Based Access Control (RBAC) for AI agents.

Define One Scope Per Tool

Descope recommends defining one scope per MCP tool or tool group. For example, instead of a single mcp:database scope, you should have mcp:invoice.create, mcp:invoice.read, and mcp:employee.lookup. This enables fine-grained authorization and provides clear user consent. When a user grants permission, they know exactly what is being accessed.

Separate Capabilities

Checkmarx emphasizes that read-only tools should not have write permissions. We should separate capabilities into narrow, operation-specific tools. This means your MCP server should expose distinct endpoints for reading data and writing data. An agent that only needs to query a database should never have the ability to modify it.

Deny by Default

Start with no tools enabled. Then, selectively enable them based on the user’s role. If a user is a developer, they might need mcp:code.deploy. If they are a manager, they might only need mcp:report.read. This principle of least privilege is non-negotiable for internal tools.

Use Virtual Keys or Scoped Tokens

Maxim AI proposes using virtual keys or scoped tokens that enforce permissions at the gateway level. This means the MCP server itself doesn’t need to handle complex authentication logic. Instead, a gateway validates the token’s scopes before passing the request to the MCP server. This keeps the MCP server simple and the security logic centralized.

Mitigating Prompt Injection and Credential Leaks

Even with perfect scoping, prompt injection remains a threat. If a malicious document instructs an agent to “delete all records,” and the agent has the mcp:records.delete scope, it will do it. Scoping doesn’t prevent the injection; it limits the damage.

Narrow Scopes Limit Damage

Cerbos notes that even if an injection succeeds, the agent can only perform actions within its specific scope. If the agent only has mcp:report.read scope, it cannot delete records. This is the primary defense against prompt injection escalation. By limiting the scope, you ensure that even a compromised model cannot perform actions outside its intended purpose.

Short-Lived Tokens

Checkmarx advises using short-lived tokens and resource-specific credentials. This reduces the window of exposure. If a token is leaked, it expires quickly, limiting the time an attacker has to exploit it. This is particularly important for internal tools where access is frequent and ongoing.

Explicit User Consent

Descope highlights that explicit user consent for tool invocation helps users understand the scope of access. When an agent requests a new tool, the user should be prompted to approve it. This creates an audit trail and ensures that users are aware of what the agent is doing. It also allows users to revoke access if they notice unusual behavior.

Boring Success Criteria for Your Team

When evaluating your internal MCP implementation, use these concrete questions to determine if you have achieved “boring success.” These are not theoretical; they are practical checks you can run today.

  1. Can an AI agent read a document without having database write access?
    If the answer is no, your scopes are too broad. Separate read and write permissions.

  2. If a developer’s API key is leaked, is it scoped to only the tools they need?
    If the key grants access to all tools, you have a critical vulnerability. Implement virtual keys with narrow scopes.

  3. Does the OAuth flow request only the minimum scopes required for the active task?
    If the flow requests broad permissions for simple tasks, you are creating friction and risk. Calculate minimum scopes based on active tools, as advocated by the Supabase community.

  4. Are internal tools completely invisible to customer-facing MCP clients?
    If customer-facing agents can reach internal tools, you have a cross-contamination risk. Use server-level scoping to isolate internal and external tools, and enforce tool-level permissions within each server.

Conclusion

Securing internal MCP servers is not about building a complex security architecture. It is about applying basic security principles: least privilege, separation of duties, and defense in depth.

MCP is a powerful tool for connecting AI to your internal systems. But it is a pipe, not a vault. If you treat it as such, you will avoid the common pitfalls of broad scopes, prompt injection escalation, and credential blast radius.

The goal is boring success. You want your MCP implementation to be so secure and well-scoped that no one notices it. If you are constantly worrying about permissions, you have failed. If your team can focus on building value rather than managing risk, you have succeeded.

Sources and further reading

Keep exploring

Find more practical writing from the RodyTech archive.

RodyTech publishes practical writing on AI systems, infrastructure, and software that teams can actually ship. Use the archive paths below to keep reading by topic or browse the full library.

  • Browse the full archive by publication date and topic
  • Hands-on notes from real builds, deployments, and ops work
  • Category paths for AI, infrastructure, developer tools, and security
Browse all articles More in Security Visit the main RodyTech site

Rody

Founder & CEO · RodyTech LLC

Founder of RodyTech LLC in Iowa. I write practical notes on automation, infrastructure, security, and software decisions for builders and business operators.

Next step

Turn one article into a working reading loop.

Keep the context warm: revisit the archive or stay inside the same topic while the thread is still fresh.

Explore the archive More Security
Keep reading
Cloudflare Workers AI vs Traditional APIs: The Builder’s Deployment Tradeoff Beyond the Demo: Latency, Interruptions, and Fallbacks in Voice AI

No comments yet

Leave a comment

Your email address will not be published. Required fields are marked *