UNPKG

@advanced-communities/salesforce-mcp-server

Version:

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

181 lines (180 loc) 6.85 kB
import { z } from "zod"; import { executeSfCommandRaw } from "../utils/sfCommand.js"; import { permissions } from "../config/permissions.js"; const runCodeAnalyzer = async (workspace, target, outputFile, ruleSelector, severity, configFile) => { let command = "sf code-analyzer run"; if (workspace && workspace.length > 0) { workspace.forEach((w) => { command += ` --workspace "${w}"`; }); } if (target && target.length > 0) { target.forEach((t) => { command += ` --target "${t}"`; }); } if (outputFile) { command += ` --output-file "${outputFile}"`; } if (ruleSelector && ruleSelector.length > 0) { ruleSelector.forEach((rs) => { command += ` --rule-selector "${rs}"`; }); } if (severity) { command += ` --severity-threshold ${severity}`; } if (configFile) { command += ` --config-file "${configFile}"`; } const result = await executeSfCommandRaw(command); return result; }; const listCodeAnalyzerRules = async (workspace, target, configFile, ruleSelector, view) => { let command = "sf code-analyzer rules"; if (workspace && workspace.length > 0) { workspace.forEach((w) => { command += ` --workspace "${w}"`; }); } if (target && target.length > 0) { target.forEach((t) => { command += ` --target "${t}"`; }); } if (configFile) { command += ` --config-file "${configFile}"`; } if (ruleSelector && ruleSelector.length > 0) { ruleSelector.forEach((rs) => { command += ` --rule-selector "${rs}"`; }); } if (view) { command += ` --view ${view}`; } const result = await executeSfCommandRaw(command); return result; }; export const registerCodeAnalyzerTools = (server) => { server.tool("run_code_analyzer", "Analyze code for quality and security issues. Run list_code_analyzer_rules first to select appropriate rules for ruleSelector parameter.", { input: z.object({ workspace: z .array(z.string()) .optional() .describe("Files/folders to analyze. Supports glob patterns. Multiple values are summed. Defaults to current folder '.'"), target: z .array(z.string()) .optional() .describe("Specific files/folders to target within workspace. Supports glob patterns. Defaults to all workspace files."), ruleSelector: z .array(z.string()) .describe('Select rules by engine, severity, name, or tag. Use colons to combine (e.g., "eslint:Security:3"). Multiple selectors create union. Run with "all" to see available values.'), outputFile: z .string() .optional() .describe("Save output to file. Format auto-detected from extension or defaults to JSON."), severity: z .enum(["High", "Medium", "Low"]) .optional() .describe("Exit with error on violations at/above this severity. Default: Low"), configFile: z .string() .optional() .describe("Config file to customize rules/engines. Auto-detects code-analyzer.yml/yaml in current folder. Use 'code-analyzer config' to create."), }), }, async ({ input }) => { const { workspace, target, outputFile, ruleSelector, severity, configFile, } = input; if (permissions.isReadOnly()) { return { content: [ { type: "text", text: JSON.stringify({ success: false, message: "Code analysis is disabled in read-only mode", }), }, ], }; } try { const result = await runCodeAnalyzer(workspace, target, outputFile, ruleSelector, severity, configFile); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ success: false, message: error.message || "Failed to run code analyzer", error: error, }), }, ], }; } }); server.tool("list_code_analyzer_rules", "List available code analysis rules with details. Use to determine rules for code-analyzer run command.", { input: z.object({ workspace: z .array(z.string()) .optional() .describe("Files/folders for rule discovery. Supports glob patterns."), target: z .array(z.string()) .optional() .describe("Specific files/folders to target within workspace. Supports glob patterns. Defaults to all workspace files."), configFile: z .string() .optional() .describe("Config file for rule discovery."), ruleSelector: z .array(z.string()) .optional() .describe("Filter rules by name, tag, category, or engine."), view: z .enum(["detail", "table"]) .optional() .describe("Display format: 'table' (concise) or 'detail' (full info). Default: 'table'."), }), }, async ({ input }) => { const { workspace, target, configFile, ruleSelector, view } = input; try { const result = await listCodeAnalyzerRules(workspace, target, configFile, ruleSelector, view); return { content: [ { type: "text", text: result, }, ], }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ success: false, message: error.message || "Failed to list code analyzer rules", error: error, }), }, ], }; } }); };