AGENTS.md•22.7 kB
# AGENTS.md
This document provides comprehensive guidance for AI agents (like Claude) working with the Basecamp MCP Server codebase. It covers project structure, development workflow, code conventions, and testing procedures.
## Project Overview
### What is this project?
**Basecamp MCP Server** is a TypeScript-based Model Context Protocol (MCP) server that enables Large Language Models (LLMs) to interact with Basecamp's project management platform. It acts as a bridge between AI assistants and Basecamp's API.
### Purpose
- Provide LLMs with programmatic access to Basecamp projects, messages, todos, comments, people, and kanban boards
- Implement the Model Context Protocol standard for AI tool integration
- Offer a command-line executable for easy installation in MCP-compatible clients (Claude, Cursor, VS Code, etc.)
- Handle OAuth authentication and API communication with Basecamp
### Key Features
- **25 MCP Tools** organized into 6 categories: Projects, Messages, TODOs, Comments, People, and Kanban
- **Advanced content operations**: append, prepend, search-replace for rich text editing
- **Response size management**: 25K character limit with pagination support
- **Comprehensive error handling**: User-friendly, status-specific error messages
- **OAuth token caching**: Minimize API calls and improve performance
- **Type-safe validation**: Zod schemas for all tool inputs
- **NPM package**: Distributed as `basecamp-mcp` on npm registry
### Technology Stack
| Component | Technology |
|-----------|------------|
| Language | TypeScript 5.7+ |
| Runtime | Node.js 18+ |
| Protocol | Model Context Protocol (MCP) |
| API Client | basecamp-client v1.0.7 |
| Validation | Zod v3.23.8 |
| Transport | Stdio (MCP communication) |
| Build Tool | TypeScript compiler (tsc) |
| Code Quality | Biome 2.3.0 (formatter + linter) |
| Package Manager | npm |
| Authentication | OAuth 2.0 |
## Architecture
### Directory Structure
```
basecamp-mcp/
├── src/ # TypeScript source code
│ ├── index.ts # Main entry point - server initialization
│ ├── types.ts # TypeScript interfaces and types
│ ├── constants.ts # Shared constants (limits, URLs)
│ ├── tools/ # MCP tool implementations (6 categories)
│ │ ├── projects.ts # Project operations (2 tools, 141 lines)
│ │ ├── messages.ts # Message board operations (5 tools, 369 lines)
│ │ ├── todos.ts # Todo management (5 tools, 263 lines)
│ │ ├── comments.ts # Universal commenting (3 tools, 222 lines)
│ │ ├── people.ts # User management (3 tools, 183 lines)
│ │ └── kanban.ts # Kanban board operations (7 tools, 462 lines)
│ ├── utils/ # Cross-cutting utility functions
│ │ ├── auth.ts # OAuth authentication & client setup with token caching
│ │ ├── errorHandlers.ts # API error handling with user-friendly messages
│ │ ├── contentOperations.ts # HTML content manipulation (append/prepend/replace)
│ │ └── serializers.ts # Data serialization and formatting helpers
│ └── schemas/ # Zod validation schemas
│ └── common.ts # Shared input parameter schemas
├── dist/ # Compiled JavaScript (generated by build, gitignored)
├── node_modules/ # Dependencies (gitignored)
├── package.json # NPM metadata, scripts, dependencies
├── tsconfig.json # TypeScript compiler configuration
├── biome.json # Formatter and linter configuration
├── run-evaluation.js # Evaluation test harness
├── evaluations.xml # Test cases in XML format
├── .gitignore # Git exclusions
├── README.md # User-facing documentation
└── AGENTS.md # This file - agent documentation
```
### Module Organization
#### Entry Point: `src/index.ts`
The main entry point follows this flow:
1. **Validate environment variables** - Checks for required OAuth credentials
2. **Create MCP server instance** - Initializes server with name and version
3. **Register tools** - Calls registration functions from each tool category
4. **Create stdio transport** - Sets up MCP protocol communication channel
5. **Connect and listen** - Starts server and waits for client connections
#### Tool Categories: `src/tools/*.ts`
Each tool file exports a single registration function:
```typescript
export function registerToolNameTools(server: McpServer): void {
server.registerTool("tool_name", { config }, async (params) => {
// Implementation
});
}
```
**Tool Registration Pattern:**
1. **Input Schema**: Define Zod schema for parameter validation
2. **Annotations**: Mark tool with metadata (readOnlyHint, destructiveHint, etc.)
3. **Handler**: Async function that executes the tool logic
4. **Error Handling**: Wrap in try-catch, use `handleBasecampError()`
5. **Response**: Return structured JSON via `{ content: [{ type: "text", text: JSON.stringify(...) }] }`
#### Utility Modules: `src/utils/*.ts`
- **auth.ts**: OAuth token management with in-memory caching
- **errorHandlers.ts**: Converts API errors to LLM-friendly messages
- **contentOperations.ts**: Advanced HTML editing operations
- **serializers.ts**: Data normalization and formatting
#### Type Definitions: `src/types.ts`
- `ParsedBasecampUrl`: URL parsing interface
- `PaginationMetadata`: Response pagination info
- `TruncationInfo`: Response size management
#### Shared Schemas: `src/schemas/common.ts`
- `BasecampIdSchema`: Number-based ID validation
- `BasecampUrlSchema`: Basecamp URL validation with format checking
#### Constants: `src/constants.ts`
- `CHARACTER_LIMIT = 25000`: Maximum response size
- `DEFAULT_LIMIT = 20`: Default pagination size
- `MAX_LIMIT = 100`: Maximum items per request
- `BASECAMP_API_BASE`: API endpoint base URL
- `BASECAMP_WEB_BASE`: Web UI base URL
### Design Patterns
1. **Modular Tool Registration**: Each domain exports registration functions, keeping concerns separated
2. **Token Caching**: Bearer tokens cached in memory to minimize OAuth requests
3. **Response Size Management**: Truncate large responses with metadata about what was truncated
4. **Status-Specific Error Handling**: Different error messages for 400, 401, 403, 404, 422, 429, 5xx
5. **Input Validation**: Zod schemas ensure type safety at runtime
6. **MCP Annotations**: Tools tagged with behavioral hints for clients
## Build and Test Commands
### Installation
```bash
# Install dependencies
npm install
# Install globally from npm (for end users)
npm install -g basecamp-mcp
# Run via npx (recommended for testing latest version)
npx -y basecamp-mcp@latest
```
### Development
```bash
# Run in development mode with auto-reload
npm run dev
# Start the compiled server
npm start
# Run TypeScript type checking (no output files)
npx tsc --noEmit
```
### Building
```bash
# Compile TypeScript to JavaScript
npm run build
# Clean build artifacts
npm run clean
# Clean and rebuild
npm run clean && npm run build
# Pre-publish workflow (runs automatically before npm publish)
npm run prepublishOnly
```
Build output goes to `dist/` directory. The compiled `dist/index.js` includes a shebang (`#!/usr/bin/env node`) and is marked as executable in package.json's `bin` field.
### Testing
```bash
# Run placeholder test (currently just echoes "All good")
npm test
# Run evaluation harness (requires ANTHROPIC_API_KEY)
node run-evaluation.js
```
**Evaluation Test Flow:**
1. Loads test cases from `evaluations.xml`
2. Starts MCP server as subprocess
3. Connects to server via stdio transport
4. For each question:
- Sends question to Claude API with MCP tools available
- Claude calls MCP tools to answer the question
- Compares actual answer against expected answer
5. Outputs results to `evaluation-results.json`
6. Exits with code 0 if all tests pass, 1 if any fail
### Code Quality
```bash
# Format code with Biome
npm run format
# Check formatting (no changes)
npm run format:check
# Lint and auto-fix
npm run lint
# Lint check (no changes)
npm run lint:check
# Format + lint combined (recommended)
npm run check
# CI mode - check only, no auto-fix
npm run check:ci
```
## Code Style Guidelines
### TypeScript Configuration
**Compiler Options (tsconfig.json):**
- **Target**: ES2022
- **Module**: Node16 (ESM with .js extensions in imports)
- **Strict Mode**: Enabled (strict null checks, strict function types, etc.)
- **Output**: `dist/` directory
- **Source Maps**: Enabled for debugging
- **Declarations**: Generated for library usage
- **Module Resolution**: Node16 (ESM-compatible)
**Important Rules:**
- Always use `.js` extensions in import statements (ESM requirement)
- All files must pass strict TypeScript checks
- Prefer type inference over explicit types when obvious
- Use `unknown` instead of `any` when type is truly unknown
### Biome Configuration
**Formatting Rules:**
- **Indentation**: 2 spaces (not tabs)
- **Line Width**: 80 characters
- **Line Ending**: LF (Unix-style)
- **Quotes**: Double quotes for strings
- **Semicolons**: Always required
- **Trailing Commas**: Always (even in single-line)
- **Arrow Parens**: Always (even for single parameter)
- **Bracket Spacing**: Enabled (`{ foo }` not `{foo}`)
**Linting Rules:**
- **recommended**: true (all Biome recommended rules)
- **noDoubleEquals**: error (use `===` and `!==`)
- **noExplicitAny**: warn (prefer specific types)
- **noUnusedVariables**: error
- **noUnusedLabels**: error
- **useConsistentArrayType**: error (prefer `T[]` shorthand)
### Naming Conventions
- **Files**: camelCase (e.g., `errorHandlers.ts`, `contentOperations.ts`)
- **Functions**: camelCase (e.g., `handleBasecampError`, `registerProjectTools`)
- **Classes/Interfaces**: PascalCase (e.g., `ParsedBasecampUrl`, `PaginationMetadata`)
- **Constants**: SCREAMING_SNAKE_CASE (e.g., `CHARACTER_LIMIT`, `DEFAULT_LIMIT`)
- **Variables**: camelCase
- **Tool Names**: snake_case with `basecamp_` prefix (e.g., `basecamp_list_projects`)
### Code Organization Patterns
**Tool Implementation Pattern:**
```typescript
/**
* [Category]-related tools for Basecamp MCP server
*/
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BasecampIdSchema } from "../schemas/common.js";
import { initializeBasecampClient } from "../utils/auth.js";
import { handleBasecampError } from "../utils/errorHandlers.js";
export function registerCategoryTools(server: McpServer): void {
server.registerTool(
"basecamp_tool_name",
{
title: "Human-Readable Title",
description: "Detailed description with examples",
inputSchema: {
param: z.string().describe("Parameter description"),
},
annotations: {
readOnlyHint: true, // Tool doesn't modify data
destructiveHint: false, // Tool doesn't delete data
idempotentHint: true, // Same input = same output
openWorldHint: true, // Tool returns open-ended data
},
},
async (params) => {
try {
const client = await initializeBasecampClient();
// Tool implementation
return {
content: [
{ type: "text", text: JSON.stringify(result, null, 2) }
],
};
} catch (error) {
return {
content: [
{ type: "text", text: handleBasecampError(error) }
],
};
}
},
);
}
```
**Error Handling Pattern:**
Always use `handleBasecampError()` utility:
```typescript
try {
// API call
} catch (error) {
return {
content: [{ type: "text", text: handleBasecampError(error) }],
};
}
```
**Response Format:**
Always return JSON with proper formatting:
```typescript
return {
content: [
{
type: "text",
text: JSON.stringify(data, null, 2), // 2-space indentation
},
],
};
```
### Import Order
1. External dependencies (SDK, libraries)
2. Internal utilities (auth, error handlers)
3. Internal schemas
4. Internal types/constants
Example:
```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BasecampIdSchema } from "../schemas/common.js";
import { initializeBasecampClient } from "../utils/auth.js";
import { handleBasecampError } from "../utils/errorHandlers.js";
```
## Development Workflow
### Setting Up Development Environment
1. **Clone the repository**:
```bash
git clone https://github.com/stefanoverna/basecamp-mcp.git
cd basecamp-mcp
```
2. **Install dependencies**:
```bash
npm install
```
3. **Set up environment variables** (create `.env` file):
```bash
BASECAMP_CLIENT_ID=your_client_id
BASECAMP_CLIENT_SECRET=your_client_secret
BASECAMP_REFRESH_TOKEN=your_refresh_token
BASECAMP_USER_AGENT="YourApp (your@email.com)"
BASECAMP_ACCOUNT_ID=your_account_id
```
4. **Build the project**:
```bash
npm run build
```
5. **Run in development mode**:
```bash
npm run dev
```
### Making Changes
**When adding a new tool:**
1. Choose the appropriate tool category file in `src/tools/`
2. Add the tool registration within the existing `register*Tools()` function
3. Follow the tool implementation pattern (see Code Organization Patterns)
4. Define input schema using Zod
5. Add proper error handling with `handleBasecampError()`
6. Format response as JSON with 2-space indentation
7. Update tool count in documentation if needed
**When adding a new tool category:**
1. Create new file in `src/tools/` (e.g., `calendars.ts`)
2. Implement `registerCalendarTools(server: McpServer)` function
3. Export the registration function
4. Import and call in `src/index.ts`
5. Update documentation
**When modifying utilities:**
1. Ensure backward compatibility with existing tools
2. Update JSDoc comments
3. Run type checking: `npx tsc --noEmit`
4. Test with evaluation harness if applicable
### Testing Changes
1. **Type checking**:
```bash
npx tsc --noEmit
```
2. **Format and lint**:
```bash
npm run check
```
3. **Manual testing**:
```bash
npm run dev
# Test with MCP client (Claude Code, etc.)
```
4. **Evaluation testing**:
```bash
npm run build
ANTHROPIC_API_KEY=your_key node run-evaluation.js
```
### Publishing Changes
The project uses `np` for releases:
```bash
# Run np to publish (interactive prompts)
npx np
# Pre-publish automatically runs:
# - npm run clean
# - npm run build
```
## Testing Instructions
### Manual Testing
**Prerequisites:**
- Built server: `npm run build`
- Environment variables configured
- MCP client available (Claude Code, etc.)
**Steps:**
1. Start server in development mode:
```bash
npm run dev
```
2. Connect with MCP client
3. Test tool calls with various inputs:
- Valid inputs (happy path)
- Invalid inputs (validation errors)
- Edge cases (empty lists, missing resources)
- Error conditions (403, 404, etc.)
### Automated Evaluation Testing
**Prerequisites:**
- Built server: `npm run build`
- `ANTHROPIC_API_KEY` environment variable set
- Test cases defined in `evaluations.xml`
**Run evaluations:**
```bash
node run-evaluation.js
```
**Evaluation Output:**
- Console: Progress and summary statistics
- `evaluation-results.json`: Detailed results for each test case
**Evaluation XML Format:**
```xml
<evaluation>
<qa_pair>
<question>What is the name of project 12345?</question>
<answer>Project Name</answer>
</qa_pair>
<!-- More test cases -->
</evaluation>
```
**Adding New Test Cases:**
1. Open `evaluations.xml`
2. Add new `<qa_pair>` element with question and expected answer
3. Run evaluation harness
4. Verify results in `evaluation-results.json`
**Success Criteria:**
- All tests pass (100% pass rate)
- Answers match expected results (exact or contains match)
- No errors or timeouts
## Dependencies
### Production Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| `@modelcontextprotocol/sdk` | ^1.6.1 | MCP protocol implementation |
| `basecamp-client` | ^1.0.7 | Official Basecamp API client |
| `zod` | ^3.23.8 | Runtime type validation |
### Development Dependencies
| Package | Version | Purpose |
|---------|---------|---------|
| `typescript` | ^5.7.2 | TypeScript compiler |
| `tsx` | ^4.19.2 | TypeScript execution for dev mode |
| `@biomejs/biome` | 2.3.0 | Code formatter and linter |
| `@types/node` | ^22.10.0 | Node.js type definitions |
| `@anthropic-ai/sdk` | ^0.67.0 | Claude API for evaluation testing |
| `dotenv` | ^17.2.3 | Environment variable loading |
| `xml2js` | ^0.6.2 | XML parsing for evaluation tests |
| `np` | ^10.2.0 | Publish automation tool |
## Environment Variables
**Required for Server Operation:**
- `BASECAMP_CLIENT_ID`: OAuth client ID from Basecamp app registration
- `BASECAMP_CLIENT_SECRET`: OAuth client secret
- `BASECAMP_REFRESH_TOKEN`: Refresh token for authentication
- `BASECAMP_USER_AGENT`: Application identifier (format: "YourApp (email@example.com)")
- `BASECAMP_ACCOUNT_ID`: Basecamp account ID (optional for some operations)
**Required for Evaluation Testing:**
- `ANTHROPIC_API_KEY`: Claude API key for running evaluation harness
## Tool Implementation Reference
### Available Tool Categories
1. **Projects (2 tools)**:
- `basecamp_list_projects`: List all projects with optional filtering
- `basecamp_get_project`: Get detailed project information
2. **Messages (5 tools)**:
- `basecamp_list_messages`: List messages with filtering
- `basecamp_list_message_types`: Get available message categories
- `basecamp_get_message`: Retrieve single message
- `basecamp_create_message`: Create new message
- `basecamp_update_message`: Update message with content operations
3. **TODOs (5 tools)**:
- `basecamp_get_todoset`: Get todo set container
- `basecamp_list_todos`: List todos with status filtering
- `basecamp_create_todo`: Create new todo
- `basecamp_complete_todo`: Mark complete
- `basecamp_uncomplete_todo`: Mark incomplete
4. **Comments (3 tools)**:
- `basecamp_list_comments`: List comments on any resource
- `basecamp_create_comment`: Add comment to resource
- `basecamp_update_comment`: Update comment with content operations
5. **People (3 tools)**:
- `basecamp_get_me`: Get authenticated user info
- `basecamp_list_people`: List people with filtering
- `basecamp_get_person`: Get person details
6. **Kanban (7 tools)**:
- `basecamp_list_kanban_columns`: List board columns
- `basecamp_list_kanban_cards`: List column cards
- `basecamp_get_kanban_card`: Get card details
- `basecamp_create_kanban_card`: Create card
- `basecamp_update_kanban_card`: Update card (title, dates, assignees)
- `basecamp_move_kanban_card`: Move between columns
- `basecamp_create_kanban_step`: Add checklist step
### Adding New Tools Checklist
- [ ] Choose appropriate tool category file (or create new one)
- [ ] Define Zod input schema
- [ ] Write tool description with examples
- [ ] Implement async handler function
- [ ] Use `initializeBasecampClient()` for API access
- [ ] Wrap in try-catch with `handleBasecampError()`
- [ ] Return JSON-formatted response
- [ ] Add appropriate MCP annotations
- [ ] Test with type checking: `npx tsc --noEmit`
- [ ] Test with linting: `npm run check`
- [ ] Test manually with MCP client
- [ ] Add evaluation test case if applicable
- [ ] Update README.md if needed
## Common Patterns and Best Practices
### Pagination
Use `asyncPagedToArray` from basecamp-client:
```typescript
import { asyncPagedToArray } from "basecamp-client";
const items = await asyncPagedToArray({
fetchPage: client.resource.list,
request: { query: {} },
});
```
### Filtering
Apply regex filtering after fetching:
```typescript
if (params.filter) {
const regex = new RegExp(params.filter, "i");
filteredItems = items.filter((item) => regex.test(item.name));
}
```
### Response Truncation
Check response size against `CHARACTER_LIMIT`:
```typescript
import { CHARACTER_LIMIT } from "../constants.js";
const jsonStr = JSON.stringify(data, null, 2);
if (jsonStr.length > CHARACTER_LIMIT) {
// Truncate and add metadata
}
```
### Content Operations
Use content operations for update tools:
```typescript
import { applyContentOperation } from "../utils/contentOperations.js";
const newContent = applyContentOperation(
currentContent,
params.operation,
params.content,
);
```
### OAuth Token Caching
Always use `initializeBasecampClient()` to get authenticated client:
```typescript
import { initializeBasecampClient } from "../utils/auth.js";
const client = await initializeBasecampClient();
// Token is cached automatically
```
## Troubleshooting
### Common Issues
**Issue**: `Missing required environment variables`
- **Solution**: Check `.env` file or environment has all required variables
**Issue**: `Authentication failed (401)`
- **Solution**: Refresh token may be expired - generate new one from Basecamp
**Issue**: `Module not found` errors
- **Solution**: Ensure imports use `.js` extensions (ESM requirement)
**Issue**: Type errors after changes
- **Solution**: Run `npx tsc --noEmit` to see full error details
**Issue**: Biome formatting conflicts
- **Solution**: Run `npm run format` to auto-fix formatting issues
**Issue**: Evaluation tests fail
- **Solution**: Check `evaluation-results.json` for detailed error messages
## Additional Resources
- **MCP Documentation**: https://modelcontextprotocol.io
- **Basecamp API Docs**: https://github.com/basecamp/bc3-api
- **TypeScript Handbook**: https://www.typescriptlang.org/docs/
- **Zod Documentation**: https://zod.dev
- **Biome Documentation**: https://biomejs.dev
## Version History
- **1.0.1** (Current): Production release with 25 tools across 6 categories
- **1.0.0**: Initial public release
## License
MIT License - See LICENSE file for details
## Contributing
This project follows standard open-source contribution practices:
1. Fork the repository
2. Create a feature branch
3. Make your changes following code style guidelines
4. Run tests and type checking
5. Submit pull request with clear description
---
**Last Updated**: 2025-10-28
**Maintained By**: Stefano Verna (stefano.verna@gmail.com)