@salesforce/mcp 0.20.1 → 0.21.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.d.ts +1 -0
- package/lib/index.js +22 -9
- package/lib/main-server-provider.js +2 -2
- package/lib/registry.js +2 -0
- package/lib/tools/{sf-enable-tools.js → enable_tools.js} +3 -3
- package/lib/tools/{sf-list-tools.js → list_tools.js} +4 -4
- package/lib/utils/auth.js +5 -17
- package/lib/utils/registry-utils.d.ts +1 -1
- package/lib/utils/registry-utils.js +41 -10
- package/package.json +10 -11
- /package/lib/tools/{sf-enable-tools.d.ts → enable_tools.d.ts} +0 -0
- /package/lib/tools/{sf-list-tools.d.ts → list_tools.d.ts} +0 -0
package/lib/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export default class McpServerCommand extends Command {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
orgs: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
7
7
|
toolsets: import("@oclif/core/interfaces").OptionFlag<(import("@salesforce/mcp-provider-api/src/enums.js").Toolset | "all")[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
tools: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
version: import("@oclif/core/interfaces").BooleanFlag<void>;
|
|
9
10
|
'no-telemetry': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
package/lib/index.js
CHANGED
|
@@ -73,12 +73,21 @@ You can also use special values to control access to orgs:
|
|
|
73
73
|
}),
|
|
74
74
|
toolsets: Flags.option({
|
|
75
75
|
options: ['all', ...TOOLSETS],
|
|
76
|
-
|
|
77
|
-
summary: 'Toolset to enable',
|
|
76
|
+
summary: 'Toolset(s) to enable. Set to "all" to enable every toolset',
|
|
78
77
|
multiple: true,
|
|
79
78
|
delimiter: ',',
|
|
80
|
-
exclusive: ['dynamic-
|
|
79
|
+
exclusive: ['dynamic-tools'],
|
|
81
80
|
})(),
|
|
81
|
+
// It would be nice if we could get these as an Flags.option
|
|
82
|
+
// Since the tools need `services` passed in I am not sure we
|
|
83
|
+
// can get the list of tools this early. We could build a json
|
|
84
|
+
// manifest (pre-commit) that could be read from for tool names
|
|
85
|
+
tools: Flags.string({
|
|
86
|
+
summary: 'Tool(s) to enable',
|
|
87
|
+
multiple: true,
|
|
88
|
+
delimiter: ',',
|
|
89
|
+
exclusive: ['dynamic-tools'],
|
|
90
|
+
}),
|
|
82
91
|
version: Flags.version(),
|
|
83
92
|
'no-telemetry': Flags.boolean({
|
|
84
93
|
summary: 'Disable telemetry',
|
|
@@ -98,19 +107,23 @@ You can also use special values to control access to orgs:
|
|
|
98
107
|
static examples = [
|
|
99
108
|
{
|
|
100
109
|
description: 'Start the server with all toolsets enabled and access only to the default org in the project',
|
|
101
|
-
command: '<%= config.bin %> --orgs DEFAULT_TARGET_ORG',
|
|
110
|
+
command: '<%= config.bin %> --toolsets all --orgs DEFAULT_TARGET_ORG',
|
|
102
111
|
},
|
|
103
112
|
{
|
|
104
|
-
description: 'Allow access to the default org and "my-alias"
|
|
113
|
+
description: 'Allow access to the default target org and "my-alias" with only the "data" toolset',
|
|
105
114
|
command: '<%= config.bin %> --orgs DEFAULT_TARGET_DEV_HUB,my-alias --toolsets data',
|
|
106
115
|
},
|
|
107
116
|
{
|
|
108
117
|
description: 'Allow access to 3 specific orgs and enable all toolsets',
|
|
109
|
-
command: '<%= config.bin %> --orgs test-org@example.com,my-dev-hub,my-alias',
|
|
118
|
+
command: '<%= config.bin %> --toolsets all --orgs test-org@example.com,my-dev-hub,my-alias',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
description: 'Start the server with the "data" toolset and also the "create_scratch_org" tool',
|
|
122
|
+
command: '<%= config.bin %> --orgs DEFAULT_TARGET_ORG --toolsets data --tools create_scratch_org',
|
|
110
123
|
},
|
|
111
124
|
{
|
|
112
|
-
description: 'Allow tools that are not generally available (GA) to be registered with the server',
|
|
113
|
-
command: '<%= config.bin %> --orgs DEFAULT_TARGET_ORG --allow-non-ga-tools',
|
|
125
|
+
description: 'Allow tools that are not generally available (NON-GA) to be registered with the server',
|
|
126
|
+
command: '<%= config.bin %> --toolsets all --orgs DEFAULT_TARGET_ORG --allow-non-ga-tools',
|
|
114
127
|
},
|
|
115
128
|
];
|
|
116
129
|
telemetry;
|
|
@@ -149,7 +162,7 @@ You can also use special values to control access to orgs:
|
|
|
149
162
|
debug: flags.debug,
|
|
150
163
|
},
|
|
151
164
|
});
|
|
152
|
-
await registerToolsets(flags.toolsets ?? [
|
|
165
|
+
await registerToolsets(flags.toolsets ?? [], flags.tools ?? [], flags['dynamic-tools'] ?? false, flags['allow-non-ga-tools'] ?? false, server, services);
|
|
153
166
|
const transport = new StdioServerTransport();
|
|
154
167
|
await server.connect(transport);
|
|
155
168
|
console.error(`✅ Salesforce MCP Server v${this.config.version} running on stdio`);
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { EnableToolsMcpTool } from './tools/
|
|
17
|
-
import { ListToolsMcpTool } from './tools/
|
|
16
|
+
import { EnableToolsMcpTool } from './tools/enable_tools.js';
|
|
17
|
+
import { ListToolsMcpTool } from './tools/list_tools.js';
|
|
18
18
|
export function createDynamicServerTools(server) {
|
|
19
19
|
return [new EnableToolsMcpTool(server), new ListToolsMcpTool()];
|
|
20
20
|
}
|
package/lib/registry.js
CHANGED
|
@@ -18,6 +18,7 @@ import { CodeAnalyzerMcpProvider } from '@salesforce/mcp-provider-code-analyzer'
|
|
|
18
18
|
import { LwcExpertsMcpProvider } from '@salesforce/mcp-provider-lwc-experts';
|
|
19
19
|
import { AuraExpertsMcpProvider } from '@salesforce/mcp-provider-aura-experts';
|
|
20
20
|
import { MobileWebMcpProvider } from '@salesforce/mcp-provider-mobile-web';
|
|
21
|
+
import { DevOpsMcpProvider } from '@salesforce/mcp-provider-devops';
|
|
21
22
|
/** -------- ADD McpProvider INSTANCES HERE ------------------------------------------------------------------------- */
|
|
22
23
|
export const MCP_PROVIDER_REGISTRY = [
|
|
23
24
|
new DxCoreMcpProvider(),
|
|
@@ -25,6 +26,7 @@ export const MCP_PROVIDER_REGISTRY = [
|
|
|
25
26
|
new LwcExpertsMcpProvider(),
|
|
26
27
|
new AuraExpertsMcpProvider(),
|
|
27
28
|
new MobileWebMcpProvider(),
|
|
29
|
+
new DevOpsMcpProvider(),
|
|
28
30
|
// Add new instances here
|
|
29
31
|
];
|
|
30
32
|
//# sourceMappingURL=registry.js.map
|
|
@@ -32,7 +32,7 @@ export class EnableToolsMcpTool extends McpTool {
|
|
|
32
32
|
return [Toolset.CORE];
|
|
33
33
|
}
|
|
34
34
|
getName() {
|
|
35
|
-
return '
|
|
35
|
+
return 'enable_tools';
|
|
36
36
|
}
|
|
37
37
|
getConfig() {
|
|
38
38
|
return {
|
|
@@ -40,7 +40,7 @@ export class EnableToolsMcpTool extends McpTool {
|
|
|
40
40
|
description: `Enable one or more of the tools the Salesforce MCP server provides.
|
|
41
41
|
|
|
42
42
|
AGENT INSTRUCTIONS:
|
|
43
|
-
Use
|
|
43
|
+
Use list_tools first to learn what tools are available for enabling.
|
|
44
44
|
Once you have enabled the tool, you MUST invoke that tool to accomplish the user's original request - DO NOT USE A DIFFERENT TOOL OR THE COMMAND LINE.`,
|
|
45
45
|
inputSchema: enableToolsParamsSchema.shape,
|
|
46
46
|
outputSchema: undefined,
|
|
@@ -77,4 +77,4 @@ Once you have enabled the tool, you MUST invoke that tool to accomplish the user
|
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
//# sourceMappingURL=
|
|
80
|
+
//# sourceMappingURL=enable_tools.js.map
|
|
@@ -23,7 +23,7 @@ export class ListToolsMcpTool extends McpTool {
|
|
|
23
23
|
return ReleaseState.GA;
|
|
24
24
|
}
|
|
25
25
|
getName() {
|
|
26
|
-
return '
|
|
26
|
+
return 'list_tools';
|
|
27
27
|
}
|
|
28
28
|
getConfig() {
|
|
29
29
|
return {
|
|
@@ -37,9 +37,9 @@ ONLY use this tool if:
|
|
|
37
37
|
2. You genuinely don't know what tools are available for a specific task
|
|
38
38
|
3. You need to discover new tools for an unfamiliar use case
|
|
39
39
|
|
|
40
|
-
If you find one or more tools you want to enable, call
|
|
40
|
+
If you find one or more tools you want to enable, call enable_tools with all the tool names.
|
|
41
41
|
Once you have enabled a tool, you MUST invoke the tool to accomplish the user's original request - DO NOT USE A DIFFERENT TOOL OR THE COMMAND LINE.
|
|
42
|
-
Once a tool has been enabled, you do not need to call
|
|
42
|
+
Once a tool has been enabled, you do not need to call list_tools again - instead, invoke the desired tool directly.`,
|
|
43
43
|
inputSchema: undefined,
|
|
44
44
|
outputSchema: undefined,
|
|
45
45
|
annotations: {
|
|
@@ -60,4 +60,4 @@ Once a tool has been enabled, you do not need to call sf-list-tools again - inst
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
//# sourceMappingURL=
|
|
63
|
+
//# sourceMappingURL=list_tools.js.map
|
package/lib/utils/auth.js
CHANGED
|
@@ -100,34 +100,22 @@ export async function filterAllowedOrgs(orgs, allowList) {
|
|
|
100
100
|
return false;
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
-
const defaultOrgMaps = {
|
|
104
|
-
[OrgConfigProperties.TARGET_ORG]: new Map(),
|
|
105
|
-
[OrgConfigProperties.TARGET_DEV_HUB]: new Map(),
|
|
106
|
-
};
|
|
107
103
|
// Helper function to get default config for a property
|
|
108
104
|
// Values are cached based on ConfigInfo path after first retrieval
|
|
109
105
|
// This is to prevent manipulation of the config file after server start
|
|
110
106
|
async function getDefaultConfig(property) {
|
|
111
|
-
// If the directory changes, the singleton
|
|
112
|
-
// It continues to use the old local or global config.
|
|
107
|
+
// If the directory changes, the ConfigAggregator singleton does not update.
|
|
108
|
+
// It continues to use the old local or global config instead.
|
|
109
|
+
// We call clearInstance on the singleton to read the new config.
|
|
113
110
|
await ConfigAggregator.clearInstance();
|
|
114
111
|
const aggregator = await ConfigAggregator.create();
|
|
115
112
|
const config = aggregator.getInfo(property);
|
|
116
113
|
const { value, path, key, location } = config;
|
|
117
114
|
if (!value || typeof value !== 'string' || !path)
|
|
118
115
|
return undefined;
|
|
119
|
-
//
|
|
116
|
+
// Return an typed object with only the necessary properties
|
|
120
117
|
// This reduces assertions and lowers context returned to the LLM
|
|
121
|
-
|
|
122
|
-
if (defaultOrgMaps[property].has(path)) {
|
|
123
|
-
// If the cache has the config's path set, use the cached config
|
|
124
|
-
const cachedConfig = defaultOrgMaps[property].get(path);
|
|
125
|
-
return cachedConfig;
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
defaultOrgMaps[property].set(path, typedConfig);
|
|
129
|
-
return typedConfig;
|
|
130
|
-
}
|
|
118
|
+
return { key, location, value, path };
|
|
131
119
|
}
|
|
132
120
|
export async function getDefaultTargetOrg() {
|
|
133
121
|
return getDefaultConfig(OrgConfigProperties.TARGET_ORG);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Toolset } from '@salesforce/mcp-provider-api';
|
|
2
2
|
import { SfMcpServer } from '../sf-mcp-server.js';
|
|
3
3
|
import { Services } from '../services.js';
|
|
4
|
-
export declare function registerToolsets(toolsets: Array<Toolset | 'all'>, useDynamicTools: boolean, allowNonGaTools: boolean, server: SfMcpServer, services: Services): Promise<void>;
|
|
4
|
+
export declare function registerToolsets(toolsets: Array<Toolset | 'all'>, tools: string[], useDynamicTools: boolean, allowNonGaTools: boolean, server: SfMcpServer, services: Services): Promise<void>;
|
|
@@ -13,14 +13,26 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
import { EOL } from 'node:os';
|
|
16
17
|
import { ux } from '@oclif/core';
|
|
17
18
|
import { MCP_PROVIDER_API_VERSION, ReleaseState, Toolset, TOOLSETS, } from '@salesforce/mcp-provider-api';
|
|
18
19
|
import { MCP_PROVIDER_REGISTRY } from '../registry.js';
|
|
19
20
|
import { addTool, isToolRegistered } from '../utils/tools.js';
|
|
20
21
|
import { createDynamicServerTools } from '../main-server-provider.js';
|
|
21
|
-
export async function registerToolsets(toolsets, useDynamicTools, allowNonGaTools, server, services) {
|
|
22
|
+
export async function registerToolsets(toolsets, tools, useDynamicTools, allowNonGaTools, server, services) {
|
|
23
|
+
// If no toolsets, tools, or dynamic tools flag was passed, throw an error
|
|
24
|
+
// NOTE: In the future we will also want to check for Personas here
|
|
25
|
+
if (!toolsets.length && !tools.length && !useDynamicTools) {
|
|
26
|
+
throw new Error('Tool registration error. Start server with one of the following flags: --toolsets, --tools, --dynamic-tools');
|
|
27
|
+
}
|
|
22
28
|
if (useDynamicTools) {
|
|
29
|
+
// If --dynamic-tools flag was passed, register the tools needed to handle dynamic tool registration
|
|
23
30
|
const dynamicTools = createDynamicServerTools(server);
|
|
31
|
+
// This should always be true because using `--dynamic-tools` and `--toolsets` is blocked.
|
|
32
|
+
// If that doesn't change after GA, this can be just `toolsets.push('all')`
|
|
33
|
+
const isAllToolsetEnabled = toolsets.includes('all');
|
|
34
|
+
if (!isAllToolsetEnabled)
|
|
35
|
+
toolsets.push('all');
|
|
24
36
|
ux.stderr('Registering dynamic tools.');
|
|
25
37
|
// eslint-disable-next-line no-await-in-loop
|
|
26
38
|
await registerTools(dynamicTools, server, useDynamicTools, allowNonGaTools);
|
|
@@ -30,16 +42,37 @@ export async function registerToolsets(toolsets, useDynamicTools, allowNonGaTool
|
|
|
30
42
|
}
|
|
31
43
|
const toolsetsToEnable = toolsets.includes('all')
|
|
32
44
|
? new Set(TOOLSETS)
|
|
45
|
+
// CORE toolset is always enabled
|
|
33
46
|
: new Set([Toolset.CORE, ...toolsets]);
|
|
34
|
-
const
|
|
47
|
+
const toolsetRegistry = await createToolRegistryFromProviders(MCP_PROVIDER_REGISTRY, services);
|
|
48
|
+
ux.stderr('REGISTERING TOOLSETS (--toolsets)');
|
|
35
49
|
for (const toolset of TOOLSETS) {
|
|
36
50
|
if (toolsetsToEnable.has(toolset)) {
|
|
37
|
-
ux.stderr(`Registering
|
|
51
|
+
ux.stderr(`Registering toolset: '${toolset}'`);
|
|
38
52
|
// eslint-disable-next-line no-await-in-loop
|
|
39
|
-
await registerTools(
|
|
53
|
+
await registerTools(toolsetRegistry[toolset], server, useDynamicTools, allowNonGaTools);
|
|
40
54
|
}
|
|
41
55
|
else {
|
|
42
|
-
ux.stderr(
|
|
56
|
+
ux.stderr(`!! Skipping toolset: '${toolset}'`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (tools.length > 0) {
|
|
60
|
+
ux.stderr('REGISTERING TOOLS (--tools)');
|
|
61
|
+
// Build an array of available McpTools
|
|
62
|
+
const toolRegistry = Object.values(toolsetRegistry).flat();
|
|
63
|
+
// NOTE: This validation could be removed it we implemented Flags.option
|
|
64
|
+
const existingToolNames = new Set(toolRegistry.map(tool => tool.getName()));
|
|
65
|
+
// Validate that all requested tools exist
|
|
66
|
+
const invalidTools = tools.filter(toolName => !existingToolNames.has(toolName));
|
|
67
|
+
if (invalidTools.length > 0)
|
|
68
|
+
throw new Error(`Invalid tool names provided to --tools: "${invalidTools.join('", "')}"
|
|
69
|
+
Valid tools include:
|
|
70
|
+
- ${Array.from(existingToolNames).join(`${EOL}- `)}`);
|
|
71
|
+
for (const tool of toolRegistry) {
|
|
72
|
+
if (tools.includes(tool.getName())) {
|
|
73
|
+
// eslint-disable-next-line no-await-in-loop
|
|
74
|
+
await registerTools([tool], server, useDynamicTools, allowNonGaTools);
|
|
75
|
+
}
|
|
43
76
|
}
|
|
44
77
|
}
|
|
45
78
|
}
|
|
@@ -61,7 +94,7 @@ async function registerTools(tools, server, useDynamicTools, allowNonGaTools) {
|
|
|
61
94
|
registeredTool.disable();
|
|
62
95
|
}
|
|
63
96
|
else {
|
|
64
|
-
ux.stderr(
|
|
97
|
+
ux.stderr(`-> Registering tool: '${tool.getName()}'`);
|
|
65
98
|
}
|
|
66
99
|
// eslint-disable-next-line no-await-in-loop
|
|
67
100
|
await addTool(registeredTool, tool.getName());
|
|
@@ -91,10 +124,8 @@ async function createToolRegistryFromProviders(providers, services) {
|
|
|
91
124
|
*/
|
|
92
125
|
function validateMcpProviderVersion(provider) {
|
|
93
126
|
if (provider.getVersion().major !== MCP_PROVIDER_API_VERSION.major) {
|
|
94
|
-
throw new Error(`The version '${provider
|
|
95
|
-
|
|
96
|
-
.toString()}' for '${provider.getName()}' is incompatible with this MCP Server.\n` +
|
|
97
|
-
`Expected the major version to be '${MCP_PROVIDER_API_VERSION.major}'.`);
|
|
127
|
+
throw new Error(`The version '${provider.getVersion().toString()}' for '${provider.getName()}' is incompatible with this MCP Server.
|
|
128
|
+
Expected the major version to be '${MCP_PROVIDER_API_VERSION.major}'.`);
|
|
98
129
|
}
|
|
99
130
|
}
|
|
100
131
|
//# sourceMappingURL=registry-utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "MCP Server for interacting with Salesforce instances",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sf-mcp-server": "bin/run.js"
|
|
@@ -22,8 +22,6 @@
|
|
|
22
22
|
"lint-fix": "yarn sf-lint --fix",
|
|
23
23
|
"package": "yarn pack",
|
|
24
24
|
"postpack": "sf-clean --ignore-signing-artifacts",
|
|
25
|
-
"prepack": "sf-prepack",
|
|
26
|
-
"prepare": "sf-install",
|
|
27
25
|
"start": "yarn build && npm link && mcp-inspector sf-mcp-server",
|
|
28
26
|
"test": "wireit",
|
|
29
27
|
"test:only": "wireit"
|
|
@@ -42,18 +40,19 @@
|
|
|
42
40
|
"package.json"
|
|
43
41
|
],
|
|
44
42
|
"dependencies": {
|
|
45
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
46
44
|
"@oclif/core": "^4.5.1",
|
|
47
45
|
"@salesforce/agents": "^0.15.4",
|
|
48
46
|
"@salesforce/apex-node": "^8.2.1",
|
|
49
|
-
"@salesforce/core": "^8.
|
|
47
|
+
"@salesforce/core": "^8.23.1",
|
|
50
48
|
"@salesforce/kit": "^3.1.6",
|
|
51
|
-
"@salesforce/mcp-provider-api": "0.
|
|
52
|
-
"@salesforce/mcp-provider-dx-core": "0.
|
|
53
|
-
"@salesforce/mcp-provider-code-analyzer": "0.0
|
|
54
|
-
"@salesforce/mcp-provider-lwc-experts": "0.
|
|
55
|
-
"@salesforce/mcp-provider-aura-experts": "0.
|
|
56
|
-
"@salesforce/mcp-provider-mobile-web": "0.
|
|
49
|
+
"@salesforce/mcp-provider-api": "0.4.0",
|
|
50
|
+
"@salesforce/mcp-provider-dx-core": "0.3.3",
|
|
51
|
+
"@salesforce/mcp-provider-code-analyzer": "0.1.0",
|
|
52
|
+
"@salesforce/mcp-provider-lwc-experts": "0.5.0",
|
|
53
|
+
"@salesforce/mcp-provider-aura-experts": "0.3.0",
|
|
54
|
+
"@salesforce/mcp-provider-mobile-web": "0.1.1",
|
|
55
|
+
"@salesforce/mcp-provider-devops": "0.1.1",
|
|
57
56
|
"@salesforce/source-deploy-retrieve": "^12.22.0",
|
|
58
57
|
"@salesforce/source-tracking": "^7.4.8",
|
|
59
58
|
"@salesforce/telemetry": "^6.1.0",
|
|
File without changes
|
|
File without changes
|