Skip to main content

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 installerpipx install activecollab-mcp && activecollab-mcp setup, done. setup covers 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-token and 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 CLIstart, stop, restart, status, logs, config, version.
  • Boot-persistent service (macOS) — activecollab-mcp service install registers a launchd LaunchAgent with RunAtLoad + KeepAlive so the server survives reboots and auto-restarts on crash.
  • Self-updateactivecollab-mcp update installs the latest release from PyPI. --auto registers 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.port and surfaced by status.
  • 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 identitywhoami and list_my_tasks resolve "me" / "my" from the token owner so the LLM doesn't have to ask.
  • Vision-ready image attachmentsget_image_attachment_content returns metadata first (including tempFilePath) and embeds MCP ImageContent for 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, generic application/octet-stream Content-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: true in responses), so broken endpoints aren't re-probed on every call.
  • Stale-token self-healing — on a 401 the client re-reads credentials.json and retries once with the fresh token, so auth login takes 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 by max_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's search isn't enough.
    • format=compact on list tools to drop heavy fields (body HTML, raw attachments) and save tokens.
    • Tasks are enriched with project_name and assignee_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:

  1. Runs activecollab-mcp auth login, prompting for your ActiveCollab URL, email, and password. It exchanges those for an API token via POST /issue-token and stores the token at ~/.activecollab-mcp/credentials.json (mode 0600). Skipped if you're already logged in.
  2. Installs the boot-persistent background server (macOS LaunchAgent; ad-hoc daemon elsewhere) on 127.0.0.1:8787 — used by HTTP clients like Cursor.
  3. 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 add command; the rest get a safe merge into their MCP config file (existing entries preserved, .bak backup written, unparseable configs skipped with a warning).
  4. 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.0   # 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 installlaunchctl 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.plist with RunAtLoad=true (start at login) and KeepAlive=true (auto-restart on crash).
  • Stops any running activecollab-mcp start manual 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 @reboot crontab entry pointing at activecollab-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 run fails fast with a clear error — foreground use should surface the conflict immediately. Pass --auto-port to opt into fallback behavior.
  • activecollab-mcp start (daemon) defaults to --auto-port, so it scans the next 20 ports (port+1port+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.0.

activecollab-mcp update installs the latest release published to PyPI. Pin to a specific version with --to:

activecollab-mcp update --to 1.12.0      # 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.0'

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

activecollab_mcp-1.12.0.tar.gz (120.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

activecollab_mcp-1.12.0-py3-none-any.whl (71.4 kB view details)

Uploaded Python 3

File details

Details for the file activecollab_mcp-1.12.0.tar.gz.

File metadata

  • Download URL: activecollab_mcp-1.12.0.tar.gz
  • Upload date:
  • Size: 120.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.5

File hashes

Hashes for activecollab_mcp-1.12.0.tar.gz
Algorithm Hash digest
SHA256 87cabb677043f0c4a42eae6943c689ea5c13d0bfd1d435797c6a6bc839ed4948
MD5 9b478fe312f1522af1986d655e35e23e
BLAKE2b-256 5b80654b4c8d60103b5587b5cbe47f2b764a2f001fa8104eb0af3c18e5b9fcf5

See more details on using hashes here.

File details

Details for the file activecollab_mcp-1.12.0-py3-none-any.whl.

File metadata

File hashes

Hashes for activecollab_mcp-1.12.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a265f6e65eb5f7084ded7b497d21f98fb4e47d9cf5f29d03a31163184ec4ead9
MD5 a484f0437b773a247e7b45429e61b02d
BLAKE2b-256 791c5034f908575fb6b8b0c57a039579d0a20e3e057a736051dfdfadb9a69643

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page