AI Blog

agentgateway vs ContextForge vs Obot vs Docker MCP Gateway: whose identity reaches the server

The MCP specification settles the negative — as of revision 2026-07-28 a server MUST NOT pass through the token it received from its client — but leaves RFC 8693 token exchange on the roadmap, so four gateways answer the question four different ways. agentgateway and ContextForge exchange the token; Obot attaches the user’s stored upstream token and is mid-migration between the two; Docker MCP Gateway has no user concept at all, which is honest for a workstation and disqualifying for a fleet. Pick on that axis, and notice that Docker’s isolation story is the best of the four on an axis the others do not compete on.

By Agentic AI Wiki 16 min read

Four projects call themselves MCP gateways and only one question separates them: when the call reaches the upstream server, whose identity is on it? The MCP specification settles the negative — as of revision 2026-07-28 a server MUST NOT pass through the token it received from its client — but leaves the positive mechanism, RFC 8693 token exchange, sitting on the roadmap under a working group that is still forming. So each of these four answers it differently, a feature table that reads "OAuth ✅" across the row tells you nothing, and one of the four has no multi-user identity at all. Decide on that axis and the rest is deployment preference.

At a glance

All four are open source, which is unusual for this category and means every claim below was checked in the repository rather than on a pricing page. They are not, however, the same kind of thing.

ProjectShapeIdentity reaching the upstream serverLicence
agentgatewayRust proxy, Linux Foundation projectExchanged token — RFC 8693 in the config surfaceApache 2.0
ContextForge (IBM)Python/FastAPI registry and proxyExchanged token, or a gated header passthroughApache 2.0
ObotSelf-hosted control plane, Kubernetes or DockerThe user's stored upstream OAuth tokenMIT, open-core
Docker MCP GatewayGo CLI plugin behind Docker Desktop's MCP ToolkitNothing user-specific — there is no userMIT
Where the caller’s identity stops in four MCP gateways Four rows, each showing what the gateway does with the caller’s identity and what the upstream MCP server therefore records. agentgateway exchanges the inbound token under RFC 8693, so the upstream server sees a fresh audience-bound token carrying an actor claim. ContextForge exchanges the token too, unless header passthrough has been explicitly enabled, in which case the inbound Authorization header is forwarded unchanged. Obot authenticates the user against an identity provider and attaches that user’s separately stored upstream OAuth token, so the upstream sees the user through a credential the user consented to. Docker MCP Gateway authenticates the inbound connection with a single shared static bearer token and has no user concept, so the upstream sees one workstation identity. GATEWAY WHAT IT DOES WITH THE INBOUND TOKEN WHAT THE UPSTREAM SERVER RECORDS agentgateway Exchanges it (RFC 8693) subject_token → new token, actor claim The user, acting through the gateway audience-bound, minted per request ContextForge Exchanges it — or forwards it passthrough off by default, opt-in flag The user, or a confused deputy decided by an environment variable Obot Attaches the user’s stored token held at rest, refreshed by the platform The user, via their own consent nothing about the gateway travels Docker MCP Gateway No inbound user exists one shared static bearer token One workstation correct for its scope, useless for a fleet The specification settles only the negative: a server MUST NOT pass through the token it received from its client. RFC 8693 token exchange — the mechanism that replaces passthrough — is still roadmap work, not a protocol requirement.
The four boxes on the right are what the upstream server's access log actually contains.

agentgateway is written in Rust, is a Linux Foundation project, and describes itself as a next-generation agentic proxy: JWT, API-key and OAuth authentication, fine-grained RBAC driven by a CEL policy engine, rate limiting, TLS and OpenTelemetry. ContextForge is IBM's, federating MCP alongside A2A and REST/gRPC APIs behind one registry, and shipping virtual servers that bundle a chosen subset of tools. Obot is a self-hosted governance platform of which the gateway is one component, with the most developed audit story of the four. Docker's is the one most engineers already have installed, because it arrives with Docker Desktop.

What the specification settles, and what it leaves to you

The current protocol revision is 2026-07-28, and its authorization security-considerations page is unusually direct. Two obligations matter here, both quoted from the spec:

MCP servers MUST only accept tokens specifically intended for themselves and MUST reject tokens that do not include them in the audience claim or otherwise verify that they are the intended recipient of the token.
If the MCP server makes requests to upstream APIs, it may act as an OAuth client to them. The access token used at the upstream API is a separate token, issued by the upstream authorization server. The MCP server MUST NOT pass through the token it received from the MCP client.

Note the subject: these are obligations on MCP servers. A gateway is an MCP server to everything upstream of it, so it inherits them, but the spec never addresses gateways as a category — which is exactly why four of them can be compliant in four different ways. The spec's own best-practices page names the anti-pattern and its consequence: forwarding an unmodified token creates the confused-deputy problem, and the downstream server's logs "may show requests that appear to come from a different source with a different identity". Audit corruption, not just authorisation bypass.

Now the gap. The spec requires RFC 8707 resource indicators — clients must send the resource parameter so tokens are bound to the server they are for. It does not require RFC 8693. Token exchange appears in the repository exactly once outside prose, in the development roadmap, as future work for an Agent Identity working group that is still forming, alongside Workload Identity Federation and the ID-JAG grant. So the mechanism that turns "don't pass the token through" into "here is what to do instead" is not yet part of the protocol. Every gateway below has had to decide without it, and that is the honest reason the four answers diverge.

The four answers

Four MCP gateways across four identity axes Feature matrix. Rows are agentgateway, ContextForge, Obot and Docker MCP Gateway; columns are RFC 8693 token exchange, per-user credential brokering, tool-level filtering, and per-user audit attribution. agentgateway: strong on token exchange, and instead of storing a per-user credential it keeps only a short-lived in-memory cache of exchange results, strong tool-level control through CEL policy, medium audit. ContextForge: strong on token exchange, strong on user-scoped OAuth tokens, strong tool filtering through virtual servers, medium audit. Obot: medium on token exchange because the released documentation describes it while the current main branch has replaced it, strong per-user brokering, medium tool filtering through accept-reject-mutate filters, strong per-user audit with an auditor role. Docker MCP Gateway: absent on token exchange, per-user credentials and per-user audit because it has no user concept, strong on tool filtering through server and tool flags. Where each gateway is strong RFC 8693 EXCHANGE PER-USER CREDENTIAL TOOL-LEVEL FILTER PER-USER AUDIT agentgateway In the config protos Short-TTL exchange cache CEL policy engine OTel, not an audit UI ContextForge Grant type in code User-scoped tokens Virtual servers Logs, no role split Obot Version-dependent Stored per user Accept / reject / mutate Auditor role gate Docker Absent No user exists Server & tool flags Shape metadata only Strong Partial or qualified Absent by design Docker’s row is not a scorecard failure — three of these axes require a user, and a workstation tool deliberately has none. Its strengths sit on axes this matrix does not measure.
The identity column is the one that forecloses architecture. The rest can be added later.

agentgateway and ContextForge exchange the token. Both carry RFC 8693 in code rather than in marketing. agentgateway's configuration protobuf defines a token-exchange type with subject_token_type, actor_token and actor_token_type fields; it also implements cross-app access, the ID-JAG shape the MCP roadmap points at. ContextForge implements the exchange in its tool and gateway services, keyed on a gateway configured with grant_type == "token-exchange", and refuses the call outright when no authenticated user is present — the error is "User authentication required for token-exchange gateway", which is the right failure and a good sign the path is real rather than aspirational.

ContextForge also ships the forbidden pattern, deliberately and behind a gate. Its header-passthrough feature renames an inbound X-Upstream-Authorization to Authorization and forwards it. This is passthrough, and the documentation says so with a warning at the top of the page: the feature is disabled by default, must be turned on with ENABLE_HEADER_PASSTHROUGH=true, and Authorization is deliberately excluded from the default header list with a note about token-leakage risk. That is a defensible piece of engineering — real MCP servers exist that support nothing else — and it is also the sharpest illustration of this whole comparison. Whether your deployment complies with the spec is decided by an environment variable, not by which product you bought.

Obot brokers a stored per-user token, and is visibly mid-migration. Its v0.25.0 documentation describes an MCP Server Shim performing an RFC 8693 exchange, with exchange credentials living in the shim and never exposed to the MCP server. The current main branch has deleted the shim and that language: the gateway now "obtains the user's stored upstream OAuth token when the target server requires one, and proxies the request". Both are legitimate — a stored per-user token is emphatically not passthrough, since it was issued to the user for that upstream — but they are different security properties, and a token exchanged per request is narrower than a refresh token held at rest. If you are evaluating Obot, pin the version, because the answer changed between the released docs and the branch.

Docker has no answer, because it has no user. Inbound authentication is a single static bearer token read from MCP_GATEWAY_AUTH_TOKEN or generated at startup, compared in constant time; there is no concept of a calling user anywhere in the gateway package, and --allow-unauthenticated is an explicit opt-out that logs a warning. Outbound, it acts as its own OAuth client with dynamic client registration and PKCE, and it does send the RFC 8707 resource parameter — so it is audience-correct — but the identity it obtains belongs to the workstation, not to a person. This is not a deficiency. It is a workstation tool being honest about its scope, and the failure mode is only that teams install it first and later discover it cannot become the fleet gateway.

Docker is playing a different sport, and winning it

Two layers sold under one category name Three columns. The workstation layer, represented by Docker MCP Gateway, controls container isolation, images signed and verified by digest, CPU and memory limits, SSRF and egress guards and secret scanning, but has no user identity. The fleet layer, represented by agentgateway, ContextForge and Obot, controls user identity, token exchange, policy and per-user audit, but generally does not run the server processes. The third column notes that the two layers are complements rather than substitutes, that most organisations need both, and that neither side sells the other half. Runs the servers container isolation, no-new-privs signed images, pinned by digest SSRF ranges, secret scanning DOCKER MCP GATEWAY strongest here, and has no user identity Routes to them user identity and token exchange policy, RBAC, tool scoping per-user audit attribution AGENTGATEWAY / CONTEXTFORGE / OBOT mostly do not run the server process at all You need both the layers are complements neither vendor sells the other the seam is where egress lives NOT A SHORTLIST OF FOUR pick one from each column, not one winner from four
Two layers, not four competitors. The overlap is smaller than the category name suggests.

Judge Docker's gateway on the axis it actually competes on and it has the strongest story here by a distance, because it is the only one of the four that runs the MCP servers rather than merely routing to them. Signature verification is on by default for images in Docker Hub's mcp/ namespace, which must then be referenced by digest and are verified before pull or run — third-party images outside that namespace are not covered, which is worth knowing rather than assuming. Containers start with Docker isolation, no-new-privileges, and configured CPU and memory limits. Remote URLs must be public HTTPS by default and loopback, private, link-local and metadata-service ranges are rejected, which is SSRF defence most proxies leave to the network. Host binds are read-only by default and must resolve under allowlisted roots. Secret blocking is on by default and scans both tool-call arguments and text responses.

Set against that, the enterprise proxies mostly do not run anything. Obot is the exception and the comparison is instructive: each server gets its own Kubernetes pod, and per-server domain allowlists exist but are enforced through an external controller whose only supported provider today is Aviatrix — the built-in NetworkPolicy is explicitly "not domain-aware". So the honest architecture for a company is both layers, not a winner: identity, policy and audit at the fleet gateway, isolation and supply chain at whatever runs the server process. Neither vendor sells you the other half, and the seam between them is where your egress control has to live.

Audit is downstream of identity, which is why it is the honest tiebreaker

An audit log can only record an identity the gateway possessed. That makes logging a derived property rather than an independent axis, and it collapses the shortlist quickly for anyone with a compliance obligation.

