makeplane / makeplane/plane-mcp-server
Feature: let the MCP server act as its own authorization server (PAT as the login step), so CE works with header-less connector clients
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 325
- Forks
- 178
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 2
Description
Feature: let the MCP server act as its own authorization server (PAT as the login step), so CE works with header-less connector clients
Summary
Connector-style MCP clients — Claude's "Add custom connector", ChatGPT connectors — accept only a URL plus optional OAuth client ID/secret. There is no field for custom headers. That makes the PAT transport at /http/api-key/mcp structurally unusable for this entire class of client: it needs Authorization: Bearer <pat> and X-Workspace-Slug, and the client has no way to send either.
On Plane CE the OAuth transport at /http/mcp doesn't work either, because it proxies to /auth/o/* endpoints CE doesn't have. So for a self-hosted CE instance the remote-transport matrix is empty:
| Transport | Mount | CE status |
|---|---|---|
| OAuth HTTP | /http/mcp |
upstream /auth/o/* → 404 |
| OAuth SSE | /sse |
same |
| PAT header | /http/api-key/mcp |
works, but unreachable from connector clients (no header field) |
This is the gap I'd like to propose closing inside this repo, without waiting on makeplane/plane#8782.
Evidence
Against a self-hosted CE instance (plane-aio-community:v1.4.1, host genericised):
$ for p in /auth/o/authorize-app/ /auth/o/token/ /auth/o/app-installation/ /api/v1/users/me/; do
printf "%-30s " "$p"; curl -s -o /dev/null -w "%{http_code}\n" "https://plane.example.com$p"
done
/auth/o/authorize-app/ 404
/auth/o/token/ 404
/auth/o/app-installation/ 404
/api/v1/users/me/ 401
$ curl -s -X POST "https://plane.example.com/auth/o/token/" -d 'grant_type=authorization_code&code=x'
{"error": "Page not found."}
The 401 on /api/v1/users/me/ and the Django-shaped JSON 404 confirm the requests reach the API — the routes genuinely don't exist, it isn't a proxy misconfiguration. The CE proxy config forwards /auth/* to the API service, so there is nothing to fix on the deployment side.
For the record, this is unchanged in 0.3.0. Diffing 0.3.0 against 0.2.10: __main__.py, client.py, auth/plane_oauth_provider.py, auth/plane_header_auth_provider.py and storage.py are byte-identical; server.py only factors the shared middleware into _configured(). The 139 → 28 tool consolidation is unrelated to auth.
Proposal
Add a transport where the MCP server is the authorization server, and the login step is the user pasting their own Plane PAT, which the server validates against the instance and exchanges for a per-user, revocable token.
The pieces are already here:
- Protocol plumbing —
fastmcp3.2.0 shipsfastmcp/server/auth/providers/in_memory.py::InMemoryOAuthProviderwith DCR, PKCE, auth codes, refresh and revocation. It is explicitly a test double (in-memory,authorize()auto-approves with no user interaction), so this isn't "just use it" — but the OAuth 2.1 surface doesn't need to be written from scratch. - PAT validation —
PlaneHeaderAuthProvider._validate_api_key()already does exactly the needed check:GET /api/v1/users/me/withx-api-key. - Durable storage —
storage.py::build_token_store()already provides the Redis-backed store used by the OAuth proxy, so issued tokens have somewhere to live. - Redirect allowlist —
DEFAULT_ALLOWED_REDIRECT_URISinserver.pyalready listshttps://claude.ai/*and the ChatGPT connector callbacks.
Missing: a consent/login screen that takes a PAT (plus workspace slug, which today comes from X-Workspace-Slug or installations[0] and has no other source — see client.py::get_plane_client_context), and a provider that persists the PAT↔token binding.
Sketch of the flow:
- Client hits
/http/self/mcp→ 401 withWWW-Authenticate, discovery proceeds against this server's own metadata. - Client registers via DCR (or the operator pastes a client ID/secret).
/authorizerenders a form: Plane PAT + workspace slug.- Server validates the PAT via
/api/v1/users/me/, stores the binding, issues code → token. - Tool calls resolve the caller's own PAT;
get_plane_client_context()already supportsauth_method="api_key_header", so downstream code is unchanged.
Why this and not the alternatives
- Waiting for makeplane/plane#8782 (OAuth apps in CE) — that's the right long-term fix, but it's Plane-side, still unshipped, and even once it lands it won't help anyone on an older CE. This proposal is independent of the Plane version and works against every CE that has
/api/v1/users/me/. - Reverse proxy injecting the headers, with a secret in the URL path — works today (I have it working), but the URL becomes a permanent shared bearer credential: not revocable per user, and every write in Plane is attributed to whichever account owns the injected PAT.
- An external IdP via the bundled
fastmcpproviders (GitHub/Google/WorkOS) — fixes who can reach the endpoint, but the Plane-side identity is still a single shared PAT, so attribution inside Plane is unchanged.
Only "server as AS with the user's own PAT" gives per-user identity and revocation on CE.
Related
- makeplane/plane#8782 — add OAuth app registration to CE (Plane-side fix; assigned, unshipped)
- #131 — HTTP mode requires
PLANE_OAUTH_PROVIDER_*stubs even for PAT-only deployments (the same mount is involved; a self-contained mode would sidestep it) - #171 — is CE a supported target
Environment
plane-mcp-server0.3.0 (and 0.2.10 — identical in the relevant modules)- Plane self-hosted CE,
makeplane/plane-aio-community:v1.4.1 - Client: Claude custom connector
Happy to send a PR if the direction is acceptable — the main thing I'd want steering on is whether a PAT-login AS is welcome in this repo at all, or whether CE auth is considered strictly a Plane-side concern.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with server.py, client.py::get_plane_client_context, PlaneHeaderAuthProvider._validate_api_key(), and storage.py::build_token_store(); then review the relevant fastmcp OAuth provider. Define the consent flow, PAT/workspace validation, durable PAT-to-token binding, and revocation behavior before implementing. Done means header-less connector clients can authenticate against /http/self/mcp with per-user Plane attribution on CE.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, authentication, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100