Inside Uber's MCP Gateway: 800 Servers, 5,000 Tools, One Control Plane

·11 min read·Evergreen Tools Team

On October 1, 2026, Uber's engineering team published the design of its MCP Gateway. By its own account, the service now hosts more than 800 MCP servers and over 5,000 tools, and is the foundational microservice powering all MCP interactions at Uber, acting as an orchestration and routing layer between AI agents and existing back-end services and native MCP servers. The value of this story is not the protocol itself but how a large organisation collapses discovery, governance, authorization and operations into a single gateway once MCP graduates from a few teams' experiment to hundreds of teams' infrastructure. Here is the control plane, the data plane, the discovery engine and the security model, plus a design checklist you can copy.

1. An operations problem first, a protocol problem second

Uber's starting point was an operations problem. Per the blog, early ad-hoc integrations with MCP proved their value: agents became dramatically more capable when they could access live business context, query internal services, and take meaningful actions on behalf of users. But as adoption accelerated, significant challenges emerged. Individual teams independently built integrations, leading to fragmentation in tooling and duplicated infrastructure; MCP tools were hard to discover and difficult to operate reliably, and were tightly coupled to specific services or agent implementations. Those approaches worked at small scale, but as hundreds of teams began exploring agentic workflows, scaling MCP without a unified architecture would increase operational complexity, security risk and developer friction.

# Uber's starting point is an operations problem, not a protocol problem.
# Early MCP integrations were powerful but ad hoc: individual teams built
# their own integrations, so tooling fragmented and infrastructure was
# duplicated. That works until hundreds of teams join in.

EARLY_MCP_PAIN = {
    "tooling": "fragmented across teams",
    "infrastructure": "duplicated",
    "discoverability": "tools were hard to find",
    "coupling": "MCP tied to specific services or agent implementations",
    "root_cause": "growth without a unified architecture",
    "risk": "operational complexity, security risk, developer friction",
}
Centralising MCP logic into one gateway

Fragmentation is an operations problem, not a protocol one

2. The skeleton: a control plane and a data plane

The answer is to centralise MCP logic into a single foundational microservice, split cleanly in two. The control plane is the MCP Registry, which maintains a catalog of hundreds of MCP servers backed by internal services, along with thousands of MCP tools. It is the single source of truth for discovery, ownership and enablement, and its tools range from no-code definitions that expose existing APIs up to fully native implementations built against the MCP specification. The data plane is the Proxy Gateway, which executes MCP requests at runtime: it translates MCP protocol calls into HTTP, gRPC or TChannel requests, forwards them to the appropriate back-end service, and converts the responses back into MCP-compatible results. Agents get one consistent MCP interface; the underlying services need no changes at all.

# The fix is a single microservice with two halves. Uber splits the
# system cleanly into a control plane and a data plane.

MCP_GATEWAY = {
    "MCP Registry": {
        "role": "control plane",
        "holds": ["hundreds of MCP servers", "thousands of MCP tools"],
        "is": "single source of truth for discovery, ownership, enablement",
        "tool_kinds": ["no-code API-as-tool", "fully native MCP servers"],
    },
    "Proxy Gateway": {
        "role": "data plane",
        "job": "execute MCP requests at runtime",
        "does": ["translate MCP calls to HTTP/gRPC/TChannel",
                 "forward to the right back-end",
                 "convert responses back to MCP-compatible results"],
    },
}

# The design goal: agents talk one consistent MCP interface, while the
# underlying services stay exactly as they are.

3. AutoCrawler: making discovery happen automatically

Asking teams to hand-author an MCP server would be slow and painful, so Uber built AutoCrawler. Per the blog, it is a Cadence-powered distributed workflow system subscribed to Uber's IDL registry and internal service signals; on a fixed schedule, a cron job triggers a Cadence workflow that scans for newly added services, APIs and schema changes. For services defined through Protobuf or Thrift IDLs, AutoCrawler upserts a virtual MCP server, parses the IDL for method names, request and response schemas, and documentation comments, uses an LLM to generate rich, agent-friendly tool descriptions, translates the schemas into MCP-compatible JSON-RPC 2.0 schemas, and registers the tools in a disabled-by-default state. For native MCP servers, it monitors heartbeat metrics, calls listTools to retrieve the tools they explicitly expose, and creates a virtual proxy MCP server in the registry, again disabled by default.

# AutoCrawler is the discovery engine that keeps service teams off the
# critical path. It is a Cadence-powered distributed workflow subscribed
# to Uber's IDL registry and internal service signals.

AUTOCRAWLER_STEPS = [
    "Upsert an MCP server for the discovered service/API group",
    "Parse protobuf/Thrift IDLs for methods, schemas and comments",
    "Use an LLM to generate agent-friendly tool descriptions",
    "Translate protobuf/Thrift schemas into MCP JSON-RPC 2.0 schemas",
    "Register tools in the registry DISABLED BY DEFAULT",
]

def discover_native_servers(heartbeat_metrics):
    """Native MCP servers announce themselves; AutoCrawler watches."""
    for server in heartbeat_metrics:
        tools = server.listTools()          # explicit tool + schema export
        yield {"proxy_server": server.name, "tools": tools, "state": "disabled"}
Automatic discovery and disabled by default

Discovery does not imply exposure: owners keep the switch

4. The data plane: change behaviour without a restart

