[SILO-1181] feat: support configurable subpath prefix for MCP server#106
[SILO-1181] feat: support configurable subpath prefix for MCP server#106Prashant-Surya wants to merge 1 commit intomainfrom
Conversation
Allows deploying the HTTP server under an ingress subpath (e.g. /mcp) by mounting the OAuth, header-auth, and SSE transports under the prefix and passing the prefix into each OAuth provider's base_url so advertised metadata URLs (issuer, authorize, token, register, resource) resolve to the externally-visible path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Linked to Plane Work Item(s) This comment was auto-generated by Plane |
📝 WalkthroughWalkthroughAdded Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
plane_mcp/__main__.py (2)
136-136:⚠️ Potential issue | 🟡 MinorStartup log message no longer matches mounted paths.
The message still says
/mcpand/header/mcp, but mounts are now under/http/mcpand/http/api-key/mcp(plus optional prefix). Update this to avoid operational confusion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plane_mcp/__main__.py` at line 136, Update the startup log in __main__.py so the logger.info message reflects the actual mounted routes; replace the outdated "/mcp" and "/header/mcp" text in the logger.info call with the current paths "/http/mcp" and "/http/api-key/mcp" (and include any configured optional prefix if your app prepends one) so the log accurately shows where the HTTP endpoints are mounted.
91-115:⚠️ Potential issue | 🟠 MajorNormalize
MCP_PATH_PREFIXbefore composing routes.Raw concatenation can produce malformed paths (for example
/→//http,mcp→mcp/http). Normalize once, then reuse.Proposed fix
- prefix = os.getenv("MCP_PATH_PREFIX") or "" + raw_prefix = (os.getenv("MCP_PATH_PREFIX") or "").strip() + if raw_prefix in {"", "/"}: + prefix = "" + else: + prefix = raw_prefix if raw_prefix.startswith("/") else f"/{raw_prefix}" + prefix = prefix.rstrip("/") oauth_mcp = get_oauth_mcp(prefix + "/http") oauth_app = oauth_mcp.http_app(stateless_http=True) header_app = get_header_mcp().http_app(stateless_http=True) sse_mcp = get_oauth_mcp(prefix) sse_app = sse_mcp.http_app(transport="sse")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@plane_mcp/__main__.py` around lines 91 - 115, Normalize the MCP_PATH_PREFIX string once instead of raw concatenation: compute prefix_raw = os.getenv("MCP_PATH_PREFIX") or "" then normalize to prefix = ("/" + prefix_raw.strip("/")) if prefix_raw.strip("/") else "" so the value either is "" or begins with a single leading slash and has no trailing slash; then update calls that currently do prefix + "/http" and prefix + "/http/api-key" (the get_oauth_mcp calls and the Mount arguments) to use f"{prefix}/http" and f"{prefix}/http/api-key" (or construct the path with the normalized prefix), and keep Mount(prefix or "/", app=sse_app) as-is so routes do not get double slashes; change the prefix assignment near where prefix is defined and leave get_oauth_mcp, get_header_mcp, oauth_mcp, sse_mcp, oauth_well_known, sse_well_known, and the Starlette routes/Mount entries otherwise unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@plane_mcp/__main__.py`:
- Line 136: Update the startup log in __main__.py so the logger.info message
reflects the actual mounted routes; replace the outdated "/mcp" and
"/header/mcp" text in the logger.info call with the current paths "/http/mcp"
and "/http/api-key/mcp" (and include any configured optional prefix if your app
prepends one) so the log accurately shows where the HTTP endpoints are mounted.
- Around line 91-115: Normalize the MCP_PATH_PREFIX string once instead of raw
concatenation: compute prefix_raw = os.getenv("MCP_PATH_PREFIX") or "" then
normalize to prefix = ("/" + prefix_raw.strip("/")) if prefix_raw.strip("/")
else "" so the value either is "" or begins with a single leading slash and has
no trailing slash; then update calls that currently do prefix + "/http" and
prefix + "/http/api-key" (the get_oauth_mcp calls and the Mount arguments) to
use f"{prefix}/http" and f"{prefix}/http/api-key" (or construct the path with
the normalized prefix), and keep Mount(prefix or "/", app=sse_app) as-is so
routes do not get double slashes; change the prefix assignment near where prefix
is defined and leave get_oauth_mcp, get_header_mcp, oauth_mcp, sse_mcp,
oauth_well_known, sse_well_known, and the Starlette routes/Mount entries
otherwise unchanged.
Summary
MCP_PATH_PREFIXenv var so the HTTP server can be deployed behind an ingress that serves it under a subpath (e.g./mcp)./http/mcp), header-auth streamable-http (/http/api-key/mcp), and SSE (/sse) — are now mounted under the prefix.base_urlis built with the prefix so advertised metadata (issuer, authorize, token, register, and protected-resource URLs) resolves to the externally-visible path.mcp_pathfor well-known routes stays at/mcpand/sse(relative tobase_url) to avoid double-prefixing the advertised resource URL.MCP_PATH_PREFIXis unset, routing and metadata are identical to before — no behavior change for existing deployments.Tracks: SILO-1181.
Test plan
MCP_PATH_PREFIX=/mcp,PLANE_OAUTH_PROVIDER_BASE_URL=http://localhost:8211(no prefix in env var).GET /.well-known/oauth-authorization-server/mcp/httpreturns issuerhttp://localhost:8211/mcp/httpand registration endpointhttp://localhost:8211/mcp/http/register.GET /.well-known/oauth-authorization-server/mcp(SSE) returns issuerhttp://localhost:8211/mcp.GET /.well-known/oauth-protected-resource/mcp/http/mcpadvertises resourcehttp://localhost:8211/mcp/http/mcp.POST /mcp/http/mcpreturns 401 (OAuth challenge) — not 404.POST /mcp/http/api-key/mcpwith validx-workspace-slug+authorizationheaders initializes successfully.GET /mcp/ssereturns 401 (not 404).test_client.py::test_oauth_mcpagainsthttp://localhost:8211/mcp/http/mcpcompletes successfully.MCP_PATH_PREFIXset, existing URLs (/http/mcp,/http/api-key/mcp,/sse) continue to work unchanged.🤖 Generated with Claude Code
Summary by CodeRabbit