144 lines
4.0 KiB
TypeScript

/**
* 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<string, any>;
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');
}
}