@plukio/mcp-server
v0.18.0
Published
MCP (Model Context Protocol) server that gives AI coding agents access to Pluk tasks. Pull tasks, update status, leave notes for reviewers — all from within Claude Code, Cursor, or any MCP-compatible client.
Readme
@plukio/mcp-server
MCP (Model Context Protocol) server that gives AI coding agents access to Pluk tasks. Pull tasks, update status, leave notes for reviewers — all from within Claude Code, Cursor, or any MCP-compatible client.
Important: one server per project
Pluk API keys are scoped to a single site. Each of your client sites has its own key, its own tasks, and its own context. This means:
- Configure the MCP server locally in each project repo, not globally
- Each project gets the API key for its corresponding Pluk site
- Tasks, company defaults, and site context all come from the site tied to that key
This is by design — Pluk operates at the site level so your AI agent always has the right context for the project it's working in, without cross-site data leaking between repos.
Setup
1. Get your site's API key
Generate one from the Pluk dashboard under your site's settings. Keys start with pluk_.
2. Configure your MCP client
Always add this to the project directory (e.g.
.mcp.jsonin the repo root), not to your global config. Each project needs its own Pluk connection tied to the correct site.
Claude Code
Add to your project's .mcp.json:
{
"mcpServers": {
"pluk": {
"command": "npx",
"args": ["-y", "-p", "@plukio/mcp-server", "plukio-mcp-server"],
"env": {
"PLUK_API_KEY": "pluk_your_key_here"
}
}
}
}Or via CLI (run from the project directory):
claude mcp add pluk -- npx -y -p @plukio/mcp-server plukio-mcp-serverThen set PLUK_API_KEY in the MCP config's env block.
Cursor / Other MCP clients
Point to the same command (npx -y -p @plukio/mcp-server plukio-mcp-server) with PLUK_API_KEY set in the environment. Configure it per-project, not globally.
Environment variables
| Variable | Required | Description |
|---|---|---|
| PLUK_API_KEY | Yes | Your site's Pluk API key (site-scoped) |
| PLUK_API_URL | No | API base URL (defaults to https://pluk.io) |
Tools
pluk_list_tasks
List tasks with optional filters. Returns lightweight summaries sorted by priority (expedited first, then standard, in Kanban board order). Only backlog tasks are returned by default.
| Parameter | Type | Description |
|---|---|---|
| type | string | Filter by type: code, content, design, support |
| status | string | Comma-separated: backlog, on_deck, in_progress, needs_review, done |
| intent | string | expedited or standard |
| priority | string | normal or focus |
| zone | string | Strict positive filter on repo area — only tasks tagged with exactly this zone match; untagged tasks are excluded |
| exclude_zones | string[] | Skip tasks in these zones (max 64). Untagged tasks stay eligible — a null zone means nobody derived one yet, not "safe to skip" |
| limit | number | Max results (default 5, max 100) |
| offset | number | Skip N tasks for pagination |
Each returned task carries zone (string or null) — the repo-area id it was last tagged with, e.g. web-dashboard. Zone ids come from a repo's committed .plukit-zones.json, and the stored value is a cache, never truth: a Pluk site can front more than one repo, and repos restructure. Re-validate a zone against your own zone file and ignore ids you don't recognise.
pluk_claim_next_task
Atomically claim the next task for this coder and get it back already marked in_progress and owned by you, or { task: null }. Runs the whole pickup waterfall server-side — prefer it over pluk_list_tasks for picking up work.
| Parameter | Type | Required | Description |
|---|---|---|---|
| coder_id | string | Yes | Stable per-folder coder identity (e.g. contents of ./.plukit/id) |
| type | string | No | Task type slug to claim (default: any). Use code in coding sessions. |
| zone | string | No | Strict positive filter on repo area — only tasks tagged with exactly this zone are claimable; untagged tasks are excluded |
| exclude_zones | string[] | No | Skip tasks in these zones (max 64) — the common path, for avoiding areas another live worker holds. Untagged tasks stay claimable. |
| site_id | string | No | Site UUID to claim from. Only needed when the key spans multiple sites. |
Zone filters are applied inside the atomic claim, so a worker steering away from a busy area never races between peeking and claiming. The claimed task carries zone — re-validate it against your .plukit-zones.json before trusting it.
pluk_get_task
Get full task details by ID, including description, custom fields, site info, company defaults, and testing findings (review feedback). The response includes zone (string or null) — the repo-area id the task was last tagged with, a per-repo cache to re-validate rather than trust.
| Parameter | Type | Required | Description |
|---|---|---|---|
| task_id | string | Yes | Task UUID |
pluk_update_task
Update a task's status, title, or notes.
| Parameter | Type | Required | Description |
|---|---|---|---|
| task_id | string | Yes | Task UUID |
| title | string | No | Updated title (use to prepend "NEED INFO - ") |
| status | string | No | backlog, on_deck, in_progress, needs_review, done, blocked |
| ai_coder_notes | string | No | ELI5 summary for the reviewer. Required when setting needs_review. |
pluk_get_company
Get company context: tech stack, repo URL, staging URL, and active sites.
| Parameter | Type | Required | Description |
|---|---|---|---|
| company_id | string | Yes | Company UUID (from a task's organization field) |
pluk_whoami
Diagnostic health check. Takes no parameters. Confirms which API key, organization, and site the server authenticated as — run it first whenever tools return 401 or seem to be hitting the wrong org.
Returns:
{
"authenticated": true,
"organization": "s2s Communications",
"organization_id": "…",
"site": { "id": "…", "name": "…" },
"scopes": ["tasks:read", "tasks:write"],
"key_fingerprint": "a1b2"
}key_fingerprint is the last 4 characters of the key actually in use — compare it against the key in your config to confirm the server booted with the right one. A 401 here (rather than a result) means the key is missing or rejected; the error body includes key_status (key_missing vs key_rejected) and the fingerprint the server saw.
Rotating your API key
Rotating a key is not just a config edit — a stale server process or the Claude Code config cache can keep the old key alive. If tools 401 after a rotation even though the new key works via curl, follow this exactly:
- Rotate the key in the Pluk dashboard and copy the new one.
- Update
PLUK_API_KEYin both places — your project's.mcp.jsonand the cached copy in~/.claude.json(this cache overrides.mcp.json, so updating only one leaves the old key in play). - Verify the new key works:
curl -H "X-Pluk-API-Key: <NEW_KEY>" https://pluk.io/api/mcp/whoami - Kill orphaned server processes — a plain restart may reconnect to one still holding the revoked key:
pkill -f "@plukio/mcp-server" - Restart your MCP client (Claude Code) to spawn a fresh server.
- Run
pluk_whoamiand confirmkey_fingerprintmatches your new key and the org is correct.
Workflow
pluk_list_tasks (limit=1) → Get next task
pluk_get_task (task_id) → Read full details
pluk_update_task (in_progress) → Mark as started
... do the work ...
pluk_update_task (needs_review, ai_coder_notes) → Submit for reviewIf a task lacks enough detail to start:
pluk_update_task (title="NEED INFO - ...", status=on_deck)Development
# Build
pnpm build
# Watch mode
pnpm dev
# Test locally with Claude Code (point .mcp.json at local build)
# "args": ["./packages/mcp-server/dist/index.js"]License
Private — Pluk.io
