@ai-sdk/mcp 1.0.73 → 1.0.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/index.js +46 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/tool/mcp-client.ts +11 -2
- package/src/tool/oauth.ts +61 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.75",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"cross-spawn": "^7.0.6",
|
|
36
36
|
"pkce-challenge": "^5.0.0",
|
|
37
37
|
"@ai-sdk/provider": "3.0.15",
|
|
38
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
38
|
+
"@ai-sdk/provider-utils": "4.0.48"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/cross-spawn": "^6.0.6",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"scripts": {
|
|
73
73
|
"build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
|
|
74
74
|
"build:watch": "pnpm clean && tsup --watch",
|
|
75
|
-
"clean": "
|
|
75
|
+
"clean": "del-cli dist *.tsbuildinfo",
|
|
76
76
|
"type-check": "tsc --build",
|
|
77
77
|
"test": "pnpm test:node && pnpm test:edge",
|
|
78
78
|
"test:update": "pnpm test:node -u",
|
package/src/tool/mcp-client.ts
CHANGED
|
@@ -903,8 +903,17 @@ class DefaultMCPClient implements MCPClient {
|
|
|
903
903
|
}: {
|
|
904
904
|
schemas?: TOOL_SCHEMAS;
|
|
905
905
|
} = {}): Promise<McpToolSet<TOOL_SCHEMAS>> {
|
|
906
|
-
|
|
907
|
-
|
|
906
|
+
let definitions = await this.listTools();
|
|
907
|
+
const tools = [...definitions.tools];
|
|
908
|
+
|
|
909
|
+
while (definitions.nextCursor != null) {
|
|
910
|
+
definitions = await this.listTools({
|
|
911
|
+
params: { cursor: definitions.nextCursor },
|
|
912
|
+
});
|
|
913
|
+
tools.push(...definitions.tools);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
return this.toolsFromDefinitions({ ...definitions, tools }, {
|
|
908
917
|
schemas,
|
|
909
918
|
} as { schemas?: TOOL_SCHEMAS });
|
|
910
919
|
}
|
package/src/tool/oauth.ts
CHANGED
|
@@ -27,8 +27,11 @@ import {
|
|
|
27
27
|
resourceUrlStripSlash,
|
|
28
28
|
} from '../util/oauth-util';
|
|
29
29
|
import { LATEST_PROTOCOL_VERSION } from './types';
|
|
30
|
-
import {
|
|
31
|
-
|
|
30
|
+
import {
|
|
31
|
+
parseJSON,
|
|
32
|
+
validateDownloadUrl,
|
|
33
|
+
type FetchFunction,
|
|
34
|
+
} from '@ai-sdk/provider-utils';
|
|
32
35
|
export type AuthResult = 'AUTHORIZED' | 'REDIRECT';
|
|
33
36
|
|
|
34
37
|
export interface OAuthAuthorizationServerInformation {
|
|
@@ -123,6 +126,45 @@ function normalizeUrl(url: string | URL): string {
|
|
|
123
126
|
return new URL(url).href;
|
|
124
127
|
}
|
|
125
128
|
|
|
129
|
+
/** Allow loopback HTTP(S) for local MCP OAuth (RFC 8252 §7.3, RFC 6761 §6.3). */
|
|
130
|
+
function isOAuthLoopbackHost(hostname: string): boolean {
|
|
131
|
+
const normalized = hostname.toLowerCase().replace(/\.+$/, '');
|
|
132
|
+
return (
|
|
133
|
+
normalized === 'localhost' ||
|
|
134
|
+
normalized.endsWith('.localhost') ||
|
|
135
|
+
normalized === '127.0.0.1' ||
|
|
136
|
+
normalized === '[::1]' ||
|
|
137
|
+
normalized === '::1'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Guards metadata-derived token/registration URLs before credentials are sent.
|
|
143
|
+
* Loopback is allowed for local OAuth; every other target uses the shared
|
|
144
|
+
* download URL guard (http(s) only, no private/link-local IPs).
|
|
145
|
+
*
|
|
146
|
+
* Credential POSTs use `redirect: 'error'` instead of
|
|
147
|
+
* `fetchWithValidatedRedirects`, which is GET-only and would follow hops with
|
|
148
|
+
* the authorization code, PKCE verifier, and client secret still attached.
|
|
149
|
+
*/
|
|
150
|
+
function assertSafeOAuthEndpoint(endpointUrl: URL): void {
|
|
151
|
+
if (
|
|
152
|
+
(endpointUrl.protocol === 'http:' || endpointUrl.protocol === 'https:') &&
|
|
153
|
+
isOAuthLoopbackHost(endpointUrl.hostname)
|
|
154
|
+
) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
validateDownloadUrl(endpointUrl.href);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new MCPClientOAuthError({
|
|
162
|
+
message: `OAuth endpoint URL is not allowed: ${endpointUrl.href}`,
|
|
163
|
+
cause: error,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
126
168
|
function createAuthorizationServerInformation(
|
|
127
169
|
authorizationServerUrl: string | URL,
|
|
128
170
|
metadata?: AuthorizationServerMetadata,
|
|
@@ -872,6 +914,7 @@ export async function exchangeAuthorization(
|
|
|
872
914
|
const tokenUrl = metadata?.token_endpoint
|
|
873
915
|
? new URL(metadata.token_endpoint)
|
|
874
916
|
: new URL('/token', authorizationServerUrl);
|
|
917
|
+
assertSafeOAuthEndpoint(tokenUrl);
|
|
875
918
|
|
|
876
919
|
if (
|
|
877
920
|
metadata?.grant_types_supported &&
|
|
@@ -919,6 +962,7 @@ export async function exchangeAuthorization(
|
|
|
919
962
|
method: 'POST',
|
|
920
963
|
headers,
|
|
921
964
|
body: params,
|
|
965
|
+
redirect: 'error',
|
|
922
966
|
});
|
|
923
967
|
|
|
924
968
|
if (!response.ok) {
|
|
@@ -975,6 +1019,7 @@ export async function refreshAuthorization(
|
|
|
975
1019
|
} else {
|
|
976
1020
|
tokenUrl = new URL('/token', authorizationServerUrl);
|
|
977
1021
|
}
|
|
1022
|
+
assertSafeOAuthEndpoint(tokenUrl);
|
|
978
1023
|
|
|
979
1024
|
const headers = new Headers({
|
|
980
1025
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
@@ -1011,6 +1056,7 @@ export async function refreshAuthorization(
|
|
|
1011
1056
|
method: 'POST',
|
|
1012
1057
|
headers,
|
|
1013
1058
|
body: params,
|
|
1059
|
+
redirect: 'error',
|
|
1014
1060
|
});
|
|
1015
1061
|
if (!response.ok) {
|
|
1016
1062
|
throw await parseErrorResponse(response);
|
|
@@ -1050,6 +1096,7 @@ export async function registerClient(
|
|
|
1050
1096
|
} else {
|
|
1051
1097
|
registrationUrl = new URL('/register', authorizationServerUrl);
|
|
1052
1098
|
}
|
|
1099
|
+
assertSafeOAuthEndpoint(registrationUrl);
|
|
1053
1100
|
|
|
1054
1101
|
const response = await (fetchFn ?? fetch)(registrationUrl, {
|
|
1055
1102
|
method: 'POST',
|
|
@@ -1057,6 +1104,7 @@ export async function registerClient(
|
|
|
1057
1104
|
'Content-Type': 'application/json',
|
|
1058
1105
|
},
|
|
1059
1106
|
body: JSON.stringify(clientMetadata),
|
|
1107
|
+
redirect: 'error',
|
|
1060
1108
|
});
|
|
1061
1109
|
|
|
1062
1110
|
if (!response.ok) {
|
|
@@ -1192,6 +1240,12 @@ async function authInternal(
|
|
|
1192
1240
|
);
|
|
1193
1241
|
const currentAuthorizationServerInformation =
|
|
1194
1242
|
createAuthorizationServerInformation(authorizationServerUrl, metadata);
|
|
1243
|
+
const clientMetadata = provider.clientMetadata;
|
|
1244
|
+
const selectedScope = selectScope({
|
|
1245
|
+
scope,
|
|
1246
|
+
resourceMetadata,
|
|
1247
|
+
clientMetadata,
|
|
1248
|
+
});
|
|
1195
1249
|
|
|
1196
1250
|
/** Load or register client credentials with the AS pin attached. */
|
|
1197
1251
|
let clientInformation = await Promise.resolve(provider.clientInformation());
|
|
@@ -1210,7 +1264,10 @@ async function authInternal(
|
|
|
1210
1264
|
|
|
1211
1265
|
const fullInformation = await registerClient(authorizationServerUrl, {
|
|
1212
1266
|
metadata,
|
|
1213
|
-
clientMetadata:
|
|
1267
|
+
clientMetadata: {
|
|
1268
|
+
...clientMetadata,
|
|
1269
|
+
scope: selectedScope,
|
|
1270
|
+
},
|
|
1214
1271
|
fetchFn,
|
|
1215
1272
|
});
|
|
1216
1273
|
|
|
@@ -1337,11 +1394,7 @@ async function authInternal(
|
|
|
1337
1394
|
clientInformation,
|
|
1338
1395
|
state,
|
|
1339
1396
|
redirectUrl: provider.redirectUrl,
|
|
1340
|
-
scope:
|
|
1341
|
-
scope,
|
|
1342
|
-
resourceMetadata,
|
|
1343
|
-
clientMetadata: provider.clientMetadata,
|
|
1344
|
-
}),
|
|
1397
|
+
scope: selectedScope,
|
|
1345
1398
|
resource,
|
|
1346
1399
|
},
|
|
1347
1400
|
);
|