UNPKG

@advanced-communities/salesforce-mcp-server

Version:

MCP server enabling AI assistants to interact with Salesforce orgs through the Salesforce CLI

143 lines (142 loc) 6.78 kB
import { z } from "zod"; import { executeSfCommand } from "../utils/sfCommand.js"; import { permissions } from "../config/permissions.js"; const executeSoqlQuery = async (targetOrg, sObject, selectClause, where, limit, orderBy) => { let query = `SELECT ${selectClause} FROM ${sObject}`; if (where) query += " WHERE " + where; if (orderBy) query += " ORDER BY " + orderBy; if (limit) query += " LIMIT " + limit; const sfCommand = `sf data query --target-org ${targetOrg} --query "${query}" --json`; try { const result = await executeSfCommand(sfCommand); return result.result.records || []; } catch (error) { throw error; } }; const executeSoqlQueryToFile = async (targetOrg, sObject, selectClause, where, outputFileName, outputFileFormat = "csv", orderBy) => { let query = `SELECT ${selectClause} FROM ${sObject}`; if (where) query += " WHERE " + where; if (orderBy) query += " ORDER BY " + orderBy; const sfCommand = `sf data export bulk --query "${query}" --target-org ${targetOrg} --output-file "${outputFileName || "output"}" --result-format ${outputFileFormat} --json -w 30`; try { const result = await executeSfCommand(sfCommand); return result.result; } catch (error) { throw error; } }; export const registerQueryTools = (server) => { server.tool("query_records", "Query records from a SINGLE Salesforce object using structured field conditions. Use this for precise queries on ONE object with specific field criteria (e.g., Status = 'Open', Amount > 1000). NOT for text searches across multiple objects - use search_records for that. This executes SOQL queries with SELECT, WHERE, ORDER BY, and LIMIT clauses on a single SObject. The results are returned in JSON format. IMPORTANT: Always execute the `sobject_list` tool first to understand which objects are available in the org, and optionally execute `sobject_describe` for the specific SObject to understand its fields and structure before querying.", { input: z.object({ targetOrg: z .string() .describe("Target Salesforce Org to execute the query against"), sObject: z .string() .describe("Salesforce SObject to query from"), selectClause: z .string() .describe("SELECT clause content - can include fields, functions (COUNT, SUM, AVG, etc.), expressions, and aliases"), where: z .string() .optional() .describe("Optional WHERE clause for the query"), limit: z .number() .optional() .describe("Optional limit for the number of records returned"), orderBy: z .string() .optional() .describe("Optional ORDER BY clause for sorting results (e.g., 'Name ASC', 'CreatedDate DESC')"), }), }, async ({ input }) => { const { targetOrg, sObject, selectClause, where, limit, orderBy } = input; // Check org permissions if (!permissions.isOrgAllowed(targetOrg)) { return { content: [ { type: "text", text: JSON.stringify({ success: false, message: `Access denied: Org '${targetOrg}' is not in the allowed list`, }), }, ], }; } const result = await executeSoqlQuery(targetOrg, sObject, selectClause, where, limit, orderBy); return { content: [ { type: "text", text: JSON.stringify(result), }, ], }; }); server.tool("query_records_to_file", "Query records from a Salesforce SObject and save to a file. This command allows you to execute a SOQL query against a specified Salesforce SObject in a given Org and save the results to a file. You can specify the SELECT clause (fields, functions like COUNT(), aggregations, etc.), an optional WHERE clause, and save the results in various formats. The results can be saved in CSV format by default, or in other formats if specified. IMPORTANT: Always execute the `sobject_list` tool first to understand which objects are available in the org, and optionally execute `sobject_describe` for the specific SObject to understand its fields and structure before querying.", { input: z.object({ targetOrg: z .string() .describe("Target Salesforce Org to execute the query against"), sObject: z .string() .describe("Salesforce SObject to query from"), selectClause: z .string() .describe("SELECT clause content - can include fields, functions (COUNT, SUM, AVG, etc.), expressions, and aliases"), where: z .string() .optional() .describe("Optional WHERE clause for the query"), outputFileName: z .string() .optional() .describe("Optional output file name to save the results"), outputFileFormat: z .enum(["csv", "json"]) .optional() .default("csv") .describe("Optional output file format to save the results, default is csv"), orderBy: z .string() .optional() .describe("Optional ORDER BY clause for sorting results (e.g., 'Name ASC', 'CreatedDate DESC')"), }), }, async ({ input }) => { const { targetOrg, sObject, selectClause, where, outputFileName, outputFileFormat, orderBy, } = input; // Check org permissions if (!permissions.isOrgAllowed(targetOrg)) { return { content: [ { type: "text", text: JSON.stringify({ success: false, message: `Access denied: Org '${targetOrg}' is not in the allowed list`, }), }, ], }; } const result = await executeSoqlQueryToFile(targetOrg, sObject, selectClause, where, outputFileName, outputFileFormat, orderBy); return { content: [ { type: "text", text: JSON.stringify(result), }, ], }; }); };