tiny-http-mcp-server 0.1.20 → 0.1.22
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/dist/composition.json +1 -1
- package/node_modules/mcp-oauth/README.md +22 -1
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +27 -13
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +5 -0
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +134 -72
- package/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +2 -0
- package/node_modules/mcp-oauth/dist/client/token-endpoint.js +5 -1
- package/node_modules/mcp-oauth/dist/client/types.d.ts +9 -1
- package/node_modules/tiny-mcp-client/README.md +5 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +18 -3
- package/node_modules/tiny-mcp-client/dist/index.js +243 -117
- package/package.json +1 -1
|
@@ -4102,47 +4102,137 @@ function getOwnEntry4(record2, key2) {
|
|
|
4102
4102
|
|
|
4103
4103
|
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
4104
4104
|
async function createLoopbackAuthorizationSession(options = {}) {
|
|
4105
|
-
|
|
4105
|
+
options.signal?.throwIfAborted();
|
|
4106
|
+
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
4107
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
4108
|
+
throw new Error("OAuth authorization timeoutMs must be a positive supported timer interval");
|
|
4109
|
+
const target = loopbackTarget(options);
|
|
4106
4110
|
const server = options.createServer ? options.createServer() : http.createServer();
|
|
4107
|
-
const
|
|
4108
|
-
|
|
4111
|
+
const controller = new AbortController();
|
|
4112
|
+
let closed = false;
|
|
4113
|
+
let used = false;
|
|
4114
|
+
const callerAbort = () => controller.abort(options.signal?.reason);
|
|
4115
|
+
const teardown = () => {
|
|
4116
|
+
if (closed)
|
|
4117
|
+
return;
|
|
4118
|
+
closed = true;
|
|
4119
|
+
clearTimeout(timer);
|
|
4120
|
+
options.signal?.removeEventListener("abort", callerAbort);
|
|
4121
|
+
server.closeAllConnections?.();
|
|
4122
|
+
server.close();
|
|
4123
|
+
};
|
|
4124
|
+
const timer = setTimeout(() => controller.abort(new Error("OAuth authorization timed out")), timeoutMs);
|
|
4125
|
+
timer.unref?.();
|
|
4126
|
+
controller.signal.addEventListener("abort", teardown, { once: true });
|
|
4127
|
+
options.signal?.addEventListener("abort", callerAbort, { once: true });
|
|
4128
|
+
if (options.signal?.aborted)
|
|
4129
|
+
callerAbort();
|
|
4130
|
+
let port;
|
|
4131
|
+
try {
|
|
4132
|
+
port = await startServer(server, target.port, target.host, controller.signal);
|
|
4133
|
+
} catch (error) {
|
|
4134
|
+
controller.abort(error);
|
|
4135
|
+
throw error;
|
|
4136
|
+
}
|
|
4137
|
+
const redirectUri = options.redirectUri ?? `http://127.0.0.1:${port}${target.callbackPath}`;
|
|
4109
4138
|
return {
|
|
4110
4139
|
redirectUri,
|
|
4111
4140
|
async waitForCode(authorizationUrl) {
|
|
4112
|
-
|
|
4141
|
+
controller.signal.throwIfAborted();
|
|
4142
|
+
if (used)
|
|
4143
|
+
throw new Error("OAuth authorization session has already been used");
|
|
4144
|
+
used = true;
|
|
4145
|
+
try {
|
|
4146
|
+
return await waitForAuthorizationCode(server, authorizationUrl, options, target.callbackPath, controller.signal);
|
|
4147
|
+
} finally {
|
|
4148
|
+
clearTimeout(timer);
|
|
4149
|
+
}
|
|
4113
4150
|
},
|
|
4114
4151
|
close() {
|
|
4115
|
-
|
|
4116
|
-
server.close();
|
|
4152
|
+
controller.abort(new Error("OAuth authorization session closed"));
|
|
4117
4153
|
}
|
|
4118
4154
|
};
|
|
4119
4155
|
}
|
|
4120
|
-
|
|
4156
|
+
function loopbackTarget(options) {
|
|
4157
|
+
if (options.redirectUri !== void 0) {
|
|
4158
|
+
let url;
|
|
4159
|
+
try {
|
|
4160
|
+
url = new URL(options.redirectUri);
|
|
4161
|
+
} catch (cause) {
|
|
4162
|
+
throw new Error("Invalid OAuth loopback redirect URI", { cause });
|
|
4163
|
+
}
|
|
4164
|
+
const forbiddenQuery = ["code", "state", "error", "error_description", "iss"];
|
|
4165
|
+
if (url.protocol !== "http:" || !["localhost", "127.0.0.1", "[::1]"].includes(url.hostname) || url.username || url.password || url.hash || url.port === "0" || forbiddenQuery.some((name) => url.searchParams.has(name)) || [...options.redirectUri].some((char) => char.codePointAt(0) <= 32) || options.callbackPath !== void 0 && options.callbackPath !== url.pathname)
|
|
4166
|
+
throw new Error("Invalid OAuth loopback redirect URI");
|
|
4167
|
+
return { port: url.port ? Number(url.port) : 80, host: url.hostname === "[::1]" ? "::1" : url.hostname, callbackPath: url.pathname };
|
|
4168
|
+
}
|
|
4169
|
+
const callbackPath = options.callbackPath ?? "/callback";
|
|
4170
|
+
const parsed = new URL(callbackPath, "http://127.0.0.1");
|
|
4171
|
+
if (!callbackPath.startsWith("/") || parsed.origin !== "http://127.0.0.1" || parsed.pathname !== callbackPath || parsed.search || parsed.hash)
|
|
4172
|
+
throw new Error("Invalid OAuth loopback callback path");
|
|
4173
|
+
return { port: 0, host: "127.0.0.1", callbackPath };
|
|
4174
|
+
}
|
|
4175
|
+
async function startServer(server, port, host, signal) {
|
|
4176
|
+
signal.throwIfAborted();
|
|
4121
4177
|
return new Promise((resolve, reject) => {
|
|
4122
|
-
const
|
|
4178
|
+
const cleanup = () => {
|
|
4123
4179
|
server.off("error", handleError);
|
|
4180
|
+
signal.removeEventListener("abort", aborted);
|
|
4181
|
+
};
|
|
4182
|
+
const handleError = (error) => {
|
|
4183
|
+
cleanup();
|
|
4124
4184
|
reject(error);
|
|
4125
4185
|
};
|
|
4186
|
+
const aborted = () => {
|
|
4187
|
+
cleanup();
|
|
4188
|
+
reject(signal.reason);
|
|
4189
|
+
};
|
|
4126
4190
|
server.once("error", handleError);
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4191
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
4192
|
+
try {
|
|
4193
|
+
server.listen(port, host, () => {
|
|
4194
|
+
cleanup();
|
|
4195
|
+
if (signal.aborted) {
|
|
4196
|
+
server.close();
|
|
4197
|
+
reject(signal.reason);
|
|
4198
|
+
return;
|
|
4199
|
+
}
|
|
4200
|
+
const address = server.address();
|
|
4201
|
+
if (address === null || typeof address === "string") {
|
|
4202
|
+
reject(new Error("OAuth listener has no TCP address"));
|
|
4203
|
+
return;
|
|
4204
|
+
}
|
|
4205
|
+
resolve(address.port);
|
|
4206
|
+
});
|
|
4207
|
+
} catch (error) {
|
|
4208
|
+
cleanup();
|
|
4209
|
+
reject(error);
|
|
4210
|
+
}
|
|
4132
4211
|
});
|
|
4133
4212
|
}
|
|
4134
|
-
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath) {
|
|
4213
|
+
function waitForAuthorizationCode(server, authorizationUrl, options, callbackPath, signal) {
|
|
4214
|
+
signal.throwIfAborted();
|
|
4135
4215
|
const expectedAuthorization = readExpectedAuthorizationCallback(authorizationUrl);
|
|
4136
4216
|
return new Promise((resolve, reject) => {
|
|
4137
4217
|
let settled = false;
|
|
4138
4218
|
const settle = (fn) => {
|
|
4139
|
-
if (
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4219
|
+
if (settled)
|
|
4220
|
+
return;
|
|
4221
|
+
settled = true;
|
|
4222
|
+
server.off("request", request);
|
|
4223
|
+
signal.removeEventListener("abort", aborted);
|
|
4224
|
+
fn();
|
|
4143
4225
|
};
|
|
4144
|
-
|
|
4145
|
-
|
|
4226
|
+
const aborted = () => settle(() => reject(signal.reason));
|
|
4227
|
+
const request = (req, res) => {
|
|
4228
|
+
let url;
|
|
4229
|
+
try {
|
|
4230
|
+
url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
4231
|
+
} catch {
|
|
4232
|
+
res.writeHead(400);
|
|
4233
|
+
res.end("Invalid callback URL");
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4146
4236
|
if (url.pathname !== callbackPath) {
|
|
4147
4237
|
res.writeHead(404);
|
|
4148
4238
|
res.end("Not found");
|
|
@@ -4157,21 +4247,8 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
4157
4247
|
};
|
|
4158
4248
|
try {
|
|
4159
4249
|
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
4163
|
-
settle(() => reject(error instanceof Error ? error : new Error(String(error))));
|
|
4164
|
-
return;
|
|
4165
|
-
}
|
|
4166
|
-
const authorizationError = callbackParameters.error;
|
|
4167
|
-
if (authorizationError !== null) {
|
|
4168
|
-
const description = callbackParameters.errorDescription ?? authorizationError;
|
|
4169
|
-
res.writeHead(400);
|
|
4170
|
-
res.end(`Authorization failed: ${description}`);
|
|
4171
|
-
settle(() => reject(createAuthorizationError(authorizationError, description)));
|
|
4172
|
-
return;
|
|
4173
|
-
}
|
|
4174
|
-
try {
|
|
4250
|
+
if (callbackParameters.error !== null)
|
|
4251
|
+
throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
4175
4252
|
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
4176
4253
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4177
4254
|
res.end(buildSuccessPage(options.landingPage));
|
|
@@ -4179,35 +4256,27 @@ function waitForAuthorizationCode(server, authorizationUrl, options, callbackPat
|
|
|
4179
4256
|
} catch (error) {
|
|
4180
4257
|
res.writeHead(400);
|
|
4181
4258
|
res.end(error instanceof Error ? error.message : "Invalid OAuth callback");
|
|
4182
|
-
settle(() => reject(error
|
|
4259
|
+
settle(() => reject(error));
|
|
4183
4260
|
}
|
|
4184
|
-
}
|
|
4261
|
+
};
|
|
4262
|
+
server.on("request", request);
|
|
4263
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
4185
4264
|
if (options.readLine !== void 0) {
|
|
4186
|
-
options.readLine().then((input) => {
|
|
4187
|
-
|
|
4188
|
-
if (callbackParameters === null) {
|
|
4189
|
-
settle(() => reject(new Error("OAuth callback missing authorization code")));
|
|
4265
|
+
void Promise.resolve().then(() => settled ? void 0 : options.readLine()).then((input) => {
|
|
4266
|
+
if (settled)
|
|
4190
4267
|
return;
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
} catch (error) {
|
|
4201
|
-
settle(() => reject(error instanceof Error ? error : new Error(String(error))));
|
|
4202
|
-
}
|
|
4203
|
-
}).catch((error) => {
|
|
4204
|
-
settle(() => reject(error instanceof Error ? error : new Error(String(error))));
|
|
4205
|
-
});
|
|
4268
|
+
const callbackParameters = extractCallbackParametersFromInput(input);
|
|
4269
|
+
if (callbackParameters === null)
|
|
4270
|
+
throw new Error("OAuth callback missing authorization code");
|
|
4271
|
+
validateAuthorizationCallbackBinding(callbackParameters, expectedAuthorization);
|
|
4272
|
+
if (callbackParameters.error !== null)
|
|
4273
|
+
throw createAuthorizationError(callbackParameters.error, callbackParameters.errorDescription ?? callbackParameters.error);
|
|
4274
|
+
const code = validateAuthorizationCallbackParameters(callbackParameters, expectedAuthorization);
|
|
4275
|
+
settle(() => resolve(code));
|
|
4276
|
+
}).catch((error) => settle(() => reject(error)));
|
|
4206
4277
|
}
|
|
4207
4278
|
if (options.openBrowser !== void 0) {
|
|
4208
|
-
options.openBrowser(authorizationUrl).catch((error) =>
|
|
4209
|
-
settle(() => reject(error));
|
|
4210
|
-
});
|
|
4279
|
+
void Promise.resolve().then(() => settled ? void 0 : options.openBrowser(authorizationUrl)).catch((error) => settle(() => reject(error)));
|
|
4211
4280
|
}
|
|
4212
4281
|
});
|
|
4213
4282
|
}
|
|
@@ -4387,6 +4456,7 @@ async function exchangeAuthorizationCode(input) {
|
|
|
4387
4456
|
resource
|
|
4388
4457
|
},
|
|
4389
4458
|
fetch: input.fetch,
|
|
4459
|
+
signal: input.signal,
|
|
4390
4460
|
now: input.now
|
|
4391
4461
|
});
|
|
4392
4462
|
}
|
|
@@ -4402,6 +4472,7 @@ async function refreshAccessToken(input) {
|
|
|
4402
4472
|
resource
|
|
4403
4473
|
},
|
|
4404
4474
|
fetch: input.fetch,
|
|
4475
|
+
signal: input.signal,
|
|
4405
4476
|
now: input.now
|
|
4406
4477
|
});
|
|
4407
4478
|
}
|
|
@@ -4413,7 +4484,9 @@ async function requestTokens(input) {
|
|
|
4413
4484
|
if (input.clientSecret !== void 0) {
|
|
4414
4485
|
body.set("client_secret", input.clientSecret);
|
|
4415
4486
|
}
|
|
4416
|
-
|
|
4487
|
+
input.signal?.throwIfAborted();
|
|
4488
|
+
const deadline = AbortSignal.timeout(3e4);
|
|
4489
|
+
const signal = input.signal === void 0 ? deadline : AbortSignal.any([input.signal, deadline]);
|
|
4417
4490
|
const response = await fetchMcpResponse(input.fetch, input.tokenEndpoint, {
|
|
4418
4491
|
method: "POST",
|
|
4419
4492
|
headers: {
|
|
@@ -4525,7 +4598,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4525
4598
|
async authorizeRequest(input) {
|
|
4526
4599
|
assertNoAccessTokenInUrl(input.requestUrl, "Protected resource request URL");
|
|
4527
4600
|
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
4528
|
-
const session = await ensureAuthorizedSession(requestUrl, void 0, input.fetch, false);
|
|
4601
|
+
const session = await ensureAuthorizedSession(requestUrl, void 0, input.fetch, false, false, input.signal);
|
|
4529
4602
|
const accessToken = session?.tokens?.accessToken;
|
|
4530
4603
|
if (session === null || accessToken === void 0 || session.tokens === void 0 || isExpired(session.tokens, now)) {
|
|
4531
4604
|
return;
|
|
@@ -4543,12 +4616,13 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4543
4616
|
const session = await ensureAuthorizedSession(resource, {
|
|
4544
4617
|
...input.discovery,
|
|
4545
4618
|
resource
|
|
4546
|
-
}, input.fetch, true, forceRefresh);
|
|
4619
|
+
}, input.fetch, true, forceRefresh, input.signal);
|
|
4547
4620
|
if (session?.tokens?.accessToken === void 0) {
|
|
4548
4621
|
return { action: "fail" };
|
|
4549
4622
|
}
|
|
4550
4623
|
return { action: "retry" };
|
|
4551
4624
|
} catch (error) {
|
|
4625
|
+
input.signal?.throwIfAborted();
|
|
4552
4626
|
return {
|
|
4553
4627
|
action: "fail",
|
|
4554
4628
|
error: error instanceof Error ? error : new Error(String(error))
|
|
@@ -4556,9 +4630,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4556
4630
|
}
|
|
4557
4631
|
}
|
|
4558
4632
|
};
|
|
4559
|
-
async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false) {
|
|
4633
|
+
async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false, signal) {
|
|
4634
|
+
signal?.throwIfAborted();
|
|
4560
4635
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
4561
4636
|
let session = await loadSession(canonicalResource);
|
|
4637
|
+
signal?.throwIfAborted();
|
|
4562
4638
|
if (discovery !== void 0 && getOwnString2(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer) {
|
|
4563
4639
|
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
4564
4640
|
}
|
|
@@ -4571,7 +4647,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4571
4647
|
return session;
|
|
4572
4648
|
}
|
|
4573
4649
|
if (session?.tokens?.refreshToken !== void 0 && sessionDiscovery !== void 0 && (forceRefresh || isExpired(session.tokens, now))) {
|
|
4574
|
-
session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch2);
|
|
4650
|
+
session = await refreshSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
|
|
4575
4651
|
if (session?.tokens !== void 0 && !isExpired(session.tokens, now)) {
|
|
4576
4652
|
return session;
|
|
4577
4653
|
}
|
|
@@ -4583,9 +4659,12 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4583
4659
|
if (!allowInteractive || sessionDiscovery === void 0) {
|
|
4584
4660
|
return session;
|
|
4585
4661
|
}
|
|
4586
|
-
|
|
4662
|
+
if (options.allowInteractive === false)
|
|
4663
|
+
throw new Error("OAuth interactive authorization is disabled");
|
|
4664
|
+
return authorizeSession(canonicalResource, session, sessionDiscovery, fetch2, signal);
|
|
4587
4665
|
}
|
|
4588
|
-
async function refreshSession(resource, session, discovery, fetch2) {
|
|
4666
|
+
async function refreshSession(resource, session, discovery, fetch2, signal) {
|
|
4667
|
+
signal?.throwIfAborted();
|
|
4589
4668
|
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
4590
4669
|
const inFlight = refreshPromises.get(resource);
|
|
4591
4670
|
if (inFlight !== void 0) {
|
|
@@ -4607,10 +4686,12 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4607
4686
|
refreshToken: session.tokens.refreshToken,
|
|
4608
4687
|
resource,
|
|
4609
4688
|
fetch: fetch2,
|
|
4689
|
+
signal,
|
|
4610
4690
|
now
|
|
4611
4691
|
});
|
|
4612
4692
|
break;
|
|
4613
4693
|
} catch (error) {
|
|
4694
|
+
signal?.throwIfAborted();
|
|
4614
4695
|
if (error instanceof OAuthError && error.error === "invalid_grant") {
|
|
4615
4696
|
const clearedSession = clearSessionTokens(session);
|
|
4616
4697
|
await saveSession(resource, clearedSession);
|
|
@@ -4645,7 +4726,8 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4645
4726
|
refreshPromises.set(resource, promise);
|
|
4646
4727
|
return promise;
|
|
4647
4728
|
}
|
|
4648
|
-
async function authorizeSession(resource, existingSession, discovery, fetch2) {
|
|
4729
|
+
async function authorizeSession(resource, existingSession, discovery, fetch2, signal) {
|
|
4730
|
+
signal?.throwIfAborted();
|
|
4649
4731
|
const inFlight = authorizationPromises.get(resource);
|
|
4650
4732
|
if (inFlight !== void 0) {
|
|
4651
4733
|
return inFlight;
|
|
@@ -4661,11 +4743,14 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4661
4743
|
openBrowser: options.browser.openBrowser,
|
|
4662
4744
|
readLine: options.browser.readLine,
|
|
4663
4745
|
createServer: options.browser.createServer,
|
|
4664
|
-
landingPage: options.browser.landingPage
|
|
4746
|
+
landingPage: options.browser.landingPage,
|
|
4747
|
+
redirectUri: options.browser.redirectUri,
|
|
4748
|
+
signal: options.browser.signal === void 0 ? signal : signal === void 0 ? options.browser.signal : AbortSignal.any([signal, options.browser.signal]),
|
|
4749
|
+
timeoutMs: options.browser.timeoutMs
|
|
4665
4750
|
});
|
|
4666
4751
|
let resolvedClient = null;
|
|
4667
4752
|
try {
|
|
4668
|
-
resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch2);
|
|
4753
|
+
resolvedClient = await resolveClient(currentSession, discovery, loopback.redirectUri, fetch2, signal);
|
|
4669
4754
|
const sessionWithoutTokens = {
|
|
4670
4755
|
resource,
|
|
4671
4756
|
authorizationServer: discovery.authorizationServer,
|
|
@@ -4693,6 +4778,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4693
4778
|
redirectUri: loopback.redirectUri,
|
|
4694
4779
|
resource,
|
|
4695
4780
|
fetch: fetch2,
|
|
4781
|
+
signal,
|
|
4696
4782
|
now
|
|
4697
4783
|
});
|
|
4698
4784
|
const session = {
|
|
@@ -4702,6 +4788,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4702
4788
|
await saveSession(resource, session);
|
|
4703
4789
|
return session;
|
|
4704
4790
|
} catch (error) {
|
|
4791
|
+
signal?.throwIfAborted();
|
|
4705
4792
|
if (shouldReRegisterStoredDynamicClient(error, resolvedClient, reRegistrationAttempted)) {
|
|
4706
4793
|
reRegistrationAttempted = true;
|
|
4707
4794
|
await clearRegisteredClient(discovery.authorizationServer);
|
|
@@ -4727,7 +4814,8 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4727
4814
|
authorizationPromises.set(resource, finalPromise);
|
|
4728
4815
|
return finalPromise;
|
|
4729
4816
|
}
|
|
4730
|
-
async function resolveClient(existingSession, discovery, redirectUri, fetch2) {
|
|
4817
|
+
async function resolveClient(existingSession, discovery, redirectUri, fetch2, parentSignal) {
|
|
4818
|
+
parentSignal?.throwIfAborted();
|
|
4731
4819
|
const configuredClient = normalizeConfiguredClient(options.client);
|
|
4732
4820
|
if (options.client.mode === "static") {
|
|
4733
4821
|
if (configuredClient === null) {
|
|
@@ -4777,7 +4865,8 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4777
4865
|
}
|
|
4778
4866
|
}
|
|
4779
4867
|
const registrationBody = buildClientRegistrationBody(getClientMetadata(options.client), redirectUri);
|
|
4780
|
-
const
|
|
4868
|
+
const deadline = AbortSignal.timeout(3e4);
|
|
4869
|
+
const signal = parentSignal === void 0 ? deadline : AbortSignal.any([parentSignal, deadline]);
|
|
4781
4870
|
const response = await fetchMcpResponse(fetch2, registrationEndpoint, {
|
|
4782
4871
|
method: "POST",
|
|
4783
4872
|
headers: {
|
|
@@ -5340,8 +5429,10 @@ async function readJsonResponse(response, label, signal) {
|
|
|
5340
5429
|
throw new Error(`${label} response must be valid JSON`);
|
|
5341
5430
|
}
|
|
5342
5431
|
}
|
|
5343
|
-
async function fetchMetadata(fetch2, location, label) {
|
|
5344
|
-
|
|
5432
|
+
async function fetchMetadata(fetch2, location, label, parentSignal) {
|
|
5433
|
+
parentSignal?.throwIfAborted();
|
|
5434
|
+
const deadline = AbortSignal.timeout(1e4);
|
|
5435
|
+
const signal = parentSignal === void 0 ? deadline : AbortSignal.any([deadline, parentSignal]);
|
|
5345
5436
|
const response = await fetchMcpResponse(fetch2, location, {
|
|
5346
5437
|
method: "GET",
|
|
5347
5438
|
headers: { Accept: "application/json" },
|
|
@@ -5443,7 +5534,7 @@ var OAuthMetadataDiscovery = class {
|
|
|
5443
5534
|
this.fetchImpl = fetch2;
|
|
5444
5535
|
this.cache = cache;
|
|
5445
5536
|
}
|
|
5446
|
-
async discoverProtectedResource(resource, resourceMetadataUrl) {
|
|
5537
|
+
async discoverProtectedResource(resource, resourceMetadataUrl, signal) {
|
|
5447
5538
|
const locations = /* @__PURE__ */ new Set([resolveProtectedResourceMetadataUrl(resource, resourceMetadataUrl)]);
|
|
5448
5539
|
if (resourceMetadataUrl === void 0) {
|
|
5449
5540
|
locations.add(new URL("/.well-known/oauth-protected-resource", resource).toString());
|
|
@@ -5452,17 +5543,19 @@ var OAuthMetadataDiscovery = class {
|
|
|
5452
5543
|
for (const location of locations) {
|
|
5453
5544
|
try {
|
|
5454
5545
|
const metadata = validateProtectedResourceMetadata(
|
|
5455
|
-
await fetchMetadata(this.fetchImpl, location, "Protected resource metadata"),
|
|
5546
|
+
await fetchMetadata(this.fetchImpl, location, "Protected resource metadata", signal),
|
|
5456
5547
|
resource
|
|
5457
5548
|
);
|
|
5458
5549
|
return { location, metadata };
|
|
5459
5550
|
} catch (error) {
|
|
5551
|
+
signal?.throwIfAborted();
|
|
5460
5552
|
lastError = error;
|
|
5461
5553
|
}
|
|
5462
5554
|
}
|
|
5463
5555
|
throw lastError;
|
|
5464
5556
|
}
|
|
5465
|
-
async discover(resourceUrl, { resourceMetadataUrl } = {}) {
|
|
5557
|
+
async discover(resourceUrl, { resourceMetadataUrl, signal } = {}) {
|
|
5558
|
+
signal?.throwIfAborted();
|
|
5466
5559
|
const cacheKey = canonicalizeResourceIndicator(resourceUrl);
|
|
5467
5560
|
resolveProtectedResourceMetadataUrl(cacheKey, resourceMetadataUrl);
|
|
5468
5561
|
const memoryCachedResult = this.memoryCache.get(cacheKey);
|
|
@@ -5470,6 +5563,7 @@ var OAuthMetadataDiscovery = class {
|
|
|
5470
5563
|
return structuredClone(memoryCachedResult);
|
|
5471
5564
|
}
|
|
5472
5565
|
const sharedCachedResult = await this.cache?.get(cacheKey);
|
|
5566
|
+
signal?.throwIfAborted();
|
|
5473
5567
|
if (sharedCachedResult !== null && sharedCachedResult !== void 0 && resourceMetadataUrl === void 0) {
|
|
5474
5568
|
try {
|
|
5475
5569
|
const result = validateCachedDiscovery(sharedCachedResult, cacheKey);
|
|
@@ -5479,7 +5573,7 @@ var OAuthMetadataDiscovery = class {
|
|
|
5479
5573
|
await this.cache?.delete?.(cacheKey);
|
|
5480
5574
|
}
|
|
5481
5575
|
}
|
|
5482
|
-
const { location: resourceMetadataLocation, metadata: resourceMetadata } = await this.discoverProtectedResource(cacheKey, resourceMetadataUrl);
|
|
5576
|
+
const { location: resourceMetadataLocation, metadata: resourceMetadata } = await this.discoverProtectedResource(cacheKey, resourceMetadataUrl, signal);
|
|
5483
5577
|
const authorizationServerErrors = [];
|
|
5484
5578
|
for (const authorizationServer of resourceMetadata.authorization_servers) {
|
|
5485
5579
|
const normalizedAuthorizationServer = validateAuthorizationServerIssuer(authorizationServer);
|
|
@@ -5487,7 +5581,7 @@ var OAuthMetadataDiscovery = class {
|
|
|
5487
5581
|
for (const authorizationServerMetadataUrl of metadataLocations) {
|
|
5488
5582
|
try {
|
|
5489
5583
|
const authorizationServerMetadata = validateAuthorizationServerMetadata(
|
|
5490
|
-
await fetchMetadata(this.fetchImpl, authorizationServerMetadataUrl, "Authorization server metadata"),
|
|
5584
|
+
await fetchMetadata(this.fetchImpl, authorizationServerMetadataUrl, "Authorization server metadata", signal),
|
|
5491
5585
|
normalizedAuthorizationServer
|
|
5492
5586
|
);
|
|
5493
5587
|
const result = {
|
|
@@ -5502,6 +5596,7 @@ var OAuthMetadataDiscovery = class {
|
|
|
5502
5596
|
await this.cache?.set(cacheKey, structuredClone(result));
|
|
5503
5597
|
return result;
|
|
5504
5598
|
} catch (error) {
|
|
5599
|
+
signal?.throwIfAborted();
|
|
5505
5600
|
authorizationServerErrors.push(
|
|
5506
5601
|
`${authorizationServerMetadataUrl}: ${error instanceof Error ? error.message : String(error)}`
|
|
5507
5602
|
);
|
|
@@ -6505,6 +6600,16 @@ async function createTestPair(server, createClient) {
|
|
|
6505
6600
|
};
|
|
6506
6601
|
return { client, cleanup };
|
|
6507
6602
|
}
|
|
6603
|
+
var HttpTransportError = class extends Error {
|
|
6604
|
+
constructor(message, status, method) {
|
|
6605
|
+
super(message);
|
|
6606
|
+
this.status = status;
|
|
6607
|
+
this.method = method;
|
|
6608
|
+
this.name = "HttpTransportError";
|
|
6609
|
+
}
|
|
6610
|
+
status;
|
|
6611
|
+
method;
|
|
6612
|
+
};
|
|
6508
6613
|
function defaultStdioSpawn(command, args, options) {
|
|
6509
6614
|
return spawn2(command, args, options);
|
|
6510
6615
|
}
|
|
@@ -6632,6 +6737,7 @@ var HttpTransport = class {
|
|
|
6632
6737
|
oauthProvider;
|
|
6633
6738
|
oauthMetadataDiscovery;
|
|
6634
6739
|
inFlightFetchAbortControllers = /* @__PURE__ */ new Set();
|
|
6740
|
+
inFlightOAuthAbortControllers = /* @__PURE__ */ new Set();
|
|
6635
6741
|
openResponseReaders = /* @__PURE__ */ new Set();
|
|
6636
6742
|
modernRequests = /* @__PURE__ */ new Map();
|
|
6637
6743
|
modernMode = false;
|
|
@@ -6701,7 +6807,7 @@ var HttpTransport = class {
|
|
|
6701
6807
|
this.rejectLegacyEndpoint = void 0;
|
|
6702
6808
|
this.resolveLegacyEndpoint = void 0;
|
|
6703
6809
|
this.toolParameterHeaders.clear();
|
|
6704
|
-
this.abortInFlightFetches();
|
|
6810
|
+
this.abortInFlightFetches(reason);
|
|
6705
6811
|
this.cancelOpenResponseReaders();
|
|
6706
6812
|
if (!this.writeStream.destroyed && !this.writeStream.writableEnded) {
|
|
6707
6813
|
this.writeStream.end();
|
|
@@ -6737,12 +6843,14 @@ var HttpTransport = class {
|
|
|
6737
6843
|
this.resolveClosed = void 0;
|
|
6738
6844
|
resolveClosed?.({ reason: closeReason });
|
|
6739
6845
|
}
|
|
6740
|
-
abortInFlightFetches() {
|
|
6741
|
-
for (const controller of this.modernRequests.values()) controller.abort();
|
|
6846
|
+
abortInFlightFetches(reason) {
|
|
6847
|
+
for (const controller of this.modernRequests.values()) controller.abort(reason);
|
|
6742
6848
|
this.modernRequests.clear();
|
|
6743
6849
|
for (const abortController of this.inFlightFetchAbortControllers) {
|
|
6744
|
-
abortController.abort();
|
|
6850
|
+
abortController.abort(reason);
|
|
6745
6851
|
}
|
|
6852
|
+
for (const controller of this.inFlightOAuthAbortControllers) controller.abort(reason);
|
|
6853
|
+
this.inFlightOAuthAbortControllers.clear();
|
|
6746
6854
|
this.inFlightFetchAbortControllers.clear();
|
|
6747
6855
|
}
|
|
6748
6856
|
cancelOpenResponseReaders() {
|
|
@@ -6808,7 +6916,7 @@ var HttpTransport = class {
|
|
|
6808
6916
|
const response = await this.fetchWithOAuthRetry({
|
|
6809
6917
|
url: postUrl,
|
|
6810
6918
|
method: "POST",
|
|
6811
|
-
createHeaders: () => this.createPostHeaders(message, modern),
|
|
6919
|
+
createHeaders: (signal) => this.createPostHeaders(message, modern, signal),
|
|
6812
6920
|
body: line,
|
|
6813
6921
|
controller
|
|
6814
6922
|
});
|
|
@@ -6819,7 +6927,7 @@ var HttpTransport = class {
|
|
|
6819
6927
|
if (hasSessionId && response.status === 404) {
|
|
6820
6928
|
void response.body?.cancel().catch(() => void 0);
|
|
6821
6929
|
this.sessionId = void 0;
|
|
6822
|
-
this.dispose(new
|
|
6930
|
+
this.dispose(new HttpTransportError("HTTP transport session expired (404 response)", 404, "POST"));
|
|
6823
6931
|
return;
|
|
6824
6932
|
}
|
|
6825
6933
|
if (await this.throwForPostHttpError(
|
|
@@ -6848,7 +6956,7 @@ var HttpTransport = class {
|
|
|
6848
6956
|
this.modernRequests.delete(id);
|
|
6849
6957
|
}
|
|
6850
6958
|
}
|
|
6851
|
-
async createPostHeaders(message, modern = false) {
|
|
6959
|
+
async createPostHeaders(message, modern = false, signal) {
|
|
6852
6960
|
const headers = new Headers(this.headers);
|
|
6853
6961
|
headers.set("Accept", "application/json, text/event-stream");
|
|
6854
6962
|
headers.set("Content-Type", "application/json");
|
|
@@ -6878,9 +6986,9 @@ var HttpTransport = class {
|
|
|
6878
6986
|
headers.set("Mcp-Session-Id", this.sessionId);
|
|
6879
6987
|
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
6880
6988
|
}
|
|
6881
|
-
return this.authorizeRequestHeaders(headers);
|
|
6989
|
+
return this.authorizeRequestHeaders(headers, signal);
|
|
6882
6990
|
}
|
|
6883
|
-
async createGetHeaders() {
|
|
6991
|
+
async createGetHeaders(signal) {
|
|
6884
6992
|
const headers = new Headers(this.headers);
|
|
6885
6993
|
headers.set("Accept", "text/event-stream");
|
|
6886
6994
|
if (this.sessionId !== void 0) {
|
|
@@ -6890,20 +6998,26 @@ var HttpTransport = class {
|
|
|
6890
6998
|
if (this.lastEventId !== void 0) {
|
|
6891
6999
|
headers.set("Last-Event-ID", this.lastEventId);
|
|
6892
7000
|
}
|
|
6893
|
-
return this.authorizeRequestHeaders(headers);
|
|
7001
|
+
return this.authorizeRequestHeaders(headers, signal);
|
|
6894
7002
|
}
|
|
6895
|
-
async createDeleteHeaders(sessionId) {
|
|
7003
|
+
async createDeleteHeaders(sessionId, signal) {
|
|
6896
7004
|
const headers = new Headers(this.headers);
|
|
6897
7005
|
headers.set("Mcp-Session-Id", sessionId);
|
|
6898
7006
|
headers.set("MCP-Protocol-Version", MCP_PROTOCOL_VERSION);
|
|
6899
|
-
return this.authorizeRequestHeaders(headers);
|
|
7007
|
+
return this.authorizeRequestHeaders(headers, signal);
|
|
6900
7008
|
}
|
|
6901
|
-
async authorizeRequestHeaders(headers) {
|
|
7009
|
+
async authorizeRequestHeaders(headers, signal) {
|
|
7010
|
+
signal?.throwIfAborted();
|
|
6902
7011
|
await this.oauthProvider?.authorizeRequest?.({
|
|
6903
7012
|
requestUrl: new URL(this.url),
|
|
6904
7013
|
headers,
|
|
6905
|
-
|
|
7014
|
+
signal,
|
|
7015
|
+
fetch: (url, init) => fetchMcpResponse(this.fetchImpl, url, {
|
|
7016
|
+
...init,
|
|
7017
|
+
signal: signal === void 0 ? init?.signal : init?.signal == null ? signal : AbortSignal.any([signal, init.signal])
|
|
7018
|
+
})
|
|
6906
7019
|
});
|
|
7020
|
+
signal?.throwIfAborted();
|
|
6907
7021
|
return headers;
|
|
6908
7022
|
}
|
|
6909
7023
|
captureSessionId(response) {
|
|
@@ -6940,7 +7054,7 @@ var HttpTransport = class {
|
|
|
6940
7054
|
return this.legacyEndpointReady;
|
|
6941
7055
|
}
|
|
6942
7056
|
async sendSessionTerminationRequest(sessionId, signal) {
|
|
6943
|
-
const headers = await this.createDeleteHeaders(sessionId);
|
|
7057
|
+
const headers = await this.createDeleteHeaders(sessionId, signal);
|
|
6944
7058
|
signal.throwIfAborted();
|
|
6945
7059
|
const response = await fetchMcpResponse(this.fetchImpl, this.url, {
|
|
6946
7060
|
method: "DELETE",
|
|
@@ -6958,12 +7072,12 @@ var HttpTransport = class {
|
|
|
6958
7072
|
const responseBody = (await readBoundedResponseText(response, this.maxResponseBytes, this.openResponseReaders, signal)).trim();
|
|
6959
7073
|
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
6960
7074
|
const message = responseBody.length === 0 ? `HTTP transport DELETE failed (${statusDescriptor})` : `HTTP transport DELETE failed (${statusDescriptor}): ${responseBody}`;
|
|
6961
|
-
throw new
|
|
7075
|
+
throw new HttpTransportError(message, response.status, "DELETE");
|
|
6962
7076
|
}
|
|
6963
7077
|
async consumeGetSseStream() {
|
|
6964
7078
|
const response = await this.fetchWithOAuthRetry({
|
|
6965
7079
|
method: "GET",
|
|
6966
|
-
createHeaders: () => this.createGetHeaders()
|
|
7080
|
+
createHeaders: (signal) => this.createGetHeaders(signal)
|
|
6967
7081
|
});
|
|
6968
7082
|
if (this.disposed) {
|
|
6969
7083
|
void response.body?.cancel().catch(() => void 0);
|
|
@@ -6971,18 +7085,19 @@ var HttpTransport = class {
|
|
|
6971
7085
|
}
|
|
6972
7086
|
if (response.status === 405) {
|
|
6973
7087
|
void response.body?.cancel().catch(() => void 0);
|
|
7088
|
+
if (this.mode === "sse") throw new HttpTransportError("Legacy SSE GET failed (405)", 405, "GET");
|
|
6974
7089
|
throw new HttpTransportGetSseNotSupportedError();
|
|
6975
7090
|
}
|
|
6976
7091
|
if (response.status === 404) {
|
|
6977
7092
|
void response.body?.cancel().catch(() => void 0);
|
|
6978
7093
|
this.sessionId = void 0;
|
|
6979
|
-
throw new
|
|
7094
|
+
throw new HttpTransportError("HTTP transport session expired (GET 404 response)", 404, "GET");
|
|
6980
7095
|
}
|
|
6981
7096
|
if (!response.ok) {
|
|
6982
7097
|
const responseBody = (await readBoundedResponseText(response, this.maxResponseBytes, this.openResponseReaders)).trim();
|
|
6983
7098
|
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
6984
7099
|
const message = responseBody.length === 0 ? `HTTP transport GET failed (${statusDescriptor})` : `HTTP transport GET failed (${statusDescriptor}): ${responseBody}`;
|
|
6985
|
-
throw new
|
|
7100
|
+
throw new HttpTransportError(message, response.status, "GET");
|
|
6986
7101
|
}
|
|
6987
7102
|
const contentType = response.headers.get("Content-Type");
|
|
6988
7103
|
if (contentType === null) {
|
|
@@ -7043,9 +7158,9 @@ var HttpTransport = class {
|
|
|
7043
7158
|
}
|
|
7044
7159
|
const statusDescriptor = `${response.status} ${response.statusText}`.trim();
|
|
7045
7160
|
const message = responseBody.length === 0 ? `HTTP transport POST failed (${statusDescriptor})` : `HTTP transport POST failed (${statusDescriptor}): ${responseBody}`;
|
|
7046
|
-
throw new
|
|
7161
|
+
throw new HttpTransportError(message, response.status, "POST");
|
|
7047
7162
|
}
|
|
7048
|
-
async maybeHandleUnauthorizedResponse(response) {
|
|
7163
|
+
async maybeHandleUnauthorizedResponse(response, signal) {
|
|
7049
7164
|
if (response.status !== 401 || this.oauthProvider === void 0) {
|
|
7050
7165
|
return false;
|
|
7051
7166
|
}
|
|
@@ -7056,7 +7171,7 @@ var HttpTransport = class {
|
|
|
7056
7171
|
const challenge = parseBearerWwwAuthenticateHeader(response.headers.get("WWW-Authenticate"));
|
|
7057
7172
|
const resourceMetadataUrl = challenge?.params.resource_metadata;
|
|
7058
7173
|
try {
|
|
7059
|
-
const discovery = await discoveryClient.discover(this.url, { resourceMetadataUrl });
|
|
7174
|
+
const discovery = await discoveryClient.discover(this.url, { resourceMetadataUrl, signal });
|
|
7060
7175
|
const providerResponse = response.clone();
|
|
7061
7176
|
let result;
|
|
7062
7177
|
try {
|
|
@@ -7065,8 +7180,13 @@ var HttpTransport = class {
|
|
|
7065
7180
|
response: providerResponse,
|
|
7066
7181
|
challenge,
|
|
7067
7182
|
discovery,
|
|
7068
|
-
|
|
7183
|
+
signal,
|
|
7184
|
+
fetch: (url, init) => fetchMcpResponse(this.fetchImpl, url, {
|
|
7185
|
+
...init,
|
|
7186
|
+
signal: init?.signal == null ? signal : AbortSignal.any([signal, init.signal])
|
|
7187
|
+
})
|
|
7069
7188
|
});
|
|
7189
|
+
signal.throwIfAborted();
|
|
7070
7190
|
} finally {
|
|
7071
7191
|
void providerResponse.body?.cancel().catch(() => void 0);
|
|
7072
7192
|
}
|
|
@@ -7196,25 +7316,30 @@ var HttpTransport = class {
|
|
|
7196
7316
|
`);
|
|
7197
7317
|
}
|
|
7198
7318
|
async fetchWithOAuthRetry(input) {
|
|
7199
|
-
const
|
|
7200
|
-
|
|
7201
|
-
|
|
7319
|
+
const controller = input.controller ?? new AbortController();
|
|
7320
|
+
this.inFlightOAuthAbortControllers.add(controller);
|
|
7321
|
+
const request = async () => {
|
|
7322
|
+
controller.signal.throwIfAborted();
|
|
7323
|
+
const headers = await input.createHeaders(controller.signal);
|
|
7324
|
+
controller.signal.throwIfAborted();
|
|
7325
|
+
return this.fetchWithAbort(input.url ?? this.url, {
|
|
7202
7326
|
method: input.method,
|
|
7203
|
-
headers
|
|
7327
|
+
headers,
|
|
7204
7328
|
body: input.body
|
|
7205
|
-
},
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7329
|
+
}, controller);
|
|
7330
|
+
};
|
|
7331
|
+
try {
|
|
7332
|
+
let response = await request();
|
|
7333
|
+
if (await this.maybeHandleUnauthorizedResponse(response, controller.signal)) response = await request();
|
|
7334
|
+
const oauthError = this.oauthProvider === void 0 ? null : this.readOAuthChallengeError(response);
|
|
7335
|
+
if (oauthError !== null) {
|
|
7336
|
+
void response.body?.cancel().catch(() => void 0);
|
|
7337
|
+
throw oauthError;
|
|
7338
|
+
}
|
|
7339
|
+
return response;
|
|
7340
|
+
} finally {
|
|
7341
|
+
this.inFlightOAuthAbortControllers.delete(controller);
|
|
7216
7342
|
}
|
|
7217
|
-
return response;
|
|
7218
7343
|
}
|
|
7219
7344
|
readOAuthChallengeError(response) {
|
|
7220
7345
|
if (response.status !== 401 && response.status !== 403) {
|
|
@@ -8123,6 +8248,7 @@ export {
|
|
|
8123
8248
|
ERROR_METHOD_NOT_FOUND,
|
|
8124
8249
|
ERROR_PARSE,
|
|
8125
8250
|
HttpTransport,
|
|
8251
|
+
HttpTransportError,
|
|
8126
8252
|
JsonRpcMessageLayer,
|
|
8127
8253
|
McpClient,
|
|
8128
8254
|
McpError,
|