Skip to content

[SILO-1181] feat: support configurable subpath prefix for MCP server#106

Open
Prashant-Surya wants to merge 1 commit intomainfrom
feat/mcp-path-prefix
Open

[SILO-1181] feat: support configurable subpath prefix for MCP server#106
Prashant-Surya wants to merge 1 commit intomainfrom
feat/mcp-path-prefix

Conversation

@Prashant-Surya
Copy link
Copy Markdown
Member

@Prashant-Surya Prashant-Surya commented Apr 13, 2026

Summary

  • Adds an optional MCP_PATH_PREFIX env var so the HTTP server can be deployed behind an ingress that serves it under a subpath (e.g. /mcp).
  • All three transports — OAuth streamable-http (/http/mcp), header-auth streamable-http (/http/api-key/mcp), and SSE (/sse) — are now mounted under the prefix.
  • Each OAuth provider's base_url is built with the prefix so advertised metadata (issuer, authorize, token, register, and protected-resource URLs) resolves to the externally-visible path.
  • mcp_path for well-known routes stays at /mcp and /sse (relative to base_url) to avoid double-prefixing the advertised resource URL.
  • When MCP_PATH_PREFIX is unset, routing and metadata are identical to before — no behavior change for existing deployments.

Tracks: SILO-1181.

Test plan

  • Start locally with MCP_PATH_PREFIX=/mcp, PLANE_OAUTH_PROVIDER_BASE_URL=http://localhost:8211 (no prefix in env var).
  • Verify GET /.well-known/oauth-authorization-server/mcp/http returns issuer http://localhost:8211/mcp/http and registration endpoint http://localhost:8211/mcp/http/register.
  • Verify GET /.well-known/oauth-authorization-server/mcp (SSE) returns issuer http://localhost:8211/mcp.
  • Verify GET /.well-known/oauth-protected-resource/mcp/http/mcp advertises resource http://localhost:8211/mcp/http/mcp.
  • POST /mcp/http/mcp returns 401 (OAuth challenge) — not 404.
  • POST /mcp/http/api-key/mcp with valid x-workspace-slug + authorization headers initializes successfully.
  • GET /mcp/sse returns 401 (not 404).
  • End-to-end OAuth flow via test_client.py::test_oauth_mcp against http://localhost:8211/mcp/http/mcp completes successfully.
  • Without MCP_PATH_PREFIX set, existing URLs (/http/mcp, /http/api-key/mcp, /sse) continue to work unchanged.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for environment variable configuration of API path prefixes in HTTP mode. This enables flexible customization of OAuth endpoints and route mounting paths, allowing deployment across different infrastructure configurations and supporting advanced deployment scenarios without requiring code changes.

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>
@makeplane
Copy link
Copy Markdown

makeplane Bot commented Apr 13, 2026

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

Added MCP_PATH_PREFIX environment variable support to plane_mcp/__main__.py for HTTP mode. The prefix is computed from the environment variable (defaulting to empty string) and used to construct OAuth endpoints and Starlette route mounting paths, enabling flexible deployment under different path prefixes while adjusting SSE mount behavior.

Changes

Cohort / File(s) Summary
HTTP Path Prefix Configuration
plane_mcp/__main__.py
Added environment variable-driven prefix support for OAuth endpoints and route mounting. Updated OAuth endpoint construction to include prefix, modified Starlette route mounts for header API-key and OAuth HTTP routes to use the prefix, and adjusted SSE mount to handle empty-string prefix cases.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A prefix path comes into play,
Routes mount in a flexible way,
OAuth flows with a customized start,
Environment variables work their art,
Hopping forward with mounting grace! 🌿

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding support for a configurable subpath prefix for the MCP server, which is the core feature introduced in this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/mcp-path-prefix

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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 | 🟡 Minor

Startup log message no longer matches mounted paths.

The message still says /mcp and /header/mcp, but mounts are now under /http/mcp and /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 | 🟠 Major

Normalize MCP_PATH_PREFIX before composing routes.

Raw concatenation can produce malformed paths (for example ///http, mcpmcp/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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: db30d750-6489-470a-b2c3-a821ab3f05b4

📥 Commits

Reviewing files that changed from the base of the PR and between 24565ab and b5ad536.

📒 Files selected for processing (1)
  • plane_mcp/__main__.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant