Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

A modern **TypeScript MCP server** for Basecamp 3, providing seamless integration with Claude Desktop and Cursor IDE through the Model Context Protocol. Built with the official @modelcontextprotocol/sdk and ready for NPX installation.

βœ… **TypeScript-First:** Modern, type-safe implementation with full async/await support
πŸš€ **NPX Ready:** Install and run with `npx @basecamp/mcp-server`
⚑ **46 API Tools:** Complete Basecamp 3 integration with all major features
βœ… **TypeScript-First:** Modern, type-safe implementation with full async/await support
πŸš€ **NPX Ready:** Install and run with `npx @basecamp/mcp-server`
⚑ **51 API Tools:** Complete Basecamp 3 integration with all major features including full TODO CRUD operations

## Quick Setup

Expand All @@ -22,10 +22,45 @@ npx jhliberty/basecamp-mcp-server config claude # For Claude Desktop
npx jhliberty/basecamp-mcp-server config cursor # For Cursor IDE
```

### Claude Code CLI

Add the Basecamp MCP server to Claude Code CLI with a single command:

```bash
claude mcp add --transport stdio basecamp -- npx -y jhliberty/basecamp-mcp-server
```

**With environment variables:**
```bash
claude mcp add --transport stdio basecamp \
--env BASECAMP_ACCOUNT_ID=your_account_id \
-- npx -y jhliberty/basecamp-mcp-server
```

**For team/project shared use:**
```bash
claude mcp add --transport stdio basecamp --scope project \
-- npx -y jhliberty/basecamp-mcp-server
```

**Verify installation:**
```bash
# List all configured MCP servers
claude mcp list

