Basecamp MCP Server
README.md
# Basecamp MCP Server
[](https://www.python.org/)
[](https://modelcontextprotocol.io/)
[](https://basecamp.com/)
[](LICENSE)
An MCP server for Basecamp 3. It lets MCP-capable clients such as Codex, Cursor, and Claude Desktop read and manage Basecamp projects through OAuth-authenticated Basecamp API calls.
The main server is [`basecamp_fastmcp.py`](basecamp_fastmcp.py). It uses the official `mcp.server.fastmcp` Python SDK and exposes 79 tools covering projects, todos, message boards, campfires, card tables, inbox forwards, documents, uploads, comments, events, webhooks, and search.
## What It Can Do
- Browse Basecamp projects and project details.
- Search across projects, todos, messages, campfire lines, comments, uploads, and schedules.
- Read and manage todolists, todos, todo groups, and completion state.
- Read and create message board messages, including drafts and categories.
- Read campfire lines.
- Read and create comments.
- Work with card tables, columns, cards, and card steps.
- Read inbox forwards and replies.
- Read daily check-ins and answers.
- Upload attachments and inspect uploads.
- Read and manage documents, including drafts.
- List events and manage webhooks.
- Generate local MCP configuration for Codex, Cursor, and Claude Desktop.
## Requirements
- Python 3.10 or newer.
- A Basecamp 3 account.
- A Basecamp OAuth application from <https://launchpad.37signals.com/integrations>.
- A client that can run local MCP servers, such as Codex, Cursor, or Claude Desktop.
If your system Python is older, use `uv`; it can create a virtual environment with a newer Python version.
## Quick Start
Clone the repository and install dependencies:
```bash
git clone https://github.com/georgeantonopoulos/Basecamp-MCP-Server.git
cd Basecamp-MCP-Server
uv venv --python 3.12 venv
source venv/bin/activate
uv pip install -r requirements.txt
```
Or, if `python` already points to Python 3.10 or newer:
```bash
python setup.py
```
Create a `.env` file from the example and fill in your Basecamp OAuth details:
```bash
cp .env.example .env
```
Required values:
```bash
BASECAMP_CLIENT_ID=your-client-id
BASECAMP_CLIENT_SECRET=your-client-secret
BASECAMP_ACCOUNT_ID=your-account-id
USER_AGENT="Your App Name (your@email.com)"
```
Authenticate with Basecamp:
```bash
python oauth_app.py
```
Open <http://localhost:8000> and complete the OAuth flow. The token is stored locally in `oauth_tokens.json` by default.
## Configure Your MCP Client
### Codex
```bash
python generate_codex_config.py
codex mcp get basecamp
```
Useful options:
```bash
python generate_codex_config.py --dry-run
python generate_codex_config.py --legacy
```
The script writes a `basecamp` server entry to `~/.codex/config.toml` and points it at this checkout's virtual environment and [`basecamp_fastmcp.py`](basecamp_fastmcp.py).
### Cursor
```bash
python generate_cursor_config.py
```
Then restart Cursor and check Settings -> MCP. The server should appear as `basecamp`.
### Claude Desktop
```bash
python generate_claude_desktop_config.py
```
Then fully quit and reopen Claude Desktop. The generated config is written to:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `~/AppData/Roaming/Claude/claude_desktop_config.json`
- Linux: `~/.config/claude-desktop/claude_desktop_config.json`
## Verify The Server
Run the FastMCP server through stdio and ask for its tool list:
```bash
printf '%s\n%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| python basecamp_fastmcp.py
```
Run the automated tests:
```bash
python -m pytest tests/ -v
```
## Available Tools
The FastMCP server exposes 82 tools.
### Projects And Search
- `get_projects`
- `get_project`
- `search_basecamp`
- `global_search`
### Reports
- `get_assignable_people` — all people who can have to-dos assigned to them
(`GET /reports/todos/assigned.json`)
- `get_person_assignments` — all active, pending to-dos assigned to one
person across **all** projects (`GET /reports/todos/assigned/{id}.json`,
optional `group_by: bucket|date`). Prefer this over iterating projects
when you need everything assigned to a single person.
- `get_overdue_todos` — all overdue to-dos across all projects, grouped by
lateness (`GET /reports/todos/overdue.json`)
### Todos
- `get_todolists`
- `get_todolist`
- `create_todolist`
- `update_todolist`
- `trash_todolist`
- `get_todos`
- `get_todo`
- `create_todo`
- `update_todo`
- `delete_todo`
- `archive_todo`
- `complete_todo`
- `uncomplete_todo`
- `reposition_todo`
- `get_todolist_groups`
- `create_todolist_group`
- `reposition_todolist_group`
### Messages, Campfires, And Check-Ins
- `get_message_board`
- `get_messages`
- `get_message`
- `get_message_categories`
- `create_message`
- `create_draft_message`
Pass `publish: false` to `create_message` to create a draft message instead
of posting it immediately. Agents can also call `create_draft_message` directly
when the intended operation is specifically to create a draft.
- `get_campfire_lines`
- `get_daily_check_ins`
- `get_question_answers`
### Comments
- `get_comments`
- `create_comment`
### Card Tables
- `get_card_tables`
- `get_card_table`
- `get_columns`
- `get_column`
- `create_column`
- `update_column`
- `move_column`
- `update_column_color`
- `put_column_on_hold`
- `remove_column_hold`
- `watch_column`
- `unwatch_column`
- `get_cards`
- `get_card`
- `create_card`
- `update_card`
- `move_card`
- `complete_card`
- `uncomplete_card`
- `get_card_steps`
- `create_card_step`
- `get_card_step`
- `update_card_step`
- `delete_card_step`
- `complete_card_step`
- `uncomplete_card_step`
### Inbox Forwards
- `get_inbox`
- `get_forwards`
- `get_forward`
- `get_inbox_replies`
- `get_inbox_reply`
- `trash_forward`
### Documents, Uploads, Attachments, Events, And Webhooks
- `create_attachment`
- `get_uploads`
- `get_upload`
- `download_upload` — download a vault Upload recording (Docs & Files) and
return its bytes as MCP content (``ImageContent`` for image MIME types,
``EmbeddedResource`` / ``BlobResourceContents`` otherwise). The MCP host
forwards the blob to the model, so PDFs, images, and documents are read
natively without an out-of-band fetch.
- `download_attachment` — download an inline comment/message attachment by its
``content_attachments[].download_url`` and return it as MCP content. Use this
for files embedded into a comment or message body. Inline attachments are
``Attachment`` objects with their own IDs and cannot be resolved through
``/uploads/{id}`` — that endpoint returns 404. For files that are their own
Upload recording in a vault, use ``download_upload`` instead.
> **Host compatibility for `download_upload` and `download_attachment`.**
> Both tools return MCP content blocks. The file is only readable by the
> model if the MCP host forwards `ImageContent` / `EmbeddedResource`
> (`BlobResourceContents`) on. Status as of June 2026:
>
> - **Claude Code (CLI)** — fully supported, including `application/pdf`
> and other binary blob resources.
> - **Claude Desktop / claude.ai web** — image content blocks work, but
> non-image `EmbeddedResource` blocks are rejected with `"Resources of
> type 'application/pdf' are not currently supported"`. The bytes reach
> the host but never the model. Once the client adds support, these
> tools become useful in those frontends without server changes.
- `get_documents`
- `get_document`
- `create_document`
- `create_draft_document`
Pass `publish: false` to `create_document` to create a draft document instead
of publishing it immediately. Agents can also call `create_draft_document`
directly when the intended operation is specifically to create a draft.
- `update_document`
- `trash_document`
- `get_events`
- `get_webhooks`
- `create_webhook`
- `delete_webhook`
## Example Prompts
- "Show me all my Basecamp projects."
- "Search Basecamp for deadline."
- "Get the todolists for project 123456."
- "Create a todo called Review PR in todolist 987654."
- "Show me the message board categories for project 123456."
- "Post an Announcement to the project message board."
- "Show me the card table columns for project 123456."
- "Move this card to the Done column."
- "List the latest uploads in this project's vault."
- "Download the screenshot attached to that comment so you can read it."
## Architecture
- [`basecamp_fastmcp.py`](basecamp_fastmcp.py): FastMCP stdio server used by MCP clients.
- [`basecamp_client.py`](basecamp_client.py): Synchronous Basecamp 3 API client.
- [`search_utils.py`](search_utils.py): Higher-level search helpers across Basecamp resources.
- [`oauth_app.py`](oauth_app.py): Local Flask OAuth flow for Basecamp authentication.
- [`auth_manager.py`](auth_manager.py): OAuth refresh helper used before API calls.
- [`token_storage.py`](token_storage.py): Local OAuth token storage.
- [`generate_codex_config.py`](generate_codex_config.py): Codex MCP configuration generator.
- [`generate_cursor_config.py`](generate_cursor_config.py): Cursor MCP configuration generator.
- [`generate_claude_desktop_config.py`](generate_claude_desktop_config.py): Claude Desktop configuration generator.
- [`mcp_server_cli.py`](mcp_server_cli.py): Legacy JSON-RPC server kept for compatibility and tests.
## Authentication And Token Storage
The recommended path is OAuth 2.0:
1. Create a Basecamp OAuth app.
2. Put the client ID, client secret, account ID, redirect URI, and user agent in `.env`.
3. Run `python oauth_app.py`.
4. Complete the browser flow at <http://localhost:8000>.
By default, OAuth tokens are stored in `<project>/oauth_tokens.json`. For containers, read-only checkouts, or mounted token volumes, set `BASECAMP_MCP_TOKEN_FILE`:
```bash
export BASECAMP_MCP_TOKEN_FILE=/var/lib/basecamp-mcp/oauth_tokens.json
```
Both the OAuth app and the MCP server read the same variable. `token_storage.py` expands `~` and environment variables in this path, creates the parent directory if needed, and attempts to set the token file permissions to `0o600` when writing. Parent directory permissions are still your responsibility.
## Troubleshooting
If tools do not appear in your MCP client:
1. Confirm the virtual environment exists and has the MCP SDK:
```bash
./venv/bin/python -c "import mcp; print('MCP available')"
```
2. Confirm `.env` contains `BASECAMP_ACCOUNT_ID`.
3. Re-run the relevant config generator.
4. Fully quit and restart your MCP client.
If authentication fails:
```bash
python oauth_app.py
```
Then open <http://localhost:8000> and complete the Basecamp OAuth flow again.
For Claude Desktop on macOS, MCP logs are usually under:
```bash
~/Library/Logs/Claude/
```
## Security Notes
- Do not commit `.env` or `oauth_tokens.json`.
- Use a descriptive `USER_AGENT` that includes contact information, as Basecamp expects API clients to identify themselves.
- Keep token files on local or appropriately permissioned storage.
- This server is designed for local MCP client use. Review the code and deployment model before exposing it on a network.
## License
MIT. See [`LICENSE`](LICENSE).
## Star History
<a href="https://www.star-history.com/#georgeantonopoulos/Basecamp-MCP-Server&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=georgeantonopoulos/Basecamp-MCP-Server&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=georgeantonopoulos/Basecamp-MCP-Server&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=georgeantonopoulos/Basecamp-MCP-Server&type=Date" />
</picture>
</a>
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessSlow