feat: env-driven HTTP host/port and /healthz for self-hosting#137
feat: env-driven HTTP host/port and /healthz for self-hosting#137Maziak2520 wants to merge 5 commits into
Conversation
- resolve_bind() reads MCP_HOST (default 0.0.0.0) and MCP_PORT (default 8211);
$PORT takes precedence over MCP_PORT so platforms like Cloud Run that inject it
bind the right port (replaces the hardcoded 0.0.0.0:8211).
- Add GET /healthz returning 200 {"status":"ok"} for container/Cloud Run probes.
- stdio, sse and the existing OAuth/header HTTP mounts are unchanged.
- Replace the dead ENV FASTMCP_PORT (ignored by uvicorn.run) with ENV MCP_PORT. - Add docker-compose.example.yml and .env.example for a self-hosted HTTP deployment.
Add a 'Self-hosting the HTTP server' section: env-var table, the OAuth and per-user PAT mounts, Docker/Compose, and Cloud Run notes ($PORT + /healthz).
Covers resolve_bind() incl. $PORT precedence, transport parsing, PLANE_BASE_URL / PLANE_INTERNAL_BASE_URL propagation, the header-auth gate, and /healthz. Network mocked.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a self-hosted HTTP server path: environment-driven bind resolution (PORT precedence), a /healthz endpoint, updated uvicorn startup, configuration examples (.env, Dockerfile, docker-compose), README docs, and tests covering bind logic, auth routing, and the health probe. ChangesSelf-hosted HTTP deployment for Plane MCP Server
Sequence DiagramsequenceDiagram
participant Main as main()
participant ResolveBind as resolve_bind()
participant Starlette as Starlette app
participant Uvicorn as uvicorn
participant Healthz as /healthz handler
Main->>ResolveBind: call resolve_bind()
ResolveBind-->>Main: returns host, port
Main->>Starlette: register Route("/healthz", healthz)
Main->>Uvicorn: run(app, host, port)
Uvicorn->>Healthz: GET /healthz
Healthz-->>Uvicorn: {"status":"ok"}
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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.
Actionable comments posted: 2
🤖 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 `@docker-compose.example.yml`:
- Around line 16-24: Remove the hard-coded MCP_HOST/MCP_PORT values that
override env_file and make the service use Compose variable interpolation:
delete the literal MCP_HOST: 0.0.0.0 and MCP_PORT: "8211" lines, add environment
entries that read from the .env (e.g. MCP_HOST: "${MCP_HOST:-0.0.0.0}" and
MCP_PORT: "${MCP_PORT:-8211}"), change the published port mapping to use the env
var (ports: - "${MCP_PORT:-8211}:${MCP_PORT:-8211}") and update the healthcheck
test to reference the same interpolated port (test: ["CMD", "curl", "-fsS",
"http://localhost:${MCP_PORT:-8211}/healthz"]) so env_file, ports and
healthcheck all honor .env settings.
In `@tests/test_self_hosted_http.py`:
- Around line 79-83: The test
test_header_app_rejects_request_without_workspace_slug is not exercising the
PAT-specific branch because no Authorization header is sent; modify the
TestClient POST to include a Bearer token (e.g., set an Authorization: Bearer
<token> header) while deliberately omitting the X-Workspace-Slug header so the
logic in plane_mcp/auth/plane_header_auth_provider.py that checks for the
workspace slug (lines handling x-workspace-slug) is actually reached and the
response asserts 401/403 as intended.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 80f25845-4140-4351-a90f-28d505e4b280
📒 Files selected for processing (6)
.env.exampleDockerfileREADME.mddocker-compose.example.ymlplane_mcp/__main__.pytests/test_self_hosted_http.py
- compose example: honor .env via ${MCP_PORT}/${MCP_HOST} interpolation for ports,
env, and healthcheck (no longer pinned to 8211).
- header-auth test: send a bearer token + omit X-Workspace-slug so the workspace-slug
gate is actually exercised.
|
Thanks @coderabbitai — both addressed in 1e0c56e:
|
|
Tip For best results, initiate chat on the files or code changes.
Both concerns are fully addressed. 🐇✨ If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
feat: env-driven HTTP host/port + /healthz for self-hosting
Problem
The README documents the HTTP transport only against Plane's hosted
mcp.plane.soendpoints, andthe HTTP server has two gaps that make self-hosting it (in a container / on Cloud Run) awkward:
0.0.0.0:8211in__main__.py. Platforms like Cloud Run inject a$PORTthe container must bind, so the service binds the wrong port and fails health checks. TheDockerfile's
ENV FASTMCP_PORT=8211is dead config (uvicorn.runignores it).What changed
resolve_bind()readsMCP_HOST(default0.0.0.0) andMCP_PORT(default
8211).$PORTtakes precedence overMCP_PORT, so Cloud Run / Heroku-style platforms workout of the box. Defaults match the previous hardcoded values, so nothing changes if you set nothing.
GET /healthz→200 {"status":"ok"}, mounted in HTTP mode for startup/liveness probes.ENV FASTMCP_PORT=8211withENV MCP_PORT=8211.and per-user PAT mounts, Docker/Compose, Cloud Run notes), plus
.env.exampleanddocker-compose.example.yml.Backward compatibility
stdio,sse, and the existing OAuth (/http/mcp) and header-PAT (/http/api-key/mcp) mounts areunchanged. The only behavioural change in the default path is reading
MCP_HOST/MCP_PORT/$PORT(all defaulting to the previous
0.0.0.0:8211) and adding the/healthzroute.server.pyisuntouched.
How it was tested
New
tests/test_self_hosted_http.py(network mocked):resolve_bind()including$PORTprecedence,transport parsing,
PLANE_BASE_URL/PLANE_INTERNAL_BASE_URLpropagation to the client, theheader-auth gate still returning 401/403 without credentials, and
/healthzreturning 200. Ranend-to-end locally in HTTP mode (healthz 200; OAuth/header mounts respond as before;
$PORTprecedenceconfirmed).
Why it matters
Makes the HTTP server deployable as a self-hosted remote MCP service on containers / Cloud Run with no
code changes — relevant to #93 (deployment setup) and #77 (container image). Per-user identity is
preserved via the existing OAuth and PAT-header mounts.
Summary by CodeRabbit
New Features
Documentation
Tests