productive-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@productive-mcplog 3 hours on the Q4 marketing campaign project"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
productive-mcp
An MCP server for Productive.io — log time, inspect projects, and manage time entries from any MCP-compatible client (Claude Code, Claude Desktop, Cursor, etc.) using plain English:
"log 2.5 hours on the Acme security review project"
"show me my time entries for last week"
"delete time entry 123456"
Built around the Productive.io JSON:API v2 with fuzzy project matching, a local disk cache, and per-project default-service memory so the common case — logging time to a project you use regularly — is a single sentence.
Features
9 tools covering projects, services, and time entries (list / create / update / delete)
Fuzzy project matching —
"Acme","1099 Acme", or"1099"all resolve to the same projectRemembers your default service per project so you don't have to specify it every time
Local cache for projects and services (1-hour TTL, manually refreshable)
Credential safety: API token is read from the macOS Keychain by default (environment variables supported as a fallback for Linux/WSL or headless use)
hoursin,hoursout — the API uses minutes internally, but you never see themScoped 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 (recommended, for Keychain integration) — Linux / WSL work via environment variables
Install
1. Clone and install
git clone https://github.com/<you>/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: |
Person ID | Your own user ID — open your profile in Productive; it's the numeric segment in the URL |
3. Store credentials
Option A — macOS Keychain (recommended on macOS)
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>" -UThe server looks these up at startup via the security CLI. They are never written to disk by this project.
Option B — environment variables (Linux / WSL / CI / 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 Keychain lookups, so they're also the simplest way to test alternate accounts temporarily.
4. Register the server with your MCP client
Claude Code (~/.claude.json)
Add under mcpServers:
{
"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
If you want one shared install that isn't tied to a clone directory:
bash scripts/install.shThis 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 of the venv binary. Re-running the script upgrades the venv in place.
Tools
All tools are prefixed with productive_ so they namespace cleanly alongside other MCP servers.
Tool | Purpose |
| List all active projects (id, name, number, company) |
| Fuzzy-search projects by name and/or number |
| List services (billable activity types) on a given project |
| Create a time entry |
| List time entries with optional date/project/owner filters |
| Edit hours / date / note / service on an existing entry |
| Permanently delete a time entry |
| Force an immediate cache refresh (after creating new projects/services) |
| Override the remembered default service for a project |
Tool reference
productive_log_time
project : str — Project name (fuzzy) or numeric id. e.g. "1099 Acme", "42"
hours : float — Hours worked (e.g. 2.5). Converted to minutes internally.
note : str? — Description of the work
date : str? — ISO date (YYYY-MM-DD). Defaults to today.
service_hint : str? — Service name or id (only needed when the project has
multiple services and no remembered default)On success, returns the created entry plus a service_resolution note explaining how the service was chosen ("auto-selected only service" / "used remembered default" / "matched by name" etc.).
If the project has only one service, it's auto-selected and saved as the default for next time. If it has multiple, the server returns an actionable error listing the options.
productive_find_project
query : str — Partial name or number (e.g. "Acme", "1099 acme", "1099")
limit : int — Max matches (default 5)Returns scored matches sorted best-first. The fuzzy scorer combines token substring matching, a sequence-matcher fallback for typos, and a boost when the query contains an exact project number.
productive_list_time_entries
after : str? — Include entries on/after this ISO date
before : str? — Include entries on/before this ISO date
project : str? — Filter by project name or id
mine_only : bool — Default True; set False to see the whole team's entriesEntries are returned newest-first, with totals rolled up in total_hours.
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, services; 1h TTL)
└── preferences.json (per-project default service)Key design decisions
Services are scoped to Deals, which belong to Projects. Productive's data model makes this visible; the MCP walks the hierarchy transparently so
"services on project X"Just Works.Hours are the only user-facing unit. The API stores time in minutes; this layer converts at the boundary.
Remembered defaults reduce back-and-forth. After you log time to a project once, subsequent calls can omit
service_hint— the default is remembered per project inpreferences.json.Fuzzy matching over exact matching. Most time-logging requests say something like "that security review for Acme", not "project #1099". The resolver biases toward natural language.
Mine-only by default. You're almost always logging time for yourself; the one person who needs team-wide visibility can pass
mine_only=False.
Local state
Two files live under ~/.config/productive-mcp/, both created with 0600 permissions:
cache.json— trimmed project list and per-project services lookup. Refreshes automatically on TTL expiry or viaproductive_refresh_cache.preferences.json—{ "default_services": { "<project_id>": "<service_id>" } }.
Neither file ever contains credentials.
Credential lookup order
Environment variable (
PRODUCTIVE_MCP_TOKEN/_ORG_ID/_PERSON_ID)macOS Keychain (
security find-generic-password -s productive-mcp -a <account>)Error — the server refuses to start with a helpful message pointing at both options.
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 srcRun the server directly for debugging:
.venv/bin/productive-mcp
# or
python -m productive_mcpIt talks stdio JSON-RPC, so to exercise it by hand you'll want an MCP client or mcp-inspector.
Project layout
src/productive_mcp/
├── __main__.py Entrypoint (`python -m productive_mcp`)
├── server.py FastMCP tool definitions
├── client.py Async Productive.io API client + fuzzy matcher
├── auth.py Keychain / env-var credential loader
└── storage.py Local cache + preferences persistence
scripts/
└── install.sh One-shot deploy script for the global launcher
tests/
└── test_client.py Unit tests for trimming + fuzzy matchingTroubleshooting
"Keychain lookup failed"
Either the item doesn't exist yet (re-run the security add-generic-password commands in step 3) or you're not on macOS. Use environment variables instead.
"No project matches 'X'"
Your cache may be stale if the project was created recently. Call productive_refresh_cache and try again.
"Ambiguous project query" Two projects scored nearly identically. Be more specific — add the project number, or the company name.
"Project has multiple services; pass service_hint"
The project hasn't had a default set yet. Either pass service_hint="…" on this call (it'll be remembered), or call productive_set_default_service once upfront.
The server starts, but the client reports "no tools"
Make sure your MCP client is pointing at the venv's productive-mcp binary (or the run.sh launcher), not at a source file. FastMCP advertises tools at handshake time — if the process can't import mcp, no tools will appear.
Security notes
API tokens are never written to disk by this project. They live in the Keychain or in environment variables only.
The cache and preferences files are created with
0600permissions and contain no credentials.The Productive API token grants the same access your user has. Treat it accordingly. Rotate via Productive → Settings → API integrations if you suspect exposure.
Alternatives
Several other Productive.io MCP servers exist — worth checking before adopting this one:
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? This server is narrowly focused on the "log time and manage entries" workflow, and the UX is built around three opinionated choices:
Credentials in the macOS Keychain by default, not a
.env— no risk of a token ending up ingit status.Fuzzy project matching with project-number boost — say
"1099 Acme"or just"Acme"; both work.Per-project default service memory — after the first
productive_log_timeon a project, subsequent calls don't need to specify a service.
If you primarily need tasks/boards/workflows rather than time logging, use berwickgeek/productive-mcp instead — or run both side-by-side (they use different tool prefixes).
License
MIT — see LICENSE.
Contributing
Issues and PRs welcome. This is a small project; please keep changes focused and include tests for any new behaviour in client.py.
Not affiliated with Productive.io. "Productive" is a trademark of its respective owners.
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/cameronfairbairn/productive-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server