Skip to main content
Glama
andrejberg
by andrejberg

ya-planka-mcp

A Model Context Protocol (MCP) server that enables AI clients to read and manage your Planka boards using the Planka REST API.

Tests

Getting Started

New here? Four steps to a running, registered server — minimal detail, each one links to the full section below if you need it.

  1. Clone the repo:

    git clone https://github.com/andrejberg/ya-planka-mcp
    cd ya-planka-mcp
  2. Install dependencies: uv sync (see Installation for the pip alternative).

  3. Get an API key and fill in .env: log in to Planka as an admin, generate an API key, then:

    cp .env.example .env
    # edit .env: set PLANKA_BASE_URL and PLANKA_API_KEY

    Full walkthrough: Obtaining API Credentials.

  4. Register the server with your MCP client — Claude Desktop, Claude Code, or any generic client: see MCP Client Configuration.

That's the whole path. For dependency details, alternate install methods, credential fallbacks, troubleshooting, and everything else, keep scrolling.

Related MCP server: Plane MCP Server

Overview

ya-planka-mcp provides a lightweight bridge between MCP clients and your self‑hosted Planka instance. It exposes projects, boards, lists, cards, tasks, and labels through MCP tools, allowing agents to use planka for the full project management and execution live cycle.

Features

  • List projects, boards, lists, labels, and members — full dump or scoped to one board

  • Search and retrieve cards with multiple detail levels

  • Read card checklists with real task IDs and accurate progress counts

  • Create, update, delete, and bulk-move cards

  • Create, update, and delete checklist tasks and task lists

  • Manage labels (create idempotently, assign, remove) and card members

  • Read and write card comments

  • Scaffold complete boards (canonical kanban lists + lifecycle labels) in one call

  • Composite workflow tools: next workable card, mechanical readiness checks, and a computed one-call board status report

  • Token-optimized: detail levels, scoped responses, slimmed schemas — measured by a committed audit script

  • Works with any MCP‑compatible client

Example use cases:

  • "Show all 'In Progress' cards across my workspace."

  • "Create a new card in <Board> / TODO with subtasks…"

  • "Find the 'Login bug' card and list all tasks."

  • "Mark the 'Write tests' task as complete."

  • "Add the 'Urgent' label to the 'Deploy' card."

Prerequisites

  • Python 3.10+

  • uv (recommended) or pip + venv

  • Access to a Planka instance

  • A Planka account you're comfortable letting the MCP server act as (see Obtaining API Credentials below — the account's role determines what the server can do; see Roles & Permissions)

Installation

1. Clone the repository

git clone https://github.com/andrejberg/ya-planka-mcp
cd ya-planka-mcp

2. Install dependencies

With uv (recommended — creates .venv/ and resolves the lockfile):

uv sync

This installs runtime dependencies only. If you also want to run the test suite (see Running Tests below), install the test extra instead:

uv sync --extra test

Or with pip in a manually created virtualenv (the last two lines below — installing requirements.txt then an editable install — are exactly what CI runs in .github/workflows/test.yml, just without a venv there):

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install -e .

Note the venv directory name differs between the two paths: uv sync creates .venv/; the pip walkthrough above creates venv/. This matters for the run_server.sh helper script and the MCP client config below — pick one path and stay consistent with it.

3. Create your .env file

cp .env.example .env

Then fill in the variables below.

Environment variables

Variable

Required?

Purpose

PLANKA_BASE_URL

Yes

Base URL of your Planka instance (e.g. https://planka.example.com, no trailing slash)

PLANKA_API_KEY

Option 1

Recommended. The admin-generated API key from Planka's user administration screen — see Obtaining API Credentials. Checked second, only if PLANKA_API_TOKEN is unset.

PLANKA_API_TOKEN

Option 2

JWT access token from the legacy /api/access-tokens exchange — see Fallback: JWT access-token exchange. Checked first (takes priority over PLANKA_API_KEY if both are set).

PLANKA_EMAIL + PLANKA_PASSWORD

Option 3

Login credentials. The server exchanges them for a token automatically at startup. Checked last, only if neither token variable is set.

Precedence is fixed in src/planka_mcp/api_client.py::initialize_auth: PLANKA_API_TOKEN > PLANKA_API_KEY > PLANKA_EMAIL+PLANKA_PASSWORD — this only matters if you set more than one. For a fresh setup, set just PLANKA_API_KEY (the admin-generated key); see Obtaining API Credentials below for how to get it.

.env.example also documents role-scoped variables (PLANKA_API_TOKEN_BOARD_MANAGER, PLANKA_API_TOKEN_BOARD_MEMBER, PLANKA_SMOKE_LIST_ID, PLANKA_SMOKE_LABEL_ID) — these are only read by the opt-in test suite (see Opt-in permission smoke under Running Tests), not by the server itself.

Obtaining API Credentials

This server was developed and tested against Planka Community v2.0.3. In that version, generating an API key requires admin access — there is no self-service "generate my key" option for a regular board member.

  1. Log in to your Planka instance as an admin user.

  2. Open User Administration (the admin-only user management screen).

  3. Generate an API key for the account you want the MCP server to act as — this can be the admin account itself or any other account. The key carries exactly that account's Planka permissions; see Roles & Permissions below for what a given account can and can't do through this server before picking which one to use.

  4. Copy the generated key into .env:

    PLANKA_API_KEY=your-generated-api-key

That's the whole flow. If your Planka instance predates v2.0.3 or otherwise has no admin-generated API key screen, use one of the fallback methods below instead.

Fallback: JWT access-token exchange

Older Planka versions (and any instance without an admin-generated API key screen) expose a JWT token via the /api/access-tokens endpoint — the same exchange the Planka web client performs internally on login:

curl -X POST https://your-planka-instance.com/api/access-tokens \
  -H "Content-Type: application/json" \
  -d '{
    "emailOrUsername": "your-email@example.com",
    "password": "your-password"
  }'

Response:

{
  "item": {
    "accessToken": "..."
  }
}

Copy the accessToken value into .env as PLANKA_API_TOKEN.

Note: JWT tokens may expire. If you get authentication errors later, repeat the exchange to mint a new one.

Fallback: Email/Password

If you'd rather not mint a token up front, set PLANKA_EMAIL and PLANKA_PASSWORD directly — the server performs the same JWT exchange automatically at startup:

PLANKA_EMAIL=your-email@example.com
PLANKA_PASSWORD=your-password

This is only used if PLANKA_API_TOKEN and PLANKA_API_KEY are both unset.

MCP Client Configuration

The server is a stdio MCP server; the actual entry point every config below runs is mcp_server.py (main.py exists only as a legacy wrapper that subprocesses into mcp_server.py). Replace /path/to/ya-planka-mcp with your clone's absolute path.

Claude Desktop

Edit claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json; Windows: %APPDATA%\Claude\claude_desktop_config.json).

If you installed with uv sync (step 2 above):

{
  "mcpServers": {
    "ya-planka-mcp": {
      "command": "uv",
      "args": ["--directory", "/path/to/ya-planka-mcp", "run", "python", "mcp_server.py"],
      "env": {
        "PLANKA_BASE_URL": "https://your.domain",
        "PLANKA_API_KEY": "<token>"
      }
    }
  }
}

If you installed with pip into venv/ (step 2 above), point directly at that interpreter — use absolute paths for both command and the script argument, since the client's working directory when it spawns the process is not guaranteed to be this repo:

{
  "mcpServers": {
    "ya-planka-mcp": {
      "command": "/path/to/ya-planka-mcp/venv/bin/python",
      "args": ["/path/to/ya-planka-mcp/mcp_server.py"],
      "env": {
        "PLANKA_BASE_URL": "https://your.domain",
        "PLANKA_API_KEY": "<token>"
      }
    }
  }
}

Claude Code

Either add a project-scoped .mcp.json at the root of the project you're working in (not inside ya-planka-mcp itself):

{
  "mcpServers": {
    "ya-planka-mcp": {
      "type": "stdio",
      "command": "uv",
      "args": ["--directory", "/path/to/ya-planka-mcp", "run", "python", "mcp_server.py"],
      "env": {
        "PLANKA_BASE_URL": "https://your.domain",
        "PLANKA_API_KEY": "<token>"
      }
    }
  }
}

or register it with the CLI:

claude mcp add ya-planka-mcp \
  --env PLANKA_BASE_URL=https://your.domain \
  --env PLANKA_API_KEY=<token> \
  -- uv --directory /path/to/ya-planka-mcp run python mcp_server.py

claude mcp add defaults to --scope local (visible only to you, in this project); pass --scope project to commit .mcp.json for teammates, or --scope user to make the server available across all your projects.

Generic MCP client

Any client that can spawn a stdio subprocess needs only the same three pieces of information as the configs above:

  • command/args: uv --directory /path/to/ya-planka-mcp run python mcp_server.py (or the direct venv-interpreter form if you installed with pip)

  • env: PLANKA_BASE_URL and one auth variable (see Environment variables above)

run_server.sh is an alternative launcher some clients may prefer over a raw uv/python command — it activates ./venv (not .venv) if present and runs mcp_server.py, or falls back to the system python3 running main.py otherwise. It only auto-detects a venv literally named venv/, so it is compatible with the pip install path above but not with uv sync's .venv/; the explicit uv --directory ... / venv-interpreter forms above work regardless of install method and are recommended.

Tools & Capabilities

29 tools, grouped by kind:

Read

Tool

Purpose

planka_get_workspace

Workspace structure (projects, boards, lists, labels, users). Pass board_name/project_name to scope the response — a scoped call is a fraction of the full dump

planka_get_me

Authenticated user identity (id, username, role)

planka_get_labels

All labels on a board (fast targeted refresh)

planka_list_cards

Filter and list cards with detail levels (preview/summary/detailed) and pagination

planka_find_and_get_card

Search and fetch a specific card

planka_get_card

Fetch a card with full checklist detail (real task IDs, progress counts)

planka_get_comments

List a card's comments, newest first, with optional prefix filter (e.g. Blocked:)

Write

Tool

Purpose

planka_create_board

Scaffold a board with canonical kanban lists and lifecycle labels in one call

planka_create_list / planka_update_list

Create, rename, and reorder board lists

planka_create_card / planka_update_card / planka_delete_card

Card CRUD

planka_move_cards

Bulk-move 1–50 cards to a list (by id or name), order-preserving, per-card results

planka_create_task_list / planka_add_task / planka_update_task / planka_delete_task

Checklist management

planka_create_label / planka_update_label / planka_delete_label

Label CRUD (create is idempotent by name)

planka_add_card_label / planka_remove_card_label

Assign/remove board labels on cards

planka_add_card_member / planka_remove_card_member

Assign/remove card members

planka_add_comment

Write a comment to a card

Composite workflow

Tool

Purpose

planka_next_card

Top card of a list (default TODO) with everything needed to execute it — description, tasks with ids, labels, members, latest comments — in one round-trip

planka_card_ready_check

Mechanical readiness validation (labels, description sections, checklist shape)

planka_board_status

One-call board report: per-list counts, in-progress ages, blocked cards with latest Blocked: comment, unsatisfied gates, done-window points

Token efficiency

Responses support response_format (markdown/json) and, where reads can be large, detail_level and scope filters. scripts/token_audit.py measures the per-session tool-definition cost and live response sizes against your instance:

uv pip install tiktoken   # optional, for exact o200k_base counts
.venv/bin/python scripts/token_audit.py [--board <name>] [--skip-live]

Usage Examples

Ask your assistant:

  • "List all my boards."

  • "Search for cards mentioning 'invoice'."

  • "Create a card named 'App release checklist' with these subtasks…"

  • "Move the 'Integrate payment API' card to 'Done'."

  • "Mark the 'Write tests' task on the 'Backend sprint' card as complete."

  • "Add the 'Urgent' label to the 'Deploy hotfix' card."

Roles & Permissions

The MCP server does no role handling of its own. It acts as whatever Planka user the configured API key (or email/password) belongs to — whatever that account can do in Planka, the server can do through its tools; whatever it can't, the server can't either. You control what the agent is capable of entirely by which account's key you put in .env, and by adjusting that account's rights inside Planka itself.

Planka distinguishes two kinds of board access, and this section uses Planka's own terms throughout:

  • Board manager — a user in the board's managers list. Only accounts with at least Project Owner or Admin role can be added as a board manager. Board managers get full card/task access plus board-level management (settings, members, labels).

  • Board member — a user added to a board with either edit or view-only rights. Edit rights allow card/task mutations; view-only is read-only. Board members — even with edit rights — cannot manage the board's members list or most board-level settings.

