From 40cb303a0171bafa345a67d9b1b787f3f67caca7 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:16:46 +0530 Subject: [PATCH 01/13] Add docker-compose.yml for local development Adds a dev-focused Docker Compose setup with: - mcp service: builds from local Dockerfile, mounts source for editable install so code changes take effect on container restart without rebuild - redis service: Redis 7 alpine for OAuth token storage - .env.docker template with all required environment variables - .env.docker.local added to .gitignore for local secret overrides Co-Authored-By: Claude Sonnet 4.6 --- .env.docker | 25 ++++++++++++++++++++++++ .gitignore | 1 + docker-compose.yml | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 .env.docker create mode 100644 docker-compose.yml diff --git a/.env.docker b/.env.docker new file mode 100644 index 0000000..a112cc7 --- /dev/null +++ b/.env.docker @@ -0,0 +1,25 @@ +# Plane MCP Server — Docker development environment template +# Copy this file to .env and fill in your values: +# cp .env.docker .env + +# ------------------------------------------------------------------- +# Plane API +# ------------------------------------------------------------------- +PLANE_BASE_URL=https://api.plane.so + +# Internal URL for server-to-server calls (optional; leave blank for cloud) +PLANE_INTERNAL_BASE_URL= + +# ------------------------------------------------------------------- +# OAuth Provider (required for OAuth flow via /mcp) +# ------------------------------------------------------------------- +PLANE_OAUTH_PROVIDER_CLIENT_ID= +PLANE_OAUTH_PROVIDER_CLIENT_SECRET= +# Should match the public URL your MCP clients reach the server on +PLANE_OAUTH_PROVIDER_BASE_URL=http://localhost:8211 + +# ------------------------------------------------------------------- +# Redis — set automatically by docker-compose; override only if needed +# ------------------------------------------------------------------- +# REDIS_HOST=redis +# REDIS_PORT=6379 diff --git a/.gitignore b/.gitignore index 88f9cb1..0a3ace7 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ dmypy.json .env .env.local .env.test.local +.env.docker.local # Ignore cursor AI rules .cursor/rules/codacy.mdc diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..47a5dc3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,47 @@ +# Docker Compose for local development +# +# Setup: +# cp .env.docker .env # copy the template +# # edit .env with your values +# docker compose up --build +# +# The MCP server runs in HTTP mode and is available at: +# http://localhost:8211/mcp (OAuth) +# http://localhost:8211/http/api-key/mcp (header API key) + +name: plane-mcp-dev + +services: + mcp: + build: + context: . + dockerfile: Dockerfile + # Install in editable mode so volume-mounted source changes take effect on restart + command: > + sh -c "uv pip install --system -e '.[dev]' && python -m plane_mcp http" + ports: + - "8211:8211" + volumes: + # Mount local source so code changes don't require a full rebuild + - ./plane_mcp:/app/plane_mcp + env_file: + - .env + environment: + # Redis is wired automatically via the redis service below + REDIS_HOST: redis + REDIS_PORT: "6379" + depends_on: + redis: + condition: service_healthy + restart: unless-stopped + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + restart: unless-stopped From 67f3e9433d3817eb061ca69ac6c8f36b4c5f365e Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:25:06 +0530 Subject: [PATCH 02/13] Add deploy folder, production docker-compose, and README local dev docs - deploy/docker-compose.yaml: production setup using published image (no build), with Redis volume for persistent token storage - deploy/variables.env: production env template with all required variables - README: add Self-Hosting and Local Development sections covering Docker Compose, standalone uv setup, tests, and linting - .gitignore: ignore deploy/variables.env.local for local secret overrides Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + README.md | 74 +++++++++++++++++++++++++++++++++++--- deploy/docker-compose.yaml | 37 +++++++++++++++++++ deploy/variables.env | 30 ++++++++++++++++ 4 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 deploy/docker-compose.yaml create mode 100644 deploy/variables.env diff --git a/.gitignore b/.gitignore index 0a3ace7..97b656a 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ dmypy.json .env.local .env.test.local .env.docker.local +deploy/variables.env.local # Ignore cursor AI rules .cursor/rules/codacy.mdc diff --git a/README.md b/README.md index 24392f9..54f48fc 100644 --- a/README.md +++ b/README.md @@ -226,18 +226,84 @@ The server provides comprehensive tools for interacting with Plane. All tools us **Total Tools**: 55+ tools across 8 categories -## Development +## Self-Hosting + +Use the production compose setup in the `deploy/` folder. It pulls the published image — no build required. + +```bash +cd deploy +# edit variables.env with your values +docker compose up -d +``` + +The server will be available at: +- `http://localhost:8211/mcp` — OAuth endpoint +- `http://localhost:8211/http/api-key/mcp` — PAT / header API key endpoint + +To pin a specific version, set `APP_RELEASE_VERSION` in `variables.env`: +``` +APP_RELEASE_VERSION=v0.2.8 +``` + +--- + +## Local Development + +### Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose +- [uv](https://docs.astral.sh/uv/) (for running outside Docker) + +### Running with Docker Compose (recommended) + +```bash +# 1. Clone the repo +git clone https://github.com/makeplane/plane-mcp-server.git +cd plane-mcp-server + +# 2. Create your local env file +cp .env.docker .env +# Edit .env and fill in PLANE_OAUTH_PROVIDER_* values + +# 3. Start the server and Redis +docker compose up --build +``` + +The server starts in HTTP mode at `http://localhost:8211`. +Source code is mounted into the container — changes to `plane_mcp/` take effect on container restart (`docker compose restart mcp`). + +### Running without Docker + +```bash +# Install dependencies +uv pip install -e ".[dev]" + +# Stdio mode (simplest — no Redis or OAuth needed) +export PLANE_API_KEY=your-api-key +export PLANE_WORKSPACE_SLUG=your-workspace-slug +python -m plane_mcp stdio + +# HTTP mode +export PLANE_OAUTH_PROVIDER_CLIENT_ID=... +export PLANE_OAUTH_PROVIDER_CLIENT_SECRET=... +export PLANE_OAUTH_PROVIDER_BASE_URL=http://localhost:8211 +python -m plane_mcp http +``` ### Running Tests ```bash -pytest +# Copy and fill in test env +cp .env.test .env.test.local +# Edit .env.test.local with your Plane API key and workspace slug + +export $(cat .env.test.local | xargs) && pytest tests/ -v ``` -### Code Formatting +### Code Formatting & Linting ```bash -black plane_mcp/ +ruff format plane_mcp/ ruff check plane_mcp/ ``` diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml new file mode 100644 index 0000000..2c13f87 --- /dev/null +++ b/deploy/docker-compose.yaml @@ -0,0 +1,37 @@ +# Plane MCP Server — Production Deployment +# +# Setup: +# cp variables.env.example variables.env # copy the template +# # edit variables.env with your values +# docker compose up -d + +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: redis + REDIS_PORT: "6379" + depends_on: + redis: + condition: service_healthy + + redis: + image: redis:7-alpine + restart: always + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + +volumes: + redis-data: diff --git a/deploy/variables.env b/deploy/variables.env new file mode 100644 index 0000000..44ea76a --- /dev/null +++ b/deploy/variables.env @@ -0,0 +1,30 @@ +# Plane MCP Server — Production Environment Variables +# Edit this file with your values before running docker compose up + +# ------------------------------------------------------------------- +# Release +# ------------------------------------------------------------------- +APP_RELEASE_VERSION=latest + +# ------------------------------------------------------------------- +# Plane API +# ------------------------------------------------------------------- +PLANE_BASE_URL=https://api.plane.so + +# Internal URL for server-to-server calls (optional) +# Use this if the MCP server and Plane API are on the same network +PLANE_INTERNAL_BASE_URL= + +# ------------------------------------------------------------------- +# OAuth Provider (required for OAuth flow via /mcp) +# ------------------------------------------------------------------- +PLANE_OAUTH_PROVIDER_CLIENT_ID= +PLANE_OAUTH_PROVIDER_CLIENT_SECRET= +# Public URL your MCP clients reach the server on +PLANE_OAUTH_PROVIDER_BASE_URL=http://localhost:8211 + +# ------------------------------------------------------------------- +# Redis — managed by docker-compose, override only if using external Redis +# ------------------------------------------------------------------- +# REDIS_HOST=redis +# REDIS_PORT=6379 From cb0f380596dc3cea4606236decf5c290d0b7f421 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:26:03 +0530 Subject: [PATCH 03/13] Rename deploy/ to deployments/ Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 2 +- README.md | 4 ++-- {deploy => deployments}/docker-compose.yaml | 0 {deploy => deployments}/variables.env | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename {deploy => deployments}/docker-compose.yaml (100%) rename {deploy => deployments}/variables.env (100%) diff --git a/.gitignore b/.gitignore index 97b656a..8f85fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,7 @@ dmypy.json .env.local .env.test.local .env.docker.local -deploy/variables.env.local +deployments/variables.env.local # Ignore cursor AI rules .cursor/rules/codacy.mdc diff --git a/README.md b/README.md index 54f48fc..7d49699 100644 --- a/README.md +++ b/README.md @@ -228,10 +228,10 @@ The server provides comprehensive tools for interacting with Plane. All tools us ## Self-Hosting -Use the production compose setup in the `deploy/` folder. It pulls the published image — no build required. +Use the production compose setup in the `deployments/` folder. It pulls the published image — no build required. ```bash -cd deploy +cd deployments # edit variables.env with your values docker compose up -d ``` diff --git a/deploy/docker-compose.yaml b/deployments/docker-compose.yaml similarity index 100% rename from deploy/docker-compose.yaml rename to deployments/docker-compose.yaml diff --git a/deploy/variables.env b/deployments/variables.env similarity index 100% rename from deploy/variables.env rename to deployments/variables.env From b68deb1b7e8884f1d8131b992d1f96aeecb05355 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:27:15 +0530 Subject: [PATCH 04/13] Add deployments/README.md with Docker Compose and Helm Chart (stub) docs Co-Authored-By: Claude Sonnet 4.6 --- deployments/README.md | 91 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 deployments/README.md diff --git a/deployments/README.md b/deployments/README.md new file mode 100644 index 0000000..9fc1eec --- /dev/null +++ b/deployments/README.md @@ -0,0 +1,91 @@ +# Plane MCP Server — Production Deployment + +This folder contains production deployment configurations for the Plane MCP Server. + +> **Note**: These setups use the published Docker image. For local development, see the [Local Development](../README.md#local-development) section in the root README. + +--- + +## Option 1: Docker Compose + +### Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) and Docker Compose v2+ + +### Setup + +```bash +cd deployments + +# 1. Edit variables.env with your values +# (fill in OAuth credentials and Plane API URL) +vi variables.env + +# 2. Start the server +docker compose up -d + +# 3. Check logs +docker compose logs -f mcp +``` + +### Services + +| Service | Port | Description | +|---------|------|-------------| +| `mcp` | `8211` | Plane MCP Server (HTTP mode) | +| `redis` | — | Token storage for OAuth (internal only) | + +### Endpoints + +| Endpoint | Auth | Description | +|----------|------|-------------| +| `http://:8211/mcp` | OAuth | OAuth-based MCP endpoint | +| `http://:8211/http/api-key/mcp` | PAT header | Personal Access Token endpoint | +| `http://:8211/sse` | OAuth | Legacy SSE endpoint (deprecated) | + +### Configuration + +All configuration is done via `variables.env`. Key variables: + +| Variable | Required | Description | +|----------|----------|-------------| +| `APP_RELEASE_VERSION` | No | Image tag to deploy (default: `latest`) | +| `PLANE_BASE_URL` | No | Plane API URL (default: `https://api.plane.so`) | +| `PLANE_INTERNAL_BASE_URL` | No | Internal API URL for server-to-server calls | +| `PLANE_OAUTH_PROVIDER_CLIENT_ID` | Yes | OAuth client ID | +| `PLANE_OAUTH_PROVIDER_CLIENT_SECRET` | Yes | OAuth client secret | +| `PLANE_OAUTH_PROVIDER_BASE_URL` | Yes | Public URL the server is reachable on | + +### Upgrading + +```bash +# Pull the latest image and restart +docker compose pull +docker compose up -d +``` + +--- + +## Option 2: Helm Chart + +> 🚧 Coming soon. + +--- + +## Troubleshooting + +**Server not starting?** +```bash +docker compose logs mcp +``` + +**Redis connection issues?** +```bash +docker compose exec redis redis-cli ping +``` + +**Reset and start fresh:** +```bash +docker compose down -v # removes Redis volume too +docker compose up -d +``` From af806f70e2836ac8707863bb79cd803898881fa0 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:29:50 +0530 Subject: [PATCH 05/13] Replace Redis with Valkey; remove exposed Valkey port - Switch to valkey/valkey:8-alpine in dev and prod compose files - Remove exposed port for Valkey in both (internal only) - Update README troubleshooting and services table accordingly Co-Authored-By: Claude Sonnet 4.6 --- deployments/README.md | 6 +++--- deployments/docker-compose.yaml | 14 +++++++------- docker-compose.yml | 14 ++++++-------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/deployments/README.md b/deployments/README.md index 9fc1eec..641c0ab 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -33,7 +33,7 @@ docker compose logs -f mcp | Service | Port | Description | |---------|------|-------------| | `mcp` | `8211` | Plane MCP Server (HTTP mode) | -| `redis` | — | Token storage for OAuth (internal only) | +| `valkey` | — | Token storage for OAuth (internal only) | ### Endpoints @@ -79,9 +79,9 @@ docker compose up -d docker compose logs mcp ``` -**Redis connection issues?** +**Valkey connection issues?** ```bash -docker compose exec redis redis-cli ping +docker compose exec valkey valkey-cli ping ``` **Reset and start fresh:** diff --git a/deployments/docker-compose.yaml b/deployments/docker-compose.yaml index 2c13f87..0a14479 100644 --- a/deployments/docker-compose.yaml +++ b/deployments/docker-compose.yaml @@ -16,22 +16,22 @@ services: env_file: - variables.env environment: - REDIS_HOST: redis + REDIS_HOST: valkey REDIS_PORT: "6379" depends_on: - redis: + valkey: condition: service_healthy - redis: - image: redis:7-alpine + valkey: + image: valkey/valkey:8-alpine restart: always volumes: - - redis-data:/data + - valkey-data:/data healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD", "valkey-cli", "ping"] interval: 5s timeout: 3s retries: 5 volumes: - redis-data: + valkey-data: diff --git a/docker-compose.yml b/docker-compose.yml index 47a5dc3..b2b703a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,20 +27,18 @@ services: env_file: - .env environment: - # Redis is wired automatically via the redis service below - REDIS_HOST: redis + # Valkey is wired automatically via the valkey service below + REDIS_HOST: valkey REDIS_PORT: "6379" depends_on: - redis: + valkey: condition: service_healthy restart: unless-stopped - redis: - image: redis:7-alpine - ports: - - "6379:6379" + valkey: + image: valkey/valkey:8-alpine healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD", "valkey-cli", "ping"] interval: 5s timeout: 3s retries: 5 From 99fa5dd366e7838a26750981679807854f7fe116 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:31:23 +0530 Subject: [PATCH 06/13] Fix Dockerfile: install uv via pip instead of ghcr.io multi-stage copy COPY --from=ghcr.io/astral-sh/uv:latest fails when GHCR is inaccessible. Switch to pip install uv which works without registry auth. Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 97e65f9..ce7707d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,13 +4,8 @@ FROM python:3.11-slim # Set working directory WORKDIR /app -# Install system dependencies -RUN apt-get update && apt-get install -y \ - curl \ - && rm -rf /var/lib/apt/lists/* - # Install uv for faster package management -COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv +RUN pip install --no-cache-dir uv # Copy dependency files and application code COPY pyproject.toml ./ From 7e3035fda013cf2d232ea67f357cc00c7d809f41 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:41:39 +0530 Subject: [PATCH 07/13] Make OAuth provider optional in HTTP mode If PLANE_OAUTH_PROVIDER_CLIENT_ID / CLIENT_SECRET are not set, the server now starts successfully with only the header API-key endpoint available. OAuth and SSE routes are skipped with a warning log instead of crashing. Co-Authored-By: Claude Sonnet 4.6 --- plane_mcp/__main__.py | 49 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/plane_mcp/__main__.py b/plane_mcp/__main__.py index d943d90..569dccd 100644 --- a/plane_mcp/__main__.py +++ b/plane_mcp/__main__.py @@ -87,27 +87,44 @@ def main() -> None: return if server_mode == ServerMode.HTTP: - oauth_mcp = get_oauth_mcp("/http") - oauth_app = oauth_mcp.http_app(stateless_http=True) - header_app = get_header_mcp().http_app(stateless_http=True) - - sse_mcp = get_oauth_mcp() - sse_app = sse_mcp.http_app(transport="sse") + oauth_enabled = bool( + os.getenv("PLANE_OAUTH_PROVIDER_CLIENT_ID") and os.getenv("PLANE_OAUTH_PROVIDER_CLIENT_SECRET") + ) - oauth_well_known = oauth_mcp.auth.get_well_known_routes(mcp_path="/mcp") - sse_well_known = sse_mcp.auth.get_well_known_routes(mcp_path="/sse") + if oauth_enabled: + logger.info("OAuth credentials found — enabling OAuth and SSE endpoints") + else: + logger.warning( + "PLANE_OAUTH_PROVIDER_CLIENT_ID / CLIENT_SECRET not set — " + "OAuth and SSE endpoints disabled. Only header API-key endpoint will be available." + ) - app = Starlette( - routes=[ - # Well-known routes for OAuth and Header HTTP - *oauth_well_known, - *sse_well_known, - # Mount both MCP servers + header_app = get_header_mcp().http_app(stateless_http=True) + routes = [Mount("/http/api-key", app=header_app)] + + if oauth_enabled: + oauth_mcp = get_oauth_mcp("/http") + oauth_app = oauth_mcp.http_app(stateless_http=True) + sse_mcp = get_oauth_mcp() + sse_app = sse_mcp.http_app(transport="sse") + routes = [ + *oauth_mcp.auth.get_well_known_routes(mcp_path="/mcp"), + *sse_mcp.auth.get_well_known_routes(mcp_path="/sse"), Mount("/http/api-key", app=header_app), Mount("/http", app=oauth_app), Mount("/", app=sse_app), - ], - lifespan=lambda app: combined_lifespan(oauth_app, header_app, sse_app), + ] + + async def _lifespan(app): + if oauth_enabled: + async with combined_lifespan(oauth_app, header_app, sse_app): + yield + else: + yield + + app = Starlette( + routes=routes, + lifespan=_lifespan, ) app.add_middleware( From d9d503b3283717e0708fa92f94323a559d37c36e Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:42:26 +0530 Subject: [PATCH 08/13] Revert "Make OAuth provider optional in HTTP mode" This reverts commit 7e3035fda013cf2d232ea67f357cc00c7d809f41. --- plane_mcp/__main__.py | 49 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/plane_mcp/__main__.py b/plane_mcp/__main__.py index 569dccd..d943d90 100644 --- a/plane_mcp/__main__.py +++ b/plane_mcp/__main__.py @@ -87,44 +87,27 @@ def main() -> None: return if server_mode == ServerMode.HTTP: - oauth_enabled = bool( - os.getenv("PLANE_OAUTH_PROVIDER_CLIENT_ID") and os.getenv("PLANE_OAUTH_PROVIDER_CLIENT_SECRET") - ) + oauth_mcp = get_oauth_mcp("/http") + oauth_app = oauth_mcp.http_app(stateless_http=True) + header_app = get_header_mcp().http_app(stateless_http=True) - if oauth_enabled: - logger.info("OAuth credentials found — enabling OAuth and SSE endpoints") - else: - logger.warning( - "PLANE_OAUTH_PROVIDER_CLIENT_ID / CLIENT_SECRET not set — " - "OAuth and SSE endpoints disabled. Only header API-key endpoint will be available." - ) + sse_mcp = get_oauth_mcp() + sse_app = sse_mcp.http_app(transport="sse") - header_app = get_header_mcp().http_app(stateless_http=True) - routes = [Mount("/http/api-key", app=header_app)] - - if oauth_enabled: - oauth_mcp = get_oauth_mcp("/http") - oauth_app = oauth_mcp.http_app(stateless_http=True) - sse_mcp = get_oauth_mcp() - sse_app = sse_mcp.http_app(transport="sse") - routes = [ - *oauth_mcp.auth.get_well_known_routes(mcp_path="/mcp"), - *sse_mcp.auth.get_well_known_routes(mcp_path="/sse"), + oauth_well_known = oauth_mcp.auth.get_well_known_routes(mcp_path="/mcp") + sse_well_known = sse_mcp.auth.get_well_known_routes(mcp_path="/sse") + + app = Starlette( + routes=[ + # Well-known routes for OAuth and Header HTTP + *oauth_well_known, + *sse_well_known, + # Mount both MCP servers Mount("/http/api-key", app=header_app), Mount("/http", app=oauth_app), Mount("/", app=sse_app), - ] - - async def _lifespan(app): - if oauth_enabled: - async with combined_lifespan(oauth_app, header_app, sse_app): - yield - else: - yield - - app = Starlette( - routes=routes, - lifespan=_lifespan, + ], + lifespan=lambda app: combined_lifespan(oauth_app, header_app, sse_app), ) app.add_middleware( From e141ef63354bef90e8340bf8abe05cb9bc67b6c2 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:44:24 +0530 Subject: [PATCH 09/13] Fix dev docker-compose: use entrypoint override instead of command command appends to the Dockerfile ENTRYPOINT, causing python -m plane_mcp to receive 'sh' as the server mode argument. Use entrypoint override instead. Co-Authored-By: Claude Sonnet 4.6 --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index b2b703a..19762bf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,9 +16,9 @@ services: build: context: . dockerfile: Dockerfile - # Install in editable mode so volume-mounted source changes take effect on restart - command: > - sh -c "uv pip install --system -e '.[dev]' && python -m plane_mcp http" + # Override entrypoint to install in editable mode before starting the server + # (command would be appended to the Dockerfile ENTRYPOINT, causing arg parse errors) + entrypoint: ["sh", "-c", "uv pip install --system -e '.[dev]' && python -m plane_mcp http"] ports: - "8211:8211" volumes: From e2f661b8f2b4d4662f817789507497c996ae873c Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:47:45 +0530 Subject: [PATCH 10/13] Document dummy OAuth values for local dev in .env.docker OAuth credentials are required by the app to start. For local dev without a real OAuth app, dummy values allow the server to start with the header API-key endpoint functional. Co-Authored-By: Claude Sonnet 4.6 --- .env.docker | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.env.docker b/.env.docker index a112cc7..d15ba1e 100644 --- a/.env.docker +++ b/.env.docker @@ -12,9 +12,12 @@ PLANE_INTERNAL_BASE_URL= # ------------------------------------------------------------------- # OAuth Provider (required for OAuth flow via /mcp) +# For local dev without OAuth, use dummy values below — the server will +# start and the header API-key endpoint (/http/api-key/mcp) will work. +# Real OAuth flow requires valid credentials from your Plane OAuth app. # ------------------------------------------------------------------- -PLANE_OAUTH_PROVIDER_CLIENT_ID= -PLANE_OAUTH_PROVIDER_CLIENT_SECRET= +PLANE_OAUTH_PROVIDER_CLIENT_ID=dev +PLANE_OAUTH_PROVIDER_CLIENT_SECRET=dev # Should match the public URL your MCP clients reach the server on PLANE_OAUTH_PROVIDER_BASE_URL=http://localhost:8211 From f99a5364d2fb7fa9c740fce9eb9d3bd9886bf760 Mon Sep 17 00:00:00 2001 From: Manish Gupta Date: Tue, 24 Mar 2026 17:51:16 +0530 Subject: [PATCH 11/13] Add Helm chart deployment docs to deployments/README.md Documents install/upgrade/uninstall via helm.plane.so, minimal values.yaml example, and full key values reference based on the plane-mcp-server chart. Co-Authored-By: Claude Sonnet 4.6 --- deployments/README.md | 78 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/deployments/README.md b/deployments/README.md index 641c0ab..b378790 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -68,7 +68,83 @@ docker compose up -d ## Option 2: Helm Chart -> 🚧 Coming soon. +### Prerequisites + +- Kubernetes cluster (v1.21+) +- [Helm](https://helm.sh/docs/intro/install/) v3+ +- An ingress controller (e.g. nginx) + +### Add the Helm Repository + +```bash +helm repo add plane https://helm.plane.so +helm repo update +``` + +### Install + +```bash +helm install plane-mcp plane/plane-mcp-server \ + --namespace plane-mcp \ + --create-namespace \ + -f values.yaml +``` + +### Minimal `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: '' + client_secret: '' + provider_base_url: 'https://mcp.yourdomain.com' +``` + +### Key Values + +| Value | Default | Description | +|-------|---------|-------------| +| `dockerRegistry.default_tag` | `latest` | Image tag to deploy | +| `ingress.enabled` | `true` | Enable ingress | +| `ingress.host` | `mcp.example.com` | Public hostname | +| `ingress.ingressClass` | `nginx` | Ingress class name | +| `ingress.ssl.enabled` | `false` | Enable TLS via cert-manager | +| `ingress.ssl.issuer` | `cloudflare` | ACME issuer (`cloudflare`, `digitalocean`, `http`) | +| `services.api.replicas` | `1` | Number of MCP server replicas | +| `services.api.plane_base_url` | `''` | Plane API URL | +| `services.api.plane_oauth.enabled` | `false` | Enable 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 the server is reachable on | +| `services.redis.local_setup` | `true` | Deploy Valkey in-cluster | +| `services.redis.external_redis_url` | `''` | External Valkey/Redis URL (if not using in-cluster) | +| `services.proxy.enabled` | `false` | Enable nginx proxy sidecar | + +### Upgrade + +```bash +helm upgrade plane-mcp plane/plane-mcp-server \ + --namespace plane-mcp \ + -f values.yaml +``` + +### Uninstall + +```bash +helm uninstall plane-mcp --namespace plane-mcp +``` --- From d72e27fdd28ab63d375eaa31f757584897eaf103 Mon Sep 17 00:00:00 2001 From: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:10:34 +0530 Subject: [PATCH 12/13] updated as per CodeRabbit suggestions --- README.md | 2 +- deployments/docker-compose.yaml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d49699..fab2199 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ The server will be available at: - `http://localhost:8211/http/api-key/mcp` — PAT / header API key endpoint To pin a specific version, set `APP_RELEASE_VERSION` in `variables.env`: -``` +```env APP_RELEASE_VERSION=v0.2.8 ``` diff --git a/deployments/docker-compose.yaml b/deployments/docker-compose.yaml index 0a14479..c6d34d4 100644 --- a/deployments/docker-compose.yaml +++ b/deployments/docker-compose.yaml @@ -1,7 +1,6 @@ # Plane MCP Server — Production Deployment # # Setup: -# cp variables.env.example variables.env # copy the template # # edit variables.env with your values # docker compose up -d From b8cbe121b124f6c377515c18fa4180cf1160f5e5 Mon Sep 17 00:00:00 2001 From: Manish Gupta <59428681+mguptahub@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:25:42 +0530 Subject: [PATCH 13/13] implemented suggestion --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fab2199..dfd9579 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ docker compose up -d ``` The server will be available at: -- `http://localhost:8211/mcp` — OAuth endpoint +- `http://localhost:8211/http/mcp` — OAuth endpoint - `http://localhost:8211/http/api-key/mcp` — PAT / header API key endpoint To pin a specific version, set `APP_RELEASE_VERSION` in `variables.env`: