@salesforce/b2c-dx-mcp 2.1.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.
Files changed (34) hide show
  1. package/dist/commands/mcp.js +1 -7
  2. package/dist/server.d.ts +1 -1
  3. package/dist/server.js +3 -1
  4. package/dist/services.js +1 -5
  5. package/dist/tools/adapter.d.ts +1 -2
  6. package/dist/tools/adapter.js +1 -1
  7. package/dist/tools/cartridges/index.js +5 -19
  8. package/dist/tools/diagnostics/config-inspect.js +3 -7
  9. package/dist/tools/diagnostics/debug-capture-at-breakpoint.js +3 -9
  10. package/dist/tools/diagnostics/debug-start-session.js +3 -7
  11. package/dist/tools/diagnostics/debug-wait-for-stop.js +1 -3
  12. package/dist/tools/diagnostics/logs-get-recent.js +2 -5
  13. package/dist/tools/diagnostics/logs-list-files.js +1 -1
  14. package/dist/tools/diagnostics/logs-watch-start.js +4 -9
  15. package/dist/tools/diagnostics/mrt-logs-watch-poll.js +2 -5
  16. package/dist/tools/diagnostics/mrt-logs-watch-start.js +2 -7
  17. package/dist/tools/docs/docs-list.js +4 -6
  18. package/dist/tools/docs/docs-read.js +2 -9
  19. package/dist/tools/docs/docs-schema-list.js +1 -1
  20. package/dist/tools/docs/docs-schema-read.js +2 -3
  21. package/dist/tools/docs/docs-schema-search.js +2 -2
  22. package/dist/tools/docs/docs-search.js +3 -9
  23. package/dist/tools/docs/storefront.js +3 -3
  24. package/dist/tools/docs/topics.js +3 -1
  25. package/dist/tools/mrt/index.js +2 -2
  26. package/dist/tools/project-context.d.ts +3 -7
  27. package/dist/tools/project-context.js +7 -23
  28. package/dist/tools/pwav3/pwa-kit-development-guidelines.js +3 -43
  29. package/dist/tools/scapi/metrics-get.js +4 -22
  30. package/dist/tools/scapi/scapi-custom-api-generate-scaffold.js +3 -8
  31. package/dist/tools/scapi/scapi-custom-apis-get-status.js +2 -13
  32. package/dist/tools/scapi/scapi-schemas-list.js +10 -11
  33. package/oclif.manifest.json +1 -1
  34. package/package.json +3 -3
@@ -136,6 +136,7 @@ import path from 'node:path';
136
136
  import { Flags } from '@oclif/core';
137
137
  import { BaseCommand, MrtCommand, InstanceCommand, loadConfig, extractInstanceFlags, extractMrtFlags, } from '@salesforce/b2c-tooling-sdk/cli';
138
138
  import { EnvSource, readProjectEnvironment } from '@salesforce/b2c-tooling-sdk/config';
139
+ // eslint-disable-next-line import/no-unresolved -- SDK 1.30's types export misresolves runtime .js subpaths.
139
140
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
140
141
  import { B2CDxMcpServer } from '../server.js';
141
142
  import { Services } from '../services.js';
@@ -391,13 +392,6 @@ export default class McpServerCommand extends BaseCommand {
391
392
  // Register toolsets with loader function that loads config and creates Services on each tool call
392
393
  // This allows tools to pick up changes to config files (dw.json, ~/.mobify) between invocations
393
394
  const loadServices = this.loadServices.bind(this);
394
- const configuredProjectDirectory = this.flags['project-directory'];
395
- loadServices.projectContextDefaults = {
396
- projectDirectory: {
397
- path: path.resolve(configuredProjectDirectory ?? process.cwd()),
398
- source: configuredProjectDirectory ? 'config' : 'cwd',
399
- },
400
- };
401
395
  await registerToolsets(startupFlags, server, loadServices, this.serverContext);
402
396
  // Connect to stdio transport
403
397
  const transport = new StdioServerTransport();
package/dist/server.d.ts CHANGED
@@ -2,7 +2,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import type { CallToolResult, Implementation } from '@modelcontextprotocol/sdk/types.js';
3
3
  import type { ServerOptions } from '@modelcontextprotocol/sdk/server/index.js';
4
4
  import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
5
- import type { ZodRawShape } from 'zod';
5
+ import { type ZodRawShape } from 'zod';
6
6
  import type { Telemetry } from '@salesforce/b2c-tooling-sdk/telemetry';
7
7
  /**
8
8
  * Extended server options.
package/dist/server.js CHANGED
@@ -3,7 +3,9 @@
3
3
  * SPDX-License-Identifier: Apache-2
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
+ // eslint-disable-next-line import/no-unresolved -- SDK 1.30's types export misresolves runtime .js subpaths.
6
7
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
8
+ import { z } from 'zod';
7
9
  import { getLogger } from '@salesforce/b2c-tooling-sdk/logging';
8
10
  /**
9
11
  * A server implementation that extends the base MCP server.
@@ -79,7 +81,7 @@ export class B2CDxMcpServer extends McpServer {
79
81
  }
80
82
  };
81
83
  // Use the new registerTool API (tool() is deprecated)
82
- this.registerTool(name, { description, inputSchema }, wrappedHandler);
84
+ this.registerTool(name, { description, inputSchema: z.object(inputSchema).strict() }, wrappedHandler);
83
85
  }
84
86
  /**
85
87
  * Connect to a transport.
package/dist/services.js CHANGED
@@ -388,11 +388,7 @@ export class Services {
388
388
  if (override) {
389
389
  return { path: override, source: 'argument' };
390
390
  }
391
- const configured = this.resolvedConfig.values.projectDirectory;
392
- if (configured) {
393
- return { path: configured, source: 'config' };
394
- }
395
- return { path: process.cwd(), source: 'cwd' };
391
+ return { ...this.resolution.projectDirectory };
396
392
  }
397
393
  /**
398
394
  * Resolve a path relative to the project directory.
@@ -69,11 +69,10 @@ 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
71
  import type { ServerContext } from '../server-context.js';
72
- import { type DirectoryResolutionInfo, type ProjectContextDefaults, type ProjectContextInput, type ToolResolution } from './project-context.js';
72
+ import { type DirectoryResolutionInfo, type ProjectContextInput, type ToolResolution } from './project-context.js';
73
73
  /** Services loader enriched with registration-time project fallback provenance. */
74
74
  export interface ServicesLoader {
75
75
  (projectContext?: ProjectContextInput): Promise<Services> | Services;
76
- projectContextDefaults?: ProjectContextDefaults;
77
76
  }
78
77
  /**
79
78
  * Context provided to tool execute functions.
@@ -197,7 +197,7 @@ export function createToolAdapter(options, loadServices, serverContext) {
197
197
  : undefined;
198
198
  const effectiveInputSchema = projectContextKind
199
199
  ? {
200
- ...createProjectContextInputSchema(projectContextKind, loadServices.projectContextDefaults),
200
+ ...createProjectContextInputSchema(projectContextKind),
201
201
  ...inputSchema,
202
202
  }
203
203
  : inputSchema;
@@ -35,12 +35,8 @@ function createCartridgeDeployTool(loadServices, injections) {
35
35
  const getActiveCodeVersionFn = injections?.getActiveCodeVersion || getActiveCodeVersion;
36
36
  return createToolAdapter({
37
37
  name: 'cartridge_deploy',
38
- description: 'Finds and deploys cartridges to a B2C Commerce instance via WebDAV. ' +
39
- 'Searches the directory for cartridges (by .project files), applies include/exclude filters, ' +
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,
@@ -57,19 +53,9 @@ function createCartridgeDeployTool(loadServices, injections) {
57
53
  cartridges: z
58
54
  .array(z.string())
59
55
  .optional()
60
- .describe('Array of cartridge names to include in the deployment. If not specified, all cartridges found in the directory are deployed. ' +
61
- 'Use this to selectively deploy specific cartridges when you have multiple cartridges but only want to update some.'),
62
- exclude: z
63
- .array(z.string())
64
- .optional()
65
- .describe('Array of cartridge names to exclude from the deployment. Use this to skip deploying certain cartridges, ' +
66
- 'such as third-party or unchanged cartridges. Applied after the include filter.'),
67
- reload: z
68
- .boolean()
69
- .optional()
70
- .describe('Whether to reload (re-activate) the code version after deployment. ' +
71
- 'Set to true to make the deployed code immediately active on the instance. ' +
72
- '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.'),
73
59
  },
74
60
  async execute(args, context) {
75
61
  // Get instance from context (guaranteed by adapter when requiresInstance is true)
@@ -18,10 +18,8 @@ import { createToolAdapter, jsonResult } from '../adapter.js';
18
18
  export function createConfigInspectTool(loadServices) {
19
19
  return createToolAdapter({
20
20
  name: 'config_inspect',
21
- description: 'Inspect the resolved B2C Commerce configuration the MCP server is using — instance hostname, auth, SCAPI, MRT, and other settings — along with which source (dw.json, environment variables, flags) provided each value. ' +
22
- 'Secrets (passwords, client secrets, API keys) are redacted by default. ' +
23
- 'Pass projectDirectory, configPath, and/or instanceName to inspect the same project, configuration catalog, and named instance another tool would use. The detailed source graph is supplemented by the same compact resolution provenance returned by other configuration-aware tools. ' +
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,
@@ -30,14 +28,12 @@ export function createConfigInspectTool(loadServices) {
30
28
  unmask: z
31
29
  .boolean()
32
30
  .optional()
33
- .describe('Show sensitive values (passwords, secrets, API keys) unmasked. Defaults to false secrets are redacted. Only set this when the user explicitly needs the raw secret values.'),
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 trigger an HTTP request, wait for the breakpoint to be hit, and capture a diagnostic snapshot (stack, variables, expression results). ' +
19
- 'Use trigger_url to have the tool fire the request itself (recommended) this avoids needing to coordinate a separate request while the tool blocks. ' +
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);
@@ -12,16 +12,14 @@ 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 on a B2C Commerce instance to debug SFRA controllers, custom API scripts, hooks, jobs, or any server-side script. ' +
16
- 'Uses projectDirectory to load project configuration and discover cartridges. cartridgeDirectory may override only the cartridge discovery/source-mapping root. ' +
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('Optional cartridge discovery and debugger source-mapping root. Relative paths resolve from projectDirectory. Defaults to projectDirectory.'),
22
+ .describe('Cartridge discovery and source-mapping root; relative to projectDirectory.'),
25
23
  },
26
24
  usesConfigurationContext: true,
27
25
  async execute(args, context) {
@@ -33,7 +31,6 @@ 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);
38
35
  context.setResolvedDirectory('cartridgeDirectory', {
39
36
  path: cartridgeDir,
@@ -81,7 +78,6 @@ export function createDebugStartSessionTool(loadServices, serverContext) {
81
78
  cartridge_mappings: cartridgeMappings,
82
79
  session_cookie: dwsid ? { name: 'dwsid', value: dwsid } : null,
83
80
  warnings,
84
- projectDirectory,
85
81
  cartridgeDirectory: cartridgeDir,
86
82
  };
87
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 at a breakpoint or step. ' +
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.'),
@@ -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 log entries from the configured B2C Commerce instance in a single request/response. ' +
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 to read. Defaults to ["error", "customerror"]. Use a path like "internal/server" to read logs from a subdirectory.'),
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('Filter by log prefixes (e.g., ["error", "customerror"]). Returns all when omitted. Use a path like "internal/server" to list logs in a subdirectory.'),
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 background log watch on the configured B2C Commerce instance. Returns a watch_id immediately. ' +
16
- 'Recommended workflow: call logs_watch_start BEFORE triggering the action that should produce logs ' +
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 to watch. Defaults to ["error", "customerror"]. Use a path like "internal/server" to watch logs in a subdirectory.'),
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('Number of pre-existing entries per file to emit on startup. Defaults to 0 so a fresh ' +
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()
@@ -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 entries from an MRT log watch. If the buffer is empty, blocks up to timeout_ms waiting for ' +
15
- 'new entries. Returns immediately if entries are already buffered or the stream has stopped. Set ' +
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: "Start a background tail of a Managed Runtime (MRT) environment's application logs over a WebSocket. " +
29
- 'Returns a watch_id immediately. MRT logs are always a live stream there is no historical fetch — so ' +
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: {
@@ -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: 'Enumerate B2C Commerce documentation entries (id + title + category only) for a category or workspace. ' +
21
- 'Prefer docs_search for questions — this tool is for browsing a known category. Without a category or ' +
22
- 'workspace it returns just a category directory (counts), not the full corpus. Results are a table of ' +
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('Limit to a workspace\'s relevant categories. "auto" uses the auto-detected workspace; ' +
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 documentation (markdown) for a class, module, job step, guide, or Help article. ' +
16
- 'Accepts an exact id (e.g. "dw.catalog.ProductMgr", "sfnext/sfnext-get-started") or a fuzzy ' +
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 every available B2C Commerce XSD schema id. Use to discover schema names for docs_schema_read.',
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() {
@@ -9,9 +9,8 @@ import { createToolAdapter, errorResult, jsonResult } from '../adapter.js';
9
9
  export function createDocsSchemaReadTool(loadServices) {
10
10
  return createToolAdapter({
11
11
  name: 'docs_schema_read',
12
- description: 'Read the contents of a bundled B2C Commerce XSD schema (raw XML). ' +
13
- 'Accepts an exact id or fuzzy query. Returns the schema body plus the on-disk path. ' +
14
- 'Schemas can be large; if you do not know the id, call docs_schema_search first.',
12
+ description: 'Read a bundled B2C Commerce (SFCC/Demandware) XSD schema as XML by ID or fuzzy query. ' +
13
+ 'Use docs_schema_search to find IDs.',
15
14
  toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'MRT', 'PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
16
15
  inputSchema: {
17
16
  query: z.string().min(1).describe('Schema name or partial match.'),
@@ -9,8 +9,8 @@ import { createToolAdapter, jsonResult } from '../adapter.js';
9
9
  export function createDocsSchemaSearchTool(loadServices) {
10
10
  return createToolAdapter({
11
11
  name: 'docs_schema_search',
12
- description: 'Fuzzy-search bundled B2C Commerce XSD schemas by id (e.g., "catalog", "order", "system-objecttype"). ' +
13
- 'Returns matching schema ids + relevance score. Use BEFORE docs_schema_read when the exact id is unknown.',
12
+ description: 'Search bundled B2C Commerce (SFCC/Demandware) XSD schemas by ID. ' +
13
+ 'Returns matching IDs and scores; use docs_schema_read for content.',
14
14
  toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'MRT', 'PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
15
15
  inputSchema: {
16
16
  query: z.string().min(1).describe('Schema name or partial match (e.g., "catalog", "order").'),
@@ -38,13 +38,8 @@ function leanResult(entry, score, verbose) {
38
38
  export function createDocsSearchTool(loadServices, detectedWorkspaces = [], enabledCategories) {
39
39
  return createToolAdapter({
40
40
  name: 'docs_search',
41
- description: 'PRIMARY entry point for B2C Commerce docs: Script API reference (e.g. "ProductMgr"), standard job steps, ' +
42
- 'Developer Center guides (commerce-api, pwa-kit-managed-runtime, sfnext, sfra, b2c-commerce), and this ' +
43
- "tooling's own guides. Use for ANY B2C Commerce developer or admin question not already grounded in a " +
44
- 'loaded skill or the current project. Content-aware ranking — pass a natural-language query (prefer this ' +
45
- 'over docs_list, which only enumerates). Optionally restrict by category or workspace. Returns id, title, ' +
46
- 'category, summary, and score for triage; pass verbose=true for keywords+url. Call this BEFORE docs_read ' +
47
- 'when you do not know the exact id.' +
41
+ description: 'Search B2C Commerce (SFCC/Demandware) Script API, job steps, developer guides, admin/merchant help, and tooling docs. ' +
42
+ 'Use for natural-language queries or unknown IDs; call docs_read with a result ID.' +
48
43
  enabledCategoriesNote(enabledCategories) +
49
44
  detectedWorkspaceNote(detectedWorkspaces),
50
45
  toolsets: ['CARTRIDGES', 'DIAGNOSTICS', 'MRT', 'PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
@@ -54,8 +49,7 @@ export function createDocsSearchTool(loadServices, detectedWorkspaces = [], enab
54
49
  workspace: z
55
50
  .enum(WORKSPACE_VALUES)
56
51
  .optional()
57
- .describe('Workspace context. "auto" (default) favors the auto-detected workspace\'s docs; ' +
58
- '"all" disables the preference; or name a type (cartridges, sfra, pwa-kit-v3, storefront-next).'),
52
+ .describe('"auto" uses startup workspace; "all" disables weighting; or select a workspace type.'),
59
53
  limit: z
60
54
  .number()
61
55
  .int()
@@ -9,8 +9,8 @@
9
9
  */
10
10
  export const PROJECT_TYPE_LABELS = {
11
11
  cartridges: 'Cartridges',
12
- sfra: 'SFRA (cartridges)',
13
- 'pwa-kit-v3': 'PWA Kit (Composable Storefront)',
12
+ sfra: 'SFRA',
13
+ 'pwa-kit-v3': 'PWA Kit',
14
14
  'storefront-next': 'Storefront Next',
15
15
  };
16
16
  /**
@@ -43,6 +43,6 @@ export function detectedWorkspaceNote(detected) {
43
43
  if (detected.length === 0)
44
44
  return '';
45
45
  const labels = detected.map((t) => PROJECT_TYPE_LABELS[t] ?? t).join(' + ');
46
- return ` Detected workspace: ${labels} — by default results favor this workspace's docs (pass workspace="all" to disable).`;
46
+ return ` Workspace at startup: ${labels}.`;
47
47
  }
48
48
  //# sourceMappingURL=storefront.js.map
@@ -33,6 +33,8 @@ export function categoryEnumValues(enabledCategories) {
33
33
  export function enabledCategoriesNote(enabledCategories) {
34
34
  if (!enabledCategories || enabledCategories.length === 0)
35
35
  return '';
36
- return ` Documentation is restricted at startup to: ${enabledCategories.join(', ')}.`;
36
+ if (DOC_CATEGORIES.every((category) => enabledCategories.includes(category)))
37
+ return '';
38
+ return ` Topics: ${enabledCategories.join(', ')}.`;
37
39
  }
38
40
  //# sourceMappingURL=topics.js.map
@@ -137,11 +137,11 @@ function createMrtBundlePushTool(loadServices, injections) {
137
137
  ssrOnly: z
138
138
  .string()
139
139
  .optional()
140
- .describe('Glob patterns for server-only files (comma-separated or JSON array). Defaults vary by project type: Storefront Next, PWA Kit v3, or generic.'),
140
+ .describe('Server-only globs; comma-separated or JSON array. Defaults by project type.'),
141
141
  ssrShared: z
142
142
  .string()
143
143
  .optional()
144
- .describe('Glob patterns for shared files (comma-separated or JSON array). Defaults vary by project type: Storefront Next, PWA Kit v3, or generic.'),
144
+ .describe('Shared-file globs; comma-separated or JSON array. Defaults by project type.'),
145
145
  deploy: z
146
146
  .boolean()
147
147
  .optional()
@@ -33,20 +33,16 @@ export interface ToolResolution {
33
33
  directories?: Record<string, DirectoryResolutionInfo>;
34
34
  projectDirectory: ProjectDirectoryInfo;
35
35
  }
36
- /** Defaults known when MCP tool schemas are registered. */
37
- export interface ProjectContextDefaults {
38
- projectDirectory: ProjectDirectoryInfo;
39
- }
40
36
  /** Whether a tool needs only a project root or full configuration selection. */
41
37
  export type ProjectContextKind = 'configuration' | 'project';
42
- /** Build the canonical project-directory field with the effective fallback embedded in its description. */
43
- export declare function createProjectDirectoryInput(defaults?: ProjectContextDefaults): z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
38
+ /** Build the canonical project-directory field. */
39
+ export declare function createProjectDirectoryInput(): z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
44
40
  /** Build the canonical explicit primary dw.json field. */
45
41
  export declare function createConfigPathInput(): z.ZodOptional<z.ZodString>;
46
42
  /** Build the canonical named-instance selection field. */
47
43
  export declare function createInstanceNameInput(): z.ZodOptional<z.ZodString>;
48
44
  /** Build flat canonical schema fields for a local-project or configuration-aware tool. */
49
- export declare function createProjectContextInputSchema(kind: ProjectContextKind, defaults?: ProjectContextDefaults): ZodRawShape;
45
+ export declare function createProjectContextInputSchema(kind: ProjectContextKind): ZodRawShape;
50
46
  /** Static field for schemas declared outside the shared adapter. */
51
47
  export declare const projectDirectoryInput: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
52
48
  /** Static configuration schema for legacy/manual tool definitions. */
@@ -5,41 +5,25 @@
5
5
  */
6
6
  import path from 'node:path';
7
7
  import { z } from 'zod';
8
- function defaultProjectContext() {
9
- return { projectDirectory: { path: process.cwd(), source: 'cwd' } };
10
- }
11
- /** Build the canonical project-directory field with the effective fallback embedded in its description. */
12
- export function createProjectDirectoryInput(defaults = defaultProjectContext()) {
13
- const fallback = defaults.projectDirectory;
14
- const sourceDescription = fallback.source === 'cwd'
15
- ? 'the MCP process working directory'
16
- : 'the server-level --project-directory / SFCC_PROJECT_DIRECTORY value';
8
+ /** Build the canonical project-directory field. */
9
+ export function createProjectDirectoryInput() {
17
10
  return z
18
11
  .string()
19
12
  .refine((value) => path.isAbsolute(value), 'projectDirectory must be an absolute path')
20
13
  .optional()
21
- .describe(`Optional absolute project root for this call. Overrides the server-level project directory. ` +
22
- `When omitted, uses ${sourceDescription}: ${fallback.path}`);
14
+ .describe('Absolute project root; overrides server default. See config_inspect for resolved paths.');
23
15
  }
24
16
  /** Build the canonical explicit primary dw.json field. */
25
17
  export function createConfigPathInput() {
26
- return z
27
- .string()
28
- .optional()
29
- .describe('Optional path to a dw.json-format configuration file. Relative paths resolve from projectDirectory. ' +
30
- 'Selects the primary file ahead of server and project automatic selection; the shared default dw.json remains available as a fallback and for named-instance lookup.');
18
+ return z.string().optional().describe('Path to dw.json-format config; relative to projectDirectory.');
31
19
  }
32
20
  /** Build the canonical named-instance selection field. */
33
21
  export function createInstanceNameInput() {
34
- return z
35
- .string()
36
- .min(1)
37
- .optional()
38
- .describe('Optional named instance to select from the resolved primary and default dw.json files. The primary file is searched first. When omitted, the active/default instance is used.');
22
+ return z.string().min(1).optional().describe('Named instance from dw.json.');
39
23
  }
40
24
  /** Build flat canonical schema fields for a local-project or configuration-aware tool. */
41
- export function createProjectContextInputSchema(kind, defaults = defaultProjectContext()) {
42
- const project = { projectDirectory: createProjectDirectoryInput(defaults) };
25
+ export function createProjectContextInputSchema(kind) {
26
+ const project = { projectDirectory: createProjectDirectoryInput() };
43
27
  if (kind === 'project')
44
28
  return project;
45
29
  return {
@@ -61,16 +61,6 @@ const SECTIONS_METADATA = [
61
61
  * Derived: array of section keys for validation.
62
62
  */
63
63
  const _SECTIONS = SECTIONS_METADATA.map((s) => s.key);
64
- /**
65
- * Generates the topics list for the tool description.
66
- * Excludes meta-sections (like quick-reference) that don't have descriptions.
67
- * @returns Comma-separated list of topics
68
- */
69
- function generateTopicsList() {
70
- return SECTIONS_METADATA.filter((s) => s.description !== null)
71
- .map((s) => s.description)
72
- .join(', ');
73
- }
74
64
  /**
75
65
  * Detailed section content loaded from markdown files.
76
66
  * Built dynamically from SECTIONS_METADATA to avoid duplication.
@@ -96,12 +86,7 @@ const DEFAULT_SECTIONS = ['quick-reference', 'components', 'data-fetching', 'rou
96
86
  export function createDeveloperGuidelinesTool(loadServices) {
97
87
  return createToolAdapter({
98
88
  name: 'pwakit_get_guidelines',
99
- description: 'ESSENTIAL FIRST STEP for PWA Kit v3 development. Returns critical architecture rules, coding standards, and best practices. ' +
100
- 'Use this tool FIRST before writing any PWA Kit code to understand non-negotiable patterns for React components, ' +
101
- 'data fetching, routing, configuration, and framework constraints. Returns comprehensive guidelines by default (quick-reference + key sections); ' +
102
- 'supports retrieving specific topic sections. ' +
103
- 'CRITICAL INSTRUCTION: ALWAYS present ALL returned content in FULL - DO NOT SUMMARIZE, DO NOT ADD SUMMARIES, ' +
104
- 'DO NOT ADD OVERVIEWS. The returned content IS the complete answer - display it exactly as provided.',
89
+ description: 'Get PWA Kit v3 architecture and implementation guidelines. Returns core sections by default; use sections to narrow the result.',
105
90
  toolsets: ['PWAV3'],
106
91
  isGA: true,
107
92
  requiresInstance: false,
@@ -109,13 +94,7 @@ export function createDeveloperGuidelinesTool(loadServices) {
109
94
  sections: z
110
95
  .array(z.enum([..._SECTIONS]))
111
96
  .optional()
112
- .describe('Optional array of specific sections to retrieve. If not specified, returns comprehensive guidelines ' +
113
- '(quick-reference, components, data-fetching, routing). ' +
114
- 'CRITICAL: Present ALL returned content in FULL - DO NOT SUMMARIZE. ' +
115
- 'Available sections: quick-reference, components, data-fetching, routing, config, state-management, ' +
116
- 'extensibility, testing, i18n, styling. ' +
117
- `Topics covered: ${generateTopicsList()}. ` +
118
- 'Content is complete - present exactly as provided, no summaries.'),
97
+ .describe('Guideline sections to return; defaults to core sections.'),
119
98
  },
120
99
  async execute(args) {
121
100
  // Handle empty array case explicitly
@@ -124,26 +103,7 @@ export function createDeveloperGuidelinesTool(loadServices) {
124
103
  }
125
104
  // Default to comprehensive set of key sections if no sections specified
126
105
  const sections = args.sections || DEFAULT_SECTIONS;
127
- // Multiple sections: combine with separators
128
- const combinedContent = sections.map((section) => SECTION_CONTENT[section]).join('\n\n---\n\n');
129
- // Apply instructions for all multi-section responses to ensure full content display
130
- const isMultiSection = sections.length > 1;
131
- // Prepend explicit instruction to present full content (not summarized)
132
- const fullContentInstruction = isMultiSection
133
- ? '⚠️ CRITICAL: Display the FULL content below. DO NOT summarize, condense, or add overviews.\n\n' +
134
- '📋 PWA KIT DEVELOPMENT GUIDELINES\n\n' +
135
- '---\n\n'
136
- : '';
137
- // Add footer instruction to reinforce the message for multi-section responses
138
- const footerInstruction = isMultiSection
139
- ? '\n\n---\n\n⚠️ END OF CONTENT - Full content displayed above. Do not add summaries.\n'
140
- : '';
141
- // For single sections, return directly (backward compatible)
142
- // For multiple sections, wrap with instructions
143
- if (sections.length === 1) {
144
- return SECTION_CONTENT[sections[0]];
145
- }
146
- return fullContentInstruction + combinedContent + footerInstruction;
106
+ return sections.map((section) => SECTION_CONTENT[section]).join('\n\n---\n\n');
147
107
  },
148
108
  formatOutput: (output) => textResult(output),
149
109
  }, loadServices);
@@ -40,26 +40,8 @@ import { getMetricsByCategory, resolveMetricsWindow, enrichMetricsTags, } from '
40
40
  export function createMetricsGetTool(loadServices) {
41
41
  return createToolAdapter({
42
42
  name: 'metrics_get',
43
- description: `CLOSED BETA: the Metrics API must be enabled for your organization, and its behavior, output, and OAuth scopes may change without notice.
44
-
45
- Retrieve observability metrics time-series for a B2C Commerce tenant. Returns metrics data grouped by category with time-series data points.
46
-
47
- **Categories:**
48
- - overall: Aggregate site metrics (requests, response times, errors)
49
- - sales: Sales and order metrics
50
- - ecdn: Edge CDN performance metrics
51
- - third-party: External service metrics (use thirdPartyServiceId filter)
52
- - scapi: SCAPI endpoint metrics (use apiFamily/apiName filters)
53
- - scapi-hooks: SCAPI hooks execution metrics
54
- - mrt: Managed Runtime (PWA Kit) metrics
55
- - controller: Controller execution metrics
56
- - ocapi: OCAPI endpoint metrics (use ocapiCategory/ocapiApi filters)
57
-
58
- **Time window:** Provide "from" and/or "to" as a relative duration ("1h", "7d" — interpreted as ago) or an ISO 8601 timestamp, and/or "window" as a duration ("1h", "30m"). The tool always sends an explicit from+to range, defaulting to a 24-hour window: from + window → to = from + window; to + window → from = to - window; window alone → the last <window>; from alone → 24h forward from it (capped at now); to alone → 24h back from it; nothing → the last 24h. Do not supply from, to, and window together. The API caps a window at 24h and retains 30 days; an explicit range wider than 24h is sent as-is and the API returns a clear error.
59
-
60
- **Response:** { query, data } — "query" echoes the resolved from/to (ISO + epoch seconds), filters, and defaultedWindow/clampedFrom flags; "data[]" contains metricId, title, description, unit, and dataSeries[] with time-series points (timestamp in epoch milliseconds, value). Each series also carries a structured "tags" object (realm, environment, any applied filters, and per-series dimensions like apiFamily/host/cacheStatus) parsed client-side from the packed series id — use these to group/filter rather than parsing the series id string.
61
-
62
- **Requirements:** OAuth with sfcc.metrics scope.`,
43
+ description: 'CLOSED BETA. Retrieve B2C observability metric time series by category and time range. ' +
44
+ 'Defaults to the last 24 hours. Requires Metrics API access and OAuth scope sfcc.metrics.',
63
45
  toolsets: ['SCAPI'],
64
46
  isGA: false,
65
47
  requiresInstance: false, // SCAPI uses OAuth directly
@@ -67,7 +49,7 @@ Retrieve observability metrics time-series for a B2C Commerce tenant. Returns me
67
49
  inputSchema: {
68
50
  category: z
69
51
  .enum(['overall', 'sales', 'ecdn', 'third-party', 'scapi', 'scapi-hooks', 'mrt', 'controller', 'ocapi'])
70
- .describe('Metrics category: overall (aggregate), sales, ecdn (CDN), third-party (external), scapi (SCAPI APIs), scapi-hooks, mrt (PWA Kit), controller, ocapi'),
52
+ .describe('Metrics category.'),
71
53
  from: z
72
54
  .string()
73
55
  .optional()
@@ -79,7 +61,7 @@ Retrieve observability metrics time-series for a B2C Commerce tenant. Returns me
79
61
  window: z
80
62
  .string()
81
63
  .optional()
82
- .describe('Window duration ("1h", "30m", "2d"). With from to=from+window; with to → from=to-window; alone the last <window>. Defaults to 24h.'),
64
+ .describe('Duration combined with from or to; alone selects the latest window. Default: 24h.'),
83
65
  thirdPartyServiceId: z
84
66
  .string()
85
67
  .optional()
@@ -147,23 +147,18 @@ export async function executeScaffoldCustomApi(args, services, overrides) {
147
147
  export function createScaffoldCustomApiTool(loadServices, executeOverrides) {
148
148
  return createToolAdapter({
149
149
  name: 'scapi_custom_api_generate_scaffold',
150
- description: `Generate a new custom SCAPI endpoint (OAS 3.0 schema, api.json, script.js) in an existing cartridge. \
151
- Required: apiName (kebab-case). Optional: cartridgeName (defaults to first cartridge found in project), apiType (shopper|admin) default to shopper, \
152
- apiDescription, cartridgeDirectory, outputDirectory.`,
150
+ description: 'Generate a custom SCAPI endpoint scaffold in an existing cartridge: OpenAPI schema, api.json, and script.js. apiName must be kebab-case.',
153
151
  toolsets: ['PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
154
152
  isGA: true,
155
153
  requiresInstance: false,
156
154
  usesProjectContext: true,
157
155
  inputSchema: {
158
- apiName: z
159
- .string()
160
- .min(1)
161
- .describe('API name in kebab-case (e.g. my-products). Must start with lowercase letter, only letters, numbers, hyphens.'),
156
+ apiName: z.string().min(1).describe('Kebab-case API name starting with a lowercase letter.'),
162
157
  cartridgeName: z
163
158
  .string()
164
159
  .min(1)
165
160
  .nullish()
166
- .describe('Cartridge name that will contain the API. Optional; omit to use the first cartridge found under project root).'),
161
+ .describe('Target cartridge; defaults to the first discovered cartridge.'),
167
162
  apiType: z
168
163
  .enum(['admin', 'shopper'])
169
164
  .optional()
@@ -82,15 +82,7 @@ function buildResponse(withMeta, args, columnList, activeCodeVersion) {
82
82
  export function createScapiCustomApisStatusTool(loadServices) {
83
83
  return createToolAdapter({
84
84
  name: 'scapi_custom_apis_get_status',
85
- description: `List Custom SCAPI endpoint registration status (active/not_registered). Returns one row per endpoint per site. For schemas, use scapi_schemas_list with apiFamily: "custom".
86
-
87
- Use cases: Check endpoint status, verify deployment, get per-site details. Use status: "active" to filter, groupBy: "site" to group, columns: "field1,field2" for specific fields, or omit columns for defaults.
88
-
89
- Output: Default (7 fields): type,apiName,cartridgeName,endpointPath,httpMethod,status,siteId. All fields: type,apiName,apiVersion,cartridgeName,endpointPath,httpMethod,status,siteId,securityScheme,operationId,schemaFile,implementationScript,errorReason,id.
90
-
91
- Requires OAuth (sfcc.custom-apis scope) and instance config (shortCode, tenantId). Returns remoteError on failure.
92
-
93
- CLI: b2c scapi custom status`,
85
+ description: 'List Custom SCAPI endpoint registration status per site. Supports filtering, grouping, and selected columns. Requires shortCode, tenantId, and sfcc.custom-apis scope. Use scapi_schemas_list for schemas.',
94
86
  toolsets: ['PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
95
87
  isGA: true,
96
88
  requiresInstance: false,
@@ -98,10 +90,7 @@ CLI: b2c scapi custom status`,
98
90
  inputSchema: {
99
91
  status: z.enum(['active', 'not_registered']).optional().describe('Filter by status. Omit for all.'),
100
92
  groupBy: z.enum(['site', 'type']).optional().describe('Group by siteId or type (Admin/Shopper).'),
101
- columns: z
102
- .string()
103
- .optional()
104
- .describe('Comma-separated fields. Omit for defaults (7 fields). All fields: type,apiName,apiVersion,cartridgeName,endpointPath,httpMethod,status,siteId,securityScheme,operationId,schemaFile,implementationScript,errorReason,id'),
93
+ columns: z.string().optional().describe('Comma-separated output fields; omit for defaults.'),
105
94
  },
106
95
  async execute(args, { services: svc }) {
107
96
  let endpoints = [];
@@ -37,6 +37,13 @@ import { collapseOpenApiSchema } from '@salesforce/b2c-tooling-sdk/schemas';
37
37
  function buildScapiApiUrl(shortCode, apiFamily, apiName, apiVersion) {
38
38
  return `https://${shortCode}.api.commercecloud.salesforce.com/${apiFamily}/${apiName}/${apiVersion}`;
39
39
  }
40
+ function getSchemasApiError(error, response) {
41
+ const message = getApiErrorMessage(error, response);
42
+ if (response.status === 401 || response.status === 403) {
43
+ return `${message}. Verify OAuth credentials include the sfcc.scapi-schemas scope.`;
44
+ }
45
+ return message;
46
+ }
40
47
  /**
41
48
  * Fetches a specific schema from the SCAPI Schemas API.
42
49
  *
@@ -60,7 +67,7 @@ async function fetchSpecificSchema(params) {
60
67
  },
61
68
  });
62
69
  if (error) {
63
- throw new Error(`Failed to fetch schema for ${apiFamily}/${apiName}/${apiVersion}: ${getApiErrorMessage(error, response)}`);
70
+ throw new Error(`Failed to fetch schema for ${apiFamily}/${apiName}/${apiVersion}: ${getSchemasApiError(error, response)}`);
64
71
  }
65
72
  // Apply collapsing unless expandAll is requested
66
73
  const collapsed = !expandAll;
@@ -106,7 +113,7 @@ async function fetchSchemasList(params) {
106
113
  },
107
114
  });
108
115
  if (error) {
109
- throw new Error(`Failed to fetch SCAPI schemas: ${getApiErrorMessage(error, response)}`);
116
+ throw new Error(`Failed to fetch SCAPI schemas: ${getSchemasApiError(error, response)}`);
110
117
  }
111
118
  const schemas = data?.data ?? [];
112
119
  const filteredSchemas = prepareSchemaListForConsumer(schemas, shortCode);
@@ -187,15 +194,7 @@ function getAvailableFilters(schemas) {
187
194
  export function createScapiSchemasListTool(loadServices) {
188
195
  return createToolAdapter({
189
196
  name: 'scapi_schemas_list',
190
- description: `List or fetch SCAPI schema metadata and OpenAPI specs for standard SCAPI (Shop/Admin/Shopper) and custom APIs (apiFamily: "custom"). For endpoint registration status, use scapi_custom_apis_get_status.
191
-
192
- **Modes:**
193
- - **List (discovery):** Omit includeSchemas or any identifier. Returns metadata: schemas[], total, availableApiFamilies/Names/Versions.
194
- - **Fetch:** Set includeSchemas=true + all three: apiFamily, apiName, apiVersion. Returns full OpenAPI schema (collapsed by default; set expandAll=true for full).
195
-
196
- **Rules:** includeSchemas requires all three identifiers. status only works in list mode (use "current" for active schemas, "deprecated" for phased-out schemas). Custom APIs use apiFamily: "custom".
197
-
198
- **Requirements:** OAuth with sfcc.scapi-schemas scope.`,
197
+ description: 'List SCAPI schema metadata or fetch an OpenAPI schema. Fetch requires includeSchemas, apiFamily, apiName, and apiVersion. Use scapi_custom_apis_get_status for endpoint status.',
199
198
  toolsets: ['PWAV3', 'SCAPI', 'STOREFRONTNEXT'],
200
199
  isGA: true,
201
200
  requiresInstance: false, // SCAPI uses OAuth directly, doesn't need B2CInstance (hostname)
@@ -429,5 +429,5 @@
429
429
  "enableJsonFlag": false
430
430
  }
431
431
  },
432
- "version": "2.1.0"
432
+ "version": "2.1.1"
433
433
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/b2c-dx-mcp",
3
3
  "description": "MCP server for B2C Commerce developer experience tools",
4
- "version": "2.1.0",
4
+ "version": "2.1.1",
5
5
  "author": "Salesforce",
6
6
  "license": "Apache-2.0",
7
7
  "repository": "SalesforceCommerceCloud/b2c-developer-tooling",
@@ -73,7 +73,7 @@
73
73
  }
74
74
  },
75
75
  "dependencies": {
76
- "@modelcontextprotocol/sdk": "1.26.0",
76
+ "@modelcontextprotocol/sdk": "1.30.0",
77
77
  "@oclif/core": "4.8.0",
78
78
  "glob": "13.0.0",
79
79
  "ts-morph": "27.0.2",
@@ -85,7 +85,7 @@
85
85
  "devDependencies": {
86
86
  "@eslint/compat": "^1",
87
87
  "@eslint/js": "^9",
88
- "@modelcontextprotocol/inspector": "^0.18.0",
88
+ "@modelcontextprotocol/inspector": "2.2.0",
89
89
  "@oclif/prettier-config": "^0.2.1",
90
90
  "@salesforce/dev-config": "^4.3.2",
91
91
  "@types/chai": "^4",