@adcp/sdk 14.3.0 → 15.0.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.
Files changed (66) hide show
  1. package/bin/adcp.js +8 -5
  2. package/dist/lib/auth/oauth/ClientCredentialsFlow.js +4 -1
  3. package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +4 -1
  4. package/dist/lib/auth/oauth/MCPOAuthProvider.d.mts +25 -8
  5. package/dist/lib/auth/oauth/MCPOAuthProvider.d.ts +25 -8
  6. package/dist/lib/auth/oauth/MCPOAuthProvider.js +144 -19
  7. package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +157 -19
  8. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.d.mts +1 -1
  9. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.d.ts +1 -1
  10. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.js +1 -1
  11. package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +1 -1
  12. package/dist/lib/auth/oauth/client-credentials-schema.d.mts +17 -0
  13. package/dist/lib/auth/oauth/client-credentials-schema.d.ts +17 -0
  14. package/dist/lib/auth/oauth/client-credentials-schema.js +37 -0
  15. package/dist/lib/auth/oauth/client-credentials-schema.mjs +13 -0
  16. package/dist/lib/auth/oauth/diagnose.js +14 -0
  17. package/dist/lib/auth/oauth/diagnose.mjs +14 -0
  18. package/dist/lib/auth/oauth/file-storage.d.mts +4 -2
  19. package/dist/lib/auth/oauth/file-storage.d.ts +4 -2
  20. package/dist/lib/auth/oauth/file-storage.js +14 -1
  21. package/dist/lib/auth/oauth/file-storage.mjs +14 -1
  22. package/dist/lib/auth/oauth/index.d.mts +10 -6
  23. package/dist/lib/auth/oauth/index.d.ts +10 -6
  24. package/dist/lib/auth/oauth/index.js +9 -3
  25. package/dist/lib/auth/oauth/index.mjs +8 -3
  26. package/dist/lib/auth/oauth/types.d.mts +17 -0
  27. package/dist/lib/auth/oauth/types.d.ts +17 -0
  28. package/dist/lib/auth/oauth/types.js +40 -0
  29. package/dist/lib/auth/oauth/types.mjs +38 -0
  30. package/dist/lib/auth/oauth/web-flow.d.mts +2 -0
  31. package/dist/lib/auth/oauth/web-flow.d.ts +2 -0
  32. package/dist/lib/auth/oauth/web-flow.js +48 -5
  33. package/dist/lib/auth/oauth/web-flow.mjs +57 -6
  34. package/dist/lib/index.d.mts +1 -1
  35. package/dist/lib/index.d.ts +1 -1
  36. package/dist/lib/index.js +2 -0
  37. package/dist/lib/index.mjs +3 -1
  38. package/dist/lib/protocols/mcp.js +2 -0
  39. package/dist/lib/protocols/mcp.mjs +2 -0
  40. package/dist/lib/schemas/index.d.mts +2 -0
  41. package/dist/lib/schemas/index.d.ts +2 -0
  42. package/dist/lib/schemas/index.js +3 -0
  43. package/dist/lib/schemas/index.mjs +2 -0
  44. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  45. package/dist/lib/testing/client.js +8 -3
  46. package/dist/lib/testing/client.mjs +8 -3
  47. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  48. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  49. package/dist/lib/testing/types.d.mts +10 -1
  50. package/dist/lib/testing/types.d.ts +10 -1
  51. package/dist/lib/types/adcp.d.mts +6 -0
  52. package/dist/lib/types/adcp.d.ts +6 -0
  53. package/dist/lib/version.d.mts +3 -3
  54. package/dist/lib/version.d.ts +3 -3
  55. package/dist/lib/version.js +3 -3
  56. package/dist/lib/version.mjs +3 -3
  57. package/docs/TYPE-SUMMARY.md +1 -1
  58. package/docs/ZOD-SCHEMAS.md +19 -0
  59. package/docs/guides/OAUTH-ISSUER-BINDING.md +46 -0
  60. package/docs/guides/WEB-OAUTH.md +58 -163
  61. package/docs/guides/account-resolution.md +1 -1
  62. package/docs/llms.txt +1 -1
  63. package/docs/migration-14.0-to-14.1.md +1 -1
  64. package/docs/migration-14.x-rc-worksheet.md +8 -8
  65. package/docs/migration-oauth-issuer-bindings.md +17 -0
  66. package/package.json +5 -5
@@ -1,6 +1,47 @@
1
- import { DEFAULT_CLIENT_METADATA, toMCPTokens, fromMCPTokens, toMCPClientInfo, fromMCPClientInfo } from "./types.mjs";
1
+ import { fileURLToPath as __adcpFileURLToPath } from "node:url";
2
+ import { dirname as __adcpDirname } from "node:path";
3
+ import { createRequire as __adcpCreateRequire } from "node:module";
4
+ const __dirname = __adcpDirname(__adcpFileURLToPath(import.meta.url));
5
+ const require2 = __adcpCreateRequire(import.meta.url);
6
+ import {
7
+ DEFAULT_CLIENT_METADATA,
8
+ toMCPTokens,
9
+ fromMCPTokens,
10
+ toMCPClientInfo,
11
+ fromMCPClientInfo,
12
+ assertOAuthCredentialIssuer,
13
+ assertOAuthServerIssuer,
14
+ OAuthError
15
+ } from "./types.mjs";
2
16
  import { randomBytes } from "crypto";
