Skip to content

design: add MCP client proxy design doc - #674

Closed
R-Lawton wants to merge 1 commit into
mainfrom
design/mcp-client-proxy
Closed

design: add MCP client proxy design doc#674
R-Lawton wants to merge 1 commit into
mainfrom
design/mcp-client-proxy

Conversation

@R-Lawton

@R-Lawton R-Lawton commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Defines the proxy architecture for MCP Inspector-like functionality in the console plugin
  • Proxy lives in the developer-portal-controller (shared backend for both console plugin and RHDH)
  • Covers gateway discovery, auth passthrough, session management, and multi-gateway support

Key decisions

  • Custom proxy needed because MCP servers don't enable CORS and ConsolePlugin spec.proxy is static (can't handle dynamic multi-gateway)
  • Proxy discovers gateways from MCPGatewayExtension CRs and routes MCP requests to the correct gateway
  • Auth tokens are passed through to the gateway — proxy never stores or validates them
  • Session management is stateless in the proxy — frontend holds the Mcp-Session-Id
  • Dev preview scope: tools (list, call), prompts (list, get)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Added a design document for an integrated MCP inspection and testing experience within OpenShift and RHDH.
    • Documented gateway discovery, endpoint resolution, readiness reporting and MCP request forwarding.
    • Defined authentication and session-header handling, including behaviour for expired or invalid sessions.
    • Recorded security requirements and potential future support for OAuth/OIDC and server-sent notifications.

Defines the proxy architecture for MCP Inspector-like functionality.
The proxy lives in the developer-portal-controller and handles gateway
discovery, auth passthrough, session management, and multi-gateway support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Signed-off-by: R-Lawton <rlawton@redhat.com>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a design document for an MCP Client Proxy in developer-portal-controller, covering gateway discovery, MCP request forwarding, authorization pass-through, session relay, security requirements, and future OAuth/OIDC and SSE support.

Changes

MCP Client Proxy

Layer / File(s) Summary
Proxy scope and boundaries
docs/designs/2026-07-15-mcp-client-proxy-design.md
Defines the development-preview scope for MCP tool and prompt inspection, with OAuth/OIDC and SSE excluded.
Gateway discovery and proxy API
docs/designs/2026-07-15-mcp-client-proxy-design.md
Describes MCPGatewayExtension discovery, endpoint resolution, readiness reporting, and the GET /gateways and POST /gateways/<namespace>/<name>/mcp endpoints.
MCP protocol, authentication, and sessions
docs/designs/2026-07-15-mcp-client-proxy-design.md
Defines JSON-RPC forwarding, authorization pass-through, session header relay, invalid-session handling, security requirements, and future extensions.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Possibly related issues

  • Kuadrant/mcp-gateway-console-plugin#6 — Covers Inspector UI integration enabled by the documented gateway discovery and MCP proxy endpoints.
  • Kuadrant/kuadrant-console-plugin#671 — Covers Inspector UI gateway discovery and MCP tool operations supported by the documented API.

Poem

