agentic-community / agentic-community/mcp-gateway-registry

Umbrella: Coding-Assistant OAuth Integration (catalog + gateway modes)

Ouverte
#988 1 commentaire 0 réactions 1 personne assignée Réclamée par @aarora79 Voir sur GitHub
architecture authentication enhancement feature-request mcp-spec oauth
Langage dominant
Python
Étoiles
911
Forks
234
Merge moyen
1 j 11 h
PR mergées (30 j)
62

Description

# Umbrella: Coding-Assistant OAuth Integration (catalog mode + gateway mode)

**Labels:** `enhancement`, `oauth`, `mcp-spec`, `authentication`, `architecture`, `feature-request`

**Linked existing issues:** #256 (spec umbrella), #614 (tactical PKCE + metadata), #889 (TTL), #936 (to be closed as superseded), #895, #896

**Owner:** @aarora79

**Status:** Proposed

---

## Summary

Two distinct modes for how a coding assistant interacts with MCP servers relative to the Registry. No middle ground, no federation layer.

### Catalog mode (Registry is discovery-only; Registry is NOT in the data path)

AI Registry Tools (`intelligent_tool_finder`, `list_services`, `list_agents`, `list_skills`) returns the **direct** URL of an external MCP server (e.g. `https://public.cloudflare.com/mcp`). The coding assistant adds that external URL to its own `mcp.json`. Any OAuth or auth negotiation happens directly between the coding assistant and that external server's IdP. The Registry is never in the data path, does not proxy, does not mediate auth, takes on no IdP-compatibility burden. Cloudflare's auth is Cloudflare's problem.

Catalog mode is existing AI Registry Tools behavior; the only umbrella-relevant change is confirming that `intelligent_tool_finder` returns external URLs (not rewritten-through-gateway URLs) for external servers.

### Gateway mode (Registry-routed paths; Registry IS in the data path)

The coding assistant points at a Registry-gateway URL (e.g. `https://mcpgateway.example/ai-registry-tools/mcp` or `https://mcpgateway.example/cloudflare-api/mcp`). The gateway is on every request. It enforces identity + entitlement on ingress via `X-Authorization`. Backend auth on egress has two sub-cases:

1. **Backend under the Registry's trust domain** (today's pattern): gateway injects the backend's credential on egress. One header in `mcp.json`: the ingress `X-Authorization`. The user doesn't see or manage any backend token. This is what today's `cloudflare-docs`-style registrations already do.
2. **Backend with its own external IdP** (e.g. a Cloudflare API MCP server that demands its own OAuth token): `mcp.json` has TWO headers — `X-Authorization` (ingress, Registry-issued, 8h, refreshed via UI button or API per #936) and `Authorization` (egress, backend-token, refreshed out-of-band by a helper script per #936). Gateway validates `X-Authorization`, forwards `Authorization` untouched to the backend. Backend validates its own token.

Both gateway-mode egress cases preserve the Registry's ingress-identity layer (non-negotiable — it's what makes the gateway valuable). Neither requires the gateway to broker OAuth, hold multiple IdP trust relationships, or proxy `.well-known` paths.

## What we considered and declined

Writing this down explicitly so the conversation doesn't reopen.

- **Domain B pass-through** (gateway forwards `//.well-known/*` and `//mcp`; client does a second OAuth dance against the backend's IdP; gateway is pure bits-on-wire). Declined: if the gateway drops ingress auth to make this work, we lose user identity + entitlement (the gateway's reason to exist). If the gateway keeps ingress auth, the coding assistant needs to set TWO headers while *also* doing client-managed OAuth on one of them — no OAuth-native client supports that pattern today. Either way breaks.
- **Gateway-brokered token exchange (Domain C / RFC 8693 OBO-style).** Registry exchanges the user's ingress token for a backend-scoped token at the backend's AS, caches per-user. Declined: requires the Registry to hold a bilateral trust relationship with every backend's IdP, implement per-backend AS client config, and maintain RFC 8693 compatibility across Entra/Okta/Auth0/Keycloak/Cognito/whatever-Cloudflare-uses. Operationally too heavy for the value; we'd be running a federated identity service.
- **Proxying backend `.well-known` paths on any basis.** Declined: even discovery-only proxying implies a trust relationship we don't have and invites confusion about who's responsible when it breaks.

The fallback for any backend that doesn't fit catalog mode or either gateway-mode sub-case is: **register it in catalog mode.** The coding assistant configures it directly. We don't pretend to mediate what we shouldn't mediate.

## Why now

- MCP 2025-06-18 spec makes PRM (RFC 9728) + AS metadata (RFC 8414) + PKCE (RFC 7636) + RFC 8707 + `WWW-Authenticate` on 401 MUSTs on the resource-server side. We do not yet advertise these for the gateway's own paths.
- Claude Code v2.1.64+ already implements PRM discovery, PKCE, DCR, CIMD auto-discovery, scope restriction, secure token storage, and automatic refresh. Cursor supports static pre-configured OAuth credentials only. VS Code Copilot documents no remote-MCP OAuth flow. Targeting Claude Code end-to-end gives us spec compliance by default.
- The November 2025 spec direction (Aaron Parecki, RFC 8693-based ID-JAG, CIMD) is crystallizing. Doing PRM + CIMD + ID-JAG puts us on-spec without the DCR detour.
- Today's gateway-mode UX is manual: operator pastes a self-signed 8h token into `mcp.json`. TTL is hardcoded (#889), no refresh, no revocation propagation. Sub-issues A + E retire this path entirely.
- Concrete pressure points from deployments in the field: Entra v1 scope format compatibility, Entra PKCE interop, and verbatim `api:///...` scope preservation required by AgentCore Identity integrations.

## Goals

1. Ship the RFC 9728 / 8414 / 7636 / 8707 MUSTs so Claude Code `mcp add ` works end-to-end with no manual token paste for the gateway itself.
2. Retire the hardcoded 8h self-signed JWT on the gateway ingress path in favor of IdP-signed tokens with real refresh and revocation.
3. Close #889 by wiring `MAX_TOKEN_LIFETIME_HOURS` + per-server `expires_in_hours` into the registration model.
4. Close #936 as superseded. Its skill + token-vending tool are obsoleted by A (PRM/PKCE auto-discovery) and E (8h self-signed JWT retirement).
5. Track the 2025 spec direction (CIMD publisher + consumer, ID-JAG receiver) so we are not on a dead-end DCR path.

## Non-goals

- **No RFC 7591 Dynamic Client Registration server-side.** Spec is moving off it; we own the upstream IdP config. See sub-issue G (decision doc).
- **No Registry-mediated OAuth for external backends.** No pass-through of external `.well-known`, no gateway-brokered token exchange, no per-backend AS trust setup. Declined above.
- **No changes to the web-UI operator login flow.** That path already works.
- **No custom coding-assistant header conventions.** We use `X-Authorization` for ingress + `Authorization` for egress, and we accept that the user pastes both in `mcp.json` for external-IdP egress. We do not expect coding assistants to know about our conventions or behave differently from spec.

## Header model for gateway mode

