@salesforce/b2c-dx-mcp 1.0.14 → 1.1.1
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/README.md +1 -1
- package/dist/commands/mcp.d.ts +5 -0
- package/dist/commands/mcp.js +16 -6
- package/dist/registry.d.ts +3 -21
- package/dist/registry.js +13 -3
- package/dist/server-context.d.ts +27 -0
- package/dist/server-context.js +37 -0
- package/dist/server.js +7 -2
- package/dist/services.d.ts +9 -0
- package/dist/services.js +10 -0
- package/dist/tools/adapter.d.ts +7 -1
- package/dist/tools/adapter.js +2 -1
- package/dist/tools/diagnostics/debug-capture-at-breakpoint.d.ts +4 -0
- package/dist/tools/diagnostics/debug-capture-at-breakpoint.js +118 -0
- package/dist/tools/diagnostics/debug-continue.d.ts +4 -0
- package/dist/tools/diagnostics/debug-continue.js +27 -0
- package/dist/tools/diagnostics/debug-end-session.d.ts +4 -0
- package/dist/tools/diagnostics/debug-end-session.js +38 -0
- package/dist/tools/diagnostics/debug-evaluate.d.ts +4 -0
- package/dist/tools/diagnostics/debug-evaluate.js +30 -0
- package/dist/tools/diagnostics/debug-get-stack.d.ts +4 -0
- package/dist/tools/diagnostics/debug-get-stack.js +31 -0
- package/dist/tools/diagnostics/debug-get-variables.d.ts +4 -0
- package/dist/tools/diagnostics/debug-get-variables.js +45 -0
- package/dist/tools/diagnostics/debug-list-sessions.d.ts +4 -0
- package/dist/tools/diagnostics/debug-list-sessions.js +37 -0
- package/dist/tools/diagnostics/debug-set-breakpoints.d.ts +4 -0
- package/dist/tools/diagnostics/debug-set-breakpoints.js +56 -0
- package/dist/tools/diagnostics/debug-start-session.d.ts +4 -0
- package/dist/tools/diagnostics/debug-start-session.js +74 -0
- package/dist/tools/diagnostics/debug-step.d.ts +4 -0
- package/dist/tools/diagnostics/debug-step.js +38 -0
- package/dist/tools/diagnostics/debug-wait-for-stop.d.ts +4 -0
- package/dist/tools/diagnostics/debug-wait-for-stop.js +45 -0
- package/dist/tools/diagnostics/index.d.ts +4 -0
- package/dist/tools/diagnostics/index.js +32 -0
- package/dist/tools/diagnostics/session-registry.d.ts +56 -0
- package/dist/tools/diagnostics/session-registry.js +142 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/oclif.manifest.json +29 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Full documentation: [https://salesforcecommercecloud.github.io/b2c-developer-too
|
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
11
|
-
**Prerequisites:** Node.js 22.
|
|
11
|
+
**Prerequisites:** Node.js 22.16.0 or higher, MCP client (Cursor, Claude Code, GitHub Copilot, or compatible)
|
|
12
12
|
|
|
13
13
|
**Cursor** (project-level configuration - recommended):
|
|
14
14
|
|
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -39,6 +39,9 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
|
|
|
39
39
|
'auth-methods': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
40
40
|
'user-auth': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
41
41
|
'account-manager-host': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
42
|
+
'jwt-cert': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
43
|
+
'jwt-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
44
|
+
'jwt-passphrase': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
42
45
|
'log-level': import("@oclif/core/interfaces").OptionFlag<"trace" | "debug" | "info" | "warn" | "error" | "silent" | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
43
46
|
debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
44
47
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -56,6 +59,8 @@ export default class McpServerCommand extends BaseCommand<typeof McpServerComman
|
|
|
56
59
|
'cloud-origin': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
57
60
|
'credentials-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
58
61
|
};
|
|
62
|
+
/** Server-scoped persistent state (debug sessions, log watches, etc.) */
|
|
63
|
+
private serverContext?;
|
|
59
64
|
/** Signal that triggered shutdown (if any) - used to exit process after finally() */
|
|
60
65
|
private shutdownSignal?;
|
|
61
66
|
/** Promise that resolves when stdin closes (MCP client disconnects) */
|
package/dist/commands/mcp.js
CHANGED
|
@@ -137,6 +137,7 @@ import { BaseCommand, MrtCommand, InstanceCommand, loadConfig, extractInstanceFl
|
|
|
137
137
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
138
138
|
import { B2CDxMcpServer } from '../server.js';
|
|
139
139
|
import { Services } from '../services.js';
|
|
140
|
+
import { ServerContext } from '../server-context.js';
|
|
140
141
|
import { registerToolsets } from '../registry.js';
|
|
141
142
|
import { TOOLSETS } from '../utils/index.js';
|
|
142
143
|
/**
|
|
@@ -199,6 +200,8 @@ export default class McpServerCommand extends BaseCommand {
|
|
|
199
200
|
default: false,
|
|
200
201
|
}),
|
|
201
202
|
};
|
|
203
|
+
/** Server-scoped persistent state (debug sessions, log watches, etc.) */
|
|
204
|
+
serverContext;
|
|
202
205
|
/** Signal that triggered shutdown (if any) - used to exit process after finally() */
|
|
203
206
|
shutdownSignal;
|
|
204
207
|
/** Promise that resolves when stdin closes (MCP client disconnects) */
|
|
@@ -315,9 +318,11 @@ export default class McpServerCommand extends BaseCommand {
|
|
|
315
318
|
},
|
|
316
319
|
telemetry: this.telemetry,
|
|
317
320
|
});
|
|
321
|
+
// Create server context for persistent state (debug sessions, log watches)
|
|
322
|
+
this.serverContext = new ServerContext();
|
|
318
323
|
// Register toolsets with loader function that loads config and creates Services on each tool call
|
|
319
324
|
// This allows tools to pick up changes to config files (dw.json, ~/.mobify) between invocations
|
|
320
|
-
await registerToolsets(startupFlags, server, this.loadServices.bind(this));
|
|
325
|
+
await registerToolsets(startupFlags, server, this.loadServices.bind(this), this.serverContext);
|
|
321
326
|
// Connect to stdio transport
|
|
322
327
|
const transport = new StdioServerTransport();
|
|
323
328
|
await server.connect(transport);
|
|
@@ -326,11 +331,16 @@ export default class McpServerCommand extends BaseCommand {
|
|
|
326
331
|
this.stdinClosePromise = new Promise((resolve) => {
|
|
327
332
|
const sendStopAndResolve = (signal) => {
|
|
328
333
|
this.shutdownSignal = signal;
|
|
329
|
-
this.
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
+
const cleanup = this.serverContext?.destroyAll() ?? Promise.resolve();
|
|
335
|
+
cleanup
|
|
336
|
+
.catch(() => { })
|
|
337
|
+
.then(() => {
|
|
338
|
+
this.telemetry?.sendEvent('SERVER_STOPPED', { signal });
|
|
339
|
+
const flushPromise = this.telemetry?.flush() ?? Promise.resolve();
|
|
340
|
+
return flushPromise;
|
|
341
|
+
})
|
|
342
|
+
.then(() => resolve())
|
|
343
|
+
.catch(() => resolve());
|
|
334
344
|
};
|
|
335
345
|
// Handle stdin close (MCP client disconnects normally)
|
|
336
346
|
process.stdin.on('close', () => sendStopAndResolve('stdin_close'));
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpTool, Toolset, StartupFlags } from './utils/index.js';
|
|
2
2
|
import type { B2CDxMcpServer } from './server.js';
|
|
3
3
|
import type { Services } from './services.js';
|
|
4
|
+
import type { ServerContext } from './server-context.js';
|
|
4
5
|
/**
|
|
5
6
|
* Registry of tools organized by toolset.
|
|
6
7
|
* Tools can belong to multiple toolsets via their `toolsets` array.
|
|
@@ -14,24 +15,5 @@ export type ToolRegistry = Record<Toolset, McpTool[]>;
|
|
|
14
15
|
* @param loadServices - Function that loads configuration and returns Services instance
|
|
15
16
|
* @returns Complete tool registry
|
|
16
17
|
*/
|
|
17
|
-
export declare function createToolRegistry(loadServices: () => Promise<Services> | Services): ToolRegistry;
|
|
18
|
-
|
|
19
|
-
* Register tools with the MCP server based on startup flags.
|
|
20
|
-
*
|
|
21
|
-
* Tool selection logic:
|
|
22
|
-
* 1. If no valid tools result from --toolsets and --tools, perform auto-discovery
|
|
23
|
-
* 2. Start with all tools from --toolsets (or auto-discovered toolsets)
|
|
24
|
-
* 3. Add individual tools from --tools (can be from any toolset)
|
|
25
|
-
*
|
|
26
|
-
* Auto-discovery always enables at least the BASE_TOOLSET (SCAPI), even if no
|
|
27
|
-
* project types are detected in the workspace.
|
|
28
|
-
*
|
|
29
|
-
* Example:
|
|
30
|
-
* --toolsets STOREFRONTNEXT,MRT --tools cartridge_deploy
|
|
31
|
-
* This enables STOREFRONTNEXT and MRT toolsets, plus adds cartridge_deploy from CARTRIDGES.
|
|
32
|
-
*
|
|
33
|
-
* @param flags - Startup flags from CLI
|
|
34
|
-
* @param server - B2CDxMcpServer instance
|
|
35
|
-
* @param loadServices - Function that loads configuration and returns Services instance
|
|
36
|
-
*/
|
|
37
|
-
export declare function registerToolsets(flags: StartupFlags, server: B2CDxMcpServer, loadServices: () => Promise<Services> | Services): Promise<void>;
|
|
18
|
+
export declare function createToolRegistry(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): ToolRegistry;
|
|
19
|
+
export declare function registerToolsets(flags: StartupFlags, server: B2CDxMcpServer, loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): Promise<void>;
|
package/dist/registry.js
CHANGED
|
@@ -7,6 +7,7 @@ import { getLogger } from '@salesforce/b2c-tooling-sdk/logging';
|
|
|
7
7
|
import { detectWorkspaceType } from '@salesforce/b2c-tooling-sdk/discovery';
|
|
8
8
|
import { ALL_TOOLSETS, TOOLSETS, VALID_TOOLSET_NAMES } from './utils/index.js';
|
|
9
9
|
import { createCartridgesTools } from './tools/cartridges/index.js';
|
|
10
|
+
import { createDiagnosticsTools } from './tools/diagnostics/index.js';
|
|
10
11
|
import { createMrtTools } from './tools/mrt/index.js';
|
|
11
12
|
import { createPwav3Tools } from './tools/pwav3/index.js';
|
|
12
13
|
import { createScapiTools } from './tools/scapi/index.js';
|
|
@@ -61,7 +62,7 @@ function getToolsetsForProjectTypes(projectTypes) {
|
|
|
61
62
|
* @param loadServices - Function that loads configuration and returns Services instance
|
|
62
63
|
* @returns Complete tool registry
|
|
63
64
|
*/
|
|
64
|
-
export function createToolRegistry(loadServices) {
|
|
65
|
+
export function createToolRegistry(loadServices, serverContext) {
|
|
65
66
|
const registry = {
|
|
66
67
|
CARTRIDGES: [],
|
|
67
68
|
MRT: [],
|
|
@@ -72,6 +73,7 @@ export function createToolRegistry(loadServices) {
|
|
|
72
73
|
// Collect all tools from all factories
|
|
73
74
|
const allTools = [
|
|
74
75
|
...createCartridgesTools(loadServices),
|
|
76
|
+
...createDiagnosticsTools(loadServices, serverContext),
|
|
75
77
|
...createMrtTools(loadServices),
|
|
76
78
|
...createPwav3Tools(loadServices),
|
|
77
79
|
...createScapiTools(loadServices),
|
|
@@ -134,13 +136,21 @@ async function performAutoDiscovery(flags, reason) {
|
|
|
134
136
|
* @param server - B2CDxMcpServer instance
|
|
135
137
|
* @param loadServices - Function that loads configuration and returns Services instance
|
|
136
138
|
*/
|
|
137
|
-
|
|
139
|
+
// Guards against accidental double-registration. The MCP SDK throws on duplicate
|
|
140
|
+
// `addTool` names, but tracking servers we've already populated lets callers fail
|
|
141
|
+
// fast with an explicit message instead of a cryptic SDK error.
|
|
142
|
+
const REGISTERED_SERVERS = new WeakSet();
|
|
143
|
+
export async function registerToolsets(flags, server, loadServices, serverContext) {
|
|
144
|
+
if (REGISTERED_SERVERS.has(server)) {
|
|
145
|
+
throw new Error('registerToolsets() was called more than once for the same server instance');
|
|
146
|
+
}
|
|
147
|
+
REGISTERED_SERVERS.add(server);
|
|
138
148
|
const toolsets = flags.toolsets ?? [];
|
|
139
149
|
const individualTools = flags.tools ?? [];
|
|
140
150
|
const allowNonGaTools = flags.allowNonGaTools ?? false;
|
|
141
151
|
const logger = getLogger();
|
|
142
152
|
// Create the tool registry (all available tools)
|
|
143
|
-
const toolRegistry = createToolRegistry(loadServices);
|
|
153
|
+
const toolRegistry = createToolRegistry(loadServices, serverContext);
|
|
144
154
|
// Build flat list of all tools for lookup
|
|
145
155
|
const allTools = Object.values(toolRegistry).flat();
|
|
146
156
|
const allToolsByName = new Map(allTools.map((tool) => [tool.name, tool]));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { DebugSessionRegistry } from './tools/diagnostics/session-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Server-scoped persistent state that lives for the lifetime of the MCP
|
|
4
|
+
* server process. Holds registries for stateful resources (debug sessions,
|
|
5
|
+
* future log watches) that need to span multiple tool invocations.
|
|
6
|
+
*
|
|
7
|
+
* ## Multi-agent / shared-state caveats
|
|
8
|
+
*
|
|
9
|
+
* One `ServerContext` is created per MCP server process. With the default
|
|
10
|
+
* stdio transport, each MCP client connection spawns its own server
|
|
11
|
+
* subprocess, so this state is naturally isolated per client/agent.
|
|
12
|
+
*
|
|
13
|
+
* If this server is ever wired up to a shared transport (e.g. HTTP with
|
|
14
|
+
* multiple connected clients), `ServerContext` state would be shared
|
|
15
|
+
* across all connected clients. Sub-agents that run within the same MCP
|
|
16
|
+
* client would also share the same context. Tools that mutate registries
|
|
17
|
+
* (like the debug tools) should not assume single-tenant access.
|
|
18
|
+
*
|
|
19
|
+
* The debug registry already handles concurrent sessions via session IDs
|
|
20
|
+
* and host:client_id pairs, so multi-agent use is functional but agents
|
|
21
|
+
* may see each other's sessions via `debug_list_sessions`.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ServerContext {
|
|
24
|
+
readonly debugSessions: DebugSessionRegistry;
|
|
25
|
+
constructor();
|
|
26
|
+
destroyAll(): Promise<void>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { DebugSessionRegistry } from './tools/diagnostics/session-registry.js';
|
|
7
|
+
/**
|
|
8
|
+
* Server-scoped persistent state that lives for the lifetime of the MCP
|
|
9
|
+
* server process. Holds registries for stateful resources (debug sessions,
|
|
10
|
+
* future log watches) that need to span multiple tool invocations.
|
|
11
|
+
*
|
|
12
|
+
* ## Multi-agent / shared-state caveats
|
|
13
|
+
*
|
|
14
|
+
* One `ServerContext` is created per MCP server process. With the default
|
|
15
|
+
* stdio transport, each MCP client connection spawns its own server
|
|
16
|
+
* subprocess, so this state is naturally isolated per client/agent.
|
|
17
|
+
*
|
|
18
|
+
* If this server is ever wired up to a shared transport (e.g. HTTP with
|
|
19
|
+
* multiple connected clients), `ServerContext` state would be shared
|
|
20
|
+
* across all connected clients. Sub-agents that run within the same MCP
|
|
21
|
+
* client would also share the same context. Tools that mutate registries
|
|
22
|
+
* (like the debug tools) should not assume single-tenant access.
|
|
23
|
+
*
|
|
24
|
+
* The debug registry already handles concurrent sessions via session IDs
|
|
25
|
+
* and host:client_id pairs, so multi-agent use is functional but agents
|
|
26
|
+
* may see each other's sessions via `debug_list_sessions`.
|
|
27
|
+
*/
|
|
28
|
+
export class ServerContext {
|
|
29
|
+
debugSessions;
|
|
30
|
+
constructor() {
|
|
31
|
+
this.debugSessions = new DebugSessionRegistry();
|
|
32
|
+
}
|
|
33
|
+
async destroyAll() {
|
|
34
|
+
await this.debugSessions.destroyAll();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=server-context.js.map
|
package/dist/server.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
|
+
import { getLogger } from '@salesforce/b2c-tooling-sdk/logging';
|
|
7
8
|
/**
|
|
8
9
|
* A server implementation that extends the base MCP server.
|
|
9
10
|
*
|
|
@@ -53,7 +54,9 @@ export class B2CDxMcpServer extends McpServer {
|
|
|
53
54
|
runTimeMs,
|
|
54
55
|
isError: result.isError ?? false,
|
|
55
56
|
})
|
|
56
|
-
.catch(() => {
|
|
57
|
+
.catch((error_) => {
|
|
58
|
+
getLogger().debug({ err: error_, toolName: name }, '[mcp] telemetry sendEventAndFlush failed');
|
|
59
|
+
});
|
|
57
60
|
return result;
|
|
58
61
|
}
|
|
59
62
|
catch (error) {
|
|
@@ -64,7 +67,9 @@ export class B2CDxMcpServer extends McpServer {
|
|
|
64
67
|
runTimeMs,
|
|
65
68
|
isError: true,
|
|
66
69
|
})
|
|
67
|
-
.catch(() => {
|
|
70
|
+
.catch((error_) => {
|
|
71
|
+
getLogger().debug({ err: error_, toolName: name }, '[mcp] telemetry sendEventAndFlush failed');
|
|
72
|
+
});
|
|
68
73
|
throw error;
|
|
69
74
|
}
|
|
70
75
|
};
|
package/dist/services.d.ts
CHANGED
|
@@ -122,6 +122,15 @@ export declare class Services {
|
|
|
122
122
|
* @returns True if exists, false otherwise
|
|
123
123
|
*/
|
|
124
124
|
exists(targetPath: string): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Get Basic auth credentials for SDAPI operations (script debugger).
|
|
127
|
+
* Returns undefined if credentials are not configured.
|
|
128
|
+
*/
|
|
129
|
+
getBasicAuthCredentials(): undefined | {
|
|
130
|
+
hostname: string;
|
|
131
|
+
username: string;
|
|
132
|
+
password: string;
|
|
133
|
+
};
|
|
125
134
|
/**
|
|
126
135
|
* Get Custom APIs client for managing custom SCAPI endpoints.
|
|
127
136
|
* Requires shortCode, tenantId, and OAuth credentials to be configured.
|
package/dist/services.js
CHANGED
|
@@ -126,6 +126,16 @@ export class Services {
|
|
|
126
126
|
exists(targetPath) {
|
|
127
127
|
return fs.existsSync(targetPath);
|
|
128
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Get Basic auth credentials for SDAPI operations (script debugger).
|
|
131
|
+
* Returns undefined if credentials are not configured.
|
|
132
|
+
*/
|
|
133
|
+
getBasicAuthCredentials() {
|
|
134
|
+
const { hostname, username, password } = this.resolvedConfig.values;
|
|
135
|
+
if (!hostname || !username || !password)
|
|
136
|
+
return undefined;
|
|
137
|
+
return { hostname, username, password };
|
|
138
|
+
}
|
|
129
139
|
/**
|
|
130
140
|
* Get Custom APIs client for managing custom SCAPI endpoints.
|
|
131
141
|
* Requires shortCode, tenantId, and OAuth credentials to be configured.
|
package/dist/tools/adapter.d.ts
CHANGED
|
@@ -68,6 +68,7 @@ import { type ZodRawShape } from 'zod';
|
|
|
68
68
|
import type { B2CInstance } from '@salesforce/b2c-tooling-sdk';
|
|
69
69
|
import type { McpTool, ToolResult, Toolset } from '../utils/index.js';
|
|
70
70
|
import type { Services, MrtConfig } from '../services.js';
|
|
71
|
+
import type { ServerContext } from '../server-context.js';
|
|
71
72
|
/**
|
|
72
73
|
* Context provided to tool execute functions.
|
|
73
74
|
* Contains the B2CInstance and/or MRT config based on tool requirements.
|
|
@@ -89,6 +90,11 @@ export interface ToolExecutionContext {
|
|
|
89
90
|
* Services instance for file system access and other utilities.
|
|
90
91
|
*/
|
|
91
92
|
services: Services;
|
|
93
|
+
/**
|
|
94
|
+
* Server-scoped persistent state (debug sessions, log watches, etc.).
|
|
95
|
+
* Created once at server startup and shared across all tool invocations.
|
|
96
|
+
*/
|
|
97
|
+
serverContext?: ServerContext;
|
|
92
98
|
}
|
|
93
99
|
/**
|
|
94
100
|
* Options for creating a tool adapter.
|
|
@@ -206,4 +212,4 @@ export declare function jsonResult(data: unknown, indent?: number): ToolResult;
|
|
|
206
212
|
* }, loadServices);
|
|
207
213
|
* ```
|
|
208
214
|
*/
|
|
209
|
-
export declare function createToolAdapter<TInput, TOutput>(options: ToolAdapterOptions<TInput, TOutput>, loadServices: () => Promise<Services> | Services): McpTool;
|
|
215
|
+
export declare function createToolAdapter<TInput, TOutput>(options: ToolAdapterOptions<TInput, TOutput>, loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|
package/dist/tools/adapter.js
CHANGED
|
@@ -168,7 +168,7 @@ function formatZodErrors(error) {
|
|
|
168
168
|
* }, loadServices);
|
|
169
169
|
* ```
|
|
170
170
|
*/
|
|
171
|
-
export function createToolAdapter(options, loadServices) {
|
|
171
|
+
export function createToolAdapter(options, loadServices, serverContext) {
|
|
172
172
|
const { name, description, inputSchema, toolsets, isGA = true, requiresInstance = false, requiresMrtAuth = false, execute, formatOutput, } = options;
|
|
173
173
|
// Create Zod schema from inputSchema definition
|
|
174
174
|
const zodSchema = z.object(inputSchema);
|
|
@@ -214,6 +214,7 @@ export function createToolAdapter(options, loadServices) {
|
|
|
214
214
|
b2cInstance,
|
|
215
215
|
mrtConfig,
|
|
216
216
|
services,
|
|
217
|
+
serverContext,
|
|
217
218
|
};
|
|
218
219
|
const output = await execute(args, context);
|
|
219
220
|
// 6. Format output
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpTool } from '../../utils/index.js';
|
|
2
|
+
import type { Services } from '../../services.js';
|
|
3
|
+
import type { ServerContext } from '../../server-context.js';
|
|
4
|
+
export declare function createDebugCaptureAtBreakpointTool(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
8
|
+
import { projectFrame, projectVariable, resolveBreakpointPath, } from '@salesforce/b2c-tooling-sdk/operations/debug';
|
|
9
|
+
import { getRegistry, getSessionEntry } from './session-registry.js';
|
|
10
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
11
|
+
const MAX_TIMEOUT_MS = 120_000;
|
|
12
|
+
export function createDebugCaptureAtBreakpointTool(loadServices, serverContext) {
|
|
13
|
+
return createToolAdapter({
|
|
14
|
+
name: 'debug_capture_at_breakpoint',
|
|
15
|
+
description: 'Set a breakpoint, optionally trigger an HTTP request, wait for the breakpoint to be hit, and capture a diagnostic snapshot (stack, variables, expression results). ' +
|
|
16
|
+
'Use trigger_url to have the tool fire the request itself (recommended) — this avoids needing to coordinate a separate request while the tool blocks. ' +
|
|
17
|
+
'Without trigger_url, the tool BLOCKS until the breakpoint is hit or timeout expires and requires the user to trigger a request externally. ' +
|
|
18
|
+
'For more control, use the non-blocking workflow: debug_set_breakpoints → trigger request → debug_list_sessions (check halted_threads) → debug_get_variables.',
|
|
19
|
+
toolsets: ['CARTRIDGES', 'SCAPI'],
|
|
20
|
+
inputSchema: {
|
|
21
|
+
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
22
|
+
file: z.string().describe('Local file path or server script path for the breakpoint.'),
|
|
23
|
+
line: z.number().int().positive().describe('Line number for the breakpoint.'),
|
|
24
|
+
condition: z.string().optional().describe('Optional conditional expression for the breakpoint.'),
|
|
25
|
+
expressions: z.array(z.string()).optional().describe('Expressions to evaluate when the breakpoint is hit.'),
|
|
26
|
+
timeout_ms: z
|
|
27
|
+
.number()
|
|
28
|
+
.int()
|
|
29
|
+
.positive()
|
|
30
|
+
.max(MAX_TIMEOUT_MS)
|
|
31
|
+
.optional()
|
|
32
|
+
.describe(`Timeout in milliseconds waiting for the breakpoint to be hit (default: ${DEFAULT_TIMEOUT_MS}, max: ${MAX_TIMEOUT_MS}).`),
|
|
33
|
+
auto_continue: z
|
|
34
|
+
.boolean()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('If true, resume the thread after capturing the snapshot. Defaults to false.'),
|
|
37
|
+
trigger_url: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('URL to request after arming the breakpoint. The tool fires this HTTP GET in the background, then waits for the breakpoint to halt. ' +
|
|
41
|
+
'This is the recommended approach — it avoids needing to coordinate a separate request while the tool blocks.'),
|
|
42
|
+
},
|
|
43
|
+
async execute(args, context) {
|
|
44
|
+
const entry = getSessionEntry(context, args.session_id);
|
|
45
|
+
const registry = getRegistry(context);
|
|
46
|
+
const timeout = Math.min(args.timeout_ms ?? DEFAULT_TIMEOUT_MS, MAX_TIMEOUT_MS);
|
|
47
|
+
const scriptPath = resolveBreakpointPath(args.file, entry.sourceMapper, entry.cartridges);
|
|
48
|
+
// Add breakpoint to existing set
|
|
49
|
+
const allBps = [
|
|
50
|
+
...entry.breakpoints.map((bp) => ({
|
|
51
|
+
script_path: bp.script_path,
|
|
52
|
+
line_number: bp.line_number,
|
|
53
|
+
condition: bp.condition,
|
|
54
|
+
})),
|
|
55
|
+
{ script_path: scriptPath, line_number: args.line, condition: args.condition },
|
|
56
|
+
];
|
|
57
|
+
entry.breakpoints = await entry.manager.setBreakpoints(allBps);
|
|
58
|
+
const breakpointInfo = {
|
|
59
|
+
file: entry.sourceMapper.toLocalPath(scriptPath) ?? null,
|
|
60
|
+
line: args.line,
|
|
61
|
+
script_path: scriptPath,
|
|
62
|
+
};
|
|
63
|
+
// Fire trigger URL in the background (it will hang when the breakpoint halts the thread)
|
|
64
|
+
const triggerPromise = args.trigger_url
|
|
65
|
+
? fetch(args.trigger_url, { redirect: 'follow' })
|
|
66
|
+
.then((r) => r.status)
|
|
67
|
+
.catch(() => undefined)
|
|
68
|
+
: undefined;
|
|
69
|
+
const thread = await registry.waitForHalt(entry, timeout);
|
|
70
|
+
if (!thread) {
|
|
71
|
+
return {
|
|
72
|
+
breakpoint: breakpointInfo,
|
|
73
|
+
halted: false,
|
|
74
|
+
timed_out: true,
|
|
75
|
+
auto_continued: false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const threadDetail = await entry.manager.client.getThread(thread.id);
|
|
79
|
+
const stack = threadDetail.call_stack.map((frame) => projectFrame(frame, entry.sourceMapper));
|
|
80
|
+
const varsResult = await entry.manager.client.getVariables(thread.id, 0);
|
|
81
|
+
const variables = varsResult.object_members.map((m) => projectVariable(m));
|
|
82
|
+
const evaluations = [];
|
|
83
|
+
if (args.expressions) {
|
|
84
|
+
for (const expr of args.expressions) {
|
|
85
|
+
try {
|
|
86
|
+
// eslint-disable-next-line no-await-in-loop -- sequential: each eval must complete before the next
|
|
87
|
+
const evalResult = await entry.manager.client.evaluate(thread.id, 0, expr);
|
|
88
|
+
evaluations.push({ expression: evalResult.expression, result: evalResult.result });
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
evaluations.push({
|
|
92
|
+
expression: expr,
|
|
93
|
+
result: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
let autoContinued = false;
|
|
99
|
+
if (args.auto_continue) {
|
|
100
|
+
await entry.manager.resume(thread.id);
|
|
101
|
+
autoContinued = true;
|
|
102
|
+
}
|
|
103
|
+
const triggerStatus = triggerPromise ? await triggerPromise : undefined;
|
|
104
|
+
return {
|
|
105
|
+
breakpoint: breakpointInfo,
|
|
106
|
+
halted: true,
|
|
107
|
+
thread_id: thread.id,
|
|
108
|
+
stack,
|
|
109
|
+
variables,
|
|
110
|
+
evaluations: evaluations.length > 0 ? evaluations : undefined,
|
|
111
|
+
auto_continued: autoContinued,
|
|
112
|
+
trigger_status: triggerStatus,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
formatOutput: (output) => jsonResult(output),
|
|
116
|
+
}, loadServices, serverContext);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=debug-capture-at-breakpoint.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpTool } from '../../utils/index.js';
|
|
2
|
+
import type { Services } from '../../services.js';
|
|
3
|
+
import type { ServerContext } from '../../server-context.js';
|
|
4
|
+
export declare function createDebugContinueTool(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
8
|
+
import { getSessionEntry } from './session-registry.js';
|
|
9
|
+
export function createDebugContinueTool(loadServices, serverContext) {
|
|
10
|
+
return createToolAdapter({
|
|
11
|
+
name: 'debug_continue',
|
|
12
|
+
description: 'Resume execution of a halted thread. ' +
|
|
13
|
+
'The thread runs until the next breakpoint or completes the current request.',
|
|
14
|
+
toolsets: ['CARTRIDGES', 'SCAPI'],
|
|
15
|
+
inputSchema: {
|
|
16
|
+
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
17
|
+
thread_id: z.number().int().describe('Thread ID of the halted thread to resume.'),
|
|
18
|
+
},
|
|
19
|
+
async execute(args, context) {
|
|
20
|
+
const entry = getSessionEntry(context, args.session_id);
|
|
21
|
+
await entry.manager.resume(args.thread_id);
|
|
22
|
+
return { thread_id: args.thread_id, status: 'resumed' };
|
|
23
|
+
},
|
|
24
|
+
formatOutput: (output) => jsonResult(output),
|
|
25
|
+
}, loadServices, serverContext);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=debug-continue.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpTool } from '../../utils/index.js';
|
|
2
|
+
import type { Services } from '../../services.js';
|
|
3
|
+
import type { ServerContext } from '../../server-context.js';
|
|
4
|
+
export declare function createDebugEndSessionTool(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
8
|
+
import { getRegistry, getSessionEntry } from './session-registry.js';
|
|
9
|
+
export function createDebugEndSessionTool(loadServices, serverContext) {
|
|
10
|
+
return createToolAdapter({
|
|
11
|
+
name: 'debug_end_session',
|
|
12
|
+
description: 'End a script debugger session and free its slot on the instance. ' +
|
|
13
|
+
'IMPORTANT: Always call this when finished debugging — leaving sessions open can interfere with other debuggers and consumes a debugger client slot.',
|
|
14
|
+
toolsets: ['CARTRIDGES', 'SCAPI'],
|
|
15
|
+
inputSchema: {
|
|
16
|
+
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
17
|
+
clear_breakpoints: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('If true, delete all breakpoints before disconnecting. Defaults to false.'),
|
|
21
|
+
},
|
|
22
|
+
async execute(args, context) {
|
|
23
|
+
const entry = getSessionEntry(context, args.session_id);
|
|
24
|
+
if (args.clear_breakpoints) {
|
|
25
|
+
try {
|
|
26
|
+
await entry.manager.client.deleteBreakpoints();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Best-effort
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
await getRegistry(context).destroySession(args.session_id);
|
|
33
|
+
return { session_id: args.session_id, status: 'disconnected' };
|
|
34
|
+
},
|
|
35
|
+
formatOutput: (output) => jsonResult(output),
|
|
36
|
+
}, loadServices, serverContext);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=debug-end-session.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpTool } from '../../utils/index.js';
|
|
2
|
+
import type { Services } from '../../services.js';
|
|
3
|
+
import type { ServerContext } from '../../server-context.js';
|
|
4
|
+
export declare function createDebugEvaluateTool(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
8
|
+
import { getSessionEntry } from './session-registry.js';
|
|
9
|
+
export function createDebugEvaluateTool(loadServices, serverContext) {
|
|
10
|
+
return createToolAdapter({
|
|
11
|
+
name: 'debug_evaluate',
|
|
12
|
+
description: 'Evaluate a JavaScript expression in the context of a halted thread and stack frame. ' +
|
|
13
|
+
'WARNING: Expressions may have side effects (modify variables, call functions). Use with care.',
|
|
14
|
+
toolsets: ['CARTRIDGES', 'SCAPI'],
|
|
15
|
+
inputSchema: {
|
|
16
|
+
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
17
|
+
thread_id: z.number().int().describe('Thread ID from debug_wait_for_stop or debug_list_sessions.'),
|
|
18
|
+
frame_index: z.number().int().min(0).optional().describe('Stack frame index (0 = top frame). Defaults to 0.'),
|
|
19
|
+
expression: z.string().describe('JavaScript expression to evaluate in the frame context.'),
|
|
20
|
+
},
|
|
21
|
+
async execute(args, context) {
|
|
22
|
+
const entry = getSessionEntry(context, args.session_id);
|
|
23
|
+
const frameIndex = args.frame_index ?? 0;
|
|
24
|
+
const result = await entry.manager.client.evaluate(args.thread_id, frameIndex, args.expression);
|
|
25
|
+
return { expression: result.expression, result: result.result };
|
|
26
|
+
},
|
|
27
|
+
formatOutput: (output) => jsonResult(output),
|
|
28
|
+
}, loadServices, serverContext);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=debug-evaluate.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpTool } from '../../utils/index.js';
|
|
2
|
+
import type { Services } from '../../services.js';
|
|
3
|
+
import type { ServerContext } from '../../server-context.js';
|
|
4
|
+
export declare function createDebugGetStackTool(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
8
|
+
import { projectFrame } from '@salesforce/b2c-tooling-sdk/operations/debug';
|
|
9
|
+
import { getSessionEntry } from './session-registry.js';
|
|
10
|
+
export function createDebugGetStackTool(loadServices, serverContext) {
|
|
11
|
+
return createToolAdapter({
|
|
12
|
+
name: 'debug_get_stack',
|
|
13
|
+
description: 'Get the call stack for a halted thread. ' +
|
|
14
|
+
'Returns frames with mapped local file paths and server script paths.',
|
|
15
|
+
toolsets: ['CARTRIDGES', 'SCAPI'],
|
|
16
|
+
inputSchema: {
|
|
17
|
+
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
18
|
+
thread_id: z.number().int().describe('Thread ID from debug_wait_for_stop or debug_list_sessions.'),
|
|
19
|
+
},
|
|
20
|
+
async execute(args, context) {
|
|
21
|
+
const entry = getSessionEntry(context, args.session_id);
|
|
22
|
+
const thread = await entry.manager.client.getThread(args.thread_id);
|
|
23
|
+
return {
|
|
24
|
+
thread_id: thread.id,
|
|
25
|
+
frames: thread.call_stack.map((frame) => projectFrame(frame, entry.sourceMapper)),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
formatOutput: (output) => jsonResult(output),
|
|
29
|
+
}, loadServices, serverContext);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=debug-get-stack.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpTool } from '../../utils/index.js';
|
|
2
|
+
import type { Services } from '../../services.js';
|
|
3
|
+
import type { ServerContext } from '../../server-context.js';
|
|
4
|
+
export declare function createDebugGetVariablesTool(loadServices: () => Promise<Services> | Services, serverContext?: ServerContext): McpTool;
|