/** * Salesforce MCP Server */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { SalesforceClient } from './clients/salesforce.js'; import { z } from 'zod'; export interface SalesforceServerConfig { accessToken: string; instanceUrl: string; apiVersion?: string; } export class SalesforceServer { private server: Server; private client: SalesforceClient; constructor(config: SalesforceServerConfig) { this.server = new Server( { name: 'salesforce-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, } ); this.client = new SalesforceClient({ accessToken: config.accessToken, instanceUrl: config.instanceUrl, apiVersion: config.apiVersion, }); this.setupHandlers(); } /** * Setup MCP handlers */ private setupHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // Foundation tools - additional tools can be added via tool modules later { name: 'salesforce_query', description: 'Execute a SOQL query against Salesforce', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'SOQL query string', }, }, required: ['query'], }, }, { name: 'salesforce_create_record', description: 'Create a new Salesforce record', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: 'Salesforce object type (e.g., Account, Contact)', }, data: { type: 'object', description: 'Record data', }, }, required: ['objectType', 'data'], }, }, { name: 'salesforce_update_record', description: 'Update an existing Salesforce record', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: 'Salesforce object type', }, id: { type: 'string', description: 'Record ID', }, data: { type: 'object', description: 'Fields to update', }, }, required: ['objectType', 'id', 'data'], }, }, { name: 'salesforce_delete_record', description: 'Delete a Salesforce record', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: 'Salesforce object type', }, id: { type: 'string', description: 'Record ID', }, }, required: ['objectType', 'id'], }, }, { name: 'salesforce_describe_object', description: 'Get metadata for a Salesforce object', inputSchema: { type: 'object', properties: { objectType: { type: 'string', description: 'Salesforce object type', }, }, required: ['objectType'], }, }, ], }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { // Route to appropriate handler switch (name) { case 'salesforce_query': return await this.handleQuery(args as { query: string }); case 'salesforce_create_record': return await this.handleCreateRecord( args as { objectType: string; data: Record } ); case 'salesforce_update_record': return await this.handleUpdateRecord( args as { objectType: string; id: string; data: Record } ); case 'salesforce_delete_record': return await this.handleDeleteRecord( args as { objectType: string; id: string } ); case 'salesforce_describe_object': return await this.handleDescribeObject( args as { objectType: string } ); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return this.formatError(error); } }); // List resources (for UI apps) this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'salesforce://dashboard', name: 'Salesforce Dashboard', description: 'Overview of Salesforce data and metrics', mimeType: 'application/json', }, ], }; }); // Read resource this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; if (uri === 'salesforce://dashboard') { const limits = this.client.getApiLimits(); return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify( { apiLimits: limits, instanceUrl: this.client['instanceUrl'], }, null, 2 ), }, ], }; } throw new Error(`Unknown resource: ${uri}`); }); } /** * Handle SOQL query */ private async handleQuery(args: { query: string }) { const result = await this.client.query(args.query); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } /** * Handle create record */ private async handleCreateRecord(args: { objectType: string; data: Record; }) { const result = await this.client.createRecord(args.objectType, args.data); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } /** * Handle update record */ private async handleUpdateRecord(args: { objectType: string; id: string; data: Record; }) { await this.client.updateRecord(args.objectType, args.id, args.data); return { content: [ { type: 'text', text: 'Record updated successfully', }, ], }; } /** * Handle delete record */ private async handleDeleteRecord(args: { objectType: string; id: string }) { await this.client.deleteRecord(args.objectType, args.id); return { content: [ { type: 'text', text: 'Record deleted successfully', }, ], }; } /** * Handle describe object */ private async handleDescribeObject(args: { objectType: string }) { const result = await this.client.describe(args.objectType); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } /** * Format error response */ private formatError(error: unknown) { const message = error instanceof Error ? error.message : 'Unknown error'; return { content: [ { type: 'text', text: JSON.stringify( { error: true, message, }, null, 2 ), }, ], isError: true, }; } /** * Start the server */ public async start(): Promise { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Salesforce MCP Server running on stdio'); } /** * Graceful shutdown */ public async close(): Promise { await this.server.close(); } /** * Get the underlying Server instance */ public getServer(): Server { return this.server; } /** * Get the Salesforce client */ public getClient(): SalesforceClient { return this.client; } }