Skip to content

Self-host the MCP server ​

This guide is for teams that want to run their own instance of the Plane MCP server — either because they use a self-hosted Plane installation that needs OAuth against their own domain, or because they want full control over the MCP infrastructure.

If you're a Plane Cloud user connecting to mcp.plane.so, you don't need this. Use the MCP server setup guide instead.

Prerequisites ​

  • A running Plane instance (self-hosted or Cloud) with workspace admin access. OAuth application registration is available on Plane Cloud and Plane Commercial Edition; Plane Community Edition does not include it, so the OAuth transport cannot be used against a Community Edition instance. Community Edition users should run the server in local (stdio) mode with a personal access token instead.
  • Docker and Docker Compose v2+, or Kubernetes v1.21+ with Helm v3+
  • A public URL for the MCP server (e.g. https://mcp.yourdomain.com) — OAuth callbacks must reach it over HTTPS

Register an OAuth app in Plane ​

The MCP server authenticates users through Plane's OAuth 2.0 system. You need to register an app to get a Client ID and Client Secret.

  1. Go to Workspace settings → Integrations:

    text
    https://<your-plane-domain>/<workspace>/settings/integrations/
  2. Click Build your own.

  3. Fill in the application details:

    FieldValue
    App NameAnything descriptive (e.g. Plane MCP Server)
    Setup URLYour MCP server's public URL (e.g. https://mcp.yourdomain.com)
    Redirect URIBoth URIs listed below, space-separated
    Webhook URLLeave empty unless you need webhook events

    Add both redirect URIs

    FastMCP exposes one callback under the HTTP mount and one under the SSE mount:

    TransportRedirect URI
    Streamable HTTP<MCP_SERVER_URL>/http/auth/callback
    SSE (deprecated)<MCP_SERVER_URL>/auth/callback

    For https://mcp.yourdomain.com, paste this into the Redirect URI field:

    text
    https://mcp.yourdomain.com/http/auth/callback https://mcp.yourdomain.com/auth/callback

    A previously registered https://mcp.yourdomain.com/callback URI is harmless but unnecessary.

  4. Under Scopes & permissions, select both read and write scopes.

  5. Save. Copy the generated Client ID and Client Secret - you'll need them in the next step.

WARNING

Never expose the Client Secret in client-side code or commit it to version control.

For more detail on OAuth app creation, see Create an OAuth Application.


Deploy ​

Option A: Docker Compose ​

1. Create a docker-compose.yaml:

yaml
name: plane-mcp

services:
  mcp:
    image: makeplane/plane-mcp-server:${APP_RELEASE_VERSION:-latest}
    restart: always
    ports:
      - "8211:8211"
    env_file:
      - variables.env
    environment:
      REDIS_HOST: valkey
      REDIS_PORT: "6379"
    depends_on:
      valkey:
        condition: service_healthy

  valkey:
    image: valkey/valkey:8-alpine
    restart: always
    volumes:
      - valkey-data:/data
    healthcheck:
      test: ["CMD", "valkey-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  valkey-data:

2. Create a variables.env with your OAuth credentials from Step 1:

env
# Image tag - pin to a specific version in production
APP_RELEASE_VERSION=latest

# Plane API URL - use your self-hosted instance URL or https://api.plane.so for Cloud
PLANE_BASE_URL=https://api.plane.so

# Optional: internal URL for server-to-server calls (same-network setups)
# PLANE_INTERNAL_BASE_URL=

# OAuth credentials from Step 1
PLANE_OAUTH_PROVIDER_CLIENT_ID=your-client-id
PLANE_OAUTH_PROVIDER_CLIENT_SECRET=your-client-secret

# Public URL where MCP clients reach this server (must match what you registered in Step 1)
PLANE_OAUTH_PROVIDER_BASE_URL=https://mcp.yourdomain.com

3. Start:

bash
docker compose up -d

4. Verify:

bash
docker compose logs -f mcp           # follow startup logs
curl http://localhost:8211/http/mcp  # expect: 401 or MCP protocol response

Terminate TLS in front of this container

The container listens on plain HTTP at :8211. Put it behind a reverse proxy (nginx, Caddy, Traefik, Cloudflare) that handles TLS. OAuth callbacks will fail without HTTPS, and PLANE_OAUTH_PROVIDER_BASE_URL must be the https:// URL that proxy exposes.

Environment variable reference ​

VariableRequiredDescription
APP_RELEASE_VERSIONNoImage tag to deploy. Defaults to latest. Pin in production.
PLANE_BASE_URLNoPublic Plane API URL. Defaults to https://api.plane.so.
PLANE_INTERNAL_BASE_URLNoInternal Plane URL for server-to-server calls. Falls back to PLANE_BASE_URL.
PLANE_OAUTH_PROVIDER_CLIENT_IDYesOAuth Client ID from Step 1.
PLANE_OAUTH_PROVIDER_CLIENT_SECRETYesOAuth Client Secret from Step 1.
PLANE_OAUTH_PROVIDER_BASE_URLYesPublic URL of this MCP server, not your Plane instance.
PLANE_OAUTH_PROVIDER_ENABLE_CIMDNoEnables client ID metadata documents. Defaults to false.
PLANE_OAUTH_ALLOWED_REDIRECT_URISNoComma-separated extra client redirect patterns. * can match a port, path segment, or subdomain; keep hosts pinned.
MCP_PATH_PREFIXNoPrefix for every route. For example, /plane serves MCP at /plane/http/mcp.
REDIS_HOSTNoRedis or Valkey host for persistent OAuth token storage. Without it, tokens use in-memory storage.
REDIS_PORTNoRedis or Valkey port.
REDIS_PASSWORDNoStatic Redis or Valkey password.
REDIS_SSLNoEnables TLS for Redis or Valkey when set to true.
ELASTICACHE_SECRET_ARNNoAWS Secrets Manager ARN containing a rotating ElastiCache authentication token.
AWS_REGIONNoAWS region for ELASTICACHE_SECRET_ARN.
REDIS_AUTH_TOKEN_KEYNoJSON key that contains the rotating token in the AWS secret.
LOG_USER_INFONoLogs the user's display name when true. Defaults to false; the display name is PII.

Onboard a new MCP client ​

The built-in redirect allowlist contains:

  • http://localhost:*, http://localhost:*/*, http://127.0.0.1:*, and http://127.0.0.1:*/*
  • cursor://anysphere.cursor-mcp/oauth/* and https://www.cursor.com/*
  • https://vscode.dev/redirect and https://insiders.vscode.dev/redirect
  • https://antigravity.google/oauth-callback
  • https://claude.ai/*
  • https://chatgpt.com/connector/oauth/* and https://chatgpt.com/connector_platform_oauth_redirect

Append new client callbacks without releasing a new server version:

env
PLANE_OAUTH_ALLOWED_REDIRECT_URIS=https://newclient.com/cb,https://other.app/oauth/*

The * wildcard can match any port, path segment, or subdomain. Keep the host pinned to a domain you trust.

Upgrading ​

bash
docker compose pull
docker compose up -d

Option B: Helm ​

1. Add the Plane Helm repo:

bash
helm repo add plane https://helm.plane.so
helm repo update

2. Create a values.yaml:

yaml
ingress:
  enabled: true
  host: mcp.yourdomain.com
  ingressClass: nginx
  ssl:
    enabled: true
    issuer: cloudflare # cloudflare | digitalocean | http
    email: you@yourdomain.com

services:
  api:
    plane_base_url: "https://api.plane.so"
    plane_oauth:
      enabled: true
      client_id: "<your-oauth-client-id>"
      client_secret: "<your-oauth-client-secret>"
      provider_base_url: "https://mcp.yourdomain.com"

3. Install:

bash
helm install plane-mcp plane/plane-mcp-server \
  --namespace plane-mcp \
  --create-namespace \
  -f values.yaml

Helm values reference ​

ValueDefaultDescription
dockerRegistry.default_taglatestImage tag to deploy
ingress.enabledtrueEnable ingress
ingress.hostmcp.example.comPublic hostname
ingress.ingressClassnginxIngress class name
ingress.ssl.enabledfalseEnable TLS via cert-manager
ingress.ssl.issuercloudflareACME issuer (cloudflare, digitalocean, http)
services.api.replicas1Number of MCP server replicas
services.api.plane_base_url""Plane API URL
services.api.plane_oauth.enabledfalseEnable OAuth endpoints
services.api.plane_oauth.client_id""OAuth Client ID
services.api.plane_oauth.client_secret""OAuth Client Secret
services.api.plane_oauth.provider_base_url""Public URL this server is reachable on
services.redis.local_setuptrueDeploy Valkey in-cluster
services.redis.external_redis_url""External Valkey/Redis URL (if not using in-cluster)

Environment variables that have no Helm value — for example PLANE_OAUTH_ALLOWED_REDIRECT_URIS or LOG_USER_INFO — must be set as environment variables on the MCP server deployment.

Upgrading ​

bash
helm upgrade plane-mcp plane/plane-mcp-server \
  --namespace plane-mcp \
  -f values.yaml

Uninstalling ​

bash
helm uninstall plane-mcp --namespace plane-mcp

Logging and observability ​

The server emits structured JSON logs with the tool name, duration, status, opaque user ID, and workspace slug.

LOG_USER_INFO defaults to false. Setting it to true also logs the user's display name, which is personally identifiable information.

Even with LOG_USER_INFO=false, log entries contain the opaque user ID and the workspace slug, which can identify a person or organisation when combined with other data. Treat log storage as sensitive: restrict who can read it, set a retention period, and redact those fields before sharing logs outside your team.

Connect AI clients ​

Once the server is running, your available endpoints are:

EndpointAuthDescription
https://mcp.yourdomain.com/http/mcpOAuthRecommended for most clients
https://mcp.yourdomain.com/http/api-key/mcpAuthorization: Bearer <PAT>, x-workspace-slug: <slug>CI, scripts, and headless setups
https://mcp.yourdomain.com/sseOAuthDeprecated HTTP+SSE transport

Client configuration is identical to the MCP server setup guide. Swap https://mcp.plane.so for your server's host in each configuration.


Troubleshooting ​

Server not starting:

bash
docker compose logs mcp

Valkey not reachable:

bash
docker compose exec valkey valkey-cli ping
# Expect: PONG

If Valkey is unhealthy, tokens are stored in-memory and lost on restart. Verify REDIS_HOST and REDIS_PORT are set correctly in your environment.

OAuth errors:

  • Confirm both redirect URIs are registered in your Plane OAuth app: /http/auth/callback and /auth/callback. An existing /callback registration is harmless but unnecessary.

  • Check that PLANE_OAUTH_PROVIDER_CLIENT_ID and PLANE_OAUTH_PROVIDER_CLIENT_SECRET match what Plane generated.

  • Check that PLANE_OAUTH_PROVIDER_BASE_URL is the publicly reachable https:// URL of this MCP server - not your Plane instance URL.

  • If the client reports redirect_uri is not allowed, add its exact callback or a host-pinned pattern to PLANE_OAUTH_ALLOWED_REDIRECT_URIS, then restart the deployment.

  • Clear any cached auth tokens on the client side:

    bash
    rm -rf ~/.mcp-auth

Reset Docker Compose (deletes Valkey data):

bash
docker compose down -v
docker compose up -d

Still stuck:

  1. Double-check OAuth credentials and redirect URIs in Plane workspace settings.
  2. Check the plane-mcp-server repo for known issues.
  3. Contact support@plane.so.

→ For client configuration details, see the MCP server setup guide. → For the full list of available tools, see the tool reference.