A rabbit hops through gateways bright,
Relaying MCP by moonlit night.
Tokens pass, sessions softly flow,
Tools and prompts begin to show.
“No SSE yet,” the bunny sings—
“But OAuth may grow new wings!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding an MCP client proxy design document.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch design/mcp-client-proxy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/designs/2026-07-15-mcp-client-proxy-design.md`:
- Around line 36-39: Update the endpoint selection and outbound proxy flow
described in the MCPGatewayExtension design to validate privateHost before use:
allow only http/https, approved internal hostname forms and ports, reject
resolutions to loopback, link-local, private, or otherwise unauthorized IP
ranges, and disable or revalidate redirects against the same rules. Ensure
forwarded Authorization headers are sent only after the target passes
validation, while preserving the derived Gateway endpoint behavior.
- Around line 74-104: Update the “MCP protocol” and request/response flow
sections to explicitly document the proxy’s HTTP contract: require Accept to
include both application/json and text/event-stream, describe handling of 202
notification responses with no body, and state whether the proxy supports these
behaviors or intentionally exposes a JSON-only subset. If JSON-only, define the
rejection behavior for unsupported Accept values and notification-only requests,
including status and response handling.
- Around line 118-127: The proxy flow described in the session-handling section
must automatically recover from a 404 on any session-bearing request: clear the
stale Mcp-Session-Id, issue a fresh initialize request, capture the replacement
session header, and retry the original request. Update the statements under the
session lifecycle and 404 behavior to document this recovery instead of
requiring manual reconnect.
- Around line 80-88: Add explicit deadlines and cancellation propagation to the
proxy’s outbound POST, enforce maximum request and response body sizes, and
apply a concurrency limit around the gateway call. Update the “Sends this
request to the gateway” flow while preserving the existing header and body
forwarding behavior.
- Around line 34-39: Update the endpoint-resolution design to use the targeted
Gateway’s reported status/address data rather than assuming gatewayClassName
determines a Service name. If the generated <gateway-name>-<gatewayClassName>
convention is required, explicitly define it as an MCP Gateway contract and add
validation that the referenced Gateway conforms before constructing the
endpoint.
- Around line 106-115: Clarify the Auth model section to explicitly state the
intended access-control behavior for gateways without an AuthPolicy, including
that they are publicly callable if that is the design. Distinguish controller
ServiceAccount RBAC, which only governs discovery, from user authorization for
GET /gateways and POST /gateways/{namespace}/{name}/mcp; if uncovered gateways
should not be public, define the required per-gateway authorization or filtering
model.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8a560c51-e9da-47c1-b32d-25954583c67b

📥 Commits

Reviewing files that changed from the base of the PR and between 1d54f91 and ee84cf6.

📒 Files selected for processing (1)
  • docs/designs/2026-07-15-mcp-client-proxy-design.md

Comment thread docs/designs/2026-07-15-mcp-client-proxy-design.md
Comment thread docs/designs/2026-07-15-mcp-client-proxy-design.md
Comment thread docs/designs/2026-07-15-mcp-client-proxy-design.md
Comment thread docs/designs/2026-07-15-mcp-client-proxy-design.md
Comment thread docs/designs/2026-07-15-mcp-client-proxy-design.md
Comment thread docs/designs/2026-07-15-mcp-client-proxy-design.md
@R-Lawton
R-Lawton requested a review from jasonmadigan July 23, 2026 14:09
@jasonmadigan

Copy link
Copy Markdown
Member

Will dig into this today

@jasonmadigan

jasonmadigan commented Aug 11, 2026

Copy link
Copy Markdown
Member

After reading this and some experimentation, I've got a different take on what we should do here.

Taking as given that we want a way to debug/test MCP in-console without reaching for mcp-inspector.*

@R-Lawton

Some thoughts:

  • Re: CORS, most MCP servers won't support it OOTB because they expect to be called by harnesses rather than browsers, and CORS is a browser thing. But that's the wrong hop for this design. Our plugin never calls those servers, it calls the mcp-gateway, and the gateway is ours to configure.
  • mcp-gateway already has most of the infra we need for browser-based clients to work. There's a guide for enabling CORS in the listener/router setup.
  • That guide has bugs that make CORS unusable OOTB, which we should fix regardless of what we decide here:
    • Access-Control-Expose-Headers is missing, so there's no way to read Mcp-Session-Id after init
    • Mcp-Session-Id isn't in Access-Control-Allow-Headers either, so preflight fails on every request after initialize
    • It pairs Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true, which browsers reject
  • To confirm, I fixed those three on a local cluster and ran a full initialize / notifications/initialized / tools/list cycle and stuff worked fine.
  • Agree we don't want the embedded inspector able to talk to any host, only gateways we know about. Worth noting that CORS gives us that in both directions: the gateway decides which origins may call it via allowedOrigins, and the plugin can only reach gateways it discovered from MCPGatewayExtension. The proxy design goes the other way, spec.privateHost is an arbitrary URL the proxy will dial, which is an SSRF surface we'd have to defend (and it would be quite difficult to defend)
  • The fix I'd suggest is a spec.cors block on MCPGatewayExtension so the controller stamps the headers onto the HTTPRoute it already manages, always including the protocol headers so nobody has to know to add them. Right now the guide's approach requires httpRouteManagement: Disabled and hand-maintaining the route per extension, which is probably why nobody's using it
  • This scales fine with multiple gateways. You get one extension per namespace and one per Gateway port (not per listener, the EnvoyFilter patches by portNumber), so several extensions means several Gateways plus ReferenceGrants. Each gets its own broker Deployment, Service, EnvoyFilter and controller-managed HTTPRoute in its own namespace. The route is already per-tenant anyway.
  • On the auth model: spec.oauthProtectedResource already exists on MCPGatewayExtension v1 and carries the authorisation servers, resource, scopes and bearer methods. That's enough for RFC 9728 discovery and Authorisation Code with PKCE from the browser, so we shouldn't need users pasting a bearer token into a text box even for dev preview.

Extending the developer-portal-controller bothered me a bit:

  • The proxy would need cluster-wide access to every MCPGatewayExtension and Gateway, plus deeper network access into a plethora of namespaces than we'd probably like, which is a real minefield. OCP network policies for pod-to-pod access would become an issue quickly.
  • It also inherits the authorisation problem. The plugin runs on the signed-in user's token today and gets RBAC from the API server for free. A proxy with its own ServiceAccount sees everything, and the design says it validates nothing, so GET /gateways would hand the full cluster inventory to any console user.

*I could easily be swayed that we don't want this at all. Is it something we think we want, or has it come from elsewhere? For what it's worth it doesn't look like it originated in this PR: #667 is an accepted epic for MCP management UI with a Figma prototype behind it, and #670-673 hang off that, so someone has designed for it. Worth knowing who's asking before we build the plumbing.

@R-Lawton

Copy link
Copy Markdown
Contributor Author

hey @jasonmadigan, i think your approach makes a lot of sense here — pushing CORS config into the gateway via spec.cors on MCPGatewayExtension is cleaner than the proxy and avoids the security concerns you flagged.

would you mind writing up a design doc for your proposal? i'd like to close this PR in favour of that. we can link them together for context.

@jasonmadigan

Copy link
Copy Markdown
Member

I should have a draft design doc for this a little later today. Want to write a small PoC to prove whether some issues with content security policies are real or imaginary...

jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 17, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 17, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 17, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 17, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 17, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 17, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
jasonmadigan added a commit to jasonmadigan/kuadrant-console-plugin that referenced this pull request Aug 18, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
@R-Lawton

Copy link
Copy Markdown
Contributor Author

closing in favour of #748

@R-Lawton R-Lawton closed this Aug 18, 2026
@github-project-automation github-project-automation Bot moved this to Done in Kuadrant Aug 18, 2026
pascazzioIsAbyayalan pushed a commit to pascazzioIsAbyayalan/kuadrant-console-plugin that referenced this pull request Aug 18, 2026
In-console MCP inspection via a new spec.cors on MCPGatewayExtension: the browser speaks Streamable HTTP directly to the gateway, superseding the proxy in Kuadrant#674. Includes PoC results (two-host tool list + call, CORS and CSP findings on real ROSA 4.21) and the remaining gaps. Refs Kuadrant#667

Signed-off-by: Jason Madigan <jason@jasonmadigan.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants