@salesforce/b2c-dx-mcp 2.0.0 → 2.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 +15 -62
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +41 -9
- package/dist/registry.d.ts +3 -4
- package/dist/registry.js +7 -14
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -1
- package/dist/services.d.ts +21 -1
- package/dist/services.js +39 -6
- package/dist/tools/adapter.d.ts +18 -2
- package/dist/tools/adapter.js +49 -10
- package/dist/tools/cartridges/index.js +16 -22
- package/dist/tools/diagnostics/config-inspect.js +4 -8
- package/dist/tools/diagnostics/debug-capture-at-breakpoint.js +3 -9
- package/dist/tools/diagnostics/debug-list-sessions.js +1 -0
- package/dist/tools/diagnostics/debug-start-session.js +16 -9
- package/dist/tools/diagnostics/debug-wait-for-stop.js +1 -3
- package/dist/tools/diagnostics/log-watch-registry.d.ts +3 -0
- package/dist/tools/diagnostics/log-watch-registry.js +1 -0
- package/dist/tools/diagnostics/logs-get-recent.js +2 -5
- package/dist/tools/diagnostics/logs-list-files.js +1 -1
- package/dist/tools/diagnostics/logs-watch-list.js +1 -0
- package/dist/tools/diagnostics/logs-watch-start.js +10 -10
- package/dist/tools/diagnostics/mrt-log-watch-registry.d.ts +3 -0
- package/dist/tools/diagnostics/mrt-log-watch-registry.js +0 -0
- package/dist/tools/diagnostics/mrt-logs-watch-list.js +1 -0
- package/dist/tools/diagnostics/mrt-logs-watch-poll.js +2 -5
- package/dist/tools/diagnostics/mrt-logs-watch-start.js +9 -8
- package/dist/tools/diagnostics/session-registry.d.ts +3 -0
- package/dist/tools/diagnostics/session-registry.js +2 -1
- package/dist/tools/docs/docs-list.js +4 -6
- package/dist/tools/docs/docs-read.js +2 -9
- package/dist/tools/docs/docs-schema-list.js +1 -1
- package/dist/tools/docs/docs-schema-read.js +2 -3
- package/dist/tools/docs/docs-schema-search.js +2 -2
- package/dist/tools/docs/docs-search.js +3 -9
- package/dist/tools/docs/storefront.js +3 -3
- package/dist/tools/docs/topics.js +3 -1
- package/dist/tools/index.d.ts +0 -1
- package/dist/tools/index.js +0 -1
- package/dist/tools/mrt/index.js +6 -2
- package/dist/tools/project-context.d.ts +38 -11
- package/dist/tools/project-context.js +32 -15
- package/dist/tools/pwav3/pwa-kit-development-guidelines.js +3 -43
- package/dist/tools/scapi/metrics-get.js +5 -23
- package/dist/tools/scapi/scapi-custom-api-generate-scaffold.d.ts +4 -0
- package/dist/tools/scapi/scapi-custom-api-generate-scaffold.js +29 -14
- package/dist/tools/scapi/scapi-custom-apis-get-status.js +3 -14
- package/dist/tools/scapi/scapi-schemas-list.js +11 -11
- package/dist/utils/constants.d.ts +2 -13
- package/dist/utils/constants.js +1 -20
- package/oclif.manifest.json +2 -2
- package/package.json +5 -5
- package/content/sfnext/auth.md +0 -62
- package/content/sfnext/components.md +0 -123
- package/content/sfnext/config.md +0 -180
- package/content/sfnext/data-fetching.md +0 -323
- package/content/sfnext/extensions.md +0 -80
- package/content/sfnext/i18n.md +0 -121
- package/content/sfnext/page-designer.md +0 -78
- package/content/sfnext/performance.md +0 -80
- package/content/sfnext/pitfalls.md +0 -141
- package/content/sfnext/quick-reference.md +0 -226
- package/content/sfnext/state-management.md +0 -75
- package/content/sfnext/styling.md +0 -51
- package/content/sfnext/testing.md +0 -232
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.d.ts +0 -24
- package/dist/tools/storefrontnext/figma/figma-to-component/figma-url-parser.js +0 -53
- package/dist/tools/storefrontnext/figma/figma-to-component/index.d.ts +0 -45
- package/dist/tools/storefrontnext/figma/figma-to-component/index.js +0 -332
- package/dist/tools/storefrontnext/figma/generate-component/decision.d.ts +0 -40
- package/dist/tools/storefrontnext/figma/generate-component/decision.js +0 -312
- package/dist/tools/storefrontnext/figma/generate-component/formatter.d.ts +0 -9
- package/dist/tools/storefrontnext/figma/generate-component/formatter.js +0 -92
- package/dist/tools/storefrontnext/figma/generate-component/index.d.ts +0 -117
- package/dist/tools/storefrontnext/figma/generate-component/index.js +0 -102
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.d.ts +0 -71
- package/dist/tools/storefrontnext/figma/map-tokens/css-parser.js +0 -260
- package/dist/tools/storefrontnext/figma/map-tokens/index.d.ts +0 -64
- package/dist/tools/storefrontnext/figma/map-tokens/index.js +0 -242
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.d.ts +0 -65
- package/dist/tools/storefrontnext/figma/map-tokens/token-matcher.js +0 -268
- package/dist/tools/storefrontnext/index.d.ts +0 -30
- package/dist/tools/storefrontnext/index.js +0 -28
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.d.ts +0 -169
- package/dist/tools/storefrontnext/page-designer-decorator/analyzer.js +0 -567
- package/dist/tools/storefrontnext/page-designer-decorator/index.d.ts +0 -259
- package/dist/tools/storefrontnext/page-designer-decorator/index.js +0 -622
- package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.d.ts +0 -8
- package/dist/tools/storefrontnext/page-designer-decorator/rules/1-mode-selection.js +0 -65
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.d.ts +0 -13
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2a-auto-mode.js +0 -87
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.d.ts +0 -4
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-0-interactive-overview.js +0 -55
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.d.ts +0 -22
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-1-interactive-analyze.js +0 -109
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.d.ts +0 -21
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-2-interactive-select-props.js +0 -60
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.d.ts +0 -27
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-3-interactive-configure-attrs.js +0 -68
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.d.ts +0 -4
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-4-interactive-configure-regions.js +0 -65
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.d.ts +0 -11
- package/dist/tools/storefrontnext/page-designer-decorator/rules/2b-5-interactive-confirm-generation.js +0 -92
- package/dist/tools/storefrontnext/page-designer-decorator/rules.d.ts +0 -51
- package/dist/tools/storefrontnext/page-designer-decorator/rules.js +0 -70
- package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.d.ts +0 -116
- package/dist/tools/storefrontnext/page-designer-decorator/templates/decorator-generator.js +0 -350
- package/dist/tools/storefrontnext/sfnext-development-guidelines.d.ts +0 -9
- package/dist/tools/storefrontnext/sfnext-development-guidelines.js +0 -141
- package/dist/tools/storefrontnext/site-theming/color-contrast.d.ts +0 -92
- package/dist/tools/storefrontnext/site-theming/color-contrast.js +0 -186
- package/dist/tools/storefrontnext/site-theming/color-mapping.d.ts +0 -16
- package/dist/tools/storefrontnext/site-theming/color-mapping.js +0 -131
- package/dist/tools/storefrontnext/site-theming/guidance-merger.d.ts +0 -11
- package/dist/tools/storefrontnext/site-theming/guidance-merger.js +0 -78
- package/dist/tools/storefrontnext/site-theming/index.d.ts +0 -14
- package/dist/tools/storefrontnext/site-theming/index.js +0 -128
- package/dist/tools/storefrontnext/site-theming/response-builder.d.ts +0 -16
- package/dist/tools/storefrontnext/site-theming/response-builder.js +0 -316
- package/dist/tools/storefrontnext/site-theming/theming-store.d.ts +0 -64
- package/dist/tools/storefrontnext/site-theming/theming-store.js +0 -417
- package/dist/tools/storefrontnext/site-theming/types.d.ts +0 -36
- package/dist/tools/storefrontnext/site-theming/types.js +0 -7
package/dist/tools/adapter.js
CHANGED
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
* ```
|
|
71
71
|
*/
|
|
72
72
|
import { z } from 'zod';
|
|
73
|
-
import {
|
|
73
|
+
import { createProjectContextInputSchema, } from './project-context.js';
|
|
74
74
|
/**
|
|
75
75
|
* Creates a text-only success result.
|
|
76
76
|
*
|
|
@@ -121,6 +121,25 @@ export function jsonResult(data, indent = 2) {
|
|
|
121
121
|
content: [{ type: 'text', text: JSON.stringify(data, null, indent) }],
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
+
/** Attach compact resolution provenance while preserving existing tool output. */
|
|
125
|
+
export function attachResolution(result, resolution) {
|
|
126
|
+
let content = result.content;
|
|
127
|
+
let structuredContent = { ...result.structuredContent, resolution };
|
|
128
|
+
if (content.length === 1 && content[0]?.type === 'text') {
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(content[0].text);
|
|
131
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
132
|
+
const output = { ...parsed, resolution };
|
|
133
|
+
content = [{ ...content[0], text: JSON.stringify(output, null, 2) }];
|
|
134
|
+
structuredContent = { ...output, ...result.structuredContent, resolution };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Plain-text tools expose resolution through structuredContent only.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return { ...result, content, structuredContent };
|
|
142
|
+
}
|
|
124
143
|
/**
|
|
125
144
|
* Formats Zod validation errors into a human-readable string.
|
|
126
145
|
*
|
|
@@ -170,10 +189,17 @@ function formatZodErrors(error) {
|
|
|
170
189
|
* ```
|
|
171
190
|
*/
|
|
172
191
|
export function createToolAdapter(options, loadServices, serverContext) {
|
|
173
|
-
const { name, description, inputSchema, toolsets, isGA = true, requiresInstance = false, requiresMrtAuth = false, usesProjectContext = false, execute, formatOutput, } = options;
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
192
|
+
const { name, description, inputSchema, toolsets, isGA = true, requiresInstance = false, requiresMrtAuth = false, usesProjectContext = false, usesConfigurationContext = false, execute, formatOutput, } = options;
|
|
193
|
+
const projectContextKind = requiresInstance || requiresMrtAuth || usesConfigurationContext
|
|
194
|
+
? 'configuration'
|
|
195
|
+
: usesProjectContext
|
|
196
|
+
? 'project'
|
|
197
|
+
: undefined;
|
|
198
|
+
const effectiveInputSchema = projectContextKind
|
|
199
|
+
? {
|
|
200
|
+
...createProjectContextInputSchema(projectContextKind),
|
|
201
|
+
...inputSchema,
|
|
202
|
+
}
|
|
177
203
|
: inputSchema;
|
|
178
204
|
// Create Zod schema from inputSchema definition
|
|
179
205
|
const zodSchema = z.object(effectiveInputSchema);
|
|
@@ -190,15 +216,21 @@ export function createToolAdapter(options, loadServices, serverContext) {
|
|
|
190
216
|
return errorResult(`Invalid input: ${formatZodErrors(parseResult.error)}`);
|
|
191
217
|
}
|
|
192
218
|
const args = parseResult.data;
|
|
219
|
+
let resolution;
|
|
193
220
|
try {
|
|
194
221
|
// 2. Load Services to get fresh configuration (re-reads config files)
|
|
195
|
-
const projectContext =
|
|
222
|
+
const projectContext = projectContextKind ? args : undefined;
|
|
196
223
|
const services = await loadServices(projectContext);
|
|
224
|
+
const executionResolution = services.getResolution();
|
|
225
|
+
if (projectContextKind === 'project') {
|
|
226
|
+
delete executionResolution.configuration;
|
|
227
|
+
}
|
|
228
|
+
resolution = projectContextKind ? executionResolution : undefined;
|
|
197
229
|
// 3. Get B2CInstance if required (loaded on each call)
|
|
198
230
|
let b2cInstance;
|
|
199
231
|
if (requiresInstance) {
|
|
200
232
|
if (!services.b2cInstance) {
|
|
201
|
-
return errorResult('B2C instance error: Instance configuration required. Provide --server flag, set SFCC_SERVER environment variable, or configure dw.json');
|
|
233
|
+
return attachResolution(errorResult('B2C instance error: Instance configuration required. Provide --server flag, set SFCC_SERVER environment variable, or configure dw.json'), executionResolution);
|
|
202
234
|
}
|
|
203
235
|
b2cInstance = services.b2cInstance;
|
|
204
236
|
}
|
|
@@ -206,7 +238,7 @@ export function createToolAdapter(options, loadServices, serverContext) {
|
|
|
206
238
|
let mrtConfig;
|
|
207
239
|
if (requiresMrtAuth) {
|
|
208
240
|
if (!services.mrtConfig.auth) {
|
|
209
|
-
return errorResult('MRT auth error: MRT API key required. Provide --api-key, set MRT_API_KEY environment variable, or configure ~/.mobify');
|
|
241
|
+
return attachResolution(errorResult('MRT auth error: MRT API key required. Provide --api-key, set MRT_API_KEY environment variable, or configure ~/.mobify'), executionResolution);
|
|
210
242
|
}
|
|
211
243
|
mrtConfig = {
|
|
212
244
|
auth: services.mrtConfig.auth,
|
|
@@ -221,15 +253,22 @@ export function createToolAdapter(options, loadServices, serverContext) {
|
|
|
221
253
|
mrtConfig,
|
|
222
254
|
services,
|
|
223
255
|
serverContext,
|
|
256
|
+
resolution: executionResolution,
|
|
257
|
+
setResolvedDirectory(name, value) {
|
|
258
|
+
executionResolution.directories ??= {};
|
|
259
|
+
executionResolution.directories[name] = value;
|
|
260
|
+
},
|
|
224
261
|
};
|
|
225
262
|
const output = await execute(args, context);
|
|
226
263
|
// 6. Format output
|
|
227
|
-
|
|
264
|
+
const result = formatOutput(output);
|
|
265
|
+
return resolution ? attachResolution(result, executionResolution) : result;
|
|
228
266
|
}
|
|
229
267
|
catch (error) {
|
|
230
268
|
// Handle execution errors
|
|
231
269
|
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
-
|
|
270
|
+
const result = errorResult(`Execution error: ${message}`);
|
|
271
|
+
return resolution ? attachResolution(result, resolution) : result;
|
|
233
272
|
}
|
|
234
273
|
},
|
|
235
274
|
};
|
|
@@ -35,38 +35,27 @@ function createCartridgeDeployTool(loadServices, injections) {
|
|
|
35
35
|
const getActiveCodeVersionFn = injections?.getActiveCodeVersion || getActiveCodeVersion;
|
|
36
36
|
return createToolAdapter({
|
|
37
37
|
name: 'cartridge_deploy',
|
|
38
|
-
description: '
|
|
39
|
-
|
|
40
|
-
'creates a zip archive, uploads via WebDAV, and optionally reloads the code version. ' +
|
|
41
|
-
'Use this tool to deploy custom code cartridges for SFRA or other B2C Commerce code. ' +
|
|
42
|
-
'Requires the instance to have a code version configured. ' +
|
|
43
|
-
"After deploy, add new cartridges to your site's cartridge path in Business Manager: Sites → Manage Sites → [site] → Settings tab → Cartridges.",
|
|
38
|
+
description: 'Find and deploy cartridges to B2C Commerce via WebDAV. Supports include/exclude filters and code-version reload. ' +
|
|
39
|
+
"After deployment, add new cartridges to the site's cartridge path in Business Manager: Sites → Manage Sites → Settings tab → Cartridges.",
|
|
44
40
|
toolsets: ['CARTRIDGES'],
|
|
45
41
|
isGA: true,
|
|
46
42
|
requiresInstance: true,
|
|
47
43
|
usesProjectContext: true,
|
|
48
44
|
inputSchema: {
|
|
45
|
+
cartridgeDirectory: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe('Optional cartridge discovery root. Relative paths resolve from projectDirectory. Defaults to projectDirectory.'),
|
|
49
49
|
directory: z
|
|
50
50
|
.string()
|
|
51
51
|
.optional()
|
|
52
|
-
.describe('
|
|
53
|
-
'The tool will recursively search this directory for .project files to identify cartridges.'),
|
|
52
|
+
.describe('Deprecated alias for cartridgeDirectory. cartridgeDirectory takes precedence when both are supplied.'),
|
|
54
53
|
cartridges: z
|
|
55
54
|
.array(z.string())
|
|
56
55
|
.optional()
|
|
57
|
-
.describe('
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.array(z.string())
|
|
61
|
-
.optional()
|
|
62
|
-
.describe('Array of cartridge names to exclude from the deployment. Use this to skip deploying certain cartridges, ' +
|
|
63
|
-
'such as third-party or unchanged cartridges. Applied after the include filter.'),
|
|
64
|
-
reload: z
|
|
65
|
-
.boolean()
|
|
66
|
-
.optional()
|
|
67
|
-
.describe('Whether to reload (re-activate) the code version after deployment. ' +
|
|
68
|
-
'Set to true to make the deployed code immediately active on the instance. ' +
|
|
69
|
-
'Defaults to false. Use this when you want changes to take effect right away.'),
|
|
56
|
+
.describe('Cartridge names to deploy; omit for all discovered cartridges.'),
|
|
57
|
+
exclude: z.array(z.string()).optional().describe('Cartridge names to exclude after the include filter.'),
|
|
58
|
+
reload: z.boolean().optional().describe('Reload the code version after deployment. Default: false.'),
|
|
70
59
|
},
|
|
71
60
|
async execute(args, context) {
|
|
72
61
|
// Get instance from context (guaranteed by adapter when requiresInstance is true)
|
|
@@ -89,7 +78,12 @@ function createCartridgeDeployTool(loadServices, injections) {
|
|
|
89
78
|
}
|
|
90
79
|
// Resolve directory path: relative paths are resolved relative to project directory, absolute paths are used as-is
|
|
91
80
|
const projectDirectory = context.services.resolveProjectDirectory(args.projectDirectory);
|
|
92
|
-
const
|
|
81
|
+
const directoryArgument = args.cartridgeDirectory ?? args.directory;
|
|
82
|
+
const directory = context.services.resolveWithProjectDirectory(directoryArgument, args.projectDirectory);
|
|
83
|
+
context.setResolvedDirectory('cartridgeDirectory', {
|
|
84
|
+
path: directory,
|
|
85
|
+
source: directoryArgument ? 'argument' : 'projectDirectory',
|
|
86
|
+
});
|
|
93
87
|
// Parse options
|
|
94
88
|
const options = {
|
|
95
89
|
include: args.cartridges,
|
|
@@ -18,26 +18,22 @@ import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
|
18
18
|
export function createConfigInspectTool(loadServices) {
|
|
19
19
|
return createToolAdapter({
|
|
20
20
|
name: 'config_inspect',
|
|
21
|
-
description: 'Inspect
|
|
22
|
-
'
|
|
23
|
-
'Pass projectDirectory and/or configPath to inspect the same project and dw.json-format file a CLI command would use. The output includes the effective projectDirectory and source provenance, which is useful for diagnosing why the server targets the wrong instance or cannot find a project. ' +
|
|
24
|
-
'Use this first when configuration seems wrong, auth is failing, or the server appears to be operating in the wrong directory.',
|
|
21
|
+
description: 'Inspect resolved B2C configuration, source provenance, warnings, and paths. Secrets are redacted unless unmask=true. ' +
|
|
22
|
+
'Use to diagnose configuration, authentication, or project-context issues.',
|
|
25
23
|
toolsets: ['DIAGNOSTICS'],
|
|
26
24
|
isGA: true,
|
|
27
25
|
requiresInstance: false,
|
|
28
|
-
|
|
26
|
+
usesConfigurationContext: true,
|
|
29
27
|
inputSchema: {
|
|
30
28
|
unmask: z
|
|
31
29
|
.boolean()
|
|
32
30
|
.optional()
|
|
33
|
-
.describe('
|
|
31
|
+
.describe('Return secrets unmasked. Default: false; use only when explicitly requested.'),
|
|
34
32
|
},
|
|
35
33
|
async execute(args, { services }) {
|
|
36
34
|
const resolved = services.getResolvedConfig();
|
|
37
|
-
const projectDirectory = services.resolveProjectDirectory(args.projectDirectory);
|
|
38
35
|
return {
|
|
39
36
|
config: redactConfigValues(resolved.values, { unmask: args.unmask ?? false }),
|
|
40
|
-
projectDirectory,
|
|
41
37
|
sources: resolved.sources,
|
|
42
38
|
warnings: resolved.warnings.length > 0 ? resolved.warnings.map((w) => w.message) : undefined,
|
|
43
39
|
};
|
|
@@ -15,10 +15,8 @@ const TIMEOUT_HINT = 'Breakpoint not hit. First confirm the triggered request ac
|
|
|
15
15
|
export function createDebugCaptureAtBreakpointTool(loadServices, serverContext) {
|
|
16
16
|
return createToolAdapter({
|
|
17
17
|
name: 'debug_capture_at_breakpoint',
|
|
18
|
-
description: 'Set a breakpoint, optionally
|
|
19
|
-
'
|
|
20
|
-
'Without trigger_url, the tool BLOCKS until the breakpoint is hit or timeout expires and requires the user to trigger a request externally. ' +
|
|
21
|
-
'For more control, use the non-blocking workflow: debug_set_breakpoints → trigger request → debug_list_sessions (check halted_threads) → debug_get_variables.',
|
|
18
|
+
description: 'Set a breakpoint, optionally GET trigger_url, wait for a halt, and return stack, variables, and expressions. ' +
|
|
19
|
+
'Without trigger_url, blocks until an external request hits the breakpoint or timeout expires.',
|
|
22
20
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'SCAPI'],
|
|
23
21
|
inputSchema: {
|
|
24
22
|
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
@@ -37,11 +35,7 @@ export function createDebugCaptureAtBreakpointTool(loadServices, serverContext)
|
|
|
37
35
|
.boolean()
|
|
38
36
|
.optional()
|
|
39
37
|
.describe('If true, resume the thread after capturing the snapshot. Defaults to false.'),
|
|
40
|
-
trigger_url: z
|
|
41
|
-
.string()
|
|
42
|
-
.optional()
|
|
43
|
-
.describe('URL to request after arming the breakpoint. The tool fires this HTTP GET in the background, then waits for the breakpoint to halt. ' +
|
|
44
|
-
'This is the recommended approach — it avoids needing to coordinate a separate request while the tool blocks.'),
|
|
38
|
+
trigger_url: z.string().optional().describe('HTTP GET URL to invoke after arming the breakpoint.'),
|
|
45
39
|
},
|
|
46
40
|
async execute(args, context) {
|
|
47
41
|
const entry = getSessionEntry(context, args.session_id);
|
|
@@ -30,6 +30,7 @@ export function createDebugListSessionsTool(loadServices, serverContext) {
|
|
|
30
30
|
session_cookie: dwsid ? { name: 'dwsid', value: dwsid } : null,
|
|
31
31
|
created_at: new Date(entry.createdAt).toISOString(),
|
|
32
32
|
last_activity_at: new Date(entry.lastActivityAt).toISOString(),
|
|
33
|
+
resolution: entry.resolution,
|
|
33
34
|
};
|
|
34
35
|
}),
|
|
35
36
|
};
|
|
@@ -12,18 +12,16 @@ import { getRegistry } from './session-registry.js';
|
|
|
12
12
|
export function createDebugStartSessionTool(loadServices, serverContext) {
|
|
13
13
|
return createToolAdapter({
|
|
14
14
|
name: 'debug_start_session',
|
|
15
|
-
description: 'Start a script debugger session
|
|
16
|
-
'
|
|
17
|
-
'Returns a session_id for use with other debug tools, plus discovered cartridge mappings. ' +
|
|
18
|
-
'WARNING: Debug sessions halt remote request threads on the instance. Always call debug_end_session when finished.',
|
|
15
|
+
description: 'Start a B2C script debugger session and discover cartridge mappings. Returns session_id for follow-up tools. ' +
|
|
16
|
+
'Debugging halts remote request threads; always call debug_end_session.',
|
|
19
17
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'SCAPI'],
|
|
20
18
|
inputSchema: {
|
|
21
19
|
cartridgeDirectory: z
|
|
22
20
|
.string()
|
|
23
21
|
.optional()
|
|
24
|
-
.describe('
|
|
22
|
+
.describe('Cartridge discovery and source-mapping root; relative to projectDirectory.'),
|
|
25
23
|
},
|
|
26
|
-
|
|
24
|
+
usesConfigurationContext: true,
|
|
27
25
|
async execute(args, context) {
|
|
28
26
|
const registry = getRegistry(context);
|
|
29
27
|
const credentials = context.services.getBasicAuthCredentials();
|
|
@@ -33,8 +31,11 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
|
|
|
33
31
|
}
|
|
34
32
|
const { hostname, username, password } = credentials;
|
|
35
33
|
const clientId = `b2c-dx-mcp-${randomUUID()}`;
|
|
36
|
-
const projectDirectory = context.services.resolveProjectDirectory(args.projectDirectory);
|
|
37
34
|
const cartridgeDir = context.services.resolveWithProjectDirectory(args.cartridgeDirectory, args.projectDirectory);
|
|
35
|
+
context.setResolvedDirectory('cartridgeDirectory', {
|
|
36
|
+
path: cartridgeDir,
|
|
37
|
+
source: args.cartridgeDirectory ? 'argument' : 'projectDirectory',
|
|
38
|
+
});
|
|
38
39
|
const cartridges = findCartridges(cartridgeDir);
|
|
39
40
|
const warnings = [];
|
|
40
41
|
if (cartridges.length === 0) {
|
|
@@ -55,7 +56,14 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
|
|
|
55
56
|
};
|
|
56
57
|
const manager = new DebugSessionManager({ hostname, username, password, clientId, cartridgeRoots: cartridges }, callbacks);
|
|
57
58
|
await manager.connect();
|
|
58
|
-
const entry = registry.registerSession({
|
|
59
|
+
const entry = registry.registerSession({
|
|
60
|
+
hostname,
|
|
61
|
+
clientId,
|
|
62
|
+
manager,
|
|
63
|
+
sourceMapper,
|
|
64
|
+
cartridges,
|
|
65
|
+
resolution: structuredClone(context.resolution),
|
|
66
|
+
});
|
|
59
67
|
const cartridgeMappings = {};
|
|
60
68
|
for (const c of cartridges)
|
|
61
69
|
cartridgeMappings[c.name] = c.src;
|
|
@@ -70,7 +78,6 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
|
|
|
70
78
|
cartridge_mappings: cartridgeMappings,
|
|
71
79
|
session_cookie: dwsid ? { name: 'dwsid', value: dwsid } : null,
|
|
72
80
|
warnings,
|
|
73
|
-
projectDirectory,
|
|
74
81
|
cartridgeDirectory: cartridgeDir,
|
|
75
82
|
};
|
|
76
83
|
},
|
|
@@ -15,9 +15,7 @@ const TIMEOUT_HINT = 'Breakpoint not hit. First confirm the request actually exe
|
|
|
15
15
|
export function createDebugWaitForStopTool(loadServices, serverContext) {
|
|
16
16
|
return createToolAdapter({
|
|
17
17
|
name: 'debug_wait_for_stop',
|
|
18
|
-
description: 'Wait for a thread to halt
|
|
19
|
-
'Returns immediately if a thread is already halted; otherwise BLOCKS until a halt occurs or the timeout expires. ' +
|
|
20
|
-
'Preferred non-blocking alternative: after debug_set_breakpoints, trigger the request yourself, then use debug_list_sessions to check halted_threads before calling debug_get_stack/debug_get_variables.',
|
|
18
|
+
description: 'Wait for a debugger thread to halt. Returns immediately if already halted; otherwise blocks until a halt or timeout.',
|
|
21
19
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'SCAPI'],
|
|
22
20
|
inputSchema: {
|
|
23
21
|
session_id: z.string().describe('Session ID returned by debug_start_session.'),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LogEntry, LogFile, TailLogsResult } from '@salesforce/b2c-tooling-sdk/operations/logs';
|
|
2
2
|
import type { ToolExecutionContext } from '../adapter.js';
|
|
3
|
+
import type { ToolResolution } from '../project-context.js';
|
|
3
4
|
/** Maximum number of buffered entries before oldest are evicted. */
|
|
4
5
|
export declare const DEFAULT_BUFFER_CAP = 5000;
|
|
5
6
|
/**
|
|
@@ -38,6 +39,7 @@ export interface LogWatchEntry {
|
|
|
38
39
|
createdAt: number;
|
|
39
40
|
lastActivityAt: number;
|
|
40
41
|
stopped: boolean;
|
|
42
|
+
resolution?: ToolResolution;
|
|
41
43
|
}
|
|
42
44
|
export interface RegisterWatchOptions {
|
|
43
45
|
hostname: string;
|
|
@@ -45,6 +47,7 @@ export interface RegisterWatchOptions {
|
|
|
45
47
|
tailResult: TailLogsResult;
|
|
46
48
|
bufferCap?: number;
|
|
47
49
|
bufferBytesCap?: number;
|
|
50
|
+
resolution?: ToolResolution;
|
|
48
51
|
}
|
|
49
52
|
export declare class LogWatchRegistry {
|
|
50
53
|
private cleanupTimer;
|
|
@@ -12,17 +12,14 @@ export function createLogsGetRecentTool(loadServices, serverContext, injections)
|
|
|
12
12
|
const getRecentLogsFn = injections?.getRecentLogs ?? getRecentLogs;
|
|
13
13
|
return createToolAdapter({
|
|
14
14
|
name: 'logs_get_recent',
|
|
15
|
-
description: 'Fetch recent
|
|
16
|
-
'Best for quick lookups of the most recent errors. For monitoring across an action you trigger, ' +
|
|
17
|
-
'use logs_watch_start + logs_watch_poll instead so entries are not missed between calls. ' +
|
|
18
|
-
'Filters (since, level, search) are applied client-side after fetching.',
|
|
15
|
+
description: 'Fetch recent B2C instance logs. Use for quick lookups; start a log watch before actions whose entries must not be missed.',
|
|
19
16
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'SCAPI'],
|
|
20
17
|
requiresInstance: true,
|
|
21
18
|
inputSchema: {
|
|
22
19
|
prefixes: z
|
|
23
20
|
.array(z.string())
|
|
24
21
|
.optional()
|
|
25
|
-
.describe('Log prefixes
|
|
22
|
+
.describe('Log prefixes. Default: ["error", "customerror"]; paths may include subdirectories.'),
|
|
26
23
|
count: z.number().int().positive().optional().describe('Maximum number of entries to return. Defaults to 50.'),
|
|
27
24
|
since: z
|
|
28
25
|
.string()
|
|
@@ -18,7 +18,7 @@ export function createLogsListFilesTool(loadServices, serverContext, injections)
|
|
|
18
18
|
prefixes: z
|
|
19
19
|
.array(z.string())
|
|
20
20
|
.optional()
|
|
21
|
-
.describe('
|
|
21
|
+
.describe('Log-prefix filter; omit for all. Paths may include subdirectories.'),
|
|
22
22
|
sort_by: z.enum(['date', 'name', 'size']).optional().describe('Sort field. Defaults to "date".'),
|
|
23
23
|
sort_order: z.enum(['asc', 'desc']).optional().describe('Sort order. Defaults to "desc".'),
|
|
24
24
|
},
|
|
@@ -12,26 +12,21 @@ export function createLogsWatchStartTool(loadServices, serverContext, injections
|
|
|
12
12
|
const tailLogsFn = injections?.tailLogs ?? tailLogs;
|
|
13
13
|
return createToolAdapter({
|
|
14
14
|
name: 'logs_watch_start',
|
|
15
|
-
description: 'Start a
|
|
16
|
-
'
|
|
17
|
-
'(e.g., a storefront request, a job, a debug session). Then call logs_watch_poll to drain buffered ' +
|
|
18
|
-
'entries (it blocks up to timeout_ms). Always call logs_watch_stop when done. ' +
|
|
19
|
-
'Only one active watch per hostname at a time — use logs_watch_list to find an existing one.',
|
|
15
|
+
description: 'Start a B2C log watch and return watch_id. Start before the target action, poll with logs_watch_poll, ' +
|
|
16
|
+
'and always stop with logs_watch_stop. One active watch per hostname.',
|
|
20
17
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'SCAPI'],
|
|
21
18
|
requiresInstance: true,
|
|
22
19
|
inputSchema: {
|
|
23
20
|
prefixes: z
|
|
24
21
|
.array(z.string())
|
|
25
22
|
.optional()
|
|
26
|
-
.describe('Log prefixes
|
|
23
|
+
.describe('Log prefixes. Default: ["error", "customerror"]; paths may include subdirectories.'),
|
|
27
24
|
last_entries: z
|
|
28
25
|
.number()
|
|
29
26
|
.int()
|
|
30
27
|
.min(0)
|
|
31
28
|
.optional()
|
|
32
|
-
.describe('
|
|
33
|
-
'watch only captures NEW entries (matches the recommended "start before triggering" workflow). ' +
|
|
34
|
-
'Set >0 to include recent context.'),
|
|
29
|
+
.describe('Existing entries per file to emit at startup. Default: 0.'),
|
|
35
30
|
poll_interval_ms: z
|
|
36
31
|
.number()
|
|
37
32
|
.int()
|
|
@@ -97,7 +92,12 @@ export function createLogsWatchStartTool(loadServices, serverContext, injections
|
|
|
97
92
|
// it isn't orphaned (it would otherwise poll WebDAV until process exit).
|
|
98
93
|
let entry;
|
|
99
94
|
try {
|
|
100
|
-
entry = registry.registerWatch({
|
|
95
|
+
entry = registry.registerWatch({
|
|
96
|
+
hostname,
|
|
97
|
+
prefixes,
|
|
98
|
+
tailResult,
|
|
99
|
+
resolution: structuredClone(context.resolution),
|
|
100
|
+
});
|
|
101
101
|
}
|
|
102
102
|
catch (error) {
|
|
103
103
|
await tailResult.stop().catch(() => { });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MrtLogEntry, TailMrtLogsResult } from '@salesforce/b2c-tooling-sdk/operations/mrt';
|
|
2
2
|
import type { ToolExecutionContext } from '../adapter.js';
|
|
3
|
+
import type { ToolResolution } from '../project-context.js';
|
|
3
4
|
/** Maximum number of buffered entries before oldest are evicted. */
|
|
4
5
|
export declare const DEFAULT_BUFFER_CAP = 5000;
|
|
5
6
|
/**
|
|
@@ -42,6 +43,7 @@ export interface MrtLogWatchEntry {
|
|
|
42
43
|
* watch keeps its buffered entries until drained or idle-reaped.
|
|
43
44
|
*/
|
|
44
45
|
stopped: boolean;
|
|
46
|
+
resolution?: ToolResolution;
|
|
45
47
|
}
|
|
46
48
|
export interface RegisterMrtWatchOptions {
|
|
47
49
|
project: string;
|
|
@@ -50,6 +52,7 @@ export interface RegisterMrtWatchOptions {
|
|
|
50
52
|
tailResult: TailMrtLogsResult;
|
|
51
53
|
bufferCap?: number;
|
|
52
54
|
bufferBytesCap?: number;
|
|
55
|
+
resolution?: ToolResolution;
|
|
53
56
|
}
|
|
54
57
|
/**
|
|
55
58
|
* Stable dedup key for an MRT log watch. A developer may tail several
|
|
Binary file
|
|
@@ -11,11 +11,8 @@ const DEFAULT_MAX_ENTRIES = 200;
|
|
|
11
11
|
export function createMrtLogsWatchPollTool(loadServices, serverContext) {
|
|
12
12
|
return createToolAdapter({
|
|
13
13
|
name: 'mrt_logs_watch_poll',
|
|
14
|
-
description: 'Drain buffered
|
|
15
|
-
'
|
|
16
|
-
'truncated=true if there are more entries beyond max_entries — call again to get the rest. When ' +
|
|
17
|
-
'stopped=true the underlying WebSocket has closed (stopped, idle-timed-out, or connection lost); check ' +
|
|
18
|
-
'errors for the reason.',
|
|
14
|
+
description: 'Drain buffered MRT logs, blocking up to timeout_ms when empty. Repeat if truncated=true. ' +
|
|
15
|
+
'stopped=true means the stream closed; inspect errors.',
|
|
19
16
|
toolsets: ['DIAGNOSTICS', 'PWAV3', 'STOREFRONTNEXT'],
|
|
20
17
|
inputSchema: {
|
|
21
18
|
watch_id: z.string().describe('Watch id from mrt_logs_watch_start.'),
|
|
@@ -25,13 +25,8 @@ export function createMrtLogsWatchStartTool(loadServices, serverContext, injecti
|
|
|
25
25
|
const getProfileFn = injections?.getProfile ?? getProfile;
|
|
26
26
|
return createToolAdapter({
|
|
27
27
|
name: 'mrt_logs_watch_start',
|
|
28
|
-
description:
|
|
29
|
-
'
|
|
30
|
-
'call mrt_logs_watch_start BEFORE triggering the request/SSR action you want to capture, then call ' +
|
|
31
|
-
'mrt_logs_watch_poll to drain buffered entries (it blocks up to timeout_ms). Always call ' +
|
|
32
|
-
'mrt_logs_watch_stop when done. Requires MRT project + environment (from --project/--environment flags, ' +
|
|
33
|
-
'MRT_PROJECT/MRT_ENVIRONMENT env vars, or dw.json). Only one active watch per project/environment/origin ' +
|
|
34
|
-
'— use mrt_logs_watch_list to find an existing one.',
|
|
28
|
+
description: 'Start a live MRT application-log stream and return watch_id. Start before the target action, poll with ' +
|
|
29
|
+
'mrt_logs_watch_poll, and always stop with mrt_logs_watch_stop. Requires MRT project and environment.',
|
|
35
30
|
toolsets: ['DIAGNOSTICS', 'PWAV3', 'STOREFRONTNEXT'],
|
|
36
31
|
requiresMrtAuth: true,
|
|
37
32
|
inputSchema: {
|
|
@@ -113,7 +108,13 @@ export function createMrtLogsWatchStartTool(loadServices, serverContext, injecti
|
|
|
113
108
|
// exit).
|
|
114
109
|
let entry;
|
|
115
110
|
try {
|
|
116
|
-
entry = registry.registerWatch({
|
|
111
|
+
entry = registry.registerWatch({
|
|
112
|
+
project,
|
|
113
|
+
environment,
|
|
114
|
+
origin,
|
|
115
|
+
tailResult,
|
|
116
|
+
resolution: structuredClone(context.resolution),
|
|
117
|
+
});
|
|
117
118
|
}
|
|
118
119
|
catch (error) {
|
|
119
120
|
tailResult.stop();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DebugSessionManager, SourceMapper, SdapiBreakpoint, SdapiScriptThread } from '@salesforce/b2c-tooling-sdk/operations/debug';
|
|
2
2
|
import type { CartridgeMapping } from '@salesforce/b2c-tooling-sdk/operations/code';
|
|
3
3
|
import type { ToolExecutionContext } from '../adapter.js';
|
|
4
|
+
import type { ToolResolution } from '../project-context.js';
|
|
4
5
|
export interface HaltWaiter {
|
|
5
6
|
resolve: (thread: SdapiScriptThread) => void;
|
|
6
7
|
reject: (error: Error) => void;
|
|
@@ -17,6 +18,7 @@ export interface DebugSessionEntry {
|
|
|
17
18
|
haltWaiters: HaltWaiter[];
|
|
18
19
|
createdAt: number;
|
|
19
20
|
lastActivityAt: number;
|
|
21
|
+
resolution?: ToolResolution;
|
|
20
22
|
}
|
|
21
23
|
export interface RegisterSessionOptions {
|
|
22
24
|
hostname: string;
|
|
@@ -24,6 +26,7 @@ export interface RegisterSessionOptions {
|
|
|
24
26
|
manager: DebugSessionManager;
|
|
25
27
|
sourceMapper: SourceMapper;
|
|
26
28
|
cartridges: CartridgeMapping[];
|
|
29
|
+
resolution?: ToolResolution;
|
|
27
30
|
}
|
|
28
31
|
export declare class DebugSessionRegistry {
|
|
29
32
|
private cleanupTimer;
|
|
@@ -64,7 +64,7 @@ export class DebugSessionRegistry {
|
|
|
64
64
|
return [...this.sessions.values()];
|
|
65
65
|
}
|
|
66
66
|
registerSession(opts) {
|
|
67
|
-
const { hostname, clientId, manager, sourceMapper, cartridges } = opts;
|
|
67
|
+
const { hostname, clientId, manager, sourceMapper, cartridges, resolution } = opts;
|
|
68
68
|
const existing = this.findByHostAndClientId(hostname, clientId);
|
|
69
69
|
if (existing) {
|
|
70
70
|
throw new Error(`A debug session already exists for ${hostname} ` +
|
|
@@ -84,6 +84,7 @@ export class DebugSessionRegistry {
|
|
|
84
84
|
haltWaiters: [],
|
|
85
85
|
createdAt: now,
|
|
86
86
|
lastActivityAt: now,
|
|
87
|
+
resolution,
|
|
87
88
|
};
|
|
88
89
|
this.sessions.set(sessionId, entry);
|
|
89
90
|
return entry;
|
|
@@ -17,10 +17,9 @@ function toListEntry(entry) {
|
|
|
17
17
|
export function createDocsListTool(loadServices, detectedWorkspaces = [], enabledCategories) {
|
|
18
18
|
return createToolAdapter({
|
|
19
19
|
name: 'docs_list',
|
|
20
|
-
description: '
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'contents; paginated via limit/offset. Use docs_read for content.' +
|
|
20
|
+
description: 'List IDs and titles for B2C Commerce (SFCC/Demandware) Script API, job steps, developer guides, admin/merchant help, and tooling docs. ' +
|
|
21
|
+
'Without a filter, returns category counts. ' +
|
|
22
|
+
'Use docs_search for questions and docs_read for content.' +
|
|
24
23
|
enabledCategoriesNote(enabledCategories) +
|
|
25
24
|
detectedWorkspaceNote(detectedWorkspaces),
|
|
26
25
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'MRT', 'PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
|
|
@@ -32,8 +31,7 @@ export function createDocsListTool(loadServices, detectedWorkspaces = [], enable
|
|
|
32
31
|
workspace: z
|
|
33
32
|
.enum(WORKSPACE_VALUES)
|
|
34
33
|
.optional()
|
|
35
|
-
.describe('
|
|
36
|
-
'or name a type. Omit for the category directory.'),
|
|
34
|
+
.describe('Workspace filter. "auto" uses startup workspace; omit for category directory.'),
|
|
37
35
|
limit: z.number().int().positive().optional().describe(`Max entries per page. Defaults to ${DEFAULT_LIMIT}.`),
|
|
38
36
|
offset: z.number().int().nonnegative().optional().describe('Number of entries to skip (for pagination).'),
|
|
39
37
|
},
|
|
@@ -12,15 +12,8 @@ const DEFAULT_MAX_LENGTH = 12_000;
|
|
|
12
12
|
export function createDocsReadTool(loadServices, enabledCategories, detectedWorkspaces = []) {
|
|
13
13
|
return createToolAdapter({
|
|
14
14
|
name: 'docs_read',
|
|
15
|
-
description: 'Read B2C Commerce
|
|
16
|
-
'
|
|
17
|
-
'query — best match wins (a fuzzy query favors the detected workspace, matching docs_search). ' +
|
|
18
|
-
'Job-step content is bundled; Script API, Developer Center guide, and Salesforce Help content is ' +
|
|
19
|
-
'fetched from its published URL on demand and cached locally (with a summary/headings fallback if ' +
|
|
20
|
-
'the network is unavailable). If you do not know the id, call docs_search first. Long docs are ' +
|
|
21
|
-
'truncated to maxLength chars; page with offset when truncated=true. The returned entry ' +
|
|
22
|
-
'includes the canonical url for citation and relatedEntries ids for directly connected Help or ' +
|
|
23
|
-
'Developer Center articles.' +
|
|
15
|
+
description: 'Read a B2C Commerce (SFCC/Demandware) Script API reference, job step, developer guide, admin/merchant help article, or tooling doc by ID or fuzzy query. ' +
|
|
16
|
+
'Use docs_search to find IDs.' +
|
|
24
17
|
enabledCategoriesNote(enabledCategories),
|
|
25
18
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'MRT', 'PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
|
|
26
19
|
inputSchema: {
|
|
@@ -8,7 +8,7 @@ import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
|
8
8
|
export function createDocsSchemaListTool(loadServices) {
|
|
9
9
|
return createToolAdapter({
|
|
10
10
|
name: 'docs_schema_list',
|
|
11
|
-
description: 'List
|
|
11
|
+
description: 'List bundled B2C Commerce (SFCC/Demandware) XSD schema IDs for docs_schema_read.',
|
|
12
12
|
toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'MRT', 'PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
|
|
13
13
|
inputSchema: {},
|
|
14
14
|
async execute() {
|