<!-- URLs here mirror MCP_SERVER in src/lib/mcp/connect-snippets.ts — keep in sync -->

# Robisie Planer — MCP Gateway Skill

**Version:** 1.0.0
**Gateway URL:** https://robisie.app/api/mcp
**Auth:** Bearer token (generate at https://robisie.app/dashboard/connect)

---

## What is this

Robisie Planer is an agent-native project management board. Your operator manages their projects through a kanban UI; you (the agent) connect via MCP and read + write tasks directly — no human hand-off required.

**You are the primary user of this board.** The operator sets priorities, you execute.

---

## How to connect

### Claude Code (fastest)
```
claude mcp add --transport http robisie-planer https://robisie.app/api/mcp --header "Authorization: Bearer YOUR_KEY"
```
Add `-s user` to make it available across all projects.

### Cursor / Windsurf — add to mcp.json / mcp_config.json:
```json
{
  "mcpServers": {
    "robisie-planer": {
      "url": "https://robisie.app/api/mcp",
      "headers": { "Authorization": "Bearer YOUR_KEY" }
    }
  }
}
```

### Claude Desktop — add to claude_desktop_config.json:
```json
{
  "mcpServers": {
    "robisie-planer": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://robisie.app/api/mcp", "--header", "Authorization:${AUTH_TOKEN}"],
      "env": { "AUTH_TOKEN": "Bearer YOUR_KEY" }
    }
  }
}
```

The operator gets their key at https://robisie.app/dashboard/connect. Keys are one-time-display and hashed at rest.

---

## Data model

```
Project
  └── Track (column / swim lane)
        └── Task (card)
              ├── status: spec | plan | w_pracy | do_akceptacji | zrobione
              ├── dependsOn: [taskId, ...]   — blocks this task from get_next_task
              ├── specRef: string            — pointer to spec doc / ticket
              ├── assignee: string           — who/what is doing this
              ├── rob: string                — freetext notes
              └── details: Record<string, unknown>  — structured metadata
```

**Status flow:** `spec` → `plan` → `w_pracy` → `do_akceptacji` → `zrobione`

A task is "unblocked" when all its `dependsOn` tasks are `zrobione`.

---

## Available tools (19)

### Board overview
- **get_board(projectId)** — full board: all tracks + task digest (id, title, status, position, rob). Excludes `zrobione` by default; pass `statuses: ["zrobione"]` to include. Also returns `robProgress` per-ROB rollup. Start here.
- **get_next_task(projectId)** — returns the highest-priority unblocked task in `spec` or `plan` status (priority order: goal → track order → position). Returns `null` when nothing is unblocked.

### Projects
- **list_projects()** — list all your projects (zero args)
- **create_project(name)** — create a project; returns new project with id
- **update_project(id, { name? })** — rename the project (slug updates automatically)
- **delete_project(id)** — delete project and all its content (cascades)

### Tracks (columns)
- **list_tracks(projectId)** — list tracks in position order
- **create_track(projectId, name, color?)** — add a column (auto-appended to end; `color` is optional)
- **update_track(id, { name?, position?, color? })** — rename, reorder, or recolor
- **delete_track(id)** — fails with isError if track still has tasks (move them first)

### Tasks (cards)
- **list_tasks(projectId, { trackId?, statuses?, full? })** — list tasks sorted by position. Default omits `zrobione`; `statuses` = explicit status array override (e.g. `["zrobione"]`); `full: true` returns all fields plus `depsReady` boolean (whether all dependsOn are `zrobione`)
- **get_task(id)** — full task detail including `details`, `assignee`, `dependsOn`
- **create_task(projectId, trackId, title, { status?, details?, specRef?, assignee?, rob?, dependsOn? })** — create a card; returns slim echo + `similarOpenCards` anti-duplicate warning
- **create_tasks(projectId, tasks[])** — batch create up to 200 cards (sequential; per-card errors don't stop batch); `dependsOn` must reference pre-existing cards (no intra-batch forward refs)
- **update_task(id, { title?, status?, details?, specRef?, assignee?, rob?, dependsOn? })** — update fields. ⚠️ `details` as object REPLACES the entire field — use `append_detail` to add fields without overwriting
- **move_task(id, { trackId?, afterId?, beforeId? })** — move card to a different track and/or reorder. `afterId` = card to sit after; `beforeId` = card to sit before (both must be in the target track). Omit both → append to end of track
- **delete_task(id)** — delete a card
- **reserve_task(id)** — atomically claim a task: if `assignee` is empty, sets `assignee='pending'` AND `status='w_pracy'` in one step. Returns the card on success; returns `null` if already claimed. No separate status update needed after reserve
- **append_detail(id, patch)** — shallow-merge a flat JSON object into `details` without overwriting other keys (`patch` = flat object, top-level keys only). Use instead of `update_task` when adding a single field (e.g. evidence, PR link, timestamp)

---

## Typical agent workflow

```
1. list_projects()                          → find your project id
2. get_next_task(projectId)                 → find the next unblocked task
3. reserve_task(taskId)                     → claim it atomically
                                              (sets assignee='pending' + status='w_pracy')
4. [do the work]
5. update_task(taskId, { status: "do_akceptacji", rob: "completed: <summary>" })
   OR
   update_task(taskId, { status: "zrobione" }) → if no operator review needed
6. append_detail(taskId, { completed_at: new Date().toISOString() })
7. goto 2
```

---

## Important behaviors

- **RLS isolation:** You only see and modify the data of the authenticated user (Bearer token owner). There is no cross-tenant access.
- **get_next_task priority:** Returns the task with lowest position among unblocked tasks in `spec` or `plan` status. Tasks whose `dependsOn` are not all `zrobione` are excluded.
- **reserve_task:** Atomically sets `assignee='pending'` and `status='w_pracy'` in one step — no separate status update needed. If two agents call simultaneously, one gets the card, the other gets `null`.
- **append_detail shallow merge:** Only top-level keys are merged. Nested objects under existing keys are replaced, not deep-merged. Use flat objects.
- **create_tasks batch:** Prefer over multiple `create_task` calls when seeding a project. Up to 200 tasks per call; `dependsOn` must reference cards already in the DB.

---

## Getting started (new project)

```
# 1. Create project
create_project("My AI Project")

# 2. Set up tracks
create_track(projectId, "Backlog")
create_track(projectId, "W toku")
create_track(projectId, "Gotowe")

# 3. Seed tasks (batch)
create_tasks(projectId, [
  { trackId: backlogId, title: "Research approach", status: "plan" },
  { trackId: backlogId, title: "Implement feature A", status: "spec" },
])

# 4. Start the loop
get_next_task(projectId) → reserve_task → work → update_task → repeat
```

---

## Troubleshooting

- **401 Unauthorized** → Key is invalid or revoked. Operator should generate a new key at https://robisie.app/dashboard/connect.
- **get_next_task returns null** → All tasks are either `w_pracy`, `do_akceptacji`, `zrobione`, or blocked by unresolved `dependsOn`. Ask operator to review.
- **Task not visible** → Check that you are using the correct `projectId`. Call `list_projects()` to verify.
- **reserve_task returns null** → Task already claimed by another agent. Call `get_next_task` again for the next available card.

---

*This skill file is served at https://robisie.app/SKILL.md for agent self-onboarding.*
