5.7 KiB
5.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
This is a Basecamp 3 MCP (Model Context Protocol) Server that allows AI assistants (Cursor, Claude Desktop) to interact with Basecamp directly. It uses OAuth 2.0 for authentication and provides 46+ tools for Basecamp operations.
Development Commands
# Setup (one-time) - requires Python 3.10+
# Option 1: Using uv (recommended - auto-downloads Python 3.12)
uv venv --python 3.12 venv && source venv/bin/activate && uv pip install -r requirements.txt && uv pip install mcp
# Option 2: Using pip (if Python 3.10+ already installed)
python setup.py # Creates venv, installs deps, tests server
# OAuth Authentication
python oauth_app.py # Start OAuth server at http://localhost:8000
# Run the MCP server (for testing)
./venv/bin/python basecamp_fastmcp.py # FastMCP server (recommended)
./venv/bin/python mcp_server_cli.py # Legacy CLI server
# Test the server manually
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | python basecamp_fastmcp.py
# Run tests
python -m pytest tests/ -v # All tests
python -m pytest tests/test_cli_server.py -v # Specific test file
# Generate client configs
python generate_cursor_config.py # For Cursor IDE
python generate_claude_desktop_config.py # For Claude Desktop
Architecture
Core Files
| File | Purpose |
|---|---|
basecamp_fastmcp.py |
Main MCP server using official Anthropic FastMCP framework (46 tools) |
mcp_server_cli.py |
Legacy JSON-RPC server (same tools, custom implementation) |
basecamp_client.py |
Basecamp 3 API client - all HTTP methods and endpoints |
basecamp_oauth.py |
OAuth 2.0 client for 37signals Launchpad |
auth_manager.py |
Automatic token refresh before API calls |
token_storage.py |
Thread-safe OAuth token persistence (oauth_tokens.json) |
search_utils.py |
Cross-project search functionality |
oauth_app.py |
Flask app for OAuth flow (browser-based login) |
Data Flow
MCP Client (Cursor/Claude)
↓ JSON-RPC via stdio
basecamp_fastmcp.py (MCP Server)
↓ calls
auth_manager.ensure_authenticated() → token_storage → basecamp_oauth.refresh_token()
↓ if valid
basecamp_client.py (API calls)
↓ HTTP requests
Basecamp 3 API (https://3.basecampapi.com/{account_id})
Authentication Flow
- User runs
python oauth_app.pyand visitshttp://localhost:8000 - Redirected to 37signals for authorization
- Callback stores tokens in
oauth_tokens.json(600 permissions) - MCP server uses
auth_manager.ensure_authenticated()to auto-refresh expired tokens
Tool Categories (46 total)
- Projects:
get_projects,get_project - Todos:
get_todolists,get_todos,create_todo,update_todo,delete_todo,complete_todo,uncomplete_todo - Card Tables (Kanban):
get_card_table,get_columns,get_cards,create_card,move_card,complete_card, etc. - Card Steps:
get_card_steps,create_card_step,complete_card_step, etc. - Comments:
get_comments,create_comment - Campfire (Chat):
get_campfire_lines - Documents:
get_documents,create_document,update_document,trash_document - Search:
search_basecamp,global_search - Webhooks:
get_webhooks,create_webhook,delete_webhook - Other:
get_daily_check_ins,get_question_answers,get_events,create_attachment,get_uploads
Key Patterns
Adding New MCP Tools (FastMCP)
# In basecamp_fastmcp.py
@mcp.tool()
async def new_tool_name(project_id: str, other_param: Optional[str] = None) -> Dict[str, Any]:
"""Tool description shown to AI.
Args:
project_id: The project ID
other_param: Optional description
"""
client = _get_basecamp_client()
if not client:
return _get_auth_error_response()
try:
result = await _run_sync(client.some_method, project_id, other_param)
return {"status": "success", "data": result}
except Exception as e:
logger.error(f"Error: {e}")
return {"error": "Execution error", "message": str(e)}
Adding Basecamp API Methods
# In basecamp_client.py
def new_api_method(self, project_id, resource_id):
"""Method description."""
endpoint = f'buckets/{project_id}/resource/{resource_id}.json'
response = self.get(endpoint) # or .post(), .put(), .delete(), .patch()
if response.status_code == 200:
return response.json()
else:
raise Exception(f"Failed: {response.status_code} - {response.text}")
Pagination Handling
Basecamp paginates list endpoints (~15 items/page). See get_todos() in basecamp_client.py for the pattern using Link header.
Environment Configuration
Required in .env:
BASECAMP_CLIENT_ID=your_client_id
BASECAMP_CLIENT_SECRET=your_client_secret
BASECAMP_ACCOUNT_ID=your_account_id
BASECAMP_REDIRECT_URI=http://localhost:8000/auth/callback
USER_AGENT="Your App Name (your@email.com)"
The account ID can be found in your Basecamp URL: https://3.basecamp.com/{account_id}/projects
Troubleshooting
- Token expired: Visit
http://localhost:8000to re-authenticate (auto-refresh usually handles this) - Missing tools in Cursor/Claude: Restart the client completely after config changes
- Logs: Check
basecamp_fastmcp.logormcp_cli_server.logfor errors - Test token validity:
python auth_manager.pyto force refresh check
Reference
- API docs in
reference/bc3-api/sections/- useful when implementing new endpoints - Local queries/scripts go in
local_queries/(git-ignored)