Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.5.4] - 2025-10-22

### Fixed

- **Salesforce CLI Error Handling**: Fixed `executeSfCommand` to properly parse and return JSON error responses from Salesforce CLI
- When SF CLI commands fail with `--json` flag, errors are now properly captured from stdout
- Error details (name, message, exitCode, context, stack) are now correctly returned to MCP clients
- Fixes issue where deployment errors like "No source-backed components present in the package" were not visible
- Maintains backward compatibility with existing error handling

## [1.5.3] - 2025-09-20

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"dxt_version": "0.2",
"name": "salesforce-mcp-server",
"display_name": "Salesforce MCP Server",
"version": "1.5.3",
"version": "1.5.4",
"description": "Salesforce MCP Server - Interact with Salesforce orgs through AI assistants",
"icon": "icon.png",
"long_description": "Enables AI assistants to execute Apex code, query Salesforce data, and manage org metadata using your existing Salesforce CLI authentication. Perfect for developers and administrators who want to automate Salesforce tasks through natural language interactions.\n\nSupports environment variables:\n- READ_ONLY=true - Prevents Apex code execution\n- ALLOWED_ORGS=ALL or comma-separated org list - Restricts access to specific orgs (default: ALL)",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@advanced-communities/salesforce-mcp-server",
"version": "1.5.3",
"version": "1.5.4",
"description": "MCP server enabling AI assistants to interact with Salesforce orgs through the Salesforce CLI",
"main": "./src/index.ts",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function buildServerDescription(): string {

const server = new McpServer({
name: "salesforce-mcp-server",
version: "1.5.0",
version: "1.5.4",
description: buildServerDescription(),
capabilities: {
tools: {},
Expand Down
116 changes: 76 additions & 40 deletions src/utils/sfCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ function findSfPath(): string {
}

const currentPlatform = platform();
const pathsToCheck = COMMON_SF_PATHS[currentPlatform as keyof typeof COMMON_SF_PATHS] || COMMON_SF_PATHS.linux;
const pathsToCheck =
COMMON_SF_PATHS[currentPlatform as keyof typeof COMMON_SF_PATHS] ||
COMMON_SF_PATHS.linux;

for (const path of pathsToCheck) {
if (path && existsSync(path)) {
Expand All @@ -51,29 +53,50 @@ export function executeSfCommand(command: string): Promise<any> {
const fullCommand = command.replace(/^sf\s+/, `"${sfPath}" `);

return new Promise((resolve, reject) => {
exec(fullCommand, { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
if (error) {
if (error.message.includes("command not found") || error.message.includes("is not recognized")) {
reject(new Error(
"Salesforce CLI (sf) not found. Please ensure it is installed and accessible. " +
"Visit https://developer.salesforce.com/tools/salesforcecli for installation instructions."
));
} else {
exec(
fullCommand,
{ maxBuffer: 50 * 1024 * 1024 },
(error, stdout, stderr) => {
if (error) {
if (
error.message.includes("command not found") ||
error.message.includes("is not recognized")
) {
reject(
new Error(
"Salesforce CLI (sf) not found. Please ensure it is installed and accessible. " +
"Visit https://developer.salesforce.com/tools/salesforcecli for installation instructions."
)
);
return;
}
// When --json flag is used, SF CLI returns errors as JSON in stdout
// Try to parse stdout as JSON error response
if (stdout && stdout.trim().length > 0) {
try {
const result = JSON.parse(stdout);
// If it's a valid JSON response (error or success), return it
resolve(result);
return;
} catch (parseError) {
// If JSON parsing fails, fall through to reject with original error
}
}
reject(error);
return;
}
if (stderr && !stderr.includes("Warning")) {
reject(new Error(stderr));
return;
}
try {
const result = JSON.parse(stdout);
resolve(result);
} catch (parseError) {
reject(parseError);
}
return;
}
if (stderr && !stderr.includes("Warning")) {
reject(new Error(stderr));
return;
}
try {
const result = JSON.parse(stdout);
resolve(result);
} catch (parseError) {
reject(parseError);
}
});
);
});
}

Expand All @@ -82,26 +105,39 @@ export function executeSfCommandRaw(command: string): Promise<string> {
const fullCommand = command.replace(/^sf\s+/, `"${sfPath}" `);

return new Promise((resolve, reject) => {
exec(fullCommand, { maxBuffer: 50 * 1024 * 1024 }, (error, stdout, stderr) => {
if (error) {
if (error.message.includes("command not found") || error.message.includes("is not recognized")) {
reject(new Error(
"Salesforce CLI (sf) not found. Please ensure it is installed and accessible. " +
"Visit https://developer.salesforce.com/tools/salesforcecli for installation instructions."
));
} else {
// For scanner commands, non-zero exit code with stdout means violations were found
// We should still return the output in this case
if (stdout && (command.includes("scanner") || command.includes("code-analyzer"))) {
resolve(stdout);
return;
exec(
fullCommand,
{ maxBuffer: 50 * 1024 * 1024 },
(error, stdout, stderr) => {
if (error) {
if (
error.message.includes("command not found") ||
error.message.includes("is not recognized")
) {
reject(
new Error(
"Salesforce CLI (sf) not found. Please ensure it is installed and accessible. " +
"Visit https://developer.salesforce.com/tools/salesforcecli for installation instructions."
)
);
} else {
// For scanner commands, non-zero exit code with stdout means violations were found
// We should still return the output in this case
if (
stdout &&
(command.includes("scanner") ||
command.includes("code-analyzer"))
) {
resolve(stdout);
return;
}
reject(error);
}
reject(error);
return;
}
return;
// Return raw stdout without JSON parsing
resolve(stdout);
}
// Return raw stdout without JSON parsing
resolve(stdout);
});
);
});
}
}