Role capability matrix

Capability

Board manager token

Board member token

Evidence

List cards on a board list (planka_list_cards)

Works

Not exercised by the board member live smoke (it never calls this tool)

tests/test_live_smoke.py::test_non_owner_live_smoke_permissions

Create a card, read fresh card detail, delete the card

Works

Works

test_non_owner_live_smoke_permissions; test_board_member_live_smoke_permissions

Create a task list, add/update/delete a task

Works

Not exercised by the board member live smoke

test_non_owner_live_smoke_permissions

Add / remove a card label

Works

Not exercised by the board member live smoke

test_non_owner_live_smoke_permissions

Read cardMemberships on a card detail

Works (implied)

Works — asserted explicitly (cardMemberships key must be present)

test_board_member_live_smoke_permissions (docstring: "PERM-01 evidence")

Assign / remove another board member as a card member

Works

Not exercised — the board member smoke only self-assigns, consistent with board member tokens being unable to enumerate other users (see next row)

test_non_owner_live_smoke_permissions (include_member_mutations=True)

Assign / remove yourself as a card member

Works (not the focus of this test)

Works — self-assign then self-remove is the entire point of the test

test_board_member_live_smoke_permissions

planka_get_workspace users list

Populated

Empty. Planka returns 403 on /api/users for board member tokens; the server catches it and degrades gracefully (returns {} plus _users_hidden_due_to_permissions: true in JSON) instead of raising

Source: fetch_workspace_data in src/planka_mcp/handlers/workspace.py (tagged PERM-03); unit tests test_fetch_workspace_data_users_403_returns_empty_map and test_get_workspace_permission_hint_when_users_empty in tests/test_workspace.py (mocked 403, not part of the live smoke suite)

Rows marked "not exercised" are not asserted anywhere in this repo's tests — they are not claimed to fail, only that the board member live smoke does not cover them. Only claim behavior beyond this table after adding a test that proves it.

Gotchas

  • A 403 on /api/users is invisible by design. If planka_get_workspace comes back with no users, check the account's board role (planka_get_me) before assuming a bug — a board member token gets an empty users map on purpose, not an error.

  • Non-admin tokens silently see less, not an error. This is a Planka platform characteristic (not asserted by this repo's own test suite): a Planka user — and therefore a token scoped to that user — only sees projects/boards/lists/cards it is a member of. A wrong or under-scoped token does not raise; planka_get_workspace and planka_list_cards just return fewer boards/cards than you expect. If something you know exists is missing, check board membership for that token's user first.

Security recommendations

  • The MCP server accesses only what the authenticated Planka user can access.

  • API token recommended over email/password.

  • Use HTTPS when exposing Planka externally.

  • Consider using a dedicated Planka service user with restricted permissions.

Troubleshooting & FAQ

401 Unauthorized Check token validity and .env configuration.

Client cannot connect to server Verify the correct Python/uv path, firewall rules, and execution permissions.

No boards or cards returned Confirm the Planka user has workspace access.

Task creation fails Ensure you are passing a valid task_list_id. Use planka_get_card to retrieve real task list IDs from the card's checklist.

Development

This project uses an editable install so the src/ directory is automatically on the Python path.

# Install in editable mode
uv sync
# or: pip install -e .

Running Tests

Test dependencies (pytest and friends) live behind the test extra, not the base install — run this once before the commands below:

uv sync --extra test
# Run all offline tests
uv run pytest --cov=src/planka_mcp --cov-report=term-missing

# Run a specific test file
uv run pytest tests/test_cards.py -v

Opt-in live smoke test

The repo includes an opt-in live smoke path that creates a card, task, and label on a real Planka board, then cleans up. It is skipped unless you opt in.

Required environment variables:

  • PLANKA_RUN_LIVE_SMOKE=1

  • PLANKA_BASE_URL

  • One auth method: PLANKA_API_KEY, PLANKA_API_TOKEN, or PLANKA_EMAIL + PLANKA_PASSWORD

The smoke test targets any writable list on your Planka instance — no disposable list or label IDs are required. Point it at a safe test board (e.g. "Test Project").

pytest does not auto-load .env — source it into the shell first:

set -a; source .env; set +a
uv run pytest -m live tests/test_live_smoke.py -q --no-cov

Safety contract:

  • Creates a disposable card and checklist task, then removes them

  • Label add/remove uses a reusable board label

  • Cleanup runs in finally even on failure

  • Fresh-state assertions after task mutation use a new API client (not cached reads)

Card state and cache behavior

planka_get_card caches per-card detail payloads for up to 60 seconds. Card mutation tools that can affect detail output invalidate that card cache: comments, task lists/tasks, labels, members, custom field values, card updates, moves, and deletes.

planka_list_cards reads the board view live for every call. Preview and summary output use state present in that board response, including commentsTotal when comment bodies are not loaded. Detailed list output fetches fresh detail for each paginated card before rendering so tasks and comments reflect live Planka state instead of a stale detail cache.

Opt-in permission smoke (role tokens)

For board-permission verification, run the gated permission smoke with role-scoped tokens. Tests are role-dependent:

  • test_non_owner_live_smoke_permissions uses PLANKA_API_TOKEN_BOARD_MANAGER

  • test_board_member_live_smoke_permissions uses PLANKA_API_TOKEN_BOARD_MEMBER

Required environment variables:

  • PLANKA_RUN_PERMISSION_SMOKE=1

  • PLANKA_BASE_URL (or PLANKA_PERM_BASE_URL)

  • PLANKA_API_TOKEN_BOARD_MANAGER for board manager-role smoke

  • PLANKA_API_TOKEN_BOARD_MEMBER for board member-role smoke

  • Optional explicit disposable targets: PLANKA_SMOKE_LIST_ID and PLANKA_SMOKE_LABEL_ID (or PLANKA_PERM_SMOKE_*)

set -a; source .env; set +a
PLANKA_RUN_PERMISSION_SMOKE=1 uv run pytest -m live tests/test_live_smoke.py -q --no-cov

Test with MCP Inspector

npx @modelcontextprotocol/inspector uv run python mcp_server.py

Contributing

Dev environment setup

git clone https://github.com/andrejberg/ya-planka-mcp
cd ya-planka-mcp
uv sync                 # editable install, src/ on the Python path
cp .env.example .env    # fill in PLANKA_BASE_URL + a token before touching live tests

Before opening a PR

  1. Run the offline suite with coverage — CI (.github/workflows/test.yml) runs pytest --cov=src/planka_mcp --cov-report=xml --cov-fail-under=80 on every push/PR to main; match that locally:

    uv run pytest --cov=src/planka_mcp --cov-report=term-missing --cov-fail-under=80
  2. If the change touches role/permission behavior (anything in src/planka_mcp/handlers/workspace.py, card membership, or label handling), run the opt-in permission smoke locally against a real Planka instance with both role tokens before describing the change as verified — see "Opt-in permission smoke (role tokens)" above:

    set -a; source .env; set +a
    PLANKA_RUN_PERMISSION_SMOKE=1 uv run pytest -m live tests/test_live_smoke.py -q --no-cov

    This is not part of CI (it requires live credentials) — it is a pre-PR check for the author, not a merge gate.

  3. Keep the Roles & Permissions matrix above honest: if a PR changes what a board manager or board member token can do, update the matrix and its test in the same PR — don't let the docs drift from tests/test_live_smoke.py.

  4. New tools/handlers follow the existing layout: Pydantic models in src/planka_mcp/models.py, handler functions in src/planka_mcp/handlers/, matching tests in tests/.

PR expectations

  • Offline tests pass and coverage stays at or above 80% (CI enforces this).

  • No secrets (.env, tokens) committed.

  • README/docs updated when behavior, tools, or setup steps change.

Acknowledgements

Credits & Attribution

This project is a fork of another-planka-mcp by Roel van der Ven, adapted and substantially extended as ya-planka-mcp. The original implementation provided the foundation for Planka MCP integration; this fork introduces significant enhancements, new features, and production-oriented improvements.

License

MIT License. See LICENSE.

Install Server
A
license - permissive license
C
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/andrejberg/ya-planka-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server