An MCP (Model Context Protocol) server that enables Claude Desktop, Claude Code, and other MCP-compatible clients to interact with the Productive.io API.
- Task Briefing:
get_task_overviewreturns everything about one task in a single call, so reading an issue does not cost a dozen round trips - Companies & Projects: List companies and projects with status filtering
- Folders: Full CRUD with archive/restore for organizing project content
- Task Lists: Full lifecycle management — create, update, archive/restore, copy, move, reposition
- Task Management: List, create, update, delete tasks with various filters
- Subtasks: Create and list subtasks under parent tasks
- Task Operations: Comments, status updates, sprint assignment, repositioning
- Comments: Full CRUD with pin/unpin and reactions
- Todos: Checklist items on tasks — create, update, close/reopen, delete
- Pages/Docs: Full document management with nested page hierarchies, move, and copy
- People Management: List people in your organization with filtering options
- Workflow Management: List and work with workflow statuses for proper task status updates
- Time Tracking: List and create time entries with service/deal integration
- User Context: Supports "me" references when PRODUCTIVE_USER_ID is configured
- Activity Tracking: View activities and recent updates across your organization
Install globally:
npm install -g productive-mcpOr run directly with npx (no installation required):
npx productive-mcp- Clone this repository
- Install dependencies:
npm install
- Build the project:
npm run build
To obtain your Productive.io credentials:
- Log in to Productive.io
- Go to Settings → API integrations
- Generate a new token (choose read-only for safety, or full access for task creation)
- Copy the token and organization ID
To find your user ID:
- You can use the API to list people and find your ID
- Or check the URL when viewing your profile in Productive.io
The server requires the following environment variables:
| Variable | Required | Description |
|---|---|---|
PRODUCTIVE_API_TOKEN |
Yes | Your Productive.io API token |
PRODUCTIVE_ORG_ID |
Yes | Your organization ID |
PRODUCTIVE_USER_ID |
No | Your user ID (required for my_tasks tool) |
Add the server to your Claude Desktop configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"productive": {
"command": "npx",
"args": ["-y", "productive-mcp"],
"env": {
"PRODUCTIVE_API_TOKEN": "your_api_token_here",
"PRODUCTIVE_ORG_ID": "your_organization_id_here",
"PRODUCTIVE_USER_ID": "your_user_id_here"
}
}
}
}{
"mcpServers": {
"productive": {
"command": "productive-mcp",
"env": {
"PRODUCTIVE_API_TOKEN": "your_api_token_here",
"PRODUCTIVE_ORG_ID": "your_organization_id_here",
"PRODUCTIVE_USER_ID": "your_user_id_here"
}
}
}
}{
"mcpServers": {
"productive": {
"command": "node",
"args": ["/path/to/productive-mcp/build/index.js"],
"env": {
"PRODUCTIVE_API_TOKEN": "your_api_token_here",
"PRODUCTIVE_ORG_ID": "your_organization_id_here",
"PRODUCTIVE_USER_ID": "your_user_id_here"
}
}
}
}Note: PRODUCTIVE_USER_ID is optional but required for the my_tasks tool to work.
After adding the configuration, restart Claude Desktop.
Add the server to your Claude Code configuration using the CLI:
claude mcp add productive -- npx -y productive-mcpThen set your environment variables. You can either:
Option 1: Add to your shell profile (~/.zshrc or ~/.bashrc):
export PRODUCTIVE_API_TOKEN="your_api_token_here"
export PRODUCTIVE_ORG_ID="your_organization_id_here"
export PRODUCTIVE_USER_ID="your_user_id_here"Option 2: Create a wrapper script and add it as an MCP server:
-
Create a script file (e.g.,
~/scripts/productive-mcp.sh):#!/bin/bash export PRODUCTIVE_API_TOKEN="your_api_token_here" export PRODUCTIVE_ORG_ID="your_organization_id_here" export PRODUCTIVE_USER_ID="your_user_id_here" npx -y productive-mcp
-
Make it executable:
chmod +x ~/scripts/productive-mcp.sh -
Add to Claude Code:
claude mcp add productive ~/scripts/productive-mcp.sh
Option 3: Edit the Claude Code settings file directly at ~/.claude/settings.json:
{
"mcpServers": {
"productive": {
"command": "npx",
"args": ["-y", "productive-mcp"],
"env": {
"PRODUCTIVE_API_TOKEN": "your_api_token_here",
"PRODUCTIVE_ORG_ID": "your_organization_id_here",
"PRODUCTIVE_USER_ID": "your_user_id_here"
}
}
}
}Restart Claude Code after configuration.
Use get_task_overview first. It answers "what is this issue about" in one call:
get_task_overview(task_id: "19300600")
It returns metadata (status, assignee, project, task list, dates, estimate vs worked time),
the full original description, then the 10 most recent comments with their complete bodies in
chronological order. HTML is rendered to plain text and stored @mention blobs are collapsed
to names, so the thread reads as prose.
Attachments are surfaced two ways, because most of them are screenshots that carry the context you need:
- Inline, at the exact point in the comment where the screenshot was posted, as
[attachment 9131629: Screenshot_2026-07-31_110620.png]. - Indexed, in an
ATTACHMENTSblock at the end listing every attachment on the task and on the comments shown, flagged[IMAGE], with the source comment and author.
Then fetch only the ones that matter with get_attachment(attachment_id: "9131629"), which
returns images inline.
The older path (get_task, then list_comments, then a get_comment per truncated comment)
still works, but costs one round trip per comment and truncates bodies to 200 characters.
| Tool | Description |
|---|---|
whoami |
Get current user context and configured user ID |
| Tool | Description |
|---|---|
list_companies |
List companies/customers. Filter by status (active/archived), limit |
list_projects |
List projects. Filter by status, company_id, limit |
| Tool | Description |
|---|---|
list_folders |
List folders in a project. Filter by project_id, status (1=active, 2=archived), limit |
get_folder |
Get folder details by folder_id |
create_folder |
Create a folder. Requires project_id, name |
update_folder |
Rename a folder. Requires folder_id, optional name |
archive_folder |
Archive a folder by folder_id |
restore_folder |
Restore an archived folder by folder_id |
| Tool | Description |
|---|---|
list_boards |
List boards. Filter by project_id, limit |
create_board |
Create a board. Requires project_id, name |
list_task_lists |
List task lists. Filter by board_id, limit |
create_task_list |
Create a task list. Requires board_id, project_id, name |
get_task_list |
Get task list details by task_list_id |
update_task_list |
Rename a task list. Requires task_list_id, optional name |
archive_task_list |
Archive a task list by task_list_id |
restore_task_list |
Restore an archived task list by task_list_id |
copy_task_list |
Copy a task list. Requires name, template_id, project_id, board_id. Optional copy_open_tasks, copy_assignees |
move_task_list |
Move a task list to another board. Requires task_list_id, board_id |
reposition_task_list |
Reorder a task list. Requires task_list_id, move_before_id |
| Tool | Description |
|---|---|
get_task_overview |
Start here for any task ID. One call returns metadata, the full description, the most recent comments in full (default 10, comment_limit up to 50) oldest-first, and an index of every attachment on the task and those comments. Requires task_id |
list_tasks |
List tasks. Filter by project_id, assignee_id, status (open/closed), limit |
get_project_tasks |
Get all tasks for a project. Requires project_id, optional status |
get_task |
Get task details by task_id. Metadata only, no comments. Prefer get_task_overview |
create_task |
Create a task. Requires title. Optional project_id, board_id, task_list_id, assignee_id ("me" supported), due_date, status |
update_task_assignment |
Assign/unassign a task. Requires task_id, assignee_id ("me" or "null" supported) |
update_task_details |
Update title/description. Requires task_id, optional title, description, description_html |
update_task_status |
Set workflow status by name or ID. Requires task_id and either status_name (e.g. "In Progress", "On Hold") or workflow_status_id. Automatically resolves the task's project workflow, supports custom statuses |
delete_task |
Delete a task by task_id |
my_tasks |
Get tasks assigned to you. Optional status, limit |
reposition_task |
Reorder a task within a list |
update_task_sprint |
Move task to a sprint/task list |
move_task_to_list |
Move a task to a different task list |
add_to_backlog |
Move a task to the backlog |
| Tool | Description |
|---|---|
list_task_dependencies |
List dependencies for a task. Filter by task_id (what it blocks) or dependent_task_id (what blocks it) |
get_task_dependency |
Get dependency details by dependency_id |
create_task_dependency |
Create a dependency. Requires task_id (blocker), dependent_task_id (blocked). Optional type_id: 1 = blocks (default), 2 = is blocked by, 3 = related to |
delete_task_dependency |
Remove a dependency by dependency_id |
| Tool | Description |
|---|---|
list_subtasks |
List subtasks of a parent task. Requires parent_task_id, optional limit |
create_subtask |
Create a subtask. Requires parent_task_id, title. Optional project_id, task_list_id, assignee_id, due_date, description |
| Tool | Description |
|---|---|
add_task_comment |
Add a comment to a task. Requires task_id, comment (supports HTML and @mentions). Optional hidden (boolean) posts an internal comment not visible to clients in the client portal |
list_comments |
List comments. Filter by task_id, project_id, limit. Bodies are truncated to 200 chars; to read a task's thread use get_task_overview instead |
get_comment |
Get full comment details by comment_id |
update_comment |
Edit a comment. Requires comment_id, body |
delete_comment |
Delete a comment by comment_id |
pin_comment |
Pin a comment by comment_id |
unpin_comment |
Unpin a comment by comment_id |
add_comment_reaction |
Add a reaction. Requires comment_id, reaction (e.g. "like") |
| Tool | Description |
|---|---|
list_todos |
List todos on a task. Filter by task_id, status (open/closed), limit |
get_todo |
Get todo details by todo_id |
create_todo |
Create a todo. Requires description. Optional task_id, deal_id, assignee_id, due_date |
update_todo |
Update a todo. Requires todo_id. Optional description, closed (boolean), due_date |
delete_todo |
Delete a todo by todo_id |
| Tool | Description |
|---|---|
list_pages |
List pages. Filter by project_id, sort (title/created_at/edited_at/updated_at), limit |
get_page |
Get full page content by page_id |
create_page |
Create a page. Requires project_id, title. Optional body (HTML), parent_page_id, root_page_id |
update_page |
Update a page. Requires page_id. Optional title, body |
delete_page |
Delete a page by page_id |
move_page |
Move page under another. Requires page_id, target_doc_id |
copy_page |
Copy a page. Requires template_id. Optional project_id |
| Tool | Description |
|---|---|
list_workflow_statuses |
List workflow statuses. Filter by workflow_id, category_id (1=Not Started, 2=Started, 3=Closed), limit |
| Tool | Description |
|---|---|
list_time_entries |
List time entries. Filter by date, after, before, person_id, project_id, task_id, service_id |
create_time_entry |
Create a time entry. Requires date, time (minutes), person_id, service_id. Optional task_id, note |
list_services |
List services. Filter by company_id, limit |
get_project_services |
Get services for a project |
list_project_deals |
List deals/budgets for a project |
list_deal_services |
List services for a deal/budget |
| Tool | Description |
|---|---|
list_activities |
List activities. Filter by task_id, project_id, person_id, item_type, event, after, before |
get_recent_updates |
Get recent updates. Optional limit, hours |
You can update a task's status by name — no need to look up IDs:
update_task_status {
"task_id": "12399194",
"status_name": "On Hold"
}
The tool automatically resolves the task's project workflow and matches the status name (case-insensitive, supports partial matching). This works with custom workflow statuses too.
If the name doesn't match or is ambiguous, it returns the available statuses for that project:
No workflow status matching "banana" found.
Available statuses:
• "Pending" (ID: 102305) — Not Started
• "Open" (ID: 102291) — Started
• "On Hold" (ID: 102306) — Started
• "Waiting" (ID: 102307) — Started
• "Closed" (ID: 102292) — Closed
You can also pass workflow_status_id directly if you already know the ID.
When PRODUCTIVE_USER_ID is configured, you can use "me" in several tools:
create_taskwith"assignee_id": "me"update_task_assignmentwith"assignee_id": "me"my_tasksto get your assigned taskswhoamito verify your configured user context
- Create a folder:
create_folder - Create task lists:
create_task_list - Create tasks:
create_task - Break down work:
create_subtaskfor sub-items,create_todofor checklists - Add comments:
add_task_comment - Update status:
update_task_statuswithstatus_name(e.g. "Open", "On Hold", "Closed") - Track progress: Use
list_activitiesorget_recent_updates
- Create a root page:
create_pagewithproject_idandtitle - Add child pages:
create_pagewithparent_page_idandroot_page_idset to the root - Nest deeper: Set
parent_page_idto the parent androot_page_idto the root page - Reorganize: Use
move_pageto reparent pages,copy_pageto duplicate
- Run in development mode:
npm run dev - Build:
npm run build - Start built server:
npm start
ISC