#!/usr/bin/env node
/**
* Pipedrive MCP Server
*
* An MCP server that provides tools for interacting with Pipedrive CRM.
* Enables Claude Code to query, create, and update CRM data.
*
* Usage:
* npx pipedrive-mcp-server
*
* Environment:
* PIPEDRIVE_API_KEY - Your Pipedrive API key (required)
*/
import "dotenv/config";
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 { validateConfig } from "./config.js";
import { toolDefinitions, getToolHandler, getToolSchema } from "./tools/index.js";
import { createErrorResponse, formatErrorForMcp } from "./utils/errors.js";
// Server metadata
const SERVER_NAME = "pipedrive-mcp-server";
const SERVER_VERSION = "1.0.0";
/**
* Main server initialization
*/
async function main() {
// Log to stderr to avoid STDIO protocol corruption
console.error(`[${SERVER_NAME}] Starting server v${SERVER_VERSION}...`);
// Validate configuration early, but don't fail - tools will report errors on use
const configValidation = validateConfig();
if (!configValidation.valid) {
console.error(`[${SERVER_NAME}] Warning: ${configValidation.error}`);
console.error(`[${SERVER_NAME}] Tools will return configuration errors until API key is provided.`);
} else {
console.error(`[${SERVER_NAME}] Configuration validated successfully.`);
}
// Create MCP server
const server = new Server(
{
name: SERVER_NAME,
version: SERVER_VERSION,
},
{
capabilities: {
tools: {},
},
}
);
// Register list tools handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error(`[${SERVER_NAME}] Listing ${toolDefinitions.length} tools`);
return {
tools: toolDefinitions,
};
});
// Register call tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
console.error(`[${SERVER_NAME}] Calling tool: ${name}`);
// Get handler and schema
const handler = getToolHandler(name);
const schema = getToolSchema(name);
if (!handler) {
console.error(`[${SERVER_NAME}] Unknown tool: ${name}`);
return {
content: [{
type: "text",
text: formatErrorForMcp(createErrorResponse(
"VALIDATION_ERROR",
`Unknown tool: ${name}`,
`Available tools: ${toolDefinitions.map(t => t.name).join(", ")}`
)),
}],
isError: true,
};
}
try {
// Validate arguments with Zod schema
let validatedArgs = args || {};
if (schema) {
const parseResult = schema.safeParse(args);
if (!parseResult.success) {
const errors = parseResult.error.errors
.map(e => `${e.path.join(".")}: ${e.message}`)
.join("; ");
console.error(`[${SERVER_NAME}] Validation error: ${errors}`);
return {
content: [{
type: "text",
text: formatErrorForMcp(createErrorResponse(
"VALIDATION_ERROR",
`Invalid arguments: ${errors}`,
"Check the tool's inputSchema for required parameters"
)),
}],
isError: true,
};
}
validatedArgs = parseResult.data;
}
// Execute handler
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = await handler(validatedArgs as any);
return result;
} catch (error) {
console.error(`[${SERVER_NAME}] Error executing ${name}:`, error);
return {
content: [{
type: "text",
text: formatErrorForMcp(createErrorResponse(
"API_ERROR",
error instanceof Error ? error.message : "Unknown error occurred",
"Check your API key and network connection"
)),
}],
isError: true,
};
}
});
// Connect via STDIO transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`[${SERVER_NAME}] Server running on STDIO`);
}
// Run server
main().catch((error) => {
console.error(`[${SERVER_NAME}] Fatal error:`, error);
process.exit(1);
});