tiny-http-mcp-server 0.1.23 → 0.1.25
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 +13 -1
- package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +38 -6
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +5 -0
- package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +1 -1
- package/node_modules/mcp-oauth/dist/client/types.d.ts +5 -1
- package/node_modules/tiny-mcp-client/dist/index.d.ts +6 -1
- package/node_modules/tiny-mcp-client/dist/index.js +48 -9
- package/package.json +1 -1
package/dist/composition.json
CHANGED
|
@@ -70,7 +70,10 @@ const verifier = createJwksTokenVerifier({
|
|
|
70
70
|
Fixed redirects support `localhost`, `127.0.0.1`, and `::1` over HTTP. Their
|
|
71
71
|
exact spelling, port, path and query are preserved through registration,
|
|
72
72
|
authorization and code exchange. Credentials, fragments, port zero and reserved
|
|
73
|
-
OAuth callback query parameters are rejected before binding a listener.
|
|
73
|
+
OAuth callback query parameters are rejected before binding a listener.
|
|
74
|
+
`createDefaultOAuthClientProvider` also checks the configured redirect before
|
|
75
|
+
creating the provider. Imported tokens that cannot be sent as HTTP header
|
|
76
|
+
values fail with diagnostics that omit their contents. Omit
|
|
74
77
|
`redirectUri` to allocate a random loopback port. Standalone callback sessions
|
|
75
78
|
accept the same `redirectUri`, `signal` and `timeoutMs` options. Cancellation,
|
|
76
79
|
timeout and explicit close settle pending code waits and release listeners.
|
|
@@ -81,6 +84,15 @@ registration, token requests and bounded token-body reads. Cancellation retains
|
|
|
81
84
|
its original reason and does not retry authorization. Custom providers should
|
|
82
85
|
observe the supplied signal and pass it to any work they start.
|
|
83
86
|
|
|
87
|
+
`authorizeRequest` may return an owned token snapshot for the request it
|
|
88
|
+
authorized. The HTTP client supplies that snapshot as `presentedTokens`, along
|
|
89
|
+
with the actual request's `requestHeaders`, to `handleUnauthorized`. Providers
|
|
90
|
+
that return `void` remain supported. The native provider compares the rejected
|
|
91
|
+
snapshot with persisted credentials: delayed 401s retry with a newer grant
|
|
92
|
+
without redeeming its refresh token again. A proven current token is refreshed
|
|
93
|
+
on 401 even when the server omits `error="invalid_token"`. Invalid provenance
|
|
94
|
+
fails without quoting token values.
|
|
95
|
+
|
|
84
96
|
Configure `client.metadata.scope` to request a precise scope set; broader
|
|
85
97
|
discovery metadata does not override it.
|
|
86
98
|
|
|
@@ -2,7 +2,7 @@ import { isIP } from "node:net";
|
|
|
2
2
|
import { fetchMcpResponse } from "../http-fetch.js";
|
|
3
3
|
import { URL } from "node:url";
|
|
4
4
|
import { createAuthStoreClientStore, createAuthStoreSessionStore } from "./auth-store-session-store.js";
|
|
5
|
-
import { createLoopbackAuthorizationSession } from "./loopback-authorization.js";
|
|
5
|
+
import { createLoopbackAuthorizationSession, loopbackTarget } from "./loopback-authorization.js";
|
|
6
6
|
import { createAuthorizationState } from "./authorization-state.js";
|
|
7
7
|
import { generateCodeChallenge, generateCodeVerifier } from "./pkce.js";
|
|
8
8
|
import { exchangeAuthorizationCode, OAuthError, refreshAccessToken, isRetryableOAuthError, readOAuthJsonObjectResponse } from "./token-endpoint.js";
|
|
@@ -15,6 +15,7 @@ export function createOAuthClientProvider(options) {
|
|
|
15
15
|
return createDefaultOAuthClientProvider(options);
|
|
16
16
|
}
|
|
17
17
|
export function createDefaultOAuthClientProvider(options) {
|
|
18
|
+
loopbackTarget(options.browser);
|
|
18
19
|
const sessionStore = options.sessionStore ?? createAuthStoreSessionStore(options.authStore);
|
|
19
20
|
const clientStore = options.authStore === undefined ? null : createAuthStoreClientStore(options.authStore);
|
|
20
21
|
const now = options.now ?? Date.now;
|
|
@@ -39,6 +40,14 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
39
40
|
};
|
|
40
41
|
if (initialGrant !== undefined && (initialGrant.tokens === undefined || initialGrant.client === null))
|
|
41
42
|
throw new Error("OAuth initial grant requires valid tokens and the original client ID");
|
|
43
|
+
if (initialGrant?.tokens !== undefined) {
|
|
44
|
+
try {
|
|
45
|
+
new Headers({ Authorization: `Bearer ${initialGrant.tokens.accessToken}` });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
throw new Error("OAuth initial grant access token is not a valid HTTP header value");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
42
51
|
let initialGrantConsumed = false;
|
|
43
52
|
return {
|
|
44
53
|
async authorizeRequest(input) {
|
|
@@ -49,7 +58,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
49
58
|
if (session === null && !initialGrantConsumed && initialGrant?.resource === requestUrl &&
|
|
50
59
|
initialGrant.tokens !== undefined && !isExpired(initialGrant.tokens, now)) {
|
|
51
60
|
input.headers.set("Authorization", `Bearer ${initialGrant.tokens.accessToken}`);
|
|
52
|
-
return;
|
|
61
|
+
return { ...initialGrant.tokens };
|
|
53
62
|
}
|
|
54
63
|
if (session === null ||
|
|
55
64
|
accessToken === undefined ||
|
|
@@ -59,6 +68,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
59
68
|
}
|
|
60
69
|
assertRequestMatchesResource(requestUrl, session.resource);
|
|
61
70
|
input.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
71
|
+
return { ...session.tokens };
|
|
62
72
|
},
|
|
63
73
|
async handleUnauthorized(input) {
|
|
64
74
|
try {
|
|
@@ -66,12 +76,28 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
66
76
|
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
67
77
|
const resource = canonicalizeResourceIndicator(input.discovery.resource);
|
|
68
78
|
assertRequestMatchesResource(requestUrl, resource);
|
|
69
|
-
const
|
|
70
|
-
|
|
79
|
+
const cached = await loadSession(resource);
|
|
80
|
+
const currentTokens = cached?.tokens ?? (!initialGrantConsumed && initialGrant?.resource === resource ? initialGrant.tokens : undefined);
|
|
81
|
+
let rejectedCurrentGrant = hasCachedAccessToken(cached) || (!initialGrantConsumed && initialGrant?.resource === resource);
|
|
82
|
+
let presentedTokens = input.presentedTokens;
|
|
83
|
+
if (input.presentedTokens !== undefined) {
|
|
84
|
+
rejectedCurrentGrant = false;
|
|
85
|
+
if (input.presentedTokens !== null) {
|
|
86
|
+
const presented = normalizeStoredTokens(input.presentedTokens);
|
|
87
|
+
const header = input.requestHeaders?.get("Authorization") ?? "";
|
|
88
|
+
const separator = header.indexOf(" ");
|
|
89
|
+
if (presented === undefined || header.slice(0, separator).toLowerCase() !== "bearer" || header.slice(separator + 1).trim() !== presented.accessToken)
|
|
90
|
+
throw new Error("OAuth rejected-request provenance does not match its authorization header");
|
|
91
|
+
presentedTokens = presented;
|
|
92
|
+
rejectedCurrentGrant = currentTokens !== undefined && sameTokenGrant(currentTokens, presented);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const challengeError = input.challenge?.params.error;
|
|
96
|
+
const forceRefresh = rejectedCurrentGrant && (challengeError === "invalid_token" || (input.presentedTokens !== undefined && challengeError === undefined));
|
|
71
97
|
const session = await ensureAuthorizedSession(resource, {
|
|
72
98
|
...input.discovery,
|
|
73
99
|
resource
|
|
74
|
-
}, input.fetch, true, forceRefresh, input.signal);
|
|
100
|
+
}, input.fetch, true, forceRefresh, input.signal, presentedTokens);
|
|
75
101
|
if (session?.tokens?.accessToken === undefined) {
|
|
76
102
|
return { action: "fail" };
|
|
77
103
|
}
|
|
@@ -86,7 +112,7 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
86
112
|
}
|
|
87
113
|
}
|
|
88
114
|
};
|
|
89
|
-
async function ensureAuthorizedSession(resource, discovery, fetch, allowInteractive, forceRefresh = false, signal) {
|
|
115
|
+
async function ensureAuthorizedSession(resource, discovery, fetch, allowInteractive, forceRefresh = false, signal, rejectedTokens) {
|
|
90
116
|
signal?.throwIfAborted();
|
|
91
117
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
92
118
|
let session = await loadSession(canonicalResource);
|
|
@@ -111,6 +137,8 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
111
137
|
initialGrantConsumed = true;
|
|
112
138
|
signal?.throwIfAborted();
|
|
113
139
|
}
|
|
140
|
+
if (forceRefresh && rejectedTokens !== undefined && (rejectedTokens === null || session?.tokens === undefined || !sameTokenGrant(session.tokens, rejectedTokens)))
|
|
141
|
+
forceRefresh = false;
|
|
114
142
|
const sessionDiscovery = resolveDiscovery(discovery, session);
|
|
115
143
|
if (session?.tokens !== undefined && !forceRefresh && !isExpired(session.tokens, now)) {
|
|
116
144
|
return session;
|
|
@@ -445,6 +473,10 @@ function resolveDiscovery(discovery, session) {
|
|
|
445
473
|
authorizationServerMetadata: metadata
|
|
446
474
|
};
|
|
447
475
|
}
|
|
476
|
+
function sameTokenGrant(left, right) {
|
|
477
|
+
return left.accessToken === right.accessToken && left.refreshToken === right.refreshToken &&
|
|
478
|
+
left.tokenType === right.tokenType && left.expiresAt === right.expiresAt && left.scope === right.scope;
|
|
479
|
+
}
|
|
448
480
|
function clearSessionTokens(session) {
|
|
449
481
|
const nextSession = { ...session };
|
|
450
482
|
delete nextSession.tokens;
|
|
@@ -21,5 +21,10 @@ export interface LoopbackAuthorizationSession {
|
|
|
21
21
|
close(): void;
|
|
22
22
|
}
|
|
23
23
|
export declare function createLoopbackAuthorizationSession(options?: LoopbackAuthorizationOptions): Promise<LoopbackAuthorizationSession>;
|
|
24
|
+
export declare function loopbackTarget(options: LoopbackAuthorizationOptions): {
|
|
25
|
+
port: number;
|
|
26
|
+
host: string;
|
|
27
|
+
callbackPath: string;
|
|
28
|
+
};
|
|
24
29
|
export declare function extractCodeFromInput(input: string): string | null;
|
|
25
30
|
export declare function buildSuccessPage(landingPage?: OAuthLandingPage): string;
|
|
@@ -33,7 +33,7 @@ export interface OAuthClientProvider {
|
|
|
33
33
|
headers: Headers;
|
|
34
34
|
fetch: OAuthMetadataFetch;
|
|
35
35
|
signal?: AbortSignal;
|
|
36
|
-
}): Promise<void> | void;
|
|
36
|
+
}): Promise<StoredOAuthTokens | void> | StoredOAuthTokens | void;
|
|
37
37
|
handleUnauthorized(input: {
|
|
38
38
|
requestUrl: URL;
|
|
39
39
|
response: Response;
|
|
@@ -41,6 +41,10 @@ export interface OAuthClientProvider {
|
|
|
41
41
|
discovery: OAuthDiscoveryResult;
|
|
42
42
|
fetch: OAuthMetadataFetch;
|
|
43
43
|
signal?: AbortSignal;
|
|
44
|
+
/** Headers actually attached to this rejected request. */
|
|
45
|
+
requestHeaders?: Headers;
|
|
46
|
+
/** Owned snapshot returned when this request was authorized, or null if absent. */
|
|
47
|
+
presentedTokens?: StoredOAuthTokens | null;
|
|
44
48
|
}): Promise<{
|
|
45
49
|
action: "retry";
|
|
46
50
|
} | {
|
|
@@ -110,7 +110,7 @@ interface OAuthClientProvider {
|
|
|
110
110
|
headers: Headers;
|
|
111
111
|
fetch: OAuthMetadataFetch;
|
|
112
112
|
signal?: AbortSignal;
|
|
113
|
-
}): Promise<void> | void;
|
|
113
|
+
}): Promise<StoredOAuthTokens | void> | StoredOAuthTokens | void;
|
|
114
114
|
handleUnauthorized(input: {
|
|
115
115
|
requestUrl: URL;
|
|
116
116
|
response: Response;
|
|
@@ -118,6 +118,10 @@ interface OAuthClientProvider {
|
|
|
118
118
|
discovery: OAuthDiscoveryResult;
|
|
119
119
|
fetch: OAuthMetadataFetch;
|
|
120
120
|
signal?: AbortSignal;
|
|
121
|
+
/** Headers actually attached to this rejected request. */
|
|
122
|
+
requestHeaders?: Headers;
|
|
123
|
+
/** Owned snapshot returned when this request was authorized, or null if absent. */
|
|
124
|
+
presentedTokens?: StoredOAuthTokens | null;
|
|
121
125
|
}): Promise<{
|
|
122
126
|
action: "retry";
|
|
123
127
|
} | {
|
|
@@ -649,6 +653,7 @@ declare class HttpTransport implements McpTransport {
|
|
|
649
653
|
private readonly oauthMetadataDiscovery;
|
|
650
654
|
private readonly inFlightFetchAbortControllers;
|
|
651
655
|
private readonly inFlightOAuthAbortControllers;
|
|
656
|
+
private readonly oauthRequestTokens;
|
|
652
657
|
private readonly openResponseReaders;
|
|
653
658
|
private readonly modernRequests;
|
|
654
659
|
private modernMode;
|
|
@@ -4594,6 +4594,7 @@ function createOAuthClientProvider(options) {
|
|
|
4594
4594
|
return createDefaultOAuthClientProvider(options);
|
|
4595
4595
|
}
|
|
4596
4596
|
function createDefaultOAuthClientProvider(options) {
|
|
4597
|
+
loopbackTarget(options.browser);
|
|
4597
4598
|
const sessionStore = options.sessionStore ?? createAuthStoreSessionStore(options.authStore);
|
|
4598
4599
|
const clientStore = options.authStore === void 0 ? null : createAuthStoreClientStore(options.authStore);
|
|
4599
4600
|
const now = options.now ?? Date.now;
|
|
@@ -4617,6 +4618,13 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4617
4618
|
};
|
|
4618
4619
|
if (initialGrant !== void 0 && (initialGrant.tokens === void 0 || initialGrant.client === null))
|
|
4619
4620
|
throw new Error("OAuth initial grant requires valid tokens and the original client ID");
|
|
4621
|
+
if (initialGrant?.tokens !== void 0) {
|
|
4622
|
+
try {
|
|
4623
|
+
new Headers({ Authorization: `Bearer ${initialGrant.tokens.accessToken}` });
|
|
4624
|
+
} catch {
|
|
4625
|
+
throw new Error("OAuth initial grant access token is not a valid HTTP header value");
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4620
4628
|
let initialGrantConsumed = false;
|
|
4621
4629
|
return {
|
|
4622
4630
|
async authorizeRequest(input) {
|
|
@@ -4626,13 +4634,14 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4626
4634
|
const accessToken = session?.tokens?.accessToken;
|
|
4627
4635
|
if (session === null && !initialGrantConsumed && initialGrant?.resource === requestUrl && initialGrant.tokens !== void 0 && !isExpired(initialGrant.tokens, now)) {
|
|
4628
4636
|
input.headers.set("Authorization", `Bearer ${initialGrant.tokens.accessToken}`);
|
|
4629
|
-
return;
|
|
4637
|
+
return { ...initialGrant.tokens };
|
|
4630
4638
|
}
|
|
4631
4639
|
if (session === null || accessToken === void 0 || session.tokens === void 0 || isExpired(session.tokens, now)) {
|
|
4632
4640
|
return;
|
|
4633
4641
|
}
|
|
4634
4642
|
assertRequestMatchesResource(requestUrl, session.resource);
|
|
4635
4643
|
input.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
4644
|
+
return { ...session.tokens };
|
|
4636
4645
|
},
|
|
4637
4646
|
async handleUnauthorized(input) {
|
|
4638
4647
|
try {
|
|
@@ -4640,11 +4649,28 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4640
4649
|
const requestUrl = canonicalizeResourceIndicator(input.requestUrl);
|
|
4641
4650
|
const resource = canonicalizeResourceIndicator(input.discovery.resource);
|
|
4642
4651
|
assertRequestMatchesResource(requestUrl, resource);
|
|
4643
|
-
const
|
|
4652
|
+
const cached = await loadSession(resource);
|
|
4653
|
+
const currentTokens = cached?.tokens ?? (!initialGrantConsumed && initialGrant?.resource === resource ? initialGrant.tokens : void 0);
|
|
4654
|
+
let rejectedCurrentGrant = hasCachedAccessToken(cached) || !initialGrantConsumed && initialGrant?.resource === resource;
|
|
4655
|
+
let presentedTokens = input.presentedTokens;
|
|
4656
|
+
if (input.presentedTokens !== void 0) {
|
|
4657
|
+
rejectedCurrentGrant = false;
|
|
4658
|
+
if (input.presentedTokens !== null) {
|
|
4659
|
+
const presented = normalizeStoredTokens(input.presentedTokens);
|
|
4660
|
+
const header = input.requestHeaders?.get("Authorization") ?? "";
|
|
4661
|
+
const separator = header.indexOf(" ");
|
|
4662
|
+
if (presented === void 0 || header.slice(0, separator).toLowerCase() !== "bearer" || header.slice(separator + 1).trim() !== presented.accessToken)
|
|
4663
|
+
throw new Error("OAuth rejected-request provenance does not match its authorization header");
|
|
4664
|
+
presentedTokens = presented;
|
|
4665
|
+
rejectedCurrentGrant = currentTokens !== void 0 && sameTokenGrant(currentTokens, presented);
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
const challengeError = input.challenge?.params.error;
|
|
4669
|
+
const forceRefresh = rejectedCurrentGrant && (challengeError === "invalid_token" || input.presentedTokens !== void 0 && challengeError === void 0);
|
|
4644
4670
|
const session = await ensureAuthorizedSession(resource, {
|
|
4645
4671
|
...input.discovery,
|
|
4646
4672
|
resource
|
|
4647
|
-
}, input.fetch, true, forceRefresh, input.signal);
|
|
4673
|
+
}, input.fetch, true, forceRefresh, input.signal, presentedTokens);
|
|
4648
4674
|
if (session?.tokens?.accessToken === void 0) {
|
|
4649
4675
|
return { action: "fail" };
|
|
4650
4676
|
}
|
|
@@ -4658,7 +4684,7 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4658
4684
|
}
|
|
4659
4685
|
}
|
|
4660
4686
|
};
|
|
4661
|
-
async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false, signal) {
|
|
4687
|
+
async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false, signal, rejectedTokens) {
|
|
4662
4688
|
signal?.throwIfAborted();
|
|
4663
4689
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
4664
4690
|
let session = await loadSession(canonicalResource);
|
|
@@ -4685,6 +4711,8 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
4685
4711
|
initialGrantConsumed = true;
|
|
4686
4712
|
signal?.throwIfAborted();
|
|
4687
4713
|
}
|
|
4714
|
+
if (forceRefresh && rejectedTokens !== void 0 && (rejectedTokens === null || session?.tokens === void 0 || !sameTokenGrant(session.tokens, rejectedTokens)))
|
|
4715
|
+
forceRefresh = false;
|
|
4688
4716
|
const sessionDiscovery = resolveDiscovery(discovery, session);
|
|
4689
4717
|
if (session?.tokens !== void 0 && !forceRefresh && !isExpired(session.tokens, now)) {
|
|
4690
4718
|
return session;
|
|
@@ -5006,6 +5034,9 @@ function resolveDiscovery(discovery, session) {
|
|
|
5006
5034
|
authorizationServerMetadata: metadata
|
|
5007
5035
|
};
|
|
5008
5036
|
}
|
|
5037
|
+
function sameTokenGrant(left, right) {
|
|
5038
|
+
return left.accessToken === right.accessToken && left.refreshToken === right.refreshToken && left.tokenType === right.tokenType && left.expiresAt === right.expiresAt && left.scope === right.scope;
|
|
5039
|
+
}
|
|
5009
5040
|
function clearSessionTokens(session) {
|
|
5010
5041
|
const nextSession = { ...session };
|
|
5011
5042
|
delete nextSession.tokens;
|
|
@@ -6781,6 +6812,7 @@ var HttpTransport = class {
|
|
|
6781
6812
|
oauthMetadataDiscovery;
|
|
6782
6813
|
inFlightFetchAbortControllers = /* @__PURE__ */ new Set();
|
|
6783
6814
|
inFlightOAuthAbortControllers = /* @__PURE__ */ new Set();
|
|
6815
|
+
oauthRequestTokens = /* @__PURE__ */ new WeakMap();
|
|
6784
6816
|
openResponseReaders = /* @__PURE__ */ new Set();
|
|
6785
6817
|
modernRequests = /* @__PURE__ */ new Map();
|
|
6786
6818
|
modernMode = false;
|
|
@@ -7051,7 +7083,7 @@ var HttpTransport = class {
|
|
|
7051
7083
|
}
|
|
7052
7084
|
async authorizeRequestHeaders(headers, signal) {
|
|
7053
7085
|
signal?.throwIfAborted();
|
|
7054
|
-
await this.oauthProvider?.authorizeRequest?.({
|
|
7086
|
+
const tokens = await this.oauthProvider?.authorizeRequest?.({
|
|
7055
7087
|
requestUrl: new URL(this.url),
|
|
7056
7088
|
headers,
|
|
7057
7089
|
signal,
|
|
@@ -7060,6 +7092,7 @@ var HttpTransport = class {
|
|
|
7060
7092
|
signal: signal === void 0 ? init?.signal : init?.signal == null ? signal : AbortSignal.any([signal, init.signal])
|
|
7061
7093
|
})
|
|
7062
7094
|
});
|
|
7095
|
+
if (tokens !== void 0) this.oauthRequestTokens.set(headers, { ...tokens });
|
|
7063
7096
|
signal?.throwIfAborted();
|
|
7064
7097
|
return headers;
|
|
7065
7098
|
}
|
|
@@ -7203,7 +7236,7 @@ var HttpTransport = class {
|
|
|
7203
7236
|
const message = responseBody.length === 0 ? `HTTP transport POST failed (${statusDescriptor})` : `HTTP transport POST failed (${statusDescriptor}): ${responseBody}`;
|
|
7204
7237
|
throw new HttpTransportError(message, response.status, "POST");
|
|
7205
7238
|
}
|
|
7206
|
-
async maybeHandleUnauthorizedResponse(response, signal) {
|
|
7239
|
+
async maybeHandleUnauthorizedResponse(response, signal, requestHeaders, presentedTokens) {
|
|
7207
7240
|
if (response.status !== 401 || this.oauthProvider === void 0) {
|
|
7208
7241
|
return false;
|
|
7209
7242
|
}
|
|
@@ -7224,6 +7257,8 @@ var HttpTransport = class {
|
|
|
7224
7257
|
challenge,
|
|
7225
7258
|
discovery,
|
|
7226
7259
|
signal,
|
|
7260
|
+
requestHeaders: new Headers(requestHeaders),
|
|
7261
|
+
presentedTokens,
|
|
7227
7262
|
fetch: (url, init) => fetchMcpResponse(this.fetchImpl, url, {
|
|
7228
7263
|
...init,
|
|
7229
7264
|
signal: init?.signal == null ? signal : AbortSignal.any([signal, init.signal])
|
|
@@ -7364,16 +7399,20 @@ var HttpTransport = class {
|
|
|
7364
7399
|
const request = async () => {
|
|
7365
7400
|
controller.signal.throwIfAborted();
|
|
7366
7401
|
const headers = await input.createHeaders(controller.signal);
|
|
7402
|
+
const headerSnapshot = new Headers(headers);
|
|
7403
|
+
const tokens = this.oauthRequestTokens.get(headers) ?? null;
|
|
7367
7404
|
controller.signal.throwIfAborted();
|
|
7368
|
-
|
|
7405
|
+
const response = await this.fetchWithAbort(input.url ?? this.url, {
|
|
7369
7406
|
method: input.method,
|
|
7370
7407
|
headers,
|
|
7371
7408
|
body: input.body
|
|
7372
7409
|
}, controller);
|
|
7410
|
+
return { response, headers: headerSnapshot, tokens };
|
|
7373
7411
|
};
|
|
7374
7412
|
try {
|
|
7375
|
-
let
|
|
7376
|
-
if (await this.maybeHandleUnauthorizedResponse(response, controller.signal))
|
|
7413
|
+
let attempt = await request();
|
|
7414
|
+
if (await this.maybeHandleUnauthorizedResponse(attempt.response, controller.signal, attempt.headers, attempt.tokens)) attempt = await request();
|
|
7415
|
+
const response = attempt.response;
|
|
7377
7416
|
const oauthError = this.oauthProvider === void 0 ? null : this.readOAuthChallengeError(response);
|
|
7378
7417
|
if (oauthError !== null) {
|
|
7379
7418
|
void response.body?.cancel().catch(() => void 0);
|