This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Plane MCP Server — a Python-based Model Context Protocol server that exposes Plane's project management API as MCP tools. Built on FastMCP with the official plane-sdk. Supports three transport modes: stdio (local), HTTP (with OAuth or header auth), and SSE (legacy).
# Install dependencies (uses uv)
uv pip install -e ".[dev]"
# Run the server locally (stdio mode)
PLANE_API_KEY=... PLANE_WORKSPACE_SLUG=... python -m plane_mcp stdio
# Run HTTP server
python -m plane_mcp http
# Run all tests
pytest
# Run a single test
pytest tests/test_integration.py::test_full_integration -v
# Run tests with env vars from file
export $(cat .env.test.local | xargs) && pytest tests/ -v
# Format code (line length: 100)
black plane_mcp/
# Lint (rules: E, F, I, UP, B; line length: 100)
ruff check plane_mcp/plane_mcp/__main__.py parses a positional arg (stdio, http, or sse) and launches the corresponding server:
- stdio: Requires
PLANE_API_KEY+PLANE_WORKSPACE_SLUGenv vars. Runs locally. - http: Starts on port 8211 with two auth endpoints — OAuth (
/oauth/mcp) and header-based PAT (/http/api-key/mcp). - sse: Legacy OAuth-only SSE transport.
Three factory functions (get_oauth_mcp, get_header_mcp, get_stdio_mcp) each create a FastMCP instance, register all tools, and configure the appropriate auth provider. OAuth/HTTP modes use Redis for token storage (falls back to in-memory).
get_plane_client_context() returns a PlaneClientContext(client, workspace_slug) namedtuple. It resolves credentials from the MCP request context (OAuth token or header API key) or from environment variables (stdio mode). Prefers PLANE_INTERNAL_BASE_URL for server-to-server calls.
PlaneOAuthProvider— Full OAuth flow with token verification against the Plane API.PlaneHeaderAuthProvider— Simple header-based auth usingx-api-keyandx-workspace-slugheaders.
19 tool modules organized by Plane domain (projects, work_items, cycles, modules, etc.), totaling 55+ tools. Each module exports a register_*_tools(mcp: FastMCP) function called from tools/__init__.py.
Tool pattern:
def register_*_tools(mcp: FastMCP) -> None:
@mcp.tool()
def tool_name(param: str, optional_param: str | None = None) -> SomePlaneModel:
"""Docstring with Args and Returns sections."""
client, workspace_slug = get_plane_client_context()
return client.endpoint.operation(workspace_slug=workspace_slug, ...)Tools return Pydantic models from plane-sdk and use Python 3.10+ union syntax (str | None).
Integration tests in tests/test_integration.py use FastMCP.Client with StreamableHttpTransport. Tests run against a live Plane instance — configure via .env.test (copy to .env.test.local with real values).
| Variable | Required For | Purpose |
|---|---|---|
PLANE_API_KEY |
stdio | API key for authentication |
PLANE_WORKSPACE_SLUG |
stdio | Target workspace |
PLANE_BASE_URL |
all (default: https://api.plane.so) | Plane API URL |
PLANE_INTERNAL_BASE_URL |
http/sse (optional) | Internal URL for server-to-server calls |
REDIS_HOST / REDIS_PORT |
http/sse (optional) | Token storage (falls back to in-memory) |
PLANE_OAUTH_PROVIDER_* |
http/sse OAuth | OAuth client credentials and base URL |