Virtual Desktop Brief

Building an MCP Server for Internal Enterprise Tools

Streamable HTTP and OAuth 2.1 are non-negotiable for enterprise MCP deployments.

Staff Writer · · 10 min read
Cover illustration for “Building an MCP Server for Internal Enterprise Tools”
Protocols & Integration · September 23, 2026 · 10 min read · 2,282 words

What MCP is and where it stands as a standard today

Anthropic introduced MCP in November 2024, pitching it with a comparison that stuck: USB-C for AI. Just as a USB-C port lets any peripheral talk to any laptop without a custom cable, MCP gives any AI application a consistent way to talk to any external tool or data source. A standardized JSON-RPC interface defines the shape every request and response must take, and that is what does the actual work behind that comparison. Servers expose three kinds of capabilities, Tools, Resources, and Prompts, and clients discover what's on offer through a handshake instead of hardcoded, tool-specific logic baked into the application itself.

The protocol solved a real math problem before it solved anything else. Multiply the AI tools in play by the internal systems they need to reach, and the number of separate integrations explodes, each with its own auth flow, its own data format, its own error handling. Claude talks to Postgres one way, ChatGPT talks to Jira another way, Cursor talks to GitHub a third way, and none of that code carries to the next pairing. Teams working through this N×M problem report spending more than a quarter of their implementation time on data integration alone, time that never touches what the AI is actually supposed to do. Point-to-point connections at that scale don't just cost more, they become ungovernable: when every connector is bespoke, there's no single place to enforce a permission policy or produce an audit trail.

MCP fixes the math. It does not tell you who's allowed to call a given tool, what that caller can see, or how a compliance team reconstructs what happened six months later. That part belongs to whoever builds the server, and most teams shipping MCP infrastructure right now treat it as an afterthought rather than the actual architecture. A server that passes a demo and a server that passes a security review are not the same project, and confusing the two is the mistake this piece is written to correct.

The protocol's governance took a real step on December 9, 2025, when Anthropic handed MCP over to the newly formed Agentic AI Foundation (AAIF), housed under the Linux Foundation. Anthropic, Block, and OpenAI co-founded the effort, with Platinum members including AWS, Bloomberg, Cloudflare, Google, and Microsoft alongside the founders. The foundation launched with more than 150 member organizations, reportedly the fastest-growing foundation in Linux Foundation history, and it carries three anchor projects: MCP itself, Block's open-source agent framework goose, and AGENTS.md. Mike Krieger, who co-leads Anthropic Labs after serving as the company's CPO, said the donation "ensures it stays open, neutral, and community-driven as it becomes critical infrastructure for AI." The adoption curve backs that up: SDK downloads went from roughly 100,000 in the protocol's first month to 97 million monthly downloads by March 2026, a jump of many multiples across eighteen months.

The three MCP primitives and why their boundaries matter for enterprise tool design

MCP defines exactly three kinds of things a server can expose, and each one carries a different control model. Get that distinction wrong, and every permission decision downstream inherits the mistake.

Tools are executable functions, and they're model-controlled: the AI agent itself decides when to call one and with what arguments. Querying a database, kicking off a workflow, hitting an external API, all Tools. Resources are file-like data objects the agent reads for context, but control over them sits with the application, not the model on a whim. An API response, a document, a row pulled from a table, all Resources. Prompts are predefined templates that steer the model's behavior, and control over those sits with the user.

Tools represent action. Resources represent context. Prompts represent instruction. Collapse those categories, treat a Tool like a Resource, or let a Prompt template quietly trigger a Tool call, and the permission model built on top of them stops making sense. An agent that can "read" something it should only be allowed to view now has an implicit path to act on it. That's the exact mechanism by which unpredictable agent behavior sneaks into a system that looked correct on a whiteboard.

Choosing a transport layer: why stdio is a dead end for any multi-client deployment

MCP supports two transports in active use, and they are not interchangeable choices, one of them simply does not belong in production. stdio runs the MCP server as a local subprocess, passing JSON-RPC messages over stdin and stdout. It works fine when one client launches one local server on the same machine, the pattern behind tools like Cursor. Streamable HTTP, introduced in the March 2025 spec (2025-03-26) and refined in the July 2026 spec (2026-07-28) to drop any requirement for long-lived session affinity, is the transport built for servers that live remotely and get called by more than one client.

An earlier transport, HTTP+SSE, was deprecated outright in that same 2025-03-26 spec. Nobody building a new server today has a reason to reach for it.

stdio's failure mode at scale is a wall, not a slope. It's local-only and single-client by design, and production testing has shown it collapsing once concurrency rises even a little: 20 of 22 requests failed under just 20 simultaneous connections. That's not degraded performance; it's the architecture being asked to do something it was never built to do.

Streamable HTTP was built for the opposite case, and it's the only defensible choice for anything remote-facing. It runs fully stateless, so multiple instances sit behind a load balancer with no sticky sessions and no connection affinity required. When traffic spikes, more instances go up, and there's no session state to migrate between them. That statelessness is what makes multi-tenant deployment realistic at all, the kind where ChatGPT, Claude, Microsoft Copilot, and a company's own internal agents are all calling into the same server layer at once.

Diagram: stdio vs. Streamable HTTP: A Tale of Two Failure Modes. Visualizes: Contrast the two MCP transports on the single dimension that matters most for production decisions — concurrency tolerance.

Authentication architecture: OAuth 2.1 as a requirement, not an option

Diagram: MCP Authentication Reality: Where Public Servers Actually Stand. Visualizes: Show the breakdown of authentication implementations found in a 2026 security audit of public MCP servers: 25% had no authentication, 53% relied on long-lived…

A 2026 security audit of public MCP servers found that 25% had no authentication. Another 53% relied on long-lived static API keys or Personal Access Tokens. Only 8.5% had implemented OAuth 2.1, despite it being the mandatory standard for any remote deployment under the spec. Read together, those numbers show that most MCP servers running in public today are not compliant with their own protocol's authentication requirements, and that noncompliance is the actual attack surface, not some hypothetical future risk.

The spec is specific about what remote servers need: OAuth 2.1 with PKCE, Dynamic Client Registration under RFC 7591, and Authorization Server Metadata under the relevant IETF specification. Static API keys still show up constantly in local-server contexts, particularly in AI-first IDEs like Cursor, and that's fine when the server never leaves a developer's laptop. A static key sitting in a remote-facing MCP server is a different animal entirely: a liability with no expiration date. If that key leaks, whoever has it gets in and stays in until someone notices and rotates it by hand, and there's no telling how long that takes in practice.

The spec's real architectural insight is the split it forces between two roles. The MCP server acts purely as an OAuth 2.1 resource server: it validates tokens and enforces scopes, full stop. A separate authorization server handles login, consent, and token issuance. That separation keeps identity logic out of the MCP server's codebase entirely, so an organization's existing identity provider, whichever one it already runs, stays the single source of truth for who a user is. The server's only job is checking a token's validity and its permissions.

Designing OAuth scopes that match the actual risk surface of each tool

Scope design comes down to one principle: request access by resource, by verb, and by sensitivity level. Anything looser than that is a shortcut that costs more later than it saves now.

An MCP server facing a CRM could expose one scope, crm:access, and call it done. That's the wrong pattern, because it means anyone who can read a customer record can also delete one. The right pattern splits that single scope into five: customers:read, customers:write, customers:delete, customers:export, customers:merge. Each scope maps to exactly one tool, and single-responsibility tool design isn't a style preference here, it's a prerequisite. A tool that does five things under one name can't be scoped five different ways, no matter how carefully the permissions get written afterward.

Fine-grained scoping is slower going in. Procurement review takes longer, and installation involves more upfront decisions about which scopes a given integration actually needs. That cost gets paid back on the other side, though: once the pattern is established, security and compliance review moves faster, because the reviewer isn't being asked to trust a black box. They're being asked to approve five narrow, named permissions, and that's a far easier yes than approving one that grants everything.

Scopes should tie to organizational roles, not to individual users. A support engineer's role grants customers:read; a support manager's role adds customers:export. None of that logic should live inside the MCP server itself. It gets inherited through the org's existing HR and identity provider workflows, so access is granted and revoked automatically as someone changes roles or leaves, instead of depending on someone remembering to walk back a one-off grant.

Audit logging requirements and what a compliance-ready log entry contains

Audit logging has to be decided at design time, not bolted on after a vendor risk review flags it. Retrofitting log schemas and correlation IDs across tool calls scattered through distributed services after the fact is expensive and slow.

A compliance-ready log entry for an MCP tool call needs a unique request identifier for correlating that call across distributed traces, the agent's identity drawn from the OAuth subject claim, the tool's name and version, and the input parameters, sanitized so no raw credential values or PII end up sitting in a log file, regardless of whether someone remembers to strip them by hand. It also needs the scopes present in the token at call time, a response status and a summary of the result rather than the full payload (since that payload might be large or might itself hold sensitive data), a timestamp, the call's latency, and which upstream system got hit.

A distributed tracing layer is the natural instrumentation approach for this. Every tool invocation gets logged against a unique request ID, threading a line from the original AI prompt through to whatever downstream service actually got called. From there, logs need configurable retention and export into whatever monitoring platform the organization already runs: Prometheus, Datadog, or an equivalent SIEM. Compliance teams in regulated industries won't accept a system that can't produce this trail on demand, and there's no version of that requirement that gets negotiated away.

Multi-tenancy, namespace isolation, and scaling the server under real enterprise load

Namespaces, sometimes called server groups, let a single MCP deployment partition workloads by environment (dev, staging, production) or by business unit, without standing up a separate server cluster for each one. That's a real cost difference at the infrastructure level, and it's also where a lot of the governance work actually gets enforced.

Isolation here is not a tidiness concern: it separates a contained incident from a genuine breach. A finance team's Snowflake tools and a support team's Salesforce tools shouldn't share a scope namespace, and they shouldn't share a session context either. OWASP's MCP Top 10 project names context over-sharing across shared agent sessions as its own threat category, and the reasoning is straightforward: an agent session that bleeds context between two business units can leak information nobody meant to expose, without any single tool call ever looking suspicious on its own.

Containerized microservices behind a load balancer are the deployment pattern that supports this cleanly. Streamable HTTP's stateless mode is what makes it workable, since instances can be added or removed without any session migration logic running first. Given that stdio fell over at just 20 simultaneous connections in testing, any production Streamable HTTP deployment needs load-testing well past the expected concurrent agent count before it goes anywhere near a go-live date. That margin isn't a formality, it's the only way to find the actual ceiling before real traffic finds it instead.

The threat categories that specifically target MCP servers

OWASP has moved fast enough that MCP now has its own formalized threat categories, distinct from the standard web API security checklist. That distinction is not academic: an MCP server is a system where an AI model decides which calls to make, and that changes what an attacker actually needs to compromise it. A fixed API surface can be tested and locked down. A model deciding in real time which tool to call, with what arguments, based on a prompt it received seconds earlier, cannot be locked down the same way.

Context over-sharing across sessions, covered above, is one entry on that list. Scope creep, where a tool ends up with broader permissions than any of its actual call sites need, is another, and it's the clearest argument for the per-verb scoping pattern covered earlier, not as a tidiness exercise but as damage control. A permission model built from five narrow scopes has a far smaller blast radius than one wide crm:access grant, if a token gets compromised or a tool gets tricked into calling something it shouldn't.

None of these threats are exotic taken one at a time. What makes them distinct to MCP is that the real target is the agent's decision-making process rather than the API surface behind it, and any threat model that only accounts for the API surface is going to miss the part of the system actually making the calls.

Sources

  1. What is the Model Context Protocol (MCP)? - Model Context Protocol
  2. The 2026-07-28 MCP Specification Release Candidate
  3. apigene.ai
  4. baeseokjae.github.io
  5. modelcontextprotocol.io

More in Protocols & Integration