/** * Pipedrive MCP Server Class * Implements lazy-loaded tool modules for optimal performance */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { PipedriveClient } from './client.js'; import type { ToolDefinition, ToolResult } from './types.js'; interface ToolModule { tools: ToolDefinition[]; handle: ( client: PipedriveClient, name: string, args: Record ) => Promise; } interface LazyGroup { path: string; module?: ToolModule; toolNames: string[]; } export class PipedriveMCPServer { private server: Server; private client: PipedriveClient; private groups: LazyGroup[] = []; private toolToGroup: Map = new Map(); private allTools: ToolDefinition[] = []; constructor(client: PipedriveClient) { this.client = client; this.server = new Server( { name: 'pipedrive-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.setupToolModules(); this.setupHandlers(); } private setupToolModules() { // Tool groups — metadata loaded eagerly, handlers loaded lazily this.groups = [ // Core tools { path: './tools/deals-tools.js', toolNames: [] }, { path: './tools/persons-tools.js', toolNames: [] }, { path: './tools/organizations-tools.js', toolNames: [] }, { path: './tools/activities-tools.js', toolNames: [] }, { path: './tools/pipelines-tools.js', toolNames: [] }, { path: './tools/stages-tools.js', toolNames: [] }, { path: './tools/products-tools.js', toolNames: [] }, { path: './tools/leads-tools.js', toolNames: [] }, { path: './tools/notes-tools.js', toolNames: [] }, { path: './tools/files-tools.js', toolNames: [] }, { path: './tools/filters-tools.js', toolNames: [] }, { path: './tools/goals-tools.js', toolNames: [] }, { path: './tools/webhooks-tools.js', toolNames: [] }, { path: './tools/users-tools.js', toolNames: [] }, { path: './tools/mail-tools.js', toolNames: [] }, { path: './tools/subscriptions-tools.js', toolNames: [] }, // UI apps { path: './apps/deal-dashboard.js', toolNames: [] }, { path: './apps/deal-detail.js', toolNames: [] }, { path: './apps/deal-grid.js', toolNames: [] }, { path: './apps/pipeline-kanban.js', toolNames: [] }, { path: './apps/pipeline-analytics.js', toolNames: [] }, { path: './apps/pipeline-funnel.js', toolNames: [] }, { path: './apps/person-detail.js', toolNames: [] }, { path: './apps/person-grid.js', toolNames: [] }, { path: './apps/org-detail.js', toolNames: [] }, { path: './apps/org-grid.js', toolNames: [] }, { path: './apps/activity-dashboard.js', toolNames: [] }, { path: './apps/activity-calendar.js', toolNames: [] }, { path: './apps/lead-inbox.js', toolNames: [] }, { path: './apps/lead-detail.js', toolNames: [] }, { path: './apps/product-catalog.js', toolNames: [] }, { path: './apps/product-detail.js', toolNames: [] }, { path: './apps/note-manager.js', toolNames: [] }, { path: './apps/file-manager.js', toolNames: [] }, { path: './apps/goal-tracker.js', toolNames: [] }, { path: './apps/revenue-dashboard.js', toolNames: [] }, { path: './apps/email-inbox.js', toolNames: [] }, { path: './apps/filter-manager.js', toolNames: [] }, { path: './apps/user-stats.js', toolNames: [] }, { path: './apps/deals-timeline.js', toolNames: [] }, { path: './apps/subscription-manager.js', toolNames: [] }, { path: './apps/search-results.js', toolNames: [] }, { path: './apps/won-deals.js', toolNames: [] }, ]; } private async loadGroupMetadata(): Promise { const toolDefs: ToolDefinition[] = []; for (let i = 0; i < this.groups.length; i++) { const mod = (await import(this.groups[i].path)) as ToolModule; this.groups[i].module = mod; this.groups[i].toolNames = mod.tools.map((t) => t.name); for (const tool of mod.tools) { this.toolToGroup.set(tool.name, i); toolDefs.push(tool); } } this.allTools = toolDefs; } private async getHandler( toolName: string ): Promise { const idx = this.toolToGroup.get(toolName); if (idx === undefined) return null; const group = this.groups[idx]; if (!group.module) { group.module = (await import(group.path)) as ToolModule; } return group.module.handle; } private setupHandlers() { // List Tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { // Load metadata on first request if (this.allTools.length === 0) { await this.loadGroupMetadata(); } return { tools: this.allTools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema, })), }; }); // Call Tool this.server.setRequestHandler(CallToolRequestSchema, async (request) => { // Ensure metadata is loaded if (this.allTools.length === 0) { await this.loadGroupMetadata(); } const { name, arguments: args } = request.params; const handler = await this.getHandler(name); if (!handler) { return { content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }], isError: true, } as Record; } const result = await handler( this.client, name, (args as Record) || {} ); return result as unknown as Record; }); } async connect(transport: any) { await this.server.connect(transport); // Load metadata after connection await this.loadGroupMetadata(); console.error( `Pipedrive MCP server — ${this.allTools.length} tools loaded across ${this.groups.length} modules` ); } }