Skip to content

Commit 00237e1

Browse files
authored
Merge pull request #2 from advancedcommunities/add-project-tools
Add project tools
2 parents 8759166 + 7f3be70 commit 00237e1

File tree

7 files changed

+214
-7
lines changed

7 files changed

+214
-7
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.4.0] - 2025-08-31
9+
10+
### Added
11+
12+
- **Project Deployment Tool**: New tool for deploying metadata to Salesforce orgs
13+
- `deploy_start` - Deploy metadata components to target org with extensive configuration options
14+
- Supports multiple deployment sources: manifest (package.xml), metadata components, source directories
15+
- Configurable test execution levels (NoTestRun, RunSpecifiedTests, RunLocalTests, RunAllTestsInOrg)
16+
- Dry-run capability for validation without actual deployment
17+
- Support for single package deployments
18+
- Respects READ_ONLY and ALLOWED_ORGS permissions
19+
- Integrated with existing permission system for secure deployments
20+
821
## [1.3.2] - 2025-08-31
922

1023
### Added

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,5 @@ The project supports Desktop Extension (.dxt) packaging for one-click installati
136136
- Check that new tools are registered in both the tool file and manifest.json
137137
- Verify permission checks are implemented for destructive operations
138138

139-
- don't forget to add shebang to the build file after creating a new build and before publishing it. it's important
139+
- don't forget to add shebang to the build file after creating a new build and before publishing it. it's important
140+
- when updating documentation, update the @manifest.json as well

manifest.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"dxt_version": "0.2",
33
"name": "salesforce-mcp-server",
44
"display_name": "Salesforce MCP Server",
5-
"version": "1.3.0",
5+
"version": "1.4.0",
66
"description": "Salesforce MCP Server - Interact with Salesforce orgs through AI assistants",
77
"icon": "icon.png",
88
"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)",
@@ -187,6 +187,14 @@
187187
{
188188
"name": "delete_record",
189189
"description": "Delete a record from a Salesforce org using REST API"
190+
},
191+
{
192+
"name": "generate_component",
193+
"description": "Generate Lightning Web Components (LWC) and Aura components with configurable templates"
194+
},
195+
{
196+
"name": "deploy_start",
197+
"description": "Deploy metadata components to a Salesforce org with test execution options"
190198
}
191199
],
192200
"compatibility": {

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@advanced-communities/salesforce-mcp-server",
3-
"version": "1.3.2",
3+
"version": "1.4.0",
44
"description": "MCP server enabling AI assistants to interact with Salesforce orgs through the Salesforce CLI",
55
"main": "./src/index.ts",
66
"scripts": {

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { registerPackageTools } from "./tools/package.js";
1313
import { registerSchemaTools } from "./tools/schema.js";
1414
import { registerSearchTools } from "./tools/search.js";
1515
import { registerLightningTools } from "./tools/lightning.js";
16+
import { registerProjectTools } from "./tools/project.js";
1617
import { permissions } from "./config/permissions.js";
1718

1819
/**
@@ -24,7 +25,7 @@ function buildServerDescription(): string {
2425
const allowedOrgs = permissions.getAllowedOrgs();
2526
const permissionInfo = [];
2627

27-
let description = `Salesforce MCP Server v1.3.2 - AI-powered Salesforce automation via CLI integration\n`;
28+
let description = `Salesforce MCP Server v1.4.0 - AI-powered Salesforce automation via CLI integration\n`;
2829
description += `Capabilities: Apex execution, SOQL queries, org management, code testing & coverage\n`;
2930

3031
if (readOnlyMode) {
@@ -41,7 +42,7 @@ function buildServerDescription(): string {
4142
description += `Security: Full access enabled for all authenticated orgs`;
4243
}
4344

44-
description += `\nTools: 29 available (apex, query, search, sobject, org management, records, admin, code analyzer, scanner, package, schema)`;
45+
description += `\nTools: 38 available (apex, query, search, sobject, org management, records, admin, code analyzer, scanner, package, schema, lightning, project deployment)`;
4546

4647
return description;
4748
}
@@ -67,6 +68,7 @@ registerPackageTools(server);
6768
registerSchemaTools(server);
6869
registerSearchTools(server);
6970
registerLightningTools(server);
71+
registerProjectTools(server);
7072

7173
async function main() {
7274
const transport = new StdioServerTransport();

src/tools/project.ts

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { z } from "zod";
2+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3+
import { permissions } from "../config/permissions.js";
4+
import { executeSfCommand, executeSfCommandRaw } from "../utils/sfCommand.js";
5+
6+
const deployStart = async (
7+
targetOrg: string,
8+
dryRun: boolean,
9+
manifest: string,
10+
metadata: string,
11+
metadataDirectory: string,
12+
singlePackage: boolean,
13+
sourceDirectory: string,
14+
tests: string,
15+
testLevel: string
16+
) => {
17+
let sfCommand = `sf project deploy start --target-org ${targetOrg} --json `;
18+
19+
if (dryRun) {
20+
sfCommand += `--dry-run `;
21+
}
22+
23+
if (manifest && manifest.length > 0) {
24+
sfCommand += `--manifest ${manifest} `;
25+
}
26+
27+
if (metadata && metadata.length > 0) {
28+
sfCommand += `--metadata ${metadata} `;
29+
}
30+
31+
if (metadataDirectory && metadataDirectory.length > 0) {
32+
sfCommand += `--metadata-dir ${metadataDirectory} `;
33+
}
34+
35+
if (singlePackage) {
36+
sfCommand += `--single-package `;
37+
}
38+
39+
if (sourceDirectory && sourceDirectory.length > 0) {
40+
sfCommand += `--source-dir ${sourceDirectory} `;
41+
}
42+
43+
if (tests && tests.length > 0) {
44+
sfCommand += `--tests ${tests} `;
45+
}
46+
47+
if (testLevel && testLevel.length > 0) {
48+
sfCommand += `--test-level ${testLevel} `;
49+
}
50+
51+
try {
52+
const result = await executeSfCommand(sfCommand);
53+
return result;
54+
} catch (error) {
55+
throw error;
56+
}
57+
};
58+
59+
export const registerProjectTools = (server: McpServer) => {
60+
server.tool(
61+
"deploy_start",
62+
"",
63+
{
64+
input: z.object({
65+
targetOrg: z
66+
.string()
67+
.describe(
68+
"Username or alias of the target org. Not required if the 'target-org' configuration variable is already set."
69+
),
70+
dryRun: z
71+
.boolean()
72+
.describe(
73+
"Validate deploy and run Apex tests but don't save to the org."
74+
),
75+
manifest: z
76+
.string()
77+
.optional()
78+
.describe(
79+
"Full file path for manifest (package.xml) of components to deploy. All child components are included. If you specify this flag, don’t specify --metadata or --source-dir."
80+
),
81+
metadata: z
82+
.string()
83+
.optional()
84+
.describe(
85+
"Metadata component names to deploy. Wildcards (`*` ) supported as long as you use quotes, such as `ApexClass:MyClass*`."
86+
),
87+
metadataDirectory: z
88+
.string()
89+
.optional()
90+
.describe(
91+
"Root of directory or zip file of metadata formatted files to deploy."
92+
),
93+
singlePackage: z
94+
.boolean()
95+
.optional()
96+
.describe(
97+
"Indicates that the metadata zip file points to a directory structure for a single package."
98+
),
99+
sourceDirectory: z
100+
.string()
101+
.optional()
102+
.describe(
103+
"Path to the local source files to deploy. The supplied path can be to a single file (in which case the operation is applied to only one file) or to a folder (in which case the operation is applied to all metadata types in the directory and its subdirectories). If you specify this flag, don’t specify --metadata or --manifest."
104+
),
105+
tests: z
106+
.string()
107+
.optional()
108+
.describe(
109+
'Apex tests to run when --test-level is RunSpecifiedTests. If a test name contains a space, enclose it in double quotes. For multiple test names, use one of the following formats: - Repeat the flag for multiple test names: --tests Test1 --tests Test2 --tests "Test With Space" - Separate the test names with spaces: --tests Test1 Test2 "Test With Space"'
110+
),
111+
testLevel: z
112+
.string()
113+
.optional()
114+
.describe(
115+
"Deployment Apex testing level. Valid values are: - NoTestRun — No tests are run. This test level applies only to deployments to development environments, such as sandbox, Developer Edition, or trial orgs. This test level is the default for development environments. - RunSpecifiedTests — Runs only the tests that you specify with the --tests flag. Code coverage requirements differ from the default coverage requirements when using this test level. Executed tests must comprise a minimum of 75% code coverage for each class and trigger in the deployment package. This coverage is computed for each class and trigger individually and is different than the overall coverage percentage. - RunLocalTests — All tests in your org are run, except the ones that originate from installed managed and unlocked packages. This test level is the default for production deployments that include Apex classes or triggers. - RunAllTestsInOrg — All tests in your org are run, including tests of managed packages.If you don’t specify a test level, the default behavior depends on the contents of your deployment package and target org."
116+
),
117+
}),
118+
},
119+
async ({ input }) => {
120+
const {
121+
targetOrg,
122+
dryRun,
123+
manifest,
124+
metadata,
125+
metadataDirectory,
126+
singlePackage,
127+
sourceDirectory,
128+
tests,
129+
testLevel,
130+
} = input;
131+
132+
if (permissions.isReadOnly()) {
133+
return {
134+
content: [
135+
{
136+
type: "text",
137+
text: JSON.stringify({
138+
success: false,
139+
compiled: false,
140+
compileProblem:
141+
"Operation not allowed: Cannot generate components in READ_ONLY mode",
142+
}),
143+
},
144+
],
145+
};
146+
}
147+
148+
if (!permissions.isOrgAllowed(targetOrg)) {
149+
return {
150+
content: [
151+
{
152+
type: "text",
153+
text: JSON.stringify({
154+
success: false,
155+
message: `Access denied: Org '${targetOrg}' is not in the allowed list`,
156+
}),
157+
},
158+
],
159+
};
160+
}
161+
162+
const result = await deployStart(
163+
targetOrg,
164+
dryRun,
165+
manifest || "",
166+
metadata || "",
167+
metadataDirectory || "",
168+
singlePackage || false,
169+
sourceDirectory || "",
170+
tests || "",
171+
testLevel || ""
172+
);
173+
return {
174+
content: [
175+
{
176+
type: "text",
177+
text: JSON.stringify(result),
178+
},
179+
],
180+
};
181+
}
182+
);
183+
};

0 commit comments

Comments
 (0)