API reference
MCP API
Connect MCPlan to any MCP-compatible client — Claude Desktop, Claude Code, Cursor, or a script you write yourself — and let it read and write your projects, sections, tasks, and labels.
Looking for the full tool list? Browse the MCP tool catalog.
Getting connected
- Sign in and generate an API key under Settings → API keys. Personal keys act on your personal workspace; organization keys act on an org (optionally scoped to a team).
- Point your MCP client at
https://api.mcplan.ai/mcpand pass the key as a bearer token. - All tool calls run server-side through Firestore with the caller's permissions. Every tool call and resource read counts against the caller's monthly cap: Free 50, Pro 1,000, Team 1,000 per seat (pooled across the org). Counters reset on the 1st of each month (UTC).
Hand-off from the UI
Every task in the MCPlan web app exposes three quick agent actions — available from the kebab menu in the task dialog and as a one-click ClipboardList icon in the task header:
- Copy agent prompt. Copies a self-contained prompt (task id, project id, title, description, sub-tasks, web link, plus step-by-step MCP instructions) to the clipboard. Paste into any MCP-aware agent — the agent fetches the task with
list_tasks/search_tasks, optionally reads attachments, does the work, then callscomplete_task. - Open in Claude. Opens
claude.ai/newin a new tab with the same prompt pre-filled — zero copy/paste. - Copy task link. Copies a shareable web URL of the form
https://mcplan.ai/projects/<projectId>?task=<taskId>— paste into Slack, email, or any tracker to deep-link straight into the task dialog.
The prompt is fully customisable per user under Settings → Agents → Agent prompt template using handlebars-style placeholders ({{taskId}}, {{title}}, {{description}}, {{subtasks}}, {{webUrl}}, etc.) and conditional blocks {{#description}}…{{/description}}.
Projects
| Tool | Input | Notes |
|---|---|---|
list_projects | { orgId?: string } | List active projects for the authenticated caller. Returns: Array of projects with `activeTaskCount`. Without `orgId`, returns personal projects. With `orgId`, returns org projects (team-scoped keys are limited to their team). |
create_project | { name, color?, view?: 'list' | 'board', icon? } | Create a personal project. Returns: The new project document. |
update_project | { projectId, name?, color?, view? } | Rename or restyle a project. |
delete_project | { projectId } | Delete a project and cascade-delete every task inside it. |
Sections
Sections live on the parent project document, not as standalone records. Use them to group tasks within a project.
| Tool | Input | Notes |
|---|---|---|
list_sections | { projectId } | List sections in a project, sorted by `order`. Returns: `[{ id, name, order, description? }]`. `description` only present if set. |
create_section | { projectId, name, description? } | Add a new section to the end of the project. |
update_section | { projectId, sectionId, name?, description? } | Rename a section or set/clear its description. Pass `description: ''` or `null` to clear. Section descriptions are visible in the edit dialog and appear in `list_sections` — good place to store context for AI workflows. |
delete_section | { projectId, sectionId } | Remove a section. Tasks currently in the section become unassigned (not deleted). |
Tasks
Subtasks are regular tasks with parentTaskId set. There is no hard depth limit, but the web UI renders two levels.
| Tool | Input | Notes |
|---|---|---|
list_tasks | { projectId, sectionId?, status?: 'active' | 'completed' | 'all', priority?: 1–4 } | List tasks in a project with optional filters. Org members see every task in shared org projects. Non-members only see tasks they created or were assigned to. |
create_task | { projectId, title, sectionId?, priority?: 1–4, dueDate?: 'YYYY-MM-DD', labels?: string[], description?, parentTaskId?, assignedTo?: string[], orgId?, teamId? } | Create a task in a project, optionally as a subtask. Labels on the caller's API key are merged into `labels` automatically, so calls from a scoped key stay tagged. |
update_task | { taskId, title?, description?, priority?, dueDate?: string | null, labels?, sectionId?: string | null } | Partial update — only provided fields are written. |
complete_task | { taskId } | Mark a task completed (stamps `completedAt`). |
delete_task | { taskId } | Delete a task and every subtask under it. Returns: `{ success, taskId, deletedSubtasks: number }`. |
search_tasks | { query?, priority?, labels?: string[], dueBefore?: 'YYYY-MM-DD', dueAfter?: 'YYYY-MM-DD' } | Search across all active tasks for the caller. Text match is case-insensitive title substring. Limited to 100 results. |
Labels
Labels are stored on the user document as a Record<string, { name, color }>. Tasks reference labels by ID, not name.
| Tool | Input | Notes |
|---|---|---|
list_labels | { } | List all labels defined on the caller's user document. |
create_label | { name, color } | Create a label. Returns the generated label ID. |
update_label | { labelId, name?, color? } | Rename or recolor a label. |
delete_label | { labelId } | Delete a label. Existing tasks keep the label ID — rename or remove them manually if needed. |
Import / export
MCPlan plans are round-trippable markdown documents. AI agents can draft a whole project in markdown, call import_plan, and later re-read the current state via export_plan.
| Tool | Input | Notes |
|---|---|---|
import_plan | { markdown } | Create a new project from a markdown plan document (see the format below). Returns: The new project ID plus counts of imported sections and tasks. |
export_plan | { projectId } | Serialize a project back to markdown. Returns: Markdown string round-trippable through `import_plan` (subtasks, labels, priorities, descriptions preserved). |
Plan markdown format
---
project: Launch checklist
color: "#4f46e5"
view: list
---
## Design
- [ ] Create wireframes
id: task_1
priority: 1
due: 2026-05-01
labels: [ui, design]
description: Need mobile and desktop variants.
- [ ] Mobile version
- [x] Desktop version
## Engineering
- [ ] Implement auth
priority: 2
labels: [backend]
- [x] Set up repo
# Unsectioned
- [ ] Buy domain- YAML frontmatter carries
project,color,view, and optionalidfor round-tripping. ## Headingdefines a section; tasks under it inherit that section.- Task metadata lives indented under the bullet (two-space indent):
id,priority,due,labels,description. - Nested bullets become subtasks.
- [x]marks a task completed. - A trailing
# Unsectionedheading groups tasks not assigned to any section.
Organizations
| Tool | Input | Notes |
|---|---|---|
list_org_members | { orgId, teamId? } | List members of an organization (optionally scoped to one team). |
Common workflows
Draft a plan from a rough spec
Ask the AI to draft a markdown plan, then call import_plan. You get a real project with real task IDs you can iterate on later.
Daily review
Call search_tasks with dueBefore: today and priority: 1 to surface the day's priorities. Use complete_task as you work through them.
Keep AI context fresh
Put long-running context (definitions, conventions, links) in update_section descriptions. Agents read them via list_sections without polluting task titles.
Scoped automations
Issue an API key with labels like ai or automation. Everything the key creates gets tagged automatically, so you can filter for AI-generated work in the web UI.