Skip to main content
Glama
server.ts7.22 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import jsforce from "jsforce"; import { z } from "zod"; const conn = new jsforce.Connection(); await conn.login( process.env.SALESFORCE_USERNAME!, process.env.SALESFORCE_PASSWORD!, ); export const server = new McpServer({ name: "Salesforce MCP", version: "0.1.0", capabilities: { resources: {}, tools: {}, logging: {}, }, }); server.tool( "update-records", "Update records", { records: z.array( z .object({ Id: z.string().describe("Record ID"), }) .passthrough() .describe("Record containing the ID and any other fielts to update."), ), sobject: z.string().describe("SObject Name"), }, async ({ records, sobject }) => { const updatedRecords = await conn.sobject(sobject).update(records as any); return { content: [ { type: "text", text: `Result of update(s):\n\n${JSON.stringify( updatedRecords, null, " ", )}`, }, ], }; }, ); server.tool( "retrieve", "Retrieve a record by Salesforce ID", { id: z.string().describe("Record ID"), sobject: z.string().describe("SObject Name"), }, async ({ id, sobject }) => { let data; try { data = await conn.sobject(sobject).retrieve(id); } catch (e) { return { content: [ { type: "text", text: "Failed to retrieve record", }, ], }; } return { content: [ { type: "text", text: `One record was retrieved:\n\n${JSON.stringify( data, null, " ", )}`, }, ], }; }, ); server.tool( "anonymous-apex", "Execute Anonymous Apex", { apexBody: z.string().describe("Anonymous Apex Body"), }, async ({ apexBody }) => { const res = await conn.tooling.executeAnonymous(apexBody); console.log(`compiled?: ${res.compiled}`); // compiled successfully console.log(`executed?: ${res.success}`); // executed successfully return { content: [ { type: "text", text: `compiled?: ${res.compiled}, executed?: ${ res.success }, lines:\n\n${JSON.stringify(res, null, " ")}`, }, ], }; }, ); server.tool( "triggers-by-sobject", "Get Apex Triggers by SObject", { sobject: z.string().describe("SObject Name"), }, async ({ sobject }) => { console.log("Get Apex Triggers by SObject"); const records = await conn.tooling .sobject("ApexTrigger") .find({ TableEnumOrId: sobject }) .execute(); console.log("Getting Triggers", sobject, records); return { content: [ { type: "text", text: `${ records.length } Apex Triggers were found on ${sobject}:\n\n${JSON.stringify( records, null, " ", )}`, }, ], }; }, ); server.tool( "create-custom-field", "Create a Custom Field", { customField: z.object({ Metadata: z.object({ type: z .enum([ "Address", "AutoNumber", "Lookup", "MasterDetail", "MetadataRelationship", "Checkbox", "Currency", "Date", "DateTime", "Email", "EncryptedText", "Note", "ExternalLookup", "IndirectLookup", "Number1", "Percent", "Phone", "Picklist", "MultiselectPicklist", "Summary", "Text", "TextArea", "LongTextArea", "Url", "Hierarchy", "File", "Html", "Location", "Time", ]) .describe("field type"), description: z.string().describe("the description of the field"), inlineHelpText: z .string() .optional() .describe("the inline text of the field"), label: z.string().describe("the label of the field"), length: z.number().optional().describe("the field length of the field"), required: z.boolean().describe("if it is required or not"), }), FullName: z .string() .describe( "the full name of the field, which is formatted as `sobjectName.fieldname__c`. For example, `Account.Name__c` or `My_Custom_Object__c.First_Name__c`.", ), }), }, async ({ customField }) => { const savedResult = await conn.tooling .sobject("CustomField") .create(customField); return { content: [ { type: "text", text: `Result:\n\n${JSON.stringify(savedResult, null, " ")}`, }, ], }; }, ); server.tool( "count", "Count Salesforce records", { sobject: z.string().describe("SObject Name"), }, async ({ sobject }) => { const data = await conn.query(`select COUNT() from ${sobject}`); if (!data.done) { return { content: [ { type: "text", text: "Failed to count records", }, ], }; } return { content: [ { type: "text", text: `Count for SObject ${sobject}: ${data.totalSize}`, }, ], }; }, ); server.tool( "query-records", "Search Salesforce records", { sobject: z.string().describe("SObject Name"), list_of_fields: z .string() .describe("Comma separated list of fields") .default("Id, Name"), limit: z.number().describe("Limit of records").default(10), }, async ({ sobject, list_of_fields, limit }) => { const data = await conn.query( `select ${list_of_fields} from ${sobject} limit ${limit}`, ); if (!data.done) { return { content: [ { type: "text", text: "Failed to retrieve records", }, ], }; } const formattedResults = data.records.map((record) => { return Object.entries(record) .filter(([_, value]) => typeof value !== "object") .map(([field, value]) => field + ": " + value || "Unknown") .join("\n"); }); const text = `Records found for the ${sobject} SObject:\n\n${formattedResults.join( "---\n", )}`; return { content: [ { type: "text", text: JSON.stringify(data, null, " "), }, ], }; }, ); server.tool( "soql-query", "Execute a SOQL query", { soqlQueryString: z.string().describe("SOQL query string"), }, async ({ soqlQueryString }) => { const data = await conn.query(soqlQueryString); if (!data.done) { return { content: [ { type: "text", text: "Failed to retrieve records", }, ], }; } return { content: [ { type: "text", text: JSON.stringify(data, null, " "), }, ], }; }, );

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jpmonette/salesforce-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server