loading…
Search for a command to run...
loading…
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 pro
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.
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.
"Acme", "1099 Acme", "Alice", or "me" all resolve naturally"this_month" shifts based on your invoicing cyclehours in, hours out — the API uses minutes internally, but you never see themlist_time_entries only shows your own entries unless you opt outgit 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)
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 |
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
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.
~/.claude.json){
"mcpServers": {
"productive": {
"type": "stdio",
"command": "/path/to/productive-mcp/.venv/bin/productive-mcp",
"args": []
}
}
}
claude_desktop_config.json){
"mcpServers": {
"productive": {
"command": "/path/to/productive-mcp/.venv/bin/productive-mcp"
}
}
}
Restart the client after editing its config.
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.
All tools are prefixed with productive_ so they namespace cleanly alongside other MCP servers.
| 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 |
| 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 |
| 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 |
| Tool | Purpose |
|---|---|
productive_refresh_cache |
Force an immediate cache refresh |
productive_set_default_service |
Override the remembered default service for a project |
productive_get_time_reportperiod : 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_briefingperiod : 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_hoursperson : 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.
| 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) |
The PRODUCTIVE_BILLING_CUTOFF_DAY controls how symbolic periods resolve for invoicing workflows:
"this_month" = previous calendar month (you're still closing invoices)"this_month" = current calendar month"this_month" always means the current calendar monthExample: with PRODUCTIVE_BILLING_CUTOFF_DAY=10, on April 5th, "this_month" resolves to March 1–31.
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.
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:
productive_get_employee_hours will simply return empty/error for users whose Productive role doesn't grant team visibility.For more on roles: User Permissions Overview | Productive API docs
┌─────────────────────┐ 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)
PRODUCTIVE_MCP_TOKEN / _ORG_ID / _PERSON_ID)keyring (Keychain / Credential Manager / Secret Service)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.
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
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
"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.
Several other Productive.io MCP servers exist:
Why pick this one?
.env file. macOS Keychain, Windows Credential Manager (TPM-backed), Linux Secret Service."Alice" or "1099 Acme" instead of looking up IDs."this_month" means what your invoicing cycle says it means.MIT — see LICENSE.
Issues and PRs welcome. Please include tests for new behaviour.
Not affiliated with Productive.io. "Productive" is a trademark of its respective owners.
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"productive-mcp": {
"command": "npx",
"args": []
}
}
}PRs, issues, code search, CI status
Database, auth and storage
Reference / test server with prompts, resources, and tools.
Secure file operations with configurable access controls.