# Get details for the Basecamp server
claude mcp get basecamp
```

Once installed, ask Claude Code questions like:
- "What are all my Basecamp projects?"
- "Show me the to-do lists in project X"
- "Create a new todo in the Marketing project"

### Prerequisites

- **Node.js 18+** (required for ES modules)
- A Basecamp 3 account
- A Basecamp 3 account
- A Basecamp OAuth application (create one at https://launchpad.37signals.com/integrations)

## Local Development Setup
Expand Down Expand Up @@ -77,7 +112,7 @@ npx jhliberty/basecamp-mcp-server config cursor # For Cursor IDE
7. **Verify in your AI assistant:**
- **Cursor**: Go to Settings β†’ MCP, look for "basecamp" with a green checkmark
- **Claude Desktop**: Look for tools icon (πŸ”) in chat interface
- Available tools: **46 tools** for complete Basecamp control
- Available tools: **51 tools** for complete Basecamp control

### Test Your Setup

Expand Down Expand Up @@ -119,7 +154,7 @@ Based on the [official MCP quickstart guide](https://modelcontextprotocol.io/qui

4. **Verify in Claude Desktop:**
- Look for the "Search and tools" icon (πŸ”) in the chat interface
- You should see "basecamp" listed with all 46 tools available
- You should see "basecamp" listed with all 51 tools available
- Toggle the tools on to enable Basecamp integration

### Claude Desktop Configuration
Expand Down Expand Up @@ -149,7 +184,10 @@ Example configuration generated:
Ask Claude things like:
- "What are my current Basecamp projects?"
- "Show me the latest campfire messages from the Technology project"
- "Create a new card in the Development column with title 'Fix login bug'"
- "Create a new todo list called 'Sprint Planning' in the Development project"
- "Create a new todo 'Fix login bug' with due date next Friday"
- "Update the todo to change the due date to next Monday"
- "Mark the todo as complete"
- "Get all todo items from the Marketing project"
- "Search for messages containing 'deadline'"

Expand All @@ -176,7 +214,12 @@ Once configured, you can use these tools in Cursor:
- `get_projects` - Get all Basecamp projects
- `get_project` - Get details for a specific project
- `get_todolists` - Get todo lists for a project
- `create_todolist` - Create a new todo list in a project
- `get_todos` - Get todos from a todo list
- `create_todo` - Create a new todo with assignees, due dates, and descriptions
- `update_todo` - Update an existing todo (content, description, assignees, dates)
- `complete_todo` - Mark a todo as complete
- `uncomplete_todo` - Mark a completed todo as incomplete
- `search_basecamp` - Search across projects, todos, and messages
- `get_comments` - Get comments for a Basecamp item
- `get_campfire_lines` - Get recent messages from a Basecamp campfire
Expand Down Expand Up @@ -226,6 +269,10 @@ Once configured, you can use these tools in Cursor:
Ask Cursor things like:
- "Show me all my Basecamp projects"
- "What todos are in project X?"
- "Create a new todo list called 'Feature Development'"
- "Add a todo 'Implement user authentication' with due date December 20th"
- "Update the todo description to include implementation details"
- "Mark the authentication todo as complete"
- "Search for messages containing 'deadline'"
- "Get details for the Technology project"
- "Show me the card table for project X"
Expand All @@ -241,7 +288,7 @@ Ask Cursor things like:

The project uses the **official @modelcontextprotocol/sdk** for maximum reliability and compatibility:

1. **MCP Server** (`src/index.ts`) - Official MCP SDK with 46 tools, compatible with both Cursor and Claude Desktop
1. **MCP Server** (`src/index.ts`) - Official MCP SDK with 51 tools, compatible with both Cursor and Claude Desktop
2. **OAuth App** (`src/lib/oauth-app.ts`) - Handles OAuth 2.0 flow with Basecamp
3. **Token Storage** (`src/lib/token-storage.ts`) - Securely stores OAuth tokens
4. **Basecamp Client** (`src/lib/basecamp-client.ts`) - Basecamp API client library
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

163 changes: 163 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,19 @@ class BasecampMCPServer {
required: ['project_id'],
},
},
{
name: 'create_todolist',
description: 'Create a new todo list in a project',
inputSchema: {
type: 'object',
properties: {
project_id: { type: 'string', description: 'The project ID' },
name: { type: 'string', description: 'The todo list name/title' },
description: { type: 'string', description: 'Optional todo list description' },
},
required: ['project_id', 'name'],
},
},
{
name: 'get_todos',
description: 'Get todos from a todo list',
Expand All @@ -172,6 +185,66 @@ class BasecampMCPServer {
required: ['project_id', 'todolist_id'],
},
},
{
name: 'create_todo',
description: 'Create a new todo in a todo list',
inputSchema: {
type: 'object',
properties: {
project_id: { type: 'string', description: 'Project ID' },
todolist_id: { type: 'string', description: 'The todo list ID' },
content: { type: 'string', description: 'The todo content/title' },
description: { type: 'string', description: 'Optional todo description' },
assignee_ids: { type: 'array', items: { type: 'string' }, description: 'Array of person IDs to assign' },
due_on: { type: 'string', description: 'Optional due date (ISO 8601 format)' },
starts_on: { type: 'string', description: 'Optional start date (ISO 8601 format)' },
notify: { type: 'boolean', description: 'Whether to notify assignees (default: false)' },
},
required: ['project_id', 'todolist_id', 'content'],
},
},
{
name: 'update_todo',
description: 'Update an existing todo',
inputSchema: {
type: 'object',
properties: {
project_id: { type: 'string', description: 'Project ID' },
todo_id: { type: 'string', description: 'The todo ID' },
content: { type: 'string', description: 'The todo content/title' },
description: { type: 'string', description: 'The todo description' },
assignee_ids: { type: 'array', items: { type: 'string' }, description: 'Array of person IDs to assign' },
due_on: { type: 'string', description: 'Due date (ISO 8601 format)' },
starts_on: { type: 'string', description: 'Start date (ISO 8601 format)' },
notify: { type: 'boolean', description: 'Whether to notify assignees (default: false)' },
},
required: ['project_id', 'todo_id'],
},
},
{
name: 'complete_todo',
description: 'Mark a todo as complete',
inputSchema: {
type: 'object',
properties: {
project_id: { type: 'string', description: 'Project ID' },
todo_id: { type: 'string', description: 'The todo ID' },
},
required: ['project_id', 'todo_id'],
},
},
{
name: 'uncomplete_todo',
description: 'Mark a todo as incomplete',
inputSchema: {
type: 'object',
properties: {
project_id: { type: 'string', description: 'Project ID' },
todo_id: { type: 'string', description: 'The todo ID' },
},
required: ['project_id', 'todo_id'],
},
},

// Card Table tools
{
Expand Down Expand Up @@ -590,6 +663,24 @@ class BasecampMCPServer {
};
}

case 'create_todolist': {
const todolist = await client.createTodoList(
typedArgs.project_id,
typedArgs.name,
typedArgs.description
);
return {
content: [{
type: 'text',
text: JSON.stringify({
status: 'success',
todolist,
message: `Todo list '${typedArgs.name}' created successfully`
}, null, 2)
}]
};
}

case 'get_todos': {
const todos = await client.getTodos(typedArgs.project_id, typedArgs.todolist_id);
return {
Expand All @@ -604,6 +695,78 @@ class BasecampMCPServer {
};
}

case 'create_todo': {
const todo = await client.createTodo(
typedArgs.project_id,
typedArgs.todolist_id,
typedArgs.content,
typedArgs.description,
typedArgs.assignee_ids,
typedArgs.due_on,
typedArgs.starts_on,
typedArgs.notify || false
);
return {
content: [{
type: 'text',
text: JSON.stringify({
status: 'success',
todo,
message: `Todo '${typedArgs.content}' created successfully`
}, null, 2)
}]
};
}

case 'update_todo': {
const todo = await client.updateTodo(
typedArgs.project_id,
typedArgs.todo_id,
typedArgs.content,
typedArgs.description,
typedArgs.assignee_ids,
typedArgs.due_on,
typedArgs.starts_on,
typedArgs.notify || false
);
return {
content: [{
type: 'text',
text: JSON.stringify({
status: 'success',
todo,
message: 'Todo updated successfully'
}, null, 2)
}]
};
}

case 'complete_todo': {
await client.completeTodo(typedArgs.project_id, typedArgs.todo_id);
return {
content: [{
type: 'text',
text: JSON.stringify({
status: 'success',
message: 'Todo marked as complete'
}, null, 2)
}]
};
}

case 'uncomplete_todo': {
await client.uncompleteTodo(typedArgs.project_id, typedArgs.todo_id);
return {
content: [{
type: 'text',
text: JSON.stringify({
status: 'success',
message: 'Todo marked as incomplete'
}, null, 2)
}]
};
}

case 'get_card_table': {
const cardTable = await client.getCardTable(typedArgs.project_id);
const cardTableDetails = await client.getCardTableDetails(typedArgs.project_id, cardTable.id);
Expand Down
Loading