@adcp/sdk 14.3.0 → 14.4.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/bin/adcp.js +2 -4
- package/dist/lib/auth/oauth/MCPOAuthProvider.d.mts +6 -2
- package/dist/lib/auth/oauth/MCPOAuthProvider.d.ts +6 -2
- package/dist/lib/auth/oauth/MCPOAuthProvider.js +63 -3
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +75 -3
- package/dist/lib/auth/oauth/diagnose.js +14 -0
- package/dist/lib/auth/oauth/diagnose.mjs +14 -0
- package/dist/lib/auth/oauth/file-storage.d.mts +4 -2
- package/dist/lib/auth/oauth/file-storage.d.ts +4 -2
- package/dist/lib/auth/oauth/file-storage.js +7 -1
- package/dist/lib/auth/oauth/file-storage.mjs +7 -1
- package/dist/lib/auth/oauth/types.d.mts +10 -0
- package/dist/lib/auth/oauth/types.d.ts +10 -0
- package/dist/lib/auth/oauth/types.js +39 -0
- package/dist/lib/auth/oauth/types.mjs +37 -0
- package/dist/lib/auth/oauth/web-flow.js +11 -1
- package/dist/lib/auth/oauth/web-flow.mjs +20 -2
- package/dist/lib/protocols/mcp.js +2 -0
- package/dist/lib/protocols/mcp.mjs +2 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/adcp.d.mts +6 -0
- package/dist/lib/types/adcp.d.ts +6 -0
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +1 -1
- package/docs/llms.txt +1 -1
- package/docs/migration-14.x-rc-worksheet.md +5 -5
- package/docs/migration-oauth-issuer-bindings.md +82 -0
- package/package.json +5 -5
package/bin/adcp.js
CHANGED
|
@@ -1386,9 +1386,7 @@ async function ensureOAuthTokensForAlias(alias, url, { quiet = false, allowHttp
|
|
|
1386
1386
|
return existing;
|
|
1387
1387
|
}
|
|
1388
1388
|
|
|
1389
|
-
//
|
|
1390
|
-
// flow the verifier lives on the saved record, and without it the MCP SDK
|
|
1391
|
-
// throws "No PKCE code verifier found" when the AS redirects back.
|
|
1389
|
+
// A fresh login starts a new PKCE leg and rediscovers the authorization server.
|
|
1392
1390
|
const tempAgent = {
|
|
1393
1391
|
id: alias,
|
|
1394
1392
|
name: alias,
|
|
@@ -1396,7 +1394,6 @@ async function ensureOAuthTokensForAlias(alias, url, { quiet = false, allowHttp
|
|
|
1396
1394
|
protocol: 'mcp',
|
|
1397
1395
|
...(existing?.oauth_client && { oauth_client: existing.oauth_client }),
|
|
1398
1396
|
...(existing?.oauth_tokens && { oauth_tokens: existing.oauth_tokens }),
|
|
1399
|
-
...(existing?.oauth_code_verifier && { oauth_code_verifier: existing.oauth_code_verifier }),
|
|
1400
1397
|
};
|
|
1401
1398
|
|
|
1402
1399
|
const { Client: MCPClient } = require('@modelcontextprotocol/sdk/client/index.js');
|
|
@@ -6671,6 +6668,7 @@ credential material — never sync or commit.
|
|
|
6671
6668
|
delete agentConfig.oauth_tokens;
|
|
6672
6669
|
delete agentConfig.oauth_client;
|
|
6673
6670
|
delete agentConfig.oauth_code_verifier;
|
|
6671
|
+
delete agentConfig.oauth_discovery_state;
|
|
6674
6672
|
saveAgent(alias, agentConfig);
|
|
6675
6673
|
|
|
6676
6674
|
console.log(`\n✅ Cleared OAuth tokens for '${alias}'`);
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Implements the MCP SDK's OAuthClientProvider interface
|
|
5
5
|
* using AgentConfig for token storage.
|
|
6
6
|
*/
|
|
7
|
-
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
7
|
+
import type { OAuthClientProvider, OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
8
8
|
import type { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens, OAuthFlowHandler, OAuthProviderConfig, OAuthConfigStorage, AgentConfig } from './types.mjs';
|
|
9
|
+
export declare function assertLegacyOAuthSdk(): void;
|
|
9
10
|
/**
|
|
10
11
|
* MCP OAuth Client Provider
|
|
11
12
|
*
|
|
@@ -90,6 +91,9 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
90
91
|
* Redirect user to authorization URL
|
|
91
92
|
*/
|
|
92
93
|
redirectToAuthorization(authorizationUrl: URL): Promise<void>;
|
|
94
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
95
|
+
saveDiscoveryState(state: OAuthDiscoveryState): Promise<void>;
|
|
96
|
+
discoveryState(): Promise<OAuthDiscoveryState | undefined>;
|
|
93
97
|
/**
|
|
94
98
|
* Save PKCE code verifier
|
|
95
99
|
*/
|
|
@@ -103,7 +107,7 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
103
107
|
/**
|
|
104
108
|
* Invalidate credentials when server indicates they're invalid
|
|
105
109
|
*/
|
|
106
|
-
invalidateCredentials(scope: '
|
|
110
|
+
invalidateCredentials(scope: Parameters<NonNullable<OAuthClientProvider['invalidateCredentials']>>[0]): Promise<void>;
|
|
107
111
|
/**
|
|
108
112
|
* Persist agent config to storage if configured
|
|
109
113
|
*/
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Implements the MCP SDK's OAuthClientProvider interface
|
|
5
5
|
* using AgentConfig for token storage.
|
|
6
6
|
*/
|
|
7
|
-
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
7
|
+
import type { OAuthClientProvider, OAuthDiscoveryState } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
8
8
|
import type { OAuthClientMetadata, OAuthClientInformation, OAuthClientInformationFull, OAuthTokens, OAuthFlowHandler, OAuthProviderConfig, OAuthConfigStorage, AgentConfig } from './types';
|
|
9
|
+
export declare function assertLegacyOAuthSdk(): void;
|
|
9
10
|
/**
|
|
10
11
|
* MCP OAuth Client Provider
|
|
11
12
|
*
|
|
@@ -90,6 +91,9 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
90
91
|
* Redirect user to authorization URL
|
|
91
92
|
*/
|
|
92
93
|
redirectToAuthorization(authorizationUrl: URL): Promise<void>;
|
|
94
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
95
|
+
saveDiscoveryState(state: OAuthDiscoveryState): Promise<void>;
|
|
96
|
+
discoveryState(): Promise<OAuthDiscoveryState | undefined>;
|
|
93
97
|
/**
|
|
94
98
|
* Save PKCE code verifier
|
|
95
99
|
*/
|
|
@@ -103,7 +107,7 @@ export declare class MCPOAuthProvider implements OAuthClientProvider {
|
|
|
103
107
|
/**
|
|
104
108
|
* Invalidate credentials when server indicates they're invalid
|
|
105
109
|
*/
|
|
106
|
-
invalidateCredentials(scope: '
|
|
110
|
+
invalidateCredentials(scope: Parameters<NonNullable<OAuthClientProvider['invalidateCredentials']>>[0]): Promise<void>;
|
|
107
111
|
/**
|
|
108
112
|
* Persist agent config to storage if configured
|
|
109
113
|
*/
|
|
@@ -18,12 +18,40 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var MCPOAuthProvider_exports = {};
|
|
20
20
|
__export(MCPOAuthProvider_exports, {
|
|
21
|
-
MCPOAuthProvider: () => MCPOAuthProvider
|
|
21
|
+
MCPOAuthProvider: () => MCPOAuthProvider,
|
|
22
|
+
assertLegacyOAuthSdk: () => assertLegacyOAuthSdk
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(MCPOAuthProvider_exports);
|
|
24
25
|
var import_types = require('./types.js');
|
|
25
26
|
var import_crypto = require("crypto");
|
|
27
|
+
var import_node_fs = require("node:fs");
|
|
28
|
+
var import_node_path = require("node:path");
|
|
29
|
+
var import_node_module = require("node:module");
|
|
26
30
|
var import_resource_url = require('./resource-url.js');
|
|
31
|
+
let legacyOAuthSdkChecked = false;
|
|
32
|
+
function assertLegacyOAuthSdk() {
|
|
33
|
+
if (legacyOAuthSdkChecked) return;
|
|
34
|
+
try {
|
|
35
|
+
const clientPath = (0, import_node_module.createRequire)((0, import_node_path.resolve)(__dirname, "MCPOAuthProvider.js")).resolve(
|
|
36
|
+
"@modelcontextprotocol/sdk/client/index.js"
|
|
37
|
+
);
|
|
38
|
+
const { version } = JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.resolve)((0, import_node_path.dirname)(clientPath), "../../../package.json"), "utf8"));
|
|
39
|
+
const match = typeof version === "string" && /^1\.(\d+)\.\d+$/.exec(version);
|
|
40
|
+
if (!match || Number(match[1]) < 31) {
|
|
41
|
+
throw new import_types.OAuthError(
|
|
42
|
+
"Legacy MCP OAuth requires @modelcontextprotocol/sdk >=1.31.0 within major 1. Upgrade the installed peer dependency.",
|
|
43
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error instanceof import_types.OAuthError) throw error;
|
|
48
|
+
throw new import_types.OAuthError(
|
|
49
|
+
"Cannot verify a patched legacy MCP SDK. Install @modelcontextprotocol/sdk ^1.31.0 and keep it external when bundling.",
|
|
50
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
legacyOAuthSdkChecked = true;
|
|
54
|
+
}
|
|
27
55
|
class MCPOAuthProvider {
|
|
28
56
|
agent;
|
|
29
57
|
storage;
|
|
@@ -97,6 +125,8 @@ class MCPOAuthProvider {
|
|
|
97
125
|
*/
|
|
98
126
|
async clientInformation() {
|
|
99
127
|
if (this.agent.oauth_client) {
|
|
128
|
+
if (this.agent.oauth_client.client_secret) assertLegacyOAuthSdk();
|
|
129
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_client, void 0, !!this.agent.oauth_client.client_secret);
|
|
100
130
|
return (0, import_types.toMCPClientInfo)(this.agent.oauth_client);
|
|
101
131
|
}
|
|
102
132
|
return void 0;
|
|
@@ -113,6 +143,8 @@ class MCPOAuthProvider {
|
|
|
113
143
|
*/
|
|
114
144
|
async tokens() {
|
|
115
145
|
if (this.agent.oauth_tokens) {
|
|
146
|
+
if (this.agent.oauth_tokens.refresh_token) assertLegacyOAuthSdk();
|
|
147
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_tokens, void 0, !!this.agent.oauth_tokens.refresh_token);
|
|
116
148
|
return (0, import_types.toMCPTokens)(this.agent.oauth_tokens);
|
|
117
149
|
}
|
|
118
150
|
return void 0;
|
|
@@ -124,13 +156,34 @@ class MCPOAuthProvider {
|
|
|
124
156
|
async saveTokens(tokens) {
|
|
125
157
|
this.agent.oauth_tokens = (0, import_types.fromMCPTokens)(tokens);
|
|
126
158
|
delete this.agent.oauth_code_verifier;
|
|
159
|
+
delete this.agent.oauth_discovery_state;
|
|
127
160
|
await this.persistAgent();
|
|
128
161
|
}
|
|
129
162
|
/**
|
|
130
163
|
* Redirect user to authorization URL
|
|
131
164
|
*/
|
|
132
165
|
async redirectToAuthorization(authorizationUrl) {
|
|
133
|
-
|
|
166
|
+
try {
|
|
167
|
+
await this.flowHandler.redirectToAuthorization(authorizationUrl);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
await this.invalidateCredentials("verifier");
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
174
|
+
async saveDiscoveryState(state) {
|
|
175
|
+
const issuer = String(state.authorizationServerUrl);
|
|
176
|
+
if (this.agent.oauth_tokens?.refresh_token) {
|
|
177
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_tokens, issuer, true);
|
|
178
|
+
}
|
|
179
|
+
if (this.agent.oauth_client?.client_secret) {
|
|
180
|
+
(0, import_types.assertOAuthCredentialIssuer)(this.agent.oauth_client, issuer, true);
|
|
181
|
+
}
|
|
182
|
+
this.agent.oauth_discovery_state = state;
|
|
183
|
+
await this.persistAgent();
|
|
184
|
+
}
|
|
185
|
+
async discoveryState() {
|
|
186
|
+
return this.agent.oauth_code_verifier ? this.agent.oauth_discovery_state : void 0;
|
|
134
187
|
}
|
|
135
188
|
/**
|
|
136
189
|
* Save PKCE code verifier
|
|
@@ -158,6 +211,7 @@ class MCPOAuthProvider {
|
|
|
158
211
|
async invalidateCredentials(scope) {
|
|
159
212
|
switch (scope) {
|
|
160
213
|
case "all":
|
|
214
|
+
delete this.agent.oauth_discovery_state;
|
|
161
215
|
delete this.agent.oauth_tokens;
|
|
162
216
|
delete this.agent.oauth_client;
|
|
163
217
|
delete this.agent.oauth_code_verifier;
|
|
@@ -166,9 +220,14 @@ class MCPOAuthProvider {
|
|
|
166
220
|
delete this.agent.oauth_tokens;
|
|
167
221
|
break;
|
|
168
222
|
case "client":
|
|
223
|
+
delete this.agent.oauth_discovery_state;
|
|
169
224
|
delete this.agent.oauth_client;
|
|
170
225
|
break;
|
|
226
|
+
case "discovery":
|
|
227
|
+
delete this.agent.oauth_discovery_state;
|
|
228
|
+
break;
|
|
171
229
|
case "verifier":
|
|
230
|
+
delete this.agent.oauth_discovery_state;
|
|
172
231
|
delete this.agent.oauth_code_verifier;
|
|
173
232
|
break;
|
|
174
233
|
}
|
|
@@ -243,5 +302,6 @@ class MCPOAuthProvider {
|
|
|
243
302
|
}
|
|
244
303
|
// Annotate the CommonJS export names for ESM import in node:
|
|
245
304
|
0 && (module.exports = {
|
|
246
|
-
MCPOAuthProvider
|
|
305
|
+
MCPOAuthProvider,
|
|
306
|
+
assertLegacyOAuthSdk
|
|
247
307
|
});
|
|
@@ -1,6 +1,46 @@
|
|
|
1
|
-
import {
|
|
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
|
+
OAuthError
|
|
14
|
+
} from "./types.mjs";
|
|
2
15
|
import { randomBytes } from "crypto";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { dirname, resolve } from "node:path";
|
|
18
|
+
import { createRequire } from "node:module";
|
|
3
19
|
import { validateOAuthResourceUrl } from "./resource-url.mjs";
|
|
20
|
+
let legacyOAuthSdkChecked = false;
|
|
21
|
+
function assertLegacyOAuthSdk() {
|
|
22
|
+
if (legacyOAuthSdkChecked) return;
|
|
23
|
+
try {
|
|
24
|
+
const clientPath = createRequire(resolve(__dirname, "MCPOAuthProvider.js")).resolve(
|
|
25
|
+
"@modelcontextprotocol/sdk/client/index.js"
|
|
26
|
+
);
|
|
27
|
+
const { version } = JSON.parse(readFileSync(resolve(dirname(clientPath), "../../../package.json"), "utf8"));
|
|
28
|
+
const match = typeof version === "string" && /^1\.(\d+)\.\d+$/.exec(version);
|
|
29
|
+
if (!match || Number(match[1]) < 31) {
|
|
30
|
+
throw new OAuthError(
|
|
31
|
+
"Legacy MCP OAuth requires @modelcontextprotocol/sdk >=1.31.0 within major 1. Upgrade the installed peer dependency.",
|
|
32
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof OAuthError) throw error;
|
|
37
|
+
throw new OAuthError(
|
|
38
|
+
"Cannot verify a patched legacy MCP SDK. Install @modelcontextprotocol/sdk ^1.31.0 and keep it external when bundling.",
|
|
39
|
+
"mcp_oauth_sdk_upgrade_required"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
legacyOAuthSdkChecked = true;
|
|
43
|
+
}
|
|
4
44
|
class MCPOAuthProvider {
|
|
5
45
|
agent;
|
|
6
46
|
storage;
|
|
@@ -74,6 +114,8 @@ class MCPOAuthProvider {
|
|
|
74
114
|
*/
|
|
75
115
|
async clientInformation() {
|
|
76
116
|
if (this.agent.oauth_client) {
|
|
117
|
+
if (this.agent.oauth_client.client_secret) assertLegacyOAuthSdk();
|
|
118
|
+
assertOAuthCredentialIssuer(this.agent.oauth_client, void 0, !!this.agent.oauth_client.client_secret);
|
|
77
119
|
return toMCPClientInfo(this.agent.oauth_client);
|
|
78
120
|
}
|
|
79
121
|
return void 0;
|
|
@@ -90,6 +132,8 @@ class MCPOAuthProvider {
|
|
|
90
132
|
*/
|
|
91
133
|
async tokens() {
|
|
92
134
|
if (this.agent.oauth_tokens) {
|
|
135
|
+
if (this.agent.oauth_tokens.refresh_token) assertLegacyOAuthSdk();
|
|
136
|
+
assertOAuthCredentialIssuer(this.agent.oauth_tokens, void 0, !!this.agent.oauth_tokens.refresh_token);
|
|
93
137
|
return toMCPTokens(this.agent.oauth_tokens);
|
|
94
138
|
}
|
|
95
139
|
return void 0;
|
|
@@ -101,13 +145,34 @@ class MCPOAuthProvider {
|
|
|
101
145
|
async saveTokens(tokens) {
|
|
102
146
|
this.agent.oauth_tokens = fromMCPTokens(tokens);
|
|
103
147
|
delete this.agent.oauth_code_verifier;
|
|
148
|
+
delete this.agent.oauth_discovery_state;
|
|
104
149
|
await this.persistAgent();
|
|
105
150
|
}
|
|
106
151
|
/**
|
|
107
152
|
* Redirect user to authorization URL
|
|
108
153
|
*/
|
|
109
154
|
async redirectToAuthorization(authorizationUrl) {
|
|
110
|
-
|
|
155
|
+
try {
|
|
156
|
+
await this.flowHandler.redirectToAuthorization(authorizationUrl);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
await this.invalidateCredentials("verifier");
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Persist the authorization-server identity across the callback leg. */
|
|
163
|
+
async saveDiscoveryState(state) {
|
|
164
|
+
const issuer = String(state.authorizationServerUrl);
|
|
165
|
+
if (this.agent.oauth_tokens?.refresh_token) {
|
|
166
|
+
assertOAuthCredentialIssuer(this.agent.oauth_tokens, issuer, true);
|
|
167
|
+
}
|
|
168
|
+
if (this.agent.oauth_client?.client_secret) {
|
|
169
|
+
assertOAuthCredentialIssuer(this.agent.oauth_client, issuer, true);
|
|
170
|
+
}
|
|
171
|
+
this.agent.oauth_discovery_state = state;
|
|
172
|
+
await this.persistAgent();
|
|
173
|
+
}
|
|
174
|
+
async discoveryState() {
|
|
175
|
+
return this.agent.oauth_code_verifier ? this.agent.oauth_discovery_state : void 0;
|
|
111
176
|
}
|
|
112
177
|
/**
|
|
113
178
|
* Save PKCE code verifier
|
|
@@ -135,6 +200,7 @@ class MCPOAuthProvider {
|
|
|
135
200
|
async invalidateCredentials(scope) {
|
|
136
201
|
switch (scope) {
|
|
137
202
|
case "all":
|
|
203
|
+
delete this.agent.oauth_discovery_state;
|
|
138
204
|
delete this.agent.oauth_tokens;
|
|
139
205
|
delete this.agent.oauth_client;
|
|
140
206
|
delete this.agent.oauth_code_verifier;
|
|
@@ -143,9 +209,14 @@ class MCPOAuthProvider {
|
|
|
143
209
|
delete this.agent.oauth_tokens;
|
|
144
210
|
break;
|
|
145
211
|
case "client":
|
|
212
|
+
delete this.agent.oauth_discovery_state;
|
|
146
213
|
delete this.agent.oauth_client;
|
|
147
214
|
break;
|
|
215
|
+
case "discovery":
|
|
216
|
+
delete this.agent.oauth_discovery_state;
|
|
217
|
+
break;
|
|
148
218
|
case "verifier":
|
|
219
|
+
delete this.agent.oauth_discovery_state;
|
|
149
220
|
delete this.agent.oauth_code_verifier;
|
|
150
221
|
break;
|
|
151
222
|
}
|
|
@@ -219,5 +290,6 @@ class MCPOAuthProvider {
|
|
|
219
290
|
}
|
|
220
291
|
}
|
|
221
292
|
export {
|
|
222
|
-
MCPOAuthProvider
|
|
293
|
+
MCPOAuthProvider,
|
|
294
|
+
assertLegacyOAuthSdk
|
|
223
295
|
};
|
|
@@ -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`,
|
|
32
|
-
*
|
|
31
|
+
* `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
|
|
32
|
+
* Those fields are authoritative: an absent value deletes the stored value.
|
|
33
|
+
* Load the complete agent before constructing a provider; partial snapshots
|
|
34
|
+
* cannot preserve omitted OAuth fields. Other fields keep 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`,
|
|
32
|
-
*
|
|
31
|
+
* `oauth_client`, `oauth_resource`, `oauth_code_verifier`, and `oauth_discovery_state`.
|
|
32
|
+
* Those fields are authoritative: an absent value deletes the stored value.
|
|
33
|
+
* Load the complete agent before constructing a provider; partial snapshots
|
|
34
|
+
* cannot preserve omitted OAuth fields. Other fields keep 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,11 @@ 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 (agent.oauth_tokens === void 0) delete next.oauth_tokens;
|
|
98
|
+
if (agent.oauth_client === void 0) delete next.oauth_client;
|
|
99
|
+
if (agent.oauth_code_verifier === void 0) delete next.oauth_code_verifier;
|
|
100
|
+
if (agent.oauth_discovery_state === void 0) delete next.oauth_discovery_state;
|
|
101
|
+
else next.oauth_discovery_state = agent.oauth_discovery_state;
|
|
96
102
|
if (agent.oauth_resource === void 0) delete next.oauth_resource;
|
|
97
103
|
config.agents[key] = next;
|
|
98
104
|
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,11 @@ 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 (agent.oauth_tokens === void 0) delete next.oauth_tokens;
|
|
65
|
+
if (agent.oauth_client === void 0) delete next.oauth_client;
|
|
66
|
+
if (agent.oauth_code_verifier === void 0) delete next.oauth_code_verifier;
|
|
67
|
+
if (agent.oauth_discovery_state === void 0) delete next.oauth_discovery_state;
|
|
68
|
+
else next.oauth_discovery_state = agent.oauth_discovery_state;
|
|
63
69
|
if (agent.oauth_resource === void 0) delete next.oauth_resource;
|
|
64
70
|
config.agents[key] = next;
|
|
65
71
|
await writeConfig(config);
|
|
@@ -13,6 +13,8 @@ export type { AgentConfig, AgentOAuthTokens, AgentOAuthClient };
|
|
|
13
13
|
* Agent config storage interface
|
|
14
14
|
*
|
|
15
15
|
* Implement this to persist OAuth tokens back to agent configuration.
|
|
16
|
+
* Round-trip pending `oauth_code_verifier` and `oauth_discovery_state` verbatim,
|
|
17
|
+
* and remove OAuth fields absent from the authoritative agent snapshot.
|
|
16
18
|
* This allows different storage backends (file, database, memory)
|
|
17
19
|
* while keeping tokens in the agent config structure.
|
|
18
20
|
*/
|
|
@@ -132,3 +134,11 @@ export declare function toMCPClientInfo(client: AgentOAuthClient): OAuthClientIn
|
|
|
132
134
|
* Convert MCP SDK OAuthClientInformationFull to AgentOAuthClient format
|
|
133
135
|
*/
|
|
134
136
|
export declare function fromMCPClientInfo(info: OAuthClientInformationFull): AgentOAuthClient;
|
|
137
|
+
/** Internal credential-binding guard; old secrets must be migrated from trusted configuration. */
|
|
138
|
+
export declare function assertOAuthCredentialIssuer(credential: {
|
|
139
|
+
issuer?: unknown;
|
|
140
|
+
}, expectedIssuer?: string, requireIssuer?: boolean): void;
|
|
141
|
+
/** Internal metadata guard, matching the modern MCP SDK's asymmetric issuer-echo rule. */
|
|
142
|
+
export declare function assertOAuthServerIssuer(metadata: {
|
|
143
|
+
issuer?: unknown;
|
|
144
|
+
} | undefined, authorizationServerUrl: string): void;
|
|
@@ -13,6 +13,8 @@ export type { AgentConfig, AgentOAuthTokens, AgentOAuthClient };
|
|
|
13
13
|
* Agent config storage interface
|
|
14
14
|
*
|
|
15
15
|
* Implement this to persist OAuth tokens back to agent configuration.
|
|
16
|
+
* Round-trip pending `oauth_code_verifier` and `oauth_discovery_state` verbatim,
|
|
17
|
+
* and remove OAuth fields absent from the authoritative agent snapshot.
|
|
16
18
|
* This allows different storage backends (file, database, memory)
|
|
17
19
|
* while keeping tokens in the agent config structure.
|
|
18
20
|
*/
|
|
@@ -132,3 +134,11 @@ export declare function toMCPClientInfo(client: AgentOAuthClient): OAuthClientIn
|
|
|
132
134
|
* Convert MCP SDK OAuthClientInformationFull to AgentOAuthClient format
|
|
133
135
|
*/
|
|
134
136
|
export declare function fromMCPClientInfo(info: OAuthClientInformationFull): AgentOAuthClient;
|
|
137
|
+
/** Internal credential-binding guard; old secrets must be migrated from trusted configuration. */
|
|
138
|
+
export declare function assertOAuthCredentialIssuer(credential: {
|
|
139
|
+
issuer?: unknown;
|
|
140
|
+
}, expectedIssuer?: string, requireIssuer?: boolean): void;
|
|
141
|
+
/** Internal metadata guard, matching the modern MCP SDK's asymmetric issuer-echo rule. */
|
|
142
|
+
export declare function assertOAuthServerIssuer(metadata: {
|
|
143
|
+
issuer?: unknown;
|
|
144
|
+
} | undefined, authorizationServerUrl: string): void;
|
|
@@ -22,6 +22,8 @@ __export(types_exports, {
|
|
|
22
22
|
OAuthCancelledError: () => OAuthCancelledError,
|
|
23
23
|
OAuthError: () => OAuthError,
|
|
24
24
|
OAuthTimeoutError: () => OAuthTimeoutError,
|
|
25
|
+
assertOAuthCredentialIssuer: () => assertOAuthCredentialIssuer,
|
|
26
|
+
assertOAuthServerIssuer: () => assertOAuthServerIssuer,
|
|
25
27
|
fromMCPClientInfo: () => fromMCPClientInfo,
|
|
26
28
|
fromMCPTokens: () => fromMCPTokens,
|
|
27
29
|
toMCPClientInfo: () => toMCPClientInfo,
|
|
@@ -60,6 +62,7 @@ class OAuthTimeoutError extends OAuthError {
|
|
|
60
62
|
}
|
|
61
63
|
function toMCPTokens(tokens) {
|
|
62
64
|
return {
|
|
65
|
+
...tokens.issuer !== void 0 && tokens.issuer !== null && { issuer: tokens.issuer },
|
|
63
66
|
access_token: tokens.access_token,
|
|
64
67
|
refresh_token: tokens.refresh_token,
|
|
65
68
|
token_type: tokens.token_type || "Bearer",
|
|
@@ -69,6 +72,7 @@ function toMCPTokens(tokens) {
|
|
|
69
72
|
}
|
|
70
73
|
function fromMCPTokens(tokens) {
|
|
71
74
|
const result = {
|
|
75
|
+
...tokens.issuer !== void 0 && tokens.issuer !== null && { issuer: tokens.issuer },
|
|
72
76
|
access_token: tokens.access_token,
|
|
73
77
|
token_type: tokens.token_type,
|
|
74
78
|
scope: tokens.scope
|
|
@@ -84,6 +88,7 @@ function fromMCPTokens(tokens) {
|
|
|
84
88
|
}
|
|
85
89
|
function toMCPClientInfo(client) {
|
|
86
90
|
return {
|
|
91
|
+
...client.issuer !== void 0 && client.issuer !== null && { issuer: client.issuer },
|
|
87
92
|
client_id: client.client_id,
|
|
88
93
|
client_secret: client.client_secret,
|
|
89
94
|
client_secret_expires_at: client.client_secret_expires_at
|
|
@@ -91,17 +96,51 @@ function toMCPClientInfo(client) {
|
|
|
91
96
|
}
|
|
92
97
|
function fromMCPClientInfo(info) {
|
|
93
98
|
return {
|
|
99
|
+
...info.issuer !== void 0 && info.issuer !== null && { issuer: info.issuer },
|
|
94
100
|
client_id: info.client_id,
|
|
95
101
|
client_secret: info.client_secret,
|
|
96
102
|
client_secret_expires_at: info.client_secret_expires_at
|
|
97
103
|
};
|
|
98
104
|
}
|
|
105
|
+
function isOAuthIssuer(value) {
|
|
106
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
107
|
+
try {
|
|
108
|
+
const url = new URL(value);
|
|
109
|
+
return (url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password && !url.search && !url.hash;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function assertOAuthCredentialIssuer(credential, expectedIssuer, requireIssuer = false) {
|
|
115
|
+
const issuer = credential.issuer;
|
|
116
|
+
if ((issuer === void 0 || issuer === null) && !requireIssuer) return;
|
|
117
|
+
if (!isOAuthIssuer(issuer)) {
|
|
118
|
+
throw new OAuthError(
|
|
119
|
+
"Saved OAuth credentials need a valid issuer. Clear them and sign in again, or set their issuer from trusted configuration.",
|
|
120
|
+
"oauth_issuer_required"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (expectedIssuer !== void 0 && issuer !== expectedIssuer && !(issuer.endsWith("/") && issuer.slice(0, -1) === expectedIssuer) && !(expectedIssuer.endsWith("/") && expectedIssuer.slice(0, -1) === issuer)) {
|
|
124
|
+
throw new OAuthError("OAuth credentials belong to a different authorization server.", "oauth_issuer_mismatch");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function assertOAuthServerIssuer(metadata, authorizationServerUrl) {
|
|
128
|
+
const issuer = metadata?.issuer;
|
|
129
|
+
if (!isOAuthIssuer(issuer) || issuer !== authorizationServerUrl && !(authorizationServerUrl.endsWith("/") && issuer === authorizationServerUrl.slice(0, -1))) {
|
|
130
|
+
throw new OAuthError(
|
|
131
|
+
"Authorization-server metadata does not match the discovered issuer.",
|
|
132
|
+
"oauth_issuer_mismatch"
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
99
136
|
// Annotate the CommonJS export names for ESM import in node:
|
|
100
137
|
0 && (module.exports = {
|
|
101
138
|
DEFAULT_CLIENT_METADATA,
|
|
102
139
|
OAuthCancelledError,
|
|
103
140
|
OAuthError,
|
|
104
141
|
OAuthTimeoutError,
|
|
142
|
+
assertOAuthCredentialIssuer,
|
|
143
|
+
assertOAuthServerIssuer,
|
|
105
144
|
fromMCPClientInfo,
|
|
106
145
|
fromMCPTokens,
|
|
107
146
|
toMCPClientInfo,
|
|
@@ -30,6 +30,7 @@ class OAuthTimeoutError extends OAuthError {
|
|
|
30
30
|
}
|
|
31
31
|
function toMCPTokens(tokens) {
|
|
32
32
|
return {
|
|
33
|
+
...tokens.issuer !== void 0 && tokens.issuer !== null && { issuer: tokens.issuer },
|
|
33
34
|
access_token: tokens.access_token,
|
|
34
35
|
refresh_token: tokens.refresh_token,
|
|
35
36
|
token_type: tokens.token_type || "Bearer",
|
|
@@ -39,6 +40,7 @@ function toMCPTokens(tokens) {
|
|
|
39
40
|
}
|
|
40
41
|
function fromMCPTokens(tokens) {
|
|
41
42
|
const result = {
|
|
43
|
+
...tokens.issuer !== void 0 && tokens.issuer !== null && { issuer: tokens.issuer },
|
|
42
44
|
access_token: tokens.access_token,
|
|
43
45
|
token_type: tokens.token_type,
|
|
44
46
|
scope: tokens.scope
|
|
@@ -54,6 +56,7 @@ function fromMCPTokens(tokens) {
|
|
|
54
56
|
}
|
|
55
57
|
function toMCPClientInfo(client) {
|
|
56
58
|
return {
|
|
59
|
+
...client.issuer !== void 0 && client.issuer !== null && { issuer: client.issuer },
|
|
57
60
|
client_id: client.client_id,
|
|
58
61
|
client_secret: client.client_secret,
|
|
59
62
|
client_secret_expires_at: client.client_secret_expires_at
|
|
@@ -61,16 +64,50 @@ function toMCPClientInfo(client) {
|
|
|
61
64
|
}
|
|
62
65
|
function fromMCPClientInfo(info) {
|
|
63
66
|
return {
|
|
67
|
+
...info.issuer !== void 0 && info.issuer !== null && { issuer: info.issuer },
|
|
64
68
|
client_id: info.client_id,
|
|
65
69
|
client_secret: info.client_secret,
|
|
66
70
|
client_secret_expires_at: info.client_secret_expires_at
|
|
67
71
|
};
|
|
68
72
|
}
|
|
73
|
+
function isOAuthIssuer(value) {
|
|
74
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
75
|
+
try {
|
|
76
|
+
const url = new URL(value);
|
|
77
|
+
return (url.protocol === "https:" || url.protocol === "http:") && !url.username && !url.password && !url.search && !url.hash;
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function assertOAuthCredentialIssuer(credential, expectedIssuer, requireIssuer = false) {
|
|
83
|
+
const issuer = credential.issuer;
|
|
84
|
+
if ((issuer === void 0 || issuer === null) && !requireIssuer) return;
|
|
85
|
+
if (!isOAuthIssuer(issuer)) {
|
|
86
|
+
throw new OAuthError(
|
|
87
|
+
"Saved OAuth credentials need a valid issuer. Clear them and sign in again, or set their issuer from trusted configuration.",
|
|
88
|
+
"oauth_issuer_required"
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
if (expectedIssuer !== void 0 && issuer !== expectedIssuer && !(issuer.endsWith("/") && issuer.slice(0, -1) === expectedIssuer) && !(expectedIssuer.endsWith("/") && expectedIssuer.slice(0, -1) === issuer)) {
|
|
92
|
+
throw new OAuthError("OAuth credentials belong to a different authorization server.", "oauth_issuer_mismatch");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function assertOAuthServerIssuer(metadata, authorizationServerUrl) {
|
|
96
|
+
const issuer = metadata?.issuer;
|
|
97
|
+
if (!isOAuthIssuer(issuer) || issuer !== authorizationServerUrl && !(authorizationServerUrl.endsWith("/") && issuer === authorizationServerUrl.slice(0, -1))) {
|
|
98
|
+
throw new OAuthError(
|
|
99
|
+
"Authorization-server metadata does not match the discovered issuer.",
|
|
100
|
+
"oauth_issuer_mismatch"
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
69
104
|
export {
|
|
70
105
|
DEFAULT_CLIENT_METADATA,
|
|
71
106
|
OAuthCancelledError,
|
|
72
107
|
OAuthError,
|
|
73
108
|
OAuthTimeoutError,
|
|
109
|
+
assertOAuthCredentialIssuer,
|
|
110
|
+
assertOAuthServerIssuer,
|
|
74
111
|
fromMCPClientInfo,
|
|
75
112
|
fromMCPTokens,
|
|
76
113
|
toMCPClientInfo,
|
|
@@ -144,6 +144,7 @@ async function startWebOAuthFlow(opts) {
|
|
|
144
144
|
if (!asMetadata) {
|
|
145
145
|
throw new import_types.OAuthError(`No OAuth metadata at ${asUrl.toString()}`, "no_authorization_server_metadata", agent.id);
|
|
146
146
|
}
|
|
147
|
+
(0, import_types.assertOAuthServerIssuer)(asMetadata, asUrl.toString());
|
|
147
148
|
const resource = resourceOverride ? new URL(resourceOverride) : prm?.resource ? new URL(prm.resource) : (0, import_auth_utils.resourceUrlFromServerUrl)(new URL(agent.agent_uri));
|
|
148
149
|
const baseClientMetadata = {
|
|
149
150
|
...import_types.DEFAULT_CLIENT_METADATA,
|
|
@@ -230,6 +231,12 @@ async function completeWebOAuthFlow(opts) {
|
|
|
230
231
|
const asMetadata = await (0, import_auth.discoverAuthorizationServerMetadata)(flow.authorizationServerUrl, {
|
|
231
232
|
fetchFn: guardedFetch
|
|
232
233
|
});
|
|
234
|
+
(0, import_types.assertOAuthServerIssuer)(asMetadata, flow.authorizationServerUrl);
|
|
235
|
+
(0, import_types.assertOAuthCredentialIssuer)(
|
|
236
|
+
flow.clientInformation,
|
|
237
|
+
flow.authorizationServerUrl,
|
|
238
|
+
!!flow.clientInformation.client_secret
|
|
239
|
+
);
|
|
233
240
|
let tokens;
|
|
234
241
|
try {
|
|
235
242
|
tokens = await (0, import_auth.exchangeAuthorization)(flow.authorizationServerUrl, {
|
|
@@ -241,6 +248,7 @@ async function completeWebOAuthFlow(opts) {
|
|
|
241
248
|
resource: flow.resource ? new URL(flow.resource) : void 0,
|
|
242
249
|
fetchFn: guardedFetch
|
|
243
250
|
});
|
|
251
|
+
tokens = { ...tokens, issuer: flow.authorizationServerUrl };
|
|
244
252
|
} catch (err) {
|
|
245
253
|
throw wrapTokenExchangeError(err);
|
|
246
254
|
}
|
|
@@ -410,6 +418,7 @@ function createSsrfSafeOAuthFetch(trustedFetchFn, allowHttp) {
|
|
|
410
418
|
async function resolveClientInformation(args) {
|
|
411
419
|
const { agent, agentStorage, asUrl, asMetadata, clientMetadata, fetchFn, allowConfidentialClient } = args;
|
|
412
420
|
if (agent.oauth_client) {
|
|
421
|
+
(0, import_types.assertOAuthCredentialIssuer)(agent.oauth_client, asUrl.toString(), !!agent.oauth_client.client_secret);
|
|
413
422
|
return (0, import_types.toMCPClientInfo)(agent.oauth_client);
|
|
414
423
|
}
|
|
415
424
|
if (!asMetadata.registration_endpoint) {
|
|
@@ -419,11 +428,12 @@ async function resolveClientInformation(args) {
|
|
|
419
428
|
agent.id
|
|
420
429
|
);
|
|
421
430
|
}
|
|
422
|
-
const
|
|
431
|
+
const registration = await (0, import_auth.registerClient)(asUrl.toString(), {
|
|
423
432
|
metadata: asMetadata,
|
|
424
433
|
clientMetadata,
|
|
425
434
|
fetchFn
|
|
426
435
|
});
|
|
436
|
+
const registered = { ...registration, issuer: asUrl.toString() };
|
|
427
437
|
if (registered.client_secret && !allowConfidentialClient) {
|
|
428
438
|
throw new ConfidentialClientNotAllowedError(agent.id);
|
|
429
439
|
}
|
|
@@ -7,7 +7,15 @@ import {
|
|
|
7
7
|
startAuthorization
|
|
8
8
|
} from "@modelcontextprotocol/sdk/client/auth.js";
|
|
9
9
|
import { checkResourceAllowed, resourceUrlFromServerUrl } from "@modelcontextprotocol/sdk/shared/auth-utils.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_CLIENT_METADATA,
|
|
12
|
+
fromMCPClientInfo,
|
|
13
|
+
fromMCPTokens,
|
|
14
|
+
OAuthError,
|
|
15
|
+
toMCPClientInfo,
|
|
16
|
+
assertOAuthCredentialIssuer,
|
|
17
|
+
assertOAuthServerIssuer
|
|
18
|
+
} from "./types.mjs";
|
|
11
19
|
import { validateOAuthResourceUrl } from "./resource-url.mjs";
|
|
12
20
|
import { ssrfSafeFetch } from "../../net/ssrf-fetch.mjs";
|
|
13
21
|
const DEFAULT_WEB_FLOW_TTL_MS = 10 * 60 * 1e3;
|
|
@@ -115,6 +123,7 @@ async function startWebOAuthFlow(opts) {
|
|
|
115
123
|
if (!asMetadata) {
|
|
116
124
|
throw new OAuthError(`No OAuth metadata at ${asUrl.toString()}`, "no_authorization_server_metadata", agent.id);
|
|
117
125
|
}
|
|
126
|
+
assertOAuthServerIssuer(asMetadata, asUrl.toString());
|
|
118
127
|
const resource = resourceOverride ? new URL(resourceOverride) : prm?.resource ? new URL(prm.resource) : resourceUrlFromServerUrl(new URL(agent.agent_uri));
|
|
119
128
|
const baseClientMetadata = {
|
|
120
129
|
...DEFAULT_CLIENT_METADATA,
|
|
@@ -201,6 +210,12 @@ async function completeWebOAuthFlow(opts) {
|
|
|
201
210
|
const asMetadata = await discoverAuthorizationServerMetadata(flow.authorizationServerUrl, {
|
|
202
211
|
fetchFn: guardedFetch
|
|
203
212
|
});
|
|
213
|
+
assertOAuthServerIssuer(asMetadata, flow.authorizationServerUrl);
|
|
214
|
+
assertOAuthCredentialIssuer(
|
|
215
|
+
flow.clientInformation,
|
|
216
|
+
flow.authorizationServerUrl,
|
|
217
|
+
!!flow.clientInformation.client_secret
|
|
218
|
+
);
|
|
204
219
|
let tokens;
|
|
205
220
|
try {
|
|
206
221
|
tokens = await exchangeAuthorization(flow.authorizationServerUrl, {
|
|
@@ -212,6 +227,7 @@ async function completeWebOAuthFlow(opts) {
|
|
|
212
227
|
resource: flow.resource ? new URL(flow.resource) : void 0,
|
|
213
228
|
fetchFn: guardedFetch
|
|
214
229
|
});
|
|
230
|
+
tokens = { ...tokens, issuer: flow.authorizationServerUrl };
|
|
215
231
|
} catch (err) {
|
|
216
232
|
throw wrapTokenExchangeError(err);
|
|
217
233
|
}
|
|
@@ -381,6 +397,7 @@ function createSsrfSafeOAuthFetch(trustedFetchFn, allowHttp) {
|
|
|
381
397
|
async function resolveClientInformation(args) {
|
|
382
398
|
const { agent, agentStorage, asUrl, asMetadata, clientMetadata, fetchFn, allowConfidentialClient } = args;
|
|
383
399
|
if (agent.oauth_client) {
|
|
400
|
+
assertOAuthCredentialIssuer(agent.oauth_client, asUrl.toString(), !!agent.oauth_client.client_secret);
|
|
384
401
|
return toMCPClientInfo(agent.oauth_client);
|
|
385
402
|
}
|
|
386
403
|
if (!asMetadata.registration_endpoint) {
|
|
@@ -390,11 +407,12 @@ async function resolveClientInformation(args) {
|
|
|
390
407
|
agent.id
|
|
391
408
|
);
|
|
392
409
|
}
|
|
393
|
-
const
|
|
410
|
+
const registration = await registerClient(asUrl.toString(), {
|
|
394
411
|
metadata: asMetadata,
|
|
395
412
|
clientMetadata,
|
|
396
413
|
fetchFn
|
|
397
414
|
});
|
|
415
|
+
const registered = { ...registration, issuer: asUrl.toString() };
|
|
398
416
|
if (registered.client_secret && !allowConfidentialClient) {
|
|
399
417
|
throw new ConfidentialClientNotAllowedError(agent.id);
|
|
400
418
|
}
|
|
@@ -34,6 +34,7 @@ var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamable
|
|
|
34
34
|
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
35
35
|
var import_auth = require("@modelcontextprotocol/sdk/client/auth.js");
|
|
36
36
|
var import_node_crypto = require("node:crypto");
|
|
37
|
+
var import_MCPOAuthProvider = require('../auth/oauth/MCPOAuthProvider.js');
|
|
37
38
|
var import_auth2 = require('../auth/index.js');
|
|
38
39
|
var import_errors = require('../errors/index.js');
|
|
39
40
|
var import_tracing = require('../observability/tracing.js');
|
|
@@ -713,6 +714,7 @@ async function connectMCP(options) {
|
|
|
713
714
|
fetchFn,
|
|
714
715
|
allowPrivateIp
|
|
715
716
|
} = options;
|
|
717
|
+
if (authProvider) (0, import_MCPOAuthProvider.assertLegacyOAuthSdk)();
|
|
716
718
|
const baseUrl = new URL(agentUrl);
|
|
717
719
|
debugLogs.push({
|
|
718
720
|
type: "info",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
12
12
|
import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
13
13
|
import { hkdfSync, randomBytes } from "node:crypto";
|
|
14
|
+
import { assertLegacyOAuthSdk } from "../auth/oauth/MCPOAuthProvider.mjs";
|
|
14
15
|
import { createMCPRequestHeaders } from "../auth/index.mjs";
|
|
15
16
|
import { is401Error } from "../errors/index.mjs";
|
|
16
17
|
import { withSpan, injectTraceHeaders } from "../observability/tracing.mjs";
|
|
@@ -700,6 +701,7 @@ async function connectMCP(options) {
|
|
|
700
701
|
fetchFn,
|
|
701
702
|
allowPrivateIp
|
|
702
703
|
} = options;
|
|
704
|
+
if (authProvider) assertLegacyOAuthSdk();
|
|
703
705
|
const baseUrl = new URL(agentUrl);
|
|
704
706
|
debugLogs.push({
|
|
705
707
|
type: "info",
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
"source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
|
|
5
5
|
"source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
|
|
6
6
|
"upstream_adcp_version": "2.5.3",
|
|
7
|
-
"synced_at": "2026-10-
|
|
7
|
+
"synced_at": "2026-10-06T19:28:36.184Z"
|
|
8
8
|
}
|
|
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
|
|
|
197
197
|
*/
|
|
198
198
|
export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
|
|
199
199
|
/** Capability semver emitted in run summaries and used for advisory expiry. */
|
|
200
|
-
export declare const RUNNER_CAPABILITY_VERSION = "14.
|
|
200
|
+
export declare const RUNNER_CAPABILITY_VERSION = "14.4.0";
|
|
201
201
|
/** True when a failed validation contributes to the owning step's grade. */
|
|
202
202
|
export declare function validationFailsStep(result: ValidationResult): boolean;
|
|
203
203
|
/**
|
|
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
|
|
|
197
197
|
*/
|
|
198
198
|
export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
|
|
199
199
|
/** Capability semver emitted in run summaries and used for advisory expiry. */
|
|
200
|
-
export declare const RUNNER_CAPABILITY_VERSION = "14.
|
|
200
|
+
export declare const RUNNER_CAPABILITY_VERSION = "14.4.0";
|
|
201
201
|
/** True when a failed validation contributes to the owning step's grade. */
|
|
202
202
|
export declare function validationFailsStep(result: ValidationResult): boolean;
|
|
203
203
|
/**
|
|
@@ -145,6 +145,8 @@ export interface DayParting {
|
|
|
145
145
|
* OAuth tokens for agent authentication
|
|
146
146
|
*/
|
|
147
147
|
export interface AgentOAuthTokens {
|
|
148
|
+
/** Authorization server that issued these tokens. Required when reusing a refresh token. */
|
|
149
|
+
issuer?: string;
|
|
148
150
|
/** OAuth access token */
|
|
149
151
|
access_token: string;
|
|
150
152
|
/** OAuth refresh token (for token renewal) */
|
|
@@ -162,6 +164,8 @@ export interface AgentOAuthTokens {
|
|
|
162
164
|
* OAuth client information (from dynamic registration)
|
|
163
165
|
*/
|
|
164
166
|
export interface AgentOAuthClient {
|
|
167
|
+
/** Authorization server where this client was registered. Required when reusing a client secret. */
|
|
168
|
+
issuer?: string;
|
|
165
169
|
/** OAuth client ID */
|
|
166
170
|
client_id: string;
|
|
167
171
|
/** OAuth client secret (for confidential clients) */
|
|
@@ -401,6 +405,8 @@ export interface AgentConfig {
|
|
|
401
405
|
* authorization flow.
|
|
402
406
|
*/
|
|
403
407
|
oauth_client_credentials?: AgentOAuthClientCredentials;
|
|
408
|
+
/** Opaque discovery state paired with the pending PKCE verifier; persist and return verbatim. */
|
|
409
|
+
oauth_discovery_state?: import('@modelcontextprotocol/sdk/client/auth.js').OAuthDiscoveryState;
|
|
404
410
|
/**
|
|
405
411
|
* Additional HTTP headers to include in every request to this agent.
|
|
406
412
|
* Useful for sending API keys, org IDs, or other vendor-specific headers
|
package/dist/lib/types/adcp.d.ts
CHANGED
|
@@ -145,6 +145,8 @@ export interface DayParting {
|
|
|
145
145
|
* OAuth tokens for agent authentication
|
|
146
146
|
*/
|
|
147
147
|
export interface AgentOAuthTokens {
|
|
148
|
+
/** Authorization server that issued these tokens. Required when reusing a refresh token. */
|
|
149
|
+
issuer?: string;
|
|
148
150
|
/** OAuth access token */
|
|
149
151
|
access_token: string;
|
|
150
152
|
/** OAuth refresh token (for token renewal) */
|
|
@@ -162,6 +164,8 @@ export interface AgentOAuthTokens {
|
|
|
162
164
|
* OAuth client information (from dynamic registration)
|
|
163
165
|
*/
|
|
164
166
|
export interface AgentOAuthClient {
|
|
167
|
+
/** Authorization server where this client was registered. Required when reusing a client secret. */
|
|
168
|
+
issuer?: string;
|
|
165
169
|
/** OAuth client ID */
|
|
166
170
|
client_id: string;
|
|
167
171
|
/** OAuth client secret (for confidential clients) */
|
|
@@ -401,6 +405,8 @@ export interface AgentConfig {
|
|
|
401
405
|
* authorization flow.
|
|
402
406
|
*/
|
|
403
407
|
oauth_client_credentials?: AgentOAuthClientCredentials;
|
|
408
|
+
/** Opaque discovery state paired with the pending PKCE verifier; persist and return verbatim. */
|
|
409
|
+
oauth_discovery_state?: import('@modelcontextprotocol/sdk/client/auth.js').OAuthDiscoveryState;
|
|
404
410
|
/**
|
|
405
411
|
* Additional HTTP headers to include in every request to this agent.
|
|
406
412
|
* Useful for sending API keys, org IDs, or other vendor-specific headers
|
package/dist/lib/version.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "14.
|
|
4
|
+
export declare const LIBRARY_VERSION = "14.4.0";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "14.
|
|
36
|
+
readonly library: "14.4.0";
|
|
37
37
|
readonly adcp: "3.2.1";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.2.1", "3.2"];
|
|
39
|
-
readonly generatedAt: "2026-10-
|
|
39
|
+
readonly generatedAt: "2026-10-06T19:00:01.077Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AdCP SDK library version
|
|
3
3
|
*/
|
|
4
|
-
export declare const LIBRARY_VERSION = "14.
|
|
4
|
+
export declare const LIBRARY_VERSION = "14.4.0";
|
|
5
5
|
/**
|
|
6
6
|
* AdCP specification version this library is built for
|
|
7
7
|
*/
|
|
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
|
|
|
33
33
|
* Full version information
|
|
34
34
|
*/
|
|
35
35
|
export declare const VERSION_INFO: {
|
|
36
|
-
readonly library: "14.
|
|
36
|
+
readonly library: "14.4.0";
|
|
37
37
|
readonly adcp: "3.2.1";
|
|
38
38
|
readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.2.1", "3.2"];
|
|
39
|
-
readonly generatedAt: "2026-10-
|
|
39
|
+
readonly generatedAt: "2026-10-06T19:00:01.077Z";
|
|
40
40
|
};
|
|
41
41
|
/**
|
|
42
42
|
* Get the AdCP specification version this library is built for
|
package/dist/lib/version.js
CHANGED
|
@@ -31,7 +31,7 @@ __export(version_exports, {
|
|
|
31
31
|
toReleasePrecisionVersion: () => toReleasePrecisionVersion
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(version_exports);
|
|
34
|
-
const LIBRARY_VERSION = "14.
|
|
34
|
+
const LIBRARY_VERSION = "14.4.0";
|
|
35
35
|
const ADCP_VERSION = "3.2.1";
|
|
36
36
|
const ADCP_MAJOR_VERSION = 3;
|
|
37
37
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -95,10 +95,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
95
95
|
"3.2"
|
|
96
96
|
];
|
|
97
97
|
const VERSION_INFO = {
|
|
98
|
-
library: "14.
|
|
98
|
+
library: "14.4.0",
|
|
99
99
|
adcp: "3.2.1",
|
|
100
100
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
101
|
-
generatedAt: "2026-10-
|
|
101
|
+
generatedAt: "2026-10-06T19:00:01.077Z"
|
|
102
102
|
};
|
|
103
103
|
function getAdcpVersion() {
|
|
104
104
|
return ADCP_VERSION;
|
package/dist/lib/version.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const LIBRARY_VERSION = "14.
|
|
1
|
+
const LIBRARY_VERSION = "14.4.0";
|
|
2
2
|
const ADCP_VERSION = "3.2.1";
|
|
3
3
|
const ADCP_MAJOR_VERSION = 3;
|
|
4
4
|
const COMPATIBLE_ADCP_VERSIONS = [
|
|
@@ -62,10 +62,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
|
|
|
62
62
|
"3.2"
|
|
63
63
|
];
|
|
64
64
|
const VERSION_INFO = {
|
|
65
|
-
library: "14.
|
|
65
|
+
library: "14.4.0",
|
|
66
66
|
adcp: "3.2.1",
|
|
67
67
|
compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
|
|
68
|
-
generatedAt: "2026-10-
|
|
68
|
+
generatedAt: "2026-10-06T19:00:01.077Z"
|
|
69
69
|
};
|
|
70
70
|
function getAdcpVersion() {
|
|
71
71
|
return ADCP_VERSION;
|
package/docs/TYPE-SUMMARY.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AdCP Type Summary
|
|
2
2
|
|
|
3
3
|
> Generated at: 2026-10-06
|
|
4
|
-
> @adcp/sdk v14.
|
|
4
|
+
> @adcp/sdk v14.4.0
|
|
5
5
|
|
|
6
6
|
Curated reference of the types that matter for using the AdCP client. For full generated types see `src/lib/types/tools.generated.ts` and `src/lib/types/core.generated.ts`.
|
|
7
7
|
|
package/docs/llms.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Ad Context Protocol (AdCP)
|
|
2
2
|
|
|
3
3
|
> Generated at: 2026-10-06
|
|
4
|
-
> Library: @adcp/sdk v14.
|
|
4
|
+
> Library: @adcp/sdk v14.4.0
|
|
5
5
|
> AdCP major version: 3
|
|
6
6
|
> Canonical URL: https://adcontextprotocol.github.io/adcp-client/llms.txt
|
|
7
7
|
> Note: the `Library` stamp reflects the package.json version at doc-generation time. The narrative below describes the surface that lands on the next-published minor — including any 6.7 helpers documented here ahead of the release tag.
|
|
@@ -11,8 +11,8 @@ below as the publication/deployment gate.
|
|
|
11
11
|
|
|
12
12
|
| Fact | Value |
|
|
13
13
|
| --- | --- |
|
|
14
|
-
| Exact npm package | `@adcp/sdk@14.
|
|
15
|
-
| npm integrity | registry-derived after publication; run `npm view @adcp/sdk@14.
|
|
14
|
+
| Exact npm package | `@adcp/sdk@14.4.0` |
|
|
15
|
+
| npm integrity | registry-derived after publication; run `npm view @adcp/sdk@14.4.0 dist.integrity` |
|
|
16
16
|
| Node.js runtime | `^20.19.0 || >=22.12.0` |
|
|
17
17
|
| Default AdCP wire release | `3.2.1` |
|
|
18
18
|
| Maintained wire releases | `v2.5`, `v2.6`, `v3`, `3.0.0`, `3.0`, `3.0.1`, `3.0.2`, `3.0.3`, `3.0.4`, `3.0.5`, `3.0.6`, `3.0.7`, `3.0.8`, `3.0.9`, `3.0.10`, `3.0.11`, `3.0.12`, `3.0.13`, `3.0.14`, `3.0.15`, `3.0.16`, `3.0.17`, `3.0.18`, `3.0.19`, `3.0.20`, `3.0.21`, `3.0.22`, `3.0.23`, `3.0.24`, `3.0.25`, `3.1.0`, `3.1`, `3.1.1`, `3.1.2`, `3.1.3`, `3.1.4`, `3.1.5`, `3.1.6`, `3.1.7`, `3.1.8`, `3.1.9`, `3.1.10`, `3.1.11`, `3.1.12`, `3.1.13`, `3.1.14`, `3.1.15`, `3.1.16`, `3.1.17`, `3.1.18`, `3.1.19`, `3.1.20`, `3.1.21`, `3.1.22`, `3.1.23`, `3.1.24`, `3.2.1`, `3.2` |
|
|
@@ -21,8 +21,8 @@ below as the publication/deployment gate.
|
|
|
21
21
|
Install exact production inputs rather than a moving range or dist-tag:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
npm install --save-exact '@adcp/sdk@14.
|
|
25
|
-
npm view '@adcp/sdk@14.
|
|
24
|
+
npm install --save-exact '@adcp/sdk@14.4.0'
|
|
25
|
+
npm view '@adcp/sdk@14.4.0' dist.integrity
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
### Required and optional peers
|
|
@@ -32,7 +32,7 @@ Install the peer packages used by your application at versions satisfying these
|
|
|
32
32
|
| Peer | Supported range | Installation |
|
|
33
33
|
| --- | --- | --- |
|
|
34
34
|
| `@a2a-js/sdk` | `^1.0.1` | Required |
|
|
35
|
-
| `@modelcontextprotocol/sdk` | `^1.
|
|
35
|
+
| `@modelcontextprotocol/sdk` | `^1.31.0` | Required |
|
|
36
36
|
| `@opentelemetry/api` | `^1.0.0` | Optional |
|
|
37
37
|
| `redis` | `^4.6.0 || ^5.0.0 || ^6.0.0` | Optional |
|
|
38
38
|
| `zod` | `^4.1.5` | Required |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# OAuth issuer bindings
|
|
2
|
+
|
|
3
|
+
The corrective release after SDK 14.3.0 upgrades the official MCP client and
|
|
4
|
+
server to 2.2.0 and requires the legacy `@modelcontextprotocol/sdk` peer at
|
|
5
|
+
`^1.31.0`. These versions address
|
|
6
|
+
[GHSA-6qxp-vccf-f47h](https://github.com/advisories/GHSA-6qxp-vccf-f47h).
|
|
7
|
+
Installations pinned to legacy SDK 1.24–1.30 must upgrade that peer dependency.
|
|
8
|
+
The legacy OAuth transport rejects vulnerable versions even if package-manager
|
|
9
|
+
peer checks were bypassed. The Node.js requirement remains `^20.19.0 || >=22.12.0`.
|
|
10
|
+
|
|
11
|
+
OAuth refresh tokens and client secrets belong to an authorization server.
|
|
12
|
+
`AgentOAuthTokens` and `AgentOAuthClient` now preserve an optional `issuer`
|
|
13
|
+
field through the SDK's conversions, providers, and file storage. Newly saved
|
|
14
|
+
credentials receive their binding from the authorization flow. The provider
|
|
15
|
+
persists discovery alongside the pending PKCE verifier and clears that temporary
|
|
16
|
+
state after exchange; a callback uses the original authorization server even if
|
|
17
|
+
resource discovery changes while the browser is away. Custom `OAuthConfigStorage`
|
|
18
|
+
implementations must preserve `oauth_discovery_state` alongside `oauth_code_verifier`. Web flows
|
|
19
|
+
validate the discovered server's metadata issuer before registration and
|
|
20
|
+
token exchange; diagnostic refreshes validate it before sending credentials.
|
|
21
|
+
The provider refuses a changed authorization server before replacing a trusted
|
|
22
|
+
confidential registration or reusing a refresh token. Clear credentials explicitly
|
|
23
|
+
if an independently verified server migration requires a fresh registration.
|
|
24
|
+
|
|
25
|
+
File storage saves the OAuth fields as an authoritative snapshot: omitted tokens,
|
|
26
|
+
client, verifier, or discovery state are deleted. Load the complete agent from
|
|
27
|
+
storage before creating a provider; keep unrelated persisted fields as before.
|
|
28
|
+
|
|
29
|
+
## Existing saved credentials
|
|
30
|
+
|
|
31
|
+
`MCPOAuthProvider` refuses to reuse a saved refresh token or client secret
|
|
32
|
+
without a valid issuer, with `oauth_issuer_required`. The web flow and
|
|
33
|
+
diagnostic runner also refuse mismatched bindings. Access-token-only and
|
|
34
|
+
public-client configurations do not require a new field. A saved unbound refresh
|
|
35
|
+
token blocks every call through the provider, even while its access token is
|
|
36
|
+
still valid; clear the saved credentials before reconnecting.
|
|
37
|
+
|
|
38
|
+
For a saved CLI alias, run `adcp <alias> --clear-oauth`, then
|
|
39
|
+
`adcp <alias> --oauth` to sign in again.
|
|
40
|
+
|
|
41
|
+
For legacy saved credentials, clear `oauth_tokens` and any confidential
|
|
42
|
+
`oauth_client` registration, then sign in again. Alternatively, an operator
|
|
43
|
+
may add the authorization-server issuer established independently from
|
|
44
|
+
trusted registration records. **Do not infer it from the current MCP server's
|
|
45
|
+
discovery metadata**, an access token's unverified claims, or its resource URL.
|
|
46
|
+
If the issuer is unknown, sign in again. Raw `issuer: null` is treated as absent for access-token-only and public-client
|
|
47
|
+
configurations; secrets still require a valid string issuer.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
agent.oauth_tokens = {
|
|
51
|
+
...agent.oauth_tokens!,
|
|
52
|
+
issuer: 'https://auth.example.com', // independently verified token issuer
|
|
53
|
+
};
|
|
54
|
+
agent.oauth_client = {
|
|
55
|
+
...agent.oauth_client!,
|
|
56
|
+
issuer: 'https://auth.example.com', // where this client was registered
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
An issuer is an authorization-server URL, including its tenant path when one
|
|
61
|
+
exists. A single trailing slash difference is tolerated for credential
|
|
62
|
+
comparison; distinct tenant paths remain distinct. Stored pending web flows
|
|
63
|
+
containing an unbound confidential client must be restarted.
|
|
64
|
+
|
|
65
|
+
## Custom providers
|
|
66
|
+
|
|
67
|
+
Preserve the complete `issuer` field passed to `saveTokens()` and
|
|
68
|
+
`saveClientInformation()`, and return it from the corresponding getters. Add
|
|
69
|
+
an independently verified issuer to pre-registered client information. The
|
|
70
|
+
upstream bundled `ClientCredentialsProvider`, `PrivateKeyJwtProvider`,
|
|
71
|
+
`StaticPrivateKeyJwtProvider`, and `CrossAppAccessProvider` also need
|
|
72
|
+
`expectedIssuer` configured. These are adopter-owned provider settings; a
|
|
73
|
+
dependency upgrade cannot reconstruct the ownership of older credentials.
|
|
74
|
+
|
|
75
|
+
Calls made directly to the upstream `exchangeAuthorization()` and
|
|
76
|
+
`refreshAuthorization()` helpers need their own credential-issuer checks.
|
|
77
|
+
The SDK's web-flow and diagnosis helpers perform those checks. The SDK's
|
|
78
|
+
client-credentials flow uses an explicitly configured token endpoint rather
|
|
79
|
+
than selecting one from MCP server discovery.
|
|
80
|
+
|
|
81
|
+
If affected credentials were sent to an untrusted authorization server,
|
|
82
|
+
follow the upstream advisory's credential-rotation guidance.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adcp/sdk",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.4.0",
|
|
4
4
|
"description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -713,10 +713,10 @@
|
|
|
713
713
|
"email": "bugs@adcontextprotocol.org"
|
|
714
714
|
},
|
|
715
715
|
"dependencies": {
|
|
716
|
-
"@modelcontextprotocol/client": "2.
|
|
716
|
+
"@modelcontextprotocol/client": "2.2.0",
|
|
717
717
|
"@modelcontextprotocol/ext-apps": "2.0.0",
|
|
718
718
|
"@modelcontextprotocol/node": "2.0.0",
|
|
719
|
-
"@modelcontextprotocol/server": "2.
|
|
719
|
+
"@modelcontextprotocol/server": "2.2.0",
|
|
720
720
|
"@types/ws": "^8.18.1",
|
|
721
721
|
"ajv": "^8.20.0",
|
|
722
722
|
"ajv-formats": "^3.0.1",
|
|
@@ -733,7 +733,7 @@
|
|
|
733
733
|
},
|
|
734
734
|
"peerDependencies": {
|
|
735
735
|
"@a2a-js/sdk": "^1.0.1",
|
|
736
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
736
|
+
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
737
737
|
"@opentelemetry/api": "^1.0.0",
|
|
738
738
|
"redis": "^4.6.0 || ^5.0.0 || ^6.0.0",
|
|
739
739
|
"zod": "^4.1.5"
|
|
@@ -753,7 +753,7 @@
|
|
|
753
753
|
"@changesets/cli": "^3.0.3",
|
|
754
754
|
"@commitlint/cli": "^20.5.3",
|
|
755
755
|
"@commitlint/config-conventional": "^20.5.3",
|
|
756
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
756
|
+
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
757
757
|
"@opentelemetry/api": "^1.9.1",
|
|
758
758
|
"@types/express": "4.17.25",
|
|
759
759
|
"@types/node": "^20.19.43",
|