DEV Community

Cover image for MCP Server Management: Centralizing Auth, RBAC, and Approval Gates Before They Sprawl
Sahajmeet Kaur
Sahajmeet Kaur

Posted on

MCP Server Management: Centralizing Auth, RBAC, and Approval Gates Before They Sprawl

Six months ago we had one or two MCP servers. Now it's common for a team here to spin up a dozen - Slack, Jira, a database, an internal API wrapped as MCP, a couple of vendor-provided ones, each with its own credentials, and no single list of which agent could reach which one. That sprawl is what pushed us to put TrueFoundry's MCP Gateway in front of everything instead of letting each developer wire up their own connections.

TL;DR

  • MCP servers accumulate credentials and access fast, and without a central registry, nobody has an inventory of which agent can call which server or tool.
  • TrueFoundry's MCP Gateway sits between every MCP client (an IDE, an agent) and every registered server, terminates two separate auth handshakes (inbound from the client, outbound to the upstream server), and applies RBAC, guardrails, and observability at that single point.

The sprawl problem, concretely

Each new MCP server used to mean a new API key or OAuth app, configured wherever the agent framework happened to run - someone's local .env, a CI secret, a config map in whatever cluster. Nobody centrally decided "this agent can use this server," it just happened because someone wired it up to get their thing working. That's fine until you need to answer "which agents can currently write to Jira" or "revoke this one server's access without touching the other eleven" - which is exactly the question we couldn't answer before centralizing this.

What the gateway actually sits between

MCP itself is a JSON-RPC protocol: a client calls methods like initialize, tools/list, tools/call, and resources/list against a server, and the server responds with what it can do and the results of invoking it. Without a gateway, every IDE and every agent framework holds its own direct connection to every server it needs - N clients times M servers, each connection independently authenticated, independently configured, and invisible to anyone outside that one developer's machine.

TrueFoundry's MCP Gateway collapses that to N clients talking to one endpoint, which then talks to M servers on their behalf. The model is registration-once, consumption-by-many: our platform team registers a server a single time, and every developer's IDE, every agent, and every workflow that needs it points at the same gateway URL instead of holding its own credentials to the server directly.

Two separate auth handshakes, not one

This is the part that took us longest to get right conceptually, because "auth" for an MCP gateway is actually two independent problems: how the client authenticates to the gateway (inbound), and how the gateway authenticates to the upstream MCP server (outbound). Per the auth and security docs, the gateway configures these separately and they mix and match.

Inbound (client → gateway) supports a TrueFoundry API key, an identity provider token, or TrueFoundry OAuth - the gateway validates whichever one shows up and applies access control based on whoever it resolves to. Some people on our team authenticate with API keys, others through SSO via our IdP, and both get evaluated against the same RBAC without us having to reconcile two separate permission systems.

Outbound (gateway → upstream server) has four modes, and picking the right one depends on what the upstream server expects:

  • API Key - shared across everyone, or issued per individual so each caller's key can be tracked and revoked separately.
  • OAuth2, Authorization Code - for servers that need a real user to authorize (GitHub, Slack, Google), where each person consents once and the gateway stores and refreshes their token afterward.
  • OAuth2, Client Credentials - for server-to-server calls with no human in the loop.
  • Token Passthrough - the gateway forwards the same token it validated on the inbound side, which only works if the MCP server can itself validate TrueFoundry or your IdP's tokens. Token Forwarding is the related-but-different option where the client instead supplies separate upstream credentials via a header (x-tfy-mcp-headers), for servers with their own auth system that doesn't recognize inbound tokens at all.

The connection flow for an IDE looks like this: a developer signs in once with TrueFoundry OAuth, their IDE gets a temporary token to talk to the gateway, and if the target server needs a per-user provider token, the gateway handles that authorization separately and stores the resulting token at the gateway layer - it never reaches the developer's machine. Every tool call after that point is logged, authorized, and rate-limited by the gateway before it ever reaches the upstream server. For servers that support it, Auth Overrides let an individual user or a virtual account supply their own upstream credentials rather than sharing one team-wide key - currently supported for individually-issued API keys and OAuth2 Authorization Code, with shared-key and client-credentials overrides listed as coming soon per the docs.

What centralizing it actually buys us, beyond auth

  • RBAC decides who can use which server, and which tools within it. We can grant a team access to a server but disable its destructive tools - delete, write, admin actions - for everyone except a smaller group.
  • Destructive tools can pause for a human. Tools marked destructive automatically stop and wait for user confirmation instead of executing silently, which matters a lot more once an agent can actually delete a record or send a message on someone's behalf.
  • Guardrails run pre- and post-tool-call, the same policy-enforcement pattern as LLM input/output guardrails, just applied to tool arguments and tool results instead of prompts and completions.
  • Every tool call is traced with the actual JSON-RPC method, not just a generic "request happened" log line, which matters when we're debugging why a specific tool call failed versus a tools/list call.

Two ways we've added servers without hand-writing one

Virtual MCP servers let us curate tools from several registered servers into one narrower server for a specific team or workflow, without standing up a new deployment. When a workflow needs two tools from Jira and one from GitHub, we expose exactly those three under one virtual server instead of granting the requesting team the entire surface area of both underlying servers.

OpenAPI to MCP auto-generates an MCP server from an existing OpenAPI specification, which mattered for us because most of what we wanted to expose was already a REST API we'd written internally - we got governed, gateway-fronted MCP tools out of our existing API surface without writing a custom MCP server by hand.

Watching it after it's running

The MCP Metrics dashboard has two views: a server-centric one (requests per second, P50/P75/P90/P99 latency, failure rate broken down by error type, and a breakdown of which JSON-RPC methods - tools/list, tools/call, and so on - make up traffic mix) and a tool-centric one that drills into invocation count, latency, and error rate per individual tool across every server. Both can be sliced by user, virtual account, or team, which is the difference between "the GitHub server is slow" and "the GitHub server is slow specifically for one team's batch job" - a distinction that's saved us real debugging time.

If you want that data outside the dashboard, there's a metrics query API that accepts the same shape of query - datasource: "mcpMetrics", fields like mcpServerName, toolName, method, and latencyMs - so you can pull p99 latency per tool or find every tool call over five seconds programmatically instead of eyeballing a chart. We use this to feed our own internal alerting rather than watching the dashboard manually.

How many MCP servers does your org actually have registered somewhere central right now, versus scattered across people's local configs? We only found out our real number once we went looking - is that universal or if some teams actually had a handle on it already.

Top comments (0)