@salesforce/mcp 0.0.1 → 0.2.6

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.
Files changed (35) hide show
  1. package/README.md +240 -3
  2. package/lib/index.d.ts +1 -0
  3. package/lib/index.js +70 -4
  4. package/lib/shared/auth.d.ts +20 -0
  5. package/lib/shared/auth.js +186 -0
  6. package/lib/shared/params.d.ts +5 -0
  7. package/lib/shared/params.js +48 -0
  8. package/lib/shared/types.d.ts +34 -0
  9. package/lib/shared/types.js +17 -0
  10. package/lib/shared/utils.d.ts +13 -0
  11. package/lib/shared/utils.js +155 -0
  12. package/lib/tools/core/index.d.ts +1 -0
  13. package/lib/tools/core/index.js +17 -0
  14. package/lib/tools/core/sf-get-username.d.ts +17 -0
  15. package/lib/tools/core/sf-get-username.js +105 -0
  16. package/lib/tools/data/index.d.ts +1 -0
  17. package/lib/tools/data/index.js +17 -0
  18. package/lib/tools/data/sf-query-org.d.ts +17 -0
  19. package/lib/tools/data/sf-query-org.js +50 -0
  20. package/lib/tools/metadata/index.d.ts +2 -0
  21. package/lib/tools/metadata/index.js +18 -0
  22. package/lib/tools/metadata/sf-deploy-metadata.d.ts +27 -0
  23. package/lib/tools/metadata/sf-deploy-metadata.js +150 -0
  24. package/lib/tools/metadata/sf-retrieve-metadata.d.ts +2 -0
  25. package/lib/tools/metadata/sf-retrieve-metadata.js +122 -0
  26. package/lib/tools/orgs/index.d.ts +1 -0
  27. package/lib/tools/orgs/index.js +17 -0
  28. package/lib/tools/orgs/sf-list-all-orgs.d.ts +2 -0
  29. package/lib/tools/orgs/sf-list-all-orgs.js +49 -0
  30. package/lib/tools/users/index.d.ts +1 -0
  31. package/lib/tools/users/index.js +17 -0
  32. package/lib/tools/users/sf-assign-permission-set.d.ts +20 -0
  33. package/lib/tools/users/sf-assign-permission-set.js +84 -0
  34. package/lib/tsconfig.tsbuildinfo +1 -1
  35. package/package.json +15 -8
package/README.md CHANGED
@@ -1,7 +1,244 @@
1
1
  # mcp
2
2
 
3
- MCP Server for interacting with Salesforce instances
3
+ MCP Server for Interacting with Salesforce Orgs
4
4
 
5
- [![NPM](https://img.shields.io/npm/v/@salesforce/mcp.svg?label=@salesforce/mcp)](https://www.npmjs.com/package/@salesforce/mcp) [![Downloads/week](https://img.shields.io/npm/dw/@salesforce/mcp.svg)](https://npmjs.org/package/@salesforce/mcp) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/license/apache-2-0)
5
+ [![NPM](https://img.shields.io/npm/v/@salesforce/mcp.svg?label=@salesforce/mcp)](https://www.npmjs.com/package/@salesforce/mcp) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/license/apache-2-0)
6
6
 
7
- Future home of the Salesforce MCP. Stay tuned
7
+ ## Overview of the Salesforce MCP Server (Developer Preview)
8
+
9
+ The Salesforce MCP Server is a specialized Model Context Protocol (MCP) implementation designed to facilitate seamless interaction between large language models (LLMs) and Salesforce orgs. This MCP server provides a robust set of tools and capabilities that enable LLMs to read, manage, and operate Salesforce resources securely.
10
+
11
+ Key Features:
12
+
13
+ - Direct interaction with Salesforce orgs through LLM-driven tools.
14
+ - Secure access using TypeScript libraries (not shelling out to the `sf` Salesforce CLI).
15
+ - Improved security by avoiding the exposure of secrets in plain text.
16
+ - Granular access control with org allowlisting.
17
+ - Modular tool architecture for easy extensibility.
18
+
19
+ **NOTE**: The Salesforce MCP Server is available as a developer preview. The feature isn’t generally available unless or until Salesforce announces its general availability in documentation or in press releases or public statements. All commands, parameters, and other features are subject to change or deprecation at any time, with or without notice. Don't implement functionality developed with these commands or tools. As we continue to enhance and refine the implementation, the available functionality and tools may evolve. We welcome feedback and contributions to help shape the future of this project.
20
+
21
+ ### Security Features
22
+
23
+ The Salesforce MCP Server was designed with security as a top priority.
24
+
25
+ - **Uses TypeScript libraries directly**
26
+
27
+ - Greatly decreases the size of the MCP Server.
28
+ - Significantly reduces the risk of remote code execution (RCE).
29
+
30
+ - **No secrets needed in configuration**
31
+
32
+ - Eliminates the risk of plain text secret exposure.
33
+ - Accesses pre-existing (encrypted) auth files on the user's machine.
34
+ - Implements allowlisting for auth info key/values to prevent sensitive data exposure.
35
+
36
+ - **No secrets exposed via MCP tools**
37
+
38
+ - Prevents other tools from accessing unencrypted tokens.
39
+ - Tools pass usernames around instead of tokens.
40
+
41
+ - **Granular access control**
42
+
43
+ - MCP Server can access auth info for only orgs that have been explicitly allowlisted.
44
+ - Users specify allowed orgs when starting the server.
45
+
46
+ ## Get Started Using VS Code as the Client
47
+
48
+ Want to jump in and see what all the fuss is about? Read on!
49
+
50
+ This example uses Visual Studio Code (VS Code) as the MCP client because it's a standard Salesforce DX development tool. After you configure it with the Salesforce MCP server, you then use GitHub Copilot and natural language to easily execute typical Salesforce DX development tasks, such as listing your authorized orgs, viewing org records, and deploying or retrieving metadata.
51
+
52
+ But you're not limited to using only VS Code and Copilot! You can [configure many other clients](README.md#configure-other-clients-to-use-the-salesforce-mcp-server) to use the Salesforce MCP server, such as Cursor, Cline, Claude Desktop, Zed, Windsurf, and more.
53
+
54
+ **Before You Begin**
55
+
56
+ For the best getting-started experience, make sure that you have a Salesforce DX environment set up on your computer. In particular:
57
+
58
+ - [Install VS Code](https://code.visualstudio.com/docs) on your computer.
59
+ - [Create a Salesforce DX project](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_create_new.htm) and open it in VS Code. You can also clone an example repo, such as [dreamhouse-lwc](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_intro_sample_repo.htm), which is a ready-to-use DX project that contains a simple Salesforce application, with metadata and test data.
60
+ - [Authorize at least one Salesforce org](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm) to use with your DX project. You can also create a scratch org.
61
+
62
+ **Let's Do It**
63
+
64
+ 1. Create a `.vscode/mcp.json` file at the root of your DX project and add this JSON:
65
+
66
+ ```json
67
+ {
68
+ "servers": {
69
+ "salesforce": {
70
+ "type": "stdio",
71
+ "command": "npx",
72
+ "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_ORG", "--toolsets", "all"]
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
78
+ You can also configure the MCP server globally by editing the VS Code [settings.json](https://code.visualstudio.com/docs/configure/settings#_settings-file-locations) file and adding a similar JSON snippet but contained in an `mcp:servers` section.
79
+
80
+ The `--orgs` argument is required and specifies the authorized orgs you're allowing the MCP server to access. The `--toolsets` argument is optional and specifies the toolsets it should consult when determining the specific tool to run. See [Configure Orgs and Toolsets](README.md#configure-orgs-and-toolsets) for the available values for the two arguments.
81
+
82
+ 1. Open VS Code, go to **View -> Command Palette** and enter **MCP: List Servers**.
83
+
84
+ TIP: You can also get to the command palette by pressing press Ctrl+Shift+P (Windows or Linux) or Command-Shift-P (macOS).
85
+
86
+ 1. Click `salesforce`, then **Start Server**.
87
+
88
+ Check the Output tab for the server status.
89
+
90
+ 1. Run **Chat: Open Chat (Agent)** from the command palette to start a new GitHub Copilot chat session.
91
+
92
+ 1. In the GitHub Copilot chat window, use natural language to explain what you want to do. The MCP server determines which configured tool to use, and then shows it to you along with other information. Click **Continue** to run the tool and see the results of your request. Try out these examples:
93
+
94
+ - List all my orgs.
95
+ - Which are my active scratch orgs?
96
+ - Show me all the accounts in the org with alias my-org.
97
+ - Deploy everything in my project to the org with alias my-org.
98
+
99
+ 1. To stop, restart, or view the MCP server configuration, run the **MCP: List Servers** command, click `salesforce`, then click the appropriate option.
100
+
101
+ ## Configure Orgs and Toolsets
102
+
103
+ You configure the Salesforce MCP server by specifying at least one authorized org and an optional list of MCP toolsets.
104
+
105
+ ### Configure Orgs
106
+
107
+ The Salesforce MCP tools require an org, and so you must include the required `--orgs` argument to specify at least one authorized org when you configure the MCP server. Separate multiple values with commas.
108
+
109
+ You must explicitly [authorize the orgs](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm) on your computer before the MCP server can access them. Use the `org login web` Salesforce CLI command or the VS Code **SFDX: Authorize an Org** command from the command palette.
110
+
111
+ These are the available values for the `--orgs` argument:
112
+
113
+ - `DEFAULT_TARGET_ORG` - Allow access to your default org. If you've set a local default org in your DX project, the MCP server uses it. If not, the server uses a globally-set default org.
114
+ - `DEFAULT_TARGET_DEV_HUB` - Allow access to your default Dev Hub org. If you've set a local default Dev Hub org in your DX project, the MCP server uses it. If not, the server uses a globally-set default Dev Hub org.
115
+ - `ALLOW_ALL_ORGS` - Allow access to all authorized orgs. Use this value with caution.
116
+ - `<username or alias>` - Allow access to a specific org by specifying its username or alias.
117
+
118
+ This example shows how to specify that the MCP tools run against your default org when you configure the MCP server for VS Code:
119
+
120
+ ```json
121
+ "mcp": {
122
+ "servers": {
123
+ "salesforce": {
124
+ "type": "stdio",
125
+ "command": "npx",
126
+ "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_ORG"]
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ This sample snippet shows how to configure access to your default Dev Hub org and an org with username `test-org@example.com`:
133
+
134
+ ```json
135
+ "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_DEV_HUB,test-org@example.com"]
136
+ ```
137
+
138
+ This sample snippet shows how to configure access to two orgs for which you specified aliases when you authorized them:
139
+
140
+ ```json
141
+ "args": ["-y", "@salesforce/mcp", "--orgs", "my-scratch-org,my-dev-hub"]
142
+ ```
143
+
144
+ ### Configure Toolsets
145
+
146
+ The Salesforce MCP Server supports **toolsets** - a way to selectively enable different groups of MCP tools based on your needs. This allows you to run the MCP server with only the tools you require, which in turn reduces the context.
147
+
148
+ Use the `--toolsets` (or short name `-t`) argument to specify the toolsets when you configure the Salesforce MCP server. Separate multiple toolsets with commas. The `--toolsets` argument is optional; if you don't specify it, the MCP server is configured with all toolsets.
149
+
150
+ These are the available toolsets:
151
+
152
+ - `all` (default) - Enables all available tools from all toolsets.
153
+ - `orgs` - [Tools to manage your authorized orgs.](README.md#orgs-toolset)
154
+ - `data` - [Tools to manage the data in your org, such as listing all accounts.](README.md#data-toolset)
155
+ - `users` - [Tools to manage org users, such as assigning a permission set.](README.md#users-toolset)
156
+ - `metadata` - [Tools to deploy and retrieve metadata to and from your org and your DX project.](README.md#metadata-toolset)
157
+
158
+ This example shows how to enable the `data`, `orgs`, and `metadata` toolsets when configuring the MCP server for VS Code:
159
+
160
+ ```json
161
+ "mcp": {
162
+ "servers": {
163
+ "salesforce": {
164
+ "type": "stdio",
165
+ "command": "npx",
166
+ "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_ORG", "--toolsets", "data,orgs,metadata"]
167
+ }
168
+ }
169
+ }
170
+ ```
171
+
172
+ #### Core Toolset (always enabled)
173
+
174
+ Includes this tool:
175
+
176
+ - `sf-get-username` - Determines the appropriate username or alias for Salesforce operations, handling both default orgs and Dev Hubs.
177
+
178
+ #### Orgs Toolset
179
+
180
+ Includes this tool:
181
+
182
+ - `sf-list-all-orgs` - Lists all configured Salesforce orgs, with optional connection status checking.
183
+
184
+ #### Data Toolset
185
+
186
+ Includes this tool:
187
+
188
+ - `sf-query-org` - Runs a SOQL query against a Salesforce org.
189
+
190
+ #### Users Toolset
191
+
192
+ Includes this tool:
193
+
194
+ - `sf-assign-permission-set` - Assigns a permission set to the user or on behalf of another user.
195
+
196
+ #### Metadata Toolset
197
+
198
+ Includes these tools:
199
+
200
+ - `sf-deploy-metadata` - Deploys metadata from your DX project to an org.
201
+ - `sf-retrieve-metadata` - Retrieves metadata from your org to your DX project.
202
+
203
+ ## Configure Other Clients to Use the Salesforce MCP Server
204
+
205
+ **Cursor**
206
+
207
+ To configure [Cursor](https://www.cursor.com/) to work with Salesforce MCP server, add this snippet to your Cursor `mcp.json` file:
208
+
209
+ ```json
210
+ {
211
+ "mcpServers": {
212
+ "salesforce": {
213
+ "command": {
214
+ "path": "npx",
215
+ "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_ORG", "--toolsets", "all"]
216
+ }
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ **Cline**
223
+
224
+ To configure [Cline](https://cline.bot), add this snippet to your Cline `cline_mcp_settings.json` file:
225
+
226
+ ```json
227
+ {
228
+ "mcpServers": {
229
+ "salesforce": {
230
+ "command": "npx",
231
+ "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_ORG", "--toolsets", "all"]
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ **Other Clients**
238
+
239
+ For these other clients, refer to their documentation for adding MCP servers and follow the same pattern as in the preceding VS Code and Cursor JSON snippets:
240
+
241
+ - [Claude Desktop](https://claude.ai/download)
242
+ - [Zed](https://github.com/zed-industries/zed)
243
+ - [Windsurf](https://www.windsurf.com/)
244
+ - [Trae](https://trae.ai)
package/lib/index.d.ts CHANGED
@@ -1 +1,2 @@
1
+ #!/usr/bin/env node
1
2
  export {};
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";
1
+ #!/usr/bin/env node
2
2
  /*
3
3
  * Copyright 2025, Salesforce, Inc.
4
4
  *
@@ -14,7 +14,73 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- Object.defineProperty(exports, "__esModule", { value: true });
18
- // eslint-disable-next-line no-console
19
- console.log('Coming soon ');
17
+ /* eslint-disable no-console */
18
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
19
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
+ import { parseStartupArguments, getEnabledToolsets } from './shared/utils.js';
21
+ import * as core from './tools/core/index.js';
22
+ import * as orgs from './tools/orgs/index.js';
23
+ import * as data from './tools/data/index.js';
24
+ import * as users from './tools/users/index.js';
25
+ import * as metadata from './tools/metadata/index.js';
26
+ // Create server instance
27
+ const server = new McpServer({
28
+ name: 'sf-mcp-server',
29
+ version: '0.0.6',
30
+ capabilities: {
31
+ resources: {},
32
+ tools: {},
33
+ },
34
+ });
35
+ const { values } = parseStartupArguments();
36
+ // Toolsets will always be set. It is 'all' by default
37
+ const availableToolsets = ['all', 'orgs', 'data', 'users', 'metadata'];
38
+ const enabledToolsets = getEnabledToolsets(availableToolsets, values.toolsets);
39
+ const all = enabledToolsets.has('all');
40
+ // TODO: Should we add annotations to our tools? https://modelcontextprotocol.io/docs/concepts/tools#tool-definition-structure
41
+ // TODO: Move tool names into a shared file, that way if we reference them in multiple places, we can update them in one place
42
+ // ************************
43
+ // CORE TOOLS (always on)
44
+ // ************************
45
+ // get username
46
+ core.registerToolGetUsername(server);
47
+ // ************************
48
+ // ORG TOOLS
49
+ // ************************
50
+ if (all || enabledToolsets.has('orgs')) {
51
+ // list all orgs
52
+ orgs.registerToolListAllOrgs(server);
53
+ }
54
+ // ************************
55
+ // DATA TOOLS
56
+ // ************************
57
+ if (all || enabledToolsets.has('data')) {
58
+ // query org
59
+ data.registerToolQueryOrg(server);
60
+ }
61
+ // ************************
62
+ // USER TOOLS
63
+ // ************************
64
+ if (all || enabledToolsets.has('users')) {
65
+ // assign permission set
66
+ users.registerToolAssignPermissionSet(server);
67
+ }
68
+ // ************************
69
+ // METADATA TOOLS
70
+ // ************************
71
+ if (all || enabledToolsets.has('metadata')) {
72
+ // deploy metadata
73
+ metadata.registerToolDeployMetadata(server);
74
+ // retrieve metadata
75
+ metadata.registerToolRetrieveMetadata(server);
76
+ }
77
+ async function main() {
78
+ const transport = new StdioServerTransport();
79
+ await server.connect(transport);
80
+ console.error('✅ Salesforce MCP Server running on stdio');
81
+ }
82
+ main().catch((error) => {
83
+ console.error('Fatal error in main():', error);
84
+ process.exit(1);
85
+ });
20
86
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,20 @@
1
+ import { Connection, type OrgAuthorization } from '@salesforce/core';
2
+ import { type ConfigInfoWithCache, type SanitizedOrgAuthorization } from './types.js';
3
+ /**
4
+ * Sanitizes org authorization data by filtering out sensitive fields
5
+ *
6
+ * @param orgs - Array of OrgAuthorization objects
7
+ * @returns Array of sanitized org authorization objects with only allowed fields
8
+ */
9
+ export declare function sanitizeOrgs(orgs: OrgAuthorization[]): SanitizedOrgAuthorization[];
10
+ export declare function suggestUsername(): Promise<{
11
+ suggestedUsername: string | undefined;
12
+ reasoning: string;
13
+ aliasForReference?: string;
14
+ }>;
15
+ export declare function getConnection(username: string): Promise<Connection>;
16
+ export declare function findOrgByUsernameOrAlias(allOrgs: SanitizedOrgAuthorization[], usernameOrAlias: string): SanitizedOrgAuthorization | undefined;
17
+ export declare function getAllAllowedOrgs(): Promise<SanitizedOrgAuthorization[]>;
18
+ export declare function filterAllowedOrgs(orgs: SanitizedOrgAuthorization[], ALLOWLIST?: Set<string>): Promise<SanitizedOrgAuthorization[]>;
19
+ export declare function getDefaultTargetOrg(): Promise<ConfigInfoWithCache | undefined>;
20
+ export declare function getDefaultTargetDevHub(): Promise<ConfigInfoWithCache | undefined>;
@@ -0,0 +1,186 @@
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
+ /* eslint-disable no-console */
17
+ import { AuthInfo, Connection, ConfigAggregator, OrgConfigProperties } from '@salesforce/core';
18
+ import { buildOrgAllowList, parseStartupArguments } from './utils.js';
19
+ const url = new URL(import.meta.url);
20
+ const params = url.searchParams.get('orgs');
21
+ const paramOrg = params ? params : undefined;
22
+ const { values } = parseStartupArguments();
23
+ const ORG_ALLOWLIST = buildOrgAllowList(paramOrg ?? values.orgs);
24
+ console.error(' - Allowed orgs:', ORG_ALLOWLIST);
25
+ /**
26
+ * Sanitizes org authorization data by filtering out sensitive fields
27
+ *
28
+ * @param orgs - Array of OrgAuthorization objects
29
+ * @returns Array of sanitized org authorization objects with only allowed fields
30
+ */
31
+ export function sanitizeOrgs(orgs) {
32
+ return orgs.map((org) => ({
33
+ aliases: org.aliases,
34
+ configs: org.configs,
35
+ username: org.username,
36
+ instanceUrl: org.instanceUrl,
37
+ isScratchOrg: org.isScratchOrg,
38
+ isDevHub: org.isDevHub,
39
+ isSandbox: org.isSandbox,
40
+ orgId: org.orgId,
41
+ oauthMethod: org.oauthMethod,
42
+ isExpired: org.isExpired,
43
+ }));
44
+ }
45
+ export async function suggestUsername() {
46
+ let reasoning;
47
+ let suggestedUsername;
48
+ let aliasForReference;
49
+ const allAllowedOrgs = await getAllAllowedOrgs();
50
+ const defaultTargetOrg = await getDefaultTargetOrg();
51
+ const defaultTargetDevHub = await getDefaultTargetDevHub();
52
+ const targetOrgLocation = defaultTargetOrg?.location ? `(${defaultTargetOrg.location}) ` : '';
53
+ const targetDevHubLocation = defaultTargetDevHub?.location ? `(${defaultTargetDevHub.location}) ` : '';
54
+ if (allAllowedOrgs.length === 1) {
55
+ suggestedUsername = allAllowedOrgs[0].username;
56
+ aliasForReference = allAllowedOrgs[0].aliases?.[0];
57
+ reasoning = 'it was the only org found in the MCP Servers allowlisted orgs';
58
+ }
59
+ else if (defaultTargetOrg?.value) {
60
+ const foundOrg = findOrgByUsernameOrAlias(allAllowedOrgs, defaultTargetOrg.value);
61
+ suggestedUsername = foundOrg?.username;
62
+ aliasForReference = foundOrg?.aliases?.[0];
63
+ reasoning = `it is the default ${targetOrgLocation}target org`;
64
+ }
65
+ else if (defaultTargetDevHub?.value) {
66
+ const foundOrg = findOrgByUsernameOrAlias(allAllowedOrgs, defaultTargetDevHub.value);
67
+ suggestedUsername = foundOrg?.username;
68
+ aliasForReference = foundOrg?.aliases?.[0];
69
+ reasoning = `it is the default ${targetDevHubLocation}dev hub org`;
70
+ }
71
+ else {
72
+ reasoning = 'Error: no org was inferred. Ask the user to specify one';
73
+ }
74
+ return {
75
+ suggestedUsername,
76
+ aliasForReference,
77
+ reasoning,
78
+ };
79
+ }
80
+ // This function is the main entry point for Tools to get an allowlisted Connection
81
+ export async function getConnection(username) {
82
+ // We get all allowed orgs each call in case the directory has changed (default configs)
83
+ const allOrgs = await getAllAllowedOrgs();
84
+ const foundOrg = findOrgByUsernameOrAlias(allOrgs, username);
85
+ if (!foundOrg)
86
+ return Promise.reject(new Error('No org found with the provided username/alias. Ask the user to specify one or check their MCP Server startup config.'));
87
+ const authInfo = await AuthInfo.create({ username: foundOrg.username });
88
+ const connection = await Connection.create({ authInfo });
89
+ return connection;
90
+ }
91
+ export function findOrgByUsernameOrAlias(allOrgs, usernameOrAlias) {
92
+ return allOrgs.find((org) => {
93
+ // Check if the org's username or alias matches the provided usernameOrAlias
94
+ const isMatchingUsername = org.username === usernameOrAlias;
95
+ const isMatchingAlias = org.aliases && Array.isArray(org.aliases) && org.aliases.includes(usernameOrAlias);
96
+ return isMatchingUsername || isMatchingAlias;
97
+ });
98
+ }
99
+ export async function getAllAllowedOrgs() {
100
+ // Get all orgs on the user's machine
101
+ const allOrgs = await AuthInfo.listAllAuthorizations();
102
+ // Sanitize the orgs to remove sensitive data
103
+ const sanitizedOrgs = sanitizeOrgs(allOrgs);
104
+ // Filter out orgs that are not in ORG_ALLOWLIST
105
+ const allowedOrgs = await filterAllowedOrgs(sanitizedOrgs, ORG_ALLOWLIST);
106
+ // If no orgs are found, stop the server
107
+ if (allowedOrgs.length === 0) {
108
+ console.error('No orgs found that match the allowed orgs configuration. Check MCP Server startup config.');
109
+ process.exit(1);
110
+ }
111
+ return allowedOrgs;
112
+ }
113
+ // Function to filter orgs based on ORG_ALLOWLIST configuration
114
+ export async function filterAllowedOrgs(orgs, ALLOWLIST = ORG_ALLOWLIST) {
115
+ // Return all orgs if ALLOW_ALL_ORGS is set
116
+ if (ALLOWLIST.has('ALLOW_ALL_ORGS'))
117
+ return orgs;
118
+ // Get default orgs for filtering
119
+ const defaultTargetOrg = await getDefaultTargetOrg();
120
+ const defaultTargetDevHub = await getDefaultTargetDevHub();
121
+ return orgs.filter((org) => {
122
+ // Skip orgs without a username
123
+ if (!org.username)
124
+ return false;
125
+ // Check if org is specifically allowed by username
126
+ if (ALLOWLIST.has(org.username))
127
+ return true;
128
+ // Check if org is allowed by alias
129
+ if (org.aliases?.some((alias) => ALLOWLIST.has(alias)))
130
+ return true;
131
+ // If DEFAULT_TARGET_ORG is set, check for a username or alias match
132
+ if (ALLOWLIST.has('DEFAULT_TARGET_ORG') && defaultTargetOrg?.value) {
133
+ if (org.username === defaultTargetOrg.value)
134
+ return true;
135
+ if (org.aliases?.includes(defaultTargetOrg.value))
136
+ return true;
137
+ }
138
+ // If DEFAULT_TARGET_DEV_HUB is set, check for a username or alias match
139
+ if (ALLOWLIST.has('DEFAULT_TARGET_DEV_HUB') && defaultTargetDevHub?.value) {
140
+ if (org.username === defaultTargetDevHub.value)
141
+ return true;
142
+ if (org.aliases?.includes(defaultTargetDevHub.value))
143
+ return true;
144
+ }
145
+ // Org not allowed
146
+ return false;
147
+ });
148
+ }
149
+ const defaultOrgMaps = {
150
+ [OrgConfigProperties.TARGET_ORG]: new Map(),
151
+ [OrgConfigProperties.TARGET_DEV_HUB]: new Map(),
152
+ };
153
+ // Helper function to get default config for a property
154
+ // Values are cached based on ConfigInfo path after first retrieval
155
+ // This is to prevent manipulation of the config file after server start
156
+ async function getDefaultConfig(property) {
157
+ // If the directory changes, the singleton instance of ConfigAggregator is not updated.
158
+ // It continues to use the old local or global config.
159
+ // Note: We could update ConfigAggregator to have a clearInstance method like StateAggregator
160
+ // @ts-expect-error Accessing private static instance to reset singleton between directory changes
161
+ ConfigAggregator.instance = undefined;
162
+ const aggregator = await ConfigAggregator.create();
163
+ const config = aggregator.getInfo(property);
164
+ const { value, path, key, location } = config;
165
+ if (!value || typeof value !== 'string' || !path)
166
+ return undefined;
167
+ // Create a typed config object with the necessary properties
168
+ // This reduces assertions and lowers context returned to the LLM
169
+ const typedConfig = { key, location, value, path };
170
+ if (defaultOrgMaps[property].has(path)) {
171
+ // If the cache has the config's path set, use the cached config
172
+ const cachedConfig = defaultOrgMaps[property].get(path);
173
+ return { ...cachedConfig, cached: true };
174
+ }
175
+ else {
176
+ defaultOrgMaps[property].set(path, typedConfig);
177
+ return typedConfig;
178
+ }
179
+ }
180
+ export async function getDefaultTargetOrg() {
181
+ return getDefaultConfig(OrgConfigProperties.TARGET_ORG);
182
+ }
183
+ export async function getDefaultTargetDevHub() {
184
+ return getDefaultConfig(OrgConfigProperties.TARGET_DEV_HUB);
185
+ }
186
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1,5 @@
1
+ import { z } from 'zod';
2
+ export declare const usernameOrAliasParam: z.ZodString;
3
+ export declare const useToolingApiParam: z.ZodOptional<z.ZodBoolean>;
4
+ export declare const baseAbsolutePathParam: z.ZodEffects<z.ZodString, string, string>;
5
+ export declare const directoryParam: z.ZodEffects<z.ZodString, string, string>;
@@ -0,0 +1,48 @@
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 { sanitizePath } from './utils.js';
18
+ /*
19
+ * A collection of reuseable Tool parameters
20
+ */
21
+ export const usernameOrAliasParam = z.string()
22
+ .describe(`The username or alias for the Salesforce org to run this tool against.
23
+
24
+ AGENT INSTRUCTIONS:
25
+ If it is not clear what username or alias is, run the #sf-get-username tool.
26
+ DO NOT use #sf-get-username if the user mentions an alias or username, like "for my an-alias org" or "for my test-prgelc2petd9@example.com org".
27
+
28
+ USAGE:
29
+ ...for the my-alias org
30
+ ...for my 'my-alias' user
31
+ ...for alias myAlias
32
+ ...for my 'test@example.com' user
33
+ ...for the 'test@example.com' org`);
34
+ export const useToolingApiParam = z
35
+ .boolean()
36
+ .optional()
37
+ .describe('Use Tooling API to insert a record in a Tooling API object');
38
+ export const baseAbsolutePathParam = z
39
+ .string()
40
+ .refine(sanitizePath, 'Invalid path: Must be an absolute path and cannot contain path traversal sequences');
41
+ export const directoryParam = baseAbsolutePathParam.describe(`The directory to run this tool from.
42
+ AGENT INSTRUCTIONS:
43
+ We need to know where the user wants to run this tool from.
44
+ Look at your current Workspace Context to determine this filepath.
45
+ ALWAYS USE A FULL PATH TO THE DIRECTORY.
46
+ Unless the user explicitly asks for a different directory, or a new directory is created from the action of a tool, use this same directory for future tool calls.
47
+ `);
48
+ //# sourceMappingURL=params.js.map
@@ -0,0 +1,34 @@
1
+ import { ConfigInfo } from '@salesforce/core';
2
+ import { type Nullable } from '@salesforce/ts-types';
3
+ export type ConfigInfoWithCache = {
4
+ key: string;
5
+ location?: ConfigInfo['location'];
6
+ value: string;
7
+ cached?: boolean;
8
+ path: string;
9
+ };
10
+ export type SanitizedOrgAuthorization = {
11
+ aliases?: Nullable<string[]>;
12
+ configs?: Nullable<string[]>;
13
+ username?: string;
14
+ instanceUrl?: string;
15
+ isScratchOrg?: boolean;
16
+ isDevHub?: boolean;
17
+ isSandbox?: boolean;
18
+ orgId?: string;
19
+ oauthMethod?: string;
20
+ isExpired?: boolean | 'unknown';
21
+ };
22
+ export type ToolTextResponse = {
23
+ isError: boolean;
24
+ content: Array<{
25
+ type: 'text';
26
+ text: string;
27
+ }>;
28
+ };
29
+ export type ParseArgsResult = {
30
+ values: {
31
+ toolsets: string;
32
+ orgs: string;
33
+ };
34
+ };
@@ -0,0 +1,17 @@
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
+ export {};
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,13 @@
1
+ import { type ToolTextResponse, type ParseArgsResult } from './types.js';
2
+ export declare function parseStartupArguments(): ParseArgsResult;
3
+ export declare function buildOrgAllowList(orgs: string): Set<string>;
4
+ export declare function textResponse(text: string, isError?: boolean): ToolTextResponse;
5
+ /**
6
+ * Gets the enabled toolsets based on user input and validates against available toolsets
7
+ *
8
+ * @param availableToolsets - The list of available toolsets
9
+ * @param toolsetsInput - The comma-separated list of toolsets
10
+ * @returns A Set of enabled toolsets
11
+ */
12
+ export declare function getEnabledToolsets(availableToolsets: string[], toolsetsInput: string): Set<string>;
13
+ export declare function sanitizePath(path: string): boolean;