MCP server for ActiveCollab — exposes projects, tasks, comments, attachments, and time tracking to MCP-compatible LLM clients.
Project description
activecollab-mcp
A Python MCP server that exposes an ActiveCollab workspace (projects, tasks,
comments, attachments, time tracking) to MCP-compatible LLM clients such as
Cursor and Claude Desktop. Ships as an installable package with a daemon CLI
and version management via pipx.
Heads up: this version supersedes the previous Node.js implementation, which has been moved to
archive/js/for reference.
Features
- One-line installer —
pipx install activecollab-mcp && activecollab-mcp setup, done.setupcovers login, the background service, registration with installed AI clients (Claude Code, Codex, Grok, Pi, OMP, Gemini CLI, Cursor, opencode), and auto-updates. - Interactive
auth login— exchanges email + password for an API token via/issue-tokenand stores it at~/.activecollab-mcp/credentials.json(mode 0600). No env vars required for the common case. - Streamable HTTP transport on
127.0.0.1:8787/mcp(single, modern MCP transport). - Daemon CLI —
start,stop,restart,status,logs,config,version. - Boot-persistent service (macOS) —
activecollab-mcp service installregisters alaunchdLaunchAgent withRunAtLoad+KeepAliveso the server survives reboots and auto-restarts on crash. - Self-update —
activecollab-mcp updateinstalls the latest release from PyPI.--autoregisters a cron entry for daily auto-updates. The running server is automatically bounced after a successful update so it picks up the new code. - Auto port fallback — if 8787 is busy, the daemon scans the next 20 ports and binds to the first free one. The actual bound port is written to
~/.activecollab-mcp/server.portand surfaced bystatus. - Read tools — projects, users, tasks, comments, attachments, time records, name resolution.
- Write tools (toggle via env) — create/update/complete/reopen/delete tasks, post comments, log time, upload and attach files,
@-mention users. - First-person identity —
whoamiandlist_my_tasksresolve "me" / "my" from the token owner so the LLM doesn't have to ask. - Vision-ready image attachments —
get_image_attachment_contentreturns metadata first (includingtempFilePath) and embeds MCPImageContentfor images under the embed budget, so hosts that truncate large image payloads still get a usable file path fallback. Survives ActiveCollab quirks: token-auth-only download endpoint, genericapplication/octet-streamContent-Type responses, and the{single: {...}, parent: {...}}wrapper on/attachments/{id}. - Resilient comments + attachments — automatic retry on transient 5xx, plus an inline fallback that pulls comments and attachments off the task payload when the dedicated endpoints return "Failed to match path". Routing-miss 500s are remembered for an hour (
endpoint_skipped: truein responses), so broken endpoints aren't re-probed on every call. - Stale-token self-healing — on a 401 the client re-reads
credentials.jsonand retries once with the fresh token, soauth logintakes effect without restarting the daemon. API keys are redacted from all error output. - LLM-friendly response shaping:
get_task_bundle— task + comments + deduped attachments in one call; image metadata is returned first with temp-file fallbacks, and small image attachments are embedded as MCP image blocks (include_image_content, capped bymax_images/max_image_bytes/max_embedded_image_bytes).get_project_overview— project metadata + recent open tasks in one call.describe_workspace— counts, top projects, top users for orientation.search_tasks— multi-field client-side scan when the API'ssearchisn't enough.format=compacton list tools to drop heavy fields (body HTML, raw attachments) and save tokens.- Tasks are enriched with
project_nameandassignee_name(s)resolved from cached lookups — no more bare IDs in the LLM output. - Cached name resolution (
find_*_by_name,list_tasks_by_user_name) with TTL. - Pagination metadata (
hasMore,nextPage) returned on every list tool.
Requirements
- Python 3.10+
- An ActiveCollab account (onboarding exchanges your email + password for an API token)
pipx will be installed automatically by the installer script if it isn't
already on your PATH.
Quick install
Published on PyPI — no git access or SSH keys needed:
pipx install activecollab-mcp && activecollab-mcp setup
setup walks through onboarding interactively:
- Runs
activecollab-mcp auth login, prompting for your ActiveCollab URL, email, and password. It exchanges those for an API token viaPOST /issue-tokenand stores the token at~/.activecollab-mcp/credentials.json(mode 0600). Skipped if you're already logged in. - Installs the boot-persistent background server (macOS LaunchAgent; ad-hoc
daemon elsewhere) on
127.0.0.1:8787— used by HTTP clients like Cursor. - Detects the AI clients installed on the machine and offers to register the
server with each one (default yes, already-registered clients are left
alone). Supported: Claude Code, Codex, Grok, Pi, OMP (oh-my-pi),
Gemini CLI, Cursor, opencode. CLI-based clients are registered via their
own
mcp addcommand; the rest get a safe merge into their MCP config file (existing entries preserved,.bakbackup written, unparseable configs skipped with a warning). - Offers to enable the daily auto-update cron (default yes).
Flags: --all-clients registers with every detected client without prompts;
--skip-clients, --skip-service, --skip-auto-update skip a step;
--relogin re-issues the token.
If you don't have pipx, run the bundled install.sh instead — it installs
pipx if needed, installs the package from PyPI, and runs setup:
/bin/bash install.sh
Installer script flags
| Flag | Behavior |
|---|---|
--no-setup |
Install only; skip the interactive onboarding. |
--upgrade |
Force-reinstall/upgrade an existing pipx install of activecollab-mcp. |
--yes, -y |
Auto-confirm installing pipx if missing. Password prompts during setup are still interactive. |
--help, -h |
Print installer help. |
Dev install (from a checkout)
pipx install . # production install in an isolated venv
pipx install --force . # reinstall over an existing version
pip install -e ".[dev]" # editable dev install in a venv
Authenticate
The fastest way to get started — let the CLI exchange your email and password
for an API token (per the
ActiveCollab /issue-token flow):
activecollab-mcp auth login
# ActiveCollab base URL: https://your-company.activecollab.com
# Email: you@example.com
# Password: ************
# Logged in as you@example.com.
# Token stored in /Users/you/.activecollab-mcp/credentials.json (mode 0600).
The token is saved to ~/.activecollab-mcp/credentials.json with 0600
permissions. From then on you can simply run activecollab-mcp start — no env
vars required.
Other auth commands:
activecollab-mcp auth show # display stored credentials (token redacted)
activecollab-mcp auth logout # remove stored credentials
Flag overrides if you want to script it (password is still prompted unless --password is passed):
activecollab-mcp auth login --url https://your.activecollab.com --email you@example.com
activecollab-mcp auth login --no-verify-tls # self-signed certs
activecollab-mcp auth login --no-append-api-v1 # URL already includes /api/v1
Configure (optional)
auth login is enough for most setups. If you'd rather manage credentials
yourself — e.g. for a headless deployment or CI — copy .env.example to
.env and set the env vars there. Env values override anything stored
by auth login.
cp .env.example .env
Credential precedence: process env → .env file → ~/.activecollab-mcp/credentials.json.
| Variable | Purpose |
|---|---|
ACTIVECOLLAB_BASE_URL |
e.g. https://your-company.activecollab.com (auto-appends /api/v1). Optional when auth login has been run. |
ACTIVECOLLAB_API_KEY |
Personal API token. Optional when auth login has been run. |
Common optional:
| Variable | Default | Notes |
|---|---|---|
ACTIVECOLLAB_AUTH_HEADER |
X-Angie-AuthApiToken |
Override for non-standard auth setups |
ACTIVECOLLAB_AUTH_PREFIX |
(empty) | e.g. Bearer for custom proxies |
ACTIVECOLLAB_APPEND_API_V1 |
true |
Set false if your base URL already includes /api/v1 |
ACTIVECOLLAB_ALLOW_SELF_SIGNED |
false |
Disable TLS verification (self-signed certs) |
ACTIVECOLLAB_TIMEOUT_SECONDS |
15 |
HTTP request timeout |
ACTIVECOLLAB_ENABLE_WRITES |
true |
Toggle write tools off for a strictly read-only deployment |
ACTIVECOLLAB_NAME_CACHE_TTL |
300 |
Seconds to cache project/user lists used for fuzzy resolution |
ACTIVECOLLAB_MCP_HOST |
127.0.0.1 |
Bind host |
ACTIVECOLLAB_MCP_PORT |
8787 |
Bind port |
ACTIVECOLLAB_MCP_LOG_LEVEL |
INFO |
DEBUG/INFO/WARNING/ERROR |
ACTIVECOLLAB_MCP_LOG_FILE |
~/.activecollab-mcp/server.log (daemon mode) |
Optional file logging |
Legacy variable names from the JS version (BASE_URL, API_TOKEN,
AUTH_HEADER, AUTH_PREFIX) are still accepted as aliases.
Use the CLI
activecollab-mcp --help # list all subcommands
activecollab-mcp version # print package version
activecollab-mcp setup # one-shot onboarding (login + service + AI clients + auto-update)
activecollab-mcp auth login # issue an API token from email+password
activecollab-mcp auth show # show stored credentials (redacted)
activecollab-mcp auth logout # remove stored credentials
activecollab-mcp config # print effective config (api_key redacted)
activecollab-mcp run # run in the foreground (Ctrl-C to stop)
activecollab-mcp start # detach and run as a background process
activecollab-mcp status # JSON: { pid, running, bound_port, mcp_url, ... }
activecollab-mcp logs -f # tail the server log
activecollab-mcp restart # stop + start
activecollab-mcp stop # graceful SIGTERM, fallback SIGKILL after 10s
activecollab-mcp update # install the latest release from PyPI
activecollab-mcp update --check # check only; don't install
activecollab-mcp update --auto # also register a daily cron auto-updater
Updating
activecollab-mcp update checks PyPI for the latest release and re-runs
pipx install --force activecollab-mcp==<version> when a newer one exists.
The update source is recorded in ~/.activecollab-mcp/updater.json; legacy
pre-1.9 installs that recorded a git URL keep updating from git.
activecollab-mcp update # install the latest PyPI release
activecollab-mcp update --check # exit 0 if an update is available, 4 if up-to-date
activecollab-mcp update --to 1.12.3 # pin to a specific version
activecollab-mcp update --repo git@bitbucket.org:org/fork.git # force git mode (dev)
Auto-update via cron
activecollab-mcp update --auto # daily at 06:17
activecollab-mcp update --auto --schedule "0 4 * * 0" # weekly, Sunday 04:00
activecollab-mcp update --auto-status # show current schedule
activecollab-mcp update --auto-disable # remove the cron entry
--auto writes a marker-bracketed block to your crontab so it can be removed
cleanly later. The cron line looks like:
# >>> activecollab-mcp auto-update >>>
17 6 * * * /Users/you/.local/bin/activecollab-mcp update --quiet >> /Users/you/.activecollab-mcp/update.log 2>&1
# <<< activecollab-mcp auto-update <<<
Output from auto-updates is appended to ~/.activecollab-mcp/update.log.
Running server is restarted automatically
After a successful update, the running server (if any) is bounced so it
picks up the new code in memory:
- LaunchAgent installed via
service install→launchctl kickstart -k - Manual daemon started via
activecollab-mcp start→ stop + start
Pass --no-restart to skip this (useful when scripting blue/green-style
rollouts or when you want to control the restart timing yourself). If no
server is running at update time, nothing happens — the new code will be
in effect the next time you start it.
The auto-update cron job also restarts the running server by default, so
clients pointed at http://127.0.0.1:8787/mcp will reconnect to the
upgraded server without manual intervention.
Run on boot (macOS LaunchAgent)
activecollab-mcp start survives shell exit but not a reboot. To keep
the server running across logins and reboots, register a LaunchAgent:
activecollab-mcp service install # writes plist + launchctl bootstrap
activecollab-mcp service status # shows service + any manual daemon
activecollab-mcp service restart # launchctl kickstart -k
activecollab-mcp service uninstall # bootout + remove plist
What service install does:
- Writes
~/Library/LaunchAgents/com.efront.activecollab-mcp.plistwithRunAtLoad=true(start at login) andKeepAlive=true(auto-restart on crash). - Stops any running
activecollab-mcp startmanual daemon first so the two don't fight over the port. launchctl bootstrap gui/<uid> <plist>loads + starts the service.
Service output is appended to ~/.activecollab-mcp/service.log. After
install, manage with the service subcommands or launchctl directly.
Linux: not yet implemented. Use an
@rebootcrontab entry pointing atactivecollab-mcp start, or write a user-level systemd unit.
Port handling
By default the server binds to 127.0.0.1:8787. When another process
(another MCP server, a stale daemon, etc.) is already on that port:
activecollab-mcp runfails fast with a clear error — foreground use should surface the conflict immediately. Pass--auto-portto opt into fallback behavior.activecollab-mcp start(daemon) defaults to--auto-port, so it scans the next 20 ports (port+1…port+20) and binds to the first free one. Disable with--no-auto-port.
The actually-bound port is written to ~/.activecollab-mcp/server.port,
returned by activecollab-mcp status as bound_port / mcp_url, and
logged on startup. Set ACTIVECOLLAB_MCP_AUTO_PORT=true / false and
ACTIVECOLLAB_MCP_PORT_SCAN_WINDOW=<n> to configure via env.
State and credentials live in ~/.activecollab-mcp/:
~/.activecollab-mcp/
├── credentials.json # base_url + API token (mode 0600), written by `auth login`
├── updater.json # source repo URL + last-check/install timestamps
├── server.pid # PID of the running daemon (manual `start`)
├── server.port # actual port the running server is bound to (post-fallback)
├── server.log # rotating log file (5 MB × 5 backups)
├── service.log # stdout/stderr captured when running under launchd
└── update.log # auto-update log (appended by the cron entry)
The LaunchAgent plist (when installed via service install) lives at:
~/Library/LaunchAgents/com.efront.activecollab-mcp.plist
Connect from an MCP client
Cursor
~/.cursor/mcp.json:
{
"mcpServers": {
"activecollab": {
"url": "http://127.0.0.1:8787/mcp"
}
}
}
Claude Desktop
Claude Desktop currently uses stdio MCP servers, so for HTTP transport use a client that supports it directly. To support stdio in the future you can run the server inside an HTTP-to-stdio proxy.
Health check
curl http://127.0.0.1:8787/healthz
# {"status": "ok", "name": "activecollab-mcp", "version": "0.2.0", "writes_enabled": true}
Tools
Read
| Tool | What it does |
|---|---|
health |
Calls /info. Use for connectivity smoke tests. |
whoami |
Identify who "me" / "I" refers to. Resolved from the token owner stored at auth login. Always call this when the user phrases a request in first person. |
list_my_tasks |
Convenience: whoami + list_user_tasks in one call. Use for "my tasks" / "what am I working on". |
describe_workspace |
Counts + top 10 projects/users for orientation. |
list_projects |
Project list with format=compact option. |
find_project_by_name |
Fuzzy resolve a name to a project ID (cached). |
get_project |
Single project by ID. |
get_project_overview |
Project + recent open tasks in one call. |
list_users |
User list, compact option. |
find_user_by_name |
Fuzzy resolve a name or email to a user ID (cached). |
get_user |
Single user by ID. |
list_user_tasks |
Tasks assigned to a user across projects. |
list_tasks |
Tasks in a project with filters. |
list_tasks_by_user_name |
Resolve names + return tasks in one call. |
search_tasks |
Client-side multi-field search within a project. |
get_task |
Single task. |
get_task_bundle |
Task + comments + deduped attachments in one call. Image metadata is returned first with temp-file fallbacks; small images are embedded as image content blocks. |
batch_get_tasks |
Parallel multi-task fetch with partial-failure reporting. |
list_task_comments |
Comments for a task. |
list_task_attachments |
Deduped attachments (inline + endpoint sources). |
list_task_image_attachments |
Image attachments only, with comment scan. |
get_attachment |
Attachment metadata, with a resolved {normalized, raw} summary so the LLM sees name, mimeType, isImage, size, downloadUrl directly. Metadata-only — use get_image_attachment_content to actually view image bytes. |
get_image_attachment_content |
Download an image and return metadata first, including tempFilePath; image content is embedded only when under max_embedded_image_bytes. Falls back through download_url / preview_url / thumbnail_url (with --DOWNLOAD-TOKEN-- substitution) if the API endpoint fails. |
debug_attachment |
Diagnostic: report all download URL candidates and which strategy works for a given attachment, without returning bytes. Use when an image fetch fails. |
list_project_time_records |
Time records for a project, filterable by date. |
list_user_time_records |
Time records logged by a user. |
Write (set ACTIVECOLLAB_ENABLE_WRITES=false to disable)
| Tool | What it does |
|---|---|
create_task |
Create a task with optional assignee, due date, labels, attachments, etc. |
update_task |
Partial update of task fields; can attach uploaded files. |
complete_task |
Mark complete. |
reopen_task |
Reopen a completed task. |
delete_task |
Move to trash (reversible via UI). |
post_task_comment |
Add a comment to a task; can attach uploaded files. |
upload_attachment |
Upload a local file, returns an upload code for attach_uploaded_files. |
log_time |
Create a time record on a project (and optionally a task). |
Attachment flow: upload_attachment(file_path) → returns {code} →
pass attach_uploaded_files: ["<code>", …] to create_task, update_task,
or post_task_comment. Attach codes promptly — ActiveCollab garbage-collects
unattached uploads.
Mentions
Write @[<user_id>] inline in a task or comment body. The server expands
each token into ActiveCollab's canonical mention anchor, and AC notifies the
mentioned user:
post_task_comment(project_id=5937, task_id=509819,
body="@[407] can you review this?")
Resolve names to ids first with find_user_by_name or list_users. An
unknown id is a hard error rather than a silently-dropped mention.
Bodies are HTML in ActiveCollab, so plain text is converted automatically —
blank lines become <p>, single newlines <br>, and - / * lines become
bullet lists. Raw text is HTML-escaped first (no injection), and mention
tokens are expanded after escaping. Pass body_is_html=true to supply your
own markup untouched.
Only
<a class="mention" href="…/users/<id>">carries the user id through ActiveCollab's sanitizer —<span class="mention">variants have their id attributes stripped and therefore never notify anyone.
Versioning
This project follows semver. Current release is v1.12.3.
activecollab-mcp update installs the latest release published to PyPI.
Pin to a specific version with --to:
activecollab-mcp update --to 1.12.3 # downgrade or hold at a version
activecollab-mcp update --check # see what's available without installing
When installing for the first time via pipx with a pinned version:
pipx install 'activecollab-mcp==1.12.3'
Releases are cut with scripts/release.sh (tests → build → twine upload →
git tag).
The version is queryable at runtime via activecollab-mcp version and the
/healthz endpoint.
Development
pip install -e ".[dev]"
pytest # unit tests (none yet — placeholders)
ruff check src
activecollab-mcp run # runs against your real ActiveCollab via .env
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file activecollab_mcp-1.12.3.tar.gz.
File metadata
- Download URL: activecollab_mcp-1.12.3.tar.gz
- Upload date:
- Size: 120.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0942ec8f93202699ebed1e405419b660905ba504f28d6cc91de8f81901a5ad5c
|
|
| MD5 |
3a0cd333a57bacadf8c94ab1379d8f5c
|
|
| BLAKE2b-256 |
aae7c319c93476f2552d6dd80d16157c3dc142ab2f8cd9b74a0381309b5df735
|
File details
Details for the file activecollab_mcp-1.12.3-py3-none-any.whl.
File metadata
- Download URL: activecollab_mcp-1.12.3-py3-none-any.whl
- Upload date:
- Size: 72.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
319a722b70ffa14857bc4f30141c14b85ec25d7b2b8dc22922acbea74652c597
|
|
| MD5 |
233150787fc3806f802d791718fc0e92
|
|
| BLAKE2b-256 |
6dcdd6c09639ea588e7b3aaa20f1cb7ad0b79987616f305892b400434ec2a6df
|