/** * Basecamp MCP Server */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { BasecampClient } from './clients/basecamp.js'; import { registerProjectsTools } from './tools/projects-tools.js'; import { registerTodolistsTools } from './tools/todolists-tools.js'; import { registerTodosTools } from './tools/todos-tools.js'; import { registerMessagesTools } from './tools/messages-tools.js'; import { registerCommentsTools } from './tools/comments-tools.js'; import { registerCampfiresTools } from './tools/campfires-tools.js'; import { registerSchedulesTools } from './tools/schedules-tools.js'; import { registerDocumentsTools } from './tools/documents-tools.js'; import { registerUploadsTools } from './tools/uploads-tools.js'; import { registerPeopleTools } from './tools/people-tools.js'; import { registerQuestionnairesTools } from './tools/questionnaires-tools.js'; import { registerWebhooksTools } from './tools/webhooks-tools.js'; import { registerRecordingsTools } from './tools/recordings-tools.js'; interface BasecampServerConfig { accountId: string; accessToken: string; userAgent?: string; } export class BasecampServer { private server: Server; private client: BasecampClient; private tools: Map; constructor(config: BasecampServerConfig) { this.server = new Server( { name: 'basecamp-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.client = new BasecampClient({ accountId: config.accountId, accessToken: config.accessToken, userAgent: config.userAgent, }); this.tools = new Map(); this.registerAllTools(); this.setupHandlers(); // Error handling this.server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } private registerAllTools() { const toolRegistrars = [ registerProjectsTools, registerTodolistsTools, registerTodosTools, registerMessagesTools, registerCommentsTools, registerCampfiresTools, registerSchedulesTools, registerDocumentsTools, registerUploadsTools, registerPeopleTools, registerQuestionnairesTools, registerWebhooksTools, registerRecordingsTools, ]; for (const registrar of toolRegistrars) { const tools = registrar(this.client); for (const tool of tools) { this.tools.set(tool.name, tool); } } console.error(`[Basecamp MCP] Registered ${this.tools.size} tools`); } private setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = Array.from(this.tools.values()).map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema, })); return { tools }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = this.tools.get(name); if (!tool) { throw new McpError( ErrorCode.MethodNotFound, `Tool not found: ${name}` ); } try { return await tool.handler(args || {}); } catch (error: any) { const errorMessage = error.message || 'Unknown error occurred'; console.error(`[Tool Error] ${name}:`, errorMessage); throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${errorMessage}` ); } }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('[Basecamp MCP] Server running on stdio'); } }