@salesforce/sfdx-agent-harness-mastra 0.97.0 → 0.99.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/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to `@salesforce/sfdx-agent-harness-mastra` are documented in this file.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.99.0] - 2026-10-06
7
+
8
+ ### Features
9
+ - **agent-sdk,harness-claude,harness-mastra,harness-openai**: add 'application-policy' kind to ToolDeclineReason @W-24368893@ ([#854](https://github.com/forcedotcom/agentic-dx/pull/854))
10
+
11
+ ### Chores
12
+ - **deps-dev**: bump eslint-plugin-n from 18.3.0 to 18.4.0 in the eslint group across 1 directory ([#859](https://github.com/forcedotcom/agentic-dx/pull/859))
13
+ - **deps-dev**: bump the vitest group with 2 updates ([#860](https://github.com/forcedotcom/agentic-dx/pull/860))
14
+
15
+ ## [0.98.0] - 2026-10-05
16
+
17
+ ### Fixes
18
+ - **agent-sdk,harness-claude,harness-mastra,harness-openai**: rewrite MCP clientInfo.name to the running agent's name @W-24101379@ ([#843](https://github.com/forcedotcom/agentic-dx/pull/843))
19
+
6
20
  ## [0.97.0] - 2026-10-05
7
21
 
8
22
  ### Features
package/README.md CHANGED
@@ -250,10 +250,10 @@ Consumer-executed tools (`AgentConfig.tools`) always bypass the gate — their e
250
250
  via `submitToolResult`. Set `StreamOptions.batchApprovals: true` to surface parallel `require-approval` requests on one
251
251
  stream for a batch-approval UI.
252
252
 
253
- With `@mastra/core` 1.62.0, an organization-policy decline is passed through `agent.declineToolCall({ reason })`. Mastra
254
- emits one native `tool-output-denied` chunk; the adapter maps it to one `tool-result(isError=true)` carrying the same
255
- exact message the model sees. Automatic policy denies use `Denied by tool policy.`; reasonless consumer declines retain
256
- Mastra's `Tool call was not approved by the user` default.
253
+ With `@mastra/core` 1.62.0, a typed-reason decline (either `ToolDeclineReason` kind) is passed through
254
+ `agent.declineToolCall({ reason })`. Mastra emits one native `tool-output-denied` chunk; the adapter maps it to one
255
+ `tool-result(isError=true)` carrying the same exact message the model sees. Automatic policy denies use
256
+ `Denied by tool policy.`; reasonless consumer declines retain Mastra's `Tool call was not approved by the user` default.
257
257
 
258
258
  ### Tool approval timeout
259
259
 
@@ -623,7 +623,7 @@ export class MastraHarness {
623
623
  // object and stores it here so we can attach each to its
624
624
  // McpServerState below (W-23231658).
625
625
  const httpErrorCaptures = new Map();
626
- const enabledServers = mapToMastraMCPServers(config.mcpServers, config.orgJwt, this.innerFetch, httpErrorCaptures, options?.mcpAuthProviders);
626
+ const enabledServers = mapToMastraMCPServers(config.mcpServers, config.orgJwt, this.innerFetch, httpErrorCaptures, options?.mcpAuthProviders, config.name);
627
627
  for (const [serverName, serverDef] of Object.entries(enabledServers)) {
628
628
  const client = new MCPClient({
629
629
  id: `${agentId}:${serverName}`,
@@ -1107,7 +1107,7 @@ export class MastraHarness {
1107
1107
  ]);
1108
1108
  const nextHttpErrorCaptures = new Map();
1109
1109
  const nextEnabledServers = hasEnabledServers(nextConfig)
1110
- ? mapToMastraMCPServers(nextConfig, config.orgJwt, this.innerFetch, nextHttpErrorCaptures, nextProviders)
1110
+ ? mapToMastraMCPServers(nextConfig, config.orgJwt, this.innerFetch, nextHttpErrorCaptures, nextProviders, config.name)
1111
1111
  : {};
1112
1112
  for (const name of allNames) {
1113
1113
  const prev = prevConfig[name];
@@ -57,4 +57,6 @@ innerFetch?: typeof fetch,
57
57
  * (W-23231658). Stdio servers contribute no entry. Omitted in call sites
58
58
  * that don't need recovery (e.g. unit tests of the mapping shape).
59
59
  */
60
- captures?: Map<string, HttpErrorCapture>, providers?: McpAuthProviders): Record<string, MastraMCPServerDefinition>;
60
+ captures?: Map<string, HttpErrorCapture>, providers?: McpAuthProviders,
61
+ /** AgentConfig.name; see {@link resolveMcpClientName}. */
62
+ agentName?: string): Record<string, MastraMCPServerDefinition>;
@@ -2,7 +2,7 @@
2
2
  * Copyright 2026, Salesforce, Inc. All rights reserved.
3
3
  * See LICENSE.txt for license terms.
4
4
  */
5
- import { resolveMcpServerHeaders, } from '@salesforce/sfdx-agent-sdk';
5
+ import { resolveMcpClientName, resolveMcpServerHeaders, } from '@salesforce/sfdx-agent-sdk';
6
6
  /**
7
7
  * Returns `true` if `config` contains at least one server that is not explicitly disabled.
8
8
  * A server is considered enabled when its `enabled` property is `true` or absent.
@@ -67,13 +67,17 @@ innerFetch = globalThis.fetch,
67
67
  * (W-23231658). Stdio servers contribute no entry. Omitted in call sites
68
68
  * that don't need recovery (e.g. unit tests of the mapping shape).
69
69
  */
70
- captures, providers = {}) {
70
+ captures, providers = {},
71
+ /** AgentConfig.name; see {@link resolveMcpClientName}. */
72
+ agentName) {
71
73
  const result = {};
72
74
  for (const [name, serverConfig] of Object.entries(config)) {
73
75
  if (serverConfig.enabled === false) {
74
76
  continue;
75
77
  }
76
78
  if (serverConfig.type === 'stdio') {
79
+ // Stdio has no fetch layer for rewriting `clientInfo`, and `@mastra/mcp`
80
+ // exposes no constructor option for it. See https://github.com/mastra-ai/mastra/issues/25473.
77
81
  const def = {
78
82
  command: serverConfig.command,
79
83
  ...(serverConfig.args !== undefined && { args: serverConfig.args }),
@@ -92,9 +96,11 @@ captures, providers = {}) {
92
96
  else if (serverConfig.type === 'remote') {
93
97
  const url = serverConfig.url instanceof URL ? serverConfig.url : new URL(serverConfig.url);
94
98
  const provider = providers[name];
99
+ // Wrap the innermost fetch so the client identity reaches every auth path.
100
+ const clientInfoFetch = wrapFetchWithClientInfoName(innerFetch, agentName, orgJwt);
95
101
  const baseFetch = provider === undefined && orgJwt
96
- ? createAuthFetch(serverConfig.url, orgJwt, innerFetch, serverConfig.headers)
97
- : innerFetch;
102
+ ? createAuthFetch(serverConfig.url, orgJwt, clientInfoFetch, serverConfig.headers)
103
+ : clientInfoFetch;
98
104
  // Wrap with HTTP-error capture when the harness asked for it, so a
99
105
  // non-2xx connect response's status + body survive the error
100
106
  // `@mastra/mcp` discards (W-23231658). The mapper owns the capture
@@ -110,7 +116,7 @@ captures, providers = {}) {
110
116
  transportFetch = wrapFetchWithErrorCapture(baseFetch, capture);
111
117
  }
112
118
  else {
113
- transportFetch = orgJwt || provider !== undefined ? baseFetch : undefined;
119
+ transportFetch = baseFetch;
114
120
  }
115
121
  const def = {
116
122
  url,
@@ -154,6 +160,54 @@ function createAuthFetch(serverUrl, orgJwt, innerFetch, existingHeaders) {
154
160
  return innerFetch(url, { ...init, headers: merged });
155
161
  };
156
162
  }
163
+ /**
164
+ * Wraps a transport fetch to rewrite `clientInfo.name` on MCP `initialize`
165
+ * requests. Other requests pass through unchanged, and extra arguments are forwarded.
166
+ *
167
+ * Only the `initialize` handshake is rewritten: a server configured with
168
+ * `protocolVersion: '2026-07-28'` sends no `initialize`, so the rewrite does nothing there.
169
+ */
170
+ function wrapFetchWithClientInfoName(baseFetch, agentName, orgJwt) {
171
+ const forward = baseFetch;
172
+ const wrapped = async (input, init, ...rest) => {
173
+ const rewrittenBody = rewriteInitializeClientInfoName(init?.body, () => resolveMcpClientName(agentName, orgJwt));
174
+ return rewrittenBody === undefined
175
+ ? forward(input, init, ...rest)
176
+ : forward(input, { ...init, body: rewrittenBody }, ...rest);
177
+ };
178
+ return wrapped;
179
+ }
180
+ /**
181
+ * If `body` is a JSON-RPC `initialize` request, return a new body string with
182
+ * `params.clientInfo.name` set to `resolveName()` (creating `clientInfo` when
183
+ * absent, preserving `version` and any other fields). Returns `undefined` — a
184
+ * pass-through signal — for any non-`initialize` request or an unparseable /
185
+ * non-string body. Best-effort: it never throws into the transport.
186
+ */
187
+ function rewriteInitializeClientInfoName(body, resolveName) {
188
+ if (typeof body !== 'string')
189
+ return undefined;
190
+ let message;
191
+ try {
192
+ message = JSON.parse(body);
193
+ }
194
+ catch {
195
+ return undefined;
196
+ }
197
+ if (message === null || typeof message !== 'object' || Array.isArray(message))
198
+ return undefined;
199
+ const msg = message;
200
+ if (msg.method !== 'initialize')
201
+ return undefined;
202
+ const params = typeof msg.params === 'object' && msg.params !== null && !Array.isArray(msg.params)
203
+ ? msg.params
204
+ : {};
205
+ const nextParams = {
206
+ ...params,
207
+ clientInfo: { ...(params.clientInfo ?? {}), name: resolveName() },
208
+ };
209
+ return JSON.stringify({ ...msg, params: nextParams });
210
+ }
157
211
  /**
158
212
  * Wrap a transport fetch so every non-2xx response's status + body is recorded
159
213
  * into `capture.latest` before the response is handed back to `@mastra/mcp`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-harness-mastra",
3
- "version": "0.97.0",
3
+ "version": "0.99.0",
4
4
  "description": "Mastra-backed AgentHarness implementation for @salesforce/sfdx-agent-sdk",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -42,20 +42,20 @@
42
42
  "openai": "^7.23.0"
43
43
  },
44
44
  "peerDependencies": {
45
- "@salesforce/sfdx-agent-sdk": "0.98.0"
45
+ "@salesforce/sfdx-agent-sdk": "0.100.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@eslint/js": "^10.0.1",
49
49
  "@salesforce/harness-conformance": "0.0.1",
50
- "@salesforce/sfdx-agent-sdk": "0.98.0",
50
+ "@salesforce/sfdx-agent-sdk": "0.100.0",
51
51
  "@types/node": "^22.20.1",
52
- "@vitest/coverage-istanbul": "^5.0.1",
52
+ "@vitest/coverage-istanbul": "^5.0.2",
53
53
  "@vitest/eslint-plugin": "^1.6.27",
54
54
  "eslint": "^10.11.0",
55
55
  "eslint-config-prettier": "^10.1.8",
56
56
  "eslint-import-resolver-typescript": "^4.4.5",
57
57
  "eslint-plugin-import": "^2.32.0",
58
- "eslint-plugin-n": "^18.3.0",
58
+ "eslint-plugin-n": "^18.4.0",
59
59
  "globals": "^17.12.0",
60
60
  "lint-staged": "^17.5.1",
61
61
  "prettier": "^3.9.7",