productive-mcp
An MCP server for Productive.io that enables users to log time, inspect projects, and manage time entries using natural language commands. It features fuzzy project matching, local caching, and remembers default services per project for streamlined time tracking.
README
productive-mcp
An MCP server for Productive.io — log time, inspect projects, manage entries, and get team reports from any MCP-compatible client (Claude Code, Claude Desktop, Cursor, etc.) using plain English:
"log 2.5 hours on the Acme security review project"
"how many hours did Alice log this week?"
"give me my weekly briefing"
Built around the Productive.io JSON:API v2 with fuzzy project and person matching, a local disk cache, billing-cutoff-aware period resolution, and per-project default-service memory.
Features
- 14 tools covering projects, people, services, time entries, reports, and briefings
- Cross-platform credential storage — macOS Keychain, Windows Credential Manager (DPAPI/TPM-backed), Linux Secret Service, or environment variables
- Fuzzy matching for projects and people —
"Acme","1099 Acme","Alice", or"me"all resolve naturally - Billing-cutoff-aware period resolution —
"this_month"shifts based on your invoicing cycle - Remembers your default service per project so you don't have to specify it every time
- Local cache for projects, people, and services (1-hour TTL, manually refreshable)
- TOON output mode — optional token-optimised encoding for 30–60% fewer tokens
hoursin,hoursout — the API uses minutes internally, but you never see them- Scoped to "me" by default —
list_time_entriesonly shows your own entries unless you opt out
Requirements
- Python 3.11+
- A Productive.io account with API access enabled
- macOS, Windows, or Linux
Install
1. Clone and install
git clone https://github.com/cameronfairbairn/productive-mcp.git
cd productive-mcp
uv venv
uv pip install -e .
(or python -m venv .venv && .venv/bin/pip install -e . if you don't use uv)
2. Get your Productive credentials
You need three values:
| Value | Where to find it |
|---|---|
| API token | Productive → Settings → API integrations → Generate new token |
| Organization ID | The numeric segment in your Productive URL: app.productive.io/<ORG_ID>/… |
| Person ID | Your own user ID — open your profile in Productive; it's the numeric segment in the URL |
3. Store credentials
Option A — OS credential store (recommended)
Works on any platform via the keyring library:
python -c "import keyring; keyring.set_password('productive-mcp', 'token', '<token>')"
python -c "import keyring; keyring.set_password('productive-mcp', 'org_id', '<org_id>')"
python -c "import keyring; keyring.set_password('productive-mcp', 'person_id', '<person_id>')"
| Platform | Backend | Security |
|---|---|---|
| macOS | Keychain | Secure Enclave where available |
| Windows | Credential Manager (DPAPI) | TPM-backed on TPM 2.0 systems |
| Linux (desktop) | Secret Service (GNOME Keyring / KWallet) | Session-encrypted |
On macOS, the security CLI also works (keyring reads from the same Keychain):
security add-generic-password -s productive-mcp -a token -w "<token>" -U
security add-generic-password -s productive-mcp -a org_id -w "<org_id>" -U
security add-generic-password -s productive-mcp -a person_id -w "<person_id>" -U
Option B — environment variables (CI / headless / override)
export PRODUCTIVE_MCP_TOKEN="<token>"
export PRODUCTIVE_MCP_ORG_ID="<org_id>"
export PRODUCTIVE_MCP_PERSON_ID="<person_id>"
Environment variables take precedence over the credential store.
4. Register the server with your MCP client
Claude Code (~/.claude.json)
{
"mcpServers": {
"productive": {
"type": "stdio",
"command": "/path/to/productive-mcp/.venv/bin/productive-mcp",
"args": []
}
}
}
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"productive": {
"command": "/path/to/productive-mcp/.venv/bin/productive-mcp"
}
}
}
Restart the client after editing its config.
5. (Optional) Global install with the bundled deploy script
bash scripts/install.sh
This creates ~/.local/share/productive-mcp/ containing a fresh venv and a run.sh launcher. Point your MCP client at ~/.local/share/productive-mcp/run.sh instead. Re-running the script upgrades in place.
Tools
All tools are prefixed with productive_ so they namespace cleanly alongside other MCP servers.
Time tracking
| Tool | Purpose |
|---|---|
productive_log_time |
Create a time entry |
productive_list_time_entries |
List time entries with date/project/owner filters |
productive_update_time_entry |
Edit hours / date / note / service on an existing entry |
productive_delete_time_entry |
Permanently delete a time entry |
Reports and briefings
| Tool | Purpose |
|---|---|
productive_get_time_report |
Categorised hours summary (worked/client/internal/holidays) |
productive_get_employee_hours |
Hours summary for any team member by name |
productive_my_projects |
Projects you've logged time on recently |
productive_my_briefing |
Weekly summary with hours-by-project breakdown |
Projects, people, and services
| Tool | Purpose |
|---|---|
productive_list_projects |
List all active projects |
productive_find_project |
Fuzzy-search projects by name and/or number |
productive_find_person |
Fuzzy-search people by name or email |
productive_list_services |
List services (billable activity types) on a project |
Administration
| Tool | Purpose |
|---|---|
productive_refresh_cache |
Force an immediate cache refresh |
productive_set_default_service |
Override the remembered default service for a project |
Key tool details
productive_get_time_report
period : str? — "this_month", "last_month", "this_week", "last_week"
after : str? — ISO date; ignored if period set
before : str? — ISO date; ignored if period set
person : str? — Name, email, id, or "me" (default)
project : str? — Scope to a specific project
Returns totals_hours: {worked, client, internal, holidays}. When PRODUCTIVE_BILLING_CUTOFF_DAY is set, "this_month" shifts based on your billing cycle.
productive_my_briefing
period : str — Default "this_week"
Returns total hours, projects touched, hours-by-project breakdown, and recent entries. Designed for the Monday-morning "what am I working on?" question.
productive_get_employee_hours
person : str — Fuzzy name, email, or "me"
period : str? — Symbolic period (default: no filter)
after : str? — ISO date
before : str? — ISO date
Finds a person by fuzzy match, returns their hours for the period.
Configuration
Environment variables
| Variable | Default | Purpose |
|---|---|---|
PRODUCTIVE_MCP_TOKEN |
— | API token (overrides credential store) |
PRODUCTIVE_MCP_ORG_ID |
— | Organization ID (overrides credential store) |
PRODUCTIVE_MCP_PERSON_ID |
— | Person ID (overrides credential store) |
PRODUCTIVE_BILLING_CUTOFF_DAY |
unset | Day of month (1–31) when "this_month" flips to the current calendar month |
PRODUCTIVE_MCP_OUTPUT_FORMAT |
json |
Set to toon for Token-Optimized Object Notation (30–60% fewer tokens) |
Billing cutoff day
The PRODUCTIVE_BILLING_CUTOFF_DAY controls how symbolic periods resolve for invoicing workflows:
- Before cutoff day:
"this_month"= previous calendar month (you're still closing invoices) - From cutoff day onward:
"this_month"= current calendar month - Unset:
"this_month"always means the current calendar month
Example: with PRODUCTIVE_BILLING_CUTOFF_DAY=10, on April 5th, "this_month" resolves to March 1–31.
Security & permissions
Credential storage
API tokens are never written to disk by this project. They live in the OS credential store (Keychain / Credential Manager / Secret Service) or in environment variables.
Productive's permission model
Each team member generates their own API token via Productive → Settings → API integrations (docs). Tokens inherit the generating user's permissions:
"API tokens inherit the same access restrictions as the user they are associated with. If a user does not have permission to access certain features or data within Productive, these limitations will also apply to their API token."
Tokens also have a read-only vs read/write scope chosen at creation time.
What this means for team deployments:
- A "Member"-role user's token cannot query other members' time entries — Productive returns 403.
- The real access boundary is the Productive role (Settings → People → Role), not MCP-side configuration.
- Tools like
productive_get_employee_hourswill simply return empty/error for users whose Productive role doesn't grant team visibility.
For more on roles: User Permissions Overview | Productive API docs
How it works
Architecture
┌─────────────────────┐ stdio ┌──────────────────────┐ HTTPS ┌─────────────────┐
│ MCP client │ ─────────► │ productive-mcp │ ────────► │ Productive.io │
│ (Claude Code etc.) │ │ (FastMCP server) │ │ JSON:API v2 │
└─────────────────────┘ └──────────┬───────────┘ └─────────────────┘
│
▼
~/.config/productive-mcp/
├── cache.json (projects, people, services)
└── preferences.json (per-project default service)
Credential lookup order
- Environment variable (
PRODUCTIVE_MCP_TOKEN/_ORG_ID/_PERSON_ID) - OS credential store via
keyring(Keychain / Credential Manager / Secret Service) - Error with a helpful message pointing at both options
Local state
Two files under ~/.config/productive-mcp/, both 0600:
cache.json— projects, people (active only), per-project services. 1-hour TTL.preferences.json—{ "default_services": { "<project_id>": "<service_id>" } }.
Neither file ever contains credentials.
Development
uv pip install -e ".[dev]"
# unit tests (no network required)
pytest
# integration tests (hit the real Productive API — requires credentials)
pytest -m integration
# lint + type check
ruff check .
mypy src
Project layout
src/productive_mcp/
├── __main__.py Entrypoint (python -m productive_mcp)
├── server.py FastMCP tool definitions (14 tools)
├── client.py Async Productive.io API client + fuzzy matcher
├── auth.py Cross-platform credential loader (keyring + env)
├── storage.py Local cache + preferences persistence
├── periods.py Billing-cutoff-aware period resolution
└── reporting.py Time entry categorisation for reports
scripts/
└── install.sh One-shot deploy script for the global launcher
tests/
├── test_client.py Fuzzy matching + trim functions
├── test_periods.py Period resolution + billing cutoff
├── test_reporting.py Entry categorisation
└── test_formatter.py TOON output encoding
Troubleshooting
"Credential store lookup failed"
The keyring backend isn't configured or accessible. Check: python -c "import keyring; print(keyring.get_keyring())". On headless Linux, use environment variables instead.
"No credential found for service=productive-mcp"
Credentials haven't been stored yet. Run the keyring.set_password(...) commands from the install section.
"No project matches 'X'"
Cache may be stale. Call productive_refresh_cache and retry.
"Ambiguous project query" Two projects scored nearly identically. Be more specific — add the project number.
"Project has multiple services; pass service_hint"
Either pass service_hint="…" (it'll be remembered), or call productive_set_default_service once upfront.
Alternatives
Several other Productive.io MCP servers exist:
- berwickgeek/productive-mcp — the most feature-complete general-purpose server (projects, tasks, boards, people, workflows). Node.js. No time tracking.
- adamchrabaszcz/productive-time-mcp — companion time-tracking server for the above.
- druellan/Productive-Simple-MCP — read-only Python/FastMCP server using TOON output for low token usage.
- laurkee/productive-mcp (Codeberg) — ticketing-focused.
Why pick this one?
- Cross-platform credential store — not a
.envfile. macOS Keychain, Windows Credential Manager (TPM-backed), Linux Secret Service. - Fuzzy matching for projects and people — say
"Alice"or"1099 Acme"instead of looking up IDs. - Per-project default service memory — log time without specifying the service after the first call.
- Billing-cutoff-aware reports —
"this_month"means what your invoicing cycle says it means. - TOON output mode — 30–60% fewer tokens for long sessions.
License
MIT — see LICENSE.
Contributing
Issues and PRs welcome. Please include tests for new behaviour.
Not affiliated with Productive.io. "Productive" is a trademark of its respective owners.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。