@salesforce/sfdx-agent-sdk 0.50.0 → 0.52.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,17 @@
3
3
  All notable changes to `@salesforce/sfdx-agent-sdk` are documented in this file.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.52.0] - 2026-08-24
7
+
8
+ ### Features
9
+ - **agent-sdk,harness-claude,harness-mastra,harness-openai**: support OAuth authProvider on remote MCP servers @W-23939243@ ([#757](https://github.com/forcedotcom/agentic-dx/pull/757))
10
+
11
+ ## [0.51.0] - 2026-08-18
12
+
13
+ ### Chores
14
+ - **deps-dev**: bump the eslint group across 1 directory with 2 updates ([#754](https://github.com/forcedotcom/agentic-dx/pull/754))
15
+ - **deps-dev**: bump the dev-dependencies group with 2 updates ([#753](https://github.com/forcedotcom/agentic-dx/pull/753))
16
+
6
17
  ## [0.50.0] - 2026-08-17
7
18
 
8
19
  ### Features
package/README.md CHANGED
@@ -386,6 +386,45 @@ type MCPRemoteServerConfig = {
386
386
  };
387
387
  ```
388
388
 
389
+ #### OAuth-protected remote MCP servers
390
+
391
+ To connect an OAuth-protected third-party remote MCP server (Figma, Linear, Notion, Sentry, Neon, Hugging Face, …),
392
+ supply an `McpOAuthClientProvider` through the manager's **`mcpAuthProviderResolver`**, not on the server config. The
393
+ provider is a credential-bearing runtime object, so it is deliberately kept **off** the persisted `AgentConfig` — it is
394
+ resolved per agent at install / restore / update and threaded to the harness at runtime, never serialized to disk.
395
+
396
+ ```ts
397
+ const manager = await createAgentManager(storageRoot, harnessFactory, {
398
+ // (agentId, config) => Record<remoteServerName, McpOAuthClientProvider> | undefined
399
+ mcpAuthProviderResolver: (_agentId, _config) => ({ figma: myFigmaProvider }),
400
+ });
401
+ ```
402
+
403
+ The harness forwards the provider into the underlying HTTP transport (`@modelcontextprotocol/sdk`'s
404
+ `StreamableHTTPClientTransport` on Claude / OpenAI, `@mastra/mcp`'s `HttpServerDefinition` on Mastra). The transport
405
+ attaches existing tokens and silently refreshes them when possible. If user interaction is required, it invokes the
406
+ provider's `redirectToAuthorization` callback and surfaces `McpServerErrorDetail.category === 'oauth-required'`;
407
+ ADX does not open a browser or accept the authorization callback code. The static `headers` seam cannot provide this
408
+ recovery because headers are snapshotted at publish time.
409
+
410
+ - **`McpOAuthClientProvider`** (exported) is a harness-neutral interface that structurally mirrors the required core of
411
+ the MCP SDK's `OAuthClientProvider`, so a real provider assigns to it with no cast and the SDK stays free of a direct
412
+ `@modelcontextprotocol/sdk` dependency. **`McpAuthProviders`** (exported) is the `Readonly<Record<serverName,
413
+ McpOAuthClientProvider>>` the resolver returns.
414
+ - **One provider instance per server**, and return the **same** instance across calls — the upstream contract forbids
415
+ tokens / codes / verifiers crossing sessions, and a harness cycles a server when its provider reference changes (so
416
+ returning a fresh instance each update reconnects that server every time).
417
+ - **One auth owner per server (enforced centrally).** The SDK normalizes the resolver's output before handing it to a
418
+ harness: a provider is **dropped** for a Salesforce Platform URL (the rotating org JWT owns auth there), a stdio /
419
+ disabled / unknown server; and a provider **paired with a static `Authorization` header** is **rejected**
420
+ (`AgentSDKErrorType.INVALID_MCP_AUTH_CONFIG`) because the MCP SDK transport would let the static header silently win
421
+ over the provider token. For a provider-owned non-Salesforce server the harness attaches the proxy-aware inner `fetch`
422
+ (no Salesforce JWT injected, `HTTPS_PROXY` preserved) while the provider drives auth.
423
+
424
+ > **Interactive authorization stays with the consumer.** AFV's provider owns the Sign in UI, browser, VS Code callback,
425
+ > and code exchange. After it saves the new tokens, call `Agent.reconnectMcpServer` to connect. ADX owns runtime provider
426
+ > forwarding and silent refresh only; the transport's `finishAuth(code)` seam is not part of the public API.
427
+
389
428
  **Tool-exposure policy** (which tools bypass the active runtime's tool-search deferral) is configured per-agent on the
390
429
  harness extension surface, not per-server here. See `MastraAgentConfig.toolSearch.alwaysActive` and
391
430
  `ClaudeAgentConfig.toolSearch.alwaysActive` for the entry shape that covers "all tools from server X", "tool Y on server
@@ -411,16 +450,17 @@ only `MCPRemoteServerConfig` carries it.
411
450
 
412
451
  #### `McpServerErrorDetail`
413
452
 
414
- Structured projection of an MCP server failure. Mirror is also attached to the `mcp-server-discovery-failed` telemetry
415
- event so subscribers can route on it without pattern-matching `error.message`.
453
+ Structured projection of an MCP server failure. Route interactive authorization on `category === 'oauth-required'`;
454
+ an ordinary missing or invalid bearer remains `http-401`. The same detail is attached to `mcp-server-discovery-failed`
455
+ telemetry so subscribers can route on it without pattern-matching `error.message`.
416
456
 
417
457
  | Field | Type | Description |
418
458
  | ----------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
419
459
  | `category` | `McpServerErrorCategory` | Stable category for routing logic. Set is additive across minor versions — values are added but never renamed or removed. |
420
460
  | `code?` | `number` | JSON-RPC error code from the underlying `McpError`, when the failure originated as a JSON-RPC error. Undefined for transport-level failures and for harnesses whose underlying SDK does not surface the code (e.g. Claude). |
421
- | `retriable` | `boolean` | Whether the SDK considers the failure transient (worth `Agent.reconnectMcpServer` / `Agent.refreshMcpAuth`) versus fatal. |
461
+ | `retriable` | `boolean` | Whether the SDK considers the failure transient (worth `Agent.reconnectMcpServer`) versus fatal. |
422
462
 
423
- `McpServerErrorCategory` values: `'connect-timeout'`, `'http-401'`, `'http-403'`, `'http-4xx'`, `'http-5xx'`,
463
+ `McpServerErrorCategory` values: `'connect-timeout'`, `'oauth-required'`, `'http-401'`, `'http-403'`, `'http-4xx'`, `'http-5xx'`,
424
464
  `'transport-eof'`, `'protocol-error'`, `'config-error'`, `'aborted'`, `'unknown'`.
425
465
 
426
466
  #### `McpToolInfo`
@@ -1,8 +1,8 @@
1
1
  import { type Clock, LogBus, type LogRecord, type Unsubscribe, type UniqueIDGenerator } from '@salesforce/agentic-common';
2
2
  import { type AgentHarness, type ConfigOf } from './harness/agent-harness.js';
3
3
  import type { HarnessFactory } from './harness/harness-factory.js';
4
- import { type AgentConfig } from './harness/harness-config.js';
5
- import { type Agent } from './agent.js';
4
+ import { type AgentConfig, type McpAuthProviderResolver } from './harness/harness-config.js';
5
+ import { type Agent, type AgentRuntimeResolvers } from './agent.js';
6
6
  import type { HooksForAgent } from './types/redaction.js';
7
7
  import { type TelemetryEventCallback } from './types/telemetry-events.js';
8
8
  import type { WireCommunicationEventCallback } from './types/wire-communication-event.js';
@@ -138,7 +138,7 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
138
138
  private readonly harnessSupportedProviderHints;
139
139
  private readonly agentIdGenerator;
140
140
  private readonly agentConnectivityResolver;
141
- private readonly hooksForAgent;
141
+ private readonly resolvers;
142
142
  private readonly clock;
143
143
  private readonly identityStore;
144
144
  private readonly agents;
@@ -161,7 +161,7 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
161
161
  * is private, so this is the only way to obtain an instance, but
162
162
  * consumers should always go through {@link createAgentManager}.
163
163
  */
164
- static __build<H extends AgentHarness>(harness: H, harnessSupportedProviderHints: readonly ProviderHint[], agentConnectivityResolver: AgentConnectivityResolver, hooksForAgent: HooksForAgent | undefined, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus): Promise<DefaultAgentManager<H>>;
164
+ static __build<H extends AgentHarness>(harness: H, harnessSupportedProviderHints: readonly ProviderHint[], agentConnectivityResolver: AgentConnectivityResolver, resolvers: AgentRuntimeResolvers, storageRootFolder: string, agentIdGenerator: UniqueIDGenerator, clock: Clock, logBus: LogBus): Promise<DefaultAgentManager<H>>;
165
165
  private init;
166
166
  shutdown(): Promise<void>;
167
167
  createAgent(projectRoot: string, config?: ConfigOf<H> & {
@@ -228,4 +228,11 @@ export declare class DefaultAgentManager<H extends AgentHarness = AgentHarness>
228
228
  export declare function createAgentManager<H extends AgentHarness = AgentHarness>(storageRootFolder: string, harnessFactory: HarnessFactory<H>, options?: {
229
229
  connectivityResolver?: AgentConnectivityResolver;
230
230
  hooksForAgent?: HooksForAgent;
231
+ /**
232
+ * Resolves the per-agent OAuth provider map for remote MCP servers.
233
+ * See {@link McpAuthProviderResolver}. Providers are supplied here at
234
+ * runtime and never persisted; required to connect OAuth-protected
235
+ * third-party remote MCP servers.
236
+ */
237
+ mcpAuthProviderResolver?: McpAuthProviderResolver;
231
238
  }): Promise<AgentManager<H>>;
@@ -7,6 +7,7 @@ import { resolve } from 'node:path';
7
7
  import { stat } from 'node:fs/promises';
8
8
  import { SUPPORTED_PROTOCOL_VERSIONS } from './harness/agent-harness.js';
9
9
  import { toHarnessConfig } from './harness/harness-config.js';
10
+ import { normalizeMcpAuthProviders } from './mcp-auth.js';
10
11
  import { DefaultAgent } from './agent.js';
11
12
  import { AgentSDKError, AgentSDKErrorType } from './errors.js';
12
13
  import { TelemetryRouter } from './internal/telemetry-router.js';
@@ -28,7 +29,7 @@ export class DefaultAgentManager {
28
29
  harnessSupportedProviderHints;
29
30
  agentIdGenerator;
30
31
  agentConnectivityResolver;
31
- hooksForAgent;
32
+ resolvers;
32
33
  clock;
33
34
  identityStore;
34
35
  agents = new Map();
@@ -43,11 +44,11 @@ export class DefaultAgentManager {
43
44
  wireRouter;
44
45
  unroutedUnsubs;
45
46
  disposed = false;
46
- constructor(harness, harnessSupportedProviderHints, agentConnectivityResolver, hooksForAgent, identityStore, agentIdGenerator, clock, logBus) {
47
+ constructor(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, identityStore, agentIdGenerator, clock, logBus) {
47
48
  this.harness = harness;
48
49
  this.harnessSupportedProviderHints = harnessSupportedProviderHints;
49
50
  this.agentConnectivityResolver = agentConnectivityResolver;
50
- this.hooksForAgent = hooksForAgent;
51
+ this.resolvers = resolvers;
51
52
  this.identityStore = identityStore;
52
53
  this.agentIdGenerator = agentIdGenerator;
53
54
  this.clock = clock;
@@ -75,9 +76,9 @@ export class DefaultAgentManager {
75
76
  * is private, so this is the only way to obtain an instance, but
76
77
  * consumers should always go through {@link createAgentManager}.
77
78
  */
78
- static async __build(harness, harnessSupportedProviderHints, agentConnectivityResolver, hooksForAgent, storageRootFolder, agentIdGenerator, clock, logBus) {
79
+ static async __build(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, storageRootFolder, agentIdGenerator, clock, logBus) {
79
80
  const identityStore = new AgentIdentityStore(storageRootFolder, harness.harnessId, logBus);
80
- const manager = new DefaultAgentManager(harness, harnessSupportedProviderHints, agentConnectivityResolver, hooksForAgent, identityStore, agentIdGenerator, clock, logBus);
81
+ const manager = new DefaultAgentManager(harness, harnessSupportedProviderHints, agentConnectivityResolver, resolvers, identityStore, agentIdGenerator, clock, logBus);
81
82
  await manager.init();
82
83
  return manager;
83
84
  }
@@ -183,8 +184,9 @@ export class DefaultAgentManager {
183
184
  if (!this.harnessSupportedProviderHints.includes(providerHint)) {
184
185
  throw new AgentSDKError(`Harness "${this.harness.harnessId}" does not support providerHint "${providerHint}". Supported: ${this.harnessSupportedProviderHints.join(', ')}.`, AgentSDKErrorType.MODEL_NOT_SUPPORTED_BY_HARNESS);
185
186
  }
186
- const hooks = this.hooksForAgent?.(agentId, config) ?? {};
187
- await this.harness.createAgent(agentId, projectRoot, runtime.modelConnectivityInfo, toHarnessConfig(config, runtime.orgJwt), { ...(options.abortSignal !== undefined ? { abortSignal: options.abortSignal } : {}), hooks });
187
+ const hooks = this.resolvers.hooksForAgent?.(agentId, config) ?? {};
188
+ const mcpAuthProviders = normalizeMcpAuthProviders(config.mcpServers, this.resolvers.mcpAuthProviderResolver?.(agentId, config));
189
+ await this.harness.createAgent(agentId, projectRoot, runtime.modelConnectivityInfo, toHarnessConfig(config, runtime.orgJwt), { ...(options.abortSignal !== undefined ? { abortSignal: options.abortSignal } : {}), hooks, mcpAuthProviders });
188
190
  const agentSlice = this.router.registerAgent(agentId);
189
191
  // Forward-compat: register the agent against the wire-communication router
190
192
  // too. Today every WireCommunicationEvent lands on the unrouted slice (no
@@ -192,7 +194,7 @@ export class DefaultAgentManager {
192
194
  // symmetrically with the telemetry router so the wiring is in place if/when
193
195
  // the event shape grows an agentId field.
194
196
  this.wireRouter.registerAgent(agentId);
195
- const agent = new DefaultAgent(this.harness, agentId, projectRoot, config, runtime.modelConnectivityInfo, runtime.orgConnection, runtime.orgJwt, this.agentConnectivityResolver, this.harnessSupportedProviderHints, this.hooksForAgent, this.identityStore, this.router, agentSlice, { telemetry: this.telemetryBus, log: this.logBus }, this.clock, this.agentIdGenerator);
197
+ const agent = new DefaultAgent(this.harness, agentId, projectRoot, config, runtime.modelConnectivityInfo, runtime.orgConnection, runtime.orgJwt, this.agentConnectivityResolver, this.harnessSupportedProviderHints, this.resolvers, this.identityStore, this.router, agentSlice, { telemetry: this.telemetryBus, log: this.logBus }, this.clock, this.agentIdGenerator);
196
198
  this.agents.set(agentId, agent);
197
199
  const agentCreatedAt = this.clock.now();
198
200
  const modelName = runtime.modelConnectivityInfo.model.name;
@@ -359,7 +361,7 @@ export async function createAgentManager(storageRootFolder, harnessFactory, opti
359
361
  }
360
362
  const agentConnectivityResolver = options?.connectivityResolver ?? new DefaultAgentConnectivityResolver();
361
363
  const clock = new RealClock();
362
- return DefaultAgentManager.__build(harness, harnessFactory.supportedProviderHints, agentConnectivityResolver, options?.hooksForAgent, storageRootFolder, new UUIDGenerator(), clock,
364
+ return DefaultAgentManager.__build(harness, harnessFactory.supportedProviderHints, agentConnectivityResolver, { hooksForAgent: options?.hooksForAgent, mcpAuthProviderResolver: options?.mcpAuthProviderResolver }, storageRootFolder, new UUIDGenerator(), clock,
363
365
  // The manager's root log bus shares the manager clock and self-reads the process environment context.
364
366
  new LogBus(clock));
365
367
  }
package/dist/agent.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type Clock, type JSONWebToken, LogBus, type LogRecord, type OrgConnection, type UniqueIDGenerator, type Unsubscribe } from '@salesforce/agentic-common';
2
2
  import type { AgentHarness } from './harness/agent-harness.js';
3
- import { type AgentConfig } from './harness/harness-config.js';
3
+ import { type AgentConfig, type McpAuthProviderResolver } from './harness/harness-config.js';
4
4
  import { type ChatSession } from './chat-session.js';
5
5
  import type { McpServerInfo } from './mcp-config.js';
6
6
  import type { AgentConnectivityResolver } from './agent-connectivity-resolver.js';
@@ -144,6 +144,15 @@ export interface Agent {
144
144
  /** Subscribe to structured log records scoped to this agent (and its sessions). Returns an unsubscribe function. */
145
145
  onLog(callback: (record: LogRecord) => void): Unsubscribe;
146
146
  }
147
+ /**
148
+ * The per-agent runtime resolvers the SDK threads from `createAgentManager` into each agent — callbacks the consumer
149
+ * supplies once and the SDK re-invokes per install / restore / update. Bundled so a new runtime binding is one field
150
+ * here, not another parameter through every manager → agent seam. Each member is optional (unset by the consumer).
151
+ */
152
+ export type AgentRuntimeResolvers = {
153
+ hooksForAgent?: HooksForAgent;
154
+ mcpAuthProviderResolver?: McpAuthProviderResolver;
155
+ };
147
156
  /**
148
157
  * Default implementation of {@link Agent} that delegates
149
158
  * agent and thread operations to an {@link AgentHarness}.
@@ -158,7 +167,7 @@ export declare class DefaultAgent implements Agent {
158
167
  private orgJwt;
159
168
  private readonly agentConnectivityResolver;
160
169
  private readonly harnessSupportedProviderHints;
161
- private readonly hooksForAgent;
170
+ private readonly resolvers;
162
171
  private readonly identityStore;
163
172
  private readonly sessions;
164
173
  private readonly sessionSliceUnregisters;
@@ -181,17 +190,18 @@ export declare class DefaultAgent implements Agent {
181
190
  * @param orgConnection - Authenticated org connection carrying identity and env inference.
182
191
  * @param orgJwt - Self-refreshing JWT for the resolved org (used for MCP auth injection).
183
192
  * @param agentConnectivityResolver - Used to re-resolve org connectivity when the org or model changes.
184
- * @param hooksForAgent - Per-agent hooks resolver supplied by the SDK consumer at `createAgentManager` time. The
185
- * agent re-invokes it on every `updateAgentConfig` (with `nextConfig`, and again with `previousConfig` on the
186
- * rollback path) so the bag the harness sees always reflects the current persisted config. `undefined` when
187
- * the consumer didn't pass a `hooksForAgent`.
193
+ * @param resolvers - Per-agent runtime resolvers ({@link AgentRuntimeResolvers}: `hooksForAgent`,
194
+ * `mcpAuthProviderResolver`) supplied by the SDK consumer at `createAgentManager` time. The agent re-invokes
195
+ * them on every `updateAgentConfig` (with `nextConfig`, and again with `previousConfig` on the rollback path)
196
+ * so what the harness sees always reflects the current persisted config. Each member is `undefined` when the
197
+ * consumer didn't pass it.
188
198
  * @param identityStore - SDK-owned persistence for the `{ agentId, projectRoot, AgentConfig }` triple. The agent
189
199
  * calls `write()` on a successful `updateAgentConfig` so disk state and in-memory state stay in lockstep.
190
200
  * @param router - Telemetry router used to obtain session slices when sessions are created.
191
201
  * @param inbound - Router slice delivering harness events routed to this agent (non-session-scoped).
192
202
  * @param parent - Manager's bus pair; this agent forwards its events upward into them.
193
203
  */
194
- constructor(harness: AgentHarness, agentId: string, projectRoot: string, config: AgentConfig, modelConnectivityInfo: ModelConnectivityInfo, orgConnection: OrgConnection | undefined, orgJwt: JSONWebToken | undefined, agentConnectivityResolver: AgentConnectivityResolver, harnessSupportedProviderHints: readonly ProviderHint[], hooksForAgent: HooksForAgent | undefined, identityStore: AgentIdentityStore, router: TelemetryRouter, inbound: TelemetrySlice, parent: AgentParentBuses, clock?: Clock, idGenerator?: UniqueIDGenerator);
204
+ constructor(harness: AgentHarness, agentId: string, projectRoot: string, config: AgentConfig, modelConnectivityInfo: ModelConnectivityInfo, orgConnection: OrgConnection | undefined, orgJwt: JSONWebToken | undefined, agentConnectivityResolver: AgentConnectivityResolver, harnessSupportedProviderHints: readonly ProviderHint[], resolvers: AgentRuntimeResolvers, identityStore: AgentIdentityStore, router: TelemetryRouter, inbound: TelemetrySlice, parent: AgentParentBuses, clock?: Clock, idGenerator?: UniqueIDGenerator);
195
205
  /**
196
206
  * @requirements
197
207
  * - MUST return the agent's ID.
package/dist/agent.js CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { LogBus, RealClock, UUIDGenerator, } from '@salesforce/agentic-common';
6
6
  import { toHarnessConfig } from './harness/harness-config.js';
7
+ import { normalizeMcpAuthProviders } from './mcp-auth.js';
7
8
  import { DefaultChatSession } from './chat-session.js';
8
9
  import { AgentSDKError, AgentSDKErrorType } from './errors.js';
9
10
  import { createTelemetryBus } from './types/telemetry-events.js';
@@ -21,7 +22,7 @@ export class DefaultAgent {
21
22
  orgJwt;
22
23
  agentConnectivityResolver;
23
24
  harnessSupportedProviderHints;
24
- hooksForAgent;
25
+ resolvers;
25
26
  identityStore;
26
27
  sessions = new Map();
27
28
  sessionSliceUnregisters = new Map();
@@ -46,17 +47,18 @@ export class DefaultAgent {
46
47
  * @param orgConnection - Authenticated org connection carrying identity and env inference.
47
48
  * @param orgJwt - Self-refreshing JWT for the resolved org (used for MCP auth injection).
48
49
  * @param agentConnectivityResolver - Used to re-resolve org connectivity when the org or model changes.
49
- * @param hooksForAgent - Per-agent hooks resolver supplied by the SDK consumer at `createAgentManager` time. The
50
- * agent re-invokes it on every `updateAgentConfig` (with `nextConfig`, and again with `previousConfig` on the
51
- * rollback path) so the bag the harness sees always reflects the current persisted config. `undefined` when
52
- * the consumer didn't pass a `hooksForAgent`.
50
+ * @param resolvers - Per-agent runtime resolvers ({@link AgentRuntimeResolvers}: `hooksForAgent`,
51
+ * `mcpAuthProviderResolver`) supplied by the SDK consumer at `createAgentManager` time. The agent re-invokes
52
+ * them on every `updateAgentConfig` (with `nextConfig`, and again with `previousConfig` on the rollback path)
53
+ * so what the harness sees always reflects the current persisted config. Each member is `undefined` when the
54
+ * consumer didn't pass it.
53
55
  * @param identityStore - SDK-owned persistence for the `{ agentId, projectRoot, AgentConfig }` triple. The agent
54
56
  * calls `write()` on a successful `updateAgentConfig` so disk state and in-memory state stay in lockstep.
55
57
  * @param router - Telemetry router used to obtain session slices when sessions are created.
56
58
  * @param inbound - Router slice delivering harness events routed to this agent (non-session-scoped).
57
59
  * @param parent - Manager's bus pair; this agent forwards its events upward into them.
58
60
  */
59
- constructor(harness, agentId, projectRoot, config, modelConnectivityInfo, orgConnection, orgJwt, agentConnectivityResolver, harnessSupportedProviderHints, hooksForAgent, identityStore, router, inbound, parent, clock = new RealClock(), idGenerator = new UUIDGenerator()) {
61
+ constructor(harness, agentId, projectRoot, config, modelConnectivityInfo, orgConnection, orgJwt, agentConnectivityResolver, harnessSupportedProviderHints, resolvers, identityStore, router, inbound, parent, clock = new RealClock(), idGenerator = new UUIDGenerator()) {
60
62
  this.harness = harness;
61
63
  this.agentId = agentId;
62
64
  this.projectRoot = projectRoot;
@@ -66,7 +68,7 @@ export class DefaultAgent {
66
68
  this.orgJwt = orgJwt;
67
69
  this.agentConnectivityResolver = agentConnectivityResolver;
68
70
  this.harnessSupportedProviderHints = harnessSupportedProviderHints;
69
- this.hooksForAgent = hooksForAgent;
71
+ this.resolvers = resolvers;
70
72
  this.identityStore = identityStore;
71
73
  this.router = router;
72
74
  this.clock = clock;
@@ -157,10 +159,12 @@ export class DefaultAgent {
157
159
  nextOrgJwt = runtime.orgJwt;
158
160
  }
159
161
  try {
160
- const nextHooks = this.hooksForAgent?.(this.agentId, nextConfig) ?? {};
162
+ const nextHooks = this.resolvers.hooksForAgent?.(this.agentId, nextConfig) ?? {};
163
+ const nextMcpAuthProviders = normalizeMcpAuthProviders(nextConfig.mcpServers, this.resolvers.mcpAuthProviderResolver?.(this.agentId, nextConfig));
161
164
  await this.harness.updateAgent(this.agentId, nextModelConnectivityInfo, toHarnessConfig(nextConfig, nextOrgJwt), {
162
165
  ...(options?.abortSignal !== undefined ? { abortSignal: options.abortSignal } : {}),
163
166
  hooks: nextHooks,
167
+ mcpAuthProviders: nextMcpAuthProviders,
164
168
  });
165
169
  // Persist before the in-memory swaps so a write failure flows through the same
166
170
  // catch block as an updateAgent failure: the rollback re-runs updateAgent against
@@ -177,8 +181,9 @@ export class DefaultAgent {
177
181
  // against its current state — if updateAgent partially applied (e.g. some MCP
178
182
  // servers were already cycled), reverting via updateAgent restores them too.
179
183
  try {
180
- const previousHooks = this.hooksForAgent?.(this.agentId, previousConfig) ?? {};
181
- await this.harness.updateAgent(this.agentId, previousModelConnectivityInfo, toHarnessConfig(previousConfig, previousOrgJwt), { hooks: previousHooks });
184
+ const previousHooks = this.resolvers.hooksForAgent?.(this.agentId, previousConfig) ?? {};
185
+ const previousMcpAuthProviders = normalizeMcpAuthProviders(previousConfig.mcpServers, this.resolvers.mcpAuthProviderResolver?.(this.agentId, previousConfig));
186
+ await this.harness.updateAgent(this.agentId, previousModelConnectivityInfo, toHarnessConfig(previousConfig, previousOrgJwt), { hooks: previousHooks, mcpAuthProviders: previousMcpAuthProviders });
182
187
  }
183
188
  catch {
184
189
  // Ignore restoration errors; rethrow the original failure.
package/dist/errors.d.ts CHANGED
@@ -4,6 +4,7 @@ export declare const AgentSDKErrorType: {
4
4
  readonly COMPACTION_FAILED: 'COMPACTION_FAILED';
5
5
  readonly DISPOSED: 'DISPOSED';
6
6
  readonly INCOMPATIBLE_HARNESS: 'INCOMPATIBLE_HARNESS';
7
+ readonly INVALID_MCP_AUTH_CONFIG: 'INVALID_MCP_AUTH_CONFIG';
7
8
  readonly INVALID_MESSAGE_CONTENT: 'INVALID_MESSAGE_CONTENT';
8
9
  readonly MCP_SERVER_DISABLED: 'MCP_SERVER_DISABLED';
9
10
  readonly MCP_SERVER_NOT_FOUND: 'MCP_SERVER_NOT_FOUND';
package/dist/errors.js CHANGED
@@ -8,6 +8,7 @@ export const AgentSDKErrorType = {
8
8
  COMPACTION_FAILED: 'COMPACTION_FAILED',
9
9
  DISPOSED: 'DISPOSED',
10
10
  INCOMPATIBLE_HARNESS: 'INCOMPATIBLE_HARNESS',
11
+ INVALID_MCP_AUTH_CONFIG: 'INVALID_MCP_AUTH_CONFIG',
11
12
  INVALID_MESSAGE_CONTENT: 'INVALID_MESSAGE_CONTENT',
12
13
  MCP_SERVER_DISABLED: 'MCP_SERVER_DISABLED',
13
14
  MCP_SERVER_NOT_FOUND: 'MCP_SERVER_NOT_FOUND',
@@ -1,5 +1,5 @@
1
1
  import type { LogRecord, Unsubscribe } from '@salesforce/agentic-common';
2
- import type { McpServerInfo } from '../mcp-config.js';
2
+ import type { McpServerInfo, McpAuthProviders } from '../mcp-config.js';
3
3
  import type { ChatStreamResult } from '../types/events.js';
4
4
  import type { Message, MessagePart } from '../types/messages.js';
5
5
  import type { TelemetryEventCallback } from '../types/telemetry-events.js';
@@ -142,11 +142,20 @@ export interface AgentHarness {
142
142
  * native error path so the original tool output never leaks to the
143
143
  * model. The SDK does not own fail-closed semantics; consumers
144
144
  * wrap their hook bodies in `try`/`catch` themselves when they
145
- * want a richer fail-closed substitute.
145
+ * want a richer fail-closed substitute.
146
+ * - `mcpAuthProviders` — per-agent OAuth provider map (remote MCP server
147
+ * name → {@link McpOAuthClientProvider}), resolved by the SDK from
148
+ * `createAgentManager`'s `mcpAuthProviderResolver` and already normalized
149
+ * (Salesforce URLs / stdio / disabled / missing servers dropped, static-
150
+ * Authorization conflicts rejected — see `normalizeMcpAuthProviders`).
151
+ * Providers are runtime bindings, NEVER part of `HarnessAgentConfig`;
152
+ * harnesses store the map on per-agent state and forward `providers[name]`
153
+ * into the remote transport for that server.
146
154
  */
147
155
  createAgent(agentId: string, projectRoot: string, modelConnectivityInfo: ModelConnectivityInfo, config?: HarnessAgentConfig, options?: {
148
156
  abortSignal?: AbortSignal;
149
157
  hooks?: AgentHooks;
158
+ mcpAuthProviders?: McpAuthProviders;
150
159
  }): Promise<void>;
151
160
  /**
152
161
  * Destroy an agent and release its resources (MCP connections, workspace, memory).
@@ -179,9 +188,12 @@ export interface AgentHarness {
179
188
  * matching the rest of the cross-harness contract.
180
189
  * - preserve the in-memory MCP client (and its discovered tool catalog)
181
190
  * for any server name whose config is `mcpServerConfigEqual` to the
182
- * currently-applied one. No transport teardown, no `tools/list`
183
- * re-run, no per-server discovery telemetry.
184
- * - cycle (disconnect-then-reconnect) any server whose config differs.
191
+ * currently-applied one AND whose applied OAuth provider reference (from
192
+ * `options.mcpAuthProviders`) is unchanged. No transport teardown, no
193
+ * `tools/list` re-run, no per-server discovery telemetry.
194
+ * - cycle (disconnect-then-reconnect) any server whose config differs OR
195
+ * whose applied provider reference changed (a swapped provider instance
196
+ * must cycle the server so the new one owns auth).
185
197
  * - disconnect any server present in the currently-applied config but
186
198
  * absent from the next config, removing it from
187
199
  * `getMcpServerInfo()`'s output.
@@ -233,10 +245,15 @@ export interface AgentHarness {
233
245
  * re-resolved on every `updateAgent` so consumers can vary hooks by
234
246
  * config (and so a rollback `updateAgent(previousConfig)` restores
235
247
  * the prior hook bag too).
248
+ * - `mcpAuthProviders` — per-agent normalized OAuth provider map, re-resolved
249
+ * on every `updateAgent` (so a provider swap cycles its server, and a
250
+ * rollback `updateAgent(previousConfig)` restores the prior providers).
251
+ * Same runtime-only semantics as `createAgent.options.mcpAuthProviders`.
236
252
  */
237
253
  updateAgent(agentId: string, modelConnectivityInfo: ModelConnectivityInfo, config?: HarnessAgentConfig, options?: {
238
254
  abortSignal?: AbortSignal;
239
255
  hooks?: AgentHooks;
256
+ mcpAuthProviders?: McpAuthProviders;
240
257
  }): Promise<void>;
241
258
  /**
242
259
  * List the IDs of all currently registered agents.
@@ -1,5 +1,5 @@
1
1
  import type { Decision, ToolDefinition, ToolPolicyRule } from '../types/tools.js';
2
- import type { MCPConfiguration } from '../mcp-config.js';
2
+ import type { MCPConfiguration, McpAuthProviders } from '../mcp-config.js';
3
3
  import type { JSONWebToken } from '@salesforce/agentic-common';
4
4
  import type { Model, ModelName } from '../models/index.js';
5
5
  /**
@@ -88,6 +88,25 @@ export type AgentConfig = {
88
88
  */
89
89
  defaultToolDecision?: Decision;
90
90
  };
91
+ /**
92
+ * Resolves the per-agent OAuth {@link McpAuthProviders} map (remote MCP server
93
+ * name → provider) from the agent's id and the config the SDK has on file.
94
+ * Invoked by `AgentManager` once per agent install (`createAgent`, boot-time
95
+ * restore) and by `Agent.updateAgentConfig`; the resolved map is normalized by
96
+ * the SDK (see `normalizeMcpAuthProviders`) and threaded to the harness via
97
+ * `AgentHarness.createAgent` / `updateAgent`'s `options.mcpAuthProviders`.
98
+ *
99
+ * This is the seam that keeps credential-bearing provider objects OFF the
100
+ * persisted {@link AgentConfig}: providers are supplied here at runtime, never
101
+ * serialized. Mirrors `HooksForAgent`.
102
+ *
103
+ * The callback is sync — the SDK does not await. Return **stable, pre-constructed**
104
+ * provider instances: returning a NEW provider object for a server on each call
105
+ * intentionally cycles that server on every update (reference identity is how a
106
+ * provider swap is detected). Consumers needing async provider setup pre-construct
107
+ * before returning.
108
+ */
109
+ export type McpAuthProviderResolver = (agentId: string, config: AgentConfig) => McpAuthProviders | undefined;
91
110
  /**
92
111
  * Harness-facing configuration for creating/updating an agent.
93
112
  *
@@ -45,6 +45,7 @@ export type { AgentHarness, HarnessFactory, WithAgentConfig, ConfigOf } from './
45
45
  export type { AgentHooks } from '../types/redaction.js';
46
46
  export { SUPPORTED_PROTOCOL_VERSIONS } from './agent-harness.js';
47
47
  export { mcpServerConfigEqual } from '../mcp-config.js';
48
+ export type { McpAuthProviders, McpOAuthClientProvider } from '../mcp-config.js';
48
49
  export { HarnessBusOwner } from './harness-bus-owner.js';
49
50
  export { lowerStreamInput, type InputMessagePart } from './stream-input.js';
50
51
  export { GenSink } from './gen-sink.js';
package/dist/index.d.ts CHANGED
@@ -6,9 +6,9 @@ export { BUILT_IN_TOOL_POLICIES, SKILL_BRIDGE_SERVER_ID, definePolicy, matcherMa
6
6
  export type { ResolverResult, ResolverTiers, ToolInvocation } from './policy-resolver.js';
7
7
  export type { ContextUsage, FinishReason, UsageMetadata } from './types/usage.js';
8
8
  export type { AgentHooks, HooksForAgent, ToolResultRedactor, ToolResultRedactionInput, ToolResultRedactionResult, } from './types/redaction.js';
9
- export type { AgentConfig, HarnessAgentConfig, StreamOptions } from './harness/harness-config.js';
9
+ export type { AgentConfig, HarnessAgentConfig, McpAuthProviderResolver, StreamOptions, } from './harness/harness-config.js';
10
10
  export { DEFAULT_MAX_STEPS } from './harness/harness-config.js';
11
- export type { MCPConfiguration, MCPServerConfig, MCPStdioServerConfig, MCPRemoteServerConfig, McpServerInfo, McpServerErrorCategory, McpServerErrorDetail, McpToolInfo, McpToolAnnotations, } from './mcp-config.js';
11
+ export type { MCPConfiguration, MCPServerConfig, MCPStdioServerConfig, MCPRemoteServerConfig, McpOAuthClientProvider, McpAuthProviders, McpServerInfo, McpServerErrorCategory, McpServerErrorDetail, McpToolInfo, McpToolAnnotations, } from './mcp-config.js';
12
12
  export { McpServerStatus, mcpServerConfigEqual } from './mcp-config.js';
13
13
  export { Model, ModelName, createClaudeModel, Models, validateMultimodalFiles, ACCEPTED_MODEL_WIRE_IDS, } from './models/index.js';
14
14
  export type { ClaudeModelOverrides, MultimodalFile, SupportedFileFormat, ThinkingMode } from './models/index.js';
@@ -28,7 +28,7 @@ export type { AgentCreatedEvent, AgentDestroyedEvent, ChatStreamCompletedEvent,
28
28
  export type { LogLevel, LogRecord, Unsubscribe } from '@salesforce/agentic-common';
29
29
  export type { EnvironmentFields } from '@salesforce/agentic-common';
30
30
  export { readEnvironmentContext, resolveFeatureId } from '@salesforce/agentic-common';
31
- export { resolveMcpServerHeaders } from './mcp-auth.js';
31
+ export { resolveMcpServerHeaders, isSalesforcePlatformMcpUrl } from './mcp-auth.js';
32
32
  export type { OrgConnection, OrgConnectionFactory } from '@salesforce/agentic-common';
33
33
  export type { JSONWebToken, JWTOptions, RequiredJWTHeaders, RequiredJWTPayload } from '@salesforce/agentic-common';
34
34
  export { Workspace } from './workspace.js';
package/dist/index.js CHANGED
@@ -23,6 +23,6 @@ export { WireCommunicationFileWriter, } from './wire-communication-file-writer.j
23
23
  export { AgentSDKError, AgentSDKErrorType } from './errors.js';
24
24
  export { readEnvironmentContext, resolveFeatureId } from '@salesforce/agentic-common';
25
25
  // ── MCP Auth ────────────────────────────────────────────────────────
26
- export { resolveMcpServerHeaders } from './mcp-auth.js';
26
+ export { resolveMcpServerHeaders, isSalesforcePlatformMcpUrl } from './mcp-auth.js';
27
27
  export { Workspace } from './workspace.js';
28
28
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,5 @@
1
1
  import type { JSONWebToken } from '@salesforce/agentic-common';
2
+ import type { MCPConfiguration, McpAuthProviders } from './mcp-config.js';
2
3
  /**
3
4
  * Returns true if the URL matches a Salesforce Hosted MCP Server endpoint.
4
5
  * Covers all environments (prod, dev, test, perf, stage) and all API versions
@@ -18,3 +19,22 @@ export declare function isSalesforcePlatformMcpUrl(url: string | URL): boolean;
18
19
  * @returns The resolved headers — original headers plus any injected auth.
19
20
  */
20
21
  export declare function resolveMcpServerHeaders(url: string | URL, orgJwt: JSONWebToken | undefined, headers?: Record<string, string>): Promise<Record<string, string>>;
22
+ /**
23
+ * Normalizes the raw provider map returned by a consumer's
24
+ * `mcpAuthProviderResolver` into the map the harness may act on, enforcing the
25
+ * "one Authorization owner per server" rule centrally (so no harness reimplements
26
+ * this policy). For each provider entry, keyed by server name:
27
+ *
28
+ * - **Dropped** (provider ignored) when the named server is missing, is a stdio
29
+ * server, is disabled, or is a Salesforce Platform URL. Salesforce URLs are
30
+ * authenticated by the rotating org JWT, which owns auth there; forwarding a
31
+ * provider alongside it would arm two competing Authorization owners.
32
+ * - **Rejected** (throws) when the server carries a static `Authorization`
33
+ * header AND a provider — the MCP SDK transport spreads static `requestInit`
34
+ * headers OVER the provider token, so the static header would silently win and
35
+ * strand the provider in a 401 loop. Failing loud at resolve time beats a
36
+ * silent auth failure at runtime.
37
+ *
38
+ * Returns a fresh, reference-only map (the same provider instances, never mutated).
39
+ */
40
+ export declare function normalizeMcpAuthProviders(mcpServers: MCPConfiguration | undefined, resolved: McpAuthProviders | undefined): McpAuthProviders;
package/dist/mcp-auth.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * Copyright 2026, Salesforce, Inc. All rights reserved.
3
3
  * See LICENSE.txt for license terms.
4
4
  */
5
+ import { AgentSDKError, AgentSDKErrorType } from './errors.js';
5
6
  const SALESFORCE_PLATFORM_MCP_URL_REGEX = /^https:\/\/((dev|test|perf|stage)\.)?api\.salesforce\.com\/platform\/mcp\//;
6
7
  /**
7
8
  * Returns true if the URL matches a Salesforce Hosted MCP Server endpoint.
@@ -37,4 +38,40 @@ export async function resolveMcpServerHeaders(url, orgJwt, headers = {}) {
37
38
  function hasAuthorizationHeader(headers) {
38
39
  return Object.keys(headers).some((key) => key.toLowerCase() === 'authorization');
39
40
  }
41
+ /**
42
+ * Normalizes the raw provider map returned by a consumer's
43
+ * `mcpAuthProviderResolver` into the map the harness may act on, enforcing the
44
+ * "one Authorization owner per server" rule centrally (so no harness reimplements
45
+ * this policy). For each provider entry, keyed by server name:
46
+ *
47
+ * - **Dropped** (provider ignored) when the named server is missing, is a stdio
48
+ * server, is disabled, or is a Salesforce Platform URL. Salesforce URLs are
49
+ * authenticated by the rotating org JWT, which owns auth there; forwarding a
50
+ * provider alongside it would arm two competing Authorization owners.
51
+ * - **Rejected** (throws) when the server carries a static `Authorization`
52
+ * header AND a provider — the MCP SDK transport spreads static `requestInit`
53
+ * headers OVER the provider token, so the static header would silently win and
54
+ * strand the provider in a 401 loop. Failing loud at resolve time beats a
55
+ * silent auth failure at runtime.
56
+ *
57
+ * Returns a fresh, reference-only map (the same provider instances, never mutated).
58
+ */
59
+ export function normalizeMcpAuthProviders(mcpServers, resolved) {
60
+ if (!resolved || !mcpServers)
61
+ return {};
62
+ const normalized = {};
63
+ for (const [name, provider] of Object.entries(resolved)) {
64
+ const server = mcpServers[name];
65
+ if (!server || server.type !== 'remote' || server.enabled === false)
66
+ continue;
67
+ if (isSalesforcePlatformMcpUrl(server.url))
68
+ continue;
69
+ if (server.headers && hasAuthorizationHeader(server.headers)) {
70
+ throw new AgentSDKError(`MCP server "${name}" has both a static Authorization header and an OAuth authProvider. ` +
71
+ `Remove the static Authorization header — the provider owns auth for this server.`, AgentSDKErrorType.INVALID_MCP_AUTH_CONFIG);
72
+ }
73
+ normalized[name] = provider;
74
+ }
75
+ return normalized;
76
+ }
40
77
  //# sourceMappingURL=mcp-auth.js.map
@@ -35,12 +35,82 @@ export type MCPStdioServerConfig = {
35
35
  /** Timeout in milliseconds for individual requests to the server. */
36
36
  timeout?: number;
37
37
  };
38
+ /**
39
+ * Harness-neutral contract for an MCP OAuth client provider, letting an
40
+ * OAuth-protected remote MCP server recover the RFC 9728 `401` challenge its
41
+ * transport receives (the static {@link MCPRemoteServerConfig.headers} seam
42
+ * cannot — headers are snapshotted at publish time).
43
+ *
44
+ * This package is harness-runtime-free (a lint rule bans importing
45
+ * `@modelcontextprotocol/*`), so this structurally mirrors the *required* core
46
+ * of the MCP SDK's `OAuthClientProvider` — same pattern as {@link McpToolAnnotations}.
47
+ * It is bidirectionally compatible: a real provider assigns to this field, and
48
+ * this field assigns into the transport option with no cast. Optional upstream
49
+ * members are omitted (a real instance still carries them at runtime); the
50
+ * harness-side compile-time drift-guard pins the compatibility.
51
+ */
52
+ export interface McpOAuthClientProvider {
53
+ /** URL the user agent is redirected to after authorization (or `undefined` for non-interactive flows). */
54
+ readonly redirectUrl: string | URL | undefined;
55
+ /** Metadata about this OAuth client. */
56
+ readonly clientMetadata: {
57
+ redirect_uris: string[];
58
+ };
59
+ /** Loads this client's registration, or `undefined` if not yet registered. */
60
+ clientInformation(): {
61
+ client_id: string;
62
+ } | undefined | Promise<{
63
+ client_id: string;
64
+ } | undefined>;
65
+ /** Persists a dynamic client registration for later {@link clientInformation} reads. */
66
+ saveClientInformation?(clientInformation: {
67
+ client_id: string;
68
+ }): void | Promise<void>;
69
+ /** Loads any existing OAuth tokens for the current session, or `undefined`. */
70
+ tokens(): {
71
+ access_token: string;
72
+ token_type: string;
73
+ } | undefined | Promise<{
74
+ access_token: string;
75
+ token_type: string;
76
+ } | undefined>;
77
+ /** Stores new OAuth tokens after a successful authorization. */
78
+ saveTokens(tokens: {
79
+ access_token: string;
80
+ token_type: string;
81
+ }): void | Promise<void>;
82
+ /** Redirects the user agent to the given URL to begin the authorization flow. */
83
+ redirectToAuthorization(authorizationUrl: URL): void | Promise<void>;
84
+ /** Saves a PKCE code verifier before redirecting to the authorization flow. */
85
+ saveCodeVerifier(codeVerifier: string): void | Promise<void>;
86
+ /** Loads the PKCE code verifier needed to validate the authorization result. */
87
+ codeVerifier(): string | Promise<string>;
88
+ }
89
+ /**
90
+ * A per-agent map of remote MCP server name → its OAuth {@link McpOAuthClientProvider}.
91
+ *
92
+ * Providers are behavioral, credential-bearing runtime objects, so they are
93
+ * NOT part of the persisted {@link MCPConfiguration}. They are supplied at
94
+ * runtime through the manager's `mcpAuthProviderResolver` and threaded to the
95
+ * harness via the `createAgent` / `updateAgent` options bag — never serialized
96
+ * to disk. A harness cycles a server when its provider reference changes.
97
+ */
98
+ export type McpAuthProviders = Readonly<Record<string, McpOAuthClientProvider>>;
38
99
  /** MCP server accessible over HTTP/SSE at a remote URL. */
39
100
  export type MCPRemoteServerConfig = {
40
101
  type: 'remote';
41
102
  /** URL of the remote MCP server endpoint. */
42
103
  url: string | URL;
43
- /** HTTP headers sent with requests to the server. */
104
+ /**
105
+ * HTTP headers sent with requests to the server.
106
+ *
107
+ * When an OAuth `authProvider` is supplied for this server (via the
108
+ * manager's `mcpAuthProviderResolver`), this must NOT carry an
109
+ * `Authorization` header — the MCP SDK transport spreads static
110
+ * `requestInit` headers OVER the provider token, so a static
111
+ * `Authorization` would silently win and strand the provider. The SDK
112
+ * rejects that combination at resolve time (see `normalizeMcpAuthProviders`).
113
+ */
44
114
  headers?: Record<string, string>;
45
115
  /** Whether this server is enabled. Defaults to `true`. */
46
116
  enabled?: boolean;
@@ -187,6 +257,8 @@ export type McpToolInfo = {
187
257
  export type McpServerErrorCategory =
188
258
  /** 3s default or configured timeout exceeded. */
189
259
  'connect-timeout'
260
+ /** OAuth needs user interaction; the host must authorize before reconnecting. */
261
+ | 'oauth-required'
190
262
  /** Unauthorized — JWT missing/expired. */
191
263
  | 'http-401'
192
264
  /** Forbidden — JWT valid but the principal lacks permission. */
@@ -230,7 +302,7 @@ export type McpServerErrorDetail = {
230
302
  code?: number;
231
303
  /**
232
304
  * Whether the failure is transient (worth a retry via
233
- * `Agent.reconnectMcpServer` / `Agent.refreshMcpAuth`) vs fatal (consumer
305
+ * `Agent.reconnectMcpServer`) vs fatal (consumer
234
306
  * must fix config). Harness implementations populate this with their best
235
307
  * heuristic; consumers may override per-category.
236
308
  */
@@ -259,6 +331,12 @@ export type McpServerErrorDetail = {
259
331
  * strings round-trip; `headers` (key-order-insensitive); `timeout`,
260
332
  * `reconnectionOptions` (field-wise).
261
333
  *
334
+ * This predicate compares only the serializable {@link MCPServerConfig}. A
335
+ * server's OAuth `authProvider` is NOT part of the config (it is a runtime
336
+ * binding supplied per-agent via `mcpAuthProviderResolver`); a harness cycles
337
+ * a server when this predicate fails OR its applied provider reference
338
+ * changed. See each harness's `updateAgent` diff.
339
+ *
262
340
  * Two configs that pass this predicate but produce different runtime tools
263
341
  * (e.g. an upstream stdio server whose binary was overwritten on disk) are
264
342
  * NOT detected here — the predicate compares declared config, not runtime
@@ -40,6 +40,12 @@ export var McpServerStatus;
40
40
  * strings round-trip; `headers` (key-order-insensitive); `timeout`,
41
41
  * `reconnectionOptions` (field-wise).
42
42
  *
43
+ * This predicate compares only the serializable {@link MCPServerConfig}. A
44
+ * server's OAuth `authProvider` is NOT part of the config (it is a runtime
45
+ * binding supplied per-agent via `mcpAuthProviderResolver`); a harness cycles
46
+ * a server when this predicate fails OR its applied provider reference
47
+ * changed. See each harness's `updateAgent` diff.
48
+ *
43
49
  * Two configs that pass this predicate but produce different runtime tools
44
50
  * (e.g. an upstream stdio server whose binary was overwritten on disk) are
45
51
  * NOT detected here — the predicate compares declared config, not runtime
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/sfdx-agent-sdk",
3
- "version": "0.50.0",
3
+ "version": "0.52.0",
4
4
  "description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -47,24 +47,24 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@eslint/js": "^10.0.1",
50
- "@salesforce/sfdx-agent-harness-claude": "0.46.0",
51
- "@salesforce/sfdx-agent-harness-mastra": "0.49.0",
52
- "@salesforce/sfdx-agent-harness-openai": "0.15.0",
50
+ "@salesforce/sfdx-agent-harness-claude": "0.48.0",
51
+ "@salesforce/sfdx-agent-harness-mastra": "0.51.0",
52
+ "@salesforce/sfdx-agent-harness-openai": "0.17.0",
53
53
  "@types/node": "^22.20.1",
54
54
  "@vitest/coverage-istanbul": "^4.1.10",
55
55
  "@vitest/eslint-plugin": "^1.6.26",
56
- "eslint": "^10.8.0",
56
+ "eslint": "^10.8.1",
57
57
  "eslint-config-prettier": "^10.1.8",
58
58
  "eslint-import-resolver-typescript": "^4.4.5",
59
59
  "eslint-plugin-import": "^2.32.0",
60
- "eslint-plugin-n": "^18.2.2",
60
+ "eslint-plugin-n": "^18.3.0",
61
61
  "globals": "^17.9.0",
62
62
  "lint-staged": "^17.3.0",
63
63
  "prettier": "^3.9.6",
64
64
  "rimraf": "^6.1.3",
65
- "tsx": "^4.23.5",
65
+ "tsx": "^4.23.12",
66
66
  "typescript": "^7.0.2",
67
- "typescript-eslint": "^8.66.0",
67
+ "typescript-eslint": "^8.67.0",
68
68
  "vitest": "^4.1.8"
69
69
  },
70
70
  "engines": {