deadbyte-mcp 0.15.16 → 0.15.17
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/MANIFEST.SHA256 +96 -94
- package/README.txt +6 -6
- package/RELEASE.json +1 -1
- package/SBOM.cdx.json +7 -33
- package/TEST-MATRIX.json +10 -25
- package/node_modules/@modelcontextprotocol/client/dist/{index-CHhhk6PQ.d.cts → index-BCtJi0p5.d.mts} +38 -6
- package/node_modules/@modelcontextprotocol/client/dist/index-BCtJi0p5.d.mts.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/{index-D4xIIEF6.d.mts → index-CEk3qkZM.d.cts} +41 -3
- package/node_modules/@modelcontextprotocol/client/dist/index-CEk3qkZM.d.cts.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/index.cjs +375 -60
- package/node_modules/@modelcontextprotocol/client/dist/index.cjs.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/index.d.cts +227 -11
- package/node_modules/@modelcontextprotocol/client/dist/index.d.cts.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/index.d.mts +227 -11
- package/node_modules/@modelcontextprotocol/client/dist/index.d.mts.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/index.mjs +369 -61
- package/node_modules/@modelcontextprotocol/client/dist/index.mjs.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/shimsWorkerd.cjs +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/shimsWorkerd.mjs +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/{src-NAgB4Mp8.cjs → src-D0qEN74R.cjs} +111 -38
- package/node_modules/@modelcontextprotocol/client/dist/src-D0qEN74R.cjs.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/{src-D_zzAWoS.mjs → src-DDAAhLnO.mjs} +106 -33
- package/node_modules/@modelcontextprotocol/client/dist/src-DDAAhLnO.mjs.map +1 -0
- package/node_modules/@modelcontextprotocol/client/dist/stdio.cjs +8 -2
- package/node_modules/@modelcontextprotocol/client/dist/stdio.cjs.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.d.cts.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.d.mts.map +1 -1
- package/node_modules/@modelcontextprotocol/client/dist/stdio.mjs +8 -2
- package/node_modules/@modelcontextprotocol/client/dist/stdio.mjs.map +1 -1
- package/node_modules/@modelcontextprotocol/client/package.json +4 -4
- package/node_modules/@modelcontextprotocol/core/dist/{auth-BWdKR39I.d.mts → auth--n_Yzytt.d.cts} +6 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-BWdKR39I.d.mts.map → auth--n_Yzytt.d.cts.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-CfDMXiII.cjs → auth-4-zilcqg.cjs} +3 -2
- package/node_modules/@modelcontextprotocol/core/dist/{auth-CfDMXiII.cjs.map → auth-4-zilcqg.cjs.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-CUe6YdwF.mjs → auth-CGP0BDVq.mjs} +3 -2
- package/node_modules/@modelcontextprotocol/core/dist/{auth-CUe6YdwF.mjs.map → auth-CGP0BDVq.mjs.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-hYtaIUZm.d.cts → auth-YUQV3RRv.d.mts} +6 -1
- package/node_modules/@modelcontextprotocol/core/dist/{auth-hYtaIUZm.d.cts.map → auth-YUQV3RRv.d.mts.map} +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.cjs +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/index.mjs +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.cjs +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.d.cts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.d.mts +1 -1
- package/node_modules/@modelcontextprotocol/core/dist/internal.mjs +1 -1
- package/node_modules/@modelcontextprotocol/core/package.json +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-dBHMsxwf.d.cts → createMcpHandler-BLVe3qmU.d.cts} +110 -7
- package/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-BLVe3qmU.d.cts.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-CLhGwQTn.d.mts → createMcpHandler-Bt6U_Fqb.d.mts} +110 -7
- package/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-Bt6U_Fqb.d.mts.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/index.cjs +144 -248
- package/node_modules/@modelcontextprotocol/server/dist/index.cjs.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.d.cts +38 -3
- package/node_modules/@modelcontextprotocol/server/dist/index.d.cts.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.d.mts +38 -3
- package/node_modules/@modelcontextprotocol/server/dist/index.d.mts.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/index.mjs +136 -243
- package/node_modules/@modelcontextprotocol/server/dist/index.mjs.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/{mcp-D7GmuPnv.cjs → mcp-DUcCQSTW.cjs} +426 -21
- package/node_modules/@modelcontextprotocol/server/dist/mcp-DUcCQSTW.cjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/{mcp-DXXb3Vv3.mjs → mcp-Dw2OlZ1f.mjs} +367 -22
- package/node_modules/@modelcontextprotocol/server/dist/mcp-Dw2OlZ1f.mjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.cjs +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.mjs +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/{src-STyD_Vvf.cjs → src-CgFsfg3E.cjs} +114 -36
- package/node_modules/@modelcontextprotocol/server/dist/src-CgFsfg3E.cjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/{src-CX2iR2pK.mjs → src-D-y6h4N7.mjs} +114 -36
- package/node_modules/@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs.map +1 -0
- package/node_modules/@modelcontextprotocol/server/dist/stdio.cjs +30 -3
- package/node_modules/@modelcontextprotocol/server/dist/stdio.cjs.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.d.cts +9 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.d.cts.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.d.mts +9 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.d.mts.map +1 -1
- package/node_modules/@modelcontextprotocol/server/dist/stdio.mjs +30 -3
- package/node_modules/@modelcontextprotocol/server/dist/stdio.mjs.map +1 -1
- package/node_modules/@modelcontextprotocol/server/package.json +3 -3
- package/package.json +91 -93
- package/scripts/commander-hardening-gate-r47.mjs +1 -0
- package/scripts/full-regression-r47.mjs +1 -1
- package/src/coding-plane.mjs +24 -0
- package/src/commander-core-bridge.mjs +26 -30
- package/src/commander-extended-bridge-r47.mjs +17 -0
- package/src/commander-extended-runtime.mjs +2 -1
- package/src/commander-wire-adapter-r47.mjs +341 -0
- package/src/machine-fs.mjs +6 -6
- package/src/mcp-server.mjs +13 -87
- package/src/tunnel-stdio-compat.mjs +3 -3
- package/src/version.mjs +1 -1
- package/test/commander-core-bridge-r47.test.mjs +1 -1
- package/test/commander-protocol-r47.test.mjs +3 -0
- package/test/commander-wire-contract-r47.test.mjs +143 -0
- package/test/machine-fs-runtime.test.mjs +19 -1
- package/test/r47-release-contract.test.mjs +3 -3
- package/node_modules/@modelcontextprotocol/client/dist/index-CHhhk6PQ.d.cts.map +0 -1
- package/node_modules/@modelcontextprotocol/client/dist/index-D4xIIEF6.d.mts.map +0 -1
- package/node_modules/@modelcontextprotocol/client/dist/src-D_zzAWoS.mjs.map +0 -1
- package/node_modules/@modelcontextprotocol/client/dist/src-NAgB4Mp8.cjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-CLhGwQTn.d.mts.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-dBHMsxwf.d.cts.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/mcp-D7GmuPnv.cjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/mcp-DXXb3Vv3.mjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/src-CX2iR2pK.mjs.map +0 -1
- package/node_modules/@modelcontextprotocol/server/dist/src-STyD_Vvf.cjs.map +0 -1
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require('./chunk-Bnu9O96Y.cjs');
|
|
2
|
-
const require_src = require('./src-
|
|
2
|
+
const require_src = require('./src-D0qEN74R.cjs');
|
|
3
3
|
let _modelcontextprotocol_client__shims = require("@modelcontextprotocol/client/_shims");
|
|
4
4
|
let pkce_challenge = require("pkce-challenge");
|
|
5
5
|
pkce_challenge = require_chunk.__toESM(pkce_challenge);
|
|
@@ -269,6 +269,12 @@ async function handleOAuthUnauthorized(provider, ctx, extraAuthOptions) {
|
|
|
269
269
|
* not in scope. Providers that key storage on `ctx.issuer` MUST treat `ctx === undefined`
|
|
270
270
|
* as "return the most-recently-saved token set" (the only consumer is the resource server
|
|
271
271
|
* the token was minted for); providers that round-trip a single blob need no change.
|
|
272
|
+
*
|
|
273
|
+
* SEP-1932 (DPoP) note: DPoP request-signing is deliberately *not* done here. When the provider
|
|
274
|
+
* implements {@linkcode OAuthClientProvider.dpop | dpop()}, the transports wrap their
|
|
275
|
+
* resource-server `fetch` with {@linkcode withDpopFromProvider}, which upgrades the `Bearer` header
|
|
276
|
+
* this adapter produces to `DPoP` + proof for DPoP-bound tokens and handles nonce challenges — at
|
|
277
|
+
* the one layer that sees the real method, URL and response of every request.
|
|
272
278
|
*/
|
|
273
279
|
function adaptOAuthProvider(provider, extraAuthOptions) {
|
|
274
280
|
return {
|
|
@@ -605,6 +611,17 @@ async function parseErrorResponse(input) {
|
|
|
605
611
|
}
|
|
606
612
|
}
|
|
607
613
|
/**
|
|
614
|
+
* Recovering from a recoverable OAuth error discards stored credentials and silently starts a
|
|
615
|
+
* fresh authorization. On a headless client whose `redirectToAuthorization()` is a no-op that
|
|
616
|
+
* recovery is indistinguishable from nothing happening at all, so name the cause. The most
|
|
617
|
+
* common case is `invalid_grant` — an expired, revoked, or rotation-reuse-detected refresh
|
|
618
|
+
* token. See issue #2034.
|
|
619
|
+
*/
|
|
620
|
+
function warnCredentialInvalidation(provider, error, invalidated) {
|
|
621
|
+
const action = provider.invalidateCredentials === void 0 ? `retrying authorization without discarding the stored ${invalidated} (provider implements no invalidateCredentials())` : `invalidating the stored ${invalidated} and retrying authorization`;
|
|
622
|
+
console.warn(`[mcp-sdk] OAuth ${JSON.stringify(error.code)} — ${action}. Cause: ${JSON.stringify(error.message)}`);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
608
625
|
* Orchestrates the full auth flow with a server.
|
|
609
626
|
*
|
|
610
627
|
* This can be used as a single entry point for all authorization functionality,
|
|
@@ -616,10 +633,12 @@ async function auth(provider, options) {
|
|
|
616
633
|
} catch (error) {
|
|
617
634
|
if (error instanceof require_src.OAuthError) {
|
|
618
635
|
if (error.code === require_src.OAuthErrorCode.InvalidClient || error.code === require_src.OAuthErrorCode.UnauthorizedClient) {
|
|
636
|
+
warnCredentialInvalidation(provider, error, "client credentials and tokens");
|
|
619
637
|
await provider.invalidateCredentials?.("client");
|
|
620
638
|
await provider.invalidateCredentials?.("tokens");
|
|
621
639
|
return await authInternal(provider, options);
|
|
622
|
-
} else if (error.code === require_src.OAuthErrorCode.InvalidGrant) {
|
|
640
|
+
} else if (error.code === require_src.OAuthErrorCode.InvalidGrant || error.code === require_src.OAuthErrorCode.InvalidDpopProof) {
|
|
641
|
+
warnCredentialInvalidation(provider, error, "tokens");
|
|
623
642
|
await provider.invalidateCredentials?.("tokens");
|
|
624
643
|
return await authInternal(provider, options);
|
|
625
644
|
}
|
|
@@ -690,8 +709,9 @@ async function authInternal(provider, { serverUrl, authorizationCode, iss, scope
|
|
|
690
709
|
} else if (!issuersMatch(recordedIssuer, issuer)) throw new AuthorizationServerMismatchError(recordedIssuer, issuer);
|
|
691
710
|
}
|
|
692
711
|
if (freshDiscoveryState) await provider.saveDiscoveryState?.(freshDiscoveryState);
|
|
693
|
-
const
|
|
694
|
-
|
|
712
|
+
const selectedResource = await selectResourceURL(serverUrl, provider, resourceMetadata);
|
|
713
|
+
const resource = selectedResource && resourceMetadata && !provider.validateResourceURL ? resourceMetadata.resource : selectedResource;
|
|
714
|
+
if (resource) await provider.saveResourceUrl?.(resourceIndicatorToString(resource));
|
|
695
715
|
const resolvedScope = determineScope({
|
|
696
716
|
requestedScope: scope,
|
|
697
717
|
resourceMetadata,
|
|
@@ -762,23 +782,30 @@ async function authInternal(provider, { serverUrl, authorizationCode, iss, scope
|
|
|
762
782
|
};
|
|
763
783
|
await provider.saveTokens(tokens, infoCtx);
|
|
764
784
|
}
|
|
765
|
-
if (tokens?.refresh_token && !forceReauthorization)
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
785
|
+
if (tokens?.refresh_token && !forceReauthorization) {
|
|
786
|
+
let newTokens;
|
|
787
|
+
try {
|
|
788
|
+
newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
789
|
+
metadata,
|
|
790
|
+
clientInformation,
|
|
791
|
+
refreshToken: tokens.refresh_token,
|
|
792
|
+
resource,
|
|
793
|
+
addClientAuthentication: provider.addClientAuthentication,
|
|
794
|
+
dpop: await provider.dpop?.(),
|
|
795
|
+
fetchFn
|
|
796
|
+
});
|
|
797
|
+
} catch (error) {
|
|
798
|
+
if (error instanceof InsecureTokenEndpointError) throw error;
|
|
799
|
+
if (!(error instanceof require_src.OAuthError) || error.code === require_src.OAuthErrorCode.ServerError) console.warn(`[mcp-sdk] Could not refresh OAuth tokens; falling back to a new authorization request. Cause: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`);
|
|
800
|
+
else throw error;
|
|
801
|
+
}
|
|
802
|
+
if (newTokens) {
|
|
803
|
+
await provider.saveTokens({
|
|
804
|
+
...newTokens,
|
|
805
|
+
issuer
|
|
806
|
+
}, infoCtx);
|
|
807
|
+
return "AUTHORIZED";
|
|
808
|
+
}
|
|
782
809
|
}
|
|
783
810
|
const state = provider.state ? await provider.state() : void 0;
|
|
784
811
|
const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
|
|
@@ -822,6 +849,17 @@ function isHttpsUrl(value) {
|
|
|
822
849
|
return false;
|
|
823
850
|
}
|
|
824
851
|
}
|
|
852
|
+
/**
|
|
853
|
+
* Selects the RFC 8707 resource indicator for an MCP server: the provider's
|
|
854
|
+
* {@linkcode OAuthClientProvider.validateResourceURL | validateResourceURL} result when
|
|
855
|
+
* implemented, otherwise the protected resource metadata's `resource` (checked against the
|
|
856
|
+
* server URL with `checkResourceAllowed`), or `undefined` when there is no metadata.
|
|
857
|
+
*
|
|
858
|
+
* The result is a parsed `URL`, so a pathless indicator such as `https://example.com` has
|
|
859
|
+
* the `href` `https://example.com/`. {@linkcode auth} therefore sends the metadata string
|
|
860
|
+
* verbatim instead of this URL's `href` (#1968); callers that emit the `resource`
|
|
861
|
+
* parameter themselves should do the same.
|
|
862
|
+
*/
|
|
825
863
|
async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
|
826
864
|
const defaultResource = require_src.resourceUrlFromServerUrl(serverUrl);
|
|
827
865
|
if (provider.validateResourceURL) return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
|
|
@@ -832,15 +870,22 @@ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
|
|
832
870
|
})) throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)`);
|
|
833
871
|
return new URL(resourceMetadata.resource);
|
|
834
872
|
}
|
|
873
|
+
/** Auth-scheme challenge tokens {@linkcode extractWWWAuthenticateParams} recognizes. */
|
|
874
|
+
const RECOGNIZED_CHALLENGE_SCHEMES = new Set(["bearer", "dpop"]);
|
|
835
875
|
/**
|
|
836
876
|
* Extract `resource_metadata`, `scope`, `error`, and `error_description` from a
|
|
837
877
|
* `WWW-Authenticate` header.
|
|
878
|
+
*
|
|
879
|
+
* Recognizes both the `Bearer` scheme (RFC 6750) and the `DPoP` scheme (RFC 9449 §7.1,
|
|
880
|
+
* SEP-1932) — a DPoP-protected resource's challenge carries the same parameters under `DPoP`
|
|
881
|
+
* instead of `Bearer`, and this must still surface `resource_metadata`/`scope` from it for
|
|
882
|
+
* discovery and SEP-2350 step-up to work against such a resource.
|
|
838
883
|
*/
|
|
839
884
|
function extractWWWAuthenticateParams(res) {
|
|
840
885
|
const authenticateHeader = res.headers.get("WWW-Authenticate");
|
|
841
886
|
if (!authenticateHeader) return {};
|
|
842
887
|
const [type, scheme] = authenticateHeader.split(" ");
|
|
843
|
-
if (type
|
|
888
|
+
if (!type || !RECOGNIZED_CHALLENGE_SCHEMES.has(type.toLowerCase()) || !scheme) return {};
|
|
844
889
|
const resourceMetadataMatch = extractFieldFromWwwAuth(res, "resource_metadata") || void 0;
|
|
845
890
|
let resourceMetadataUrl;
|
|
846
891
|
if (resourceMetadataMatch) try {
|
|
@@ -1138,6 +1183,9 @@ async function discoverOAuthServerInfo(serverUrl, opts) {
|
|
|
1138
1183
|
resourceMetadata
|
|
1139
1184
|
};
|
|
1140
1185
|
}
|
|
1186
|
+
function resourceIndicatorToString(resource) {
|
|
1187
|
+
return typeof resource === "string" ? resource : resource.href;
|
|
1188
|
+
}
|
|
1141
1189
|
/**
|
|
1142
1190
|
* Begins the authorization flow with the given server, by generating a PKCE challenge and constructing the authorization URL.
|
|
1143
1191
|
*/
|
|
@@ -1159,7 +1207,7 @@ async function startAuthorization(authorizationServerUrl, { metadata, clientInfo
|
|
|
1159
1207
|
if (state) authorizationUrl.searchParams.set("state", state);
|
|
1160
1208
|
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
|
1161
1209
|
if (scope?.split(" ").includes("offline_access")) authorizationUrl.searchParams.append("prompt", "consent");
|
|
1162
|
-
if (resource) authorizationUrl.searchParams.set("resource", resource
|
|
1210
|
+
if (resource) authorizationUrl.searchParams.set("resource", resourceIndicatorToString(resource));
|
|
1163
1211
|
return {
|
|
1164
1212
|
authorizationUrl,
|
|
1165
1213
|
codeVerifier
|
|
@@ -1188,20 +1236,35 @@ function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redire
|
|
|
1188
1236
|
* Internal helper to execute a token request with the given parameters.
|
|
1189
1237
|
* Used by {@linkcode exchangeAuthorization}, {@linkcode refreshAuthorization}, and {@linkcode fetchToken}.
|
|
1190
1238
|
*/
|
|
1191
|
-
async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
|
|
1239
|
+
async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, dpop, fetchFn }) {
|
|
1192
1240
|
const tokenUrl = assertSecureTokenEndpoint(metadata?.token_endpoint ?? new URL("/token", authorizationServerUrl));
|
|
1193
1241
|
const headers = new Headers({
|
|
1194
1242
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
1195
1243
|
Accept: "application/json"
|
|
1196
1244
|
});
|
|
1197
|
-
if (resource) tokenRequestParams.set("resource", resource
|
|
1198
|
-
if (addClientAuthentication)
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1245
|
+
if (resource) tokenRequestParams.set("resource", resourceIndicatorToString(resource));
|
|
1246
|
+
if (!addClientAuthentication && clientInformation) applyClientAuthentication(selectClientAuthMethod(clientInformation, metadata?.token_endpoint_auth_methods_supported ?? []), clientInformation, headers, tokenRequestParams);
|
|
1247
|
+
const requestOnce = async () => {
|
|
1248
|
+
const requestHeaders = new Headers(headers);
|
|
1249
|
+
if (addClientAuthentication) await addClientAuthentication(requestHeaders, tokenRequestParams, tokenUrl, metadata);
|
|
1250
|
+
if (dpop) requestHeaders.set("DPoP", await dpop.buildProof({
|
|
1251
|
+
htm: "POST",
|
|
1252
|
+
htu: tokenUrl
|
|
1253
|
+
}));
|
|
1254
|
+
return (fetchFn ?? fetch)(tokenUrl, {
|
|
1255
|
+
method: "POST",
|
|
1256
|
+
headers: requestHeaders,
|
|
1257
|
+
body: tokenRequestParams
|
|
1258
|
+
});
|
|
1259
|
+
};
|
|
1260
|
+
let response = await requestOnce();
|
|
1261
|
+
if (dpop && response.status === 400) {
|
|
1262
|
+
if ((await response.clone().json().catch(() => {}))?.error === require_src.OAuthErrorCode.UseDpopNonce) {
|
|
1263
|
+
dpop.observeNonce(response, tokenUrl);
|
|
1264
|
+
response = await requestOnce();
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
dpop?.observeNonce(response, tokenUrl);
|
|
1205
1268
|
if (!response.ok) throw await parseErrorResponse(response);
|
|
1206
1269
|
const json = await response.json();
|
|
1207
1270
|
try {
|
|
@@ -1223,7 +1286,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
|
|
|
1223
1286
|
* @returns Promise resolving to OAuth tokens
|
|
1224
1287
|
* @throws {Error} When token exchange fails or authentication is invalid
|
|
1225
1288
|
*/
|
|
1226
|
-
async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, iss, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }) {
|
|
1289
|
+
async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, iss, codeVerifier, redirectUri, resource, addClientAuthentication, dpop, fetchFn }) {
|
|
1227
1290
|
validateAuthorizationResponseIssuer({
|
|
1228
1291
|
iss,
|
|
1229
1292
|
expectedIssuer: metadata?.issuer,
|
|
@@ -1235,6 +1298,7 @@ async function exchangeAuthorization(authorizationServerUrl, { metadata, clientI
|
|
|
1235
1298
|
clientInformation,
|
|
1236
1299
|
addClientAuthentication,
|
|
1237
1300
|
resource,
|
|
1301
|
+
dpop,
|
|
1238
1302
|
fetchFn
|
|
1239
1303
|
});
|
|
1240
1304
|
}
|
|
@@ -1250,7 +1314,7 @@ async function exchangeAuthorization(authorizationServerUrl, { metadata, clientI
|
|
|
1250
1314
|
* @returns Promise resolving to OAuth tokens (preserves original `refresh_token` if not replaced)
|
|
1251
1315
|
* @throws {Error} When token refresh fails or authentication is invalid
|
|
1252
1316
|
*/
|
|
1253
|
-
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
|
|
1317
|
+
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, dpop, fetchFn }) {
|
|
1254
1318
|
return {
|
|
1255
1319
|
refresh_token: refreshToken,
|
|
1256
1320
|
...await executeTokenRequest(authorizationServerUrl, {
|
|
@@ -1262,6 +1326,7 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
|
|
|
1262
1326
|
clientInformation,
|
|
1263
1327
|
addClientAuthentication,
|
|
1264
1328
|
resource,
|
|
1329
|
+
dpop,
|
|
1265
1330
|
fetchFn
|
|
1266
1331
|
})
|
|
1267
1332
|
};
|
|
@@ -1314,6 +1379,7 @@ async function fetchToken(provider, authorizationServerUrl, { metadata, resource
|
|
|
1314
1379
|
clientInformation: clientInformation ?? void 0,
|
|
1315
1380
|
addClientAuthentication: provider.addClientAuthentication,
|
|
1316
1381
|
resource,
|
|
1382
|
+
dpop: await provider.dpop?.(),
|
|
1317
1383
|
fetchFn
|
|
1318
1384
|
});
|
|
1319
1385
|
}
|
|
@@ -2400,7 +2466,7 @@ function classifyNetworkError(error, context) {
|
|
|
2400
2466
|
if (context.environment === "browser" && isOpaqueFetchTypeError(error)) return { kind: "legacy" };
|
|
2401
2467
|
return {
|
|
2402
2468
|
kind: "error",
|
|
2403
|
-
error: new require_src.SdkError(require_src.SdkErrorCode.EraNegotiationFailed, `Version negotiation probe failed: ${describeError(error)}`, { cause: error })
|
|
2469
|
+
error: new require_src.SdkError(require_src.SdkErrorCode.EraNegotiationFailed, `Version negotiation probe failed: ${describeError(error)}`, { cause: error }, { cause: error })
|
|
2404
2470
|
};
|
|
2405
2471
|
}
|
|
2406
2472
|
function isOpaqueFetchTypeError(error) {
|
|
@@ -4431,6 +4497,160 @@ async function exchangeJwtAuthGrant(options) {
|
|
|
4431
4497
|
return parseResult.data;
|
|
4432
4498
|
}
|
|
4433
4499
|
|
|
4500
|
+
//#endregion
|
|
4501
|
+
//#region src/client/dpop.ts
|
|
4502
|
+
/** Asymmetric JWS algorithms usable for a DPoP proof (RFC 9449 §11.6 forbids symmetric algs and `none`). */
|
|
4503
|
+
const DPOP_SUPPORTED_ALGS = [
|
|
4504
|
+
"ES256",
|
|
4505
|
+
"ES384",
|
|
4506
|
+
"ES512",
|
|
4507
|
+
"RS256",
|
|
4508
|
+
"RS384",
|
|
4509
|
+
"RS512",
|
|
4510
|
+
"PS256",
|
|
4511
|
+
"PS384",
|
|
4512
|
+
"PS512",
|
|
4513
|
+
"EdDSA"
|
|
4514
|
+
];
|
|
4515
|
+
const DEFAULT_DPOP_ALG = "ES256";
|
|
4516
|
+
const DPOP_TYP = "dpop+jwt";
|
|
4517
|
+
async function importJose() {
|
|
4518
|
+
if (globalThis.crypto === void 0) throw new TypeError("crypto is not available, please ensure you have Web Crypto API support for older Node.js versions (see https://github.com/modelcontextprotocol/typescript-sdk#nodejs-web-crypto-globalthiscrypto-compatibility)");
|
|
4519
|
+
return import("jose");
|
|
4520
|
+
}
|
|
4521
|
+
/** Generate an asymmetric DPoP signing key pair. Non-extractable by default (RFC 9449 §11). */
|
|
4522
|
+
async function generateDpopKeyPair(options = {}) {
|
|
4523
|
+
const alg = options.alg ?? DEFAULT_DPOP_ALG;
|
|
4524
|
+
const jose = await importJose();
|
|
4525
|
+
const { publicKey, privateKey } = await jose.generateKeyPair(alg, { extractable: options.extractable ?? false });
|
|
4526
|
+
const publicJwk = await jose.exportJWK(publicKey);
|
|
4527
|
+
return {
|
|
4528
|
+
privateKey,
|
|
4529
|
+
publicKey,
|
|
4530
|
+
publicJwk,
|
|
4531
|
+
thumbprint: await jose.calculateJwkThumbprint(publicJwk, "sha256"),
|
|
4532
|
+
alg
|
|
4533
|
+
};
|
|
4534
|
+
}
|
|
4535
|
+
/**
|
|
4536
|
+
* Compute the `ath` claim for a DPoP proof presented alongside an access token: the
|
|
4537
|
+
* base64url-encoded SHA-256 digest of the ASCII access-token value (RFC 9449 §4.1).
|
|
4538
|
+
*
|
|
4539
|
+
* Uses Web Crypto (`crypto.subtle`) rather than a Node-only hashing API so this stays usable
|
|
4540
|
+
* in browser and edge runtimes.
|
|
4541
|
+
*/
|
|
4542
|
+
async function accessTokenHash(accessToken) {
|
|
4543
|
+
if (globalThis.crypto?.subtle === void 0) throw new TypeError("crypto.subtle is not available, please ensure you have Web Crypto API support for older Node.js versions (see https://github.com/modelcontextprotocol/typescript-sdk#nodejs-web-crypto-globalthiscrypto-compatibility)");
|
|
4544
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(accessToken));
|
|
4545
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
4546
|
+
}
|
|
4547
|
+
function base64UrlEncode(bytes) {
|
|
4548
|
+
let binary = "";
|
|
4549
|
+
for (const byte of bytes) binary += String.fromCodePoint(byte);
|
|
4550
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
4551
|
+
}
|
|
4552
|
+
/** Strip the query and fragment from a URL, per RFC 9449 §4.2 (`htu` MUST NOT contain either). */
|
|
4553
|
+
function stripQueryAndFragment(url) {
|
|
4554
|
+
const u = new URL(url.toString());
|
|
4555
|
+
return `${u.origin}${u.pathname}`;
|
|
4556
|
+
}
|
|
4557
|
+
/** A fresh, cryptographically random `jti` (RFC 9449 §4.2 — MUST be unique per proof). */
|
|
4558
|
+
function randomJti() {
|
|
4559
|
+
const bytes = new Uint8Array(16);
|
|
4560
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
4561
|
+
return base64UrlEncode(bytes);
|
|
4562
|
+
}
|
|
4563
|
+
/**
|
|
4564
|
+
* A DPoP signing identity plus the small amount of state RFC 9449 requires across requests:
|
|
4565
|
+
* the key pair and, per origin, the most recently server-supplied nonce (RFC 9449 §8/§9).
|
|
4566
|
+
*
|
|
4567
|
+
* One `DpopSession` is meant to live for the lifetime of a single OAuth client registration —
|
|
4568
|
+
* the token endpoint and the resource server are different origins and get independent nonce
|
|
4569
|
+
* slots, so a nonce challenge from one never leaks into proofs sent to the other.
|
|
4570
|
+
*/
|
|
4571
|
+
var DpopSession = class DpopSession {
|
|
4572
|
+
nonces = /* @__PURE__ */ new Map();
|
|
4573
|
+
constructor(keyPair) {
|
|
4574
|
+
this.keyPair = keyPair;
|
|
4575
|
+
}
|
|
4576
|
+
/** Create a session with a fresh key pair, or reuse a caller-supplied one. */
|
|
4577
|
+
static async create(options = {}) {
|
|
4578
|
+
return new DpopSession(options.keyPair ?? await generateDpopKeyPair({ alg: options.alg }));
|
|
4579
|
+
}
|
|
4580
|
+
/** RFC 7638 JWK SHA-256 thumbprint of the signing key — matches the token's `cnf.jkt` once bound. */
|
|
4581
|
+
get thumbprint() {
|
|
4582
|
+
return this.keyPair.thumbprint;
|
|
4583
|
+
}
|
|
4584
|
+
/** The JWS algorithm this session signs proofs with. */
|
|
4585
|
+
get alg() {
|
|
4586
|
+
return this.keyPair.alg;
|
|
4587
|
+
}
|
|
4588
|
+
/** The public JWK embedded in every proof's `jwk` header parameter. */
|
|
4589
|
+
get publicJwk() {
|
|
4590
|
+
return this.keyPair.publicJwk;
|
|
4591
|
+
}
|
|
4592
|
+
/** The remembered nonce for `url`'s origin, if the server has issued one (RFC 9449 §8/§9). */
|
|
4593
|
+
nonceFor(url) {
|
|
4594
|
+
return this.nonces.get(new URL(url.toString()).origin);
|
|
4595
|
+
}
|
|
4596
|
+
/** Record a server-supplied nonce for `url`'s origin (newest-wins, RFC 9449 §8.2). */
|
|
4597
|
+
rememberNonce(url, nonce) {
|
|
4598
|
+
this.nonces.set(new URL(url.toString()).origin, nonce);
|
|
4599
|
+
}
|
|
4600
|
+
/**
|
|
4601
|
+
* Capture a `DPoP-Nonce` response header, if present, for `url`'s origin. RFC 9449 §8.2 says a
|
|
4602
|
+
* fresh nonce may ride on *any* response (success or failure), so call this unconditionally
|
|
4603
|
+
* after every request, not only on a `use_dpop_nonce` challenge.
|
|
4604
|
+
*/
|
|
4605
|
+
observeNonce(response, url) {
|
|
4606
|
+
const nonce = response.headers.get("dpop-nonce");
|
|
4607
|
+
if (nonce) this.rememberNonce(url, nonce);
|
|
4608
|
+
}
|
|
4609
|
+
/**
|
|
4610
|
+
* Build a fresh DPoP proof JWT. Always mints a new `jti` — proofs are never cached or reused,
|
|
4611
|
+
* since RFC 9449 §4.3 step 9 requires each to be presented at most once.
|
|
4612
|
+
*/
|
|
4613
|
+
async buildProof(request) {
|
|
4614
|
+
const jose = await importJose();
|
|
4615
|
+
const htu = stripQueryAndFragment(request.htu);
|
|
4616
|
+
const nonce = request.nonce ?? this.nonceFor(request.htu);
|
|
4617
|
+
const payload = {
|
|
4618
|
+
jti: randomJti(),
|
|
4619
|
+
htm: request.htm.toUpperCase(),
|
|
4620
|
+
htu,
|
|
4621
|
+
iat: Math.floor(Date.now() / 1e3)
|
|
4622
|
+
};
|
|
4623
|
+
if (request.accessToken !== void 0) payload.ath = await accessTokenHash(request.accessToken);
|
|
4624
|
+
if (nonce !== void 0) payload.nonce = nonce;
|
|
4625
|
+
return new jose.SignJWT(payload).setProtectedHeader({
|
|
4626
|
+
alg: this.keyPair.alg,
|
|
4627
|
+
typ: DPOP_TYP,
|
|
4628
|
+
jwk: this.keyPair.publicJwk
|
|
4629
|
+
}).sign(this.keyPair.privateKey);
|
|
4630
|
+
}
|
|
4631
|
+
};
|
|
4632
|
+
/**
|
|
4633
|
+
* Whether `wwwAuthenticate` advertises a `DPoP` auth-scheme challenge (matched at the start of the
|
|
4634
|
+
* header or after a comma, per RFC 9110 §11.6.1 — a server may list several schemes together, e.g.
|
|
4635
|
+
* `Bearer …, DPoP …`).
|
|
4636
|
+
*/
|
|
4637
|
+
function hasDpopChallenge(wwwAuthenticate) {
|
|
4638
|
+
return /(?:^|,)\s*dpop(?:\s|$|,)/i.test(wwwAuthenticate);
|
|
4639
|
+
}
|
|
4640
|
+
/**
|
|
4641
|
+
* Whether `response` is a resource-server `use_dpop_nonce` challenge (RFC 9449 §9): a `401` whose
|
|
4642
|
+
* `WWW-Authenticate` header carries a `DPoP` challenge with `error="use_dpop_nonce"`.
|
|
4643
|
+
*
|
|
4644
|
+
* A conformant retry re-signs the proof with the nonce {@linkcode DpopSession.observeNonce} just
|
|
4645
|
+
* captured — never resend the original proof: RFC 9449 §4.2 requires a unique `jti` per proof, and
|
|
4646
|
+
* replaying one across the challenge/retry boundary is itself a violation.
|
|
4647
|
+
*/
|
|
4648
|
+
function isDpopNonceChallenge(response) {
|
|
4649
|
+
if (response.status !== 401) return false;
|
|
4650
|
+
const wwwAuthenticate = response.headers.get("www-authenticate") ?? "";
|
|
4651
|
+
return hasDpopChallenge(wwwAuthenticate) && /use_dpop_nonce/i.test(wwwAuthenticate);
|
|
4652
|
+
}
|
|
4653
|
+
|
|
4434
4654
|
//#endregion
|
|
4435
4655
|
//#region src/client/middleware.ts
|
|
4436
4656
|
/**
|
|
@@ -4441,6 +4661,10 @@ async function exchangeJwtAuthGrant(options) {
|
|
|
4441
4661
|
* - Handle 401 responses by attempting re-authentication
|
|
4442
4662
|
* - Retry the original request after successful auth
|
|
4443
4663
|
* - Handle OAuth errors appropriately ({@linkcode index.OAuthErrorCode.InvalidClient | OAuthErrorCode.InvalidClient}, etc.)
|
|
4664
|
+
* - When {@linkcode OAuthClientProvider.dpop | provider.dpop()} is implemented, present DPoP-bound
|
|
4665
|
+
* tokens with the `DPoP` scheme plus a per-request proof (RFC 9449 / SEP-1932) by composing
|
|
4666
|
+
* {@linkcode withDpopFromProvider} underneath — so a `use_dpop_nonce` challenge is retried
|
|
4667
|
+
* inline on every attempt, independently of the single re-authentication retry here
|
|
4444
4668
|
*
|
|
4445
4669
|
* The `baseUrl` parameter is optional and defaults to using the domain from the request URL.
|
|
4446
4670
|
* However, you should explicitly provide `baseUrl` when:
|
|
@@ -4459,7 +4683,8 @@ async function exchangeJwtAuthGrant(options) {
|
|
|
4459
4683
|
* @param baseUrl - Base URL for OAuth server discovery (defaults to request URL domain)
|
|
4460
4684
|
* @returns A fetch middleware function
|
|
4461
4685
|
*/
|
|
4462
|
-
const withOAuth = (provider, baseUrl) => (
|
|
4686
|
+
const withOAuth = (provider, baseUrl) => (baseNext) => {
|
|
4687
|
+
const next = provider.dpop ? withDpopFromProvider(provider)(baseNext) : baseNext;
|
|
4463
4688
|
return async (input, init) => {
|
|
4464
4689
|
const makeRequest = async () => {
|
|
4465
4690
|
const headers = new Headers(init?.headers);
|
|
@@ -4477,7 +4702,7 @@ const withOAuth = (provider, baseUrl) => (next) => {
|
|
|
4477
4702
|
serverUrl: baseUrl || (typeof input === "string" ? new URL(input).origin : input.origin),
|
|
4478
4703
|
resourceMetadataUrl,
|
|
4479
4704
|
scope,
|
|
4480
|
-
fetchFn:
|
|
4705
|
+
fetchFn: baseNext
|
|
4481
4706
|
});
|
|
4482
4707
|
if (result === "REDIRECT") throw new UnauthorizedError("Authentication requires user authorization - redirect initiated");
|
|
4483
4708
|
if (result !== "AUTHORIZED") throw new UnauthorizedError(`Authentication failed with result: ${result}`);
|
|
@@ -4491,6 +4716,90 @@ const withOAuth = (provider, baseUrl) => (next) => {
|
|
|
4491
4716
|
};
|
|
4492
4717
|
};
|
|
4493
4718
|
/**
|
|
4719
|
+
* Creates a fetch wrapper that presents an access token using the `DPoP` Authorization scheme
|
|
4720
|
+
* (RFC 9449 / SEP-1932) instead of `Bearer`: every request carries `Authorization: DPoP <token>`
|
|
4721
|
+
* plus a fresh `DPoP` proof bound to that request's method and URL, a resource-server
|
|
4722
|
+
* `use_dpop_nonce` challenge (RFC 9449 §9) is retried once, inline, with the server-supplied nonce,
|
|
4723
|
+
* and a `DPoP-Nonce` delivered on any response is remembered for the next proof (RFC 9449 §8.2).
|
|
4724
|
+
*
|
|
4725
|
+
* Because it wraps `fetch` itself, the proof is always bound to the request actually sent and every
|
|
4726
|
+
* response is observed — which is why the MCP transports apply this wrapper internally (via
|
|
4727
|
+
* {@linkcode withDpopFromProvider}) when their `authProvider` implements
|
|
4728
|
+
* {@linkcode OAuthClientProvider.dpop | dpop()}, rather than threading request context through
|
|
4729
|
+
* {@linkcode AuthProvider}.
|
|
4730
|
+
*
|
|
4731
|
+
* Use this directly when you already manage the access token yourself (a non-OAuth token source,
|
|
4732
|
+
* or credentials obtained out-of-band) and only need DPoP's request-signing behavior — e.g.
|
|
4733
|
+
* `fetch: withDpop(session, getToken)(fetch)` alongside a minimal `authProvider: { token }`.
|
|
4734
|
+
*
|
|
4735
|
+
* @param session - The DPoP signing session (key pair + nonce state), or a function resolving to
|
|
4736
|
+
* it. Reuse the same session across requests to the same server so its nonce state persists.
|
|
4737
|
+
* When the function resolves to `undefined` the request passes through unchanged.
|
|
4738
|
+
* @param getToken - Returns the current access token, or `undefined` if none is available (the
|
|
4739
|
+
* request passes through unchanged — any `Authorization` header already on it is left as is).
|
|
4740
|
+
* @returns A fetch middleware function
|
|
4741
|
+
*/
|
|
4742
|
+
const withDpop = (session, getToken) => (next) => {
|
|
4743
|
+
const resolveSession = typeof session === "function" ? session : () => session;
|
|
4744
|
+
return async (input, init) => {
|
|
4745
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
4746
|
+
const url = new URL(input.toString());
|
|
4747
|
+
const activeSession = await resolveSession();
|
|
4748
|
+
if (!activeSession) return next(input, init);
|
|
4749
|
+
const makeRequest = async () => {
|
|
4750
|
+
const accessToken = await getToken();
|
|
4751
|
+
if (!accessToken) return next(input, init);
|
|
4752
|
+
const headers = new Headers(init?.headers);
|
|
4753
|
+
const proof = await activeSession.buildProof({
|
|
4754
|
+
htm: method,
|
|
4755
|
+
htu: url,
|
|
4756
|
+
accessToken
|
|
4757
|
+
});
|
|
4758
|
+
headers.set("Authorization", `DPoP ${accessToken}`);
|
|
4759
|
+
headers.set("DPoP", proof);
|
|
4760
|
+
return next(input, {
|
|
4761
|
+
...init,
|
|
4762
|
+
headers
|
|
4763
|
+
});
|
|
4764
|
+
};
|
|
4765
|
+
let response = await makeRequest();
|
|
4766
|
+
if (isDpopNonceChallenge(response) && response.headers.has("dpop-nonce")) {
|
|
4767
|
+
activeSession.observeNonce(response, url);
|
|
4768
|
+
await response.text?.().catch(() => {});
|
|
4769
|
+
response = await makeRequest();
|
|
4770
|
+
}
|
|
4771
|
+
activeSession.observeNonce(response, url);
|
|
4772
|
+
return response;
|
|
4773
|
+
};
|
|
4774
|
+
};
|
|
4775
|
+
/**
|
|
4776
|
+
* {@linkcode withDpop} driven by an {@linkcode OAuthClientProvider}: the session comes from
|
|
4777
|
+
* {@linkcode OAuthClientProvider.dpop | provider.dpop()} and the token from
|
|
4778
|
+
* {@linkcode OAuthClientProvider.tokens | provider.tokens()} — presented with the DPoP scheme only
|
|
4779
|
+
* when the AS actually issued `token_type: "DPoP"` (RFC 9449 §7.1); a Bearer token passes through
|
|
4780
|
+
* untouched.
|
|
4781
|
+
*
|
|
4782
|
+
* This is what the MCP transports and {@linkcode withOAuth} compose internally when the provider
|
|
4783
|
+
* implements `dpop()`. Use it directly only when building your own fetch pipeline around an
|
|
4784
|
+
* `OAuthClientProvider` (e.g. a custom re-authorization middleware) — place it *innermost*, below
|
|
4785
|
+
* whatever sets `Authorization: Bearer` and handles 401 re-authentication.
|
|
4786
|
+
*/
|
|
4787
|
+
const withDpopFromProvider = (provider) => withDpop(async () => {
|
|
4788
|
+
try {
|
|
4789
|
+
return await provider.dpop?.();
|
|
4790
|
+
} catch (error) {
|
|
4791
|
+
throw markAuthSeamEscape(error);
|
|
4792
|
+
}
|
|
4793
|
+
}, async () => {
|
|
4794
|
+
let tokens;
|
|
4795
|
+
try {
|
|
4796
|
+
tokens = await provider.tokens();
|
|
4797
|
+
} catch (error) {
|
|
4798
|
+
throw markAuthSeamEscape(error);
|
|
4799
|
+
}
|
|
4800
|
+
return tokens?.token_type?.toLowerCase() === "dpop" ? tokens.access_token : void 0;
|
|
4801
|
+
});
|
|
4802
|
+
/**
|
|
4494
4803
|
* Creates a fetch middleware that logs HTTP requests and responses.
|
|
4495
4804
|
*
|
|
4496
4805
|
* When called without arguments `withLogging()`, it uses the default logger that:
|
|
@@ -4688,6 +4997,7 @@ var SSEClientTransport = class {
|
|
|
4688
4997
|
_skipIssuerMetadataValidation;
|
|
4689
4998
|
_fetch;
|
|
4690
4999
|
_fetchWithInit;
|
|
5000
|
+
_dpop;
|
|
4691
5001
|
_protocolVersion;
|
|
4692
5002
|
onclose;
|
|
4693
5003
|
onerror;
|
|
@@ -4699,32 +5009,33 @@ var SSEClientTransport = class {
|
|
|
4699
5009
|
this._eventSourceInit = opts?.eventSourceInit;
|
|
4700
5010
|
this._requestInit = opts?.requestInit;
|
|
4701
5011
|
this._skipIssuerMetadataValidation = opts?.skipIssuerMetadataValidation;
|
|
5012
|
+
this._fetch = opts?.fetch;
|
|
4702
5013
|
if (isOAuthClientProvider(opts?.authProvider)) {
|
|
4703
5014
|
this._oauthProvider = opts.authProvider;
|
|
4704
5015
|
this._authProvider = adaptOAuthProvider(opts.authProvider, { skipIssuerMetadataValidation: opts.skipIssuerMetadataValidation });
|
|
5016
|
+
if (opts.authProvider.dpop) {
|
|
5017
|
+
this._dpop = withDpopFromProvider(opts.authProvider);
|
|
5018
|
+
this._fetch = this._dpop(opts.fetch ?? fetch);
|
|
5019
|
+
}
|
|
4705
5020
|
} else this._authProvider = opts?.authProvider;
|
|
4706
|
-
this._fetch = opts?.fetch;
|
|
4707
5021
|
this._fetchWithInit = require_src.createFetchWithInit(opts?.fetch, opts?.requestInit);
|
|
4708
5022
|
}
|
|
4709
5023
|
_last401Response;
|
|
4710
5024
|
async _commonHeaders() {
|
|
4711
|
-
const headers =
|
|
5025
|
+
const headers = new Headers(this._requestInit?.headers || void 0);
|
|
4712
5026
|
let token;
|
|
4713
5027
|
try {
|
|
4714
5028
|
token = await this._authProvider?.token();
|
|
4715
5029
|
} catch (error) {
|
|
4716
5030
|
throw markAuthSeamEscape(error);
|
|
4717
5031
|
}
|
|
4718
|
-
if (token) headers
|
|
4719
|
-
if (this._protocolVersion) headers
|
|
4720
|
-
|
|
4721
|
-
return new Headers({
|
|
4722
|
-
...headers,
|
|
4723
|
-
...extraHeaders
|
|
4724
|
-
});
|
|
5032
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
5033
|
+
if (this._protocolVersion) headers.set("mcp-protocol-version", this._protocolVersion);
|
|
5034
|
+
return headers;
|
|
4725
5035
|
}
|
|
4726
5036
|
_startOrAuth() {
|
|
4727
|
-
const
|
|
5037
|
+
const eventSourceFetch = this._eventSourceInit?.fetch;
|
|
5038
|
+
const fetchImpl = eventSourceFetch ? this._dpop?.(eventSourceFetch) ?? eventSourceFetch : this._fetch ?? fetch;
|
|
4728
5039
|
return new Promise((resolve, reject) => {
|
|
4729
5040
|
this._eventSource = new eventsource.EventSource(this._url.href, {
|
|
4730
5041
|
...this._eventSourceInit,
|
|
@@ -4902,6 +5213,7 @@ const DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS = {
|
|
|
4902
5213
|
*/
|
|
4903
5214
|
const RESERVED_REQUEST_HEADER_NAMES = new Set([
|
|
4904
5215
|
"authorization",
|
|
5216
|
+
"dpop",
|
|
4905
5217
|
"content-type",
|
|
4906
5218
|
"mcp-protocol-version",
|
|
4907
5219
|
"mcp-method",
|
|
@@ -4977,11 +5289,12 @@ var StreamableHTTPClientTransport = class {
|
|
|
4977
5289
|
this._scope = void 0;
|
|
4978
5290
|
this._requestInit = opts?.requestInit;
|
|
4979
5291
|
this._skipIssuerMetadataValidation = opts?.skipIssuerMetadataValidation;
|
|
5292
|
+
this._fetch = opts?.fetch;
|
|
4980
5293
|
if (isOAuthClientProvider(opts?.authProvider)) {
|
|
4981
5294
|
this._oauthProvider = opts.authProvider;
|
|
4982
5295
|
this._authProvider = adaptOAuthProvider(opts.authProvider, { skipIssuerMetadataValidation: opts.skipIssuerMetadataValidation });
|
|
5296
|
+
if (opts.authProvider.dpop) this._fetch = withDpopFromProvider(opts.authProvider)(opts.fetch ?? fetch);
|
|
4983
5297
|
} else this._authProvider = opts?.authProvider;
|
|
4984
|
-
this._fetch = opts?.fetch;
|
|
4985
5298
|
this._fetchWithInit = require_src.createFetchWithInit(opts?.fetch, opts?.requestInit);
|
|
4986
5299
|
this._sessionId = opts?.sessionId;
|
|
4987
5300
|
this._protocolVersion = opts?.protocolVersion;
|
|
@@ -5035,21 +5348,17 @@ var StreamableHTTPClientTransport = class {
|
|
|
5035
5348
|
});
|
|
5036
5349
|
}
|
|
5037
5350
|
async _commonHeaders() {
|
|
5038
|
-
const headers =
|
|
5351
|
+
const headers = new Headers(this._requestInit?.headers || void 0);
|
|
5039
5352
|
let token;
|
|
5040
5353
|
try {
|
|
5041
5354
|
token = await this._authProvider?.token();
|
|
5042
5355
|
} catch (error) {
|
|
5043
5356
|
throw markAuthSeamEscape(error);
|
|
5044
5357
|
}
|
|
5045
|
-
if (token) headers
|
|
5046
|
-
if (this._sessionId) headers
|
|
5047
|
-
if (this._protocolVersion) headers
|
|
5048
|
-
|
|
5049
|
-
return new Headers({
|
|
5050
|
-
...headers,
|
|
5051
|
-
...extraHeaders
|
|
5052
|
-
});
|
|
5358
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
5359
|
+
if (this._sessionId) headers.set("mcp-session-id", this._sessionId);
|
|
5360
|
+
if (this._protocolVersion) headers.set("mcp-protocol-version", this._protocolVersion);
|
|
5361
|
+
return headers;
|
|
5053
5362
|
}
|
|
5054
5363
|
/**
|
|
5055
5364
|
* Body-derived per-request headers: when an outgoing request carries a
|
|
@@ -5065,9 +5374,8 @@ var StreamableHTTPClientTransport = class {
|
|
|
5065
5374
|
if (typeof envelopeVersion !== "string") return;
|
|
5066
5375
|
headers.set("mcp-protocol-version", envelopeVersion);
|
|
5067
5376
|
headers.set("mcp-method", message.method);
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5070
|
-
if (nameHeader !== void 0) headers.set("mcp-name", require_src.encodeMcpParamValue(nameHeader));
|
|
5377
|
+
const source = require_src.mcpNameSource(message.method, message.params);
|
|
5378
|
+
if (source?.value !== void 0) headers.set("mcp-name", require_src.encodeMcpParamValue(source.value));
|
|
5071
5379
|
}
|
|
5072
5380
|
/**
|
|
5073
5381
|
* `true` when the outbound message is a single request carrying a
|
|
@@ -5509,6 +5817,8 @@ Object.defineProperty(exports, 'DEFAULT_NEGOTIATED_PROTOCOL_VERSION', {
|
|
|
5509
5817
|
}
|
|
5510
5818
|
});
|
|
5511
5819
|
exports.DEFAULT_REQUEST_TIMEOUT_MSEC = require_src.DEFAULT_REQUEST_TIMEOUT_MSEC;
|
|
5820
|
+
exports.DPOP_SUPPORTED_ALGS = DPOP_SUPPORTED_ALGS;
|
|
5821
|
+
exports.DpopSession = DpopSession;
|
|
5512
5822
|
Object.defineProperty(exports, 'INTERNAL_ERROR', {
|
|
5513
5823
|
enumerable: true,
|
|
5514
5824
|
get: function () {
|
|
@@ -5628,6 +5938,7 @@ exports.UnauthorizedError = UnauthorizedError;
|
|
|
5628
5938
|
exports.UnsupportedProtocolVersionError = require_src.UnsupportedProtocolVersionError;
|
|
5629
5939
|
exports.UriTemplate = require_src.UriTemplate;
|
|
5630
5940
|
exports.UrlElicitationRequiredError = require_src.UrlElicitationRequiredError;
|
|
5941
|
+
exports.accessTokenHash = accessTokenHash;
|
|
5631
5942
|
exports.applyMiddlewares = applyMiddlewares;
|
|
5632
5943
|
exports.assertCompleteRequestPrompt = require_src.assertCompleteRequestPrompt;
|
|
5633
5944
|
exports.assertCompleteRequestResourceTemplate = require_src.assertCompleteRequestResourceTemplate;
|
|
@@ -5651,9 +5962,11 @@ exports.extractResourceMetadataUrl = extractResourceMetadataUrl;
|
|
|
5651
5962
|
exports.extractWWWAuthenticateParams = extractWWWAuthenticateParams;
|
|
5652
5963
|
exports.fetchToken = fetchToken;
|
|
5653
5964
|
exports.fromJsonSchema = fromJsonSchema;
|
|
5965
|
+
exports.generateDpopKeyPair = generateDpopKeyPair;
|
|
5654
5966
|
exports.getDisplayName = require_src.getDisplayName;
|
|
5655
5967
|
exports.getSupportedElicitationModes = getSupportedElicitationModes;
|
|
5656
5968
|
exports.isCallToolResult = require_src.isCallToolResult;
|
|
5969
|
+
exports.isDpopNonceChallenge = isDpopNonceChallenge;
|
|
5657
5970
|
exports.isHttpsUrl = isHttpsUrl;
|
|
5658
5971
|
exports.isInitializeRequest = require_src.isInitializeRequest;
|
|
5659
5972
|
exports.isInitializedNotification = require_src.isInitializedNotification;
|
|
@@ -5684,6 +5997,8 @@ exports.specTypeSchemas = require_src.specTypeSchemas;
|
|
|
5684
5997
|
exports.startAuthorization = startAuthorization;
|
|
5685
5998
|
exports.validateAuthorizationResponseIssuer = validateAuthorizationResponseIssuer;
|
|
5686
5999
|
exports.validateClientMetadataUrl = validateClientMetadataUrl;
|
|
6000
|
+
exports.withDpop = withDpop;
|
|
6001
|
+
exports.withDpopFromProvider = withDpopFromProvider;
|
|
5687
6002
|
exports.withInputRequired = require_src.withInputRequired;
|
|
5688
6003
|
exports.withLogging = withLogging;
|
|
5689
6004
|
exports.withOAuth = withOAuth;
|