@salesforce/mcp 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -210,6 +210,9 @@ You can also use special values to control access to orgs:
210
210
  this.logToStderr('Registering experimental tools');
211
211
  orgs.registerToolOrgOpen(server);
212
212
  // Add any experimental tools here
213
+ orgs.registerToolCreateScratchOrg(server);
214
+ orgs.registerToolDeleteOrg(server);
215
+ orgs.registerToolCreateOrgSnapshot(server);
213
216
  }
214
217
  const transport = new StdioServerTransport();
215
218
  await server.connect(transport);
@@ -16,7 +16,7 @@
16
16
  import { z } from 'zod';
17
17
  import { sanitizePath } from './utils.js';
18
18
  /*
19
- * A collection of reuseable Tool parameters
19
+ * A collection of reusable Tool parameters
20
20
  */
21
21
  export const usernameOrAliasParam = z.string()
22
22
  .describe(`The username or alias for the Salesforce org to run this tool against.
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { z } from 'zod';
17
17
  import { AgentTester } from '@salesforce/agents';
18
- import { validateSalesforceId, scratchOrgResume } from '@salesforce/core';
18
+ import { validateSalesforceId, scratchOrgResume, PollingClient } from '@salesforce/core';
19
19
  import { Duration } from '@salesforce/kit';
20
20
  import { MetadataApiDeploy } from '@salesforce/source-deploy-retrieve';
21
21
  import { textResponse } from '../../shared/utils.js';
@@ -25,6 +25,7 @@ const resumableIdPrefixes = new Map([
25
25
  ['deploy', '0Af'],
26
26
  ['scratchOrg', '2SR'],
27
27
  ['agentTest', '4KB'],
28
+ ['orgSnapshot', '0Oo'],
28
29
  ]);
29
30
  /*
30
31
  * Resume a long running operation that was not completed by another tool.
@@ -63,6 +64,8 @@ Resume the deployment to my org
63
64
  Resume scratch org creation
64
65
  Resume job 2SR1234567890
65
66
  Resume agent tests
67
+ Resume org snapshot with ID 0OoKa000000XZAbKAO
68
+ Report on my org snapshot
66
69
  `, resumeParamsSchema.shape, {
67
70
  title: 'Resume',
68
71
  openWorldHint: false,
@@ -84,6 +87,8 @@ Resume agent tests
84
87
  return resumeScratchOrg(jobId, wait);
85
88
  case resumableIdPrefixes.get('agentTest'):
86
89
  return resumeAgentTest(connection, jobId, wait);
90
+ case resumableIdPrefixes.get('orgSnapshot'):
91
+ return resumeOrgSnapshot(connection, jobId, wait);
87
92
  default:
88
93
  return textResponse(`The job id: ${jobId} is not resumeable.`, true);
89
94
  }
@@ -99,6 +104,29 @@ async function resumeDeployment(connection, jobId, wait) {
99
104
  return textResponse(`Resumed deployment failed: ${error instanceof Error ? error.message : 'Unknown error'}`, true);
100
105
  }
101
106
  }
107
+ async function resumeOrgSnapshot(connection, jobId, wait) {
108
+ try {
109
+ const poller = await PollingClient.create({
110
+ timeout: Duration.minutes(wait),
111
+ frequency: Duration.seconds(30),
112
+ poll: async () => {
113
+ const queryResult = await connection.singleRecordQuery(`SELECT Status, Id, SnapshotName, Description, ExpirationDate, CreatedDate FROM OrgSnapshot WHERE Id = '${jobId}'`);
114
+ if (queryResult.Status !== 'In Progress') {
115
+ // either done or error
116
+ return { completed: true, payload: queryResult };
117
+ }
118
+ else {
119
+ return { completed: false };
120
+ }
121
+ },
122
+ });
123
+ const result = await poller.subscribe();
124
+ return textResponse(`Org snapshot: ${JSON.stringify(result)}`);
125
+ }
126
+ catch (error) {
127
+ return textResponse(`Resumed org snapshot failed: ${error instanceof Error ? error.message : 'Unknown error'}`, true);
128
+ }
129
+ }
102
130
  async function resumeScratchOrg(jobId, wait) {
103
131
  try {
104
132
  const result = await scratchOrgResume(jobId, Duration.minutes(wait));
@@ -1,2 +1,5 @@
1
1
  export * from './sf-list-all-orgs.js';
2
+ export * from './sf-delete-org.js';
3
+ export * from './sf-create-scratch-org.js';
4
+ export * from './sf-create-org-snapshot.js';
2
5
  export * from './sf-org-open.js';
@@ -14,5 +14,8 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  export * from './sf-list-all-orgs.js';
17
+ export * from './sf-delete-org.js';
18
+ export * from './sf-create-scratch-org.js';
19
+ export * from './sf-create-org-snapshot.js';
17
20
  export * from './sf-org-open.js';
18
21
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,23 @@
1
+ import { z } from 'zod';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare const createOrgSnapshotParams: z.ZodObject<{
4
+ directory: z.ZodEffects<z.ZodString, string, string>;
5
+ devHub: z.ZodString;
6
+ sourceOrg: z.ZodString;
7
+ description: z.ZodOptional<z.ZodString>;
8
+ name: z.ZodDefault<z.ZodString>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ name: string;
11
+ directory: string;
12
+ devHub: string;
13
+ sourceOrg: string;
14
+ description?: string | undefined;
15
+ }, {
16
+ directory: string;
17
+ devHub: string;
18
+ sourceOrg: string;
19
+ description?: string | undefined;
20
+ name?: string | undefined;
21
+ }>;
22
+ export type CreateOrgSnapshotOptions = z.infer<typeof createOrgSnapshotParams>;
23
+ export declare const registerToolCreateOrgSnapshot: (server: McpServer) => void;
@@ -0,0 +1,88 @@
1
+ /*
2
+ * Copyright 2025, Salesforce, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { z } from 'zod';
17
+ import { Org } from '@salesforce/core';
18
+ import { textResponse } from '../../shared/utils.js';
19
+ import { directoryParam, usernameOrAliasParam } from '../../shared/params.js';
20
+ import { getConnection } from '../../shared/auth.js';
21
+ /*
22
+ * Create a new scratch org snapshot
23
+ *
24
+ * Parameters:
25
+ * - directory: directory to change to before running the command
26
+ * - devHub: Username or alias of the Dev Hub org
27
+ * - sourceOrg: ID or locally authenticated username or alias of scratch org to snapshot.
28
+ * - description: a description given to the snapshot
29
+ * - name: Unique name of snapshot
30
+ * Returns:
31
+ * - textResponse:
32
+ */
33
+ export const createOrgSnapshotParams = z.object({
34
+ directory: directoryParam,
35
+ devHub: usernameOrAliasParam.describe('The default devhub username, use the #sf-get-username tool to get the default devhub if unsure'),
36
+ sourceOrg: usernameOrAliasParam.describe('The org username or alias to create a snapshot of, use the #sf-get-username tool to get the default target org if unsure'),
37
+ description: z.string().describe(' Description of snapshot.').optional(),
38
+ name: z.string().describe('Unique name of snapshot').max(15).default(Date.now().toString().substring(0, 15)),
39
+ });
40
+ export const registerToolCreateOrgSnapshot = (server) => {
41
+ server.tool('sf-create-org-snapshot', `Creates a new snapshot of an org
42
+
43
+ AGENT INSTRUCTIONS:
44
+
45
+ Example usage:
46
+ Create a snapshot called 07042025
47
+ create a snapshot called 07042025 with the description, "this is a snapshot for commit 5b1a09b1743 - new login flow working"
48
+ create a snapshot of my MyScratch in myDevHub
49
+ `, createOrgSnapshotParams.shape, {
50
+ title: 'Create a new snapshot',
51
+ }, async ({ directory, devHub, description, name, sourceOrg }) => {
52
+ try {
53
+ process.chdir(directory);
54
+ const sourceOrgId = (await Org.create({ aliasOrUsername: sourceOrg })).getOrgId();
55
+ const devHubConnection = await getConnection(devHub);
56
+ const createResponse = await devHubConnection.sobject('OrgSnapshot').create({
57
+ SourceOrg: sourceOrgId,
58
+ Description: description,
59
+ SnapshotName: name,
60
+ Content: 'metadatadata',
61
+ });
62
+ if (createResponse.success === false) {
63
+ return textResponse(`An error while created the org snapshot: ${JSON.stringify(createResponse)}`, true);
64
+ }
65
+ const result = await devHubConnection.singleRecordQuery(`SELECT Id,
66
+ SnapshotName,
67
+ Description,
68
+ Status,
69
+ SourceOrg,
70
+ CreatedDate,
71
+ LastModifiedDate,
72
+ ExpirationDate,
73
+ Error FROM OrgSnapshot WHERE Id = '${createResponse.id}'`);
74
+ return textResponse(`Successfully created the org snapshot: ${JSON.stringify(result)}`);
75
+ }
76
+ catch (error) {
77
+ const e = error;
78
+ // dev hub does not have snapshot pref enabled
79
+ if (e.name === 'NOT_FOUND') {
80
+ return textResponse("Scratch Org Snapshots isn't enabled for your Dev Hub.", true);
81
+ }
82
+ else {
83
+ return textResponse(`Error: ${e.name} : ${e.message}`, true);
84
+ }
85
+ }
86
+ });
87
+ };
88
+ //# sourceMappingURL=sf-create-org-snapshot.js.map
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare const createScratchOrgParams: z.ZodObject<{
4
+ directory: z.ZodEffects<z.ZodString, string, string>;
5
+ devHub: z.ZodString;
6
+ duration: z.ZodDefault<z.ZodNumber>;
7
+ edition: z.ZodOptional<z.ZodEnum<["developer", "enterprise", "group", "professional", "partner-developer", "partner-enterprise", "partner-group", "partner-professional"]>>;
8
+ definitionFile: z.ZodDefault<z.ZodString>;
9
+ alias: z.ZodOptional<z.ZodString>;
10
+ async: z.ZodDefault<z.ZodBoolean>;
11
+ setDefault: z.ZodOptional<z.ZodBoolean>;
12
+ snapshot: z.ZodOptional<z.ZodString>;
13
+ sourceOrg: z.ZodOptional<z.ZodString>;
14
+ username: z.ZodOptional<z.ZodString>;
15
+ description: z.ZodOptional<z.ZodString>;
16
+ orgName: z.ZodOptional<z.ZodString>;
17
+ adminEmail: z.ZodOptional<z.ZodString>;
18
+ }, "strip", z.ZodTypeAny, {
19
+ duration: number;
20
+ async: boolean;
21
+ directory: string;
22
+ devHub: string;
23
+ definitionFile: string;
24
+ description?: string | undefined;
25
+ alias?: string | undefined;
26
+ snapshot?: string | undefined;
27
+ setDefault?: boolean | undefined;
28
+ username?: string | undefined;
29
+ edition?: "group" | "enterprise" | "developer" | "professional" | "partner-developer" | "partner-enterprise" | "partner-group" | "partner-professional" | undefined;
30
+ sourceOrg?: string | undefined;
31
+ orgName?: string | undefined;
32
+ adminEmail?: string | undefined;
33
+ }, {
34
+ directory: string;
35
+ devHub: string;
36
+ duration?: number | undefined;
37
+ description?: string | undefined;
38
+ alias?: string | undefined;
39
+ async?: boolean | undefined;
40
+ snapshot?: string | undefined;
41
+ setDefault?: boolean | undefined;
42
+ username?: string | undefined;
43
+ edition?: "group" | "enterprise" | "developer" | "professional" | "partner-developer" | "partner-enterprise" | "partner-group" | "partner-professional" | undefined;
44
+ definitionFile?: string | undefined;
45
+ sourceOrg?: string | undefined;
46
+ orgName?: string | undefined;
47
+ adminEmail?: string | undefined;
48
+ }>;
49
+ export type CreateScratchOrgOptions = z.infer<typeof createScratchOrgParams>;
50
+ export declare const registerToolCreateScratchOrg: (server: McpServer) => void;
@@ -0,0 +1,132 @@
1
+ /*
2
+ * Copyright 2025, Salesforce, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { join } from 'node:path';
17
+ import * as fs from 'node:fs';
18
+ import { z } from 'zod';
19
+ import { Org, scratchOrgCreate } from '@salesforce/core';
20
+ import { Duration } from '@salesforce/kit';
21
+ import { textResponse } from '../../shared/utils.js';
22
+ import { directoryParam, usernameOrAliasParam } from '../../shared/params.js';
23
+ /*
24
+ * Create a new scratch org
25
+ *
26
+ * Parameters:
27
+ * - directory: directory to change to before running the command
28
+ * - usernameOrAlias: Username or alias of the Salesforce DevHub org to use to create from
29
+ * - duration: Duration in days of the scratch org to exist, default 7
30
+ * - edition: Edition of the scratch org
31
+ * - definitionFile: path to the scratch org definition file, default config/project-scratch-def.json
32
+ * - alias: Alias to use for the scratch org
33
+ * - async: Wait for the scratch org creation process to finish, default false
34
+ * - setDefault: set the newly created org as default-target-org
35
+ * - snapshot: The snapshot name to use when creating a scratch org
36
+ * - sourceOrg: 15-character ID of the org shape that the new scratch org is based on
37
+ * - username: Username of the scratch org admin user
38
+ * - description: a description given to the scratch org
39
+ * - orgName: Name of the scratch org
40
+ * - adminEmail: Email address that will be applied to the org's admin user.
41
+ * Returns:
42
+ * - textResponse:
43
+ */
44
+ export const createScratchOrgParams = z.object({
45
+ directory: directoryParam,
46
+ devHub: usernameOrAliasParam.describe('The default devhub username, use the #sf-get-username tool to get the default devhub if unsure'),
47
+ duration: z.number().default(7).describe('number of days before the org expires'),
48
+ edition: z
49
+ .enum([
50
+ 'developer',
51
+ 'enterprise',
52
+ 'group',
53
+ 'professional',
54
+ 'partner-developer',
55
+ 'partner-enterprise',
56
+ 'partner-group',
57
+ 'partner-professional',
58
+ ])
59
+ .optional(),
60
+ definitionFile: z
61
+ .string()
62
+ .default(join('config', 'project-scratch-def.json'))
63
+ .describe('a normalized path to a scratch definition json file'),
64
+ alias: z.string().describe('the alias to be used for the scratch org').optional(),
65
+ async: z
66
+ .boolean()
67
+ .default(false)
68
+ .describe('Whether to wait for the org creation process to finish (false) or just quickly return the ID (true)'),
69
+ setDefault: z
70
+ .boolean()
71
+ .optional()
72
+ .describe('If true, will set the newly created scratch org to be the default-target-org'),
73
+ snapshot: z.string().describe('The snapshot name to use when creating a scratch org').optional(),
74
+ sourceOrg: z
75
+ .string()
76
+ .length(15)
77
+ .describe('15-character ID of the org shape that the new scratch org is based on')
78
+ .optional(),
79
+ username: z.string().describe('Username of the scratch org admin user').optional(),
80
+ description: z.string().describe('a description given to the scratch org').optional(),
81
+ orgName: z.string().describe('Name of the scratch org').optional(),
82
+ adminEmail: z.string().describe("Email address that will be applied to the org's admin user.").optional(),
83
+ });
84
+ export const registerToolCreateScratchOrg = (server) => {
85
+ server.tool('sf-create-scratch-org', `Creates a scratch org with the specified parameters.
86
+
87
+ AGENT INSTRUCTIONS:
88
+
89
+ Example usage:
90
+ Create a scratch org
91
+ create a scratch org with the definition file myDefinition.json that lasts 3 days
92
+ create a scratch org aliased as MyNewOrg and set as default and don't wait for it to finish
93
+ `, createScratchOrgParams.shape, {
94
+ title: 'Create a scratch org',
95
+ }, async ({ directory, devHub, orgName, adminEmail, description, snapshot, sourceOrg, username, edition, setDefault, async, duration, alias, definitionFile, }) => {
96
+ try {
97
+ process.chdir(directory);
98
+ const hubOrProd = await Org.create({ aliasOrUsername: devHub });
99
+ const requestParams = {
100
+ hubOrg: hubOrProd,
101
+ durationDays: duration,
102
+ wait: async ? Duration.minutes(0) : Duration.minutes(10),
103
+ orgConfig: {
104
+ ...(definitionFile
105
+ ? JSON.parse(await fs.promises.readFile(definitionFile, 'utf-8'))
106
+ : {}),
107
+ ...(edition ? { edition } : {}),
108
+ ...(snapshot ? { snapshot } : {}),
109
+ ...(username ? { username } : {}),
110
+ ...(description ? { description } : {}),
111
+ ...(orgName ? { orgName } : {}),
112
+ ...(sourceOrg ? { sourceOrg } : {}),
113
+ ...(adminEmail ? { adminEmail } : {}),
114
+ },
115
+ alias,
116
+ setDefault,
117
+ tracksSource: true,
118
+ };
119
+ const result = await scratchOrgCreate(requestParams);
120
+ if (async) {
121
+ return textResponse(`Successfully enqueued scratch org with job Id: ${JSON.stringify(result.scratchOrgInfo?.Id)} use the #sf-resume tool to resume this operation`);
122
+ }
123
+ else {
124
+ return textResponse(`Successfully created scratch org ${JSON.stringify(result)}`);
125
+ }
126
+ }
127
+ catch (e) {
128
+ return textResponse(`Failed to create org: ${e instanceof Error ? e.message : 'Unknown error'}`, true);
129
+ }
130
+ });
131
+ };
132
+ //# sourceMappingURL=sf-create-scratch-org.js.map
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare const deleteOrgParams: z.ZodObject<{
4
+ directory: z.ZodEffects<z.ZodString, string, string>;
5
+ usernameOrAlias: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ directory: string;
8
+ usernameOrAlias: string;
9
+ }, {
10
+ directory: string;
11
+ usernameOrAlias: string;
12
+ }>;
13
+ export type DeleteOrgOptions = z.infer<typeof deleteOrgParams>;
14
+ export declare const registerToolDeleteOrg: (server: McpServer) => void;
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Copyright 2025, Salesforce, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { z } from 'zod';
17
+ import { AuthRemover, Org } from '@salesforce/core';
18
+ import { textResponse } from '../../shared/utils.js';
19
+ import { directoryParam, usernameOrAliasParam } from '../../shared/params.js';
20
+ /*
21
+ * Delete a locally authorized Salesforce org
22
+ *
23
+ * Parameters:
24
+ * - directory: directory to change to before running the command
25
+ * - usernameOrAlias: Username or alias of the Salesforce org to delete.
26
+ *
27
+ * Returns:
28
+ * - textResponse: Deletion request response
29
+ */
30
+ export const deleteOrgParams = z.object({
31
+ directory: directoryParam,
32
+ usernameOrAlias: usernameOrAliasParam,
33
+ });
34
+ export const registerToolDeleteOrg = (server) => {
35
+ server.tool('sf-delete-org', `Deletes specified salesforce org.
36
+
37
+ AGENT INSTRUCTIONS:
38
+ ALWAYS confirm with the user before deleting an org
39
+
40
+ Example usage:
41
+ Can you delete my org
42
+ Can you delete MyAliasedOrg
43
+ Can you delete test-fe2n4tc8pgku@example.com
44
+ `, deleteOrgParams.shape, {
45
+ title: 'Delete an Org',
46
+ }, async ({ directory, usernameOrAlias }) => {
47
+ try {
48
+ process.chdir(directory);
49
+ const org = await Org.create({ aliasOrUsername: usernameOrAlias });
50
+ await org.delete();
51
+ return textResponse(`Successfully deleted ${usernameOrAlias}`);
52
+ }
53
+ catch (e) {
54
+ if (e instanceof Error && e.name === 'DomainNotFoundError') {
55
+ // the org has expired, so remote operations won't work
56
+ // let's clean up the files locally
57
+ const authRemover = await AuthRemover.create();
58
+ await authRemover.removeAuth(usernameOrAlias);
59
+ return textResponse(`Successfully deleted ${usernameOrAlias}`);
60
+ }
61
+ return textResponse(`Failed to delete org: ${e instanceof Error ? e.message : 'Unknown error'}`, true);
62
+ }
63
+ });
64
+ };
65
+ //# sourceMappingURL=sf-delete-org.js.map