#!/usr/bin/env node // ============================================================================ // Pipedrive MCP Server — Production Quality, 70+ tools, 27 UI apps // ============================================================================ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { PipedriveClient } from "./client.js"; import type { ToolDefinition, ToolResult } from "./types.js"; // --------------------------------------------------------------------------- // Lazy-loaded module registry // --------------------------------------------------------------------------- interface ToolModule { tools: ToolDefinition[]; handle: ( client: PipedriveClient, name: string, args: Record ) => Promise; } interface LazyGroup { path: string; module?: ToolModule; toolNames: string[]; } // Tool groups — metadata loaded eagerly, handlers loaded lazily const groups: LazyGroup[] = [ // 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: [] }, ]; const toolToGroup = new Map(); let allTools: ToolDefinition[] = []; async function loadGroupMetadata(): Promise { const toolDefs: ToolDefinition[] = []; for (let i = 0; i < groups.length; i++) { const mod = (await import(groups[i].path)) as ToolModule; groups[i].module = mod; groups[i].toolNames = mod.tools.map((t) => t.name); for (const tool of mod.tools) { toolToGroup.set(tool.name, i); toolDefs.push(tool); } } allTools = toolDefs; } async function getHandler( toolName: string ): Promise { const idx = toolToGroup.get(toolName); if (idx === undefined) return null; const group = groups[idx]; if (!group.module) { group.module = (await import(group.path)) as ToolModule; } return group.module.handle; } // --------------------------------------------------------------------------- // Server setup // --------------------------------------------------------------------------- async function main(): Promise { // Init client (validates API token) let client: PipedriveClient; try { client = new PipedriveClient(); } catch (e) { console.error( (e as Error).message || "Failed to initialize. Set PIPEDRIVE_API_TOKEN env var." ); process.exit(1); } // Load all tool metadata await loadGroupMetadata(); const server = new Server( { name: "pipedrive-mcp", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // --- List Tools --- server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: allTools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema, })), }; }); // --- Call Tool --- server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = await getHandler(name); if (!handler) { return { content: [{ type: "text" as const, text: `Unknown tool: ${name}` }], isError: true, } as Record; } const result = await handler( client, name, (args as Record) || {} ); return result as unknown as Record; }); // --- Connect stdio transport --- const transport = new StdioServerTransport(); await server.connect(transport); console.error( `Pipedrive MCP server running — ${allTools.length} tools loaded across ${groups.length} modules` ); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });