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

Open
#201 1 comment 0 reactions 0 assignees View on GitHub

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 plumbingfastmcp 3.2.0 ships fastmcp/server/auth/providers/in_memory.py::InMemoryOAuthProvider with 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 validationPlaneHeaderAuthProvider._validate_api_key() already does exactly the needed check: GET /api/v1/users/me/ with x-api-key.
  • Durable storagestorage.py::build_token_store() already provides the Redis-backed store used by the OAuth proxy, so issued tokens have somewhere to live.
  • Redirect allowlistDEFAULT_ALLOWED_REDIRECT_URIS in server.py already lists https://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:

  1. Client hits /http/self/mcp → 401 with WWW-Authenticate, discovery proceeds against this server's own metadata.
  2. Client registers via DCR (or the operator pastes a client ID/secret).
  3. /authorize renders a form: Plane PAT + workspace slug.
  4. Server validates the PAT via /api/v1/users/me/, stores the binding, issues code → token.
  5. Tool calls resolve the caller's own PAT; get_plane_client_context() already supports auth_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 fastmcp providers (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-server 0.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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.