Ingress: **`X-Authorization: Bearer `**. Gateway-issued, Registry IdP-backed, validated by the gateway for user identity + entitlement on every request. Lifetime capped by `MAX_TOKEN_LIFETIME_HOURS` (#889). Non-negotiable for all gateway-mode paths. After A ships, PRM + PKCE auto-discovery handles acquisition + refresh transparently; no manual paste.

Egress: **`Authorization: Bearer `**, only for the external-IdP egress sub-case. Gateway forwards untouched. A lightweight out-of-band refresh helper for this header will be tracked in a separate issue if and when a gateway-mode external-IdP backend is actively registered. For backends under the Registry's trust domain, the gateway injects the egress credential server-side; no `Authorization` appears in `mcp.json`.

Example `mcp.json` entry for a gateway-mode backend with external-IdP egress (abridged):

```json
{
"mcpServers": {
"cloudflare-api": {
"url": "https://mcpgateway.example/cloudflare-api/mcp",
"headers": {
"X-Authorization": "Bearer ",
"Authorization": "Bearer "
}
}
}
}
```

## Sub-issues

Each ships as a separate, scoped unit of work. Roughly ordered by dependency.

| Sub-issue | Issue | Title | Phase | Depends on | Status |
|---|---|---|---|---|---|
| A | #989 | Publish `.well-known/oauth-protected-resource` + `.well-known/oauth-authorization-server` + `WWW-Authenticate` on 401 | 1 | - | Done (PR #1115) |
| F | #990 | Entra v1 `api:///...` scope verbatim pass-through in PRM + audience normalization | 2 | #989 | Open |
| E | #991 | RFC 8707 `resource` parameter enforcement + token-proxy surface (closes #889) | 3 | #989 | Open |
| B | #992 | Registry-published CIMD for the AI Registry Tools MCP client | 4 | #989, #991 | Open |
| C | #993 | CIMD consumer: accept CIMD URL as `client_id` on `/authorize` | 4 | #989, #991, #992 | Open |
| D | #994 | ID-JAG (RFC 8693 token exchange) receiver on token proxy | 5 | #989, #991 | Open |
| G | #995 | Decision doc: "Registry will not implement RFC 7591 DCR server-side" | - | - | Open |

### Tracking checklist

- [x] #989 — [Phase 1] Publish PRM + AS metadata + `WWW-Authenticate` on 401
- [ ] #990 — [Phase 2] Entra v1 scope verbatim pass-through
- [ ] #991 — [Phase 3] RFC 8707 resource parameter enforcement (closes #889)
- [ ] #992 — [Phase 4] CIMD publisher
- [ ] #993 — [Phase 4] CIMD consumer
- [ ] #994 — [Phase 5] ID-JAG (RFC 8693) receiver
- [ ] #995 — [Decision] No RFC 7591 DCR server-side

Previously-drafted sub-issue H ("Domain B / federation") was deleted. Federation is declined above; no implementation work is tracked for it in this umbrella.

## #936 — superseded by this umbrella

#936 proposed two deliverables: a `mcp-registry-discover` Claude skill and a `generate_registration_token` MCP tool. Both become obsolete once this umbrella ships:

- **Skill's core UX (paste-JWT → `claude mcp add` → restart)** is subsumed by sub-issue A (PRM + PKCE auto-discovery on `claude mcp add `). No paste step remains.
- **Token-vending MCP tool** vends exactly the 8h self-signed JWT that sub-issue E retires. Zero residual value.

#936 will be closed with a comment pointing at this umbrella. If an external-IdP-egress backend is registered later (not on the 2-week path), a fresh lightweight issue will track the ~20-line egress-refresh helper script. The framing of "post-Phase-3 permanent role" for #936 is withdrawn.

## Acceptance for the umbrella

- [x] Seven sub-issues (A, F, E, B, C, D, G) filed with scope tight enough to be one PR each.
- [ ] #614 sub-tickets labeled to match Phases 1-3 as they absorb A / E.
- [ ] One integration test that runs end-to-end against a live Claude Code instance: `claude mcp add ` → PRM discovery → PKCE → first tool call → refresh → revocation. Green in CI.
- [ ] Docs page in [docs/](docs/) explaining: catalog mode vs. gateway mode; the two gateway-mode egress sub-cases; the `X-Authorization` + `Authorization` header model with a worked `mcp.json` example; why federation is explicitly not supported.
- [x] #936 closed with supersession comment pointing at this umbrella.
- [ ] Decision doc G merged.
- [x] AI Registry Tools confirmed returning direct URLs (not gateway-rewritten) for external servers in catalog mode. Sanity check, no new code expected.

## Verify during Phase 1 (hypothesis, not blocking)

**Claim:** if the gateway were to proxy `//.well-known/*`, a spec-compliant coding assistant would do OAuth against the advertised backend IdP and set `Authorization: Bearer ` — colliding with the gateway's ingress-auth requirement on `X-Authorization`. The coding assistant wouldn't know to send both headers, so the flow breaks.

This is the basis for declining Domain B. It is testable during Phase 1 with a ten-line config experiment. If the claim turns out to be wrong (i.e. clients do handle both), we revisit whether Domain B becomes worth building. Until then, we proceed on the assumption it holds.

## References

- Recommendation doc with sequence diagrams: [.scratchpad/coding-assistant-oauth/recommendation-2026-05-04.md](.scratchpad/coding-assistant-oauth/recommendation-2026-05-04.md)
- [MCP Authorization spec 2025-06-18](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization)
- [Aaron Parecki, MCP Authorization Spec Update (2025-11-25)](https://aaronparecki.com/2025/11/25/1/mcp-authorization-spec-update)
- [RFC 9728 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
- [RFC 8414 Authorization Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414)
- [RFC 8693 Token Exchange (ID-JAG basis)](https://datatracker.ietf.org/doc/html/rfc8693)
- [RFC 8707 Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707)
- [CIMD draft](https://datatracker.ietf.org/doc/draft-parecki-oauth-client-id-metadata-document/)
- [Claude Code MCP docs](https://code.claude.com/docs/en/mcp)
- [Cursor MCP docs](https://cursor.com/docs/context/mcp)

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.