@salesforce/b2c-dx-mcp 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/registry.js +72 -20
- package/dist/services.d.ts +9 -1
- package/dist/services.js +20 -1
- package/dist/tools/scapi/index.js +6 -4
- package/dist/tools/scapi/metrics-get.d.ts +25 -0
- package/dist/tools/scapi/metrics-get.js +144 -0
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
package/dist/registry.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
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
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
6
8
|
import { getLogger } from '@salesforce/b2c-tooling-sdk/logging';
|
|
7
9
|
import { detectWorkspaceType } from '@salesforce/b2c-tooling-sdk/discovery';
|
|
8
10
|
import { DOC_CATEGORIES, resolveEnabledCategories } from '@salesforce/b2c-tooling-sdk/docs';
|
|
@@ -102,17 +104,49 @@ export function createToolRegistry(loadServices, serverContext, detectedWorkspac
|
|
|
102
104
|
}
|
|
103
105
|
return registry;
|
|
104
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Maximum directory depth workspace auto-discovery recurses when scanning the
|
|
109
|
+
* project directory. Bounding this is what keeps startup fast (and safe) when
|
|
110
|
+
* the server is launched from a broad root: a full `**` walk of a home
|
|
111
|
+
* directory can take many seconds and blocks the MCP handshake, since detection
|
|
112
|
+
* runs before the server connects.
|
|
113
|
+
*
|
|
114
|
+
* Depth is counted in path segments relative to the project directory. 5 covers
|
|
115
|
+
* the common layouts — a cartridge at `cartridges/<name>/.project` (depth 3) and
|
|
116
|
+
* a monorepo cartridge at `packages/<app>/cartridges/<name>/.project` (depth 5) —
|
|
117
|
+
* without descending into unrelated deep trees.
|
|
118
|
+
*/
|
|
119
|
+
const DISCOVERY_MAX_DEPTH = 5;
|
|
120
|
+
/**
|
|
121
|
+
* Returns true when `dir` is a location that should never be recursively
|
|
122
|
+
* scanned for a workspace: the user's home directory or a filesystem root.
|
|
123
|
+
* These are the classic "MCP client spawned the server from ~" cases where an
|
|
124
|
+
* unbounded (or even bounded) scan is both pointless and expensive.
|
|
125
|
+
*/
|
|
126
|
+
function isUnscannableRoot(dir) {
|
|
127
|
+
const resolved = path.resolve(dir);
|
|
128
|
+
// Filesystem root: parent equals self (handles `/` and Windows drive roots).
|
|
129
|
+
if (path.dirname(resolved) === resolved) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
const home = os.homedir();
|
|
133
|
+
return Boolean(home) && path.resolve(home) === resolved;
|
|
134
|
+
}
|
|
105
135
|
/**
|
|
106
136
|
* Performs workspace auto-discovery and returns appropriate toolsets.
|
|
107
137
|
* Always includes the BASE_TOOLSETS even if no project types are detected.
|
|
108
138
|
*
|
|
139
|
+
* The scan is depth-bounded ({@link DISCOVERY_MAX_DEPTH}) and skipped entirely
|
|
140
|
+
* for home/root directories so it can never fan out across a whole home tree.
|
|
141
|
+
*
|
|
109
142
|
* @param flags - Startup flags containing projectDirectory
|
|
110
|
-
* @
|
|
111
|
-
* @returns Array of toolsets to enable
|
|
143
|
+
* @returns Array of detected project types (empty if skipped or none found)
|
|
112
144
|
*/
|
|
113
145
|
async function detectProjectTypes(flags) {
|
|
114
146
|
const logger = getLogger();
|
|
115
|
-
// Project directory from --project-directory flag or SFCC_PROJECT_DIRECTORY env
|
|
147
|
+
// Project directory from --project-directory flag or SFCC_PROJECT_DIRECTORY env
|
|
148
|
+
// var, defaulting to cwd. We always attempt detection from cwd rather than
|
|
149
|
+
// skipping it — the depth bound and home/root guard below make that safe.
|
|
116
150
|
const projectDirectory = flags.projectDirectory ?? process.cwd();
|
|
117
151
|
// Warn if project directory wasn't explicitly configured
|
|
118
152
|
if (!flags.projectDirectory) {
|
|
@@ -120,7 +154,14 @@ async function detectProjectTypes(flags) {
|
|
|
120
154
|
'MCP clients like Cursor and Claude Code often spawn servers from ~ instead of the project directory. ' +
|
|
121
155
|
'Set --project-directory or SFCC_PROJECT_DIRECTORY for reliable auto-discovery.');
|
|
122
156
|
}
|
|
123
|
-
|
|
157
|
+
// Never recursively scan a home directory or filesystem root — the scan would
|
|
158
|
+
// be expensive and would not identify a meaningful workspace anyway.
|
|
159
|
+
if (isUnscannableRoot(projectDirectory)) {
|
|
160
|
+
logger.warn({ projectDirectory }, 'Project directory is a home or root directory; skipping workspace auto-discovery. ' +
|
|
161
|
+
'Set --project-directory or SFCC_PROJECT_DIRECTORY to the project path to enable it.');
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const detectionResult = await detectWorkspaceType(projectDirectory, { maxDepth: DISCOVERY_MAX_DEPTH });
|
|
124
165
|
logger.info({ projectTypes: detectionResult.projectTypes, matchedPatterns: detectionResult.matchedPatterns }, `Workspace detection: project types: ${detectionResult.projectTypes.join(', ') || 'none'}`);
|
|
125
166
|
return detectionResult.projectTypes;
|
|
126
167
|
}
|
|
@@ -156,22 +197,21 @@ export async function registerToolsets(flags, server, loadServices, serverContex
|
|
|
156
197
|
const individualTools = flags.tools ?? [];
|
|
157
198
|
const allowNonGaTools = flags.allowNonGaTools ?? false;
|
|
158
199
|
const logger = getLogger();
|
|
159
|
-
// Detect the workspace type once up front. It informs both the
|
|
160
|
-
// docs tools (favoring the current workspace's docs and surfacing it in the
|
|
161
|
-
// tool descriptions) and toolset auto-discovery below, so we only hit the
|
|
162
|
-
// filesystem a single time.
|
|
163
|
-
const detectedWorkspaces = await detectProjectTypes(flags);
|
|
164
200
|
// Resolve the launch-time docs topic allowlist (bounds the whole docs corpus).
|
|
165
201
|
const enabledDocCategories = resolveEnabledCategories(flags.docsTopics, (invalid) => logger.warn({ invalidTopics: invalid, validTopics: DOC_CATEGORIES }, `Ignoring unknown documentation topic(s) in --docs-topics: "${invalid.join('", "')}"`));
|
|
166
202
|
if (enabledDocCategories) {
|
|
167
203
|
logger.info({ docsTopics: enabledDocCategories }, `Documentation restricted to topics: ${enabledDocCategories.join(', ')}`);
|
|
168
204
|
}
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
205
|
+
// Build the tool registry to validate flag input. The set of available tools
|
|
206
|
+
// (names, toolsets) does NOT depend on the detected workspace — only the docs
|
|
207
|
+
// tool descriptions do — so we can resolve flag validity before deciding
|
|
208
|
+
// whether workspace detection is even needed. If auto-discovery later runs,
|
|
209
|
+
// we rebuild the registry with the detected workspaces so the docs tools pick
|
|
210
|
+
// up the workspace hint.
|
|
211
|
+
let toolRegistry = createToolRegistry(loadServices, serverContext, [], enabledDocCategories);
|
|
212
|
+
const existingToolNames = new Set(Object.values(toolRegistry)
|
|
213
|
+
.flat()
|
|
214
|
+
.map((tool) => tool.name));
|
|
175
215
|
// Determine valid individual tools
|
|
176
216
|
const invalidTools = individualTools.filter((name) => !existingToolNames.has(name));
|
|
177
217
|
const validIndividualTools = individualTools.filter((name) => existingToolNames.has(name));
|
|
@@ -190,16 +230,24 @@ export async function registerToolsets(flags, server, loadServices, serverContex
|
|
|
190
230
|
const validToolsets = toolsets.filter((t) => TOOLSETS.includes(t));
|
|
191
231
|
const allNonDeprecatedToolsets = TOOLSETS.filter((t) => !DEPRECATED_TOOLSETS.includes(t));
|
|
192
232
|
const toolsetsToEnable = new Set(toolsets.includes(ALL_TOOLSETS) ? allNonDeprecatedToolsets : validToolsets);
|
|
193
|
-
// Auto-discovery:
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
233
|
+
// Auto-discovery: only when no valid toolsets AND no valid individual tools
|
|
234
|
+
// were provided. This handles both (1) no flags provided, and (2) all
|
|
235
|
+
// provided flags are invalid. Skip workspace detection entirely otherwise —
|
|
236
|
+
// when the user has explicitly chosen toolsets/tools, the filesystem scan
|
|
237
|
+
// cannot change which tools are registered, so paying for it (and risking a
|
|
238
|
+
// costly recursive walk) would be pointless.
|
|
197
239
|
if (toolsetsToEnable.size === 0 && validIndividualTools.length === 0) {
|
|
240
|
+
const detectedWorkspaces = await detectProjectTypes(flags);
|
|
198
241
|
const discoveredToolsets = getToolsetsForProjectTypes(detectedWorkspaces);
|
|
199
242
|
logger.info({ projectTypes: detectedWorkspaces, enabledToolsets: discoveredToolsets }, `Auto-discovery: enabling toolsets ${discoveredToolsets.join(', ')}`);
|
|
200
243
|
for (const toolset of discoveredToolsets) {
|
|
201
244
|
toolsetsToEnable.add(toolset);
|
|
202
245
|
}
|
|
246
|
+
// Rebuild so the docs tools reflect the detected workspace in their
|
|
247
|
+
// descriptions and default workspace resolution.
|
|
248
|
+
if (detectedWorkspaces.length > 0) {
|
|
249
|
+
toolRegistry = createToolRegistry(loadServices, serverContext, detectedWorkspaces, enabledDocCategories);
|
|
250
|
+
}
|
|
203
251
|
}
|
|
204
252
|
// Build the set of tools to register:
|
|
205
253
|
// 1. Start with tools from enabled toolsets
|
|
@@ -215,7 +263,11 @@ export async function registerToolsets(flags, server, loadServices, serverContex
|
|
|
215
263
|
}
|
|
216
264
|
}
|
|
217
265
|
}
|
|
218
|
-
// Step 2: Add individual tools from --tools (can be from any toolset)
|
|
266
|
+
// Step 2: Add individual tools from --tools (can be from any toolset).
|
|
267
|
+
// Look up against the final registry (which may have been rebuilt above).
|
|
268
|
+
const allToolsByName = new Map(Object.values(toolRegistry)
|
|
269
|
+
.flat()
|
|
270
|
+
.map((tool) => [tool.name, tool]));
|
|
219
271
|
for (const toolName of validIndividualTools) {
|
|
220
272
|
const tool = allToolsByName.get(toolName);
|
|
221
273
|
if (tool && !registeredToolNames.has(toolName)) {
|
package/dist/services.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ import fs from 'node:fs';
|
|
|
40
40
|
import type { B2CInstance } from '@salesforce/b2c-tooling-sdk';
|
|
41
41
|
import type { AuthStrategy } from '@salesforce/b2c-tooling-sdk/auth';
|
|
42
42
|
import type { ResolvedB2CConfig } from '@salesforce/b2c-tooling-sdk/config';
|
|
43
|
-
import { WebDavClient, type CustomApisClient, type ScapiSchemasClient } from '@salesforce/b2c-tooling-sdk/clients';
|
|
43
|
+
import { WebDavClient, type CustomApisClient, type MetricsClient, type ScapiSchemasClient } from '@salesforce/b2c-tooling-sdk/clients';
|
|
44
44
|
/**
|
|
45
45
|
* MRT (Managed Runtime) configuration.
|
|
46
46
|
* Groups auth, project, environment, and origin settings.
|
|
@@ -147,6 +147,14 @@ export declare class Services {
|
|
|
147
147
|
* Get the user's home directory.
|
|
148
148
|
*/
|
|
149
149
|
getHomeDir(): string;
|
|
150
|
+
/**
|
|
151
|
+
* Get Metrics client for accessing SCAPI observability metrics.
|
|
152
|
+
* Requires shortCode, tenantId, and OAuth credentials to be configured.
|
|
153
|
+
*
|
|
154
|
+
* @throws Error if shortCode, tenantId, or OAuth credentials are missing
|
|
155
|
+
* @returns Typed Metrics client
|
|
156
|
+
*/
|
|
157
|
+
getMetricsClient(): MetricsClient;
|
|
150
158
|
/**
|
|
151
159
|
* Get organization ID for SCAPI API calls.
|
|
152
160
|
* Ensures the tenant ID has the required f_ecom_ prefix.
|
package/dist/services.js
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
import fs from 'node:fs';
|
|
45
45
|
import path from 'node:path';
|
|
46
46
|
import os from 'node:os';
|
|
47
|
-
import { createCustomApisClient, createScapiSchemasClient, toOrganizationId, WebDavClient, } from '@salesforce/b2c-tooling-sdk/clients';
|
|
47
|
+
import { createCustomApisClient, createMetricsClient, createScapiSchemasClient, toOrganizationId, WebDavClient, } from '@salesforce/b2c-tooling-sdk/clients';
|
|
48
48
|
/**
|
|
49
49
|
* Services class that provides utilities for MCP tools.
|
|
50
50
|
*
|
|
@@ -167,6 +167,25 @@ export class Services {
|
|
|
167
167
|
getHomeDir() {
|
|
168
168
|
return os.homedir();
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Get Metrics client for accessing SCAPI observability metrics.
|
|
172
|
+
* Requires shortCode, tenantId, and OAuth credentials to be configured.
|
|
173
|
+
*
|
|
174
|
+
* @throws Error if shortCode, tenantId, or OAuth credentials are missing
|
|
175
|
+
* @returns Typed Metrics client
|
|
176
|
+
*/
|
|
177
|
+
getMetricsClient() {
|
|
178
|
+
const { shortCode, tenantId } = this.resolvedConfig.values;
|
|
179
|
+
if (!shortCode) {
|
|
180
|
+
throw new Error('SCAPI short code required. Provide --short-code, set SFCC_SHORTCODE, or configure short-code in dw.json.');
|
|
181
|
+
}
|
|
182
|
+
if (!tenantId) {
|
|
183
|
+
throw new Error('Tenant ID required. Provide --tenant-id, set SFCC_TENANT_ID, or configure tenant-id in dw.json.');
|
|
184
|
+
}
|
|
185
|
+
// This will throw if OAuth credentials are missing
|
|
186
|
+
const oauthStrategy = this.getOAuthStrategy();
|
|
187
|
+
return createMetricsClient({ shortCode, tenantId }, oauthStrategy);
|
|
188
|
+
}
|
|
170
189
|
/**
|
|
171
190
|
* Get organization ID for SCAPI API calls.
|
|
172
191
|
* Ensures the tenant ID has the required f_ecom_ prefix.
|
|
@@ -3,9 +3,10 @@
|
|
|
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
|
-
import {
|
|
7
|
-
import { createScapiCustomApisStatusTool } from './scapi-custom-apis-get-status.js';
|
|
6
|
+
import { createMetricsGetTool } from './metrics-get.js';
|
|
8
7
|
import { createScaffoldCustomApiTool } from './scapi-custom-api-generate-scaffold.js';
|
|
8
|
+
import { createScapiCustomApisStatusTool } from './scapi-custom-apis-get-status.js';
|
|
9
|
+
import { createScapiSchemasListTool } from './scapi-schemas-list.js';
|
|
9
10
|
/**
|
|
10
11
|
* Creates all tools for the SCAPI toolset.
|
|
11
12
|
*
|
|
@@ -14,9 +15,10 @@ import { createScaffoldCustomApiTool } from './scapi-custom-api-generate-scaffol
|
|
|
14
15
|
*/
|
|
15
16
|
export function createScapiTools(loadServices) {
|
|
16
17
|
return [
|
|
17
|
-
|
|
18
|
-
createScapiCustomApisStatusTool(loadServices),
|
|
18
|
+
createMetricsGetTool(loadServices),
|
|
19
19
|
createScaffoldCustomApiTool(loadServices),
|
|
20
|
+
createScapiCustomApisStatusTool(loadServices),
|
|
21
|
+
createScapiSchemasListTool(loadServices),
|
|
20
22
|
];
|
|
21
23
|
}
|
|
22
24
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Services } from '../../services.js';
|
|
2
|
+
import type { McpTool } from '../../utils/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates the metrics_get tool.
|
|
5
|
+
*
|
|
6
|
+
* Retrieves observability metrics time-series for a B2C Commerce tenant by category:
|
|
7
|
+
* - **overall**: Aggregate site metrics (requests, response times, errors)
|
|
8
|
+
* - **sales**: Sales and order metrics
|
|
9
|
+
* - **ecdn**: Edge CDN performance metrics
|
|
10
|
+
* - **third-party**: External service integration metrics (filter by thirdPartyServiceId)
|
|
11
|
+
* - **scapi**: SCAPI endpoint metrics (filter by apiFamily, apiName)
|
|
12
|
+
* - **scapi-hooks**: SCAPI hooks execution metrics
|
|
13
|
+
* - **mrt**: Managed Runtime (PWA Kit) metrics
|
|
14
|
+
* - **controller**: Controller execution metrics
|
|
15
|
+
* - **ocapi**: OCAPI endpoint metrics (filter by ocapiCategory, ocapiApi)
|
|
16
|
+
*
|
|
17
|
+
* Returns time-series data with metric metadata (title, description, unit) and data points
|
|
18
|
+
* (timestamp, value) grouped by series (e.g., 2xx, 4xx, 5xx response codes).
|
|
19
|
+
*
|
|
20
|
+
* **Requirements:** OAuth with `sfcc.metrics` scope.
|
|
21
|
+
*
|
|
22
|
+
* @param loadServices - Function that loads configuration and returns Services instance
|
|
23
|
+
* @returns MCP tool for retrieving metrics
|
|
24
|
+
*/
|
|
25
|
+
export declare function createMetricsGetTool(loadServices: () => Promise<Services> | Services): McpTool;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2
|
|
4
|
+
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Metrics Get tool.
|
|
8
|
+
*
|
|
9
|
+
* Retrieves observability metrics time-series data for B2C Commerce tenants via SCAPI
|
|
10
|
+
* observability/metrics/v1. Returns metrics data grouped by category (overall, sales, ecdn,
|
|
11
|
+
* third-party, scapi, scapi-hooks, mrt, controller, ocapi).
|
|
12
|
+
*
|
|
13
|
+
* @module tools/scapi/metrics-get
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { createToolAdapter, jsonResult } from '../adapter.js';
|
|
17
|
+
import { getMetricsByCategory, resolveMetricsWindow, enrichMetricsTags, } from '@salesforce/b2c-tooling-sdk';
|
|
18
|
+
/**
|
|
19
|
+
* Creates the metrics_get tool.
|
|
20
|
+
*
|
|
21
|
+
* Retrieves observability metrics time-series for a B2C Commerce tenant by category:
|
|
22
|
+
* - **overall**: Aggregate site metrics (requests, response times, errors)
|
|
23
|
+
* - **sales**: Sales and order metrics
|
|
24
|
+
* - **ecdn**: Edge CDN performance metrics
|
|
25
|
+
* - **third-party**: External service integration metrics (filter by thirdPartyServiceId)
|
|
26
|
+
* - **scapi**: SCAPI endpoint metrics (filter by apiFamily, apiName)
|
|
27
|
+
* - **scapi-hooks**: SCAPI hooks execution metrics
|
|
28
|
+
* - **mrt**: Managed Runtime (PWA Kit) metrics
|
|
29
|
+
* - **controller**: Controller execution metrics
|
|
30
|
+
* - **ocapi**: OCAPI endpoint metrics (filter by ocapiCategory, ocapiApi)
|
|
31
|
+
*
|
|
32
|
+
* Returns time-series data with metric metadata (title, description, unit) and data points
|
|
33
|
+
* (timestamp, value) grouped by series (e.g., 2xx, 4xx, 5xx response codes).
|
|
34
|
+
*
|
|
35
|
+
* **Requirements:** OAuth with `sfcc.metrics` scope.
|
|
36
|
+
*
|
|
37
|
+
* @param loadServices - Function that loads configuration and returns Services instance
|
|
38
|
+
* @returns MCP tool for retrieving metrics
|
|
39
|
+
*/
|
|
40
|
+
export function createMetricsGetTool(loadServices) {
|
|
41
|
+
return createToolAdapter({
|
|
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.`,
|
|
63
|
+
toolsets: ['SCAPI'],
|
|
64
|
+
isGA: false,
|
|
65
|
+
requiresInstance: false, // SCAPI uses OAuth directly
|
|
66
|
+
inputSchema: {
|
|
67
|
+
category: z
|
|
68
|
+
.enum(['overall', 'sales', 'ecdn', 'third-party', 'scapi', 'scapi-hooks', 'mrt', 'controller', 'ocapi'])
|
|
69
|
+
.describe('Metrics category: overall (aggregate), sales, ecdn (CDN), third-party (external), scapi (SCAPI APIs), scapi-hooks, mrt (PWA Kit), controller, ocapi'),
|
|
70
|
+
from: z
|
|
71
|
+
.string()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe('Start bound: relative ("1h", "7d" ago) or ISO 8601. Alone → a 24h window forward (capped at now).'),
|
|
74
|
+
to: z
|
|
75
|
+
.string()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe('End bound: relative ("6h" ago) or ISO 8601. Alone → a 24h window back from it.'),
|
|
78
|
+
window: z
|
|
79
|
+
.string()
|
|
80
|
+
.optional()
|
|
81
|
+
.describe('Window duration ("1h", "30m", "2d"). With from → to=from+window; with to → from=to-window; alone → the last <window>. Defaults to 24h.'),
|
|
82
|
+
thirdPartyServiceId: z
|
|
83
|
+
.string()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe('Filter by third-party service ID (third-party category only)'),
|
|
86
|
+
apiFamily: z.string().optional().describe('Filter by SCAPI API family (scapi category only)'),
|
|
87
|
+
apiName: z.string().optional().describe('Filter by SCAPI API name (scapi category only)'),
|
|
88
|
+
ocapiCategory: z.string().optional().describe('Filter by OCAPI category (ocapi category only)'),
|
|
89
|
+
ocapiApi: z.string().optional().describe('Filter by OCAPI API (ocapi category only)'),
|
|
90
|
+
},
|
|
91
|
+
async execute(args, { services: svc }) {
|
|
92
|
+
// Get client and tenant ID
|
|
93
|
+
const client = svc.getMetricsClient();
|
|
94
|
+
const tenantId = svc.getTenantId();
|
|
95
|
+
if (!tenantId) {
|
|
96
|
+
throw new Error('Tenant ID required. Provide --tenant-id, set SFCC_TENANT_ID, or configure tenant-id in dw.json.');
|
|
97
|
+
}
|
|
98
|
+
// Resolve the requested bounds into an explicit from+to range, filling any
|
|
99
|
+
// open bound from the 24-hour default window (the API caps a window at 24h
|
|
100
|
+
// and pairs a missing `to` with its own `now`). Throws a clear error on
|
|
101
|
+
// unparseable/over-specified input before the request.
|
|
102
|
+
const window = resolveMetricsWindow({ from: args.from, to: args.to, window: args.window });
|
|
103
|
+
const raw = await getMetricsByCategory(client, tenantId, args.category, {
|
|
104
|
+
from: window.from,
|
|
105
|
+
to: window.to,
|
|
106
|
+
thirdPartyServiceId: args.thirdPartyServiceId,
|
|
107
|
+
apiFamily: args.apiFamily,
|
|
108
|
+
apiName: args.apiName,
|
|
109
|
+
ocapiCategory: args.ocapiCategory,
|
|
110
|
+
ocapiApi: args.ocapiApi,
|
|
111
|
+
});
|
|
112
|
+
// Enrich each series with a structured `tags` object (realm/environment
|
|
113
|
+
// from the request, applied filters, and per-series dimensions parsed
|
|
114
|
+
// from the packed id). Additive and always-on for this machine consumer.
|
|
115
|
+
const response = enrichMetricsTags(raw, args.category, {
|
|
116
|
+
tenantId,
|
|
117
|
+
apiFamily: args.apiFamily,
|
|
118
|
+
apiName: args.apiName,
|
|
119
|
+
ocapiCategory: args.ocapiCategory,
|
|
120
|
+
ocapiApi: args.ocapiApi,
|
|
121
|
+
thirdPartyServiceId: args.thirdPartyServiceId,
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
...response,
|
|
125
|
+
query: {
|
|
126
|
+
category: args.category,
|
|
127
|
+
from: window.fromIso,
|
|
128
|
+
to: window.toIso,
|
|
129
|
+
fromEpochSeconds: window.fromEpochSeconds,
|
|
130
|
+
toEpochSeconds: window.toEpochSeconds,
|
|
131
|
+
clampedFrom: window.clampedFrom || undefined,
|
|
132
|
+
defaultedWindow: window.defaultedWindow || undefined,
|
|
133
|
+
thirdPartyServiceId: args.thirdPartyServiceId,
|
|
134
|
+
apiFamily: args.apiFamily,
|
|
135
|
+
apiName: args.apiName,
|
|
136
|
+
ocapiCategory: args.ocapiCategory,
|
|
137
|
+
ocapiApi: args.ocapiApi,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
formatOutput: (output) => jsonResult(output),
|
|
142
|
+
}, loadServices);
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=metrics-get.js.map
|
package/oclif.manifest.json
CHANGED
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": "1.
|
|
4
|
+
"version": "1.7.0",
|
|
5
5
|
"author": "Salesforce",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": "SalesforceCommerceCloud/b2c-developer-tooling",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"yaml": "2.9.0",
|
|
81
81
|
"postcss": "8.5.15",
|
|
82
82
|
"zod": "3.25.76",
|
|
83
|
-
"@salesforce/b2c-tooling-sdk": "1.
|
|
83
|
+
"@salesforce/b2c-tooling-sdk": "1.19.1"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@eslint/compat": "^1",
|