Skip to content

Bug: update_work_item state is silently ignored on self-hosted CE (inverse of state_id→state rename) #183

Description

@nowjon

Problem

update_work_item (and likely create_work_item) accept a state parameter and the official MCP/plane_sdk serialize it as "state" in the PATCH/POST body. On some Plane deployments that works; on others — including self-hosted Plane Community Edition — the REST API expects "state_id" instead.

When the wrong field name is sent, Plane returns HTTP 200 with an updated updated_at, but the work item's state is unchanged. Agents treat this as success and loop or invent wrong explanations (workflow constraints, parent blocking, etc.).

This is the inverse of the field-name mismatch fixed in community forks such as ZethicTech/plane-mcp-server#13 / #14, which renamed state_idstate for API compatibility. That direction fixes Cloud-style APIs but reintroduces a silent no-op on CE instances that only honor state_id.

Reproduction (self-hosted Plane CE)

Against a self-hosted instance (workspace homelab-ops, MCP plane_mcp_server 0.2.10 / plane_sdk 0.2.19):

  1. Call MCP update_work_item with a valid full state UUID:
    {
      "project_id": "<project-uuid>",
      "work_item_id": "<work-item-uuid>",
      "state": "ba5e07a3-b2a0-4d45-93bf-8b03b0593e13"
    }
  2. MCP reports success; response still shows the previous state (e.g. Todo).
  3. Same PATCH via the REST API:
Body Result
{"state": "<In Progress UUID>"} 200, state unchanged
{"state_id": "<In Progress UUID>"} 200, state updated

Endpoints tested (both behave the same for this field):

  • PATCH /api/v1/workspaces/<slug>/projects/<id>/issues/<id>/
  • PATCH /api/v1/workspaces/<slug>/projects/<id>/work-items/<id>/

Comments and other updates via MCP work fine on the same token — this is isolated to the state field name.

Root Cause

UpdateWorkItem in plane_sdk exposes state: str | None and serializes that key as "state". The MCP tool mirrors that parameter name. Plane CE's issue/work-item update handler appears to read "state_id" and ignore unknown/extra "state" without erroring.

Cloud (and forks that followed #13-style renames) expect "state". So a single-field rename in either direction breaks the other target.

Suggested Fix

Send both fields when a state UUID is provided, e.g.:

{
  "state": "<uuid>",
  "state_id": "<uuid>"
}

That should satisfy both Cloud (state) and CE (state_id) without version sniffing. Same pattern is worth considering for any other id/rename pairs that have flipped between API generations (parent / parent_id, etc.) if those show the same silent-ignore behavior.

Alternatively: document which field each Plane edition accepts and branch in the SDK — dual-send is simpler and avoids another silent-break cycle.

Impact

Agent clients (Hermes, Cursor, Claude, etc.) cannot reliably move work items between states via official MCP on self-hosted CE. Reads/list/comments succeed; only state transitions fail silently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions