Productive Simple MCP
Productive.io MCP Server
A Model Context Protocol (MCP) server for integrating Productive.io into AI workflows. This server allows AI assistants and tools to access projects, folders, workflow statuses, time entries, tasks, comments, pages, attachments, todos, and people. Built with FastMCP.
This implementation is optimized for read-focused operations, with optional guarded write capabilities (for example, task creation) and LLM-friendly output options (JSON and TOON). It is optimized for efficiency and simplicity, exposing only the necessary information. For a more comprehensive solution, consider BerwickGeek's implementation: Productive MCP by BerwickGeek.
Features
Read Tools
List Projects: Retrieve all projects with basic information
List Folders: Retrieve folders within a project
Get Folder: Retrieve a specific folder by ID
List Workflow Statuses: Retrieve workflow statuses with optional filters
List Time Entries: Retrieve time entries with optional date and relationship filters
List Tasks: Retrieve tasks with filtering and pagination
Get Task: Retrieve a specific task by internal ID
Get Task History: Retrieve task status changes, assignments, milestones, and activity summaries
List Comments: Retrieve comments with filtering
List Pages: Retrieve pages/documents with filtering
Get Page: Retrieve a specific page/document by ID
List Attachments: Retrieve attachments/files with filtering
List Todos: Retrieve todo checklist items with filtering
Get Todo: Retrieve a specific todo by ID
List People: Retrieve people/team members with pagination
Get Person: Retrieve a specific person by ID
List Recent Activity: Summarized activity feed for status updates
Quick Search: Fast, comprehensive search across projects, tasks, pages, and actions
Write Tools (blocked when READ_ONLY=true)
Create Task: Create a new task in a project
Update Task: Update task fields — title, description, assignee, due date, status, board, task list
Delete Task: Permanently delete a task by ID (irreversible)
Create Comment: Create a new comment on a task or project
Update Comment: Update a comment body
Delete Comment: Permanently delete a comment by ID (irreversible)
Create Time Entry: Log time spent on tasks or services
Update Time Entry: Modify existing time entries
Delete Time Entry: Remove time entries (irreversible)
Create Page: Create new documents/pages in projects
Update Page: Edit page content and titles
Delete Page: Remove pages/documents (irreversible)
Create Todo: Add checklist items to tasks
Update Todo: Modify todo items and completion status
Delete Todo: Remove todo items (irreversible)
Additional Features
LLM-Optimized Responses: Filtered output removes noise, strips HTML, and reduces token consumption
Requirements
Python 3.10+
Productive API token
FastMCP 3.x
Installation
Clone or download this repository
Install dependencies:
pip install -r requirements.txtor
uv venv && uv syncConfiguration
The server uses environment variables for configuration:
PRODUCTIVE_API_KEY: Your Productive API token (required)PRODUCTIVE_ORGANIZATION: Your Productive organization ID (required)PRODUCTIVE_BASE_URL: Base URL for Productive API (default: https://api.productive.io/api/v2)PRODUCTIVE_TIMEOUT: Request timeout in seconds (default: 30)OUTPUT_FORMAT: Output format for tool responses ("toon" or "json", default: "toon")READ_ONLY: Global write-protection toggle for write tools — create_task, update_task, delete_task, create_comment, update_comment, delete_comment, create_time_entry, update_time_entry, delete_time_entry, create_page, update_page, delete_page, create_todo, update_todo, delete_todo ("true" or "false", default: "true")
Usage
Using uvx from GitHub (Recommended for MCP clients)
"productive": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/druellan/Productive-Simple-MCP",
"productive-mcp"
],
"env": {
"PRODUCTIVE_API_KEY": "<api-key>",
"PRODUCTIVE_ORGANIZATION": "<organization-id>"
}
}Using uvx from GitHub with TOON output enabled
"productive": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/druellan/Productive-Simple-MCP",
"productive-mcp"
],
"env": {
"PRODUCTIVE_API_KEY": "<api-key>",
"PRODUCTIVE_ORGANIZATION": "<organization-id>",
"OUTPUT_FORMAT": "toon"
}
}Local Development (Direct Python Execution)
"productive": {
"command": "python",
"args": [
"server.py"
],
"env": {
"PRODUCTIVE_API_KEY": "<api-key>",
"PRODUCTIVE_ORGANIZATION": "<organization-id>"
}
}Local Development Using UV
"productive": {
"command": "uv",
"args": [
"--directory", "<path-to-productive-mcp>",
"run", "server.py"
],
"env": {
"PRODUCTIVE_API_KEY": "<api-key>",
"PRODUCTIVE_ORGANIZATION": "<organization-id>"
}
}Available Tools
Read Tools
list_projects
Retrieve all projects with basic information.
Properties:
No parameters (returns all projects)
list_folders
Retrieve folders within a specific project.
Productive's API exposes these resources through the /folders endpoint.
Properties:
project_id(int, required): Filter folders by Productive project IDstatus(int, optional): Folder status filter (1active,2archived). Defaults to1limit(int, optional): Maximum number of folders to return (default: 50, max: 200)
get_folder
Retrieve a specific folder by its ID.
Productive's API exposes this resource through the /folders endpoint.
Properties:
folder_id(int): The unique Productive folder identifier
list_workflow_statuses
Retrieve workflow statuses with optional filters.
Properties:
workflow_id(int, optional): Filter statuses by workflow IDcategory_id(int, optional): Filter by category (1Not Started,2Started,3Closed)limit(int, optional): Maximum number of statuses to return (default: 50, max: 200)
list_time_entries
Retrieve time entries with optional date and relationship filters.
Properties:
date(str, optional): Exact date filter (YYYY-MM-DD)after(str, optional): Lower bound date filter (YYYY-MM-DD)before(str, optional): Upper bound date filter (YYYY-MM-DD)person_id(int, optional): Filter by person IDproject_id(int, optional): Filter by project IDtask_id(int, optional): Filter by task IDservice_id(int, optional): Filter by service IDpage_number(int, optional): Page number for paginationlimit(int, optional): Maximum number of entries to return (default: 50, max: 200)
list_people
Retrieve people/team members with optional pagination.
Properties:
page_number(int, optional): Page number for paginationpage_size(int, optional): Number of people to return per page (max: 200)
get_person
Retrieve a specific person by ID.
Properties:
person_id(int): The unique Productive person identifier
list_tasks
Retrieve tasks with optional filtering and pagination.
Properties:
project_id(int, optional): Filter tasks by Productive project IDuser_id(int, optional): Filter tasks by assignee/user IDpage_number(int, optional): Page number for paginationpage_size(int, optional): Page size for pagination (default: 50)sort(str, optional): Sort parameter (e.g., 'last_activity_at', '-last_activity_at', 'created_at', 'due_date')extra_filters(dict, optional): Additional Productive API filters (e.g.,{'filter[status][eq]': 1}for open tasks,{'filter[status][eq]': 2}for closed tasks)
get_task
Retrieve a specific task by its internal ID. Returns task details including title, description, status, dates, time tracking metrics (initial_estimate, worked_time, billable_time, remaining_time), and todo counts.
Properties:
task_id(int): The unique Productive task identifier (internal ID, e.g., 14677418)
get_task_history
Retrieve the full history for a specific task, including status changes, assignment history, milestones, and activity summary.
Properties:
task_id(int): The unique Productive task identifier (internal ID, e.g., 14677418)hours(int, optional): Number of hours to look back for activity history (default: 720 = 30 days, max: 8760)
Returns:
status_history: Timeline of status changes with timestamps (from/to status and changed_at)assignment_history: Assignment changes showing who was assigned and when (assigned_to and changed_at)milestones: Key deliverables and completion markers from comments and activitiesactivity_summary: Counts of comments, changes, status updates, assignments, and milestones
Example:
get_task_history(14677921) # Default 30-day history
get_task_history(14677921, hours=168) # Last week only
get_task_history(14677921, hours=24) # Last 24 hourslist_comments
Retrieve comments with optional filtering and pagination.
Properties:
project_id(int, optional): Filter comments by Productive project IDtask_id(int, optional): Filter comments by Productive task IDpage_number(int, optional): Page number for paginationpage_size(int, optional): Page size for paginationextra_filters(dict, optional): Additional Productive API filters (e.g.,{'filter[discussion_id]': '123'})
list_pages
Retrieve pages/documents with optional filtering and pagination.
Properties:
project_id(int, optional): Filter pages by Productive project IDcreator_id(int, optional): Filter pages by creator IDpage_number(int, optional): Page number for paginationpage_size(int, optional): Page size for pagination
get_page
Retrieve a specific page/document by ID.
Properties:
page_id(int): The unique Productive page identifier
list_attachments
Retrieve attachments/files with optional filtering and pagination.
Properties:
page_number(int, optional): Page number for paginationpage_size(int, optional): Page size for paginationextra_filters(dict, optional): Additional Productive API filters
list_recent_activity
Get a summarized feed of recent activities and updates. Perfect for status updates.
Properties:
hours(int, optional): Number of hours to look back (default: 24, use 168 for a week)user_id(int, optional): Filter by specific user/person IDproject_id(int, optional): Filter by specific project IDactivity_type(int, optional): Filter by activity type (1: Comment, 2: Changeset, 3: Email)item_type(str, optional): Filter by item type (e.g., 'Task', 'Page', 'Deal', 'Workspace')event_type(str, optional): Filter by event type (e.g., 'create', 'copy', 'update', 'delete')task_id(int, optional): Filter by specific task IDmax_results(int, optional): Maximum number of activities to return (default: 100, max: 200)
list_todos
Retrieve todo checklist items with optional filtering and pagination.
Properties:
task_id(int, optional): Filter todos by Productive task IDpage_number(int, optional): Page number for paginationpage_size(int, optional): Page size for paginationextra_filters(dict, optional): Additional Productive API filters
quick_search
Quick search across projects, tasks, pages, and actions.
Properties:
query(str): Search query stringsearch_types(list[str], optional): List of types to search (action, project, task, page). Defaults to all.deep_search(bool, optional): Whether to perform deep search (default: True)page(int, optional): Page number for pagination (default: 1)per_page(int, optional): Results per page (default: 50)
Description: Provides fast, comprehensive search across all Productive content types including projects, tasks, pages, and actions. It's optimized for quick lookups and general search queries.
Response Format: Returns filtered results optimized for LLM consumption with only essential fields:
record_id: Unique identifier for the resourcerecord_type: Type of resource (project, task, page, etc.)title: Display title (with search highlights removed)subtitle: Additional context or descriptionicon_url: URL to the resource's icon/avatar (if available)status: Current status (active, closed, etc.)project_name: Name of the associated projectupdated_at: Last update timestampwebapp_url: Direct link to view the resource in Productive web interface
Examples:
quick_search("deployment") # Search for "deployment" across all content types
quick_search("meeting notes", search_types=["project"]) # Search only in projects
quick_search("this week summary", deep_search=False) # Quick search without deep scanget_todo
Retrieve a specific todo checklist item by ID.
Properties:
todo_id(int): The unique Productive todo checklist item identifier
Write Tools
create_task
Create a new task in Productive.
When READ_ONLY=true, this tool is blocked globally and returns a write-protection error.
Properties:
title(str, required): Task titleproject_id(int, required): Productive project ID where the task will be createddescription(str, optional): Task descriptionboard_id(int, optional): Board IDtask_list_id(int, optional): Task list IDassignee_id(int, optional): Assignee/person IDdue_date(str, optional): Due date (YYYY-MM-DD)status(str, optional):openorclosed(default:open)
update_task
Update an existing task in Productive. Only provided fields are modified (partial PATCH).
At least one field must be given. When READ_ONLY=true, this tool is blocked globally.
Properties:
task_id(int, required): Productive task ID to updatetitle(str, optional): New task titledescription(str, optional): New task descriptionassignee_id(int, optional): New assignee person ID. Use 0 or negative to unassign.due_date(str, optional): New due date (YYYY-MM-DD)status(str, optional): New status —openorclosedboard_id(int, optional): Move task to this boardtask_list_id(int, optional): Move task to this task list
delete_task
Permanently delete a task from Productive by its ID. This action is irreversible —
the task and all associated data will be removed. When READ_ONLY=true, this tool is
blocked globally.
Properties:
task_id(int, required): Productive task ID to delete
create_comment
Create a new comment on a task or project in Productive. A comment must be attached to
at least one of task or project. When READ_ONLY=true, this tool is blocked globally.
Properties:
body(str, required): Comment body text (HTML supported)task_id(int, optional): Productive task ID to attach the comment toproject_id(int, optional): Productive project ID to attach the comment to
update_comment
Update an existing comment in Productive. Only the body attribute can be modified.
When READ_ONLY=true, this tool is blocked globally.
Properties:
comment_id(int, required): Productive comment ID to updatebody(str, required): New comment body text (HTML supported)
delete_comment
Permanently delete a comment from Productive by its ID. This action is irreversible —
the comment will be removed from the task or project. When READ_ONLY=true, this tool
is blocked globally.
Properties:
comment_id(int, required): Productive comment ID to delete
create_time_entry
Create a new time entry in Productive for time tracking.
Logs time spent on tasks or services. Either task_id or service_id must be provided.
When READ_ONLY=true, this tool is blocked globally.
Properties:
date(str, required): Date for the time entry (YYYY-MM-DD)time(float, required): Time spent in hours (e.g., 2.5)person_id(int, required): Person ID who logged the timetask_id(int, optional): Task ID to associate the time entry withservice_id(int, optional): Service ID to associate the time entry withnote(str, optional): Optional note or description
update_time_entry
Update an existing time entry in Productive. Only provided fields are modified (partial PATCH).
At least one field must be given. When READ_ONLY=true, this tool is blocked globally.
Properties:
time_entry_id(int, required): Productive time entry ID to updatedate(str, optional): New date (YYYY-MM-DD)time(float, optional): New time in hoursperson_id(int, optional): New person IDtask_id(int, optional): New task IDservice_id(int, optional): New service IDnote(str, optional): New note
delete_time_entry
Permanently delete a time entry from Productive by its ID. This action is irreversible —
the time entry will be removed from time tracking records. When READ_ONLY=true, this tool
is blocked globally.
Properties:
time_entry_id(int, required): Productive time entry ID to delete
create_page
Create a new page/document in a Productive project.
Pages are documents that can contain rich text content and are organized within projects.
When READ_ONLY=true, this tool is blocked globally.
Properties:
title(str, required): Page titleproject_id(int, required): Productive project ID where the page will be createdcontent(str, optional): Page content (HTML supported)
update_page
Update an existing page/document in Productive. Only provided fields are modified (partial PATCH).
At least one field must be given. When READ_ONLY=true, this tool is blocked globally.
Properties:
page_id(int, required): Productive page ID to updatetitle(str, optional): New page titlecontent(str, optional): New page content (HTML supported)
delete_page
Permanently delete a page/document from Productive by its ID. This action is irreversible —
the page and all its content will be removed. When READ_ONLY=true, this tool is blocked globally.
Properties:
page_id(int, required): Productive page ID to delete
create_todo
Create a new todo checklist item for a task in Productive.
Todos are checkbox items within tasks for granular tracking of work items.
When READ_ONLY=true, this tool is blocked globally.
Properties:
content(str, required): Todo item content/descriptiontask_id(int, required): Productive task ID to add the todo to
update_todo
Update an existing todo checklist item in Productive. Only provided fields are modified (partial PATCH).
At least one field must be given. When READ_ONLY=true, this tool is blocked globally.
Properties:
todo_id(int, required): Productive todo ID to updatecontent(str, optional): New todo contentcompleted(bool, optional): Mark todo as completed (true) or incomplete (false)
delete_todo
Permanently delete a todo checklist item from Productive by its ID. This action is irreversible —
the todo item will be removed from the task. When READ_ONLY=true, this tool is blocked globally.
Properties:
todo_id(int, required): Productive todo ID to delete
Output Format
All tools return filtered data optimized for LLM processing. The output format can be configured via the OUTPUT_FORMAT environment variable:
TOON (default): Token-Optimized Object Notation reduces token consumption by 30-60% compared to JSON, ideal for LLM interactions
JSON: Standard JSON format for compatibility with existing tools and workflows
All tools return filtered data optimized for LLM processing:
LLM Optimizations:
Unwanted fields removed (e.g.,
creation_method_id,email_key,placementfrom tasks)HTML stripped from descriptions and comments
Empty/null values removed
Pagination links removed
List views use lightweight output (e.g.,
get_project_tasksexcludes descriptions and relationships)Web app URLs included: Each resource includes a
webapp_urlfield linking directly to the Productive web interface
Response Structure:
data: Main resource data (array for collections, object for single items)meta: Pagination and metadataincluded: Related resource data (when applicable)webapp_url: Direct link to view the resource in Productive (e.g.,https://app.productive.io/12345/tasks/67890)
Error Handling
The server provides comprehensive error handling:
401 Unauthorized: Invalid API token
404 Not Found: Resource not found
429 Rate Limited: Too many requests
500 Server Error: Productive API issues
All errors are logged via MCP context with appropriate severity levels.
Security
API tokens are loaded from environment variables
No sensitive data is logged
HTTPS is used for all API requests
Error messages don't expose internal details
License
MIT License.
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/druellan/Productive-Simple-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server