The data plane is where consistency becomes concrete. Per the blog, it continuously consumes server and tool configurations from the control plane and refreshes its in-memory state at a fixed cadence, so changes such as tool updates or enablement changes take effect in real time without service restarts or redeployments. Based on that configuration it dynamically materialises virtual MCP servers, each exposing a single //mcp endpoint as the entry point for agent execution, with incoming requests resolved to server handlers through a built-in proxy server. Each handler is tool-aware and downstream-aware: for HTTP back-ends it holds endpoint config, for gRPC/TChannel it holds procedure mappings, serialising the JSON payload into Protobuf or Thrift bytes and forwarding it before translating the response back into MCP-compatible JSON. The actual downstream request runs through Muttley, Uber's service-mesh sidecar, so the gateway inherits existing service-to-service routing.

# The data plane is where 'consistency' becomes concrete. It consumes
# server and tool configs from the control plane and refreshes its
# in-memory state on a fixed cadence, so changes take effect without a
# restart or redeploy. Each virtual server exposes ONE endpoint.

DATA_PLANE = {
    "config_source": "control plane (registry)",
    "refresh": "in-memory, fixed cadence, no redeploy",
    "entry_point": "//mcp per virtual server",
    "translation": "server handlers are tool-aware and downstream-aware",
    "id_backends": {
        "http": "endpoint config",
        "grpc_tchannel": "procedure mapping",
    },
    "execution": "delegated to Muttley, Uber's service-mesh sidecar",
}

# Delegating to the mesh means the gateway inherits existing service-to-
# service routing instead of reinventing it.

5. Security: tool-level authorization and out-of-the-box redaction

Security is built in rather than bolted on. Per the blog, MCP Gateway provides built-in authorization and redaction for all servers at tool-level granularity: it uses Uber's internal access control system to apply different charter policies to detected caller actors — humans, services and agents — with policies created at the server level and optional tool-level overrides. It also performs out-of-the-box redaction of any PII or sensitive data from tool responses. Keeping both "which tools a caller can see" and "which tools a caller can call" at the gateway layer is what makes the system auditable at scale.

# Security is baked in at tool granularity. Uber applies its internal
# access control system with charter policies per caller actor type.

CALLER_ACTORS = ["humans", "services", "agents"]

def authorize(request):
    """Server-level policy with optional tool-level overrides."""
    policy = charter_policy_for(request.actor)      # by actor type
    decision = policy.check(request.tool)           # tool-level granularity
    if not decision.allowed:
        return {"allow": False, "reason": decision.reason}
    return {"allow": True, "redact_pii": True}       # out-of-the-box redaction
Tool-level authorization and redaction

Who can see, who can call, and whether responses carry PII

6. A design checklist you can copy

Extract Uber's approach into reusable principles. First, treat MCP as a platform capability rather than something each team rebuilds, centralised in one gateway. Second, separate the control plane from the data plane, using a registry as the single source of truth for discovery, ownership and enablement. Third, keep everything disabled by default and explicitly enabled, with every description change going through a diff, an approval and a rollback path. Fourth, automate discovery from IDLs and heartbeats and generate descriptions with an LLM, so service teams stay off the critical path. Fifth, enforce tool-level authorization plus PII redaction, with policies differentiated by actor type (human, service, agent). Sixth, make config changes take effect live via in-memory refresh. Before you ship, size your limits and latency budget with our API Rate Limit Calculator and API Response Time Calculator, normalise contracts with the OpenAPI Spec Validator, and turn "define first, expose second, least privilege always" into process with the API Mock Generator and API Key Rotator.

📌 Frequently Asked Questions

Why did Uber build its own MCP Gateway?

Per Uber's engineering blog, early ad-hoc MCP integrations showed clear value: agents became dramatically more capable when they could access live business context, query internal services and take meaningful actions. But as adoption accelerated, individual teams independently built integrations, leading to fragmented tooling and duplicated infrastructure. MCP tools were hard to discover, difficult to operate reliably, and tightly coupled to specific services or agent implementations. As hundreds of teams explored agentic workflows, scaling MCP without a unified architecture would raise operational complexity, security risk and developer friction.

What are the two halves of the MCP Gateway?

Per the blog, it follows a microservice architecture with two primary components. The MCP Registry is the control plane: a catalog of hundreds of MCP servers and thousands of MCP tools, and the single source of truth for discovery, ownership and enablement. The Proxy Gateway is the data plane: it executes MCP requests at runtime, translating MCP calls into HTTP, gRPC or TChannel requests, forwarding them to the appropriate back-end service, and converting responses back into MCP-compatible results. Agents see one consistent MCP interface while the underlying services stay unchanged.

What is AutoCrawler and how does it discover tools?

Per the blog, AutoCrawler is a Cadence-powered distributed workflow system subscribed to Uber's IDL registry and internal service signals. A cron job triggers a workflow that scans for newly added services, APIs and schema changes. For services defined through Protobuf or Thrift IDLs, it upserts a virtual MCP server, parses the IDL for method names and request/response schemas plus doc comments, uses an LLM to generate agent-friendly tool descriptions, translates the schemas into MCP-compatible JSON-RPC 2.0 schemas, and registers tools in a disabled-by-default state. For native MCP servers, it monitors heartbeat metrics, calls listTools, and creates a virtual proxy server.

Why is disabled by default a core design principle?

Per the blog, a core design principle is that discovery does not imply exposure. Every MCP server and tool starts in a disabled state and must be explicitly reviewed and enabled by the owning team. Service owners can review and refine the generated tool definitions before enabling them, and every change to a tool description triggers a config change diff that must be approved by server owners, with the option to roll back to a previous known version.

What does the MCP Gateway do for security?

Per the blog, MCP Gateway provides built-in authorization and redaction for all servers at tool-level granularity. It uses Uber's internal access control system to apply different charter policies to detected caller actors — humans, services and agents — with policies created at the server level and optional tool-level overrides. It also performs out-of-the-box redaction of any PII or sensitive data from tool responses.