Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .env.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# 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)
# 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=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

# -------------------------------------------------------------------
# Redis — set automatically by docker-compose; override only if needed
# -------------------------------------------------------------------
# REDIS_HOST=redis
# REDIS_PORT=6379
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ dmypy.json
.env
.env.local
.env.test.local
.env.docker.local
deployments/variables.env.local

# Ignore cursor AI rules
.cursor/rules/codacy.mdc
7 changes: 1 addition & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 ./
Expand Down
74 changes: 70 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `deployments/` folder. It pulls the published image — no build required.

```bash
cd deployments
# edit variables.env with your values
docker compose up -d
```

The server will be available at:
- `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`:
```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/
```

Expand Down
167 changes: 167 additions & 0 deletions deployments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# 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) |
| `valkey` | — | Token storage for OAuth (internal only) |

### Endpoints

| Endpoint | Auth | Description |
|----------|------|-------------|
| `http://<host>:8211/mcp` | OAuth | OAuth-based MCP endpoint |
| `http://<host>:8211/http/api-key/mcp` | PAT header | Personal Access Token endpoint |
| `http://<host>: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

### 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: '<your-oauth-client-id>'
client_secret: '<your-oauth-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
```

---

## Troubleshooting

**Server not starting?**
```bash
docker compose logs mcp
```

**Valkey connection issues?**
```bash
docker compose exec valkey valkey-cli ping
```

**Reset and start fresh:**
```bash
docker compose down -v # removes Redis volume too
docker compose up -d
```
36 changes: 36 additions & 0 deletions deployments/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Plane MCP Server — Production Deployment
#
# Setup:
# # 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: 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:
30 changes: 30 additions & 0 deletions deployments/variables.env
Original file line number Diff line number Diff line change
@@ -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
Loading