# Plane MCP server: setup, tools, auth and read-only use

Connect Plane's official MCP server to Claude Code, Codex, Cursor or ChatGPT: endpoints, API key and workspace slug, the 30 tools, and how to limit writes.

The Plane MCP server is the official [Model Context Protocol](https://modelcontextprotocol.io) server for [Plane](https://plane.so), the open-source project-management tool. It lets an agent read and change projects, work items, cycles, modules, pages and more in your Plane workspace. Plane publishes it as [makeplane/plane-mcp-server](https://github.com/makeplane/plane-mcp-server) and also runs it hosted at `https://mcp.plane.so/http/mcp`.

Below: endpoints, auth, config for Claude Code, Codex, Cursor and ChatGPT, what the tools can change, and how to stop it changing more than you want. Details are as of 2026-11-07 and come from Plane's docs and repo.

## Official or community

The official server is the Python package `plane-mcp-server` in the `makeplane` GitHub org, MIT licensed, built on FastMCP and Plane's `plane-sdk`. It needs Python 3.10+ and [uv](https://docs.astral.sh/uv/) when you run it locally.

The older Node package `@makeplane/plane-mcp-server` is, in the repo's words, deprecated and unmaintained. If an old blog post tells you to `npx @makeplane/plane-mcp-server`, use the Python server instead. If you migrate, rename `PLANE_API_HOST_URL` to `PLANE_BASE_URL`.

## Endpoints

From [Plane's MCP server docs](https://developers.plane.so/dev-tools/mcp-server):

| Transport | Endpoint | Auth |
|---|---|---|
| Streamable HTTP (recommended) | `https://mcp.plane.so/http/mcp` | OAuth |
| Streamable HTTP | `https://mcp.plane.so/http/api-key/mcp` | Personal or workspace access token |
| Local stdio | `uvx plane-mcp-server stdio` | `PLANE_API_KEY` + `PLANE_WORKSPACE_SLUG` |
| SSE | `https://mcp.plane.so/sse` | Deprecated |

## Auth: API key and workspace slug

**OAuth.** Your client sends you to Plane, you sign in and pick a workspace, and the connection is bound to that workspace. OAuth requests `read` and `write` scopes.

**Access token.** Create one under Profile Settings → Personal Access Tokens, or Workspace Settings → Access Tokens for automations. Every request carries two headers:

```
Authorization: Bearer <PAT>
x-workspace-slug: <workspace-slug>
```

The workspace slug is the path segment after `app.plane.so/`. Plane's docs flag that the server now reads the standard `Authorization: Bearer` header, so configs that sent the token another way need updating.

**Local stdio.** Two required environment variables and one optional:

| Variable | Required | Meaning |
|---|---|---|
| `PLANE_API_KEY` | yes | Your access token |
| `PLANE_WORKSPACE_SLUG` | yes | The workspace to act in |
| `PLANE_BASE_URL` | no | Defaults to `https://api.plane.so`; set it for self-hosted Plane |

Whatever the method, the server acts as the authenticated user. Plane's docs: "Plane enforces workspace and project roles, so a Guest cannot do more through MCP."

## Connect it to Claude Code

OAuth, then sign in from inside Claude Code with `/mcp` (or `claude mcp login plane`):

```bash
claude mcp add --transport http plane https://mcp.plane.so/http/mcp
```

Access token. The `--header` flags go after the URL:

```bash
claude mcp add --transport http plane https://mcp.plane.so/http/api-key/mcp \
  --header "Authorization: Bearer <PAT>" \
  --header "x-workspace-slug: <workspace-slug>"
```

Local stdio:

```bash
claude mcp add --transport stdio plane \
  --env PLANE_API_KEY=<your-api-key> \
  --env PLANE_WORKSPACE_SLUG=<your-workspace-slug> \
  -- uvx plane-mcp-server stdio
```

Add `--scope local`, `project` or `user` to choose where the entry is saved. Check it with `claude mcp list`.

## Connect it to Codex

OAuth:

```bash
codex mcp add plane --url https://mcp.plane.so/http/mcp
codex mcp login plane
codex mcp list
```

Access token, in `~/.codex/config.toml`. Codex sends `Authorization: Bearer $PLANE_PAT`; other headers are config-file only:

```toml
[mcp_servers.plane]
url = "https://mcp.plane.so/http/api-key/mcp"
bearer_token_env_var = "PLANE_PAT"
http_headers = { "x-workspace-slug" = "<workspace-slug>" }
```

## Connect it to Cursor

In `~/.cursor/mcp.json`. Remote entries use `url` and must not include a `type` key.

```json
{
  "mcpServers": {
    "plane": {
      "url": "https://mcp.plane.so/http/mcp"
    }
  }
}
```

Access token:

```json
{
  "mcpServers": {
    "plane": {
      "url": "https://mcp.plane.so/http/api-key/mcp",
      "headers": {
        "Authorization": "Bearer ${env:PLANE_PAT}",
        "x-workspace-slug": "<workspace-slug>"
      }
    }
  }
}
```

## Connect it to ChatGPT

Turn on Developer mode, then add a connector at chatgpt.com/plugins with the OAuth URL `https://mcp.plane.so/http/mcp`. Plane's docs: "ChatGPT does not accept custom headers, so use OAuth."

## Any other client

The generic stdio entry, from the [repo README](https://github.com/makeplane/plane-mcp-server):

```json
{
  "mcpServers": {
    "plane": {
      "command": "uvx",
      "args": ["plane-mcp-server", "stdio"],
      "env": {
        "PLANE_API_KEY": "<your-api-key>",
        "PLANE_WORKSPACE_SLUG": "<your-workspace-slug>"
      }
    }
  }
}
```

A stdio-only client can reach the hosted server through `mcp-remote` (Node.js 22+):

```json
{
  "mcpServers": {
    "plane": {
      "command": "npx",
      "args": ["mcp-remote@latest", "https://mcp.plane.so/http/mcp"]
    }
  }
}
```

Windsurf and Antigravity use `serverUrl` instead of `url`. VS Code uses `.vscode/mcp.json` with a `servers` key and `"type": "http"`.

## The tools it exposes

Since 0.3 the server exposes 30 tools, one per Plane resource, covering 207 operations. Each tool takes an `action` parameter. Creating a work item looks like `workitem(action="create", project_id=..., name="Fix login")`.

The tool names, from [the tools README](https://github.com/makeplane/plane-mcp-server/blob/main/plane_mcp/tools/README.md):

| Area | Tools |
|---|---|
| Work items | `workitem`, `workitem_comment`, `workitem_link`, `workitem_relation`, `workitem_attachment`, `workitem_activity`, `workitem_property`, `workitem_type`, `work_log`, `label`, `state`, `intake` |
| Planning | `cycle`, `module`, `milestone`, `initiative`, `project_estimate` |
| Releases | `release`, `release_label`, `release_tag` |
| Projects and workspace | `project`, `workspace`, `member`, `template` |
| Docs | `page`, `collection` |
| Customers | `customer`, `customer_property`, `customer_request` |
| Query help | `get_pql_reference` |

There are no epic tools. Epics are work items of type "Epic": resolve the type with `workitem_type`, then create or list with that type id.

If your config or prompts use the 0.2.x per-operation names (`create_work_item`, `list_cycles` and so on), 169 of the 177 old names still resolve as aliases. The other seven return a message naming their replacement.

## What it can change versus read

Almost every tool can write. The `workitem` tool alone has `list`, `retrieve`, `search` and `count`, and also `create`, `update`, `delete` and `archive`. `project` can `delete` and `archive` projects. `cycle` can `complete` and `transfer_workitems`. `page`, `module`, `release` and the customer tools all have `delete`.

Three tools are read-only by their action list: `get_pql_reference`, `workitem_activity` (`list`, `retrieve`) and `member` (`me`, `list_workspace`, `list_project`, `list_roles`, `retrieve_role`).

Each action is declared with flags such as `read=True` or `destructive=True`, and the server derives its MCP annotations from them. Plane's docs say read-only tools carry the `readOnlyHint` annotation and destructive ones carry `destructiveHint`.

## Run it read-only or scoped to one project

Plane's docs say it outright: "There is no separate read-only endpoint." Your options, strongest first:

**1. Use a role-limited Plane account.** The server can do only what the authenticated user can do. A dedicated account with a Guest role, or with membership in only one project, caps what any agent can reach. Plane recommends a workspace access token "with the minimum role needed for automations." This is also the only way to scope to one project: there is no per-project setting on the connection. OAuth binds one workspace, and a token connection is scoped by its `x-workspace-slug`.

**2. Filter tools in the client.** This works at tool level, and since one tool holds both reads and deletes, denying `workitem` removes work items entirely.

Claude Code accepts a deny or ask rule for a whole server or one tool. To be asked before any Plane call:

```json
{
  "permissions": {
    "ask": ["mcp__plane__*"]
  }
}
```

To block one action rather than the whole tool, [Claude Code's permissions docs](https://code.claude.com/docs/en/permissions) say parameter rules on MCP tools must be passed with `--disallowedTools`; settings files skip any `mcp__` rule that has parentheses:

```bash
claude --disallowedTools "mcp__plane__workitem(action:delete)"
```

Codex has an allow list, a deny list and approval modes per server ([Codex MCP docs](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)):

```toml
[mcp_servers.plane]
url = "https://mcp.plane.so/http/mcp"
enabled_tools = ["workitem", "cycle", "get_pql_reference", "member"]
disabled_tools = ["project"]
default_tools_approval_mode = "writes"
```

`writes` prompts only for tools not marked read-only. With Plane's mixed tools, expect that to mean most calls prompt.

**3. Turn the server off when you are not using it.** `enabled = false` in Codex, or remove the entry.

## Self-hosted Plane

The hosted server cannot reach a private instance. Run stdio with `PLANE_BASE_URL` pointing at your API, or deploy the server yourself. Check your key first:

```bash
curl -H "x-api-key: $PLANE_API_KEY" "https://plane.yourcompany.com/api/v1/users/me/"
```

A 200 means the key works. Plane's docs say OAuth self-hosting needs Plane Cloud or Commercial Edition; Community Edition should use stdio.

## Common errors

| Symptom | Cause | Fix |
|---|---|---|
| 401 on a token connection | Wrong or revoked token, or old header | Send `Authorization: Bearer <PAT>` |
| 401 on OAuth | Session expired | Re-authenticate (`/mcp` in Claude Code, `codex mcp login plane`) |
| "workspace slug missing" | Header left out | Add `x-workspace-slug` |
| 404 | Wrong slug or id | Check the segment after `app.plane.so/` |
| 403 | Your Plane role is too low | Ask for the needed workspace or project role |
| 402 | Plan-gated feature | The message names the feature |
| 400 | Bad argument | Check the action's required fields |
| `mcp-remote` won't start | Old Node | Node.js 22+ and `npx -y mcp-remote@latest` |
| Server not listed | Bad JSON or wrong schema | Trailing commas; `url` vs `serverUrl`; no `type` in Cursor |
| Only the first page of projects | Pagination | Follow `next_cursor` or pass `per_page` |

For a stuck OAuth session through `mcp-remote`, `rm -rf ~/.mcp-auth` clears it, along with every other `mcp-remote` cache. Set `MCP_REMOTE_CONFIG_DIR` to keep Plane's separate. To debug, `claude --debug` and `claude mcp list` show what Claude Code loaded.

## What this does not cover

Client-side tool filters live in each agent's own config. A filter written for Claude Code does nothing in Cursor, and Plane's role model is the only limit that holds across every client. If you run ACP, its rules can allow, ask or deny an MCP tool by name, with the same rule on every harness whose hook sees MCP calls and one log of each call. See the [coverage table](/coverage) for which harnesses those are. That is still tool-level, so it has the same limit as above: `workitem` is one tool, reads and deletes together. For how Plane's controls compare with other MCP servers, see [/mcp-controls](/mcp-controls).

## Frequently asked questions



## Where to read more

- [Plane MCP server docs](https://developers.plane.so/dev-tools/mcp-server): endpoints, every client's config, troubleshooting
- [makeplane/plane-mcp-server](https://github.com/makeplane/plane-mcp-server): source, README, releases
- [Tool and action list](https://github.com/makeplane/plane-mcp-server/blob/main/plane_mcp/tools/README.md): all 30 tools and their actions
- [Claude Code permissions](https://code.claude.com/docs/en/permissions): `mcp__` rules and `--disallowedTools`
- [Codex MCP config](https://learn.chatgpt.com/docs/extend/mcp?surface=cli): `enabled_tools`, `disabled_tools`, approval modes