Obot's is the most developed: MCP requests and responses with the user who made them, filterable by date, user, server, operation and status, deleted after 90 days by default with an export path. Its access model is the part worth stealing regardless of what you deploy — request and response bodies are visible only to an Auditor role, and every other role including Owner and Admin sees metadata only. That is a separation-of-duties design most in-house implementations do not think to build, and it is the difference between an audit log and a second copy of your data with wider access than the original.

Docker's, again, is coherent for its scope and useless for compliance: call logging is on by default, but the documentation is explicit that logs record the tool name and argument shape metadata only, and that raw argument keys and values must not be logged. Excellent for a workstation where the arguments are your own secrets. Not a record of who did what.

The pattern generalises past these four. If a gateway cannot name the human on whose behalf a tool call was made, no amount of log volume produces an audit trail — which is the same argument the ambient authority problem makes from the other direction, and the reason delegated access and consent records keep turning up in agent regulation.

When to pick which

SituationPickBecause
One developer, local servers, wants isolation and supply-chain safetyDocker MCP GatewayBest container isolation, signed images by digest, SSRF and secret guards — and no identity model you would have to unpick later, because there is none
Fleet deployment, identity provider in place, needs per-request narrowingagentgatewayRFC 8693 exchange plus cross-app access in the config surface, CEL-based RBAC, and a Linux Foundation governance story if that matters to your procurement
Federating MCP alongside A2A and existing REST APIsContextForgeThe only one of the four that treats non-MCP backends as first class; virtual servers give tool-level scoping without deploying anything — just leave header passthrough off
Compliance obligation, self-hosted Kubernetes, audit is the deliverableObotPer-user audit with an Auditor role gating payload visibility, per-server pods and egress allowlists — pin the version and confirm which identity model that version ships
You have not yet decided who your agents act asNone of them yetEvery option above encodes an answer to that question. Decide it first; the gateway is the enforcement point, not the decision

What should not decide it: the star count, which tracks category attention rather than fit, and whether the README says "enterprise-ready". What should: run the one you are considering against a server that requires OAuth, then read the upstream server's access log. If it shows your gateway's service identity where you expected a person, you have found the answer before it becomes an incident.

FAQ

Is a stored per-user OAuth token the same as token passthrough?

No, and the distinction is the whole point. Passthrough forwards the token the client presented to the gateway, which was issued for the gateway's audience and therefore arrives upstream as a confused deputy. A stored per-user token was issued by the upstream authorization server to that user for that upstream, through a consent the user gave. It is compliant. It is also broader than an exchanged token, because it sits at rest with a refresh token attached rather than being minted per request.

Does the MCP specification require RFC 8693?

No. It requires clients to use RFC 8707 resource indicators, and it forbids servers from passing through the token they received. RFC 8693 token exchange appears in the roadmap as future work for the Agent Identity working group, alongside Workload Identity Federation and ID-JAG. Anyone telling you a gateway is "spec-compliant because it does RFC 8693" is describing a good implementation of something the spec has not asked for yet.

Can I just run Docker's gateway in production?

For a single-tenant workload where the credential legitimately belongs to a machine — a build agent, a scheduled job, one team's shared automation — yes, and you get isolation nothing else here matches. For anything acting on behalf of named humans, no: there is no user concept to attribute an action to, so you cannot scope a compromise, cannot revoke one person's access, and cannot produce an audit trail.

What breaks when an MCP server does not support OAuth at all?

You end up with a shared credential somewhere, and the only question is where. The good version is the gateway holding it, with per-user policy on the gateway side deciding who may invoke which tool, so the loss of user identity stops at a boundary you control and is recorded. The bad version is header passthrough turned on globally, which loses the identity and corrupts the upstream audit trail at the same time. Both look identical on a feature matrix.

Do I need a gateway at all if my agents call MCP servers directly?

Not until the second team, the second environment, or the first auditor. The gateway earns its place when you need one enforcement point for who may call which tool, one place tokens are exchanged rather than shared, and one log that names the human — the same consolidation argument as an AI gateway, applied to tool calls instead of model calls. Before that it is infrastructure you are operating for its own sake.

Further reading

On this wiki:

Project sources: