@salesforce/mcp 0.17.1 → 0.17.2-dev.2
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 -1
- package/lib/index.js +7 -5
- package/lib/main-server-provider.d.ts +7 -0
- package/lib/main-server-provider.js +31 -0
- package/lib/registry.d.ts +3 -29
- package/lib/registry.js +9 -113
- package/lib/scripts/build-index.js +1 -1
- package/lib/services.d.ts +12 -0
- package/lib/services.js +34 -0
- package/lib/sf-mcp-server.d.ts +12 -7
- package/lib/sf-mcp-server.js +6 -21
- package/lib/telemetry.d.ts +2 -1
- package/lib/tools/sf-enable-tools.d.ts +23 -0
- package/lib/tools/sf-enable-tools.js +77 -0
- package/lib/tools/sf-list-tools.d.ts +8 -0
- package/lib/{modules/platform-cli → tools}/sf-list-tools.js +33 -9
- package/lib/tools/sf-suggest-cli-command.d.ts +25 -0
- package/lib/{modules/platform-cli → tools}/sf-suggest-cli-command.js +46 -18
- package/lib/utils/assets.d.ts +21 -0
- package/lib/{assets.js → utils/assets.js} +23 -33
- package/lib/{shared → utils}/auth.d.ts +3 -8
- package/lib/{shared → utils}/auth.js +2 -42
- package/lib/utils/registry-utils.d.ts +4 -0
- package/lib/utils/registry-utils.js +87 -0
- package/lib/{modules/platform-cli/utils → utils}/tools.js +1 -1
- package/lib/utils/types.d.ts +5 -0
- package/package.json +8 -4
- package/README.md +0 -263
- package/lib/assets.d.ts +0 -44
- package/lib/modules/platform-cli/index.d.ts +0 -16
- package/lib/modules/platform-cli/index.js +0 -32
- package/lib/modules/platform-cli/sf-assign-permission-set.d.ts +0 -20
- package/lib/modules/platform-cli/sf-assign-permission-set.js +0 -89
- package/lib/modules/platform-cli/sf-create-org-snapshot.d.ts +0 -23
- package/lib/modules/platform-cli/sf-create-org-snapshot.js +0 -88
- package/lib/modules/platform-cli/sf-create-scratch-org.d.ts +0 -50
- package/lib/modules/platform-cli/sf-create-scratch-org.js +0 -132
- package/lib/modules/platform-cli/sf-delete-org.d.ts +0 -14
- package/lib/modules/platform-cli/sf-delete-org.js +0 -65
- package/lib/modules/platform-cli/sf-deploy-metadata.d.ts +0 -27
- package/lib/modules/platform-cli/sf-deploy-metadata.js +0 -164
- package/lib/modules/platform-cli/sf-enable-tools.d.ts +0 -2
- package/lib/modules/platform-cli/sf-enable-tools.js +0 -42
- package/lib/modules/platform-cli/sf-get-username.d.ts +0 -17
- package/lib/modules/platform-cli/sf-get-username.js +0 -109
- package/lib/modules/platform-cli/sf-list-all-orgs.d.ts +0 -11
- package/lib/modules/platform-cli/sf-list-all-orgs.js +0 -59
- package/lib/modules/platform-cli/sf-list-tools.d.ts +0 -2
- package/lib/modules/platform-cli/sf-org-open.d.ts +0 -17
- package/lib/modules/platform-cli/sf-org-open.js +0 -57
- package/lib/modules/platform-cli/sf-query-org.d.ts +0 -20
- package/lib/modules/platform-cli/sf-query-org.js +0 -66
- package/lib/modules/platform-cli/sf-resume.d.ts +0 -20
- package/lib/modules/platform-cli/sf-resume.js +0 -149
- package/lib/modules/platform-cli/sf-retrieve-metadata.d.ts +0 -2
- package/lib/modules/platform-cli/sf-retrieve-metadata.js +0 -128
- package/lib/modules/platform-cli/sf-suggest-cli-command.d.ts +0 -5
- package/lib/modules/platform-cli/sf-test-agents.d.ts +0 -21
- package/lib/modules/platform-cli/sf-test-agents.js +0 -84
- package/lib/modules/platform-cli/sf-test-apex.d.ts +0 -40
- package/lib/modules/platform-cli/sf-test-apex.js +0 -132
- package/lib/shared/params.d.ts +0 -5
- package/lib/shared/params.js +0 -46
- package/lib/shared/types.d.ts +0 -33
- package/lib/shared/utils.d.ts +0 -11
- package/lib/shared/utils.js +0 -71
- package/lib/tsconfig.tsbuildinfo +0 -1
- /package/lib/{shared → utils}/cache.d.ts +0 -0
- /package/lib/{shared → utils}/cache.js +0 -0
- /package/lib/{shared → utils}/rate-limiter.d.ts +0 -0
- /package/lib/{shared → utils}/rate-limiter.js +0 -0
- /package/lib/{modules/platform-cli/utils → utils}/tools.d.ts +0 -0
- /package/lib/{shared → utils}/types.js +0 -0
|
@@ -14,16 +14,30 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { z } from 'zod';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
const suggestCliCommandParamsSchema = z.object({
|
|
20
|
-
query: z.string().describe('The natural language query to suggest an `sf` command'),
|
|
21
|
-
});
|
|
17
|
+
import { McpTool, Toolset } from '@salesforce/mcp-provider-api';
|
|
18
|
+
import { getAssets } from '../utils/assets.js';
|
|
22
19
|
/**
|
|
23
20
|
* Suggest a Salesforce CLI (sf) command based on user input.
|
|
24
21
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const suggestCliCommandParamsSchema = z.object({
|
|
23
|
+
query: z.string().describe('The natural language query to suggest an `sf` command'),
|
|
24
|
+
});
|
|
25
|
+
export class SuggestCliCommandMcpTool extends McpTool {
|
|
26
|
+
services;
|
|
27
|
+
constructor(services) {
|
|
28
|
+
super();
|
|
29
|
+
this.services = services;
|
|
30
|
+
}
|
|
31
|
+
getToolsets() {
|
|
32
|
+
return [Toolset.CORE];
|
|
33
|
+
}
|
|
34
|
+
getName() {
|
|
35
|
+
return 'sf-suggest-cli-command';
|
|
36
|
+
}
|
|
37
|
+
getConfig() {
|
|
38
|
+
return {
|
|
39
|
+
title: 'Suggest CLI Command',
|
|
40
|
+
description: `Suggests an \`sf\` CLI command based on a natural language query. It finds relevant commands from a local index and uses an LLM to construct the final, precise command to fulfill the user's request.
|
|
27
41
|
|
|
28
42
|
AGENT INSTRUCTIONS:
|
|
29
43
|
Use this tool whenever a user:
|
|
@@ -34,22 +48,28 @@ Use this tool whenever a user:
|
|
|
34
48
|
NEVER use this tool for enabling Salesforce MCP tools (use sf-enable-tools instead).
|
|
35
49
|
NEVER use this tool for listing available Salesforce MCP tools (use sf-list-tools instead).
|
|
36
50
|
NEVER use this tool for understanding the Salesforce MCP server's capabilities.
|
|
37
|
-
NEVER use this tool for understanding the input schema of a Salesforce MCP tool.`,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
NEVER use this tool for understanding the input schema of a Salesforce MCP tool.`,
|
|
52
|
+
inputSchema: suggestCliCommandParamsSchema.shape,
|
|
53
|
+
outputSchema: undefined,
|
|
54
|
+
annotations: {
|
|
55
|
+
readOnlyHint: true,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async exec(input) {
|
|
60
|
+
const assets = await getAssets(this.services.getConfigService().getDataDir(), 'sf-commands.json', 'commands-index.bin');
|
|
41
61
|
// Embed the user query
|
|
42
|
-
const queryEmbedding = await assets.embedder(query, {
|
|
62
|
+
const queryEmbedding = await assets.embedder(input.query, {
|
|
43
63
|
pooling: 'mean',
|
|
44
64
|
normalize: true,
|
|
45
65
|
});
|
|
46
66
|
// Perform Semantic Search (FAISS)
|
|
47
|
-
const searchResults = assets.
|
|
67
|
+
const searchResults = assets.index.search(
|
|
48
68
|
// Convert the embedding tensor data to a flat array of numbers
|
|
49
69
|
Array.from(queryEmbedding.data), 5);
|
|
50
70
|
const topCandidateIds = searchResults.labels.slice(0, 5);
|
|
51
71
|
const contextCommands = topCandidateIds.map((id) => {
|
|
52
|
-
const command = assets.
|
|
72
|
+
const command = assets.data.find((c) => c.id === id);
|
|
53
73
|
// Remove the embedding text to avoid sending it to the LLM
|
|
54
74
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
55
75
|
const { embeddingText, ...commandWithoutEmbeddingText } = command;
|
|
@@ -65,7 +85,7 @@ NEVER use this tool for understanding the input schema of a Salesforce MCP tool.
|
|
|
65
85
|
- Do not use any flags or commands not listed in the reference.
|
|
66
86
|
|
|
67
87
|
User Request:
|
|
68
|
-
"${query}"
|
|
88
|
+
"${input.query}"
|
|
69
89
|
|
|
70
90
|
Command Reference:
|
|
71
91
|
${JSON.stringify(contextCommands, null, 2)}
|
|
@@ -83,7 +103,15 @@ Notes about Flag Properties:
|
|
|
83
103
|
|
|
84
104
|
Synthesize the single "sf" command that best fulfills the user's request.
|
|
85
105
|
`;
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
return {
|
|
107
|
+
isError: false,
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: 'text',
|
|
111
|
+
text: prompt,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
89
117
|
//# sourceMappingURL=sf-suggest-cli-command.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FeatureExtractionPipeline } from '@huggingface/transformers';
|
|
2
|
+
import faiss from 'faiss-node';
|
|
3
|
+
type Assets<T> = {
|
|
4
|
+
data: T;
|
|
5
|
+
embedder: FeatureExtractionPipeline;
|
|
6
|
+
index: faiss.IndexFlatL2;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Conditionally builds or rebuilds a FAISS index based on its existence and age.
|
|
10
|
+
*
|
|
11
|
+
* This function checks if a FAISS index file exists in the specified output directory.
|
|
12
|
+
* If the index exists but is older than one week, it triggers a rebuild. If the index
|
|
13
|
+
* doesn't exist, it initiates the initial build process. The build process can run as a
|
|
14
|
+
* detached child process or in the same process depending on the detached parameter.
|
|
15
|
+
*
|
|
16
|
+
* @param indexPath - The path to the FAISS index file.
|
|
17
|
+
* @param detached - Whether to run the build process detached (default: true)
|
|
18
|
+
*/
|
|
19
|
+
export declare function maybeBuildIndex(indexPath: string, detached?: boolean): Promise<void>;
|
|
20
|
+
export declare function getAssets<T>(dataDir: string, dataPath: string, indexPath: string): Promise<Assets<T>>;
|
|
21
|
+
export {};
|
|
@@ -19,7 +19,6 @@ import { spawn } from 'node:child_process';
|
|
|
19
19
|
import faiss from 'faiss-node';
|
|
20
20
|
import { pipeline } from '@huggingface/transformers';
|
|
21
21
|
import { ux } from '@oclif/core';
|
|
22
|
-
let CACHED_DATA_DIR = null;
|
|
23
22
|
/**
|
|
24
23
|
* Conditionally builds or rebuilds a FAISS index based on its existence and age.
|
|
25
24
|
*
|
|
@@ -28,36 +27,31 @@ let CACHED_DATA_DIR = null;
|
|
|
28
27
|
* doesn't exist, it initiates the initial build process. The build process can run as a
|
|
29
28
|
* detached child process or in the same process depending on the detached parameter.
|
|
30
29
|
*
|
|
31
|
-
* @param
|
|
30
|
+
* @param indexPath - The path to the FAISS index file.
|
|
32
31
|
* @param detached - Whether to run the build process detached (default: true)
|
|
33
|
-
*
|
|
34
|
-
* @remarks
|
|
35
|
-
* - Sets the global CACHED_DATA_DIR variable to the provided outputDir. This is used to locate the index file.
|
|
36
32
|
*/
|
|
37
|
-
export async function maybeBuildIndex(
|
|
38
|
-
CACHED_DATA_DIR = outputDir;
|
|
39
|
-
const faissIndexPath = join(outputDir, 'faiss-index.bin');
|
|
33
|
+
export async function maybeBuildIndex(indexPath, detached = true) {
|
|
40
34
|
try {
|
|
41
|
-
const stats = fs.statSync(
|
|
35
|
+
const stats = fs.statSync(indexPath);
|
|
42
36
|
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
43
|
-
ux.stderr(`Checking FAISS index in ${
|
|
37
|
+
ux.stderr(`Checking FAISS index in ${indexPath}...`);
|
|
44
38
|
ux.stderr(`Last modified: ${stats.mtime.toString()}`);
|
|
45
39
|
if (stats.mtime < oneWeekAgo) {
|
|
46
|
-
ux.stderr(`FAISS index is more than 1 week old - rebuilding in ${
|
|
47
|
-
await spawnBuildScript(
|
|
40
|
+
ux.stderr(`FAISS index is more than 1 week old - rebuilding in ${indexPath}...`);
|
|
41
|
+
await spawnBuildScript(indexPath, detached);
|
|
48
42
|
}
|
|
49
43
|
else {
|
|
50
|
-
ux.stderr(`FAISS index is up to date in ${
|
|
44
|
+
ux.stderr(`FAISS index is up to date in ${indexPath}. No rebuild needed.`);
|
|
51
45
|
}
|
|
52
46
|
}
|
|
53
47
|
catch (error) {
|
|
54
48
|
// File doesn't exist, so build the index
|
|
55
|
-
ux.stderr(`Building FAISS index in ${
|
|
56
|
-
await spawnBuildScript(
|
|
49
|
+
ux.stderr(`Building FAISS index in ${indexPath}...`);
|
|
50
|
+
await spawnBuildScript(indexPath, detached);
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
function spawnBuildScript(outputDir, detached) {
|
|
60
|
-
const scriptPath = resolve(import.meta.dirname, 'scripts', 'build-index.js');
|
|
54
|
+
const scriptPath = resolve(import.meta.dirname, '..', 'scripts', 'build-index.js');
|
|
61
55
|
const args = [scriptPath, outputDir];
|
|
62
56
|
if (detached) {
|
|
63
57
|
spawn('node', args, {
|
|
@@ -85,37 +79,33 @@ function spawnBuildScript(outputDir, detached) {
|
|
|
85
79
|
});
|
|
86
80
|
}
|
|
87
81
|
}
|
|
88
|
-
export async function getAssets() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
82
|
+
export async function getAssets(dataDir, dataPath, indexPath) {
|
|
83
|
+
const fullDataPath = join(dataDir, dataPath);
|
|
84
|
+
const fullIndexPath = join(dataDir, indexPath);
|
|
92
85
|
// Ensure the index is built or rebuilt if necessary
|
|
93
|
-
await maybeBuildIndex(
|
|
94
|
-
const commandsPath = join(CACHED_DATA_DIR, 'sf-commands.json');
|
|
95
|
-
const faissIndexPath = join(CACHED_DATA_DIR, 'faiss-index.bin');
|
|
86
|
+
await maybeBuildIndex(fullIndexPath, false);
|
|
96
87
|
try {
|
|
97
|
-
await fs.promises.access(
|
|
88
|
+
await fs.promises.access(fullDataPath);
|
|
98
89
|
}
|
|
99
90
|
catch {
|
|
100
|
-
throw new Error(`Commands file not found at ${
|
|
91
|
+
throw new Error(`Commands file not found at ${fullDataPath}. Please run maybeBuildIndex to build the index.`);
|
|
101
92
|
}
|
|
102
93
|
try {
|
|
103
|
-
await fs.promises.access(
|
|
94
|
+
await fs.promises.access(fullIndexPath);
|
|
104
95
|
}
|
|
105
96
|
catch {
|
|
106
|
-
throw new Error(`FAISS index not found at ${
|
|
97
|
+
throw new Error(`FAISS index not found at ${fullIndexPath}. Please run maybeBuildIndex to build the index.`);
|
|
107
98
|
}
|
|
108
99
|
try {
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
100
|
+
const dataRaw = await fs.promises.readFile(fullDataPath, 'utf-8');
|
|
101
|
+
const data = JSON.parse(dataRaw);
|
|
102
|
+
const index = faiss.IndexFlatL2.read(fullIndexPath);
|
|
112
103
|
const embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {
|
|
113
104
|
dtype: 'fp32',
|
|
114
105
|
});
|
|
115
106
|
return {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
faissIndex,
|
|
107
|
+
data,
|
|
108
|
+
index,
|
|
119
109
|
embedder,
|
|
120
110
|
};
|
|
121
111
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Connection, type OrgAuthorization } from '@salesforce/core';
|
|
2
|
-
import { type
|
|
2
|
+
import { type OrgConfigInfo, type SanitizedOrgAuthorization } from '@salesforce/mcp-provider-api';
|
|
3
3
|
/**
|
|
4
4
|
* Sanitizes org authorization data by filtering out sensitive fields
|
|
5
5
|
*
|
|
@@ -7,14 +7,9 @@ import { type ConfigInfoWithCache, type SanitizedOrgAuthorization } from './type
|
|
|
7
7
|
* @returns Array of sanitized org authorization objects with only allowed fields
|
|
8
8
|
*/
|
|
9
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
10
|
export declare function getConnection(username: string): Promise<Connection>;
|
|
16
11
|
export declare function findOrgByUsernameOrAlias(allOrgs: SanitizedOrgAuthorization[], usernameOrAlias: string): SanitizedOrgAuthorization | undefined;
|
|
17
12
|
export declare function getAllAllowedOrgs(): Promise<SanitizedOrgAuthorization[]>;
|
|
18
13
|
export declare function filterAllowedOrgs(orgs: SanitizedOrgAuthorization[], allowList: Set<string>): Promise<SanitizedOrgAuthorization[]>;
|
|
19
|
-
export declare function getDefaultTargetOrg(): Promise<
|
|
20
|
-
export declare function getDefaultTargetDevHub(): Promise<
|
|
14
|
+
export declare function getDefaultTargetOrg(): Promise<OrgConfigInfo | undefined>;
|
|
15
|
+
export declare function getDefaultTargetDevHub(): Promise<OrgConfigInfo | undefined>;
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
/* eslint-disable no-console */
|
|
17
16
|
import { AuthInfo, Connection, ConfigAggregator, OrgConfigProperties } from '@salesforce/core';
|
|
18
17
|
import Cache from './cache.js';
|
|
19
18
|
/**
|
|
@@ -36,41 +35,6 @@ export function sanitizeOrgs(orgs) {
|
|
|
36
35
|
isExpired: org.isExpired,
|
|
37
36
|
}));
|
|
38
37
|
}
|
|
39
|
-
export async function suggestUsername() {
|
|
40
|
-
let reasoning;
|
|
41
|
-
let suggestedUsername;
|
|
42
|
-
let aliasForReference;
|
|
43
|
-
const allAllowedOrgs = await getAllAllowedOrgs();
|
|
44
|
-
const defaultTargetOrg = await getDefaultTargetOrg();
|
|
45
|
-
const defaultTargetDevHub = await getDefaultTargetDevHub();
|
|
46
|
-
const targetOrgLocation = defaultTargetOrg?.location ? `(${defaultTargetOrg.location}) ` : '';
|
|
47
|
-
const targetDevHubLocation = defaultTargetDevHub?.location ? `(${defaultTargetDevHub.location}) ` : '';
|
|
48
|
-
if (allAllowedOrgs.length === 1) {
|
|
49
|
-
suggestedUsername = allAllowedOrgs[0].username;
|
|
50
|
-
aliasForReference = allAllowedOrgs[0].aliases?.[0];
|
|
51
|
-
reasoning = 'it was the only org found in the MCP Servers allowlisted orgs';
|
|
52
|
-
}
|
|
53
|
-
else if (defaultTargetOrg?.value) {
|
|
54
|
-
const foundOrg = findOrgByUsernameOrAlias(allAllowedOrgs, defaultTargetOrg.value);
|
|
55
|
-
suggestedUsername = foundOrg?.username;
|
|
56
|
-
aliasForReference = foundOrg?.aliases?.[0];
|
|
57
|
-
reasoning = `it is the default ${targetOrgLocation}target org`;
|
|
58
|
-
}
|
|
59
|
-
else if (defaultTargetDevHub?.value) {
|
|
60
|
-
const foundOrg = findOrgByUsernameOrAlias(allAllowedOrgs, defaultTargetDevHub.value);
|
|
61
|
-
suggestedUsername = foundOrg?.username;
|
|
62
|
-
aliasForReference = foundOrg?.aliases?.[0];
|
|
63
|
-
reasoning = `it is the default ${targetDevHubLocation}dev hub org`;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
reasoning = 'Error: no org was inferred. Ask the user to specify one';
|
|
67
|
-
}
|
|
68
|
-
return {
|
|
69
|
-
suggestedUsername,
|
|
70
|
-
aliasForReference,
|
|
71
|
-
reasoning,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
38
|
// This function is the main entry point for Tools to get an allowlisted Connection
|
|
75
39
|
export async function getConnection(username) {
|
|
76
40
|
// We get all allowed orgs each call in case the directory has changed (default configs)
|
|
@@ -91,11 +55,7 @@ export function findOrgByUsernameOrAlias(allOrgs, usernameOrAlias) {
|
|
|
91
55
|
});
|
|
92
56
|
}
|
|
93
57
|
export async function getAllAllowedOrgs() {
|
|
94
|
-
|
|
95
|
-
const url = new URL(import.meta.url);
|
|
96
|
-
const params = url.searchParams.get('orgs');
|
|
97
|
-
const paramOrg = params ? params : undefined;
|
|
98
|
-
const orgAllowList = paramOrg ? new Set([paramOrg]) : (await Cache.safeGet('allowedOrgs')) ?? new Set();
|
|
58
|
+
const orgAllowList = (await Cache.safeGet('allowedOrgs')) ?? new Set();
|
|
99
59
|
// Get all orgs on the user's machine
|
|
100
60
|
const allOrgs = await AuthInfo.listAllAuthorizations();
|
|
101
61
|
// Sanitize the orgs to remove sensitive data
|
|
@@ -162,7 +122,7 @@ async function getDefaultConfig(property) {
|
|
|
162
122
|
if (defaultOrgMaps[property].has(path)) {
|
|
163
123
|
// If the cache has the config's path set, use the cached config
|
|
164
124
|
const cachedConfig = defaultOrgMaps[property].get(path);
|
|
165
|
-
return
|
|
125
|
+
return cachedConfig;
|
|
166
126
|
}
|
|
167
127
|
else {
|
|
168
128
|
defaultOrgMaps[property].set(path, typedConfig);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Toolset } from '@salesforce/mcp-provider-api';
|
|
2
|
+
import { SfMcpServer } from '../sf-mcp-server.js';
|
|
3
|
+
import { Services } from '../services.js';
|
|
4
|
+
export declare function registerToolsets(toolsets: Array<Toolset | 'all'>, useDynamicTools: boolean, server: SfMcpServer, services: Services): Promise<void>;
|
|
@@ -0,0 +1,87 @@
|
|
|
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 { ux } from '@oclif/core';
|
|
17
|
+
import { MCP_PROVIDER_API_VERSION, Toolset, TOOLSETS, } from '@salesforce/mcp-provider-api';
|
|
18
|
+
import { MCP_PROVIDER_REGISTRY } from '../registry.js';
|
|
19
|
+
import { addTool } from '../utils/tools.js';
|
|
20
|
+
import { createDynamicServerTools } from '../main-server-provider.js';
|
|
21
|
+
export async function registerToolsets(toolsets, useDynamicTools, server, services) {
|
|
22
|
+
if (useDynamicTools) {
|
|
23
|
+
const dynamicTools = createDynamicServerTools(server);
|
|
24
|
+
ux.stderr('Registering dynamic tools');
|
|
25
|
+
// eslint-disable-next-line no-await-in-loop
|
|
26
|
+
await registerTools(dynamicTools, server, useDynamicTools);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
ux.stderr('Skipping registration of dynamic tools');
|
|
30
|
+
}
|
|
31
|
+
const toolsetsToEnable = toolsets.includes('all')
|
|
32
|
+
? new Set(TOOLSETS.filter((ts) => ts !== Toolset.EXPERIMENTAL))
|
|
33
|
+
: new Set([Toolset.CORE, ...toolsets]);
|
|
34
|
+
const newToolRegistry = await createToolRegistryFromProviders(MCP_PROVIDER_REGISTRY, services);
|
|
35
|
+
for (const toolset of TOOLSETS) {
|
|
36
|
+
if (toolsetsToEnable.has(toolset)) {
|
|
37
|
+
ux.stderr(`Registering ${toolset} tools`);
|
|
38
|
+
// eslint-disable-next-line no-await-in-loop
|
|
39
|
+
await registerTools(newToolRegistry[toolset], server, useDynamicTools);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
ux.stderr(`Skipping registration of ${toolset} tools`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function registerTools(tools, server, useDynamicTools) {
|
|
47
|
+
for (const tool of tools) {
|
|
48
|
+
const registeredTool = server.registerTool(tool.getName(), tool.getConfig(), (...args) => tool.exec(...args));
|
|
49
|
+
const toolsets = tool.getToolsets();
|
|
50
|
+
if (useDynamicTools && !toolsets.includes(Toolset.CORE)) {
|
|
51
|
+
registeredTool.disable();
|
|
52
|
+
}
|
|
53
|
+
// eslint-disable-next-line no-await-in-loop
|
|
54
|
+
await addTool(registeredTool, tool.getName());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function createToolRegistryFromProviders(providers, services) {
|
|
58
|
+
// Initialize an empty registry
|
|
59
|
+
const registry = Object.fromEntries(Object.values(Toolset).map((key) => [key, []]));
|
|
60
|
+
// Avoid calling await in a loop by first getting all the promises
|
|
61
|
+
const toolPromises = [];
|
|
62
|
+
for (const provider of providers) {
|
|
63
|
+
validateMcpProviderVersion(provider);
|
|
64
|
+
const toolsPromise = provider.provideTools(services);
|
|
65
|
+
toolPromises.push(toolsPromise);
|
|
66
|
+
}
|
|
67
|
+
// Get all the tools from the promises and then add them to the registry
|
|
68
|
+
const tools = (await Promise.all(toolPromises)).flat();
|
|
69
|
+
for (const tool of tools) {
|
|
70
|
+
for (const toolset of tool.getToolsets()) {
|
|
71
|
+
registry[toolset].push(tool);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return registry;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validation function to confirm that providers are at the expected major version.
|
|
78
|
+
*/
|
|
79
|
+
function validateMcpProviderVersion(provider) {
|
|
80
|
+
if (provider.getVersion().major !== MCP_PROVIDER_API_VERSION.major) {
|
|
81
|
+
throw new Error(`The version '${provider
|
|
82
|
+
.getVersion()
|
|
83
|
+
.toString()}' for '${provider.getName()}' is incompatible with this MCP Server.\n` +
|
|
84
|
+
`Expected the major version to be '${MCP_PROVIDER_API_VERSION.major}'.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=registry-utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/mcp",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2-dev.2",
|
|
4
4
|
"description": "MCP Server for interacting with Salesforce instances",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sf-mcp-server": "bin/run.js"
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"link-check": "wireit",
|
|
21
21
|
"lint": "wireit",
|
|
22
22
|
"lint-fix": "yarn sf-lint --fix",
|
|
23
|
+
"package": "yarn pack",
|
|
23
24
|
"postpack": "sf-clean --ignore-signing-artifacts",
|
|
24
25
|
"prepack": "sf-prepack",
|
|
25
26
|
"prepare": "sf-install",
|
|
@@ -36,23 +37,26 @@
|
|
|
36
37
|
"bin",
|
|
37
38
|
"lib",
|
|
38
39
|
"!lib/**/*.map",
|
|
39
|
-
"messages"
|
|
40
|
+
"messages",
|
|
41
|
+
"LICENSE.txt",
|
|
42
|
+
"package.json"
|
|
40
43
|
],
|
|
41
44
|
"dependencies": {
|
|
42
45
|
"@huggingface/transformers": "^3.7.0",
|
|
43
|
-
"@jsforce/jsforce-node": "^3.9.4",
|
|
44
46
|
"@modelcontextprotocol/sdk": "^1.16.0",
|
|
45
47
|
"@oclif/core": "^4.5.1",
|
|
46
48
|
"@salesforce/agents": "^0.15.4",
|
|
47
49
|
"@salesforce/apex-node": "^8.2.1",
|
|
48
50
|
"@salesforce/core": "^8.18.0",
|
|
49
51
|
"@salesforce/kit": "^3.1.6",
|
|
52
|
+
"@salesforce/mcp-provider-api": "^0.1.0",
|
|
53
|
+
"@salesforce/mcp-provider-dx-core": "^0.1.2",
|
|
54
|
+
"@salesforce/mcp-provider-code-analyzer": "^0.0.1",
|
|
50
55
|
"@salesforce/source-deploy-retrieve": "^12.22.0",
|
|
51
56
|
"@salesforce/source-tracking": "^7.4.8",
|
|
52
57
|
"@salesforce/telemetry": "^6.1.0",
|
|
53
58
|
"@salesforce/ts-types": "^2.0.11",
|
|
54
59
|
"faiss-node": "^0.5.1",
|
|
55
|
-
"open": "^10.1.2",
|
|
56
60
|
"zod": "^3.25.76"
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|