- READMEs added: asana, close, freshdesk, google-console, gusto, square - main.ts + server.ts (lazy loading): activecampaign, clickup, klaviyo, mailchimp, pipedrive, trello, touchbistro, closebot, close, google-console - All 13 compile with 0 TSC errors
187 lines
6.0 KiB
TypeScript
187 lines
6.0 KiB
TypeScript
/**
|
|
* 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<string, unknown>
|
|
) => Promise<ToolResult>;
|
|
}
|
|
|
|
interface LazyGroup {
|
|
path: string;
|
|
module?: ToolModule;
|
|
toolNames: string[];
|
|
}
|
|
|
|
export class PipedriveMCPServer {
|
|
private server: Server;
|
|
private client: PipedriveClient;
|
|
private groups: LazyGroup[] = [];
|
|
private toolToGroup: Map<string, number> = 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<void> {
|
|
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<ToolModule['handle'] | null> {
|
|
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<string, unknown>;
|
|
}
|
|
|
|
const result = await handler(
|
|
this.client,
|
|
name,
|
|
(args as Record<string, unknown>) || {}
|
|
);
|
|
|
|
return result as unknown as Record<string, unknown>;
|
|
});
|
|
}
|
|
|
|
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`
|
|
);
|
|
}
|
|
}
|