Plane MCP Server

repository·main·Indexed 42 days ago

https://github.com/makeplane/plane-mcp-server

A Model Context Protocol (MCP) server that provides AI agents with tools to interact with Plane. It features 30 tools covering resources such as work items, projects, cycles, and modules, supporting approximately 204 total actions via an action-parameter pattern. Version 0.3.2 supports stdio via uvx, as well as HTTP transports using OAuth or Personal Access Tokens.

Tokens
5.8K
Snippets
20
Records
27
Agent score
55%

What's inside Plane MCP Server

  1. Overview of the Plane MCP Toolkit modules

    main

    The toolkit provides shared building blocks for the tool surface, categorized by when they act during a tool's lifecycle. All modules are re-exported from __init__.py, allowing for a single import per resource module.

    ModuleActs atProvides
    spec.pydeclarationAction, build_description, build_annotations
    runtime.pycallmissing, needs, require, one_of, opt, coerce_list, page_params, as_params, ids_of
    paging.pyresponseenvelope, dump_results, pql_failure, workitem_page
    governance.pypolicyworkspace_owns_resource, workspace_owns, scoped, plan_gated
    transforms.pylistingStripOutputSchemas
  2. Manage Resource Scope: Project vs Workspace

    main

    Plane resources can be governed at either the workspace level or the project level. The MCP server uses a uniform idiom to handle this:

    • Project Scope: Supply a project_id to interact with resources belonging to a specific project.
    • Workspace Scope: Omit the project_id to interact with resources belonging to the entire workspace.

    Warning: Getting the scope wrong will not result in an error; the call will simply succeed against the wrong scope. The server uses internal governance checks to ensure that if a resource is owned exclusively by a workspace, writes to the project scope are refused.

  3. Understand Workspace Governance and scope refusal

    main

    Plane resources can be scoped to either a project or a workspace. The scope determines where writes are directed. If a write is attempted in the wrong scope, the API refuses it with specific error codes:

    Write ScenarioRefusal CodeMeaning
    Attempting to write to a project-scoped resource that is actually owned by the workspaceworkspace_managedYou omitted the project_id
    Attempting to write to a catalogue resource that is actually owned by the projectworkspace_not_managedYou passed a project_id

    Important: Governance is managed via the GOVERNED_BY mapping. You must check the specific flag for the resource you are targeting, as a workspace might own one type of resource but not another.

    Resource TypeGovernance Flag
    work item types, epics, propertieswork_item_types
    states, labels, workflows, templates, automationsstates_owned_by_workspace

    How to check ownership:

    1. Proactive (Ask first): Use workspace_owns_resource(client, slug, resource) to read the flag and pick the correct scope before attempting a write.
    2. Reactive (Handle refusal): Use workspace_owns(exc, *fields) after an exception occurs. This is useful because the flag is cached and a lockout might persist even after the flag is toggled off.
  4. Understand the Plane MCP Tool Surface

    main

    The Plane MCP server provides a compact tool surface consisting of 30 tools, each representing a Plane resource. Instead of having a separate tool for every single API operation, each tool uses an action parameter to select the specific operation to perform. This results in approximately 204 total actions.

    Tool Invocation Pattern

    Tools are called by specifying the resource name and an action. For example:

    • Create a work item: workitem(action="create", project_id=..., name="Fix login")
    • List work items with a filter: workitem(action="list", project_id=..., pql='state__group = "started"')
    • Archive a cycle: cycle(action="archive", project_id=..., cycle_id=...)

    Each tool's description explicitly lists its available actions along with their required and optional parameters.

    workitem(action="create", project_id=..., name="Fix login")
    workitem(action="list", project_id=..., pql='state__group = "started"')
    cycle(action="archive", project_id=..., cycle_id=...)
  5. How Plane MCP tools work

    main

    The server provides 30 tools, with one tool dedicated to each Plane resource. Instead of having a separate tool for every single API operation, each tool uses an action parameter to determine the specific operation to perform.

    Example usage patterns:

    • workitem(action="create", ...) to create a task.
    • workitem(action="list", ...) to retrieve tasks.
    • cycle(action="archive", ...) to archive a cycle.

    Each tool's description includes its available actions and their required/optional parameters, making the toolset self-documenting for AI agents.

    workitem(action="create", project_id=..., name="Fix login")
    workitem(action="list", project_id=..., pql='state__group = "started"')
    cycle(action="archive", project_id=..., cycle_id=...)
  6. How to create an Epic using workitem tools

    main

    In Plane, an Epic is not a separate tool but a work item with a specific type. To create an Epic, follow these three steps:

    1. Resolve the Epic type ID: Use workitem_type resolve with the project_id and set name="Epic". This returns the id which is the type_id.
    2. Create the work item: Use workitem create and provide the type_id obtained in step 1.
    3. List Epics: Use workitem list with a PQL filter: pql='type = "<type id>"'.
  7. Use HTTP with OAuth (Hosted)

    main

    The hosted OAuth transport allows for a seamless connection without storing credentials in your local configuration. The OAuth flow is handled during the connection process.

    If your MCP client does not support remote MCP natively, use mcp-remote as a bridge. This requires Node.js 22+.

    {
      "mcpServers": {
        "plane": {
          "command": "npx",
          "args": ["mcp-remote@latest", "https://mcp.plane.so/http/mcp"]
        }
      }
    }
  8. Add a newly governed resource to the toolkit

    main

    To implement governance for a new resource, follow these steps:

    1. Register the flag: Add a new row to the GOVERNED_BY mapping naming its corresponding flag.
    2. Apply the @scoped decorator: Decorate your tool with @scoped("<noun>") immediately below the @mcp.tool decorator. This ensures that any refusal results in a message naming the owning scope.
      • Note: If the API refuses by specific field (like work item types), pass the field name: @scoped("work item types", "work_item_types").
    3. Resolve scope at dispatch: Resolve the scope once at the top of your dispatch logic (not at every call site). You can return a tuple or a local _Scope object.
    4. Handle field mismatches: If a catalogue state lacks certain fields (like ordering or triage flags), they should be dropped silently during processing to simulate a successful operation.
  9. Query work items using PQL

    main

    The list, count, and search actions within the workitem tool support PQL (Plane Query Language). This allows for complex filtering and grouping of work items.

    Examples:

    • Filtering by state and priority: pql='state__group = "started" AND priority = "urgent"'
    • Counting by assignee and grouping by state: pql='assignees__id = "<member id>"', group_by="state_id"

    To see the full syntax and operators, call the get_pql_reference tool.

    workitem(action="list", project_id=..., pql='state__group = "started" AND priority = "urgent"')
    workitem(action="count", pql='assignees__id = "<member id>"', group_by="state_id")
  10. Migrate from the Node.js server to Python

    main

    The Node.js implementation (@makeplane/plane-mcp-server) is deprecated. When migrating to the Python version, update your environment variable names as follows:

    | Node.js | Python |
    |---|---|
    | `PLANE_API_KEY` | `PLANE_API_KEY` |
    | `PLANE_API_HOST_URL` | `PLANE_BASE_URL` |
    | `PLANE_WORKSPACE_SLUG` | `PLANE_WORKSPACE_SLUG` |
  11. Quick start: Install and configure Plane MCP Server via stdio

    main

    To run the Plane MCP Server locally as a subprocess of your MCP client, use uvx. This requires Python 3.10+ and no manual installation step. You must provide a Plane API key (from Workspace Settings → API tokens) and your workspace slug.

    For self-hosted Plane instances, also set the PLANE_BASE_URL environment variable.

    {
      "mcpServers": {
        "plane": {
          "command": "uvx",
          "args": ["plane-mcp-server", "stdio"],
          "env": {
            "PLANE_API_KEY": "<your-api-key>",
            "PLANE_WORKSPACE_SLUG": "<your-workspace-slug>"
          }
        }
      }
    }
  12. Use HTTP with a Personal Access Token (Hosted)

    main

    To connect to the hosted service using a Personal Access Token (PAT), use the mcp-remote bridge and provide the required Authorization and X-Workspace-slug headers.

    {
      "mcpServers": {
        "plane": {
          "command": "npx",
          "args": ["mcp-remote@latest", "https://mcp.plane.so/http/api-key/mcp"],
          "headers": {
            "Authorization": "Bearer <PAT>",
            "X-Workspace-slug": "<workspace-slug>"
          }
        }
      }
    }