- deal-dashboard: Overview stats, won/lost ratio, revenue forecast - deal-detail: Full deal with products, activities, participants, timeline - deal-grid: Sortable deal list with filters - pipeline-kanban: Drag-drop pipeline board - pipeline-analytics: Conversion rates, velocity, bottleneck analysis - pipeline-funnel: Visual funnel with stage metrics - person-detail: Contact card with deals, activities, files - person-grid: Contact directory with search - org-detail: Organization with people, deals, activities - org-grid: Organization directory - activity-dashboard: Activity calendar/list with completion tracking - activity-calendar: Monthly calendar view - lead-inbox: Lead list with labels and quick actions - product-catalog: Product list with pricing - goal-tracker: Goals with progress bars - revenue-dashboard: Revenue analytics, forecasting - email-inbox: Mail threads with preview - deals-timeline: Timeline of deal progression - search-results: Universal search - won-deals: Closed-won deals celebration view All apps use React with dark theme. Self-contained with inline shared components. Each app has App.tsx, index.html, and vite.config.ts. Ports 3000-3019 for dev servers.
186 lines
6.1 KiB
JavaScript
186 lines
6.1 KiB
JavaScript
#!/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<string, unknown>
|
|
) => Promise<ToolResult>;
|
|
}
|
|
|
|
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<string, number>();
|
|
let allTools: ToolDefinition[] = [];
|
|
|
|
async function loadGroupMetadata(): Promise<void> {
|
|
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<ToolModule["handle"] | null> {
|
|
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<void> {
|
|
// 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<string, unknown>;
|
|
}
|
|
const result = await handler(
|
|
client,
|
|
name,
|
|
(args as Record<string, unknown>) || {}
|
|
);
|
|
return result as unknown as Record<string, unknown>;
|
|
});
|
|
|
|
// --- 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);
|
|
});
|