Summary
When running the server in HTTP transport mode (python -m plane_mcp http), startup unconditionally constructs the OAuth-provider MCP app via get_oauth_mcp(prefix + "/http") in plane_mcp/__main__.py. This forces PlaneOAuthProvider.__init__ to validate three OAuth-provider environment variables even when the operator only intends to expose the PAT/header-based transport that is mounted at /http/api-key/:
PLANE_OAUTH_PROVIDER_CLIENT_ID — raises ValueError if unset
PLANE_OAUTH_PROVIDER_CLIENT_SECRET — raises ValueError if unset
PLANE_OAUTH_PROVIDER_BASE_URL — the resulting issuer URL is validated against validate_issuer_url (from mcp/server/auth/routes.py) and must be https://, otherwise ValueError
For a self-hosted deployment that only needs the PAT-header transport (Section 3 in the README, /http/api-key/mcp), the OAuth provider is never actually exercised, yet the server cannot start without those three variables.
Reproduction
- Set only the PAT-related variables (e.g.
PLANE_API_KEY, PLANE_WORKSPACE_SLUG, plus whatever else the header transport needs).
- Leave
PLANE_OAUTH_PROVIDER_CLIENT_ID, PLANE_OAUTH_PROVIDER_CLIENT_SECRET, and PLANE_OAUTH_PROVIDER_BASE_URL unset (or set the base URL to a non-HTTPS value).
- Start the server:
python -m plane_mcp http.
- Startup aborts before the header app is mounted.
Traceback summary:
File "plane_mcp/__main__.py", line ..., in main
oauth_mcp = get_oauth_mcp(prefix + "/http")
File "plane_mcp/server.py", in get_oauth_mcp
...
File "plane_mcp/auth/plane_oauth_provider.py", in PlaneOAuthProvider.__init__
raise ValueError("client_id is required - set via parameter or PLANE_OAUTH_PROVIDER_CLIENT_ID")
(If client_id/client_secret are stubbed, the next failure is validate_issuer_url rejecting a non-HTTPS PLANE_OAUTH_PROVIDER_BASE_URL.)
Current workaround
Stub all three variables with placeholder values, e.g.:
PLANE_OAUTH_PROVIDER_CLIENT_ID=stub-not-used
PLANE_OAUTH_PROVIDER_CLIENT_SECRET=stub-not-used
PLANE_OAUTH_PROVIDER_BASE_URL=https://example.invalid
This is non-obvious and undocumented; the operator has to read the source to figure it out.
Suggested fixes (options, not a prescription)
- Lazy-init the OAuth provider on first request rather than eagerly at startup, so a deployment that never routes to the OAuth mount never triggers the validation.
- Add an opt-out env var / flag (e.g.
PLANE_DISABLE_OAUTH=1) that skips constructing and mounting the OAuth app entirely, leaving only the PAT-header transport.
- Document the stub-variable workaround in the README section that covers the PAT/header transport, so PAT-only operators at least know what to set.
Any one of these would unblock PAT-only self-hosted deployments. Happy to send a PR for whichever direction the maintainers prefer.
Summary
When running the server in HTTP transport mode (
python -m plane_mcp http), startup unconditionally constructs the OAuth-provider MCP app viaget_oauth_mcp(prefix + "/http")inplane_mcp/__main__.py. This forcesPlaneOAuthProvider.__init__to validate three OAuth-provider environment variables even when the operator only intends to expose the PAT/header-based transport that is mounted at/http/api-key/:PLANE_OAUTH_PROVIDER_CLIENT_ID— raisesValueErrorif unsetPLANE_OAUTH_PROVIDER_CLIENT_SECRET— raisesValueErrorif unsetPLANE_OAUTH_PROVIDER_BASE_URL— the resulting issuer URL is validated againstvalidate_issuer_url(frommcp/server/auth/routes.py) and must behttps://, otherwiseValueErrorFor a self-hosted deployment that only needs the PAT-header transport (Section 3 in the README,
/http/api-key/mcp), the OAuth provider is never actually exercised, yet the server cannot start without those three variables.Reproduction
PLANE_API_KEY,PLANE_WORKSPACE_SLUG, plus whatever else the header transport needs).PLANE_OAUTH_PROVIDER_CLIENT_ID,PLANE_OAUTH_PROVIDER_CLIENT_SECRET, andPLANE_OAUTH_PROVIDER_BASE_URLunset (or set the base URL to a non-HTTPS value).python -m plane_mcp http.Traceback summary:
(If
client_id/client_secretare stubbed, the next failure isvalidate_issuer_urlrejecting a non-HTTPSPLANE_OAUTH_PROVIDER_BASE_URL.)Current workaround
Stub all three variables with placeholder values, e.g.:
This is non-obvious and undocumented; the operator has to read the source to figure it out.
Suggested fixes (options, not a prescription)
PLANE_DISABLE_OAUTH=1) that skips constructing and mounting the OAuth app entirely, leaving only the PAT-header transport.Any one of these would unblock PAT-only self-hosted deployments. Happy to send a PR for whichever direction the maintainers prefer.