17
+ import { readFileSync } from "node:fs";
18
+ import { dirname, resolve } from "node:path";
19
+ import { createRequire } from "node:module";
3
20
  import { validateOAuthResourceUrl } from "./resource-url.mjs";
21
+ let legacyOAuthSdkChecked = false;
22
+ function assertLegacyOAuthSdk() {
23
+ if (legacyOAuthSdkChecked) return;
24
+ try {
25
+ const clientPath = createRequire(resolve(__dirname, "MCPOAuthProvider.js")).resolve(
26
+ "@modelcontextprotocol/sdk/client/index.js"
27
+ );
28
+ const { version } = JSON.parse(readFileSync(resolve(dirname(clientPath), "../../../package.json"), "utf8"));
29
+ const match = typeof version === "string" && /^1\.(\d+)\.\d+$/.exec(version);
30
+ if (!match || Number(match[1]) < 31) {
31
+ throw new OAuthError(
32
+ "Legacy MCP OAuth requires @modelcontextprotocol/sdk >=1.31.0 within major 1. Upgrade the installed peer dependency.",
33
+ "mcp_oauth_sdk_upgrade_required"
34
+ );
35
+ }
36
+ } catch (error) {
37
+ if (error instanceof OAuthError) throw error;
38
+ throw new OAuthError(
39
+ "Cannot verify a patched legacy MCP SDK. Install @modelcontextprotocol/sdk ^1.31.0 and keep it external when bundling.",
40
+ "mcp_oauth_sdk_upgrade_required"
41
+ );
42
+ }
43
+ legacyOAuthSdkChecked = true;
44
+ }
4
45
  class MCPOAuthProvider {
5
46
  agent;
6
47
  storage;
@@ -8,6 +49,8 @@ class MCPOAuthProvider {
8
49
  _clientMetadata;
9
50
  allowHttp;
10
51
  configuredResourceOverride;
52
+ allowInteractiveAuthorization;
53
+ pendingDiscoveryState;
11
54
  constructor(config) {
12
55
  this.agent = config.agent;
13
56
  this.storage = config.storage;
@@ -15,6 +58,7 @@ class MCPOAuthProvider {
15
58
  this._clientMetadata = config.clientMetadata;
16
59
  this.allowHttp = config.allowHttp === true;
17
60
  this.configuredResourceOverride = config.resourceOverride;
61
+ this.allowInteractiveAuthorization = config.allowInteractiveAuthorization === true;
18
62
  }
19
63
  /**
20
64
  * Create a provider for CLI usage
@@ -31,7 +75,8 @@ class MCPOAuthProvider {
31
75
  storage,
32
76
  clientMetadata,
33
77
  allowHttp: options?.allowHttp,
34
- resourceOverride: options?.resourceOverride
78
+ resourceOverride: options?.resourceOverride,
79
+ allowInteractiveAuthorization: true
35
80
  });
36
81
  }
37
82
  // ========================================
@@ -72,8 +117,14 @@ class MCPOAuthProvider {
72
117
  /**
73
118
  * Load client information from agent config
74
119
  */
75
- async clientInformation() {
120
+ async clientInformation(ctx) {
121
+ const issuer = this.checkIssuerContext(ctx);
122
+ if (issuer && !this.agent.oauth_client && !this.allowInteractiveAuthorization) {
123
+ throw this.ownerReauthorizationRequired();
124
+ }
76
125
  if (this.agent.oauth_client) {
126
+ if (this.agent.oauth_client.client_secret) assertLegacyOAuthSdk();
127
+ assertOAuthCredentialIssuer(this.agent.oauth_client, void 0, !!this.agent.oauth_client.client_secret);
77
128
  return toMCPClientInfo(this.agent.oauth_client);
78
129
  }
79
130
  return void 0;
@@ -83,13 +134,16 @@ class MCPOAuthProvider {
83
134
  */
84
135
  async saveClientInformation(clientInfo) {
85
136
  this.agent.oauth_client = fromMCPClientInfo(clientInfo);
86
- await this.persistAgent();
137
+ await this.persistCredentials();
87
138
  }
88
139
  /**
89
140
  * Load existing tokens from agent config
90
141
  */
91
- async tokens() {
142
+ async tokens(ctx) {
143
+ this.checkIssuerContext(ctx);
92
144
  if (this.agent.oauth_tokens) {
145
+ if (this.agent.oauth_tokens.refresh_token) assertLegacyOAuthSdk();
146
+ assertOAuthCredentialIssuer(this.agent.oauth_tokens, void 0, !!this.agent.oauth_tokens.refresh_token);
93
147
  return toMCPTokens(this.agent.oauth_tokens);
94
148
  }
95
149
  return void 0;
@@ -100,19 +154,72 @@ class MCPOAuthProvider {
100
154
  */
101
155
  async saveTokens(tokens) {
102
156
  this.agent.oauth_tokens = fromMCPTokens(tokens);
103
- delete this.agent.oauth_code_verifier;
104
- await this.persistAgent();
157
+ if (this.allowInteractiveAuthorization) {
158
+ this.agent.oauth_code_verifier = void 0;
159
+ this.agent.oauth_discovery_state = void 0;
160
+ }
161
+ this.pendingDiscoveryState = void 0;
162
+ await this.persistCredentials();
105
163
  }
106
164
  /**
107
165
  * Redirect user to authorization URL
108
166
  */
109
167
  async redirectToAuthorization(authorizationUrl) {
110
- await this.flowHandler.redirectToAuthorization(authorizationUrl);
168
+ if (!this.allowInteractiveAuthorization) throw this.ownerReauthorizationRequired("interactive_required");
169
+ try {
170
+ await this.flowHandler.redirectToAuthorization(authorizationUrl);
171
+ } catch (error) {
172
+ await this.invalidateCredentials("verifier");
173
+ throw error;
174
+ }
175
+ }
176
+ /** Persist the authorization-server identity across the callback leg. */
177
+ async saveDiscoveryState(state) {
178
+ this.validateDiscoveryState(state);
179
+ if (!this.agent.oauth_client && !this.allowInteractiveAuthorization) throw this.ownerReauthorizationRequired();
180
+ this.pendingDiscoveryState = structuredClone(state);
181
+ if (this.allowInteractiveAuthorization) {
182
+ this.agent.oauth_discovery_state = structuredClone(state);
183
+ await this.persistAgent();
184
+ }
185
+ }
186
+ async discoveryState() {
187
+ if (!this.allowInteractiveAuthorization) return void 0;
188
+ if (!this.agent.oauth_code_verifier) return void 0;
189
+ const state = this.pendingDiscoveryState ?? this.agent.oauth_discovery_state;
190
+ if (!state) return void 0;
191
+ this.validateDiscoveryState(state);
192
+ this.pendingDiscoveryState = structuredClone(state);
193
+ return structuredClone(state);
194
+ }
195
+ validateDiscoveryState(state) {
196
+ assertOAuthServerIssuer(state.authorizationServerMetadata, String(state.authorizationServerUrl));
197
+ this.checkIssuerContext({ issuer: String(state.authorizationServerUrl) });
198
+ }
199
+ checkIssuerContext(ctx) {
200
+ const issuer = ctx?.issuer ?? this.pendingDiscoveryState?.authorizationServerUrl;
201
+ if (issuer !== void 0) {
202
+ if (this.agent.oauth_tokens) {
203
+ assertOAuthCredentialIssuer(this.agent.oauth_tokens, issuer, !!this.agent.oauth_tokens.refresh_token);
204
+ }
205
+ if (this.agent.oauth_client) {
206
+ assertOAuthCredentialIssuer(this.agent.oauth_client, issuer, !!this.agent.oauth_client.client_secret);
207
+ }
208
+ }
209
+ return issuer;
210
+ }
211
+ ownerReauthorizationRequired(code = "owner_reauthorization_required") {
212
+ return new OAuthError(
213
+ "OAuth owner reauthorization is required; this provider will not automatically clear credentials or register a new client. CLI: adcp <alias> --clear-oauth, then adcp <alias> --oauth.",
214
+ code,
215
+ this.agent.id
216
+ );
111
217
  }
112
218
  /**
113
219
  * Save PKCE code verifier
114
220
  */
115
221
  async saveCodeVerifier(codeVerifier) {
222
+ if (!this.allowInteractiveAuthorization) throw this.ownerReauthorizationRequired();
116
223
  this.agent.oauth_code_verifier = codeVerifier;
117
224
  await this.persistAgent();
118
225
  }
@@ -122,6 +229,7 @@ class MCPOAuthProvider {
122
229
  * retrieves it here during token exchange.
123
230
  */
124
231
  async codeVerifier() {
232
+ if (!this.allowInteractiveAuthorization) throw this.ownerReauthorizationRequired();
125
233
  if (!this.agent.oauth_code_verifier) {
126
234
  throw new Error(
127
235
  "No PKCE code verifier found. The OAuth flow may have been interrupted or the agent config was modified. Please try authenticating again."
@@ -133,20 +241,40 @@ class MCPOAuthProvider {
133
241
  * Invalidate credentials when server indicates they're invalid
134
242
  */
135
243
  async invalidateCredentials(scope) {
244
+ if (!this.allowInteractiveAuthorization && (scope === "discovery" || scope === "verifier")) {
245
+ this.pendingDiscoveryState = void 0;
246
+ return;
247
+ }
248
+ if (!this.allowInteractiveAuthorization && (scope === "all" || scope === "client" || scope === "tokens")) {
249
+ throw this.ownerReauthorizationRequired();
250
+ }
251
+ await this.clearCredentials(scope);
252
+ }
253
+ async clearCredentials(scope) {
136
254
  switch (scope) {
137
255
  case "all":
138
- delete this.agent.oauth_tokens;
139
- delete this.agent.oauth_client;
140
- delete this.agent.oauth_code_verifier;
256
+ this.pendingDiscoveryState = void 0;
257
+ this.agent.oauth_discovery_state = void 0;
258
+ this.agent.oauth_tokens = void 0;
259
+ this.agent.oauth_client = void 0;
260
+ this.agent.oauth_code_verifier = void 0;
141
261
  break;
142
262
  case "tokens":
143
- delete this.agent.oauth_tokens;
263
+ this.agent.oauth_tokens = void 0;
144
264
  break;
145
265
  case "client":
146
- delete this.agent.oauth_client;
266
+ this.pendingDiscoveryState = void 0;
267
+ this.agent.oauth_discovery_state = void 0;
268
+ this.agent.oauth_client = void 0;
269
+ break;
270
+ case "discovery":
271
+ this.pendingDiscoveryState = void 0;
272
+ this.agent.oauth_discovery_state = void 0;
147
273
  break;
148
274
  case "verifier":
149
- delete this.agent.oauth_code_verifier;
275
+ this.pendingDiscoveryState = void 0;
276
+ this.agent.oauth_discovery_state = void 0;
277
+ this.agent.oauth_code_verifier = void 0;
150
278
  break;
151
279
  }
152
280
  await this.persistAgent();
@@ -155,11 +283,20 @@ class MCPOAuthProvider {
155
283
  // Additional methods
156
284
  // ========================================
157
285
  /**
158
- * Persist agent config to storage if configured
286
+ * Persist a credential update without modifying independent browser state.
159
287
  */
160
- async persistAgent() {
288
+ async persistCredentials() {
289
+ let savedAgent = this.agent;
290
+ if (!this.allowInteractiveAuthorization) {
291
+ savedAgent = { ...this.agent };
292
+ delete savedAgent.oauth_code_verifier;
293
+ delete savedAgent.oauth_discovery_state;
294
+ }
295
+ await this.persistAgent(savedAgent);
296
+ }
297
+ async persistAgent(agent = this.agent) {
161
298
  if (this.storage) {
162
- await this.storage.saveAgent(this.agent);
299
+ await this.storage.saveAgent(agent);
163
300
  }
164
301
  }
165
302
  /**
@@ -201,7 +338,7 @@ class MCPOAuthProvider {
201
338
  * Clear all OAuth data for this agent
202
339
  */
203
340
  async clearAuth() {
204
- await this.invalidateCredentials("all");
341
+ await this.clearCredentials("all");
205
342
  }
206
343
  /**
207
344
  * Get the agent config this provider manages
@@ -219,5 +356,6 @@ class MCPOAuthProvider {
219
356
  }
220
357
  }
221
358
  export {
222
- MCPOAuthProvider
359
+ MCPOAuthProvider,
360
+ assertLegacyOAuthSdk
223
361
  };
@@ -21,7 +21,7 @@ export interface NonInteractiveFlowHandlerConfig {
21
21
  redirectUrl?: string | URL;
22
22
  /**
23
23
  * Optional hint shown in the error message when a browser flow is
24
- * attempted — e.g., the alias name to pass to `adcp --save-auth`.
24
+ * attempted — e.g., the saved alias for `adcp <alias> --oauth`.
25
25
  */
26
26
  agentHint?: string;
27
27
  }
@@ -21,7 +21,7 @@ export interface NonInteractiveFlowHandlerConfig {
21
21
  redirectUrl?: string | URL;
22
22
  /**
23
23
  * Optional hint shown in the error message when a browser flow is
24
- * attempted — e.g., the alias name to pass to `adcp --save-auth`.
24
+ * attempted — e.g., the saved alias for `adcp <alias> --oauth`.
25
25
  */
26
26
  agentHint?: string;
27
27
  }
@@ -33,7 +33,7 @@ class NonInteractiveFlowHandler {
33
33
  return this._redirectUrl;
34
34
  }
35
35
  async redirectToAuthorization() {
36
- const target = this.agentHint ? `adcp --save-auth ${this.agentHint} --oauth` : "adcp --save-auth <alias> <url> --oauth";
36
+ const target = this.agentHint ? `adcp ${this.agentHint} --oauth` : "adcp --save-auth <alias> <url> --oauth";
37
37
  throw new import_types.OAuthError(
38
38
  `OAuth authorization required but this context is non-interactive. Run \`${target}\` from a terminal with a browser, then retry.`,
39
39
  "interactive_required"
@@ -10,7 +10,7 @@ class NonInteractiveFlowHandler {
10
10
  return this._redirectUrl;
11
11
  }
12
12
  async redirectToAuthorization() {
13
- const target = this.agentHint ? `adcp --save-auth ${this.agentHint} --oauth` : "adcp --save-auth <alias> <url> --oauth";
13
+ const target = this.agentHint ? `adcp ${this.agentHint} --oauth` : "adcp --save-auth <alias> <url> --oauth";
14
14
  throw new OAuthError(
15
15
  `OAuth authorization required but this context is non-interactive. Run \`${target}\` from a terminal with a browser, then retry.`,
16
16
  "interactive_required"
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * OAuth client credentials configuration shape. Secret references remain
4
+ * unresolved; endpoint transport and SSRF checks run during token exchange.
5
+ */
6
+ export declare const AgentOAuthClientCredentialsSchema: z.ZodObject<{
7
+ token_endpoint: z.ZodString;
8
+ client_id: z.ZodString;
9
+ client_secret: z.ZodString;
10
+ scope: z.ZodOptional<z.ZodString>;
11
+ resource: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
12
+ audience: z.ZodOptional<z.ZodString>;
13
+ auth_method: z.ZodOptional<z.ZodEnum<{
14
+ basic: "basic";
15
+ body: "body";
16
+ }>>;
17
+ }, z.core.$strip>;
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * OAuth client credentials configuration shape. Secret references remain
4
+ * unresolved; endpoint transport and SSRF checks run during token exchange.
5
+ */
6
+ export declare const AgentOAuthClientCredentialsSchema: z.ZodObject<{
7
+ token_endpoint: z.ZodString;
8
+ client_id: z.ZodString;
9
+ client_secret: z.ZodString;
10
+ scope: z.ZodOptional<z.ZodString>;
11
+ resource: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
12
+ audience: z.ZodOptional<z.ZodString>;
13
+ auth_method: z.ZodOptional<z.ZodEnum<{
14
+ basic: "basic";
15
+ body: "body";
16
+ }>>;
17
+ }, z.core.$strip>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var client_credentials_schema_exports = {};
20
+ __export(client_credentials_schema_exports, {
21
+ AgentOAuthClientCredentialsSchema: () => AgentOAuthClientCredentialsSchema
22
+ });
23
+ module.exports = __toCommonJS(client_credentials_schema_exports);
24
+ var import_zod = require("zod");
25
+ const AgentOAuthClientCredentialsSchema = import_zod.z.object({
26
+ token_endpoint: import_zod.z.string().url(),
27
+ client_id: import_zod.z.string().min(1),
28
+ client_secret: import_zod.z.string().min(1),
29
+ scope: import_zod.z.string().optional(),
30
+ resource: import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.string())]).optional(),
31
+ audience: import_zod.z.string().optional(),
32
+ auth_method: import_zod.z.enum(["basic", "body"]).optional()
33
+ });
34
+ // Annotate the CommonJS export names for ESM import in node:
35
+ 0 && (module.exports = {
36
+ AgentOAuthClientCredentialsSchema
37
+ });
@@ -0,0 +1,13 @@
1
+ import { z } from "zod";
2
+ const AgentOAuthClientCredentialsSchema = z.object({
3
+ token_endpoint: z.string().url(),
4
+ client_id: z.string().min(1),
5
+ client_secret: z.string().min(1),
6
+ scope: z.string().optional(),
7
+ resource: z.union([z.string(), z.array(z.string())]).optional(),
8
+ audience: z.string().optional(),
9
+ auth_method: z.enum(["basic", "body"]).optional()
10
+ });
11
+ export {
12
+ AgentOAuthClientCredentialsSchema
13
+ };
@@ -23,6 +23,7 @@ __export(diagnose_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(diagnose_exports);
25
25
  var import_net = require('../../net/index.js');
26
+ var import_types = require('./types.js');
26
27
  var import_diagnostics = require('./diagnostics.js');
27
28
  const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1;
28
29
  async function runAuthDiagnosis(agent, options = {}) {
@@ -48,6 +49,17 @@ async function runAuthDiagnosis(agent, options = {}) {
48
49
  if (!options.skipRefresh && agent.oauth_tokens?.refresh_token) {
49
50
  const tokenEndpoint = extractTokenEndpoint(asCapture);
50
51
  const clientId = agent.oauth_client?.client_id;
52
+ let issuerError;
53
+ if (tokenEndpoint && clientId) {
54
+ try {
55
+ const issuer = new URL(extractIssuer(prmCapture) ?? "").toString();
56
+ (0, import_types.assertOAuthServerIssuer)(asCapture.body, issuer);
57
+ (0, import_types.assertOAuthCredentialIssuer)(agent.oauth_tokens, issuer, true);
58
+ (0, import_types.assertOAuthCredentialIssuer)(agent.oauth_client, issuer, !!agent.oauth_client?.client_secret);
59
+ } catch (error) {
60
+ issuerError = error instanceof import_types.OAuthError ? error.message : "Authorization-server issuer could not be validated";
61
+ }
62
+ }
51
63
  const resource = agentUrl;
52
64
  if (!tokenEndpoint) {
53
65
  steps.push({
@@ -59,6 +71,8 @@ async function runAuthDiagnosis(agent, options = {}) {
59
71
  name: "token_refresh_attempt",
60
72
  error: "No saved oauth_client.client_id \u2014 cannot attempt refresh without it"
61
73
  });
74
+ } else if (issuerError) {
75
+ steps.push({ name: "token_refresh_attempt", error: issuerError });
62
76
  } else {
63
77
  const refreshCapture = await attemptTokenRefresh({
64
78
  tokenEndpoint,
@@ -1,4 +1,5 @@
1
1
  import { ssrfSafeFetch, decodeBodyAsJsonOrText, SsrfRefusedError } from "../../net/index.mjs";
2
+ import { assertOAuthCredentialIssuer, assertOAuthServerIssuer, OAuthError } from "./types.mjs";
2
3
  import { decodeAccessTokenClaims, parseWWWAuthenticate, validateTokenAudience } from "./diagnostics.mjs";
3
4
  const AUTH_DIAGNOSIS_SCHEMA_VERSION = 1;
4
5
  async function runAuthDiagnosis(agent, options = {}) {
@@ -24,6 +25,17 @@ async function runAuthDiagnosis(agent, options = {}) {
24
25
  if (!options.skipRefresh && agent.oauth_tokens?.refresh_token) {
25
26
  const tokenEndpoint = extractTokenEndpoint(asCapture);
26
27
  const clientId = agent.oauth_client?.client_id;
28
+ let issuerError;
29
+ if (tokenEndpoint && clientId) {
30
+ try {
31
+ const issuer = new URL(extractIssuer(prmCapture) ?? "").toString();
32
+ assertOAuthServerIssuer(asCapture.body, issuer);
33
+ assertOAuthCredentialIssuer(agent.oauth_tokens, issuer, true);
34
+ assertOAuthCredentialIssuer(agent.oauth_client, issuer, !!agent.oauth_client?.client_secret);
35
+ } catch (error) {
36
+ issuerError = error instanceof OAuthError ? error.message : "Authorization-server issuer could not be validated";
37
+ }
38
+ }
27
39
  const resource = agentUrl;
28
40
  if (!tokenEndpoint) {
29
41
  steps.push({
@@ -35,6 +47,8 @@ async function runAuthDiagnosis(agent, options = {}) {
35
47
  name: "token_refresh_attempt",
36
48
  error: "No saved oauth_client.client_id \u2014 cannot attempt refresh without it"
37
49
  });
50
+ } else if (issuerError) {
51
+ steps.push({ name: "token_refresh_attempt", error: issuerError });
38
52
  } else {
39
53
  const refreshCapture = await attemptTokenRefresh({
40
54
  tokenEndpoint,
@@ -28,8 +28,10 @@ export interface FileOAuthStorageOptions {
28
28
  *
29
29
  * Does NOT touch fields outside the OAuth envelope (`auth_token`, custom
30
30
  * fields) when saving — the MCP provider only mutates `oauth_tokens`,
31
- * `oauth_client`, `oauth_resource`, and `oauth_code_verifier`, and we preserve
32
- * everything else.
31
+ * `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
32
+ * An own property explicitly set to undefined clears tokens, client, verifier
33
+ * or discovery state. Omitted properties preserve stored values for partial
34
+ * callers. Other fields keep their existing merge semantics.
33
35
  *
34
36
  * @example
35
37
  * ```ts
@@ -28,8 +28,10 @@ export interface FileOAuthStorageOptions {
28
28
  *
29
29
  * Does NOT touch fields outside the OAuth envelope (`auth_token`, custom
30
30
  * fields) when saving — the MCP provider only mutates `oauth_tokens`,
31
- * `oauth_client`, `oauth_resource`, and `oauth_code_verifier`, and we preserve
32
- * everything else.
31
+ * `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
32
+ * An own property explicitly set to undefined clears tokens, client, verifier
33
+ * or discovery state. Omitted properties preserve stored values for partial
34
+ * callers. Other fields keep their existing merge semantics.
33
35
  *
34
36
  * @example
35
37
  * ```ts
@@ -75,7 +75,8 @@ function createFileOAuthStorage(options) {
75
75
  oauth_client: stored.oauth_client,
76
76
  oauth_resource: stored.oauth_resource,
77
77
  oauth_client_credentials: stored.oauth_client_credentials,
78
- oauth_code_verifier: stored.oauth_code_verifier
78
+ oauth_code_verifier: stored.oauth_code_verifier,
79
+ oauth_discovery_state: stored.oauth_discovery_state
79
80
  };
80
81
  },
81
82
  async saveAgent(agent) {
@@ -93,6 +94,18 @@ function createFileOAuthStorage(options) {
93
94
  ...agent.oauth_client_credentials !== void 0 ? { oauth_client_credentials: agent.oauth_client_credentials } : {},
94
95
  ...agent.oauth_code_verifier !== void 0 ? { oauth_code_verifier: agent.oauth_code_verifier } : {}
95
96
  };
97
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_tokens") && agent.oauth_tokens === void 0) {
98
+ delete next.oauth_tokens;
99
+ }
100
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_client") && agent.oauth_client === void 0) {
101
+ delete next.oauth_client;
102
+ }
103
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_code_verifier") && agent.oauth_code_verifier === void 0) {
104
+ delete next.oauth_code_verifier;
105
+ }
106
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_discovery_state") && agent.oauth_discovery_state === void 0) {
107
+ delete next.oauth_discovery_state;
108
+ } else if (agent.oauth_discovery_state !== void 0) next.oauth_discovery_state = agent.oauth_discovery_state;
96
109
  if (agent.oauth_resource === void 0) delete next.oauth_resource;
97
110
  config.agents[key] = next;
98
111
  await writeConfig(config);
@@ -42,7 +42,8 @@ function createFileOAuthStorage(options) {
42
42
  oauth_client: stored.oauth_client,
43
43
  oauth_resource: stored.oauth_resource,
44
44
  oauth_client_credentials: stored.oauth_client_credentials,
45
- oauth_code_verifier: stored.oauth_code_verifier
45
+ oauth_code_verifier: stored.oauth_code_verifier,
46
+ oauth_discovery_state: stored.oauth_discovery_state
46
47
  };
47
48
  },
48
49
  async saveAgent(agent) {
@@ -60,6 +61,18 @@ function createFileOAuthStorage(options) {
60
61
  ...agent.oauth_client_credentials !== void 0 ? { oauth_client_credentials: agent.oauth_client_credentials } : {},
61
62
  ...agent.oauth_code_verifier !== void 0 ? { oauth_code_verifier: agent.oauth_code_verifier } : {}
62
63
  };
64
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_tokens") && agent.oauth_tokens === void 0) {
65
+ delete next.oauth_tokens;
66
+ }
67
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_client") && agent.oauth_client === void 0) {
68
+ delete next.oauth_client;
69
+ }
70
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_code_verifier") && agent.oauth_code_verifier === void 0) {
71
+ delete next.oauth_code_verifier;
72
+ }
73
+ if (Object.prototype.hasOwnProperty.call(agent, "oauth_discovery_state") && agent.oauth_discovery_state === void 0) {
74
+ delete next.oauth_discovery_state;
75
+ } else if (agent.oauth_discovery_state !== void 0) next.oauth_discovery_state = agent.oauth_discovery_state;
63
76
  if (agent.oauth_resource === void 0) delete next.oauth_resource;
64
77
  config.agents[key] = next;
65
78
  await writeConfig(config);
@@ -5,7 +5,8 @@
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
- * import { MCPOAuthProvider, CLIFlowHandler } from '@adcp/sdk';
8
+ * import { MCPOAuthProvider, CLIFlowHandler, DEFAULT_CLIENT_METADATA } from '@adcp/sdk/auth';
9
+ * import type { AgentConfig } from '@adcp/sdk';
9
10
  *
10
11
  * // Agent config - tokens will be stored here
11
12
  * const agent: AgentConfig = {
@@ -13,14 +14,15 @@
13
14
  * name: 'My Agent',
14
15
  * agent_uri: 'https://agent.example.com/mcp',
15
16
  * protocol: 'mcp',
16
- * // After OAuth flow completes:
17
- * // oauth_tokens: { access_token: '...', refresh_token: '...' }
17
+ * // A fresh interactive flow saves issuer-bound tokens here.
18
18
  * };
19
19
  *
20
20
  * // Create provider with CLI flow handler
21
21
  * const provider = new MCPOAuthProvider({
22
+ * allowInteractiveAuthorization: true,
22
23
  * agent,
23
24
  * flowHandler: new CLIFlowHandler(),
25
+ * clientMetadata: DEFAULT_CLIENT_METADATA,
24
26
  * storage: myConfigStorage // Optional: persists tokens to file/db
25
27
  * });
26
28
  *
@@ -99,9 +101,10 @@ export declare function createCLIOAuthProvider(agent: AgentConfig, options?: {
99
101
  * runs, scheduled jobs, and other non-interactive contexts where you've
100
102
  * already saved tokens via `createCLIOAuthProvider`.
101
103
  *
102
- * If refresh fails (e.g., the refresh_token is revoked or expired), the
103
- * MCP SDK will throw `UnauthorizedError` at call time — the caller should
104
- * treat that as a signal to run `adcp --save-auth <alias> --oauth` and retry.
104
+ * A failed grant can throw `OAuthError` with `owner_reauthorization_required`;
105
+ * missing or mismatched issuer bindings throw `oauth_issuer_required` or `oauth_issuer_mismatch`.
106
+ * The owner can run `adcp <alias> --clear-oauth`, then
107
+ * `adcp <alias> --oauth` to start a fresh browser sign-in.
105
108
  */
106
109
  export declare function createNonInteractiveOAuthProvider(agent: AgentConfig, options?: {
107
110
  /** Hint shown in error messages if an interactive flow is attempted. */
@@ -143,5 +146,6 @@ export { createFileOAuthStorage, type FileOAuthStorageOptions } from './file-sto
143
146
  export { exchangeClientCredentials, ensureClientCredentialsTokens, ClientCredentialsExchangeError, type ExchangeClientCredentialsOptions, type EnsureClientCredentialsOptions, } from './ClientCredentialsFlow.mjs';
144
147
  export { resolveSecret, isEnvSecretReference, toEnvSecretReference, extractEnvSecretName, MissingEnvSecretError, } from './secret-resolver.mjs';
145
148
  export type { AgentOAuthClientCredentials } from '../../types/adcp.mjs';
149
+ export { AgentOAuthClientCredentialsSchema } from './client-credentials-schema.mjs';
146
150
  export { bindAgentStorage, getAgentStorage, unbindAgentStorage } from './storage-registry.mjs';
147
151
  export { InvalidTokenError, InsufficientScopeError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
@@ -5,7 +5,8 @@
5
5
  *
6
6
  * @example
7
7
  * ```typescript
8
- * import { MCPOAuthProvider, CLIFlowHandler } from '@adcp/sdk';
8
+ * import { MCPOAuthProvider, CLIFlowHandler, DEFAULT_CLIENT_METADATA } from '@adcp/sdk/auth';
9
+ * import type { AgentConfig } from '@adcp/sdk';
9
10
  *
10
11
  * // Agent config - tokens will be stored here
11
12
  * const agent: AgentConfig = {
@@ -13,14 +14,15 @@
13
14
  * name: 'My Agent',
14
15
  * agent_uri: 'https://agent.example.com/mcp',
15
16
  * protocol: 'mcp',
16
- * // After OAuth flow completes:
17
- * // oauth_tokens: { access_token: '...', refresh_token: '...' }
17
+ * // A fresh interactive flow saves issuer-bound tokens here.
18
18
  * };
19
19
  *
20
20
  * // Create provider with CLI flow handler
21
21
  * const provider = new MCPOAuthProvider({
22
+ * allowInteractiveAuthorization: true,
22
23
  * agent,
23
24
  * flowHandler: new CLIFlowHandler(),
25
+ * clientMetadata: DEFAULT_CLIENT_METADATA,
24
26
  * storage: myConfigStorage // Optional: persists tokens to file/db
25
27
  * });
26
28
  *
@@ -99,9 +101,10 @@ export declare function createCLIOAuthProvider(agent: AgentConfig, options?: {
99
101
  * runs, scheduled jobs, and other non-interactive contexts where you've
100
102
  * already saved tokens via `createCLIOAuthProvider`.
101
103
  *
102
- * If refresh fails (e.g., the refresh_token is revoked or expired), the
103
- * MCP SDK will throw `UnauthorizedError` at call time — the caller should
104
- * treat that as a signal to run `adcp --save-auth <alias> --oauth` and retry.
104
+ * A failed grant can throw `OAuthError` with `owner_reauthorization_required`;
105
+ * missing or mismatched issuer bindings throw `oauth_issuer_required` or `oauth_issuer_mismatch`.
106
+ * The owner can run `adcp <alias> --clear-oauth`, then
107
+ * `adcp <alias> --oauth` to start a fresh browser sign-in.
105
108
  */
106
109
  export declare function createNonInteractiveOAuthProvider(agent: AgentConfig, options?: {
107
110
  /** Hint shown in error messages if an interactive flow is attempted. */
@@ -143,5 +146,6 @@ export { createFileOAuthStorage, type FileOAuthStorageOptions } from './file-sto
143
146
  export { exchangeClientCredentials, ensureClientCredentialsTokens, ClientCredentialsExchangeError, type ExchangeClientCredentialsOptions, type EnsureClientCredentialsOptions, } from './ClientCredentialsFlow';
144
147
  export { resolveSecret, isEnvSecretReference, toEnvSecretReference, extractEnvSecretName, MissingEnvSecretError, } from './secret-resolver';
145
148
  export type { AgentOAuthClientCredentials } from '../../types/adcp';
149
+ export { AgentOAuthClientCredentialsSchema } from './client-credentials-schema';
146
150
  export { bindAgentStorage, getAgentStorage, unbindAgentStorage } from './storage-registry';
147
151
  export { InvalidTokenError, InsufficientScopeError } from '@modelcontextprotocol/sdk/server/auth/errors.js';