#!/usr/bin/env node
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 { createSalesforceClient, SalesforceClient } from './salesforce.js';
import 'dotenv/config';
let sfClient: SalesforceClient;
const server = new Server(
{
name: 'salesforce-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'query',
description: 'Execute a SOQL query against Salesforce. Returns matching records.',
inputSchema: {
type: 'object',
properties: {
soql: {
type: 'string',
description: 'The SOQL query to execute (e.g., "SELECT Id, Name FROM Account LIMIT 10")'
}
},
required: ['soql']
}
},
{
name: 'describe_object',
description: 'Get metadata about a Salesforce object including its fields, types, and relationships.',
inputSchema: {
type: 'object',
properties: {
object_name: {
type: 'string',
description: 'API name of the Salesforce object (e.g., "Account", "Contact", "Opportunity")'
}
},
required: ['object_name']
}
},
{
name: 'create',
description: 'Create a new record in Salesforce.',
inputSchema: {
type: 'object',
properties: {
object_name: {
type: 'string',
description: 'API name of the Salesforce object'
},
data: {
type: 'object',
description: 'Field values for the new record'
}
},
required: ['object_name', 'data']
}
},
{
name: 'update',
description: 'Update an existing record in Salesforce.',
inputSchema: {
type: 'object',
properties: {
object_name: {
type: 'string',
description: 'API name of the Salesforce object'
},
data: {
type: 'object',
description: 'Field values to update (must include Id field)',
properties: {
Id: { type: 'string' }
},
required: ['Id']
}
},
required: ['object_name', 'data']
}
},
{
name: 'delete',
description: 'Delete a record from Salesforce.',
inputSchema: {
type: 'object',
properties: {
object_name: {
type: 'string',
description: 'API name of the Salesforce object'
},
id: {
type: 'string',
description: 'Salesforce record ID to delete'
}
},
required: ['object_name', 'id']
}
}
]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (!sfClient) {
sfClient = createSalesforceClient();
}
switch (name) {
case 'query': {
const { soql } = args as { soql: string };
const result = await sfClient.query(soql);
return {
content: [
{
type: 'text',
text: JSON.stringify({
totalSize: result.totalSize,
done: result.done,
records: result.records
}, null, 2)
}
]
};
}
case 'describe_object': {
const { object_name } = args as { object_name: string };
const result = await sfClient.describeObject(object_name);
return {
content: [
{
type: 'text',
text: JSON.stringify({
name: result.name,
label: result.label,
fields: result.fields.map(f => ({
name: f.name,
label: f.label,
type: f.type,
required: !f.nillable,
updateable: f.updateable,
createable: f.createable
}))
}, null, 2)
}
]
};
}
case 'create': {
const { object_name, data } = args as { object_name: string; data: Record<string, unknown> };
const result = await sfClient.create(object_name, data);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
case 'update': {
const { object_name, data } = args as { object_name: string; data: Record<string, unknown> };
const result = await sfClient.update(object_name, data);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
case 'delete': {
const { object_name, id } = args as { object_name: string; id: string };
const result = await sfClient.delete(object_name, id);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: 'text',
text: `Error: ${errorMessage}`
}
],
isError: true
};
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Salesforce MCP server running on stdio');
}
main().catch(console.error);