feat(planning): grille hebdomadaire complète avec API et filtres
- Connexion API via proxy Angular (résolution CORS, base path /api) - Import CSS ng-zorro global pour les modales et composants - Filtres Camion/Show câblés sur l'affichage de la grille - Camions affichés via TrucksService (linkés au show du même créneau) - Panneau de détails : spectacles + camions du jour sélectionné - Modale de création de spectacle stylisée avec fond et centrage - Positionnement précis des events à la minute dans leur créneau - Auto-scroll vers l'heure courante au chargement - Ligne "maintenant" sur la colonne du jour actuel - Régénération des services OpenAPI (nouveaux noms de types) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+21
-141
@@ -1,10 +1,6 @@
|
||||
import { OAuthClientMetadata, OAuthClientInformationMixed, OAuthTokens, OAuthMetadata, OAuthClientInformationFull, OAuthProtectedResourceMetadata, AuthorizationServerMetadata } from '../shared/auth.js';
|
||||
import { OAuthError } from '../server/auth/errors.js';
|
||||
import { FetchLike } from '../shared/transport.js';
|
||||
/**
|
||||
* Function type for adding client authentication to token requests.
|
||||
*/
|
||||
export type AddClientAuthentication = (headers: Headers, params: URLSearchParams, url: string | URL, metadata?: AuthorizationServerMetadata) => void | Promise<void>;
|
||||
import { OAuthClientMetadata, OAuthClientInformation, OAuthTokens, OAuthMetadata, OAuthClientInformationFull, OAuthProtectedResourceMetadata, AuthorizationServerMetadata } from "../shared/auth.js";
|
||||
import { OAuthError } from "../server/auth/errors.js";
|
||||
import { FetchLike } from "../shared/transport.js";
|
||||
/**
|
||||
* Implements an end-to-end OAuth client to be used with one MCP server.
|
||||
*
|
||||
@@ -15,14 +11,8 @@ export type AddClientAuthentication = (headers: Headers, params: URLSearchParams
|
||||
export interface OAuthClientProvider {
|
||||
/**
|
||||
* The URL to redirect the user agent to after authorization.
|
||||
* Return undefined for non-interactive flows that don't require user interaction
|
||||
* (e.g., client_credentials, jwt-bearer).
|
||||
*/
|
||||
get redirectUrl(): string | URL | undefined;
|
||||
/**
|
||||
* External URL the server should use to fetch client metadata document
|
||||
*/
|
||||
clientMetadataUrl?: string;
|
||||
get redirectUrl(): string | URL;
|
||||
/**
|
||||
* Metadata about this OAuth client.
|
||||
*/
|
||||
@@ -36,7 +26,7 @@ export interface OAuthClientProvider {
|
||||
* server, or returns `undefined` if the client is not registered with the
|
||||
* server.
|
||||
*/
|
||||
clientInformation(): OAuthClientInformationMixed | undefined | Promise<OAuthClientInformationMixed | undefined>;
|
||||
clientInformation(): OAuthClientInformation | undefined | Promise<OAuthClientInformation | undefined>;
|
||||
/**
|
||||
* If implemented, this permits the OAuth client to dynamically register with
|
||||
* the server. Client information saved this way should later be read via
|
||||
@@ -45,7 +35,7 @@ export interface OAuthClientProvider {
|
||||
* This method is not required to be implemented if client information is
|
||||
* statically known (e.g., pre-registered).
|
||||
*/
|
||||
saveClientInformation?(clientInformation: OAuthClientInformationMixed): void | Promise<void>;
|
||||
saveClientInformation?(clientInformation: OAuthClientInformationFull): void | Promise<void>;
|
||||
/**
|
||||
* Loads any existing OAuth tokens for the current session, or returns
|
||||
* `undefined` if there are no saved tokens.
|
||||
@@ -88,7 +78,7 @@ export interface OAuthClientProvider {
|
||||
* @param url - The token endpoint URL being called
|
||||
* @param metadata - Optional OAuth metadata for the server, which may include supported authentication methods
|
||||
*/
|
||||
addClientAuthentication?: AddClientAuthentication;
|
||||
addClientAuthentication?(headers: Headers, params: URLSearchParams, url: string | URL, metadata?: AuthorizationServerMetadata): void | Promise<void>;
|
||||
/**
|
||||
* If defined, overrides the selection and validation of the
|
||||
* RFC 8707 Resource Indicator. If left undefined, default
|
||||
@@ -103,62 +93,11 @@ export interface OAuthClientProvider {
|
||||
* This avoids requiring the user to intervene manually.
|
||||
*/
|
||||
invalidateCredentials?(scope: 'all' | 'client' | 'tokens' | 'verifier'): void | Promise<void>;
|
||||
/**
|
||||
* Prepares grant-specific parameters for a token request.
|
||||
*
|
||||
* This optional method allows providers to customize the token request based on
|
||||
* the grant type they support. When implemented, it returns the grant type and
|
||||
* any grant-specific parameters needed for the token exchange.
|
||||
*
|
||||
* If not implemented, the default behavior depends on the flow:
|
||||
* - For authorization code flow: uses code, code_verifier, and redirect_uri
|
||||
* - For client_credentials: detected via grant_types in clientMetadata
|
||||
*
|
||||
* @param scope - Optional scope to request
|
||||
* @returns Grant type and parameters, or undefined to use default behavior
|
||||
*
|
||||
* @example
|
||||
* // For client_credentials grant:
|
||||
* prepareTokenRequest(scope) {
|
||||
* return {
|
||||
* grantType: 'client_credentials',
|
||||
* params: scope ? { scope } : {}
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // For authorization_code grant (default behavior):
|
||||
* async prepareTokenRequest() {
|
||||
* return {
|
||||
* grantType: 'authorization_code',
|
||||
* params: {
|
||||
* code: this.authorizationCode,
|
||||
* code_verifier: await this.codeVerifier(),
|
||||
* redirect_uri: String(this.redirectUrl)
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
*/
|
||||
prepareTokenRequest?(scope?: string): URLSearchParams | Promise<URLSearchParams | undefined> | undefined;
|
||||
}
|
||||
export type AuthResult = 'AUTHORIZED' | 'REDIRECT';
|
||||
export type AuthResult = "AUTHORIZED" | "REDIRECT";
|
||||
export declare class UnauthorizedError extends Error {
|
||||
constructor(message?: string);
|
||||
}
|
||||
type ClientAuthMethod = 'client_secret_basic' | 'client_secret_post' | 'none';
|
||||
/**
|
||||
* Determines the best client authentication method to use based on server support and client configuration.
|
||||
*
|
||||
* Priority order (highest to lowest):
|
||||
* 1. client_secret_basic (if client secret is available)
|
||||
* 2. client_secret_post (if client secret is available)
|
||||
* 3. none (for public clients)
|
||||
*
|
||||
* @param clientInformation - OAuth client information containing credentials
|
||||
* @param supportedMethods - Authentication methods supported by the authorization server
|
||||
* @returns The selected authentication method
|
||||
*/
|
||||
export declare function selectClientAuthMethod(clientInformation: OAuthClientInformationMixed, supportedMethods: string[]): ClientAuthMethod;
|
||||
/**
|
||||
* Parses an OAuth error response from a string or Response object.
|
||||
*
|
||||
@@ -184,23 +123,9 @@ export declare function auth(provider: OAuthClientProvider, options: {
|
||||
resourceMetadataUrl?: URL;
|
||||
fetchFn?: FetchLike;
|
||||
}): Promise<AuthResult>;
|
||||
/**
|
||||
* SEP-991: URL-based Client IDs
|
||||
* Validate that the client_id is a valid URL with https scheme
|
||||
*/
|
||||
export declare function isHttpsUrl(value?: string): boolean;
|
||||
export declare function selectResourceURL(serverUrl: string | URL, provider: OAuthClientProvider, resourceMetadata?: OAuthProtectedResourceMetadata): Promise<URL | undefined>;
|
||||
/**
|
||||
* Extract resource_metadata, scope, and error from WWW-Authenticate header.
|
||||
*/
|
||||
export declare function extractWWWAuthenticateParams(res: Response): {
|
||||
resourceMetadataUrl?: URL;
|
||||
scope?: string;
|
||||
error?: string;
|
||||
};
|
||||
/**
|
||||
* Extract resource_metadata from response header.
|
||||
* @deprecated Use `extractWWWAuthenticateParams` instead.
|
||||
*/
|
||||
export declare function extractResourceMetadataUrl(res: Response): URL | undefined;
|
||||
/**
|
||||
@@ -221,7 +146,7 @@ export declare function discoverOAuthProtectedResourceMetadata(serverUrl: string
|
||||
*
|
||||
* @deprecated This function is deprecated in favor of `discoverAuthorizationServerMetadata`.
|
||||
*/
|
||||
export declare function discoverOAuthMetadata(issuer: string | URL, { authorizationServerUrl, protocolVersion }?: {
|
||||
export declare function discoverOAuthMetadata(issuer: string | URL, { authorizationServerUrl, protocolVersion, }?: {
|
||||
authorizationServerUrl?: string | URL;
|
||||
protocolVersion?: string;
|
||||
}, fetchFn?: FetchLike): Promise<OAuthMetadata | undefined>;
|
||||
@@ -229,7 +154,8 @@ export declare function discoverOAuthMetadata(issuer: string | URL, { authorizat
|
||||
* Builds a list of discovery URLs to try for authorization server metadata.
|
||||
* URLs are returned in priority order:
|
||||
* 1. OAuth metadata at the given URL
|
||||
* 2. OIDC metadata endpoints at the given URL
|
||||
* 2. OAuth metadata at root (if URL has path)
|
||||
* 3. OIDC metadata endpoints
|
||||
*/
|
||||
export declare function buildDiscoveryUrls(authorizationServerUrl: string | URL): {
|
||||
url: URL;
|
||||
@@ -251,16 +177,16 @@ export declare function buildDiscoveryUrls(authorizationServerUrl: string | URL)
|
||||
* @param options.protocolVersion - MCP protocol version to use, defaults to LATEST_PROTOCOL_VERSION
|
||||
* @returns Promise resolving to authorization server metadata, or undefined if discovery fails
|
||||
*/
|
||||
export declare function discoverAuthorizationServerMetadata(authorizationServerUrl: string | URL, { fetchFn, protocolVersion }?: {
|
||||
export declare function discoverAuthorizationServerMetadata(authorizationServerUrl: string | URL, { fetchFn, protocolVersion, }?: {
|
||||
fetchFn?: FetchLike;
|
||||
protocolVersion?: string;
|
||||
}): Promise<AuthorizationServerMetadata | undefined>;
|
||||
/**
|
||||
* Begins the authorization flow with the given server, by generating a PKCE challenge and constructing the authorization URL.
|
||||
*/
|
||||
export declare function startAuthorization(authorizationServerUrl: string | URL, { metadata, clientInformation, redirectUrl, scope, state, resource }: {
|
||||
export declare function startAuthorization(authorizationServerUrl: string | URL, { metadata, clientInformation, redirectUrl, scope, state, resource, }: {
|
||||
metadata?: AuthorizationServerMetadata;
|
||||
clientInformation: OAuthClientInformationMixed;
|
||||
clientInformation: OAuthClientInformation;
|
||||
redirectUrl: string | URL;
|
||||
scope?: string;
|
||||
state?: string;
|
||||
@@ -269,18 +195,6 @@ export declare function startAuthorization(authorizationServerUrl: string | URL,
|
||||
authorizationUrl: URL;
|
||||
codeVerifier: string;
|
||||
}>;
|
||||
/**
|
||||
* Prepares token request parameters for an authorization code exchange.
|
||||
*
|
||||
* This is the default implementation used by fetchToken when the provider
|
||||
* doesn't implement prepareTokenRequest.
|
||||
*
|
||||
* @param authorizationCode - The authorization code received from the authorization endpoint
|
||||
* @param codeVerifier - The PKCE code verifier
|
||||
* @param redirectUri - The redirect URI used in the authorization request
|
||||
* @returns URLSearchParams for the authorization_code grant
|
||||
*/
|
||||
export declare function prepareAuthorizationCodeRequest(authorizationCode: string, codeVerifier: string, redirectUri: string | URL): URLSearchParams;
|
||||
/**
|
||||
* Exchanges an authorization code for an access token with the given server.
|
||||
*
|
||||
@@ -293,14 +207,14 @@ export declare function prepareAuthorizationCodeRequest(authorizationCode: strin
|
||||
* @returns Promise resolving to OAuth tokens
|
||||
* @throws {Error} When token exchange fails or authentication is invalid
|
||||
*/
|
||||
export declare function exchangeAuthorization(authorizationServerUrl: string | URL, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }: {
|
||||
export declare function exchangeAuthorization(authorizationServerUrl: string | URL, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn, }: {
|
||||
metadata?: AuthorizationServerMetadata;
|
||||
clientInformation: OAuthClientInformationMixed;
|
||||
clientInformation: OAuthClientInformation;
|
||||
authorizationCode: string;
|
||||
codeVerifier: string;
|
||||
redirectUri: string | URL;
|
||||
resource?: URL;
|
||||
addClientAuthentication?: OAuthClientProvider['addClientAuthentication'];
|
||||
addClientAuthentication?: OAuthClientProvider["addClientAuthentication"];
|
||||
fetchFn?: FetchLike;
|
||||
}): Promise<OAuthTokens>;
|
||||
/**
|
||||
@@ -315,54 +229,20 @@ export declare function exchangeAuthorization(authorizationServerUrl: string | U
|
||||
* @returns Promise resolving to OAuth tokens (preserves original refresh_token if not replaced)
|
||||
* @throws {Error} When token refresh fails or authentication is invalid
|
||||
*/
|
||||
export declare function refreshAuthorization(authorizationServerUrl: string | URL, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }: {
|
||||
export declare function refreshAuthorization(authorizationServerUrl: string | URL, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn, }: {
|
||||
metadata?: AuthorizationServerMetadata;
|
||||
clientInformation: OAuthClientInformationMixed;
|
||||
clientInformation: OAuthClientInformation;
|
||||
refreshToken: string;
|
||||
resource?: URL;
|
||||
addClientAuthentication?: OAuthClientProvider['addClientAuthentication'];
|
||||
fetchFn?: FetchLike;
|
||||
}): Promise<OAuthTokens>;
|
||||
/**
|
||||
* Unified token fetching that works with any grant type via provider.prepareTokenRequest().
|
||||
*
|
||||
* This function provides a single entry point for obtaining tokens regardless of the
|
||||
* OAuth grant type. The provider's prepareTokenRequest() method determines which grant
|
||||
* to use and supplies the grant-specific parameters.
|
||||
*
|
||||
* @param provider - OAuth client provider that implements prepareTokenRequest()
|
||||
* @param authorizationServerUrl - The authorization server's base URL
|
||||
* @param options - Configuration for the token request
|
||||
* @returns Promise resolving to OAuth tokens
|
||||
* @throws {Error} When provider doesn't implement prepareTokenRequest or token fetch fails
|
||||
*
|
||||
* @example
|
||||
* // Provider for client_credentials:
|
||||
* class MyProvider implements OAuthClientProvider {
|
||||
* prepareTokenRequest(scope) {
|
||||
* const params = new URLSearchParams({ grant_type: 'client_credentials' });
|
||||
* if (scope) params.set('scope', scope);
|
||||
* return params;
|
||||
* }
|
||||
* // ... other methods
|
||||
* }
|
||||
*
|
||||
* const tokens = await fetchToken(provider, authServerUrl, { metadata });
|
||||
*/
|
||||
export declare function fetchToken(provider: OAuthClientProvider, authorizationServerUrl: string | URL, { metadata, resource, authorizationCode, fetchFn }?: {
|
||||
metadata?: AuthorizationServerMetadata;
|
||||
resource?: URL;
|
||||
/** Authorization code for the default authorization_code grant flow */
|
||||
authorizationCode?: string;
|
||||
addClientAuthentication?: OAuthClientProvider["addClientAuthentication"];
|
||||
fetchFn?: FetchLike;
|
||||
}): Promise<OAuthTokens>;
|
||||
/**
|
||||
* Performs OAuth 2.0 Dynamic Client Registration according to RFC 7591.
|
||||
*/
|
||||
export declare function registerClient(authorizationServerUrl: string | URL, { metadata, clientMetadata, fetchFn }: {
|
||||
export declare function registerClient(authorizationServerUrl: string | URL, { metadata, clientMetadata, fetchFn, }: {
|
||||
metadata?: AuthorizationServerMetadata;
|
||||
clientMetadata: OAuthClientMetadata;
|
||||
fetchFn?: FetchLike;
|
||||
}): Promise<OAuthClientInformationFull>;
|
||||
export {};
|
||||
//# sourceMappingURL=auth.d.ts.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+195
-325
@@ -4,22 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UnauthorizedError = void 0;
|
||||
exports.selectClientAuthMethod = selectClientAuthMethod;
|
||||
exports.parseErrorResponse = parseErrorResponse;
|
||||
exports.auth = auth;
|
||||
exports.isHttpsUrl = isHttpsUrl;
|
||||
exports.selectResourceURL = selectResourceURL;
|
||||
exports.extractWWWAuthenticateParams = extractWWWAuthenticateParams;
|
||||
exports.extractResourceMetadataUrl = extractResourceMetadataUrl;
|
||||
exports.discoverOAuthProtectedResourceMetadata = discoverOAuthProtectedResourceMetadata;
|
||||
exports.discoverOAuthMetadata = discoverOAuthMetadata;
|
||||
exports.buildDiscoveryUrls = buildDiscoveryUrls;
|
||||
exports.discoverAuthorizationServerMetadata = discoverAuthorizationServerMetadata;
|
||||
exports.startAuthorization = startAuthorization;
|
||||
exports.prepareAuthorizationCodeRequest = prepareAuthorizationCodeRequest;
|
||||
exports.exchangeAuthorization = exchangeAuthorization;
|
||||
exports.refreshAuthorization = refreshAuthorization;
|
||||
exports.fetchToken = fetchToken;
|
||||
exports.registerClient = registerClient;
|
||||
const pkce_challenge_1 = __importDefault(require("pkce-challenge"));
|
||||
const types_js_1 = require("../types.js");
|
||||
@@ -29,15 +24,10 @@ const auth_utils_js_1 = require("../shared/auth-utils.js");
|
||||
const errors_js_1 = require("../server/auth/errors.js");
|
||||
class UnauthorizedError extends Error {
|
||||
constructor(message) {
|
||||
super(message ?? 'Unauthorized');
|
||||
super(message !== null && message !== void 0 ? message : "Unauthorized");
|
||||
}
|
||||
}
|
||||
exports.UnauthorizedError = UnauthorizedError;
|
||||
function isClientAuthMethod(method) {
|
||||
return ['client_secret_basic', 'client_secret_post', 'none'].includes(method);
|
||||
}
|
||||
const AUTHORIZATION_CODE_RESPONSE_TYPE = 'code';
|
||||
const AUTHORIZATION_CODE_CHALLENGE_METHOD = 'S256';
|
||||
/**
|
||||
* Determines the best client authentication method to use based on server support and client configuration.
|
||||
*
|
||||
@@ -54,27 +44,20 @@ function selectClientAuthMethod(clientInformation, supportedMethods) {
|
||||
const hasClientSecret = clientInformation.client_secret !== undefined;
|
||||
// If server doesn't specify supported methods, use RFC 6749 defaults
|
||||
if (supportedMethods.length === 0) {
|
||||
return hasClientSecret ? 'client_secret_post' : 'none';
|
||||
}
|
||||
// Prefer the method returned by the server during client registration if valid and supported
|
||||
if ('token_endpoint_auth_method' in clientInformation &&
|
||||
clientInformation.token_endpoint_auth_method &&
|
||||
isClientAuthMethod(clientInformation.token_endpoint_auth_method) &&
|
||||
supportedMethods.includes(clientInformation.token_endpoint_auth_method)) {
|
||||
return clientInformation.token_endpoint_auth_method;
|
||||
return hasClientSecret ? "client_secret_post" : "none";
|
||||
}
|
||||
// Try methods in priority order (most secure first)
|
||||
if (hasClientSecret && supportedMethods.includes('client_secret_basic')) {
|
||||
return 'client_secret_basic';
|
||||
if (hasClientSecret && supportedMethods.includes("client_secret_basic")) {
|
||||
return "client_secret_basic";
|
||||
}
|
||||
if (hasClientSecret && supportedMethods.includes('client_secret_post')) {
|
||||
return 'client_secret_post';
|
||||
if (hasClientSecret && supportedMethods.includes("client_secret_post")) {
|
||||
return "client_secret_post";
|
||||
}
|
||||
if (supportedMethods.includes('none')) {
|
||||
return 'none';
|
||||
if (supportedMethods.includes("none")) {
|
||||
return "none";
|
||||
}
|
||||
// Fallback: use what we have
|
||||
return hasClientSecret ? 'client_secret_post' : 'none';
|
||||
return hasClientSecret ? "client_secret_post" : "none";
|
||||
}
|
||||
/**
|
||||
* Applies client authentication to the request based on the specified method.
|
||||
@@ -93,13 +76,13 @@ function selectClientAuthMethod(clientInformation, supportedMethods) {
|
||||
function applyClientAuthentication(method, clientInformation, headers, params) {
|
||||
const { client_id, client_secret } = clientInformation;
|
||||
switch (method) {
|
||||
case 'client_secret_basic':
|
||||
case "client_secret_basic":
|
||||
applyBasicAuth(client_id, client_secret, headers);
|
||||
return;
|
||||
case 'client_secret_post':
|
||||
case "client_secret_post":
|
||||
applyPostAuth(client_id, client_secret, params);
|
||||
return;
|
||||
case 'none':
|
||||
case "none":
|
||||
applyPublicAuth(client_id, params);
|
||||
return;
|
||||
default:
|
||||
@@ -111,25 +94,25 @@ function applyClientAuthentication(method, clientInformation, headers, params) {
|
||||
*/
|
||||
function applyBasicAuth(clientId, clientSecret, headers) {
|
||||
if (!clientSecret) {
|
||||
throw new Error('client_secret_basic authentication requires a client_secret');
|
||||
throw new Error("client_secret_basic authentication requires a client_secret");
|
||||
}
|
||||
const credentials = btoa(`${clientId}:${clientSecret}`);
|
||||
headers.set('Authorization', `Basic ${credentials}`);
|
||||
headers.set("Authorization", `Basic ${credentials}`);
|
||||
}
|
||||
/**
|
||||
* Applies POST body authentication (RFC 6749 Section 2.3.1)
|
||||
*/
|
||||
function applyPostAuth(clientId, clientSecret, params) {
|
||||
params.set('client_id', clientId);
|
||||
params.set("client_id", clientId);
|
||||
if (clientSecret) {
|
||||
params.set('client_secret', clientSecret);
|
||||
params.set("client_secret", clientSecret);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Applies public client authentication (RFC 6749 Section 2.1)
|
||||
*/
|
||||
function applyPublicAuth(clientId, params) {
|
||||
params.set('client_id', clientId);
|
||||
params.set("client_id", clientId);
|
||||
}
|
||||
/**
|
||||
* Parses an OAuth error response from a string or Response object.
|
||||
@@ -164,24 +147,25 @@ async function parseErrorResponse(input) {
|
||||
* instead of linking together the other lower-level functions in this module.
|
||||
*/
|
||||
async function auth(provider, options) {
|
||||
var _a, _b;
|
||||
try {
|
||||
return await authInternal(provider, options);
|
||||
}
|
||||
catch (error) {
|
||||
// Handle recoverable error types by invalidating credentials and retrying
|
||||
if (error instanceof errors_js_1.InvalidClientError || error instanceof errors_js_1.UnauthorizedClientError) {
|
||||
await provider.invalidateCredentials?.('all');
|
||||
await ((_a = provider.invalidateCredentials) === null || _a === void 0 ? void 0 : _a.call(provider, 'all'));
|
||||
return await authInternal(provider, options);
|
||||
}
|
||||
else if (error instanceof errors_js_1.InvalidGrantError) {
|
||||
await provider.invalidateCredentials?.('tokens');
|
||||
await ((_b = provider.invalidateCredentials) === null || _b === void 0 ? void 0 : _b.call(provider, 'tokens'));
|
||||
return await authInternal(provider, options);
|
||||
}
|
||||
// Throw otherwise
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn }) {
|
||||
async function authInternal(provider, { serverUrl, authorizationCode, scope, resourceMetadataUrl, fetchFn, }) {
|
||||
let resourceMetadata;
|
||||
let authorizationServerUrl;
|
||||
try {
|
||||
@@ -190,69 +174,56 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
||||
authorizationServerUrl = resourceMetadata.authorization_servers[0];
|
||||
}
|
||||
}
|
||||
catch {
|
||||
catch (_a) {
|
||||
// Ignore errors and fall back to /.well-known/oauth-authorization-server
|
||||
}
|
||||
/**
|
||||
* If we don't get a valid authorization server metadata from protected resource metadata,
|
||||
* fallback to the legacy MCP spec's implementation (version 2025-03-26): MCP server base URL acts as the Authorization server.
|
||||
* fallback to the legacy MCP spec's implementation (version 2025-03-26): MCP server acts as the Authorization server.
|
||||
*/
|
||||
if (!authorizationServerUrl) {
|
||||
authorizationServerUrl = new URL('/', serverUrl);
|
||||
authorizationServerUrl = serverUrl;
|
||||
}
|
||||
const resource = await selectResourceURL(serverUrl, provider, resourceMetadata);
|
||||
const metadata = await discoverAuthorizationServerMetadata(authorizationServerUrl, {
|
||||
fetchFn
|
||||
fetchFn,
|
||||
});
|
||||
// Handle client registration if needed
|
||||
let clientInformation = await Promise.resolve(provider.clientInformation());
|
||||
if (!clientInformation) {
|
||||
if (authorizationCode !== undefined) {
|
||||
throw new Error('Existing OAuth client information is required when exchanging an authorization code');
|
||||
throw new Error("Existing OAuth client information is required when exchanging an authorization code");
|
||||
}
|
||||
const supportsUrlBasedClientId = metadata?.client_id_metadata_document_supported === true;
|
||||
const clientMetadataUrl = provider.clientMetadataUrl;
|
||||
if (clientMetadataUrl && !isHttpsUrl(clientMetadataUrl)) {
|
||||
throw new errors_js_1.InvalidClientMetadataError(`clientMetadataUrl must be a valid HTTPS URL with a non-root pathname, got: ${clientMetadataUrl}`);
|
||||
if (!provider.saveClientInformation) {
|
||||
throw new Error("OAuth client information must be saveable for dynamic registration");
|
||||
}
|
||||
const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
|
||||
if (shouldUseUrlBasedClientId) {
|
||||
// SEP-991: URL-based Client IDs
|
||||
clientInformation = {
|
||||
client_id: clientMetadataUrl
|
||||
};
|
||||
await provider.saveClientInformation?.(clientInformation);
|
||||
}
|
||||
else {
|
||||
// Fallback to dynamic registration
|
||||
if (!provider.saveClientInformation) {
|
||||
throw new Error('OAuth client information must be saveable for dynamic registration');
|
||||
}
|
||||
const fullInformation = await registerClient(authorizationServerUrl, {
|
||||
metadata,
|
||||
clientMetadata: provider.clientMetadata,
|
||||
fetchFn
|
||||
});
|
||||
await provider.saveClientInformation(fullInformation);
|
||||
clientInformation = fullInformation;
|
||||
}
|
||||
}
|
||||
// Non-interactive flows (e.g., client_credentials, jwt-bearer) don't need a redirect URL
|
||||
const nonInteractiveFlow = !provider.redirectUrl;
|
||||
// Exchange authorization code for tokens, or fetch tokens directly for non-interactive flows
|
||||
if (authorizationCode !== undefined || nonInteractiveFlow) {
|
||||
const tokens = await fetchToken(provider, authorizationServerUrl, {
|
||||
const fullInformation = await registerClient(authorizationServerUrl, {
|
||||
metadata,
|
||||
resource,
|
||||
clientMetadata: provider.clientMetadata,
|
||||
fetchFn,
|
||||
});
|
||||
await provider.saveClientInformation(fullInformation);
|
||||
clientInformation = fullInformation;
|
||||
}
|
||||
// Exchange authorization code for tokens
|
||||
if (authorizationCode !== undefined) {
|
||||
const codeVerifier = await provider.codeVerifier();
|
||||
const tokens = await exchangeAuthorization(authorizationServerUrl, {
|
||||
metadata,
|
||||
clientInformation,
|
||||
authorizationCode,
|
||||
fetchFn
|
||||
codeVerifier,
|
||||
redirectUri: provider.redirectUrl,
|
||||
resource,
|
||||
addClientAuthentication: provider.addClientAuthentication,
|
||||
fetchFn: fetchFn,
|
||||
});
|
||||
await provider.saveTokens(tokens);
|
||||
return 'AUTHORIZED';
|
||||
return "AUTHORIZED";
|
||||
}
|
||||
const tokens = await provider.tokens();
|
||||
// Handle token refresh or new authorization
|
||||
if (tokens?.refresh_token) {
|
||||
if (tokens === null || tokens === void 0 ? void 0 : tokens.refresh_token) {
|
||||
try {
|
||||
// Attempt to refresh the token
|
||||
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
||||
@@ -261,10 +232,10 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
||||
refreshToken: tokens.refresh_token,
|
||||
resource,
|
||||
addClientAuthentication: provider.addClientAuthentication,
|
||||
fetchFn
|
||||
fetchFn,
|
||||
});
|
||||
await provider.saveTokens(newTokens);
|
||||
return 'AUTHORIZED';
|
||||
return "AUTHORIZED";
|
||||
}
|
||||
catch (error) {
|
||||
// If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry.
|
||||
@@ -284,33 +255,18 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
||||
clientInformation,
|
||||
state,
|
||||
redirectUrl: provider.redirectUrl,
|
||||
scope: scope || resourceMetadata?.scopes_supported?.join(' ') || provider.clientMetadata.scope,
|
||||
resource
|
||||
scope: scope || provider.clientMetadata.scope,
|
||||
resource,
|
||||
});
|
||||
await provider.saveCodeVerifier(codeVerifier);
|
||||
await provider.redirectToAuthorization(authorizationUrl);
|
||||
return 'REDIRECT';
|
||||
}
|
||||
/**
|
||||
* SEP-991: URL-based Client IDs
|
||||
* Validate that the client_id is a valid URL with https scheme
|
||||
*/
|
||||
function isHttpsUrl(value) {
|
||||
if (!value)
|
||||
return false;
|
||||
try {
|
||||
const url = new URL(value);
|
||||
return url.protocol === 'https:' && url.pathname !== '/';
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
return "REDIRECT";
|
||||
}
|
||||
async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
||||
const defaultResource = (0, auth_utils_js_1.resourceUrlFromServerUrl)(serverUrl);
|
||||
// If provider has custom validation, delegate to it
|
||||
if (provider.validateResourceURL) {
|
||||
return await provider.validateResourceURL(defaultResource, resourceMetadata?.resource);
|
||||
return await provider.validateResourceURL(defaultResource, resourceMetadata === null || resourceMetadata === void 0 ? void 0 : resourceMetadata.resource);
|
||||
}
|
||||
// Only include resource parameter when Protected Resource Metadata is present
|
||||
if (!resourceMetadata) {
|
||||
@@ -323,62 +279,11 @@ async function selectResourceURL(serverUrl, provider, resourceMetadata) {
|
||||
// Prefer the resource from metadata since it's what the server is telling us to request
|
||||
return new URL(resourceMetadata.resource);
|
||||
}
|
||||
/**
|
||||
* Extract resource_metadata, scope, and error from WWW-Authenticate header.
|
||||
*/
|
||||
function extractWWWAuthenticateParams(res) {
|
||||
const authenticateHeader = res.headers.get('WWW-Authenticate');
|
||||
if (!authenticateHeader) {
|
||||
return {};
|
||||
}
|
||||
const [type, scheme] = authenticateHeader.split(' ');
|
||||
if (type.toLowerCase() !== 'bearer' || !scheme) {
|
||||
return {};
|
||||
}
|
||||
const resourceMetadataMatch = extractFieldFromWwwAuth(res, 'resource_metadata') || undefined;
|
||||
let resourceMetadataUrl;
|
||||
if (resourceMetadataMatch) {
|
||||
try {
|
||||
resourceMetadataUrl = new URL(resourceMetadataMatch);
|
||||
}
|
||||
catch {
|
||||
// Ignore invalid URL
|
||||
}
|
||||
}
|
||||
const scope = extractFieldFromWwwAuth(res, 'scope') || undefined;
|
||||
const error = extractFieldFromWwwAuth(res, 'error') || undefined;
|
||||
return {
|
||||
resourceMetadataUrl,
|
||||
scope,
|
||||
error
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Extracts a specific field's value from the WWW-Authenticate header string.
|
||||
*
|
||||
* @param response The HTTP response object containing the headers.
|
||||
* @param fieldName The name of the field to extract (e.g., "realm", "nonce").
|
||||
* @returns The field value
|
||||
*/
|
||||
function extractFieldFromWwwAuth(response, fieldName) {
|
||||
const wwwAuthHeader = response.headers.get('WWW-Authenticate');
|
||||
if (!wwwAuthHeader) {
|
||||
return null;
|
||||
}
|
||||
const pattern = new RegExp(`${fieldName}=(?:"([^"]+)"|([^\\s,]+))`);
|
||||
const match = wwwAuthHeader.match(pattern);
|
||||
if (match) {
|
||||
// Pattern matches: field_name="value" or field_name=value (unquoted)
|
||||
return match[1] || match[2];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Extract resource_metadata from response header.
|
||||
* @deprecated Use `extractWWWAuthenticateParams` instead.
|
||||
*/
|
||||
function extractResourceMetadataUrl(res) {
|
||||
const authenticateHeader = res.headers.get('WWW-Authenticate');
|
||||
const authenticateHeader = res.headers.get("WWW-Authenticate");
|
||||
if (!authenticateHeader) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -394,7 +299,7 @@ function extractResourceMetadataUrl(res) {
|
||||
try {
|
||||
return new URL(match[1]);
|
||||
}
|
||||
catch {
|
||||
catch (_a) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -406,15 +311,13 @@ function extractResourceMetadataUrl(res) {
|
||||
*/
|
||||
async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn = fetch) {
|
||||
const response = await discoverMetadataWithFallback(serverUrl, 'oauth-protected-resource', fetchFn, {
|
||||
protocolVersion: opts?.protocolVersion,
|
||||
metadataUrl: opts?.resourceMetadataUrl
|
||||
protocolVersion: opts === null || opts === void 0 ? void 0 : opts.protocolVersion,
|
||||
metadataUrl: opts === null || opts === void 0 ? void 0 : opts.resourceMetadataUrl,
|
||||
});
|
||||
if (!response || response.status === 404) {
|
||||
await response?.body?.cancel();
|
||||
throw new Error(`Resource server does not implement OAuth 2.0 Protected Resource Metadata.`);
|
||||
}
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel();
|
||||
throw new Error(`HTTP ${response.status} trying to load well-known OAuth protected resource metadata.`);
|
||||
}
|
||||
return auth_js_2.OAuthProtectedResourceMetadataSchema.parse(await response.json());
|
||||
@@ -448,14 +351,16 @@ function buildWellKnownPath(wellKnownPrefix, pathname = '', options = {}) {
|
||||
if (pathname.endsWith('/')) {
|
||||
pathname = pathname.slice(0, -1);
|
||||
}
|
||||
return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`;
|
||||
return options.prependPathname
|
||||
? `${pathname}/.well-known/${wellKnownPrefix}`
|
||||
: `/.well-known/${wellKnownPrefix}${pathname}`;
|
||||
}
|
||||
/**
|
||||
* Tries to discover OAuth metadata at a specific URL
|
||||
*/
|
||||
async function tryMetadataDiscovery(url, protocolVersion, fetchFn = fetch) {
|
||||
const headers = {
|
||||
'MCP-Protocol-Version': protocolVersion
|
||||
"MCP-Protocol-Version": protocolVersion
|
||||
};
|
||||
return await fetchWithCorsRetry(url, headers, fetchFn);
|
||||
}
|
||||
@@ -463,27 +368,28 @@ async function tryMetadataDiscovery(url, protocolVersion, fetchFn = fetch) {
|
||||
* Determines if fallback to root discovery should be attempted
|
||||
*/
|
||||
function shouldAttemptFallback(response, pathname) {
|
||||
return !response || (response.status >= 400 && response.status < 500 && pathname !== '/');
|
||||
return !response || response.status === 404 && pathname !== '/';
|
||||
}
|
||||
/**
|
||||
* Generic function for discovering OAuth metadata with fallback support
|
||||
*/
|
||||
async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
|
||||
var _a, _b;
|
||||
const issuer = new URL(serverUrl);
|
||||
const protocolVersion = opts?.protocolVersion ?? types_js_1.LATEST_PROTOCOL_VERSION;
|
||||
const protocolVersion = (_a = opts === null || opts === void 0 ? void 0 : opts.protocolVersion) !== null && _a !== void 0 ? _a : types_js_1.LATEST_PROTOCOL_VERSION;
|
||||
let url;
|
||||
if (opts?.metadataUrl) {
|
||||
if (opts === null || opts === void 0 ? void 0 : opts.metadataUrl) {
|
||||
url = new URL(opts.metadataUrl);
|
||||
}
|
||||
else {
|
||||
// Try path-aware discovery first
|
||||
const wellKnownPath = buildWellKnownPath(wellKnownType, issuer.pathname);
|
||||
url = new URL(wellKnownPath, opts?.metadataServerUrl ?? issuer);
|
||||
url = new URL(wellKnownPath, (_b = opts === null || opts === void 0 ? void 0 : opts.metadataServerUrl) !== null && _b !== void 0 ? _b : issuer);
|
||||
url.search = issuer.search;
|
||||
}
|
||||
let response = await tryMetadataDiscovery(url, protocolVersion, fetchFn);
|
||||
// If path-aware discovery fails with 404 and we're not already at root, try fallback to root discovery
|
||||
if (!opts?.metadataUrl && shouldAttemptFallback(response, issuer.pathname)) {
|
||||
if (!(opts === null || opts === void 0 ? void 0 : opts.metadataUrl) && shouldAttemptFallback(response, issuer.pathname)) {
|
||||
const rootUrl = new URL(`/.well-known/${wellKnownType}`, issuer);
|
||||
response = await tryMetadataDiscovery(rootUrl, protocolVersion, fetchFn);
|
||||
}
|
||||
@@ -497,7 +403,7 @@ async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, o
|
||||
*
|
||||
* @deprecated This function is deprecated in favor of `discoverAuthorizationServerMetadata`.
|
||||
*/
|
||||
async function discoverOAuthMetadata(issuer, { authorizationServerUrl, protocolVersion } = {}, fetchFn = fetch) {
|
||||
async function discoverOAuthMetadata(issuer, { authorizationServerUrl, protocolVersion, } = {}, fetchFn = fetch) {
|
||||
if (typeof issuer === 'string') {
|
||||
issuer = new URL(issuer);
|
||||
}
|
||||
@@ -507,17 +413,15 @@ async function discoverOAuthMetadata(issuer, { authorizationServerUrl, protocolV
|
||||
if (typeof authorizationServerUrl === 'string') {
|
||||
authorizationServerUrl = new URL(authorizationServerUrl);
|
||||
}
|
||||
protocolVersion ?? (protocolVersion = types_js_1.LATEST_PROTOCOL_VERSION);
|
||||
protocolVersion !== null && protocolVersion !== void 0 ? protocolVersion : (protocolVersion = types_js_1.LATEST_PROTOCOL_VERSION);
|
||||
const response = await discoverMetadataWithFallback(authorizationServerUrl, 'oauth-authorization-server', fetchFn, {
|
||||
protocolVersion,
|
||||
metadataServerUrl: authorizationServerUrl
|
||||
metadataServerUrl: authorizationServerUrl,
|
||||
});
|
||||
if (!response || response.status === 404) {
|
||||
await response?.body?.cancel();
|
||||
return undefined;
|
||||
}
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel();
|
||||
throw new Error(`HTTP ${response.status} trying to load well-known OAuth metadata`);
|
||||
}
|
||||
return auth_js_2.OAuthMetadataSchema.parse(await response.json());
|
||||
@@ -526,7 +430,8 @@ async function discoverOAuthMetadata(issuer, { authorizationServerUrl, protocolV
|
||||
* Builds a list of discovery URLs to try for authorization server metadata.
|
||||
* URLs are returned in priority order:
|
||||
* 1. OAuth metadata at the given URL
|
||||
* 2. OIDC metadata endpoints at the given URL
|
||||
* 2. OAuth metadata at root (if URL has path)
|
||||
* 3. OIDC metadata endpoints
|
||||
*/
|
||||
function buildDiscoveryUrls(authorizationServerUrl) {
|
||||
const url = typeof authorizationServerUrl === 'string' ? new URL(authorizationServerUrl) : authorizationServerUrl;
|
||||
@@ -556,7 +461,12 @@ function buildDiscoveryUrls(authorizationServerUrl) {
|
||||
url: new URL(`/.well-known/oauth-authorization-server${pathname}`, url.origin),
|
||||
type: 'oauth'
|
||||
});
|
||||
// 2. OIDC metadata endpoints
|
||||
// Root path: https://example.com/.well-known/oauth-authorization-server
|
||||
urlsToTry.push({
|
||||
url: new URL('/.well-known/oauth-authorization-server', url.origin),
|
||||
type: 'oauth'
|
||||
});
|
||||
// 3. OIDC metadata endpoints
|
||||
// RFC 8414 style: Insert /.well-known/openid-configuration before the path
|
||||
urlsToTry.push({
|
||||
url: new URL(`/.well-known/openid-configuration${pathname}`, url.origin),
|
||||
@@ -585,11 +495,9 @@ function buildDiscoveryUrls(authorizationServerUrl) {
|
||||
* @param options.protocolVersion - MCP protocol version to use, defaults to LATEST_PROTOCOL_VERSION
|
||||
* @returns Promise resolving to authorization server metadata, or undefined if discovery fails
|
||||
*/
|
||||
async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = types_js_1.LATEST_PROTOCOL_VERSION } = {}) {
|
||||
const headers = {
|
||||
'MCP-Protocol-Version': protocolVersion,
|
||||
Accept: 'application/json'
|
||||
};
|
||||
async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fetchFn = fetch, protocolVersion = types_js_1.LATEST_PROTOCOL_VERSION, } = {}) {
|
||||
var _a;
|
||||
const headers = { 'MCP-Protocol-Version': protocolVersion };
|
||||
// Get the list of URLs to try
|
||||
const urlsToTry = buildDiscoveryUrls(authorizationServerUrl);
|
||||
// Try each URL in order
|
||||
@@ -603,7 +511,6 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
|
||||
continue;
|
||||
}
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel();
|
||||
// Continue looking for any 4xx response code.
|
||||
if (response.status >= 400 && response.status < 500) {
|
||||
continue; // Try next URL
|
||||
@@ -615,7 +522,12 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
|
||||
return auth_js_2.OAuthMetadataSchema.parse(await response.json());
|
||||
}
|
||||
else {
|
||||
return auth_js_1.OpenIdProviderDiscoveryMetadataSchema.parse(await response.json());
|
||||
const metadata = auth_js_1.OpenIdProviderDiscoveryMetadataSchema.parse(await response.json());
|
||||
// MCP spec requires OIDC providers to support S256 PKCE
|
||||
if (!((_a = metadata.code_challenge_methods_supported) === null || _a === void 0 ? void 0 : _a.includes('S256'))) {
|
||||
throw new Error(`Incompatible OIDC provider at ${endpointUrl}: does not support S256 code challenge method required by MCP specification`);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -623,97 +535,49 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
|
||||
/**
|
||||
* Begins the authorization flow with the given server, by generating a PKCE challenge and constructing the authorization URL.
|
||||
*/
|
||||
async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
|
||||
async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource, }) {
|
||||
const responseType = "code";
|
||||
const codeChallengeMethod = "S256";
|
||||
let authorizationUrl;
|
||||
if (metadata) {
|
||||
authorizationUrl = new URL(metadata.authorization_endpoint);
|
||||
if (!metadata.response_types_supported.includes(AUTHORIZATION_CODE_RESPONSE_TYPE)) {
|
||||
throw new Error(`Incompatible auth server: does not support response type ${AUTHORIZATION_CODE_RESPONSE_TYPE}`);
|
||||
if (!metadata.response_types_supported.includes(responseType)) {
|
||||
throw new Error(`Incompatible auth server: does not support response type ${responseType}`);
|
||||
}
|
||||
if (metadata.code_challenge_methods_supported &&
|
||||
!metadata.code_challenge_methods_supported.includes(AUTHORIZATION_CODE_CHALLENGE_METHOD)) {
|
||||
throw new Error(`Incompatible auth server: does not support code challenge method ${AUTHORIZATION_CODE_CHALLENGE_METHOD}`);
|
||||
if (!metadata.code_challenge_methods_supported ||
|
||||
!metadata.code_challenge_methods_supported.includes(codeChallengeMethod)) {
|
||||
throw new Error(`Incompatible auth server: does not support code challenge method ${codeChallengeMethod}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
authorizationUrl = new URL('/authorize', authorizationServerUrl);
|
||||
authorizationUrl = new URL("/authorize", authorizationServerUrl);
|
||||
}
|
||||
// Generate PKCE challenge
|
||||
const challenge = await (0, pkce_challenge_1.default)();
|
||||
const codeVerifier = challenge.code_verifier;
|
||||
const codeChallenge = challenge.code_challenge;
|
||||
authorizationUrl.searchParams.set('response_type', AUTHORIZATION_CODE_RESPONSE_TYPE);
|
||||
authorizationUrl.searchParams.set('client_id', clientInformation.client_id);
|
||||
authorizationUrl.searchParams.set('code_challenge', codeChallenge);
|
||||
authorizationUrl.searchParams.set('code_challenge_method', AUTHORIZATION_CODE_CHALLENGE_METHOD);
|
||||
authorizationUrl.searchParams.set('redirect_uri', String(redirectUrl));
|
||||
authorizationUrl.searchParams.set("response_type", responseType);
|
||||
authorizationUrl.searchParams.set("client_id", clientInformation.client_id);
|
||||
authorizationUrl.searchParams.set("code_challenge", codeChallenge);
|
||||
authorizationUrl.searchParams.set("code_challenge_method", codeChallengeMethod);
|
||||
authorizationUrl.searchParams.set("redirect_uri", String(redirectUrl));
|
||||
if (state) {
|
||||
authorizationUrl.searchParams.set('state', state);
|
||||
authorizationUrl.searchParams.set("state", state);
|
||||
}
|
||||
if (scope) {
|
||||
authorizationUrl.searchParams.set('scope', scope);
|
||||
authorizationUrl.searchParams.set("scope", scope);
|
||||
}
|
||||
if (scope?.includes('offline_access')) {
|
||||
if (scope === null || scope === void 0 ? void 0 : scope.includes("offline_access")) {
|
||||
// if the request includes the OIDC-only "offline_access" scope,
|
||||
// we need to set the prompt to "consent" to ensure the user is prompted to grant offline access
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
|
||||
authorizationUrl.searchParams.append('prompt', 'consent');
|
||||
authorizationUrl.searchParams.append("prompt", "consent");
|
||||
}
|
||||
if (resource) {
|
||||
authorizationUrl.searchParams.set('resource', resource.href);
|
||||
authorizationUrl.searchParams.set("resource", resource.href);
|
||||
}
|
||||
return { authorizationUrl, codeVerifier };
|
||||
}
|
||||
/**
|
||||
* Prepares token request parameters for an authorization code exchange.
|
||||
*
|
||||
* This is the default implementation used by fetchToken when the provider
|
||||
* doesn't implement prepareTokenRequest.
|
||||
*
|
||||
* @param authorizationCode - The authorization code received from the authorization endpoint
|
||||
* @param codeVerifier - The PKCE code verifier
|
||||
* @param redirectUri - The redirect URI used in the authorization request
|
||||
* @returns URLSearchParams for the authorization_code grant
|
||||
*/
|
||||
function prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri) {
|
||||
return new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
code: authorizationCode,
|
||||
code_verifier: codeVerifier,
|
||||
redirect_uri: String(redirectUri)
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Internal helper to execute a token request with the given parameters.
|
||||
* Used by exchangeAuthorization, refreshAuthorization, and fetchToken.
|
||||
*/
|
||||
async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequestParams, clientInformation, addClientAuthentication, resource, fetchFn }) {
|
||||
const tokenUrl = metadata?.token_endpoint ? new URL(metadata.token_endpoint) : new URL('/token', authorizationServerUrl);
|
||||
const headers = new Headers({
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json'
|
||||
});
|
||||
if (resource) {
|
||||
tokenRequestParams.set('resource', resource.href);
|
||||
}
|
||||
if (addClientAuthentication) {
|
||||
await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
|
||||
}
|
||||
else if (clientInformation) {
|
||||
const supportedMethods = metadata?.token_endpoint_auth_methods_supported ?? [];
|
||||
const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
|
||||
applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
|
||||
}
|
||||
const response = await (fetchFn ?? fetch)(tokenUrl, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: tokenRequestParams
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await parseErrorResponse(response);
|
||||
}
|
||||
return auth_js_2.OAuthTokensSchema.parse(await response.json());
|
||||
}
|
||||
/**
|
||||
* Exchanges an authorization code for an access token with the given server.
|
||||
*
|
||||
@@ -726,16 +590,48 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
|
||||
* @returns Promise resolving to OAuth tokens
|
||||
* @throws {Error} When token exchange fails or authentication is invalid
|
||||
*/
|
||||
async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn }) {
|
||||
const tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, redirectUri);
|
||||
return executeTokenRequest(authorizationServerUrl, {
|
||||
metadata,
|
||||
tokenRequestParams,
|
||||
clientInformation,
|
||||
addClientAuthentication,
|
||||
resource,
|
||||
fetchFn
|
||||
async function exchangeAuthorization(authorizationServerUrl, { metadata, clientInformation, authorizationCode, codeVerifier, redirectUri, resource, addClientAuthentication, fetchFn, }) {
|
||||
var _a;
|
||||
const grantType = "authorization_code";
|
||||
const tokenUrl = (metadata === null || metadata === void 0 ? void 0 : metadata.token_endpoint)
|
||||
? new URL(metadata.token_endpoint)
|
||||
: new URL("/token", authorizationServerUrl);
|
||||
if ((metadata === null || metadata === void 0 ? void 0 : metadata.grant_types_supported) &&
|
||||
!metadata.grant_types_supported.includes(grantType)) {
|
||||
throw new Error(`Incompatible auth server: does not support grant type ${grantType}`);
|
||||
}
|
||||
// Exchange code for tokens
|
||||
const headers = new Headers({
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json",
|
||||
});
|
||||
const params = new URLSearchParams({
|
||||
grant_type: grantType,
|
||||
code: authorizationCode,
|
||||
code_verifier: codeVerifier,
|
||||
redirect_uri: String(redirectUri),
|
||||
});
|
||||
if (addClientAuthentication) {
|
||||
addClientAuthentication(headers, params, authorizationServerUrl, metadata);
|
||||
}
|
||||
else {
|
||||
// Determine and apply client authentication method
|
||||
const supportedMethods = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.token_endpoint_auth_methods_supported) !== null && _a !== void 0 ? _a : [];
|
||||
const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
|
||||
applyClientAuthentication(authMethod, clientInformation, headers, params);
|
||||
}
|
||||
if (resource) {
|
||||
params.set("resource", resource.href);
|
||||
}
|
||||
const response = await (fetchFn !== null && fetchFn !== void 0 ? fetchFn : fetch)(tokenUrl, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: params,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await parseErrorResponse(response);
|
||||
}
|
||||
return auth_js_2.OAuthTokensSchema.parse(await response.json());
|
||||
}
|
||||
/**
|
||||
* Exchange a refresh token for an updated access token.
|
||||
@@ -749,96 +645,70 @@ async function exchangeAuthorization(authorizationServerUrl, { metadata, clientI
|
||||
* @returns Promise resolving to OAuth tokens (preserves original refresh_token if not replaced)
|
||||
* @throws {Error} When token refresh fails or authentication is invalid
|
||||
*/
|
||||
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
|
||||
const tokenRequestParams = new URLSearchParams({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refreshToken
|
||||
});
|
||||
const tokens = await executeTokenRequest(authorizationServerUrl, {
|
||||
metadata,
|
||||
tokenRequestParams,
|
||||
clientInformation,
|
||||
addClientAuthentication,
|
||||
resource,
|
||||
fetchFn
|
||||
});
|
||||
// Preserve original refresh token if server didn't return a new one
|
||||
return { refresh_token: refreshToken, ...tokens };
|
||||
}
|
||||
/**
|
||||
* Unified token fetching that works with any grant type via provider.prepareTokenRequest().
|
||||
*
|
||||
* This function provides a single entry point for obtaining tokens regardless of the
|
||||
* OAuth grant type. The provider's prepareTokenRequest() method determines which grant
|
||||
* to use and supplies the grant-specific parameters.
|
||||
*
|
||||
* @param provider - OAuth client provider that implements prepareTokenRequest()
|
||||
* @param authorizationServerUrl - The authorization server's base URL
|
||||
* @param options - Configuration for the token request
|
||||
* @returns Promise resolving to OAuth tokens
|
||||
* @throws {Error} When provider doesn't implement prepareTokenRequest or token fetch fails
|
||||
*
|
||||
* @example
|
||||
* // Provider for client_credentials:
|
||||
* class MyProvider implements OAuthClientProvider {
|
||||
* prepareTokenRequest(scope) {
|
||||
* const params = new URLSearchParams({ grant_type: 'client_credentials' });
|
||||
* if (scope) params.set('scope', scope);
|
||||
* return params;
|
||||
* }
|
||||
* // ... other methods
|
||||
* }
|
||||
*
|
||||
* const tokens = await fetchToken(provider, authServerUrl, { metadata });
|
||||
*/
|
||||
async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
|
||||
const scope = provider.clientMetadata.scope;
|
||||
// Use provider's prepareTokenRequest if available, otherwise fall back to authorization_code
|
||||
let tokenRequestParams;
|
||||
if (provider.prepareTokenRequest) {
|
||||
tokenRequestParams = await provider.prepareTokenRequest(scope);
|
||||
}
|
||||
// Default to authorization_code grant if no custom prepareTokenRequest
|
||||
if (!tokenRequestParams) {
|
||||
if (!authorizationCode) {
|
||||
throw new Error('Either provider.prepareTokenRequest() or authorizationCode is required');
|
||||
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn, }) {
|
||||
var _a;
|
||||
const grantType = "refresh_token";
|
||||
let tokenUrl;
|
||||
if (metadata) {
|
||||
tokenUrl = new URL(metadata.token_endpoint);
|
||||
if (metadata.grant_types_supported &&
|
||||
!metadata.grant_types_supported.includes(grantType)) {
|
||||
throw new Error(`Incompatible auth server: does not support grant type ${grantType}`);
|
||||
}
|
||||
if (!provider.redirectUrl) {
|
||||
throw new Error('redirectUrl is required for authorization_code flow');
|
||||
}
|
||||
const codeVerifier = await provider.codeVerifier();
|
||||
tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
|
||||
}
|
||||
const clientInformation = await provider.clientInformation();
|
||||
return executeTokenRequest(authorizationServerUrl, {
|
||||
metadata,
|
||||
tokenRequestParams,
|
||||
clientInformation: clientInformation ?? undefined,
|
||||
addClientAuthentication: provider.addClientAuthentication,
|
||||
resource,
|
||||
fetchFn
|
||||
else {
|
||||
tokenUrl = new URL("/token", authorizationServerUrl);
|
||||
}
|
||||
// Exchange refresh token
|
||||
const headers = new Headers({
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
});
|
||||
const params = new URLSearchParams({
|
||||
grant_type: grantType,
|
||||
refresh_token: refreshToken,
|
||||
});
|
||||
if (addClientAuthentication) {
|
||||
addClientAuthentication(headers, params, authorizationServerUrl, metadata);
|
||||
}
|
||||
else {
|
||||
// Determine and apply client authentication method
|
||||
const supportedMethods = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.token_endpoint_auth_methods_supported) !== null && _a !== void 0 ? _a : [];
|
||||
const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
|
||||
applyClientAuthentication(authMethod, clientInformation, headers, params);
|
||||
}
|
||||
if (resource) {
|
||||
params.set("resource", resource.href);
|
||||
}
|
||||
const response = await (fetchFn !== null && fetchFn !== void 0 ? fetchFn : fetch)(tokenUrl, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: params,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await parseErrorResponse(response);
|
||||
}
|
||||
return auth_js_2.OAuthTokensSchema.parse({ refresh_token: refreshToken, ...(await response.json()) });
|
||||
}
|
||||
/**
|
||||
* Performs OAuth 2.0 Dynamic Client Registration according to RFC 7591.
|
||||
*/
|
||||
async function registerClient(authorizationServerUrl, { metadata, clientMetadata, fetchFn }) {
|
||||
async function registerClient(authorizationServerUrl, { metadata, clientMetadata, fetchFn, }) {
|
||||
let registrationUrl;
|
||||
if (metadata) {
|
||||
if (!metadata.registration_endpoint) {
|
||||
throw new Error('Incompatible auth server: does not support dynamic client registration');
|
||||
throw new Error("Incompatible auth server: does not support dynamic client registration");
|
||||
}
|
||||
registrationUrl = new URL(metadata.registration_endpoint);
|
||||
}
|
||||
else {
|
||||
registrationUrl = new URL('/register', authorizationServerUrl);
|
||||
registrationUrl = new URL("/register", authorizationServerUrl);
|
||||
}
|
||||
const response = await (fetchFn ?? fetch)(registrationUrl, {
|
||||
method: 'POST',
|
||||
const response = await (fetchFn !== null && fetchFn !== void 0 ? fetchFn : fetch)(registrationUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(clientMetadata)
|
||||
body: JSON.stringify(clientMetadata),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw await parseErrorResponse(response);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1151
-515
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,eAAe,EACf,cAAc,EACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,iCAAiC,EACjC,eAAe,EAGf,gBAAgB,EAEhB,cAAc,EAGd,kBAAkB,EAElB,oBAAoB,EAEpB,4BAA4B,EAE5B,gBAAgB,EAEhB,YAAY,EACZ,YAAY,EACZ,mBAAmB,EAEnB,OAAO,EACP,MAAM,EACN,kBAAkB,EAClB,gBAAgB,EAEhB,kBAAkB,EAInB,MAAM,aAAa,CAAC;AAIrB,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG;IAC5C;;OAEG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,MAAM,CACjB,QAAQ,SAAS,OAAO,GAAG,OAAO,EAClC,aAAa,SAAS,YAAY,GAAG,YAAY,EACjD,OAAO,SAAS,MAAM,GAAG,MAAM,CAC/B,SAAQ,QAAQ,CAChB,aAAa,GAAG,QAAQ,EACxB,kBAAkB,GAAG,aAAa,EAClC,YAAY,GAAG,OAAO,CACvB;IAYG,OAAO,CAAC,WAAW;IAXrB,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,2BAA2B,CAA4C;IAC/E,OAAO,CAAC,IAAI,CAA2B;IAEvC;;OAEG;gBAEO,WAAW,EAAE,cAAc,EACnC,OAAO,CAAC,EAAE,aAAa;IAOzB;;;;OAIG;IACI,oBAAoB,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI;IAUnE,SAAS,CAAC,gBAAgB,CACxB,UAAU,EAAE,MAAM,kBAAkB,EACpC,MAAM,EAAE,MAAM,GACb,IAAI;IAQQ,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDrF;;OAEG;IACH,qBAAqB,IAAI,kBAAkB,GAAG,SAAS;IAIvD;;OAEG;IACH,gBAAgB,IAAI,cAAc,GAAG,SAAS;IAI9C;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,SAAS;IAIrC,SAAS,CAAC,yBAAyB,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,GAAG,IAAI;IAoErE,SAAS,CAAC,4BAA4B,CACpC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAC9B,IAAI;IAwBP,SAAS,CAAC,8BAA8B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAgCxD,IAAI,CAAC,OAAO,CAAC,EAAE,cAAc;;;IAI7B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;IAQpE,eAAe,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,cAAc;;;IAQ7D,SAAS,CACb,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAClC,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,WAAW,CACf,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACrC,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,aAAa,CACjB,MAAM,CAAC,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EACvC,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,qBAAqB,CACzB,MAAM,CAAC,EAAE,4BAA4B,CAAC,QAAQ,CAAC,EAC/C,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,YAAY,CAChB,MAAM,EAAE,mBAAmB,CAAC,QAAQ,CAAC,EACrC,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASpB,iBAAiB,CACrB,MAAM,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EAClC,OAAO,CAAC,EAAE,cAAc;;;IASpB,mBAAmB,CACvB,MAAM,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACpC,OAAO,CAAC,EAAE,cAAc;;;IASpB,QAAQ,CACZ,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,EACjC,YAAY,GACR,OAAO,oBAAoB,GAC3B,OAAO,iCAAwD,EACnE,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8C1B,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,sBAAsB;IAIxB,SAAS,CACb,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,EACnC,OAAO,CAAC,EAAE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAcpB,oBAAoB;CAG3B"}
|
||||
+72
-406
@@ -1,77 +1,12 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Client = void 0;
|
||||
exports.getSupportedElicitationModes = getSupportedElicitationModes;
|
||||
const protocol_js_1 = require("../shared/protocol.js");
|
||||
const types_js_1 = require("../types.js");
|
||||
const ajv_provider_js_1 = require("../validation/ajv-provider.js");
|
||||
const zod_compat_js_1 = require("../server/zod-compat.js");
|
||||
const client_js_1 = require("../experimental/tasks/client.js");
|
||||
const helpers_js_1 = require("../experimental/tasks/helpers.js");
|
||||
/**
|
||||
* Elicitation default application helper. Applies defaults to the data based on the schema.
|
||||
*
|
||||
* @param schema - The schema to apply defaults to.
|
||||
* @param data - The data to apply defaults to.
|
||||
*/
|
||||
function applyElicitationDefaults(schema, data) {
|
||||
if (!schema || data === null || typeof data !== 'object')
|
||||
return;
|
||||
// Handle object properties
|
||||
if (schema.type === 'object' && schema.properties && typeof schema.properties === 'object') {
|
||||
const obj = data;
|
||||
const props = schema.properties;
|
||||
for (const key of Object.keys(props)) {
|
||||
const propSchema = props[key];
|
||||
// If missing or explicitly undefined, apply default if present
|
||||
if (obj[key] === undefined && Object.prototype.hasOwnProperty.call(propSchema, 'default')) {
|
||||
obj[key] = propSchema.default;
|
||||
}
|
||||
// Recurse into existing nested objects/arrays
|
||||
if (obj[key] !== undefined) {
|
||||
applyElicitationDefaults(propSchema, obj[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(schema.anyOf)) {
|
||||
for (const sub of schema.anyOf) {
|
||||
// Skip boolean schemas (true/false are valid JSON Schemas but have no defaults)
|
||||
if (typeof sub !== 'boolean') {
|
||||
applyElicitationDefaults(sub, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Combine schemas
|
||||
if (Array.isArray(schema.oneOf)) {
|
||||
for (const sub of schema.oneOf) {
|
||||
// Skip boolean schemas (true/false are valid JSON Schemas but have no defaults)
|
||||
if (typeof sub !== 'boolean') {
|
||||
applyElicitationDefaults(sub, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines which elicitation modes are supported based on declared client capabilities.
|
||||
*
|
||||
* According to the spec:
|
||||
* - An empty elicitation capability object defaults to form mode support (backwards compatibility)
|
||||
* - URL mode is only supported if explicitly declared
|
||||
*
|
||||
* @param capabilities - The client's elicitation capabilities
|
||||
* @returns An object indicating which modes are supported
|
||||
*/
|
||||
function getSupportedElicitationModes(capabilities) {
|
||||
if (!capabilities) {
|
||||
return { supportsFormMode: false, supportsUrlMode: false };
|
||||
}
|
||||
const hasFormCapability = capabilities.form !== undefined;
|
||||
const hasUrlCapability = capabilities.url !== undefined;
|
||||
// If neither form nor url are explicitly declared, form mode is supported (backwards compatibility)
|
||||
const supportsFormMode = hasFormCapability || (!hasFormCapability && !hasUrlCapability);
|
||||
const supportsUrlMode = hasUrlCapability;
|
||||
return { supportsFormMode, supportsUrlMode };
|
||||
}
|
||||
const ajv_1 = __importDefault(require("ajv"));
|
||||
/**
|
||||
* An MCP client on top of a pluggable transport.
|
||||
*
|
||||
@@ -102,59 +37,12 @@ class Client extends protocol_js_1.Protocol {
|
||||
* Initializes this client with the given name and version information.
|
||||
*/
|
||||
constructor(_clientInfo, options) {
|
||||
var _a;
|
||||
super(options);
|
||||
this._clientInfo = _clientInfo;
|
||||
this._cachedToolOutputValidators = new Map();
|
||||
this._cachedKnownTaskTools = new Set();
|
||||
this._cachedRequiredTaskTools = new Set();
|
||||
this._listChangedDebounceTimers = new Map();
|
||||
this._capabilities = options?.capabilities ?? {};
|
||||
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new ajv_provider_js_1.AjvJsonSchemaValidator();
|
||||
// Store list changed config for setup after connection (when we know server capabilities)
|
||||
if (options?.listChanged) {
|
||||
this._pendingListChangedConfig = options.listChanged;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set up handlers for list changed notifications based on config and server capabilities.
|
||||
* This should only be called after initialization when server capabilities are known.
|
||||
* Handlers are silently skipped if the server doesn't advertise the corresponding listChanged capability.
|
||||
* @internal
|
||||
*/
|
||||
_setupListChangedHandlers(config) {
|
||||
if (config.tools && this._serverCapabilities?.tools?.listChanged) {
|
||||
this._setupListChangedHandler('tools', types_js_1.ToolListChangedNotificationSchema, config.tools, async () => {
|
||||
const result = await this.listTools();
|
||||
return result.tools;
|
||||
});
|
||||
}
|
||||
if (config.prompts && this._serverCapabilities?.prompts?.listChanged) {
|
||||
this._setupListChangedHandler('prompts', types_js_1.PromptListChangedNotificationSchema, config.prompts, async () => {
|
||||
const result = await this.listPrompts();
|
||||
return result.prompts;
|
||||
});
|
||||
}
|
||||
if (config.resources && this._serverCapabilities?.resources?.listChanged) {
|
||||
this._setupListChangedHandler('resources', types_js_1.ResourceListChangedNotificationSchema, config.resources, async () => {
|
||||
const result = await this.listResources();
|
||||
return result.resources;
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Access experimental features.
|
||||
*
|
||||
* WARNING: These APIs are experimental and may change without notice.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
get experimental() {
|
||||
if (!this._experimental) {
|
||||
this._experimental = {
|
||||
tasks: new client_js_1.ExperimentalClientTasks(this)
|
||||
};
|
||||
}
|
||||
return this._experimental;
|
||||
this._capabilities = (_a = options === null || options === void 0 ? void 0 : options.capabilities) !== null && _a !== void 0 ? _a : {};
|
||||
this._ajv = new ajv_1.default();
|
||||
}
|
||||
/**
|
||||
* Registers new capabilities. This can only be called before connecting to a transport.
|
||||
@@ -163,126 +51,13 @@ class Client extends protocol_js_1.Protocol {
|
||||
*/
|
||||
registerCapabilities(capabilities) {
|
||||
if (this.transport) {
|
||||
throw new Error('Cannot register capabilities after connecting to transport');
|
||||
throw new Error("Cannot register capabilities after connecting to transport");
|
||||
}
|
||||
this._capabilities = (0, protocol_js_1.mergeCapabilities)(this._capabilities, capabilities);
|
||||
}
|
||||
/**
|
||||
* Override request handler registration to enforce client-side validation for elicitation.
|
||||
*/
|
||||
setRequestHandler(requestSchema, handler) {
|
||||
const shape = (0, zod_compat_js_1.getObjectShape)(requestSchema);
|
||||
const methodSchema = shape?.method;
|
||||
if (!methodSchema) {
|
||||
throw new Error('Schema is missing a method literal');
|
||||
}
|
||||
// Extract literal value using type-safe property access
|
||||
let methodValue;
|
||||
if ((0, zod_compat_js_1.isZ4Schema)(methodSchema)) {
|
||||
const v4Schema = methodSchema;
|
||||
const v4Def = v4Schema._zod?.def;
|
||||
methodValue = v4Def?.value ?? v4Schema.value;
|
||||
}
|
||||
else {
|
||||
const v3Schema = methodSchema;
|
||||
const legacyDef = v3Schema._def;
|
||||
methodValue = legacyDef?.value ?? v3Schema.value;
|
||||
}
|
||||
if (typeof methodValue !== 'string') {
|
||||
throw new Error('Schema method literal must be a string');
|
||||
}
|
||||
const method = methodValue;
|
||||
if (method === 'elicitation/create') {
|
||||
const wrappedHandler = async (request, extra) => {
|
||||
const validatedRequest = (0, zod_compat_js_1.safeParse)(types_js_1.ElicitRequestSchema, request);
|
||||
if (!validatedRequest.success) {
|
||||
// Type guard: if success is false, error is guaranteed to exist
|
||||
const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid elicitation request: ${errorMessage}`);
|
||||
}
|
||||
const { params } = validatedRequest.data;
|
||||
params.mode = params.mode ?? 'form';
|
||||
const { supportsFormMode, supportsUrlMode } = getSupportedElicitationModes(this._capabilities.elicitation);
|
||||
if (params.mode === 'form' && !supportsFormMode) {
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, 'Client does not support form-mode elicitation requests');
|
||||
}
|
||||
if (params.mode === 'url' && !supportsUrlMode) {
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, 'Client does not support URL-mode elicitation requests');
|
||||
}
|
||||
const result = await Promise.resolve(handler(request, extra));
|
||||
// When task creation is requested, validate and return CreateTaskResult
|
||||
if (params.task) {
|
||||
const taskValidationResult = (0, zod_compat_js_1.safeParse)(types_js_1.CreateTaskResultSchema, result);
|
||||
if (!taskValidationResult.success) {
|
||||
const errorMessage = taskValidationResult.error instanceof Error
|
||||
? taskValidationResult.error.message
|
||||
: String(taskValidationResult.error);
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
|
||||
}
|
||||
return taskValidationResult.data;
|
||||
}
|
||||
// For non-task requests, validate against ElicitResultSchema
|
||||
const validationResult = (0, zod_compat_js_1.safeParse)(types_js_1.ElicitResultSchema, result);
|
||||
if (!validationResult.success) {
|
||||
// Type guard: if success is false, error is guaranteed to exist
|
||||
const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid elicitation result: ${errorMessage}`);
|
||||
}
|
||||
const validatedResult = validationResult.data;
|
||||
const requestedSchema = params.mode === 'form' ? params.requestedSchema : undefined;
|
||||
if (params.mode === 'form' && validatedResult.action === 'accept' && validatedResult.content && requestedSchema) {
|
||||
if (this._capabilities.elicitation?.form?.applyDefaults) {
|
||||
try {
|
||||
applyElicitationDefaults(requestedSchema, validatedResult.content);
|
||||
}
|
||||
catch {
|
||||
// gracefully ignore errors in default application
|
||||
}
|
||||
}
|
||||
}
|
||||
return validatedResult;
|
||||
};
|
||||
// Install the wrapped handler
|
||||
return super.setRequestHandler(requestSchema, wrappedHandler);
|
||||
}
|
||||
if (method === 'sampling/createMessage') {
|
||||
const wrappedHandler = async (request, extra) => {
|
||||
const validatedRequest = (0, zod_compat_js_1.safeParse)(types_js_1.CreateMessageRequestSchema, request);
|
||||
if (!validatedRequest.success) {
|
||||
const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid sampling request: ${errorMessage}`);
|
||||
}
|
||||
const { params } = validatedRequest.data;
|
||||
const result = await Promise.resolve(handler(request, extra));
|
||||
// When task creation is requested, validate and return CreateTaskResult
|
||||
if (params.task) {
|
||||
const taskValidationResult = (0, zod_compat_js_1.safeParse)(types_js_1.CreateTaskResultSchema, result);
|
||||
if (!taskValidationResult.success) {
|
||||
const errorMessage = taskValidationResult.error instanceof Error
|
||||
? taskValidationResult.error.message
|
||||
: String(taskValidationResult.error);
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
|
||||
}
|
||||
return taskValidationResult.data;
|
||||
}
|
||||
// For non-task requests, validate against appropriate schema based on tools presence
|
||||
const hasTools = params.tools || params.toolChoice;
|
||||
const resultSchema = hasTools ? types_js_1.CreateMessageResultWithToolsSchema : types_js_1.CreateMessageResultSchema;
|
||||
const validationResult = (0, zod_compat_js_1.safeParse)(resultSchema, result);
|
||||
if (!validationResult.success) {
|
||||
const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Invalid sampling result: ${errorMessage}`);
|
||||
}
|
||||
return validationResult.data;
|
||||
};
|
||||
// Install the wrapped handler
|
||||
return super.setRequestHandler(requestSchema, wrappedHandler);
|
||||
}
|
||||
// Other handlers use default behavior
|
||||
return super.setRequestHandler(requestSchema, handler);
|
||||
}
|
||||
assertCapability(capability, method) {
|
||||
if (!this._serverCapabilities?.[capability]) {
|
||||
var _a;
|
||||
if (!((_a = this._serverCapabilities) === null || _a === void 0 ? void 0 : _a[capability])) {
|
||||
throw new Error(`Server does not support ${capability} (required for ${method})`);
|
||||
}
|
||||
}
|
||||
@@ -295,12 +70,12 @@ class Client extends protocol_js_1.Protocol {
|
||||
}
|
||||
try {
|
||||
const result = await this.request({
|
||||
method: 'initialize',
|
||||
method: "initialize",
|
||||
params: {
|
||||
protocolVersion: types_js_1.LATEST_PROTOCOL_VERSION,
|
||||
capabilities: this._capabilities,
|
||||
clientInfo: this._clientInfo
|
||||
}
|
||||
clientInfo: this._clientInfo,
|
||||
},
|
||||
}, types_js_1.InitializeResultSchema, options);
|
||||
if (result === undefined) {
|
||||
throw new Error(`Server sent invalid initialize result: ${result}`);
|
||||
@@ -316,13 +91,8 @@ class Client extends protocol_js_1.Protocol {
|
||||
}
|
||||
this._instructions = result.instructions;
|
||||
await this.notification({
|
||||
method: 'notifications/initialized'
|
||||
method: "notifications/initialized",
|
||||
});
|
||||
// Set up list changed handlers now that we know server capabilities
|
||||
if (this._pendingListChangedConfig) {
|
||||
this._setupListChangedHandlers(this._pendingListChangedConfig);
|
||||
this._pendingListChangedConfig = undefined;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// Disconnect if initialization fails.
|
||||
@@ -349,154 +119,124 @@ class Client extends protocol_js_1.Protocol {
|
||||
return this._instructions;
|
||||
}
|
||||
assertCapabilityForMethod(method) {
|
||||
var _a, _b, _c, _d, _e;
|
||||
switch (method) {
|
||||
case 'logging/setLevel':
|
||||
if (!this._serverCapabilities?.logging) {
|
||||
case "logging/setLevel":
|
||||
if (!((_a = this._serverCapabilities) === null || _a === void 0 ? void 0 : _a.logging)) {
|
||||
throw new Error(`Server does not support logging (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'prompts/get':
|
||||
case 'prompts/list':
|
||||
if (!this._serverCapabilities?.prompts) {
|
||||
case "prompts/get":
|
||||
case "prompts/list":
|
||||
if (!((_b = this._serverCapabilities) === null || _b === void 0 ? void 0 : _b.prompts)) {
|
||||
throw new Error(`Server does not support prompts (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'resources/list':
|
||||
case 'resources/templates/list':
|
||||
case 'resources/read':
|
||||
case 'resources/subscribe':
|
||||
case 'resources/unsubscribe':
|
||||
if (!this._serverCapabilities?.resources) {
|
||||
case "resources/list":
|
||||
case "resources/templates/list":
|
||||
case "resources/read":
|
||||
case "resources/subscribe":
|
||||
case "resources/unsubscribe":
|
||||
if (!((_c = this._serverCapabilities) === null || _c === void 0 ? void 0 : _c.resources)) {
|
||||
throw new Error(`Server does not support resources (required for ${method})`);
|
||||
}
|
||||
if (method === 'resources/subscribe' && !this._serverCapabilities.resources.subscribe) {
|
||||
if (method === "resources/subscribe" &&
|
||||
!this._serverCapabilities.resources.subscribe) {
|
||||
throw new Error(`Server does not support resource subscriptions (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'tools/call':
|
||||
case 'tools/list':
|
||||
if (!this._serverCapabilities?.tools) {
|
||||
case "tools/call":
|
||||
case "tools/list":
|
||||
if (!((_d = this._serverCapabilities) === null || _d === void 0 ? void 0 : _d.tools)) {
|
||||
throw new Error(`Server does not support tools (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'completion/complete':
|
||||
if (!this._serverCapabilities?.completions) {
|
||||
case "completion/complete":
|
||||
if (!((_e = this._serverCapabilities) === null || _e === void 0 ? void 0 : _e.completions)) {
|
||||
throw new Error(`Server does not support completions (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'initialize':
|
||||
case "initialize":
|
||||
// No specific capability required for initialize
|
||||
break;
|
||||
case 'ping':
|
||||
case "ping":
|
||||
// No specific capability required for ping
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertNotificationCapability(method) {
|
||||
var _a;
|
||||
switch (method) {
|
||||
case 'notifications/roots/list_changed':
|
||||
if (!this._capabilities.roots?.listChanged) {
|
||||
case "notifications/roots/list_changed":
|
||||
if (!((_a = this._capabilities.roots) === null || _a === void 0 ? void 0 : _a.listChanged)) {
|
||||
throw new Error(`Client does not support roots list changed notifications (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'notifications/initialized':
|
||||
case "notifications/initialized":
|
||||
// No specific capability required for initialized
|
||||
break;
|
||||
case 'notifications/cancelled':
|
||||
case "notifications/cancelled":
|
||||
// Cancellation notifications are always allowed
|
||||
break;
|
||||
case 'notifications/progress':
|
||||
case "notifications/progress":
|
||||
// Progress notifications are always allowed
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertRequestHandlerCapability(method) {
|
||||
// Task handlers are registered in Protocol constructor before _capabilities is initialized
|
||||
// Skip capability check for task methods during initialization
|
||||
if (!this._capabilities) {
|
||||
return;
|
||||
}
|
||||
switch (method) {
|
||||
case 'sampling/createMessage':
|
||||
case "sampling/createMessage":
|
||||
if (!this._capabilities.sampling) {
|
||||
throw new Error(`Client does not support sampling capability (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'elicitation/create':
|
||||
case "elicitation/create":
|
||||
if (!this._capabilities.elicitation) {
|
||||
throw new Error(`Client does not support elicitation capability (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'roots/list':
|
||||
case "roots/list":
|
||||
if (!this._capabilities.roots) {
|
||||
throw new Error(`Client does not support roots capability (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'tasks/get':
|
||||
case 'tasks/list':
|
||||
case 'tasks/result':
|
||||
case 'tasks/cancel':
|
||||
if (!this._capabilities.tasks) {
|
||||
throw new Error(`Client does not support tasks capability (required for ${method})`);
|
||||
}
|
||||
break;
|
||||
case 'ping':
|
||||
case "ping":
|
||||
// No specific capability required for ping
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTaskCapability(method) {
|
||||
(0, helpers_js_1.assertToolsCallTaskCapability)(this._serverCapabilities?.tasks?.requests, method, 'Server');
|
||||
}
|
||||
assertTaskHandlerCapability(method) {
|
||||
// Task handlers are registered in Protocol constructor before _capabilities is initialized
|
||||
// Skip capability check for task methods during initialization
|
||||
if (!this._capabilities) {
|
||||
return;
|
||||
}
|
||||
(0, helpers_js_1.assertClientRequestTaskCapability)(this._capabilities.tasks?.requests, method, 'Client');
|
||||
}
|
||||
async ping(options) {
|
||||
return this.request({ method: 'ping' }, types_js_1.EmptyResultSchema, options);
|
||||
return this.request({ method: "ping" }, types_js_1.EmptyResultSchema, options);
|
||||
}
|
||||
async complete(params, options) {
|
||||
return this.request({ method: 'completion/complete', params }, types_js_1.CompleteResultSchema, options);
|
||||
return this.request({ method: "completion/complete", params }, types_js_1.CompleteResultSchema, options);
|
||||
}
|
||||
async setLoggingLevel(level, options) {
|
||||
return this.request({ method: 'logging/setLevel', params: { level } }, types_js_1.EmptyResultSchema, options);
|
||||
return this.request({ method: "logging/setLevel", params: { level } }, types_js_1.EmptyResultSchema, options);
|
||||
}
|
||||
async getPrompt(params, options) {
|
||||
return this.request({ method: 'prompts/get', params }, types_js_1.GetPromptResultSchema, options);
|
||||
return this.request({ method: "prompts/get", params }, types_js_1.GetPromptResultSchema, options);
|
||||
}
|
||||
async listPrompts(params, options) {
|
||||
return this.request({ method: 'prompts/list', params }, types_js_1.ListPromptsResultSchema, options);
|
||||
return this.request({ method: "prompts/list", params }, types_js_1.ListPromptsResultSchema, options);
|
||||
}
|
||||
async listResources(params, options) {
|
||||
return this.request({ method: 'resources/list', params }, types_js_1.ListResourcesResultSchema, options);
|
||||
return this.request({ method: "resources/list", params }, types_js_1.ListResourcesResultSchema, options);
|
||||
}
|
||||
async listResourceTemplates(params, options) {
|
||||
return this.request({ method: 'resources/templates/list', params }, types_js_1.ListResourceTemplatesResultSchema, options);
|
||||
return this.request({ method: "resources/templates/list", params }, types_js_1.ListResourceTemplatesResultSchema, options);
|
||||
}
|
||||
async readResource(params, options) {
|
||||
return this.request({ method: 'resources/read', params }, types_js_1.ReadResourceResultSchema, options);
|
||||
return this.request({ method: "resources/read", params }, types_js_1.ReadResourceResultSchema, options);
|
||||
}
|
||||
async subscribeResource(params, options) {
|
||||
return this.request({ method: 'resources/subscribe', params }, types_js_1.EmptyResultSchema, options);
|
||||
return this.request({ method: "resources/subscribe", params }, types_js_1.EmptyResultSchema, options);
|
||||
}
|
||||
async unsubscribeResource(params, options) {
|
||||
return this.request({ method: 'resources/unsubscribe', params }, types_js_1.EmptyResultSchema, options);
|
||||
return this.request({ method: "resources/unsubscribe", params }, types_js_1.EmptyResultSchema, options);
|
||||
}
|
||||
/**
|
||||
* Calls a tool and waits for the result. Automatically validates structured output if the tool has an outputSchema.
|
||||
*
|
||||
* For task-based execution with streaming behavior, use client.experimental.tasks.callToolStream() instead.
|
||||
*/
|
||||
async callTool(params, resultSchema = types_js_1.CallToolResultSchema, options) {
|
||||
// Guard: required-task tools need experimental API
|
||||
if (this.isToolTaskRequired(params.name)) {
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, `Tool "${params.name}" requires task-based execution. Use client.experimental.tasks.callToolStream() instead.`);
|
||||
}
|
||||
const result = await this.request({ method: 'tools/call', params }, resultSchema, options);
|
||||
const result = await this.request({ method: "tools/call", params }, resultSchema, options);
|
||||
// Check if the tool has an outputSchema
|
||||
const validator = this.getToolOutputValidator(params.name);
|
||||
if (validator) {
|
||||
@@ -507,10 +247,10 @@ class Client extends protocol_js_1.Protocol {
|
||||
// Only validate structured content if present (not when there's an error)
|
||||
if (result.structuredContent) {
|
||||
try {
|
||||
// Validate the structured content against the schema
|
||||
const validationResult = validator(result.structuredContent);
|
||||
if (!validationResult.valid) {
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Structured content does not match the tool's output schema: ${validationResult.errorMessage}`);
|
||||
// Validate the structured content (which is already an object) against the schema
|
||||
const isValid = validator(result.structuredContent);
|
||||
if (!isValid) {
|
||||
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Structured content does not match the tool's output schema: ${this._ajv.errorsText(validator.errors)}`);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
@@ -523,106 +263,32 @@ class Client extends protocol_js_1.Protocol {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
isToolTask(toolName) {
|
||||
if (!this._serverCapabilities?.tasks?.requests?.tools?.call) {
|
||||
return false;
|
||||
}
|
||||
return this._cachedKnownTaskTools.has(toolName);
|
||||
}
|
||||
/**
|
||||
* Check if a tool requires task-based execution.
|
||||
* Unlike isToolTask which includes 'optional' tools, this only checks for 'required'.
|
||||
*/
|
||||
isToolTaskRequired(toolName) {
|
||||
return this._cachedRequiredTaskTools.has(toolName);
|
||||
}
|
||||
/**
|
||||
* Cache validators for tool output schemas.
|
||||
* Called after listTools() to pre-compile validators for better performance.
|
||||
*/
|
||||
cacheToolMetadata(tools) {
|
||||
cacheToolOutputSchemas(tools) {
|
||||
this._cachedToolOutputValidators.clear();
|
||||
this._cachedKnownTaskTools.clear();
|
||||
this._cachedRequiredTaskTools.clear();
|
||||
for (const tool of tools) {
|
||||
// If the tool has an outputSchema, create and cache the validator
|
||||
// If the tool has an outputSchema, create and cache the Ajv validator
|
||||
if (tool.outputSchema) {
|
||||
const toolValidator = this._jsonSchemaValidator.getValidator(tool.outputSchema);
|
||||
this._cachedToolOutputValidators.set(tool.name, toolValidator);
|
||||
}
|
||||
// If the tool supports task-based execution, cache that information
|
||||
const taskSupport = tool.execution?.taskSupport;
|
||||
if (taskSupport === 'required' || taskSupport === 'optional') {
|
||||
this._cachedKnownTaskTools.add(tool.name);
|
||||
}
|
||||
if (taskSupport === 'required') {
|
||||
this._cachedRequiredTaskTools.add(tool.name);
|
||||
try {
|
||||
const validator = this._ajv.compile(tool.outputSchema);
|
||||
this._cachedToolOutputValidators.set(tool.name, validator);
|
||||
}
|
||||
catch (_a) {
|
||||
// Ignore schema compilation errors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get cached validator for a tool
|
||||
*/
|
||||
getToolOutputValidator(toolName) {
|
||||
return this._cachedToolOutputValidators.get(toolName);
|
||||
}
|
||||
async listTools(params, options) {
|
||||
const result = await this.request({ method: 'tools/list', params }, types_js_1.ListToolsResultSchema, options);
|
||||
const result = await this.request({ method: "tools/list", params }, types_js_1.ListToolsResultSchema, options);
|
||||
// Cache the tools and their output schemas for future validation
|
||||
this.cacheToolMetadata(result.tools);
|
||||
this.cacheToolOutputSchemas(result.tools);
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Set up a single list changed handler.
|
||||
* @internal
|
||||
*/
|
||||
_setupListChangedHandler(listType, notificationSchema, options, fetcher) {
|
||||
// Validate options using Zod schema (validates autoRefresh and debounceMs)
|
||||
const parseResult = types_js_1.ListChangedOptionsBaseSchema.safeParse(options);
|
||||
if (!parseResult.success) {
|
||||
throw new Error(`Invalid ${listType} listChanged options: ${parseResult.error.message}`);
|
||||
}
|
||||
// Validate callback
|
||||
if (typeof options.onChanged !== 'function') {
|
||||
throw new Error(`Invalid ${listType} listChanged options: onChanged must be a function`);
|
||||
}
|
||||
const { autoRefresh, debounceMs } = parseResult.data;
|
||||
const { onChanged } = options;
|
||||
const refresh = async () => {
|
||||
if (!autoRefresh) {
|
||||
onChanged(null, null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const items = await fetcher();
|
||||
onChanged(null, items);
|
||||
}
|
||||
catch (e) {
|
||||
const error = e instanceof Error ? e : new Error(String(e));
|
||||
onChanged(error, null);
|
||||
}
|
||||
};
|
||||
const handler = () => {
|
||||
if (debounceMs) {
|
||||
// Clear any pending debounce timer for this list type
|
||||
const existingTimer = this._listChangedDebounceTimers.get(listType);
|
||||
if (existingTimer) {
|
||||
clearTimeout(existingTimer);
|
||||
}
|
||||
// Set up debounced refresh
|
||||
const timer = setTimeout(refresh, debounceMs);
|
||||
this._listChangedDebounceTimers.set(listType, timer);
|
||||
}
|
||||
else {
|
||||
// No debounce, refresh immediately
|
||||
refresh();
|
||||
}
|
||||
};
|
||||
// Register notification handler
|
||||
this.setNotificationHandler(notificationSchema, handler);
|
||||
}
|
||||
async sendRootsListChanged() {
|
||||
return this.notification({ method: 'notifications/roots/list_changed' });
|
||||
return this.notification({ method: "notifications/roots/list_changed" });
|
||||
}
|
||||
}
|
||||
exports.Client = Client;
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+4
-7
@@ -1,7 +1,7 @@
|
||||
import { type ErrorEvent, type EventSourceInit } from 'eventsource';
|
||||
import { Transport, FetchLike } from '../shared/transport.js';
|
||||
import { JSONRPCMessage } from '../types.js';
|
||||
import { OAuthClientProvider } from './auth.js';
|
||||
import { type ErrorEvent, type EventSourceInit } from "eventsource";
|
||||
import { Transport, FetchLike } from "../shared/transport.js";
|
||||
import { JSONRPCMessage } from "../types.js";
|
||||
import { OAuthClientProvider } from "./auth.js";
|
||||
export declare class SseError extends Error {
|
||||
readonly code: number | undefined;
|
||||
readonly event: ErrorEvent;
|
||||
@@ -47,7 +47,6 @@ export type SSEClientTransportOptions = {
|
||||
/**
|
||||
* Client transport for SSE: this will connect to a server using Server-Sent Events for receiving
|
||||
* messages and make separate POST requests for sending messages.
|
||||
* @deprecated SSEClientTransport is deprecated. Prefer to use StreamableHTTPClientTransport where possible instead. Note that because some servers are still using SSE, clients may need to support both transports during the migration period.
|
||||
*/
|
||||
export declare class SSEClientTransport implements Transport {
|
||||
private _eventSource?;
|
||||
@@ -55,12 +54,10 @@ export declare class SSEClientTransport implements Transport {
|
||||
private _abortController?;
|
||||
private _url;
|
||||
private _resourceMetadataUrl?;
|
||||
private _scope?;
|
||||
private _eventSourceInit?;
|
||||
private _requestInit?;
|
||||
private _authProvider?;
|
||||
private _fetch?;
|
||||
private _fetchWithInit;
|
||||
private _protocolVersion?;
|
||||
onclose?: () => void;
|
||||
onerror?: (error: Error) => void;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/client/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAyC,MAAM,wBAAwB,CAAC;AACrG,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AACnE,OAAO,EAAkD,mBAAmB,EAAqB,MAAM,WAAW,CAAC;AAEnH,qBAAa,QAAS,SAAQ,KAAK;aAEX,IAAI,EAAE,MAAM,GAAG,SAAS;aAExB,KAAK,EAAE,UAAU;gBAFjB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxC,OAAO,EAAE,MAAM,GAAG,SAAS,EACX,KAAK,EAAE,UAAU;CAIxC;AAED;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACpC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAChD,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,SAAS,CAAC,CAAM;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,oBAAoB,CAAC,CAAM;IACnC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAY;IAC3B,OAAO,CAAC,cAAc,CAAY;IAClC,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAElC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAElC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,yBAAyB;YAWxC,cAAc;YAyBd,cAAc;IAoB5B,OAAO,CAAC,YAAY;IAyEd,KAAK;IAQX;;OAEG;IACG,UAAU,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDlD,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAG5C"}
|
||||
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/client/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AACnE,OAAO,EAAgD,mBAAmB,EAAqB,MAAM,WAAW,CAAC;AAEjH,qBAAa,QAAS,SAAQ,KAAK;aAEf,IAAI,EAAE,MAAM,GAAG,SAAS;aAExB,KAAK,EAAE,UAAU;gBAFjB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxC,OAAO,EAAE,MAAM,GAAG,SAAS,EACX,KAAK,EAAE,UAAU;CAIpC;AAED;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAClD,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,SAAS,CAAC,CAAM;IACxB,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,oBAAoB,CAAC,CAAM;IACnC,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAY;IAC3B,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAElC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAG5C,GAAG,EAAE,GAAG,EACR,IAAI,CAAC,EAAE,yBAAyB;YAUpB,cAAc;YAoBd,cAAc;IAiB5B,OAAO,CAAC,YAAY;IA6Ed,KAAK;IAUX;;OAEG;IACG,UAAU,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ClD,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAG1C"}
|
||||
+48
-70
@@ -2,7 +2,6 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SSEClientTransport = exports.SseError = void 0;
|
||||
const eventsource_1 = require("eventsource");
|
||||
const transport_js_1 = require("../shared/transport.js");
|
||||
const types_js_1 = require("../types.js");
|
||||
const auth_js_1 = require("./auth.js");
|
||||
class SseError extends Error {
|
||||
@@ -16,92 +15,83 @@ exports.SseError = SseError;
|
||||
/**
|
||||
* Client transport for SSE: this will connect to a server using Server-Sent Events for receiving
|
||||
* messages and make separate POST requests for sending messages.
|
||||
* @deprecated SSEClientTransport is deprecated. Prefer to use StreamableHTTPClientTransport where possible instead. Note that because some servers are still using SSE, clients may need to support both transports during the migration period.
|
||||
*/
|
||||
class SSEClientTransport {
|
||||
constructor(url, opts) {
|
||||
this._url = url;
|
||||
this._resourceMetadataUrl = undefined;
|
||||
this._scope = undefined;
|
||||
this._eventSourceInit = opts?.eventSourceInit;
|
||||
this._requestInit = opts?.requestInit;
|
||||
this._authProvider = opts?.authProvider;
|
||||
this._fetch = opts?.fetch;
|
||||
this._fetchWithInit = (0, transport_js_1.createFetchWithInit)(opts?.fetch, opts?.requestInit);
|
||||
this._eventSourceInit = opts === null || opts === void 0 ? void 0 : opts.eventSourceInit;
|
||||
this._requestInit = opts === null || opts === void 0 ? void 0 : opts.requestInit;
|
||||
this._authProvider = opts === null || opts === void 0 ? void 0 : opts.authProvider;
|
||||
this._fetch = opts === null || opts === void 0 ? void 0 : opts.fetch;
|
||||
}
|
||||
async _authThenStart() {
|
||||
var _a;
|
||||
if (!this._authProvider) {
|
||||
throw new auth_js_1.UnauthorizedError('No auth provider');
|
||||
throw new auth_js_1.UnauthorizedError("No auth provider");
|
||||
}
|
||||
let result;
|
||||
try {
|
||||
result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetchWithInit
|
||||
});
|
||||
result = await (0, auth_js_1.auth)(this._authProvider, { serverUrl: this._url, resourceMetadataUrl: this._resourceMetadataUrl, fetchFn: this._fetch });
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
throw error;
|
||||
}
|
||||
if (result !== 'AUTHORIZED') {
|
||||
if (result !== "AUTHORIZED") {
|
||||
throw new auth_js_1.UnauthorizedError();
|
||||
}
|
||||
return await this._startOrAuth();
|
||||
}
|
||||
async _commonHeaders() {
|
||||
var _a;
|
||||
const headers = {};
|
||||
if (this._authProvider) {
|
||||
const tokens = await this._authProvider.tokens();
|
||||
if (tokens) {
|
||||
headers['Authorization'] = `Bearer ${tokens.access_token}`;
|
||||
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
||||
}
|
||||
}
|
||||
if (this._protocolVersion) {
|
||||
headers['mcp-protocol-version'] = this._protocolVersion;
|
||||
headers["mcp-protocol-version"] = this._protocolVersion;
|
||||
}
|
||||
const extraHeaders = (0, transport_js_1.normalizeHeaders)(this._requestInit?.headers);
|
||||
return new Headers({
|
||||
...headers,
|
||||
...extraHeaders
|
||||
});
|
||||
return new Headers({ ...headers, ...(_a = this._requestInit) === null || _a === void 0 ? void 0 : _a.headers });
|
||||
}
|
||||
_startOrAuth() {
|
||||
const fetchImpl = (this?._eventSourceInit?.fetch ?? this._fetch ?? fetch);
|
||||
var _a, _b, _c;
|
||||
const fetchImpl = ((_c = (_b = (_a = this === null || this === void 0 ? void 0 : this._eventSourceInit) === null || _a === void 0 ? void 0 : _a.fetch) !== null && _b !== void 0 ? _b : this._fetch) !== null && _c !== void 0 ? _c : fetch);
|
||||
return new Promise((resolve, reject) => {
|
||||
this._eventSource = new eventsource_1.EventSource(this._url.href, {
|
||||
...this._eventSourceInit,
|
||||
fetch: async (url, init) => {
|
||||
const headers = await this._commonHeaders();
|
||||
headers.set('Accept', 'text/event-stream');
|
||||
headers.set("Accept", "text/event-stream");
|
||||
const response = await fetchImpl(url, {
|
||||
...init,
|
||||
headers
|
||||
headers,
|
||||
});
|
||||
if (response.status === 401 && response.headers.has('www-authenticate')) {
|
||||
const { resourceMetadataUrl, scope } = (0, auth_js_1.extractWWWAuthenticateParams)(response);
|
||||
this._resourceMetadataUrl = resourceMetadataUrl;
|
||||
this._scope = scope;
|
||||
this._resourceMetadataUrl = (0, auth_js_1.extractResourceMetadataUrl)(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
},
|
||||
});
|
||||
this._abortController = new AbortController();
|
||||
this._eventSource.onerror = event => {
|
||||
this._eventSource.onerror = (event) => {
|
||||
var _a;
|
||||
if (event.code === 401 && this._authProvider) {
|
||||
this._authThenStart().then(resolve, reject);
|
||||
return;
|
||||
}
|
||||
const error = new SseError(event.code, event.message, event);
|
||||
reject(error);
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
};
|
||||
this._eventSource.onopen = () => {
|
||||
// The connection is open, but we need to wait for the endpoint to be received.
|
||||
};
|
||||
this._eventSource.addEventListener('endpoint', (event) => {
|
||||
this._eventSource.addEventListener("endpoint", (event) => {
|
||||
var _a;
|
||||
const messageEvent = event;
|
||||
try {
|
||||
this._endpoint = new URL(messageEvent.data, this._url);
|
||||
@@ -111,29 +101,30 @@ class SSEClientTransport {
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
void this.close();
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
this._eventSource.onmessage = (event) => {
|
||||
var _a, _b;
|
||||
const messageEvent = event;
|
||||
let message;
|
||||
try {
|
||||
message = types_js_1.JSONRPCMessageSchema.parse(JSON.parse(messageEvent.data));
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
return;
|
||||
}
|
||||
this.onmessage?.(message);
|
||||
(_b = this.onmessage) === null || _b === void 0 ? void 0 : _b.call(this, message);
|
||||
};
|
||||
});
|
||||
}
|
||||
async start() {
|
||||
if (this._eventSource) {
|
||||
throw new Error('SSEClientTransport already started! If using Client class, note that connect() calls start() automatically.');
|
||||
throw new Error("SSEClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
||||
}
|
||||
return await this._startOrAuth();
|
||||
}
|
||||
@@ -142,64 +133,51 @@ class SSEClientTransport {
|
||||
*/
|
||||
async finishAuth(authorizationCode) {
|
||||
if (!this._authProvider) {
|
||||
throw new auth_js_1.UnauthorizedError('No auth provider');
|
||||
throw new auth_js_1.UnauthorizedError("No auth provider");
|
||||
}
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
authorizationCode,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetchWithInit
|
||||
});
|
||||
if (result !== 'AUTHORIZED') {
|
||||
throw new auth_js_1.UnauthorizedError('Failed to authorize');
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, { serverUrl: this._url, authorizationCode, resourceMetadataUrl: this._resourceMetadataUrl, fetchFn: this._fetch });
|
||||
if (result !== "AUTHORIZED") {
|
||||
throw new auth_js_1.UnauthorizedError("Failed to authorize");
|
||||
}
|
||||
}
|
||||
async close() {
|
||||
this._abortController?.abort();
|
||||
this._eventSource?.close();
|
||||
this.onclose?.();
|
||||
var _a, _b, _c;
|
||||
(_a = this._abortController) === null || _a === void 0 ? void 0 : _a.abort();
|
||||
(_b = this._eventSource) === null || _b === void 0 ? void 0 : _b.close();
|
||||
(_c = this.onclose) === null || _c === void 0 ? void 0 : _c.call(this);
|
||||
}
|
||||
async send(message) {
|
||||
var _a, _b, _c;
|
||||
if (!this._endpoint) {
|
||||
throw new Error('Not connected');
|
||||
throw new Error("Not connected");
|
||||
}
|
||||
try {
|
||||
const headers = await this._commonHeaders();
|
||||
headers.set('content-type', 'application/json');
|
||||
headers.set("content-type", "application/json");
|
||||
const init = {
|
||||
...this._requestInit,
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(message),
|
||||
signal: this._abortController?.signal
|
||||
signal: (_a = this._abortController) === null || _a === void 0 ? void 0 : _a.signal,
|
||||
};
|
||||
const response = await (this._fetch ?? fetch)(this._endpoint, init);
|
||||
const response = await ((_b = this._fetch) !== null && _b !== void 0 ? _b : fetch)(this._endpoint, init);
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => null);
|
||||
if (response.status === 401 && this._authProvider) {
|
||||
const { resourceMetadataUrl, scope } = (0, auth_js_1.extractWWWAuthenticateParams)(response);
|
||||
this._resourceMetadataUrl = resourceMetadataUrl;
|
||||
this._scope = scope;
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetchWithInit
|
||||
});
|
||||
if (result !== 'AUTHORIZED') {
|
||||
this._resourceMetadataUrl = (0, auth_js_1.extractResourceMetadataUrl)(response);
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, { serverUrl: this._url, resourceMetadataUrl: this._resourceMetadataUrl, fetchFn: this._fetch });
|
||||
if (result !== "AUTHORIZED") {
|
||||
throw new auth_js_1.UnauthorizedError();
|
||||
}
|
||||
// Purposely _not_ awaited, so we don't call onerror twice
|
||||
return this.send(message);
|
||||
}
|
||||
const text = await response.text().catch(() => null);
|
||||
throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${text}`);
|
||||
}
|
||||
// Release connection - POST responses don't have content we need
|
||||
await response.body?.cancel();
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_c = this.onerror) === null || _c === void 0 ? void 0 : _c.call(this, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+5
-4
@@ -1,7 +1,7 @@
|
||||
import { IOType } from 'node:child_process';
|
||||
import { Stream } from 'node:stream';
|
||||
import { Transport } from '../shared/transport.js';
|
||||
import { JSONRPCMessage } from '../types.js';
|
||||
import { IOType } from "node:child_process";
|
||||
import { Stream } from "node:stream";
|
||||
import { Transport } from "../shared/transport.js";
|
||||
import { JSONRPCMessage } from "../types.js";
|
||||
export type StdioServerParameters = {
|
||||
/**
|
||||
* The executable to run to start the server.
|
||||
@@ -45,6 +45,7 @@ export declare function getDefaultEnvironment(): Record<string, string>;
|
||||
*/
|
||||
export declare class StdioClientTransport implements Transport {
|
||||
private _process?;
|
||||
private _abortController;
|
||||
private _readBuffer;
|
||||
private _serverParams;
|
||||
private _stderrStream;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../src/client/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,MAAM,EAAe,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,qBAAqB,GAAG;IAChC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAElC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,UAiBuB,CAAC;AAE/D;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB9D;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,SAAS;IAClD,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,aAAa,CAA4B;IAEjD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAElC,MAAM,EAAE,qBAAqB;IAOzC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqD5B;;;;;;OAMG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAM1B;IAED;;;;OAIG;IACH,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,OAAO,CAAC,iBAAiB;IAenB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyC5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAc/C"}
|
||||
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../src/client/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,MAAM,EAAe,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,qBAAqB,GAAG;IAClC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAElC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,UAiBmB,CAAC;AAE3D;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkB9D;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,SAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,WAAW,CAAgC;IACnD,OAAO,CAAC,aAAa,CAAwB;IAC7C,OAAO,CAAC,aAAa,CAA4B;IAEjD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAElC,MAAM,EAAE,qBAAqB;IAOzC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgE5B;;;;;;OAMG;IACH,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAM1B;IAED;;;;OAIG;IACH,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,OAAO,CAAC,iBAAiB;IAenB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAc7C"}
|
||||
+59
-74
@@ -12,23 +12,23 @@ const stdio_js_1 = require("../shared/stdio.js");
|
||||
/**
|
||||
* Environment variables to inherit by default, if an environment is not explicitly given.
|
||||
*/
|
||||
exports.DEFAULT_INHERITED_ENV_VARS = node_process_1.default.platform === 'win32'
|
||||
exports.DEFAULT_INHERITED_ENV_VARS = node_process_1.default.platform === "win32"
|
||||
? [
|
||||
'APPDATA',
|
||||
'HOMEDRIVE',
|
||||
'HOMEPATH',
|
||||
'LOCALAPPDATA',
|
||||
'PATH',
|
||||
'PROCESSOR_ARCHITECTURE',
|
||||
'SYSTEMDRIVE',
|
||||
'SYSTEMROOT',
|
||||
'TEMP',
|
||||
'USERNAME',
|
||||
'USERPROFILE',
|
||||
'PROGRAMFILES'
|
||||
"APPDATA",
|
||||
"HOMEDRIVE",
|
||||
"HOMEPATH",
|
||||
"LOCALAPPDATA",
|
||||
"PATH",
|
||||
"PROCESSOR_ARCHITECTURE",
|
||||
"SYSTEMDRIVE",
|
||||
"SYSTEMROOT",
|
||||
"TEMP",
|
||||
"USERNAME",
|
||||
"USERPROFILE",
|
||||
"PROGRAMFILES",
|
||||
]
|
||||
: /* list inspired by the default env inheritance of sudo */
|
||||
['HOME', 'LOGNAME', 'PATH', 'SHELL', 'TERM', 'USER'];
|
||||
["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"];
|
||||
/**
|
||||
* Returns a default environment object including only environment variables deemed safe to inherit.
|
||||
*/
|
||||
@@ -39,7 +39,7 @@ function getDefaultEnvironment() {
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (value.startsWith('()')) {
|
||||
if (value.startsWith("()")) {
|
||||
// Skip functions, which are a security risk.
|
||||
continue;
|
||||
}
|
||||
@@ -54,10 +54,11 @@ function getDefaultEnvironment() {
|
||||
*/
|
||||
class StdioClientTransport {
|
||||
constructor(server) {
|
||||
this._abortController = new AbortController();
|
||||
this._readBuffer = new stdio_js_1.ReadBuffer();
|
||||
this._stderrStream = null;
|
||||
this._serverParams = server;
|
||||
if (server.stderr === 'pipe' || server.stderr === 'overlapped') {
|
||||
if (server.stderr === "pipe" || server.stderr === "overlapped") {
|
||||
this._stderrStream = new node_stream_1.PassThrough();
|
||||
}
|
||||
}
|
||||
@@ -66,40 +67,51 @@ class StdioClientTransport {
|
||||
*/
|
||||
async start() {
|
||||
if (this._process) {
|
||||
throw new Error('StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.');
|
||||
throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._process = (0, cross_spawn_1.default)(this._serverParams.command, this._serverParams.args ?? [], {
|
||||
var _a, _b, _c, _d, _e;
|
||||
this._process = (0, cross_spawn_1.default)(this._serverParams.command, (_a = this._serverParams.args) !== null && _a !== void 0 ? _a : [], {
|
||||
// merge default env with server env because mcp server needs some env vars
|
||||
env: {
|
||||
...getDefaultEnvironment(),
|
||||
...this._serverParams.env
|
||||
...this._serverParams.env,
|
||||
},
|
||||
stdio: ['pipe', 'pipe', this._serverParams.stderr ?? 'inherit'],
|
||||
stdio: ["pipe", "pipe", (_b = this._serverParams.stderr) !== null && _b !== void 0 ? _b : "inherit"],
|
||||
shell: false,
|
||||
windowsHide: node_process_1.default.platform === 'win32' && isElectron(),
|
||||
cwd: this._serverParams.cwd
|
||||
signal: this._abortController.signal,
|
||||
windowsHide: node_process_1.default.platform === "win32" && isElectron(),
|
||||
cwd: this._serverParams.cwd,
|
||||
});
|
||||
this._process.on('error', error => {
|
||||
this._process.on("error", (error) => {
|
||||
var _a, _b;
|
||||
if (error.name === "AbortError") {
|
||||
// Expected when close() is called.
|
||||
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
||||
return;
|
||||
}
|
||||
reject(error);
|
||||
this.onerror?.(error);
|
||||
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
|
||||
});
|
||||
this._process.on('spawn', () => {
|
||||
this._process.on("spawn", () => {
|
||||
resolve();
|
||||
});
|
||||
this._process.on('close', _code => {
|
||||
this._process.on("close", (_code) => {
|
||||
var _a;
|
||||
this._process = undefined;
|
||||
this.onclose?.();
|
||||
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
||||
});
|
||||
this._process.stdin?.on('error', error => {
|
||||
this.onerror?.(error);
|
||||
(_c = this._process.stdin) === null || _c === void 0 ? void 0 : _c.on("error", (error) => {
|
||||
var _a;
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
});
|
||||
this._process.stdout?.on('data', chunk => {
|
||||
(_d = this._process.stdout) === null || _d === void 0 ? void 0 : _d.on("data", (chunk) => {
|
||||
this._readBuffer.append(chunk);
|
||||
this.processReadBuffer();
|
||||
});
|
||||
this._process.stdout?.on('error', error => {
|
||||
this.onerror?.(error);
|
||||
(_e = this._process.stdout) === null || _e === void 0 ? void 0 : _e.on("error", (error) => {
|
||||
var _a;
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
});
|
||||
if (this._stderrStream && this._process.stderr) {
|
||||
this._process.stderr.pipe(this._stderrStream);
|
||||
@@ -114,10 +126,11 @@ class StdioClientTransport {
|
||||
* error output emitted by the child process.
|
||||
*/
|
||||
get stderr() {
|
||||
var _a, _b;
|
||||
if (this._stderrStream) {
|
||||
return this._stderrStream;
|
||||
}
|
||||
return this._process?.stderr ?? null;
|
||||
return (_b = (_a = this._process) === null || _a === void 0 ? void 0 : _a.stderr) !== null && _b !== void 0 ? _b : null;
|
||||
}
|
||||
/**
|
||||
* The child process pid spawned by this transport.
|
||||
@@ -125,75 +138,47 @@ class StdioClientTransport {
|
||||
* This is only available after the transport has been started.
|
||||
*/
|
||||
get pid() {
|
||||
return this._process?.pid ?? null;
|
||||
var _a, _b;
|
||||
return (_b = (_a = this._process) === null || _a === void 0 ? void 0 : _a.pid) !== null && _b !== void 0 ? _b : null;
|
||||
}
|
||||
processReadBuffer() {
|
||||
var _a, _b;
|
||||
while (true) {
|
||||
try {
|
||||
const message = this._readBuffer.readMessage();
|
||||
if (message === null) {
|
||||
break;
|
||||
}
|
||||
this.onmessage?.(message);
|
||||
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
async close() {
|
||||
if (this._process) {
|
||||
const processToClose = this._process;
|
||||
this._process = undefined;
|
||||
const closePromise = new Promise(resolve => {
|
||||
processToClose.once('close', () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
try {
|
||||
processToClose.stdin?.end();
|
||||
}
|
||||
catch {
|
||||
// ignore
|
||||
}
|
||||
await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2000).unref())]);
|
||||
if (processToClose.exitCode === null) {
|
||||
try {
|
||||
processToClose.kill('SIGTERM');
|
||||
}
|
||||
catch {
|
||||
// ignore
|
||||
}
|
||||
await Promise.race([closePromise, new Promise(resolve => setTimeout(resolve, 2000).unref())]);
|
||||
}
|
||||
if (processToClose.exitCode === null) {
|
||||
try {
|
||||
processToClose.kill('SIGKILL');
|
||||
}
|
||||
catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
this._abortController.abort();
|
||||
this._process = undefined;
|
||||
this._readBuffer.clear();
|
||||
}
|
||||
send(message) {
|
||||
return new Promise(resolve => {
|
||||
if (!this._process?.stdin) {
|
||||
throw new Error('Not connected');
|
||||
return new Promise((resolve) => {
|
||||
var _a;
|
||||
if (!((_a = this._process) === null || _a === void 0 ? void 0 : _a.stdin)) {
|
||||
throw new Error("Not connected");
|
||||
}
|
||||
const json = (0, stdio_js_1.serializeMessage)(message);
|
||||
if (this._process.stdin.write(json)) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
this._process.stdin.once('drain', resolve);
|
||||
this._process.stdin.once("drain", resolve);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.StdioClientTransport = StdioClientTransport;
|
||||
function isElectron() {
|
||||
return 'type' in node_process_1.default;
|
||||
return "type" in node_process_1.default;
|
||||
}
|
||||
//# sourceMappingURL=stdio.js.map
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../src/client/stdio.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../src/client/stdio.ts"],"names":[],"mappings":"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"}
|
||||
+8
-23
@@ -1,6 +1,6 @@
|
||||
import { Transport, FetchLike } from '../shared/transport.js';
|
||||
import { JSONRPCMessage } from '../types.js';
|
||||
import { OAuthClientProvider } from './auth.js';
|
||||
import { Transport, FetchLike } from "../shared/transport.js";
|
||||
import { JSONRPCMessage } from "../types.js";
|
||||
import { OAuthClientProvider } from "./auth.js";
|
||||
export declare class StreamableHTTPError extends Error {
|
||||
readonly code: number | undefined;
|
||||
constructor(code: number | undefined, message: string | undefined);
|
||||
@@ -22,9 +22,9 @@ export interface StartSSEOptions {
|
||||
*/
|
||||
onresumptiontoken?: (token: string) => void;
|
||||
/**
|
||||
* Override Message ID to associate with the replay message
|
||||
* so that response can be associate with the new resumed request.
|
||||
*/
|
||||
* Override Message ID to associate with the replay message
|
||||
* so that response can be associate with the new resumed request.
|
||||
*/
|
||||
replayMessageId?: string | number;
|
||||
}
|
||||
/**
|
||||
@@ -98,18 +98,12 @@ export declare class StreamableHTTPClientTransport implements Transport {
|
||||
private _abortController?;
|
||||
private _url;
|
||||
private _resourceMetadataUrl?;
|
||||
private _scope?;
|
||||
private _requestInit?;
|
||||
private _authProvider?;
|
||||
private _fetch?;
|
||||
private _fetchWithInit;
|
||||
private _sessionId?;
|
||||
private _reconnectionOptions;
|
||||
private _protocolVersion?;
|
||||
private _hasCompletedAuthFlow;
|
||||
private _lastUpscopingHeader?;
|
||||
private _serverRetryMs?;
|
||||
private _reconnectionTimeout?;
|
||||
onclose?: () => void;
|
||||
onerror?: (error: Error) => void;
|
||||
onmessage?: (message: JSONRPCMessage) => void;
|
||||
@@ -124,8 +118,9 @@ export declare class StreamableHTTPClientTransport implements Transport {
|
||||
* @returns Time to wait in milliseconds before next reconnection attempt
|
||||
*/
|
||||
private _getNextReconnectionDelay;
|
||||
private _normalizeHeaders;
|
||||
/**
|
||||
* Schedule a reconnection attempt using server-provided retry interval or backoff
|
||||
* Schedule a reconnection attempt with exponential backoff
|
||||
*
|
||||
* @param lastEventId The ID of the last received event for resumability
|
||||
* @param attemptCount Current reconnection attempt count for this specific stream
|
||||
@@ -157,15 +152,5 @@ export declare class StreamableHTTPClientTransport implements Transport {
|
||||
terminateSession(): Promise<void>;
|
||||
setProtocolVersion(version: string): void;
|
||||
get protocolVersion(): string | undefined;
|
||||
/**
|
||||
* Resume an SSE stream from a previous event ID.
|
||||
* Opens a GET SSE connection with Last-Event-ID header to replay missed events.
|
||||
*
|
||||
* @param lastEventId The event ID to resume from
|
||||
* @param options Optional callback to receive new resumption tokens
|
||||
*/
|
||||
resumeStream(lastEventId: string, options?: {
|
||||
onresumptiontoken?: (token: string) => void;
|
||||
}): Promise<void>;
|
||||
}
|
||||
//# sourceMappingURL=streamableHttp.d.ts.map
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"streamableHttp.d.ts","sourceRoot":"","sources":["../../../src/client/streamableHttp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAyC,MAAM,wBAAwB,CAAC;AACrG,OAAO,EAAwE,cAAc,EAAwB,MAAM,aAAa,CAAC;AACzI,OAAO,EAAkD,mBAAmB,EAAqB,MAAM,WAAW,CAAC;AAWnH,qBAAa,mBAAoB,SAAQ,KAAK;aAEtB,IAAI,EAAE,MAAM,GAAG,SAAS;gBAAxB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxC,OAAO,EAAE,MAAM,GAAG,SAAS;CAIlC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAC9C;;;OAGG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;;OAGG;IACH,wBAAwB,EAAE,MAAM,CAAC;IAEjC;;;OAGG;IACH,2BAA2B,EAAE,MAAM,CAAC;IAEpC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG;IAC/C;;;;;;;;;;;;;OAaG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,mBAAmB,CAAC,EAAE,iCAAiC,CAAC;IAExD;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,qBAAa,6BAA8B,YAAW,SAAS;IAC3D,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,oBAAoB,CAAC,CAAM;IACnC,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,OAAO,CAAC,aAAa,CAAC,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAY;IAC3B,OAAO,CAAC,cAAc,CAAY;IAClC,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAClC,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,oBAAoB,CAAC,CAAS;IACtC,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,oBAAoB,CAAC,CAAgC;IAE7D,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAElC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,oCAAoC;YAYnD,cAAc;YAyBd,cAAc;YAwBd,eAAe;IA4C7B;;;;;OAKG;IACH,OAAO,CAAC,yBAAyB;IAejC;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,gBAAgB;IA+GlB,KAAK;IAUX;;OAEG;IACG,UAAU,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,CACN,OAAO,EAAE,cAAc,GAAG,cAAc,EAAE,EAC1C,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,GACpF,OAAO,CAAC,IAAI,CAAC;IA0JhB,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED;;;;;;;;;;OAUG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BvC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAGzC,IAAI,eAAe,IAAI,MAAM,GAAG,SAAS,CAExC;IAED;;;;;;OAMG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAMpH"}
|
||||
{"version":3,"file":"streamableHttp.d.ts","sourceRoot":"","sources":["../../../src/client/streamableHttp.ts"],"names":[],"mappings":"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"}
|
||||
+91
-193
@@ -1,7 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.StreamableHTTPClientTransport = exports.StreamableHTTPError = void 0;
|
||||
const transport_js_1 = require("../shared/transport.js");
|
||||
const types_js_1 = require("../types.js");
|
||||
const auth_js_1 = require("./auth.js");
|
||||
const stream_1 = require("eventsource-parser/stream");
|
||||
@@ -10,7 +9,7 @@ const DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS = {
|
||||
initialReconnectionDelay: 1000,
|
||||
maxReconnectionDelay: 30000,
|
||||
reconnectionDelayGrowFactor: 1.5,
|
||||
maxRetries: 2
|
||||
maxRetries: 2,
|
||||
};
|
||||
class StreamableHTTPError extends Error {
|
||||
constructor(code, message) {
|
||||
@@ -26,77 +25,72 @@ exports.StreamableHTTPError = StreamableHTTPError;
|
||||
*/
|
||||
class StreamableHTTPClientTransport {
|
||||
constructor(url, opts) {
|
||||
this._hasCompletedAuthFlow = false; // Circuit breaker: detect auth success followed by immediate 401
|
||||
var _a;
|
||||
this._url = url;
|
||||
this._resourceMetadataUrl = undefined;
|
||||
this._scope = undefined;
|
||||
this._requestInit = opts?.requestInit;
|
||||
this._authProvider = opts?.authProvider;
|
||||
this._fetch = opts?.fetch;
|
||||
this._fetchWithInit = (0, transport_js_1.createFetchWithInit)(opts?.fetch, opts?.requestInit);
|
||||
this._sessionId = opts?.sessionId;
|
||||
this._reconnectionOptions = opts?.reconnectionOptions ?? DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS;
|
||||
this._requestInit = opts === null || opts === void 0 ? void 0 : opts.requestInit;
|
||||
this._authProvider = opts === null || opts === void 0 ? void 0 : opts.authProvider;
|
||||
this._fetch = opts === null || opts === void 0 ? void 0 : opts.fetch;
|
||||
this._sessionId = opts === null || opts === void 0 ? void 0 : opts.sessionId;
|
||||
this._reconnectionOptions = (_a = opts === null || opts === void 0 ? void 0 : opts.reconnectionOptions) !== null && _a !== void 0 ? _a : DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS;
|
||||
}
|
||||
async _authThenStart() {
|
||||
var _a;
|
||||
if (!this._authProvider) {
|
||||
throw new auth_js_1.UnauthorizedError('No auth provider');
|
||||
throw new auth_js_1.UnauthorizedError("No auth provider");
|
||||
}
|
||||
let result;
|
||||
try {
|
||||
result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetchWithInit
|
||||
});
|
||||
result = await (0, auth_js_1.auth)(this._authProvider, { serverUrl: this._url, resourceMetadataUrl: this._resourceMetadataUrl, fetchFn: this._fetch });
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
throw error;
|
||||
}
|
||||
if (result !== 'AUTHORIZED') {
|
||||
if (result !== "AUTHORIZED") {
|
||||
throw new auth_js_1.UnauthorizedError();
|
||||
}
|
||||
return await this._startOrAuthSse({ resumptionToken: undefined });
|
||||
}
|
||||
async _commonHeaders() {
|
||||
var _a;
|
||||
const headers = {};
|
||||
if (this._authProvider) {
|
||||
const tokens = await this._authProvider.tokens();
|
||||
if (tokens) {
|
||||
headers['Authorization'] = `Bearer ${tokens.access_token}`;
|
||||
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
||||
}
|
||||
}
|
||||
if (this._sessionId) {
|
||||
headers['mcp-session-id'] = this._sessionId;
|
||||
headers["mcp-session-id"] = this._sessionId;
|
||||
}
|
||||
if (this._protocolVersion) {
|
||||
headers['mcp-protocol-version'] = this._protocolVersion;
|
||||
headers["mcp-protocol-version"] = this._protocolVersion;
|
||||
}
|
||||
const extraHeaders = (0, transport_js_1.normalizeHeaders)(this._requestInit?.headers);
|
||||
const extraHeaders = this._normalizeHeaders((_a = this._requestInit) === null || _a === void 0 ? void 0 : _a.headers);
|
||||
return new Headers({
|
||||
...headers,
|
||||
...extraHeaders
|
||||
...extraHeaders,
|
||||
});
|
||||
}
|
||||
async _startOrAuthSse(options) {
|
||||
var _a, _b, _c;
|
||||
const { resumptionToken } = options;
|
||||
try {
|
||||
// Try to open an initial SSE stream with GET to listen for server messages
|
||||
// This is optional according to the spec - server may not support it
|
||||
const headers = await this._commonHeaders();
|
||||
headers.set('Accept', 'text/event-stream');
|
||||
headers.set("Accept", "text/event-stream");
|
||||
// Include Last-Event-ID header for resumable streams if provided
|
||||
if (resumptionToken) {
|
||||
headers.set('last-event-id', resumptionToken);
|
||||
headers.set("last-event-id", resumptionToken);
|
||||
}
|
||||
const response = await (this._fetch ?? fetch)(this._url, {
|
||||
method: 'GET',
|
||||
const response = await ((_a = this._fetch) !== null && _a !== void 0 ? _a : fetch)(this._url, {
|
||||
method: "GET",
|
||||
headers,
|
||||
signal: this._abortController?.signal
|
||||
signal: (_b = this._abortController) === null || _b === void 0 ? void 0 : _b.signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
await response.body?.cancel();
|
||||
if (response.status === 401 && this._authProvider) {
|
||||
// Need to authenticate
|
||||
return await this._authThenStart();
|
||||
@@ -111,7 +105,7 @@ class StreamableHTTPClientTransport {
|
||||
this._handleSseStream(response.body, options, true);
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_c = this.onerror) === null || _c === void 0 ? void 0 : _c.call(this, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -122,38 +116,47 @@ class StreamableHTTPClientTransport {
|
||||
* @returns Time to wait in milliseconds before next reconnection attempt
|
||||
*/
|
||||
_getNextReconnectionDelay(attempt) {
|
||||
// Use server-provided retry value if available
|
||||
if (this._serverRetryMs !== undefined) {
|
||||
return this._serverRetryMs;
|
||||
}
|
||||
// Fall back to exponential backoff
|
||||
// Access default values directly, ensuring they're never undefined
|
||||
const initialDelay = this._reconnectionOptions.initialReconnectionDelay;
|
||||
const growFactor = this._reconnectionOptions.reconnectionDelayGrowFactor;
|
||||
const maxDelay = this._reconnectionOptions.maxReconnectionDelay;
|
||||
// Cap at maximum delay
|
||||
return Math.min(initialDelay * Math.pow(growFactor, attempt), maxDelay);
|
||||
}
|
||||
_normalizeHeaders(headers) {
|
||||
if (!headers)
|
||||
return {};
|
||||
if (headers instanceof Headers) {
|
||||
return Object.fromEntries(headers.entries());
|
||||
}
|
||||
if (Array.isArray(headers)) {
|
||||
return Object.fromEntries(headers);
|
||||
}
|
||||
return { ...headers };
|
||||
}
|
||||
/**
|
||||
* Schedule a reconnection attempt using server-provided retry interval or backoff
|
||||
* Schedule a reconnection attempt with exponential backoff
|
||||
*
|
||||
* @param lastEventId The ID of the last received event for resumability
|
||||
* @param attemptCount Current reconnection attempt count for this specific stream
|
||||
*/
|
||||
_scheduleReconnection(options, attemptCount = 0) {
|
||||
var _a;
|
||||
// Use provided options or default options
|
||||
const maxRetries = this._reconnectionOptions.maxRetries;
|
||||
// Check if we've exceeded maximum retry attempts
|
||||
if (attemptCount >= maxRetries) {
|
||||
this.onerror?.(new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
|
||||
if (maxRetries > 0 && attemptCount >= maxRetries) {
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, new Error(`Maximum reconnection attempts (${maxRetries}) exceeded.`));
|
||||
return;
|
||||
}
|
||||
// Calculate next delay based on current attempt count
|
||||
const delay = this._getNextReconnectionDelay(attemptCount);
|
||||
// Schedule the reconnection
|
||||
this._reconnectionTimeout = setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
// Use the last event ID to resume where we left off
|
||||
this._startOrAuthSse(options).catch(error => {
|
||||
this.onerror?.(new Error(`Failed to reconnect SSE stream: ${error instanceof Error ? error.message : String(error)}`));
|
||||
var _a;
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, new Error(`Failed to reconnect SSE stream: ${error instanceof Error ? error.message : String(error)}`));
|
||||
// Schedule another attempt if this one failed, incrementing the attempt counter
|
||||
this._scheduleReconnection(options, attemptCount + 1);
|
||||
});
|
||||
@@ -165,25 +168,15 @@ class StreamableHTTPClientTransport {
|
||||
}
|
||||
const { onresumptiontoken, replayMessageId } = options;
|
||||
let lastEventId;
|
||||
// Track whether we've received a priming event (event with ID)
|
||||
// Per spec, server SHOULD send a priming event with ID before closing
|
||||
let hasPrimingEvent = false;
|
||||
// Track whether we've received a response - if so, no need to reconnect
|
||||
// Reconnection is for when server disconnects BEFORE sending response
|
||||
let receivedResponse = false;
|
||||
const processStream = async () => {
|
||||
var _a, _b, _c, _d;
|
||||
// this is the closest we can get to trying to catch network errors
|
||||
// if something happens reader will throw
|
||||
try {
|
||||
// Create a pipeline: binary stream -> text decoder -> SSE parser
|
||||
const reader = stream
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(new stream_1.EventSourceParserStream({
|
||||
onRetry: (retryMs) => {
|
||||
// Capture server-provided retry value for reconnection timing
|
||||
this._serverRetryMs = retryMs;
|
||||
}
|
||||
}))
|
||||
.pipeThrough(new stream_1.EventSourceParserStream())
|
||||
.getReader();
|
||||
while (true) {
|
||||
const { value: event, done } = await reader.read();
|
||||
@@ -193,54 +186,29 @@ class StreamableHTTPClientTransport {
|
||||
// Update last event ID if provided
|
||||
if (event.id) {
|
||||
lastEventId = event.id;
|
||||
// Mark that we've received a priming event - stream is now resumable
|
||||
hasPrimingEvent = true;
|
||||
onresumptiontoken?.(event.id);
|
||||
onresumptiontoken === null || onresumptiontoken === void 0 ? void 0 : onresumptiontoken(event.id);
|
||||
}
|
||||
// Skip events with no data (priming events, keep-alives)
|
||||
if (!event.data) {
|
||||
continue;
|
||||
}
|
||||
if (!event.event || event.event === 'message') {
|
||||
if (!event.event || event.event === "message") {
|
||||
try {
|
||||
const message = types_js_1.JSONRPCMessageSchema.parse(JSON.parse(event.data));
|
||||
if ((0, types_js_1.isJSONRPCResultResponse)(message)) {
|
||||
// Mark that we received a response - no need to reconnect for this request
|
||||
receivedResponse = true;
|
||||
if (replayMessageId !== undefined) {
|
||||
message.id = replayMessageId;
|
||||
}
|
||||
if (replayMessageId !== undefined && (0, types_js_1.isJSONRPCResponse)(message)) {
|
||||
message.id = replayMessageId;
|
||||
}
|
||||
this.onmessage?.(message);
|
||||
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle graceful server-side disconnect
|
||||
// Server may close connection after sending event ID and retry field
|
||||
// Reconnect if: already reconnectable (GET stream) OR received a priming event (POST stream with event ID)
|
||||
// BUT don't reconnect if we already received a response - the request is complete
|
||||
const canResume = isReconnectable || hasPrimingEvent;
|
||||
const needsReconnect = canResume && !receivedResponse;
|
||||
if (needsReconnect && this._abortController && !this._abortController.signal.aborted) {
|
||||
this._scheduleReconnection({
|
||||
resumptionToken: lastEventId,
|
||||
onresumptiontoken,
|
||||
replayMessageId
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// Handle stream errors - likely a network disconnect
|
||||
this.onerror?.(new Error(`SSE stream disconnected: ${error}`));
|
||||
(_c = this.onerror) === null || _c === void 0 ? void 0 : _c.call(this, new Error(`SSE stream disconnected: ${error}`));
|
||||
// Attempt to reconnect if the stream disconnects unexpectedly and we aren't closing
|
||||
// Reconnect if: already reconnectable (GET stream) OR received a priming event (POST stream with event ID)
|
||||
// BUT don't reconnect if we already received a response - the request is complete
|
||||
const canResume = isReconnectable || hasPrimingEvent;
|
||||
const needsReconnect = canResume && !receivedResponse;
|
||||
if (needsReconnect && this._abortController && !this._abortController.signal.aborted) {
|
||||
if (isReconnectable &&
|
||||
this._abortController &&
|
||||
!this._abortController.signal.aborted) {
|
||||
// Use the exponential backoff reconnection strategy
|
||||
try {
|
||||
this._scheduleReconnection({
|
||||
@@ -250,7 +218,7 @@ class StreamableHTTPClientTransport {
|
||||
}, 0);
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(new Error(`Failed to reconnect: ${error instanceof Error ? error.message : String(error)}`));
|
||||
(_d = this.onerror) === null || _d === void 0 ? void 0 : _d.call(this, new Error(`Failed to reconnect: ${error instanceof Error ? error.message : String(error)}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,7 +227,7 @@ class StreamableHTTPClientTransport {
|
||||
}
|
||||
async start() {
|
||||
if (this._abortController) {
|
||||
throw new Error('StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.');
|
||||
throw new Error("StreamableHTTPClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
||||
}
|
||||
this._abortController = new AbortController();
|
||||
}
|
||||
@@ -268,153 +236,96 @@ class StreamableHTTPClientTransport {
|
||||
*/
|
||||
async finishAuth(authorizationCode) {
|
||||
if (!this._authProvider) {
|
||||
throw new auth_js_1.UnauthorizedError('No auth provider');
|
||||
throw new auth_js_1.UnauthorizedError("No auth provider");
|
||||
}
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
authorizationCode,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetchWithInit
|
||||
});
|
||||
if (result !== 'AUTHORIZED') {
|
||||
throw new auth_js_1.UnauthorizedError('Failed to authorize');
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, { serverUrl: this._url, authorizationCode, resourceMetadataUrl: this._resourceMetadataUrl, fetchFn: this._fetch });
|
||||
if (result !== "AUTHORIZED") {
|
||||
throw new auth_js_1.UnauthorizedError("Failed to authorize");
|
||||
}
|
||||
}
|
||||
async close() {
|
||||
if (this._reconnectionTimeout) {
|
||||
clearTimeout(this._reconnectionTimeout);
|
||||
this._reconnectionTimeout = undefined;
|
||||
}
|
||||
this._abortController?.abort();
|
||||
this.onclose?.();
|
||||
var _a, _b;
|
||||
// Abort any pending requests
|
||||
(_a = this._abortController) === null || _a === void 0 ? void 0 : _a.abort();
|
||||
(_b = this.onclose) === null || _b === void 0 ? void 0 : _b.call(this);
|
||||
}
|
||||
async send(message, options) {
|
||||
var _a, _b, _c, _d;
|
||||
try {
|
||||
const { resumptionToken, onresumptiontoken } = options || {};
|
||||
if (resumptionToken) {
|
||||
// If we have at last event ID, we need to reconnect the SSE stream
|
||||
this._startOrAuthSse({ resumptionToken, replayMessageId: (0, types_js_1.isJSONRPCRequest)(message) ? message.id : undefined }).catch(err => this.onerror?.(err));
|
||||
this._startOrAuthSse({ resumptionToken, replayMessageId: (0, types_js_1.isJSONRPCRequest)(message) ? message.id : undefined }).catch(err => { var _a; return (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, err); });
|
||||
return;
|
||||
}
|
||||
const headers = await this._commonHeaders();
|
||||
headers.set('content-type', 'application/json');
|
||||
headers.set('accept', 'application/json, text/event-stream');
|
||||
headers.set("content-type", "application/json");
|
||||
headers.set("accept", "application/json, text/event-stream");
|
||||
const init = {
|
||||
...this._requestInit,
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(message),
|
||||
signal: this._abortController?.signal
|
||||
signal: (_a = this._abortController) === null || _a === void 0 ? void 0 : _a.signal,
|
||||
};
|
||||
const response = await (this._fetch ?? fetch)(this._url, init);
|
||||
const response = await ((_b = this._fetch) !== null && _b !== void 0 ? _b : fetch)(this._url, init);
|
||||
// Handle session ID received during initialization
|
||||
const sessionId = response.headers.get('mcp-session-id');
|
||||
const sessionId = response.headers.get("mcp-session-id");
|
||||
if (sessionId) {
|
||||
this._sessionId = sessionId;
|
||||
}
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => null);
|
||||
if (response.status === 401 && this._authProvider) {
|
||||
// Prevent infinite recursion when server returns 401 after successful auth
|
||||
if (this._hasCompletedAuthFlow) {
|
||||
throw new StreamableHTTPError(401, 'Server returned 401 after successful authentication');
|
||||
}
|
||||
const { resourceMetadataUrl, scope } = (0, auth_js_1.extractWWWAuthenticateParams)(response);
|
||||
this._resourceMetadataUrl = resourceMetadataUrl;
|
||||
this._scope = scope;
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetchWithInit
|
||||
});
|
||||
if (result !== 'AUTHORIZED') {
|
||||
this._resourceMetadataUrl = (0, auth_js_1.extractResourceMetadataUrl)(response);
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, { serverUrl: this._url, resourceMetadataUrl: this._resourceMetadataUrl, fetchFn: this._fetch });
|
||||
if (result !== "AUTHORIZED") {
|
||||
throw new auth_js_1.UnauthorizedError();
|
||||
}
|
||||
// Mark that we completed auth flow
|
||||
this._hasCompletedAuthFlow = true;
|
||||
// Purposely _not_ awaited, so we don't call onerror twice
|
||||
return this.send(message);
|
||||
}
|
||||
if (response.status === 403 && this._authProvider) {
|
||||
const { resourceMetadataUrl, scope, error } = (0, auth_js_1.extractWWWAuthenticateParams)(response);
|
||||
if (error === 'insufficient_scope') {
|
||||
const wwwAuthHeader = response.headers.get('WWW-Authenticate');
|
||||
// Check if we've already tried upscoping with this header to prevent infinite loops.
|
||||
if (this._lastUpscopingHeader === wwwAuthHeader) {
|
||||
throw new StreamableHTTPError(403, 'Server returned 403 after trying upscoping');
|
||||
}
|
||||
if (scope) {
|
||||
this._scope = scope;
|
||||
}
|
||||
if (resourceMetadataUrl) {
|
||||
this._resourceMetadataUrl = resourceMetadataUrl;
|
||||
}
|
||||
// Mark that upscoping was tried.
|
||||
this._lastUpscopingHeader = wwwAuthHeader ?? undefined;
|
||||
const result = await (0, auth_js_1.auth)(this._authProvider, {
|
||||
serverUrl: this._url,
|
||||
resourceMetadataUrl: this._resourceMetadataUrl,
|
||||
scope: this._scope,
|
||||
fetchFn: this._fetch
|
||||
});
|
||||
if (result !== 'AUTHORIZED') {
|
||||
throw new auth_js_1.UnauthorizedError();
|
||||
}
|
||||
return this.send(message);
|
||||
}
|
||||
}
|
||||
throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
|
||||
const text = await response.text().catch(() => null);
|
||||
throw new Error(`Error POSTing to endpoint (HTTP ${response.status}): ${text}`);
|
||||
}
|
||||
// Reset auth loop flag on successful response
|
||||
this._hasCompletedAuthFlow = false;
|
||||
this._lastUpscopingHeader = undefined;
|
||||
// If the response is 202 Accepted, there's no body to process
|
||||
if (response.status === 202) {
|
||||
await response.body?.cancel();
|
||||
// if the accepted notification is initialized, we start the SSE stream
|
||||
// if it's supported by the server
|
||||
if ((0, types_js_1.isInitializedNotification)(message)) {
|
||||
// Start without a lastEventId since this is a fresh connection
|
||||
this._startOrAuthSse({ resumptionToken: undefined }).catch(err => this.onerror?.(err));
|
||||
this._startOrAuthSse({ resumptionToken: undefined }).catch(err => { var _a; return (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, err); });
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Get original message(s) for detecting request IDs
|
||||
const messages = Array.isArray(message) ? message : [message];
|
||||
const hasRequests = messages.filter(msg => 'method' in msg && 'id' in msg && msg.id !== undefined).length > 0;
|
||||
const hasRequests = messages.filter(msg => "method" in msg && "id" in msg && msg.id !== undefined).length > 0;
|
||||
// Check the response type
|
||||
const contentType = response.headers.get('content-type');
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (hasRequests) {
|
||||
if (contentType?.includes('text/event-stream')) {
|
||||
if (contentType === null || contentType === void 0 ? void 0 : contentType.includes("text/event-stream")) {
|
||||
// Handle SSE stream responses for requests
|
||||
// We use the same handler as standalone streams, which now supports
|
||||
// reconnection with the last event ID
|
||||
this._handleSseStream(response.body, { onresumptiontoken }, false);
|
||||
}
|
||||
else if (contentType?.includes('application/json')) {
|
||||
else if (contentType === null || contentType === void 0 ? void 0 : contentType.includes("application/json")) {
|
||||
// For non-streaming servers, we might get direct JSON responses
|
||||
const data = await response.json();
|
||||
const responseMessages = Array.isArray(data)
|
||||
? data.map(msg => types_js_1.JSONRPCMessageSchema.parse(msg))
|
||||
: [types_js_1.JSONRPCMessageSchema.parse(data)];
|
||||
for (const msg of responseMessages) {
|
||||
this.onmessage?.(msg);
|
||||
(_c = this.onmessage) === null || _c === void 0 ? void 0 : _c.call(this, msg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
await response.body?.cancel();
|
||||
throw new StreamableHTTPError(-1, `Unexpected content type: ${contentType}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No requests in message but got 200 OK - still need to release connection
|
||||
await response.body?.cancel();
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_d = this.onerror) === null || _d === void 0 ? void 0 : _d.call(this, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -433,6 +344,7 @@ class StreamableHTTPClientTransport {
|
||||
* the server does not allow clients to terminate sessions.
|
||||
*/
|
||||
async terminateSession() {
|
||||
var _a, _b, _c;
|
||||
if (!this._sessionId) {
|
||||
return; // No session to terminate
|
||||
}
|
||||
@@ -440,12 +352,11 @@ class StreamableHTTPClientTransport {
|
||||
const headers = await this._commonHeaders();
|
||||
const init = {
|
||||
...this._requestInit,
|
||||
method: 'DELETE',
|
||||
method: "DELETE",
|
||||
headers,
|
||||
signal: this._abortController?.signal
|
||||
signal: (_a = this._abortController) === null || _a === void 0 ? void 0 : _a.signal,
|
||||
};
|
||||
const response = await (this._fetch ?? fetch)(this._url, init);
|
||||
await response.body?.cancel();
|
||||
const response = await ((_b = this._fetch) !== null && _b !== void 0 ? _b : fetch)(this._url, init);
|
||||
// We specifically handle 405 as a valid response according to the spec,
|
||||
// meaning the server does not support explicit session termination
|
||||
if (!response.ok && response.status !== 405) {
|
||||
@@ -454,7 +365,7 @@ class StreamableHTTPClientTransport {
|
||||
this._sessionId = undefined;
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_c = this.onerror) === null || _c === void 0 ? void 0 : _c.call(this, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -464,19 +375,6 @@ class StreamableHTTPClientTransport {
|
||||
get protocolVersion() {
|
||||
return this._protocolVersion;
|
||||
}
|
||||
/**
|
||||
* Resume an SSE stream from a previous event ID.
|
||||
* Opens a GET SSE connection with Last-Event-ID header to replay missed events.
|
||||
*
|
||||
* @param lastEventId The event ID to resume from
|
||||
* @param options Optional callback to receive new resumption tokens
|
||||
*/
|
||||
async resumeStream(lastEventId, options) {
|
||||
await this._startOrAuthSse({
|
||||
resumptionToken: lastEventId,
|
||||
onresumptiontoken: options?.onresumptiontoken
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.StreamableHTTPClientTransport = StreamableHTTPClientTransport;
|
||||
//# sourceMappingURL=streamableHttp.js.map
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { Transport } from '../shared/transport.js';
|
||||
import { JSONRPCMessage } from '../types.js';
|
||||
import { Transport } from "../shared/transport.js";
|
||||
import { JSONRPCMessage } from "../types.js";
|
||||
/**
|
||||
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../src/client/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAInE;;GAEG;AACH,qBAAa,wBAAyB,YAAW,SAAS;IACtD,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,IAAI,CAAM;IAElB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAElC,GAAG,EAAE,GAAG;IAIpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsChB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAW/C"}
|
||||
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../../src/client/websocket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAInE;;GAEG;AACH,qBAAa,wBAAyB,YAAW,SAAS;IACxD,OAAO,CAAC,OAAO,CAAC,CAAY;IAC5B,OAAO,CAAC,IAAI,CAAM;IAElB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;gBAElC,GAAG,EAAE,GAAG;IAIpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyChB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAW7C"}
|
||||
+18
-11
@@ -2,7 +2,7 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebSocketClientTransport = void 0;
|
||||
const types_js_1 = require("../types.js");
|
||||
const SUBPROTOCOL = 'mcp';
|
||||
const SUBPROTOCOL = "mcp";
|
||||
/**
|
||||
* Client transport for WebSocket: this will connect to a server over the WebSocket protocol.
|
||||
*/
|
||||
@@ -12,44 +12,51 @@ class WebSocketClientTransport {
|
||||
}
|
||||
start() {
|
||||
if (this._socket) {
|
||||
throw new Error('WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.');
|
||||
throw new Error("WebSocketClientTransport already started! If using Client class, note that connect() calls start() automatically.");
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._socket = new WebSocket(this._url, SUBPROTOCOL);
|
||||
this._socket.onerror = event => {
|
||||
const error = 'error' in event ? event.error : new Error(`WebSocket error: ${JSON.stringify(event)}`);
|
||||
this._socket.onerror = (event) => {
|
||||
var _a;
|
||||
const error = "error" in event
|
||||
? event.error
|
||||
: new Error(`WebSocket error: ${JSON.stringify(event)}`);
|
||||
reject(error);
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
};
|
||||
this._socket.onopen = () => {
|
||||
resolve();
|
||||
};
|
||||
this._socket.onclose = () => {
|
||||
this.onclose?.();
|
||||
var _a;
|
||||
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
||||
};
|
||||
this._socket.onmessage = (event) => {
|
||||
var _a, _b;
|
||||
let message;
|
||||
try {
|
||||
message = types_js_1.JSONRPCMessageSchema.parse(JSON.parse(event.data));
|
||||
}
|
||||
catch (error) {
|
||||
this.onerror?.(error);
|
||||
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
||||
return;
|
||||
}
|
||||
this.onmessage?.(message);
|
||||
(_b = this.onmessage) === null || _b === void 0 ? void 0 : _b.call(this, message);
|
||||
};
|
||||
});
|
||||
}
|
||||
async close() {
|
||||
this._socket?.close();
|
||||
var _a;
|
||||
(_a = this._socket) === null || _a === void 0 ? void 0 : _a.close();
|
||||
}
|
||||
send(message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var _a;
|
||||
if (!this._socket) {
|
||||
reject(new Error('Not connected'));
|
||||
reject(new Error("Not connected"));
|
||||
return;
|
||||
}
|
||||
this._socket?.send(JSON.stringify(message));
|
||||
(_a = this._socket) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(message));
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../../src/client/websocket.ts"],"names":[],"mappings":";;;AACA,0CAAmE;AAEnE,MAAM,WAAW,GAAG,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAa,wBAAwB;IAQjC,YAAY,GAAQ;QAChB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;IACpB,CAAC;IAED,KAAK;QACD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACX,mHAAmH,CACtH,CAAC;QACN,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC,OAAO,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAErD,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,EAAE;gBAC3B,MAAM,KAAK,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAE,KAAK,CAAC,KAAe,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACjH,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE;gBACvB,OAAO,EAAE,CAAC;YACd,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE;gBACxB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACrB,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,KAAmB,EAAE,EAAE;gBAC7C,IAAI,OAAuB,CAAC;gBAC5B,IAAI,CAAC;oBACD,OAAO,GAAG,+BAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBACjE,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACb,IAAI,CAAC,OAAO,EAAE,CAAC,KAAc,CAAC,CAAC;oBAC/B,OAAO;gBACX,CAAC;gBAED,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC;YAC9B,CAAC,CAAC;QACN,CAAC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,KAAK;QACP,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,CAAC,OAAuB;QACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACnC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAChB,MAAM,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;gBACnC,OAAO;YACX,CAAC;YAED,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5C,OAAO,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACP,CAAC;CACJ;AAjED,4DAiEC"}
|
||||
{"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../../src/client/websocket.ts"],"names":[],"mappings":";;;AACA,0CAAmE;AAEnE,MAAM,WAAW,GAAG,KAAK,CAAC;AAE1B;;GAEG;AACH,MAAa,wBAAwB;IAQnC,YAAY,GAAQ;QAClB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;IAClB,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,mHAAmH,CACpH,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,OAAO,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAErD,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;;gBAC/B,MAAM,KAAK,GACT,OAAO,IAAI,KAAK;oBACd,CAAC,CAAE,KAAK,CAAC,KAAe;oBACxB,CAAC,CAAC,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC7D,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAK,CAAC,CAAC;YACxB,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE;gBACzB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE;;gBAC1B,MAAA,IAAI,CAAC,OAAO,oDAAI,CAAC;YACnB,CAAC,CAAC;YAEF,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,KAAmB,EAAE,EAAE;;gBAC/C,IAAI,OAAuB,CAAC;gBAC5B,IAAI,CAAC;oBACH,OAAO,GAAG,+BAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/D,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAA,IAAI,CAAC,OAAO,qDAAG,KAAc,CAAC,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBAED,MAAA,IAAI,CAAC,SAAS,qDAAG,OAAO,CAAC,CAAC;YAC5B,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;;QACT,MAAA,IAAI,CAAC,OAAO,0CAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,CAAC,OAAuB;QAC1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;;YACrC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;gBACnC,OAAO;YACT,CAAC;YAED,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5C,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AApED,4DAoEC"}
|
||||
Reference in New Issue
Block a user