deadbyte-mcp 0.15.16 → 0.15.18
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 +99 -96
- package/PROVENANCE.json +1 -1
- 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/scripts/init-host-policy.ps1 +2 -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 +17 -2
- 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,4 +1,4 @@
|
|
|
1
|
-
import { $ as legacyProtocolVersions, A as isInitializeRequest, At as SdkErrorCode, B as DiscoverResultSchema, C as
|
|
1
|
+
import { $ as legacyProtocolVersions, A as isInitializeRequest, At as SdkErrorCode, B as DiscoverResultSchema, C as mcpNameSource, Ct as OAuthClientInformationFullSchema, D as assertCompleteRequestPrompt, Dt as OAuthTokensSchema, E as scanXMcpHeaderDeclarations, Et as OAuthProtectedResourceMetadataSchema, F as isJSONRPCRequest, Ft as stampErrorBrands, G as MissingRequiredClientCapabilityError, H as ListChangedOptionsBaseSchema, I as isJSONRPCResponse, J as UnsupportedProtocolVersionError, K as ProtocolError, L as isJSONRPCResultResponse, M as isInputRequiredResult, Mt as OAuthError, N as isJSONRPCErrorResponse, Nt as OAuthErrorCode, O as assertCompleteRequestResourceTemplate, Ot as OpenIdProviderDiscoveryMetadataSchema, P as isJSONRPCNotification, Pt as brandedHasInstance, Q as isModernProtocolVersion, R as isTaskAugmentedRequestParams, S as HEADER_MISMATCH_ERROR_CODE, St as IdJagTokenExchangeResponseSchema, T as encodeMcpParamValue, Tt as OAuthMetadataSchema, U as MODERN_WIRE_REVISION, V as JSONRPCMessageSchema, W as codecForVersion, X as ProtocolErrorCode, Y as UrlElicitationRequiredError, Z as SUPPORTED_MODERN_PROTOCOL_VERSIONS, _ as isSpecType, _t as SUPPORTED_PROTOCOL_VERSIONS, a as createFetchWithInit, at as INTERNAL_ERROR, b as withInputRequired, bt as checkResourceAllowed, c as deserializeMessage, ct as JSONRPC_VERSION, d as isJsonContentType, dt as METHOD_NOT_FOUND, et as modernProtocolVersions, f as mediaTypeEssence, ft as PARSE_ERROR, g as mergeCapabilities, gt as SUBSCRIPTION_ID_META_KEY, h as Protocol, ht as SERVER_INFO_META_KEY, i as UriTemplate, it as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, j as isInitializedNotification, jt as SdkHttpError, k as isCallToolResult, kt as SdkError, l as serializeMessage, lt as LATEST_PROTOCOL_VERSION, m as DEFAULT_REQUEST_TIMEOUT_MSEC, mt as RELATED_TASK_META_KEY, n as preloadSchemas, nt as CLIENT_CAPABILITIES_META_KEY, o as ReadBuffer, ot as INVALID_PARAMS, p as runInputRequiredFlow, pt as PROTOCOL_VERSION_META_KEY, q as ResourceNotFoundError, r as InMemoryTransport, rt as CLIENT_INFO_META_KEY, s as STDIO_DEFAULT_MAX_BUFFER_SIZE, st as INVALID_REQUEST, t as fromJsonSchema$1, tt as BAGGAGE_META_KEY, u as getDisplayName, ut as LOG_LEVEL_META_KEY, v as specTypeSchemas, vt as TRACEPARENT_META_KEY, w as buildMcpParamHeaders, wt as OAuthErrorResponseSchema, x as parseSchema, xt as resourceUrlFromServerUrl, y as resolveInputRequiredDriverConfig, yt as TRACESTATE_META_KEY, z as parseJSONRPCMessage } from "./src-DDAAhLnO.mjs";
|
|
2
2
|
import { CORS_IS_POSSIBLE, DefaultJsonSchemaValidator } from "@modelcontextprotocol/client/_shims";
|
|
3
3
|
import pkceChallenge from "pkce-challenge";
|
|
4
4
|
import { EventSource } from "eventsource";
|
|
@@ -266,6 +266,12 @@ async function handleOAuthUnauthorized(provider, ctx, extraAuthOptions) {
|
|
|
266
266
|
* not in scope. Providers that key storage on `ctx.issuer` MUST treat `ctx === undefined`
|
|
267
267
|
* as "return the most-recently-saved token set" (the only consumer is the resource server
|
|
268
268
|
* the token was minted for); providers that round-trip a single blob need no change.
|
|
269
|
+
*
|
|
270
|
+
* SEP-1932 (DPoP) note: DPoP request-signing is deliberately *not* done here. When the provider
|
|
271
|
+
* implements {@linkcode OAuthClientProvider.dpop | dpop()}, the transports wrap their
|
|
272
|
+
* resource-server `fetch` with {@linkcode withDpopFromProvider}, which upgrades the `Bearer` header
|
|
273
|
+
* this adapter produces to `DPoP` + proof for DPoP-bound tokens and handles nonce challenges — at
|
|
274
|
+
* the one layer that sees the real method, URL and response of every request.
|
|
269
275
|
*/
|
|
270
276
|
function adaptOAuthProvider(provider, extraAuthOptions) {
|
|
271
277
|
return {
|
|
@@ -602,6 +608,17 @@ async function parseErrorResponse(input) {
|
|
|
602
608
|
}
|
|
603
609
|
}
|
|
604
610
|
/**
|
|
611
|
+
* Recovering from a recoverable OAuth error discards stored credentials and silently starts a
|
|
612
|
+
* fresh authorization. On a headless client whose `redirectToAuthorization()` is a no-op that
|
|
613
|
+
* recovery is indistinguishable from nothing happening at all, so name the cause. The most
|
|
614
|
+
* common case is `invalid_grant` — an expired, revoked, or rotation-reuse-detected refresh
|
|
615
|
+
* token. See issue #2034.
|
|
616
|
+
*/
|
|
617
|
+
function warnCredentialInvalidation(provider, error, invalidated) {
|
|
618
|
+
const action = provider.invalidateCredentials === void 0 ? `retrying authorization without discarding the stored ${invalidated} (provider implements no invalidateCredentials())` : `invalidating the stored ${invalidated} and retrying authorization`;
|
|
619
|
+
console.warn(`[mcp-sdk] OAuth ${JSON.stringify(error.code)} — ${action}. Cause: ${JSON.stringify(error.message)}`);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
605
622
|
* Orchestrates the full auth flow with a server.
|
|
606
623
|
*
|
|
607
624
|
* This can be used as a single entry point for all authorization functionality,
|
|
@@ -613,10 +630,12 @@ async function auth(provider, options) {
|
|
|
613
630
|
} catch (error) {
|
|
614
631
|
if (error instanceof OAuthError) {
|
|
615
632
|
if (error.code === OAuthErrorCode.InvalidClient || error.code === OAuthErrorCode.UnauthorizedClient) {
|
|
633
|
+
warnCredentialInvalidation(provider, error, "client credentials and tokens");
|
|
616
634
|
await provider.invalidateCredentials?.("client");
|
|
617
635
|
await provider.invalidateCredentials?.("tokens");
|
|
618
636
|
return await authInternal(provider, options);
|
|
619
|
-
} else if (error.code === OAuthErrorCode.InvalidGrant) {
|
|
637
|
+
} else if (error.code === OAuthErrorCode.InvalidGrant || error.code === OAuthErrorCode.InvalidDpopProof) {
|
|
638
|
+
warnCredentialInvalidation(provider, error, "tokens");
|
|
620
639
|
await provider.invalidateCredentials?.("tokens");
|
|
621
640
|
return await authInternal(provider, options);
|
|
622
641
|
}
|
|
@@ -687,8 +706,9 @@ async function authInternal(provider, { serverUrl, authorizationCode, iss, scope
|
|
|
687
706
|
} else if (!issuersMatch(recordedIssuer, issuer)) throw new AuthorizationServerMismatchError(recordedIssuer, issuer);
|
|
688
707
|
}
|
|
689
708
|
if (freshDiscoveryState) await provider.saveDiscoveryState?.(freshDiscoveryState);
|
|
690
|
-
const
|
|
691
|
-
|
|
709
|
+
const selectedResource = await selectResourceURL(serverUrl, provider, resourceMetadata);
|
|
710
|
+
const resource = selectedResource && resourceMetadata && !provider.validateResourceURL ? resourceMetadata.resource : selectedResource;
|
|
711
|
+
if (resource) await provider.saveResourceUrl?.(resourceIndicatorToString(resource));
|
|
692
712
|
const resolvedScope = determineScope({
|
|
693
713
|
requestedScope: scope,
|
|
694
714
|
resourceMetadata,
|
|
@@ -759,23 +779,30 @@ async function authInternal(provider, { serverUrl, authorizationCode, iss, scope
|
|
|
759
779
|
};
|
|
760
780
|
await provider.saveTokens(tokens, infoCtx);
|
|
761
781
|
}
|
|
762
|
-
if (tokens?.refresh_token && !forceReauthorization)
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
782
|
+
if (tokens?.refresh_token && !forceReauthorization) {
|
|
783
|
+
let newTokens;
|
|
784
|
+
try {
|
|
785
|
+
newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
786
|
+
metadata,
|
|
787
|
+
clientInformation,
|
|
788
|
+
refreshToken: tokens.refresh_token,
|
|
789
|
+
resource,
|
|
790
|
+
addClientAuthentication: provider.addClientAuthentication,
|
|
791
|
+
dpop: await provider.dpop?.(),
|
|
792
|
+
fetchFn
|
|
793
|
+
});
|
|
794
|
+
} catch (error) {
|
|
795
|
+
if (error instanceof InsecureTokenEndpointError) throw error;
|
|
796
|
+
if (!(error instanceof OAuthError) || error.code === 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))}`);
|
|
797
|
+
else throw error;
|
|
798
|
+
}
|
|
799
|
+
if (newTokens) {
|
|
800
|
+
await provider.saveTokens({
|
|
801
|
+
...newTokens,
|
|
802
|
+
issuer
|
|
803
|
+
}, infoCtx);
|
|
804
|
+
return "AUTHORIZED";
|
|
805
|
+
}
|
|
779
806
|
}
|
|
780
807
|
const state = provider.state ? await provider.state() : void 0;
|
|
781
808
|
const { authorizationUrl, codeVerifier } = await startAuthorization(authorizationServerUrl, {
|
|
@@ -819,6 +846,17 @@ function isHttpsUrl(value) {
|
|
|
819
846
|
return false;
|
|
820
847
|
}
|
|
821
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* Selects the RFC 8707 resource indicator for an MCP server: the provider's
|
|
851
|
+
* {@linkcode OAuthClientProvider.validateResourceURL | validateResourceURL} result when
|
|
852
|
+
* implemented, otherwise the protected resource metadata's `resource` (checked against the
|
|
853
|
+
* server URL with `checkResourceAllowed`), or `undefined` when there is no metadata.
|
|
854
|
+
*
|
|
855
|
+
* The result is a parsed `URL`, so a pathless indicator such as `https://example.com` has
|
|
856
|
+
* the `href` `https://example.com/`. {@linkcode auth} therefore sends the metadata string
|
|
857
|
+
* verbatim instead of this URL's `href` (#1968); callers that emit the `resource`
|
|
858
|
+
* parameter themselves should do the same.
|
|
859
|
+
*/
|
|
822
860
|
async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
|
823
861
|
const defaultResource = resourceUrlFromServerUrl(serverUrl);
|
|
824
862
|
if (provider.validateResourceURL) return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
|
|
@@ -829,15 +867,22 @@ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
|
|
829
867
|
})) throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${defaultResource} (or origin)`);
|
|
830
868
|
return new URL(resourceMetadata.resource);
|
|
831
869
|
}
|
|
870
|
+
/** Auth-scheme challenge tokens {@linkcode extractWWWAuthenticateParams} recognizes. */
|
|
871
|
+
const RECOGNIZED_CHALLENGE_SCHEMES = new Set(["bearer", "dpop"]);
|
|
832
872
|
/**
|
|
833
873
|
* Extract `resource_metadata`, `scope`, `error`, and `error_description` from a
|
|
834
874
|
* `WWW-Authenticate` header.
|
|
875
|
+
*
|
|
876
|
+
* Recognizes both the `Bearer` scheme (RFC 6750) and the `DPoP` scheme (RFC 9449 §7.1,
|
|
877
|
+
* SEP-1932) — a DPoP-protected resource's challenge carries the same parameters under `DPoP`
|
|
878
|
+
* instead of `Bearer`, and this must still surface `resource_metadata`/`scope` from it for
|
|
879
|
+
* discovery and SEP-2350 step-up to work against such a resource.
|
|
835
880
|
*/
|
|
836
881
|
function extractWWWAuthenticateParams(res) {
|
|
837
882
|
const authenticateHeader = res.headers.get("WWW-Authenticate");
|
|
838
883
|
if (!authenticateHeader) return {};
|
|
839
884
|
const [type, scheme] = authenticateHeader.split(" ");
|
|
840
|
-
if (type
|
|
885
|
+
if (!type || !RECOGNIZED_CHALLENGE_SCHEMES.has(type.toLowerCase()) || !scheme) return {};
|
|
841
886
|
const resourceMetadataMatch = extractFieldFromWwwAuth(res, "resource_metadata") || void 0;
|
|
842
887
|
let resourceMetadataUrl;
|
|
843
888
|
if (resourceMetadataMatch) try {
|
|
@@ -1135,6 +1180,9 @@ async function discoverOAuthServerInfo(serverUrl, opts) {
|
|
|
1135
1180
|
resourceMetadata
|
|
1136
1181
|
};
|
|
1137
1182
|
}
|
|
1183
|
+
function resourceIndicatorToString(resource) {
|
|
1184
|
+
return typeof resource === "string" ? resource : resource.href;
|
|
1185
|
+
}
|
|
1138
1186
|
/**
|
|
1139
1187
|
* Begins the authorization flow with the given server, by generating a PKCE challenge and constructing the authorization URL.
|
|
1140
1188
|
*/
|
|
@@ -1156,7 +1204,7 @@ async function startAuthorization(authorizationServerUrl, { metadata, clientInfo
|
|
|
1156
1204
|
if (state) authorizationUrl.searchParams.set("state", state);
|
|
1157
1205
|
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
|
1158
1206
|
if (scope?.split(" ").includes("offline_access")) authorizationUrl.searchParams.append("prompt", "consent");
|
|
1159
|
-
if (resource) authorizationUrl.searchParams.set("resource", resource
|
|
1207
|
+
if (resource) authorizationUrl.searchParams.set("resource", resourceIndicatorToString(resource));
|
|
1160
1208
|
return {
|
|
1161
1209
|
authorizationUrl,
|
|
1162
1210
|
codeVerifier
|
|
@@ -1185,20 +1233,35 @@ function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redire
|
|
|
1185
1233
|
* Internal helper to execute a token request with the given parameters.
|
|
1186
1234
|
* Used by {@linkcode exchangeAuthorization}, {@linkcode refreshAuthorization}, and {@linkcode fetchToken}.
|
|
1187
1235
|
*/
|
|
1188
|
-
async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
|
|
1236
|
+
async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, dpop, fetchFn }) {
|
|
1189
1237
|
const tokenUrl = assertSecureTokenEndpoint(metadata?.token_endpoint ?? new URL("/token", authorizationServerUrl));
|
|
1190
1238
|
const headers = new Headers({
|
|
1191
1239
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
1192
1240
|
Accept: "application/json"
|
|
1193
1241
|
});
|
|
1194
|
-
if (resource) tokenRequestParams.set("resource", resource
|
|
1195
|
-
if (addClientAuthentication)
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1242
|
+
if (resource) tokenRequestParams.set("resource", resourceIndicatorToString(resource));
|
|
1243
|
+
if (!addClientAuthentication && clientInformation) applyClientAuthentication(selectClientAuthMethod(clientInformation, metadata?.token_endpoint_auth_methods_supported ?? []), clientInformation, headers, tokenRequestParams);
|
|
1244
|
+
const requestOnce = async () => {
|
|
1245
|
+
const requestHeaders = new Headers(headers);
|
|
1246
|
+
if (addClientAuthentication) await addClientAuthentication(requestHeaders, tokenRequestParams, tokenUrl, metadata);
|
|
1247
|
+
if (dpop) requestHeaders.set("DPoP", await dpop.buildProof({
|
|
1248
|
+
htm: "POST",
|
|
1249
|
+
htu: tokenUrl
|
|
1250
|
+
}));
|
|
1251
|
+
return (fetchFn ?? fetch)(tokenUrl, {
|
|
1252
|
+
method: "POST",
|
|
1253
|
+
headers: requestHeaders,
|
|
1254
|
+
body: tokenRequestParams
|
|
1255
|
+
});
|
|
1256
|
+
};
|
|
1257
|
+
let response = await requestOnce();
|
|
1258
|
+
if (dpop && response.status === 400) {
|
|
1259
|
+
if ((await response.clone().json().catch(() => {}))?.error === OAuthErrorCode.UseDpopNonce) {
|
|
1260
|
+
dpop.observeNonce(response, tokenUrl);
|
|
1261
|
+
response = await requestOnce();
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
dpop?.observeNonce(response, tokenUrl);
|
|
1202
1265
|
if (!response.ok) throw await parseErrorResponse(response);
|
|
1203
1266
|
const json = await response.json();
|
|
1204
1267
|
try {
|
|
@@ -1220,7 +1283,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
|
|
|
1220
1283
|
* @returns Promise resolving to OAuth tokens
|
|
1221
1284
|
* @throws {Error} When token exchange fails or authentication is invalid
|
|
1222
1285
|
*/
|
|
1223
|
-
async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, iss, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }) {
|
|
1286
|
+
async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, iss, codeVerifier, redirectUri, resource, addClientAuthentication, dpop, fetchFn }) {
|
|
1224
1287
|
validateAuthorizationResponseIssuer({
|
|
1225
1288
|
iss,
|
|
1226
1289
|
expectedIssuer: metadata?.issuer,
|
|
@@ -1232,6 +1295,7 @@ async function exchangeAuthorization(authorizationServerUrl, { metadata, clientI
|
|
|
1232
1295
|
clientInformation,
|
|
1233
1296
|
addClientAuthentication,
|
|
1234
1297
|
resource,
|
|
1298
|
+
dpop,
|
|
1235
1299
|
fetchFn
|
|
1236
1300
|
});
|
|
1237
1301
|
}
|
|
@@ -1247,7 +1311,7 @@ async function exchangeAuthorization(authorizationServerUrl, { metadata, clientI
|
|
|
1247
1311
|
* @returns Promise resolving to OAuth tokens (preserves original `refresh_token` if not replaced)
|
|
1248
1312
|
* @throws {Error} When token refresh fails or authentication is invalid
|
|
1249
1313
|
*/
|
|
1250
|
-
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
|
|
1314
|
+
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, dpop, fetchFn }) {
|
|
1251
1315
|
return {
|
|
1252
1316
|
refresh_token: refreshToken,
|
|
1253
1317
|
...await executeTokenRequest(authorizationServerUrl, {
|
|
@@ -1259,6 +1323,7 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
|
|
|
1259
1323
|
clientInformation,
|
|
1260
1324
|
addClientAuthentication,
|
|
1261
1325
|
resource,
|
|
1326
|
+
dpop,
|
|
1262
1327
|
fetchFn
|
|
1263
1328
|
})
|
|
1264
1329
|
};
|
|
@@ -1311,6 +1376,7 @@ async function fetchToken(provider, authorizationServerUrl, { metadata, resource
|
|
|
1311
1376
|
clientInformation: clientInformation ?? void 0,
|
|
1312
1377
|
addClientAuthentication: provider.addClientAuthentication,
|
|
1313
1378
|
resource,
|
|
1379
|
+
dpop: await provider.dpop?.(),
|
|
1314
1380
|
fetchFn
|
|
1315
1381
|
});
|
|
1316
1382
|
}
|
|
@@ -2397,7 +2463,7 @@ function classifyNetworkError(error, context) {
|
|
|
2397
2463
|
if (context.environment === "browser" && isOpaqueFetchTypeError(error)) return { kind: "legacy" };
|
|
2398
2464
|
return {
|
|
2399
2465
|
kind: "error",
|
|
2400
|
-
error: new SdkError(SdkErrorCode.EraNegotiationFailed, `Version negotiation probe failed: ${describeError(error)}`, { cause: error })
|
|
2466
|
+
error: new SdkError(SdkErrorCode.EraNegotiationFailed, `Version negotiation probe failed: ${describeError(error)}`, { cause: error }, { cause: error })
|
|
2401
2467
|
};
|
|
2402
2468
|
}
|
|
2403
2469
|
function isOpaqueFetchTypeError(error) {
|
|
@@ -4428,6 +4494,160 @@ async function exchangeJwtAuthGrant(options) {
|
|
|
4428
4494
|
return parseResult.data;
|
|
4429
4495
|
}
|
|
4430
4496
|
|
|
4497
|
+
//#endregion
|
|
4498
|
+
//#region src/client/dpop.ts
|
|
4499
|
+
/** Asymmetric JWS algorithms usable for a DPoP proof (RFC 9449 §11.6 forbids symmetric algs and `none`). */
|
|
4500
|
+
const DPOP_SUPPORTED_ALGS = [
|
|
4501
|
+
"ES256",
|
|
4502
|
+
"ES384",
|
|
4503
|
+
"ES512",
|
|
4504
|
+
"RS256",
|
|
4505
|
+
"RS384",
|
|
4506
|
+
"RS512",
|
|
4507
|
+
"PS256",
|
|
4508
|
+
"PS384",
|
|
4509
|
+
"PS512",
|
|
4510
|
+
"EdDSA"
|
|
4511
|
+
];
|
|
4512
|
+
const DEFAULT_DPOP_ALG = "ES256";
|
|
4513
|
+
const DPOP_TYP = "dpop+jwt";
|
|
4514
|
+
async function importJose() {
|
|
4515
|
+
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)");
|
|
4516
|
+
return import("jose");
|
|
4517
|
+
}
|
|
4518
|
+
/** Generate an asymmetric DPoP signing key pair. Non-extractable by default (RFC 9449 §11). */
|
|
4519
|
+
async function generateDpopKeyPair(options = {}) {
|
|
4520
|
+
const alg = options.alg ?? DEFAULT_DPOP_ALG;
|
|
4521
|
+
const jose = await importJose();
|
|
4522
|
+
const { publicKey, privateKey } = await jose.generateKeyPair(alg, { extractable: options.extractable ?? false });
|
|
4523
|
+
const publicJwk = await jose.exportJWK(publicKey);
|
|
4524
|
+
return {
|
|
4525
|
+
privateKey,
|
|
4526
|
+
publicKey,
|
|
4527
|
+
publicJwk,
|
|
4528
|
+
thumbprint: await jose.calculateJwkThumbprint(publicJwk, "sha256"),
|
|
4529
|
+
alg
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
4532
|
+
/**
|
|
4533
|
+
* Compute the `ath` claim for a DPoP proof presented alongside an access token: the
|
|
4534
|
+
* base64url-encoded SHA-256 digest of the ASCII access-token value (RFC 9449 §4.1).
|
|
4535
|
+
*
|
|
4536
|
+
* Uses Web Crypto (`crypto.subtle`) rather than a Node-only hashing API so this stays usable
|
|
4537
|
+
* in browser and edge runtimes.
|
|
4538
|
+
*/
|
|
4539
|
+
async function accessTokenHash(accessToken) {
|
|
4540
|
+
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)");
|
|
4541
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(accessToken));
|
|
4542
|
+
return base64UrlEncode(new Uint8Array(digest));
|
|
4543
|
+
}
|
|
4544
|
+
function base64UrlEncode(bytes) {
|
|
4545
|
+
let binary = "";
|
|
4546
|
+
for (const byte of bytes) binary += String.fromCodePoint(byte);
|
|
4547
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
4548
|
+
}
|
|
4549
|
+
/** Strip the query and fragment from a URL, per RFC 9449 §4.2 (`htu` MUST NOT contain either). */
|
|
4550
|
+
function stripQueryAndFragment(url) {
|
|
4551
|
+
const u = new URL(url.toString());
|
|
4552
|
+
return `${u.origin}${u.pathname}`;
|
|
4553
|
+
}
|
|
4554
|
+
/** A fresh, cryptographically random `jti` (RFC 9449 §4.2 — MUST be unique per proof). */
|
|
4555
|
+
function randomJti() {
|
|
4556
|
+
const bytes = new Uint8Array(16);
|
|
4557
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
4558
|
+
return base64UrlEncode(bytes);
|
|
4559
|
+
}
|
|
4560
|
+
/**
|
|
4561
|
+
* A DPoP signing identity plus the small amount of state RFC 9449 requires across requests:
|
|
4562
|
+
* the key pair and, per origin, the most recently server-supplied nonce (RFC 9449 §8/§9).
|
|
4563
|
+
*
|
|
4564
|
+
* One `DpopSession` is meant to live for the lifetime of a single OAuth client registration —
|
|
4565
|
+
* the token endpoint and the resource server are different origins and get independent nonce
|
|
4566
|
+
* slots, so a nonce challenge from one never leaks into proofs sent to the other.
|
|
4567
|
+
*/
|
|
4568
|
+
var DpopSession = class DpopSession {
|
|
4569
|
+
nonces = /* @__PURE__ */ new Map();
|
|
4570
|
+
constructor(keyPair) {
|
|
4571
|
+
this.keyPair = keyPair;
|
|
4572
|
+
}
|
|
4573
|
+
/** Create a session with a fresh key pair, or reuse a caller-supplied one. */
|
|
4574
|
+
static async create(options = {}) {
|
|
4575
|
+
return new DpopSession(options.keyPair ?? await generateDpopKeyPair({ alg: options.alg }));
|
|
4576
|
+
}
|
|
4577
|
+
/** RFC 7638 JWK SHA-256 thumbprint of the signing key — matches the token's `cnf.jkt` once bound. */
|
|
4578
|
+
get thumbprint() {
|
|
4579
|
+
return this.keyPair.thumbprint;
|
|
4580
|
+
}
|
|
4581
|
+
/** The JWS algorithm this session signs proofs with. */
|
|
4582
|
+
get alg() {
|
|
4583
|
+
return this.keyPair.alg;
|
|
4584
|
+
}
|
|
4585
|
+
/** The public JWK embedded in every proof's `jwk` header parameter. */
|
|
4586
|
+
get publicJwk() {
|
|
4587
|
+
return this.keyPair.publicJwk;
|
|
4588
|
+
}
|
|
4589
|
+
/** The remembered nonce for `url`'s origin, if the server has issued one (RFC 9449 §8/§9). */
|
|
4590
|
+
nonceFor(url) {
|
|
4591
|
+
return this.nonces.get(new URL(url.toString()).origin);
|
|
4592
|
+
}
|
|
4593
|
+
/** Record a server-supplied nonce for `url`'s origin (newest-wins, RFC 9449 §8.2). */
|
|
4594
|
+
rememberNonce(url, nonce) {
|
|
4595
|
+
this.nonces.set(new URL(url.toString()).origin, nonce);
|
|
4596
|
+
}
|
|
4597
|
+
/**
|
|
4598
|
+
* Capture a `DPoP-Nonce` response header, if present, for `url`'s origin. RFC 9449 §8.2 says a
|
|
4599
|
+
* fresh nonce may ride on *any* response (success or failure), so call this unconditionally
|
|
4600
|
+
* after every request, not only on a `use_dpop_nonce` challenge.
|
|
4601
|
+
*/
|
|
4602
|
+
observeNonce(response, url) {
|
|
4603
|
+
const nonce = response.headers.get("dpop-nonce");
|
|
4604
|
+
if (nonce) this.rememberNonce(url, nonce);
|
|
4605
|
+
}
|
|
4606
|
+
/**
|
|
4607
|
+
* Build a fresh DPoP proof JWT. Always mints a new `jti` — proofs are never cached or reused,
|
|
4608
|
+
* since RFC 9449 §4.3 step 9 requires each to be presented at most once.
|
|
4609
|
+
*/
|
|
4610
|
+
async buildProof(request) {
|
|
4611
|
+
const jose = await importJose();
|
|
4612
|
+
const htu = stripQueryAndFragment(request.htu);
|
|
4613
|
+
const nonce = request.nonce ?? this.nonceFor(request.htu);
|
|
4614
|
+
const payload = {
|
|
4615
|
+
jti: randomJti(),
|
|
4616
|
+
htm: request.htm.toUpperCase(),
|
|
4617
|
+
htu,
|
|
4618
|
+
iat: Math.floor(Date.now() / 1e3)
|
|
4619
|
+
};
|
|
4620
|
+
if (request.accessToken !== void 0) payload.ath = await accessTokenHash(request.accessToken);
|
|
4621
|
+
if (nonce !== void 0) payload.nonce = nonce;
|
|
4622
|
+
return new jose.SignJWT(payload).setProtectedHeader({
|
|
4623
|
+
alg: this.keyPair.alg,
|
|
4624
|
+
typ: DPOP_TYP,
|
|
4625
|
+
jwk: this.keyPair.publicJwk
|
|
4626
|
+
}).sign(this.keyPair.privateKey);
|
|
4627
|
+
}
|
|
4628
|
+
};
|
|
4629
|
+
/**
|
|
4630
|
+
* Whether `wwwAuthenticate` advertises a `DPoP` auth-scheme challenge (matched at the start of the
|
|
4631
|
+
* header or after a comma, per RFC 9110 §11.6.1 — a server may list several schemes together, e.g.
|
|
4632
|
+
* `Bearer …, DPoP …`).
|
|
4633
|
+
*/
|
|
4634
|
+
function hasDpopChallenge(wwwAuthenticate) {
|
|
4635
|
+
return /(?:^|,)\s*dpop(?:\s|$|,)/i.test(wwwAuthenticate);
|
|
4636
|
+
}
|
|
4637
|
+
/**
|
|
4638
|
+
* Whether `response` is a resource-server `use_dpop_nonce` challenge (RFC 9449 §9): a `401` whose
|
|
4639
|
+
* `WWW-Authenticate` header carries a `DPoP` challenge with `error="use_dpop_nonce"`.
|
|
4640
|
+
*
|
|
4641
|
+
* A conformant retry re-signs the proof with the nonce {@linkcode DpopSession.observeNonce} just
|
|
4642
|
+
* captured — never resend the original proof: RFC 9449 §4.2 requires a unique `jti` per proof, and
|
|
4643
|
+
* replaying one across the challenge/retry boundary is itself a violation.
|
|
4644
|
+
*/
|
|
4645
|
+
function isDpopNonceChallenge(response) {
|
|
4646
|
+
if (response.status !== 401) return false;
|
|
4647
|
+
const wwwAuthenticate = response.headers.get("www-authenticate") ?? "";
|
|
4648
|
+
return hasDpopChallenge(wwwAuthenticate) && /use_dpop_nonce/i.test(wwwAuthenticate);
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4431
4651
|
//#endregion
|
|
4432
4652
|
//#region src/client/middleware.ts
|
|
4433
4653
|
/**
|
|
@@ -4438,6 +4658,10 @@ async function exchangeJwtAuthGrant(options) {
|
|
|
4438
4658
|
* - Handle 401 responses by attempting re-authentication
|
|
4439
4659
|
* - Retry the original request after successful auth
|
|
4440
4660
|
* - Handle OAuth errors appropriately ({@linkcode index.OAuthErrorCode.InvalidClient | OAuthErrorCode.InvalidClient}, etc.)
|
|
4661
|
+
* - When {@linkcode OAuthClientProvider.dpop | provider.dpop()} is implemented, present DPoP-bound
|
|
4662
|
+
* tokens with the `DPoP` scheme plus a per-request proof (RFC 9449 / SEP-1932) by composing
|
|
4663
|
+
* {@linkcode withDpopFromProvider} underneath — so a `use_dpop_nonce` challenge is retried
|
|
4664
|
+
* inline on every attempt, independently of the single re-authentication retry here
|
|
4441
4665
|
*
|
|
4442
4666
|
* The `baseUrl` parameter is optional and defaults to using the domain from the request URL.
|
|
4443
4667
|
* However, you should explicitly provide `baseUrl` when:
|
|
@@ -4456,7 +4680,8 @@ async function exchangeJwtAuthGrant(options) {
|
|
|
4456
4680
|
* @param baseUrl - Base URL for OAuth server discovery (defaults to request URL domain)
|
|
4457
4681
|
* @returns A fetch middleware function
|
|
4458
4682
|
*/
|
|
4459
|
-
const withOAuth = (provider, baseUrl) => (
|
|
4683
|
+
const withOAuth = (provider, baseUrl) => (baseNext) => {
|
|
4684
|
+
const next = provider.dpop ? withDpopFromProvider(provider)(baseNext) : baseNext;
|
|
4460
4685
|
return async (input, init) => {
|
|
4461
4686
|
const makeRequest = async () => {
|
|
4462
4687
|
const headers = new Headers(init?.headers);
|
|
@@ -4474,7 +4699,7 @@ const withOAuth = (provider, baseUrl) => (next) => {
|
|
|
4474
4699
|
serverUrl: baseUrl || (typeof input === "string" ? new URL(input).origin : input.origin),
|
|
4475
4700
|
resourceMetadataUrl,
|
|
4476
4701
|
scope,
|
|
4477
|
-
fetchFn:
|
|
4702
|
+
fetchFn: baseNext
|
|
4478
4703
|
});
|
|
4479
4704
|
if (result === "REDIRECT") throw new UnauthorizedError("Authentication requires user authorization - redirect initiated");
|
|
4480
4705
|
if (result !== "AUTHORIZED") throw new UnauthorizedError(`Authentication failed with result: ${result}`);
|
|
@@ -4488,6 +4713,90 @@ const withOAuth = (provider, baseUrl) => (next) => {
|
|
|
4488
4713
|
};
|
|
4489
4714
|
};
|
|
4490
4715
|
/**
|
|
4716
|
+
* Creates a fetch wrapper that presents an access token using the `DPoP` Authorization scheme
|
|
4717
|
+
* (RFC 9449 / SEP-1932) instead of `Bearer`: every request carries `Authorization: DPoP <token>`
|
|
4718
|
+
* plus a fresh `DPoP` proof bound to that request's method and URL, a resource-server
|
|
4719
|
+
* `use_dpop_nonce` challenge (RFC 9449 §9) is retried once, inline, with the server-supplied nonce,
|
|
4720
|
+
* and a `DPoP-Nonce` delivered on any response is remembered for the next proof (RFC 9449 §8.2).
|
|
4721
|
+
*
|
|
4722
|
+
* Because it wraps `fetch` itself, the proof is always bound to the request actually sent and every
|
|
4723
|
+
* response is observed — which is why the MCP transports apply this wrapper internally (via
|
|
4724
|
+
* {@linkcode withDpopFromProvider}) when their `authProvider` implements
|
|
4725
|
+
* {@linkcode OAuthClientProvider.dpop | dpop()}, rather than threading request context through
|
|
4726
|
+
* {@linkcode AuthProvider}.
|
|
4727
|
+
*
|
|
4728
|
+
* Use this directly when you already manage the access token yourself (a non-OAuth token source,
|
|
4729
|
+
* or credentials obtained out-of-band) and only need DPoP's request-signing behavior — e.g.
|
|
4730
|
+
* `fetch: withDpop(session, getToken)(fetch)` alongside a minimal `authProvider: { token }`.
|
|
4731
|
+
*
|
|
4732
|
+
* @param session - The DPoP signing session (key pair + nonce state), or a function resolving to
|
|
4733
|
+
* it. Reuse the same session across requests to the same server so its nonce state persists.
|
|
4734
|
+
* When the function resolves to `undefined` the request passes through unchanged.
|
|
4735
|
+
* @param getToken - Returns the current access token, or `undefined` if none is available (the
|
|
4736
|
+
* request passes through unchanged — any `Authorization` header already on it is left as is).
|
|
4737
|
+
* @returns A fetch middleware function
|
|
4738
|
+
*/
|
|
4739
|
+
const withDpop = (session, getToken) => (next) => {
|
|
4740
|
+
const resolveSession = typeof session === "function" ? session : () => session;
|
|
4741
|
+
return async (input, init) => {
|
|
4742
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
4743
|
+
const url = new URL(input.toString());
|
|
4744
|
+
const activeSession = await resolveSession();
|
|
4745
|
+
if (!activeSession) return next(input, init);
|
|
4746
|
+
const makeRequest = async () => {
|
|
4747
|
+
const accessToken = await getToken();
|
|
4748
|
+
if (!accessToken) return next(input, init);
|
|
4749
|
+
const headers = new Headers(init?.headers);
|
|
4750
|
+
const proof = await activeSession.buildProof({
|
|
4751
|
+
htm: method,
|
|
4752
|
+
htu: url,
|
|
4753
|
+
accessToken
|
|
4754
|
+
});
|
|
4755
|
+
headers.set("Authorization", `DPoP ${accessToken}`);
|
|
4756
|
+
headers.set("DPoP", proof);
|
|
4757
|
+
return next(input, {
|
|
4758
|
+
...init,
|
|
4759
|
+
headers
|
|
4760
|
+
});
|
|
4761
|
+
};
|
|
4762
|
+
let response = await makeRequest();
|
|
4763
|
+
if (isDpopNonceChallenge(response) && response.headers.has("dpop-nonce")) {
|
|
4764
|
+
activeSession.observeNonce(response, url);
|
|
4765
|
+
await response.text?.().catch(() => {});
|
|
4766
|
+
response = await makeRequest();
|
|
4767
|
+
}
|
|
4768
|
+
activeSession.observeNonce(response, url);
|
|
4769
|
+
return response;
|
|
4770
|
+
};
|
|
4771
|
+
};
|
|
4772
|
+
/**
|
|
4773
|
+
* {@linkcode withDpop} driven by an {@linkcode OAuthClientProvider}: the session comes from
|
|
4774
|
+
* {@linkcode OAuthClientProvider.dpop | provider.dpop()} and the token from
|
|
4775
|
+
* {@linkcode OAuthClientProvider.tokens | provider.tokens()} — presented with the DPoP scheme only
|
|
4776
|
+
* when the AS actually issued `token_type: "DPoP"` (RFC 9449 §7.1); a Bearer token passes through
|
|
4777
|
+
* untouched.
|
|
4778
|
+
*
|
|
4779
|
+
* This is what the MCP transports and {@linkcode withOAuth} compose internally when the provider
|
|
4780
|
+
* implements `dpop()`. Use it directly only when building your own fetch pipeline around an
|
|
4781
|
+
* `OAuthClientProvider` (e.g. a custom re-authorization middleware) — place it *innermost*, below
|
|
4782
|
+
* whatever sets `Authorization: Bearer` and handles 401 re-authentication.
|
|
4783
|
+
*/
|
|
4784
|
+
const withDpopFromProvider = (provider) => withDpop(async () => {
|
|
4785
|
+
try {
|
|
4786
|
+
return await provider.dpop?.();
|
|
4787
|
+
} catch (error) {
|
|
4788
|
+
throw markAuthSeamEscape(error);
|
|
4789
|
+
}
|
|
4790
|
+
}, async () => {
|
|
4791
|
+
let tokens;
|
|
4792
|
+
try {
|
|
4793
|
+
tokens = await provider.tokens();
|
|
4794
|
+
} catch (error) {
|
|
4795
|
+
throw markAuthSeamEscape(error);
|
|
4796
|
+
}
|
|
4797
|
+
return tokens?.token_type?.toLowerCase() === "dpop" ? tokens.access_token : void 0;
|
|
4798
|
+
});
|
|
4799
|
+
/**
|
|
4491
4800
|
* Creates a fetch middleware that logs HTTP requests and responses.
|
|
4492
4801
|
*
|
|
4493
4802
|
* When called without arguments `withLogging()`, it uses the default logger that:
|
|
@@ -4685,6 +4994,7 @@ var SSEClientTransport = class {
|
|
|
4685
4994
|
_skipIssuerMetadataValidation;
|
|
4686
4995
|
_fetch;
|
|
4687
4996
|
_fetchWithInit;
|
|
4997
|
+
_dpop;
|
|
4688
4998
|
_protocolVersion;
|
|
4689
4999
|
onclose;
|
|
4690
5000
|
onerror;
|
|
@@ -4696,32 +5006,33 @@ var SSEClientTransport = class {
|
|
|
4696
5006
|
this._eventSourceInit = opts?.eventSourceInit;
|
|
4697
5007
|
this._requestInit = opts?.requestInit;
|
|
4698
5008
|
this._skipIssuerMetadataValidation = opts?.skipIssuerMetadataValidation;
|
|
5009
|
+
this._fetch = opts?.fetch;
|
|
4699
5010
|
if (isOAuthClientProvider(opts?.authProvider)) {
|
|
4700
5011
|
this._oauthProvider = opts.authProvider;
|
|
4701
5012
|
this._authProvider = adaptOAuthProvider(opts.authProvider, { skipIssuerMetadataValidation: opts.skipIssuerMetadataValidation });
|
|
5013
|
+
if (opts.authProvider.dpop) {
|
|
5014
|
+
this._dpop = withDpopFromProvider(opts.authProvider);
|
|
5015
|
+
this._fetch = this._dpop(opts.fetch ?? fetch);
|
|
5016
|
+
}
|
|
4702
5017
|
} else this._authProvider = opts?.authProvider;
|
|
4703
|
-
this._fetch = opts?.fetch;
|
|
4704
5018
|
this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
|
|
4705
5019
|
}
|
|
4706
5020
|
_last401Response;
|
|
4707
5021
|
async _commonHeaders() {
|
|
4708
|
-
const headers =
|
|
5022
|
+
const headers = new Headers(this._requestInit?.headers || void 0);
|
|
4709
5023
|
let token;
|
|
4710
5024
|
try {
|
|
4711
5025
|
token = await this._authProvider?.token();
|
|
4712
5026
|
} catch (error) {
|
|
4713
5027
|
throw markAuthSeamEscape(error);
|
|
4714
5028
|
}
|
|
4715
|
-
if (token) headers
|
|
4716
|
-
if (this._protocolVersion) headers
|
|
4717
|
-
|
|
4718
|
-
return new Headers({
|
|
4719
|
-
...headers,
|
|
4720
|
-
...extraHeaders
|
|
4721
|
-
});
|
|
5029
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
5030
|
+
if (this._protocolVersion) headers.set("mcp-protocol-version", this._protocolVersion);
|
|
5031
|
+
return headers;
|
|
4722
5032
|
}
|
|
4723
5033
|
_startOrAuth() {
|
|
4724
|
-
const
|
|
5034
|
+
const eventSourceFetch = this._eventSourceInit?.fetch;
|
|
5035
|
+
const fetchImpl = eventSourceFetch ? this._dpop?.(eventSourceFetch) ?? eventSourceFetch : this._fetch ?? fetch;
|
|
4725
5036
|
return new Promise((resolve, reject) => {
|
|
4726
5037
|
this._eventSource = new EventSource(this._url.href, {
|
|
4727
5038
|
...this._eventSourceInit,
|
|
@@ -4899,6 +5210,7 @@ const DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS = {
|
|
|
4899
5210
|
*/
|
|
4900
5211
|
const RESERVED_REQUEST_HEADER_NAMES = new Set([
|
|
4901
5212
|
"authorization",
|
|
5213
|
+
"dpop",
|
|
4902
5214
|
"content-type",
|
|
4903
5215
|
"mcp-protocol-version",
|
|
4904
5216
|
"mcp-method",
|
|
@@ -4974,11 +5286,12 @@ var StreamableHTTPClientTransport = class {
|
|
|
4974
5286
|
this._scope = void 0;
|
|
4975
5287
|
this._requestInit = opts?.requestInit;
|
|
4976
5288
|
this._skipIssuerMetadataValidation = opts?.skipIssuerMetadataValidation;
|
|
5289
|
+
this._fetch = opts?.fetch;
|
|
4977
5290
|
if (isOAuthClientProvider(opts?.authProvider)) {
|
|
4978
5291
|
this._oauthProvider = opts.authProvider;
|
|
4979
5292
|
this._authProvider = adaptOAuthProvider(opts.authProvider, { skipIssuerMetadataValidation: opts.skipIssuerMetadataValidation });
|
|
5293
|
+
if (opts.authProvider.dpop) this._fetch = withDpopFromProvider(opts.authProvider)(opts.fetch ?? fetch);
|
|
4980
5294
|
} else this._authProvider = opts?.authProvider;
|
|
4981
|
-
this._fetch = opts?.fetch;
|
|
4982
5295
|
this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
|
|
4983
5296
|
this._sessionId = opts?.sessionId;
|
|
4984
5297
|
this._protocolVersion = opts?.protocolVersion;
|
|
@@ -5032,21 +5345,17 @@ var StreamableHTTPClientTransport = class {
|
|
|
5032
5345
|
});
|
|
5033
5346
|
}
|
|
5034
5347
|
async _commonHeaders() {
|
|
5035
|
-
const headers =
|
|
5348
|
+
const headers = new Headers(this._requestInit?.headers || void 0);
|
|
5036
5349
|
let token;
|
|
5037
5350
|
try {
|
|
5038
5351
|
token = await this._authProvider?.token();
|
|
5039
5352
|
} catch (error) {
|
|
5040
5353
|
throw markAuthSeamEscape(error);
|
|
5041
5354
|
}
|
|
5042
|
-
if (token) headers
|
|
5043
|
-
if (this._sessionId) headers
|
|
5044
|
-
if (this._protocolVersion) headers
|
|
5045
|
-
|
|
5046
|
-
return new Headers({
|
|
5047
|
-
...headers,
|
|
5048
|
-
...extraHeaders
|
|
5049
|
-
});
|
|
5355
|
+
if (token) headers.set("Authorization", `Bearer ${token}`);
|
|
5356
|
+
if (this._sessionId) headers.set("mcp-session-id", this._sessionId);
|
|
5357
|
+
if (this._protocolVersion) headers.set("mcp-protocol-version", this._protocolVersion);
|
|
5358
|
+
return headers;
|
|
5050
5359
|
}
|
|
5051
5360
|
/**
|
|
5052
5361
|
* Body-derived per-request headers: when an outgoing request carries a
|
|
@@ -5062,9 +5371,8 @@ var StreamableHTTPClientTransport = class {
|
|
|
5062
5371
|
if (typeof envelopeVersion !== "string") return;
|
|
5063
5372
|
headers.set("mcp-protocol-version", envelopeVersion);
|
|
5064
5373
|
headers.set("mcp-method", message.method);
|
|
5065
|
-
const
|
|
5066
|
-
|
|
5067
|
-
if (nameHeader !== void 0) headers.set("mcp-name", encodeMcpParamValue(nameHeader));
|
|
5374
|
+
const source = mcpNameSource(message.method, message.params);
|
|
5375
|
+
if (source?.value !== void 0) headers.set("mcp-name", encodeMcpParamValue(source.value));
|
|
5068
5376
|
}
|
|
5069
5377
|
/**
|
|
5070
5378
|
* `true` when the outbound message is a single request carrying a
|
|
@@ -5477,5 +5785,5 @@ function fromJsonSchema(schema, validator) {
|
|
|
5477
5785
|
}
|
|
5478
5786
|
|
|
5479
5787
|
//#endregion
|
|
5480
|
-
export { AuthorizationServerMismatchError, BAGGAGE_META_KEY, CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, Client, ClientCredentialsProvider, CrossAppAccessProvider, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, DEFAULT_REQUEST_TIMEOUT_MSEC, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, InMemoryResponseCacheStore, InMemoryTransport, InsecureTokenEndpointError, InsufficientScopeError, IssuerMismatchError, JSONRPC_VERSION, LATEST_PROTOCOL_VERSION, LOG_LEVEL_META_KEY, MAX_CACHE_TTL_MS, METHOD_NOT_FOUND, MissingRequiredClientCapabilityError, OAuthClientFlowError, OAuthError, OAuthErrorCode, PARSE_ERROR, PROTOCOL_VERSION_META_KEY, PrivateKeyJwtProvider, Protocol, ProtocolError, ProtocolErrorCode, RELATED_TASK_META_KEY, ReadBuffer, RegistrationRejectedError, ResourceNotFoundError, SERVER_INFO_META_KEY, SSEClientTransport, STDIO_DEFAULT_MAX_BUFFER_SIZE, SUBSCRIPTION_ID_META_KEY, SUPPORTED_PROTOCOL_VERSIONS, SdkError, SdkErrorCode, SdkHttpError, SseError, StaticPrivateKeyJwtProvider, StreamableHTTPClientTransport, TRACEPARENT_META_KEY, TRACESTATE_META_KEY, UnauthorizedError, UnsupportedProtocolVersionError, UriTemplate, UrlElicitationRequiredError, applyMiddlewares, assertCompleteRequestPrompt, assertCompleteRequestResourceTemplate, assertSecureTokenEndpoint, auth, buildDiscoveryUrls, checkResourceAllowed, computeScopeUnion, createFetchWithInit, createMiddleware, createPrivateKeyJwtAuth, deserializeMessage, discoverAndRequestJwtAuthGrant, discoverAuthorizationServerMetadata, discoverOAuthMetadata, discoverOAuthProtectedResourceMetadata, discoverOAuthServerInfo, exchangeAuthorization, exchangeJwtAuthGrant, extractResourceMetadataUrl, extractWWWAuthenticateParams, fetchToken, fromJsonSchema, getDisplayName, getSupportedElicitationModes, isCallToolResult, isHttpsUrl, isInitializeRequest, isInitializedNotification, isInputRequiredResult, isJSONRPCErrorResponse, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResponse, isJSONRPCResultResponse, isJsonContentType, isSpecType, isStrictScopeSuperset, isTaskAugmentedRequestParams, mergeCapabilities, parseErrorResponse, parseJSONRPCMessage, preloadSchemas, prepareAuthorizationCodeRequest, refreshAuthorization, registerClient, requestJwtAuthorizationGrant, resolveClientMetadata, resourceUrlFromServerUrl, selectClientAuthMethod, selectResourceURL, serializeMessage, specTypeSchemas, startAuthorization, validateAuthorizationResponseIssuer, validateClientMetadataUrl, withInputRequired, withLogging, withOAuth };
|
|
5788
|
+
export { AuthorizationServerMismatchError, BAGGAGE_META_KEY, CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, Client, ClientCredentialsProvider, CrossAppAccessProvider, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, DEFAULT_REQUEST_TIMEOUT_MSEC, DPOP_SUPPORTED_ALGS, DpopSession, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, InMemoryResponseCacheStore, InMemoryTransport, InsecureTokenEndpointError, InsufficientScopeError, IssuerMismatchError, JSONRPC_VERSION, LATEST_PROTOCOL_VERSION, LOG_LEVEL_META_KEY, MAX_CACHE_TTL_MS, METHOD_NOT_FOUND, MissingRequiredClientCapabilityError, OAuthClientFlowError, OAuthError, OAuthErrorCode, PARSE_ERROR, PROTOCOL_VERSION_META_KEY, PrivateKeyJwtProvider, Protocol, ProtocolError, ProtocolErrorCode, RELATED_TASK_META_KEY, ReadBuffer, RegistrationRejectedError, ResourceNotFoundError, SERVER_INFO_META_KEY, SSEClientTransport, STDIO_DEFAULT_MAX_BUFFER_SIZE, SUBSCRIPTION_ID_META_KEY, SUPPORTED_PROTOCOL_VERSIONS, SdkError, SdkErrorCode, SdkHttpError, SseError, StaticPrivateKeyJwtProvider, StreamableHTTPClientTransport, TRACEPARENT_META_KEY, TRACESTATE_META_KEY, UnauthorizedError, UnsupportedProtocolVersionError, UriTemplate, UrlElicitationRequiredError, accessTokenHash, applyMiddlewares, assertCompleteRequestPrompt, assertCompleteRequestResourceTemplate, assertSecureTokenEndpoint, auth, buildDiscoveryUrls, checkResourceAllowed, computeScopeUnion, createFetchWithInit, createMiddleware, createPrivateKeyJwtAuth, deserializeMessage, discoverAndRequestJwtAuthGrant, discoverAuthorizationServerMetadata, discoverOAuthMetadata, discoverOAuthProtectedResourceMetadata, discoverOAuthServerInfo, exchangeAuthorization, exchangeJwtAuthGrant, extractResourceMetadataUrl, extractWWWAuthenticateParams, fetchToken, fromJsonSchema, generateDpopKeyPair, getDisplayName, getSupportedElicitationModes, isCallToolResult, isDpopNonceChallenge, isHttpsUrl, isInitializeRequest, isInitializedNotification, isInputRequiredResult, isJSONRPCErrorResponse, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResponse, isJSONRPCResultResponse, isJsonContentType, isSpecType, isStrictScopeSuperset, isTaskAugmentedRequestParams, mergeCapabilities, parseErrorResponse, parseJSONRPCMessage, preloadSchemas, prepareAuthorizationCodeRequest, refreshAuthorization, registerClient, requestJwtAuthorizationGrant, resolveClientMetadata, resourceUrlFromServerUrl, selectClientAuthMethod, selectResourceURL, serializeMessage, specTypeSchemas, startAuthorization, validateAuthorizationResponseIssuer, validateClientMetadataUrl, withDpop, withDpopFromProvider, withInputRequired, withLogging, withOAuth };
|
|
5481
5789
|
//# sourceMappingURL=index.mjs.map
|