Skip to main content
Glama
README.md
# Plane MCP

A [Model Context Protocol](https://modelcontextprotocol.io) server for **[Plane](https://plane.so)** (self-hosted or Cloud). It lets an LLM client — Claude Desktop, Claude Code, Cursor, etc. — fully drive your Plane workspace: discover projects, and create / read / update / delete / re-organize work items, states, labels, cycles, modules, and comments.

> Verified against a self-hosted instance at `plane.nzminds.com` (workspace `nzmt`).

## What it can do

| Resource | Tools |
|---|---|
| **Projects** | `list_projects`, `find_project`, `get_project`, `create_project`, `update_project`, `delete_project` |
| **Work items** | `list_work_items`, `get_work_item`, `get_work_item_by_number`, `search_work_items`, `create_work_item`, `update_work_item`, `set_work_item_status`, `delete_work_item` |
| **States (statuses)** | `list_states`, `find_state`, `create_state`, `update_state`, `delete_state` |
| **Labels** | `list_labels`, `create_label`, `update_label`, `delete_label` |
| **Cycles (sprints)** | `list_cycles`, `get_cycle`, `create_cycle`, `update_cycle`, `delete_cycle`, `list_cycle_work_items`, `add_work_items_to_cycle`, `remove_work_item_from_cycle` |
| **Modules** | `list_modules`, `get_module`, `create_module`, `update_module`, `delete_module`, `list_module_work_items`, `add_work_items_to_module`, `remove_work_item_from_module` |
| **Comments** | `list_comments`, `add_comment`, `update_comment`, `delete_comment` |
| **Members** | `list_members`, `find_member` |

## How Plane's model works (quick mental model)

```
Workspace (slug, e.g. "nzmt")
 └─ Project (UUID)           ← discover with list_projects
     ├─ Work Items / Issues  ← the tickets; have a UUID + human number (TRAVELXS-391)
     ├─ States               ← the statuses (Backlog/Todo/In Progress/Done…), each a UUID
     ├─ Labels               ← UUIDs
     ├─ Cycles               ← time-boxed sprints
     └─ Modules              ← feature groupings
```

**Everything in a request body is a UUID, not a name.** So the normal flow is:
1. `list_projects` → pick a project UUID.
2. `list_states` → map "In Progress" → its UUID. `list_members` / `list_labels` likewise.
3. Create/update the work item. **To change status, set `state` to the target state UUID** (or use `set_work_item_status`).

Most tools take an optional `project_id`; if omitted they fall back to `PLANE_DEFAULT_PROJECT_ID` from your `.env`.

## Setup

### 1. Get an API key
See **[SETUP.md](SETUP.md)** for step-by-step instructions (Profile Settings → Personal Access Tokens).

### 2. Configure
```bash
cp .env.example .env
# edit .env: PLANE_BASE_URL, PLANE_API_KEY, PLANE_WORKSPACE_SLUG
```

### 3. Install
Requires Python ≥ 3.10.
```bash
python -m pip install -e .
```
(or with [uv](https://docs.astral.sh/uv/): `uv pip install -e .`)

### 4. Run
```bash
plane-mcp          # or:  python -m plane_mcp
```
It speaks MCP over **stdio**, so you normally don't run it by hand — your MCP client launches it.

## Connecting to a client

### Claude Desktop / Claude Code
Add to your MCP config (`claude_desktop_config.json`, or via `claude mcp add`):

```json
{
  "mcpServers": {
    "plane": {
      "command": "plane-mcp",
      "env": {
        "PLANE_BASE_URL": "https://plane.nzminds.com",
        "PLANE_API_KEY": "plane_api_xxxxxxxx",
        "PLANE_WORKSPACE_SLUG": "nzmt"
      }
    }
  }
}
```

Or, if you'd rather rely on the `.env` file, drop the `env` block and use the absolute interpreter:

```json
{
  "mcpServers": {
    "plane": {
      "command": "python",
      "args": ["-m", "plane_mcp"],
      "cwd": "d:/Govind/Plane MCP"
    }
  }
}
```

Claude Code one-liner:
```bash
claude mcp add plane -- python -m plane_mcp
```

## Example prompts once connected

- "List my projects in Plane."
- "In TravelXS, create a high-priority bug 'Login fails on Safari' and assign it to govind.kumar."
- "Move TRAVELXS-391 to In Progress."
- "Create a cycle 'Sprint 12' from 2026-07-01 to 2026-07-14 and add TRAVELXS-394 and -376 to it."
- "Add a comment to TRAVELXS-308 saying the fix is deployed."

## Notes & limits

- **Rate limit:** 60 requests/min per key. The client auto-retries on `429` with backoff.
- **Pagination:** list tools follow Plane's cursor pagination and return *all* results.
- **Destructive ops:** the hard `delete_*` tools (project, work item, state, label, cycle, module, comment) are **disabled by default**. Set `PLANE_ALLOW_DELETE=true` in `.env` to expose them. Relationship removals (`remove_work_item_from_cycle` / `_module`) are always available since they're reversible.
- **Self-hosted path quirk:** cycle/module work-item sub-resources are `/cycle-issues/` and `/module-issues/` (the public docs say `work_items`, which 404s on self-hosted). This server uses the verified paths.

## Project layout

```
src/plane_mcp/
├── config.py        # env loading & validation
├── client.py        # async HTTP client: pagination, 429 retry, errors
├── server.py        # FastMCP instance + tool registration
├── __main__.py      # entry point (stdio)
└── tools/           # one module per resource
    ├── projects.py  issues.py  states.py  labels.py
    ├── cycles.py    modules.py comments.py members.py
```

## License
MIT

TDQS

B3.1/5.0

Scored across 38 tools

Disambiguation4/5

Tools are generally distinct, with clear separation via resource type (project, work item, cycle, module, comment, label, member, state) and action. The main overlap is set_work_item_status being a convenience wrapper around update_work_item, but descriptions clarify the purpose. Some pairs like list_projects/find_project could be confused but are well-defined.

Naming Consistency5/5

Names follow a consistent verb_noun pattern (list_*, get_*, create_*, update_*, find_*, add_*, remove_*). Variations like get_work_item_by_number and set_work_item_status are logically extended forms, not inconsistent. The convention is uniform across all 38 tools.

Tool Count2/5

With 38 tools, the server is well over the 25+ threshold that typically indicates too many. While the scope is broad (multiple project management entities), the set includes many similar list/get/update tools per resource, making it feel heavy. A more streamlined set could consolidate some operations.

Completeness3/5

The server covers create, read, and update for most resources, plus work item status changes and assignment to cycles/modules. However, there are no delete operations for any entity (projects, work items, labels, cycles, modules, comments, states), which is a notable gap in CRUD coverage. Also missing get_label and get_comment, but these are less critical.

Maintenance

ActivityInactive
ResponsivenessNo issues