Skip to content

Self-host MCP Server

This guide is for teams that want to run their own instance of the Plane MCP Server - either because they're on 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
  • 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 be able to 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:

    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 URIAll three URIs listed below, space-separated
    Webhook URLLeave empty unless you need webhook events

    Add all three redirect URIs

    The server exposes callbacks on three paths to support all transports and MCP clients:

    TransportRedirect URI
    OAuth callback<MCP_SERVER_URL>/callback
    HTTP with OAuth<MCP_SERVER_URL>/http/auth/callback
    SSE (legacy)<MCP_SERVER_URL>/auth/callback

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

    https://mcp.yourdomain.com/callback https://mcp.yourdomain.com/http/auth/callback https://mcp.yourdomain.com/auth/callback
  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_URLNoPlane 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.
MCP_PATH_PREFIXNoPath prefix for all routes. Use when reverse-proxying alongside other apps.

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)

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

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/mcpPAT headerFor CI/CD, scripts, headless setups
https://mcp.yourdomain.com/sseOAuthLegacy SSE transport (deprecated)

Client configuration is identical to the MCP Server setup guide - replace https://mcp.plane.so with your server's URL in every config snippet.


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 all three redirect URIs are registered in your Plane OAuth app: /callback, /http/auth/callback, /auth/callback.

  • 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.

  • 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 MCP Server Tool Reference.