修改后台权限

This commit is contained in:
yoyuzh
2026-03-24 14:30:59 +08:00
parent 00f902f475
commit b2d9db7be9
9310 changed files with 1246063 additions and 48 deletions

View File

@@ -0,0 +1,23 @@
/**
* Utilities for handling OAuth resource URIs.
*/
/**
* Converts a server URL to a resource URL by removing the fragment.
* RFC 8707 section 2 states that resource URIs "MUST NOT include a fragment component".
* Keeps everything else unchanged (scheme, domain, port, path, query).
*/
export declare function resourceUrlFromServerUrl(url: URL | string): URL;
/**
* Checks if a requested resource URL matches a configured resource URL.
* A requested resource matches if it has the same scheme, domain, port,
* and its path starts with the configured resource's path.
*
* @param requestedResource The resource URL being requested
* @param configuredResource The resource URL that has been configured
* @returns true if the requested resource matches the configured resource, false otherwise
*/
export declare function checkResourceAllowed({ requestedResource, configuredResource }: {
requestedResource: URL | string;
configuredResource: URL | string;
}): boolean;
//# sourceMappingURL=auth-utils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"auth-utils.d.ts","sourceRoot":"","sources":["../../../src/shared/auth-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,CAI/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,iBAAiB,EACjB,kBAAkB,EACrB,EAAE;IACC,iBAAiB,EAAE,GAAG,GAAG,MAAM,CAAC;IAChC,kBAAkB,EAAE,GAAG,GAAG,MAAM,CAAC;CACpC,GAAG,OAAO,CAwBV"}

View File

@@ -0,0 +1,48 @@
"use strict";
/**
* Utilities for handling OAuth resource URIs.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.resourceUrlFromServerUrl = resourceUrlFromServerUrl;
exports.checkResourceAllowed = checkResourceAllowed;
/**
* Converts a server URL to a resource URL by removing the fragment.
* RFC 8707 section 2 states that resource URIs "MUST NOT include a fragment component".
* Keeps everything else unchanged (scheme, domain, port, path, query).
*/
function resourceUrlFromServerUrl(url) {
const resourceURL = typeof url === 'string' ? new URL(url) : new URL(url.href);
resourceURL.hash = ''; // Remove fragment
return resourceURL;
}
/**
* Checks if a requested resource URL matches a configured resource URL.
* A requested resource matches if it has the same scheme, domain, port,
* and its path starts with the configured resource's path.
*
* @param requestedResource The resource URL being requested
* @param configuredResource The resource URL that has been configured
* @returns true if the requested resource matches the configured resource, false otherwise
*/
function checkResourceAllowed({ requestedResource, configuredResource }) {
const requested = typeof requestedResource === 'string' ? new URL(requestedResource) : new URL(requestedResource.href);
const configured = typeof configuredResource === 'string' ? new URL(configuredResource) : new URL(configuredResource.href);
// Compare the origin (scheme, domain, and port)
if (requested.origin !== configured.origin) {
return false;
}
// Handle cases like requested=/foo and configured=/foo/
if (requested.pathname.length < configured.pathname.length) {
return false;
}
// Check if the requested path starts with the configured path
// Ensure both paths end with / for proper comparison
// This ensures that if we have paths like "/api" and "/api/users",
// we properly detect that "/api/users" is a subpath of "/api"
// By adding a trailing slash if missing, we avoid false positives
// where paths like "/api123" would incorrectly match "/api"
const requestedPath = requested.pathname.endsWith('/') ? requested.pathname : requested.pathname + '/';
const configuredPath = configured.pathname.endsWith('/') ? configured.pathname : configured.pathname + '/';
return requestedPath.startsWith(configuredPath);
}
//# sourceMappingURL=auth-utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"auth-utils.js","sourceRoot":"","sources":["../../../src/shared/auth-utils.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAOH,4DAIC;AAWD,oDA8BC;AAlDD;;;;GAIG;AACH,SAAgB,wBAAwB,CAAC,GAAiB;IACtD,MAAM,WAAW,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/E,WAAW,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,kBAAkB;IACzC,OAAO,WAAW,CAAC;AACvB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAAC,EACjC,iBAAiB,EACjB,kBAAkB,EAIrB;IACG,MAAM,SAAS,GAAG,OAAO,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvH,MAAM,UAAU,GAAG,OAAO,kBAAkB,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAE3H,gDAAgD;IAChD,IAAI,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,wDAAwD;IACxD,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,8DAA8D;IAC9D,qDAAqD;IACrD,mEAAmE;IACnE,8DAA8D;IAC9D,kEAAkE;IAClE,4DAA4D;IAC5D,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,GAAG,GAAG,CAAC;IACvG,MAAM,cAAc,GAAG,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,GAAG,GAAG,CAAC;IAE3G,OAAO,aAAa,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACpD,CAAC"}

View File

@@ -0,0 +1,240 @@
import * as z from 'zod/v4';
/**
* Reusable URL validation that disallows javascript: scheme
*/
export declare const SafeUrlSchema: z.ZodURL;
/**
* RFC 9728 OAuth Protected Resource Metadata
*/
export declare const OAuthProtectedResourceMetadataSchema: z.ZodObject<{
resource: z.ZodString;
authorization_servers: z.ZodOptional<z.ZodArray<z.ZodURL>>;
jwks_uri: z.ZodOptional<z.ZodString>;
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
bearer_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
resource_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
resource_name: z.ZodOptional<z.ZodString>;
resource_documentation: z.ZodOptional<z.ZodString>;
resource_policy_uri: z.ZodOptional<z.ZodString>;
resource_tos_uri: z.ZodOptional<z.ZodString>;
tls_client_certificate_bound_access_tokens: z.ZodOptional<z.ZodBoolean>;
authorization_details_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
dpop_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
dpop_bound_access_tokens_required: z.ZodOptional<z.ZodBoolean>;
}, z.core.$loose>;
/**
* RFC 8414 OAuth 2.0 Authorization Server Metadata
*/
export declare const OAuthMetadataSchema: z.ZodObject<{
issuer: z.ZodString;
authorization_endpoint: z.ZodURL;
token_endpoint: z.ZodURL;
registration_endpoint: z.ZodOptional<z.ZodURL>;
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
response_types_supported: z.ZodArray<z.ZodString>;
response_modes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
grant_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
token_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
token_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
service_documentation: z.ZodOptional<z.ZodURL>;
revocation_endpoint: z.ZodOptional<z.ZodURL>;
revocation_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
revocation_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
introspection_endpoint: z.ZodOptional<z.ZodString>;
introspection_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
introspection_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
code_challenge_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
client_id_metadata_document_supported: z.ZodOptional<z.ZodBoolean>;
}, z.core.$loose>;
/**
* OpenID Connect Discovery 1.0 Provider Metadata
* see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
*/
export declare const OpenIdProviderMetadataSchema: z.ZodObject<{
issuer: z.ZodString;
authorization_endpoint: z.ZodURL;
token_endpoint: z.ZodURL;
userinfo_endpoint: z.ZodOptional<z.ZodURL>;
jwks_uri: z.ZodURL;
registration_endpoint: z.ZodOptional<z.ZodURL>;
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
response_types_supported: z.ZodArray<z.ZodString>;
response_modes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
grant_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
acr_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
subject_types_supported: z.ZodArray<z.ZodString>;
id_token_signing_alg_values_supported: z.ZodArray<z.ZodString>;
id_token_encryption_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
id_token_encryption_enc_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
userinfo_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
userinfo_encryption_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
userinfo_encryption_enc_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
request_object_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
request_object_encryption_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
request_object_encryption_enc_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
token_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
token_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
display_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
claim_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
claims_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
service_documentation: z.ZodOptional<z.ZodString>;
claims_locales_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
ui_locales_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
claims_parameter_supported: z.ZodOptional<z.ZodBoolean>;
request_parameter_supported: z.ZodOptional<z.ZodBoolean>;
request_uri_parameter_supported: z.ZodOptional<z.ZodBoolean>;
require_request_uri_registration: z.ZodOptional<z.ZodBoolean>;
op_policy_uri: z.ZodOptional<z.ZodURL>;
op_tos_uri: z.ZodOptional<z.ZodURL>;
client_id_metadata_document_supported: z.ZodOptional<z.ZodBoolean>;
}, z.core.$loose>;
/**
* OpenID Connect Discovery metadata that may include OAuth 2.0 fields
* This schema represents the real-world scenario where OIDC providers
* return a mix of OpenID Connect and OAuth 2.0 metadata fields
*/
export declare const OpenIdProviderDiscoveryMetadataSchema: z.ZodObject<{
code_challenge_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
issuer: z.ZodString;
authorization_endpoint: z.ZodURL;
token_endpoint: z.ZodURL;
userinfo_endpoint: z.ZodOptional<z.ZodURL>;
jwks_uri: z.ZodURL;
registration_endpoint: z.ZodOptional<z.ZodURL>;
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
response_types_supported: z.ZodArray<z.ZodString>;
response_modes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
grant_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
acr_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
subject_types_supported: z.ZodArray<z.ZodString>;
id_token_signing_alg_values_supported: z.ZodArray<z.ZodString>;
id_token_encryption_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
id_token_encryption_enc_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
userinfo_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
userinfo_encryption_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
userinfo_encryption_enc_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
request_object_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
request_object_encryption_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
request_object_encryption_enc_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
token_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
token_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
display_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
claim_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
claims_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
service_documentation: z.ZodOptional<z.ZodString>;
claims_locales_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
ui_locales_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
claims_parameter_supported: z.ZodOptional<z.ZodBoolean>;
request_parameter_supported: z.ZodOptional<z.ZodBoolean>;
request_uri_parameter_supported: z.ZodOptional<z.ZodBoolean>;
require_request_uri_registration: z.ZodOptional<z.ZodBoolean>;
op_policy_uri: z.ZodOptional<z.ZodURL>;
op_tos_uri: z.ZodOptional<z.ZodURL>;
client_id_metadata_document_supported: z.ZodOptional<z.ZodBoolean>;
}, z.core.$strip>;
/**
* OAuth 2.1 token response
*/
export declare const OAuthTokensSchema: z.ZodObject<{
access_token: z.ZodString;
id_token: z.ZodOptional<z.ZodString>;
token_type: z.ZodString;
expires_in: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
scope: z.ZodOptional<z.ZodString>;
refresh_token: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
/**
* OAuth 2.1 error response
*/
export declare const OAuthErrorResponseSchema: z.ZodObject<{
error: z.ZodString;
error_description: z.ZodOptional<z.ZodString>;
error_uri: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
/**
* Optional version of SafeUrlSchema that allows empty string for retrocompatibility on tos_uri and logo_uri
*/
export declare const OptionalSafeUrlSchema: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodPipe<z.ZodLiteral<"">, z.ZodTransform<undefined, "">>]>;
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
*/
export declare const OAuthClientMetadataSchema: z.ZodObject<{
redirect_uris: z.ZodArray<z.ZodURL>;
token_endpoint_auth_method: z.ZodOptional<z.ZodString>;
grant_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
response_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
client_name: z.ZodOptional<z.ZodString>;
client_uri: z.ZodOptional<z.ZodURL>;
logo_uri: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodPipe<z.ZodLiteral<"">, z.ZodTransform<undefined, "">>]>;
scope: z.ZodOptional<z.ZodString>;
contacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
tos_uri: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodPipe<z.ZodLiteral<"">, z.ZodTransform<undefined, "">>]>;
policy_uri: z.ZodOptional<z.ZodString>;
jwks_uri: z.ZodOptional<z.ZodURL>;
jwks: z.ZodOptional<z.ZodAny>;
software_id: z.ZodOptional<z.ZodString>;
software_version: z.ZodOptional<z.ZodString>;
software_statement: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration client information
*/
export declare const OAuthClientInformationSchema: z.ZodObject<{
client_id: z.ZodString;
client_secret: z.ZodOptional<z.ZodString>;
client_id_issued_at: z.ZodOptional<z.ZodNumber>;
client_secret_expires_at: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
*/
export declare const OAuthClientInformationFullSchema: z.ZodObject<{
redirect_uris: z.ZodArray<z.ZodURL>;
token_endpoint_auth_method: z.ZodOptional<z.ZodString>;
grant_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
response_types: z.ZodOptional<z.ZodArray<z.ZodString>>;
client_name: z.ZodOptional<z.ZodString>;
client_uri: z.ZodOptional<z.ZodURL>;
logo_uri: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodPipe<z.ZodLiteral<"">, z.ZodTransform<undefined, "">>]>;
scope: z.ZodOptional<z.ZodString>;
contacts: z.ZodOptional<z.ZodArray<z.ZodString>>;
tos_uri: z.ZodUnion<[z.ZodOptional<z.ZodURL>, z.ZodPipe<z.ZodLiteral<"">, z.ZodTransform<undefined, "">>]>;
policy_uri: z.ZodOptional<z.ZodString>;
jwks_uri: z.ZodOptional<z.ZodURL>;
jwks: z.ZodOptional<z.ZodAny>;
software_id: z.ZodOptional<z.ZodString>;
software_version: z.ZodOptional<z.ZodString>;
software_statement: z.ZodOptional<z.ZodString>;
client_id: z.ZodString;
client_secret: z.ZodOptional<z.ZodString>;
client_id_issued_at: z.ZodOptional<z.ZodNumber>;
client_secret_expires_at: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>;
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration error response
*/
export declare const OAuthClientRegistrationErrorSchema: z.ZodObject<{
error: z.ZodString;
error_description: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
/**
* RFC 7009 OAuth 2.0 Token Revocation request
*/
export declare const OAuthTokenRevocationRequestSchema: z.ZodObject<{
token: z.ZodString;
token_type_hint: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type OAuthMetadata = z.infer<typeof OAuthMetadataSchema>;
export type OpenIdProviderMetadata = z.infer<typeof OpenIdProviderMetadataSchema>;
export type OpenIdProviderDiscoveryMetadata = z.infer<typeof OpenIdProviderDiscoveryMetadataSchema>;
export type OAuthTokens = z.infer<typeof OAuthTokensSchema>;
export type OAuthErrorResponse = z.infer<typeof OAuthErrorResponseSchema>;
export type OAuthClientMetadata = z.infer<typeof OAuthClientMetadataSchema>;
export type OAuthClientInformation = z.infer<typeof OAuthClientInformationSchema>;
export type OAuthClientInformationFull = z.infer<typeof OAuthClientInformationFullSchema>;
export type OAuthClientInformationMixed = OAuthClientInformation | OAuthClientInformationFull;
export type OAuthClientRegistrationError = z.infer<typeof OAuthClientRegistrationErrorSchema>;
export type OAuthTokenRevocationRequest = z.infer<typeof OAuthTokenRevocationRequestSchema>;
export type OAuthProtectedResourceMetadata = z.infer<typeof OAuthProtectedResourceMetadataSchema>;
export type AuthorizationServerMetadata = OAuthMetadata | OpenIdProviderDiscoveryMetadata;
//# sourceMappingURL=auth.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/shared/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,eAAO,MAAM,aAAa,UAmBrB,CAAC;AAEN;;GAEG;AACH,eAAO,MAAM,oCAAoC;;;;;;;;;;;;;;;iBAe/C,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;iBAoB9B,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqCvC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKhD,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;;iBASlB,CAAC;AAEb;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;iBAInC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,qBAAqB,mGAAwE,CAAC;AAE3G;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;iBAmB1B,CAAC;AAEb;;GAEG;AACH,eAAO,MAAM,4BAA4B;;;;;iBAO7B,CAAC;AAEb;;GAEG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;;;iBAAgE,CAAC;AAE9G;;GAEG;AACH,eAAO,MAAM,kCAAkC;;;iBAKnC,CAAC;AAEb;;GAEG;AACH,eAAO,MAAM,iCAAiC;;;iBAKlC,CAAC;AAEb,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,+BAA+B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qCAAqC,CAAC,CAAC;AAEpG,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAC5E,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAC1F,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,GAAG,0BAA0B,CAAC;AAC9F,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAC9F,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oCAAoC,CAAC,CAAC;AAGlG,MAAM,MAAM,2BAA2B,GAAG,aAAa,GAAG,+BAA+B,CAAC"}

View File

@@ -0,0 +1,224 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuthTokenRevocationRequestSchema = exports.OAuthClientRegistrationErrorSchema = exports.OAuthClientInformationFullSchema = exports.OAuthClientInformationSchema = exports.OAuthClientMetadataSchema = exports.OptionalSafeUrlSchema = exports.OAuthErrorResponseSchema = exports.OAuthTokensSchema = exports.OpenIdProviderDiscoveryMetadataSchema = exports.OpenIdProviderMetadataSchema = exports.OAuthMetadataSchema = exports.OAuthProtectedResourceMetadataSchema = exports.SafeUrlSchema = void 0;
const z = __importStar(require("zod/v4"));
/**
* Reusable URL validation that disallows javascript: scheme
*/
exports.SafeUrlSchema = z
.url()
.superRefine((val, ctx) => {
if (!URL.canParse(val)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'URL must be parseable',
fatal: true
});
return z.NEVER;
}
})
.refine(url => {
const u = new URL(url);
return u.protocol !== 'javascript:' && u.protocol !== 'data:' && u.protocol !== 'vbscript:';
}, { message: 'URL cannot use javascript:, data:, or vbscript: scheme' });
/**
* RFC 9728 OAuth Protected Resource Metadata
*/
exports.OAuthProtectedResourceMetadataSchema = z.looseObject({
resource: z.string().url(),
authorization_servers: z.array(exports.SafeUrlSchema).optional(),
jwks_uri: z.string().url().optional(),
scopes_supported: z.array(z.string()).optional(),
bearer_methods_supported: z.array(z.string()).optional(),
resource_signing_alg_values_supported: z.array(z.string()).optional(),
resource_name: z.string().optional(),
resource_documentation: z.string().optional(),
resource_policy_uri: z.string().url().optional(),
resource_tos_uri: z.string().url().optional(),
tls_client_certificate_bound_access_tokens: z.boolean().optional(),
authorization_details_types_supported: z.array(z.string()).optional(),
dpop_signing_alg_values_supported: z.array(z.string()).optional(),
dpop_bound_access_tokens_required: z.boolean().optional()
});
/**
* RFC 8414 OAuth 2.0 Authorization Server Metadata
*/
exports.OAuthMetadataSchema = z.looseObject({
issuer: z.string(),
authorization_endpoint: exports.SafeUrlSchema,
token_endpoint: exports.SafeUrlSchema,
registration_endpoint: exports.SafeUrlSchema.optional(),
scopes_supported: z.array(z.string()).optional(),
response_types_supported: z.array(z.string()),
response_modes_supported: z.array(z.string()).optional(),
grant_types_supported: z.array(z.string()).optional(),
token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
service_documentation: exports.SafeUrlSchema.optional(),
revocation_endpoint: exports.SafeUrlSchema.optional(),
revocation_endpoint_auth_methods_supported: z.array(z.string()).optional(),
revocation_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
introspection_endpoint: z.string().optional(),
introspection_endpoint_auth_methods_supported: z.array(z.string()).optional(),
introspection_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
code_challenge_methods_supported: z.array(z.string()).optional(),
client_id_metadata_document_supported: z.boolean().optional()
});
/**
* OpenID Connect Discovery 1.0 Provider Metadata
* see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
*/
exports.OpenIdProviderMetadataSchema = z.looseObject({
issuer: z.string(),
authorization_endpoint: exports.SafeUrlSchema,
token_endpoint: exports.SafeUrlSchema,
userinfo_endpoint: exports.SafeUrlSchema.optional(),
jwks_uri: exports.SafeUrlSchema,
registration_endpoint: exports.SafeUrlSchema.optional(),
scopes_supported: z.array(z.string()).optional(),
response_types_supported: z.array(z.string()),
response_modes_supported: z.array(z.string()).optional(),
grant_types_supported: z.array(z.string()).optional(),
acr_values_supported: z.array(z.string()).optional(),
subject_types_supported: z.array(z.string()),
id_token_signing_alg_values_supported: z.array(z.string()),
id_token_encryption_alg_values_supported: z.array(z.string()).optional(),
id_token_encryption_enc_values_supported: z.array(z.string()).optional(),
userinfo_signing_alg_values_supported: z.array(z.string()).optional(),
userinfo_encryption_alg_values_supported: z.array(z.string()).optional(),
userinfo_encryption_enc_values_supported: z.array(z.string()).optional(),
request_object_signing_alg_values_supported: z.array(z.string()).optional(),
request_object_encryption_alg_values_supported: z.array(z.string()).optional(),
request_object_encryption_enc_values_supported: z.array(z.string()).optional(),
token_endpoint_auth_methods_supported: z.array(z.string()).optional(),
token_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
display_values_supported: z.array(z.string()).optional(),
claim_types_supported: z.array(z.string()).optional(),
claims_supported: z.array(z.string()).optional(),
service_documentation: z.string().optional(),
claims_locales_supported: z.array(z.string()).optional(),
ui_locales_supported: z.array(z.string()).optional(),
claims_parameter_supported: z.boolean().optional(),
request_parameter_supported: z.boolean().optional(),
request_uri_parameter_supported: z.boolean().optional(),
require_request_uri_registration: z.boolean().optional(),
op_policy_uri: exports.SafeUrlSchema.optional(),
op_tos_uri: exports.SafeUrlSchema.optional(),
client_id_metadata_document_supported: z.boolean().optional()
});
/**
* OpenID Connect Discovery metadata that may include OAuth 2.0 fields
* This schema represents the real-world scenario where OIDC providers
* return a mix of OpenID Connect and OAuth 2.0 metadata fields
*/
exports.OpenIdProviderDiscoveryMetadataSchema = z.object({
...exports.OpenIdProviderMetadataSchema.shape,
...exports.OAuthMetadataSchema.pick({
code_challenge_methods_supported: true
}).shape
});
/**
* OAuth 2.1 token response
*/
exports.OAuthTokensSchema = z
.object({
access_token: z.string(),
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
token_type: z.string(),
expires_in: z.coerce.number().optional(),
scope: z.string().optional(),
refresh_token: z.string().optional()
})
.strip();
/**
* OAuth 2.1 error response
*/
exports.OAuthErrorResponseSchema = z.object({
error: z.string(),
error_description: z.string().optional(),
error_uri: z.string().optional()
});
/**
* Optional version of SafeUrlSchema that allows empty string for retrocompatibility on tos_uri and logo_uri
*/
exports.OptionalSafeUrlSchema = exports.SafeUrlSchema.optional().or(z.literal('').transform(() => undefined));
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
*/
exports.OAuthClientMetadataSchema = z
.object({
redirect_uris: z.array(exports.SafeUrlSchema),
token_endpoint_auth_method: z.string().optional(),
grant_types: z.array(z.string()).optional(),
response_types: z.array(z.string()).optional(),
client_name: z.string().optional(),
client_uri: exports.SafeUrlSchema.optional(),
logo_uri: exports.OptionalSafeUrlSchema,
scope: z.string().optional(),
contacts: z.array(z.string()).optional(),
tos_uri: exports.OptionalSafeUrlSchema,
policy_uri: z.string().optional(),
jwks_uri: exports.SafeUrlSchema.optional(),
jwks: z.any().optional(),
software_id: z.string().optional(),
software_version: z.string().optional(),
software_statement: z.string().optional()
})
.strip();
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration client information
*/
exports.OAuthClientInformationSchema = z
.object({
client_id: z.string(),
client_secret: z.string().optional(),
client_id_issued_at: z.number().optional(),
client_secret_expires_at: z.number().optional()
})
.strip();
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
*/
exports.OAuthClientInformationFullSchema = exports.OAuthClientMetadataSchema.merge(exports.OAuthClientInformationSchema);
/**
* RFC 7591 OAuth 2.0 Dynamic Client Registration error response
*/
exports.OAuthClientRegistrationErrorSchema = z
.object({
error: z.string(),
error_description: z.string().optional()
})
.strip();
/**
* RFC 7009 OAuth 2.0 Token Revocation request
*/
exports.OAuthTokenRevocationRequestSchema = z
.object({
token: z.string(),
token_type_hint: z.string().optional()
})
.strip();
//# sourceMappingURL=auth.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import { BaseMetadata } from '../types.js';
/**
* Utilities for working with BaseMetadata objects.
*/
/**
* Gets the display name for an object with BaseMetadata.
* For tools, the precedence is: title → annotations.title → name
* For other objects: title → name
* This implements the spec requirement: "if no title is provided, name should be used for display purposes"
*/
export declare function getDisplayName(metadata: BaseMetadata | (BaseMetadata & {
annotations?: {
title?: string;
};
})): string;
//# sourceMappingURL=metadataUtils.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadataUtils.d.ts","sourceRoot":"","sources":["../../../src/shared/metadataUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,YAAY,GAAG,CAAC,YAAY,GAAG;IAAE,WAAW,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,GAAG,MAAM,CAarH"}

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDisplayName = getDisplayName;
/**
* Utilities for working with BaseMetadata objects.
*/
/**
* Gets the display name for an object with BaseMetadata.
* For tools, the precedence is: title → annotations.title → name
* For other objects: title → name
* This implements the spec requirement: "if no title is provided, name should be used for display purposes"
*/
function getDisplayName(metadata) {
// First check for title (not undefined and not empty string)
if (metadata.title !== undefined && metadata.title !== '') {
return metadata.title;
}
// Then check for annotations.title (only present in Tool objects)
if ('annotations' in metadata && metadata.annotations?.title) {
return metadata.annotations.title;
}
// Finally fall back to name
return metadata.name;
}
//# sourceMappingURL=metadataUtils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadataUtils.js","sourceRoot":"","sources":["../../../src/shared/metadataUtils.ts"],"names":[],"mappings":";;AAYA,wCAaC;AAvBD;;GAEG;AAEH;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,QAA8E;IACzG,6DAA6D;IAC7D,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,IAAI,QAAQ,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;QACxD,OAAO,QAAQ,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,kEAAkE;IAClE,IAAI,aAAa,IAAI,QAAQ,IAAI,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;QAC3D,OAAO,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC;IACtC,CAAC;IAED,4BAA4B;IAC5B,OAAO,QAAQ,CAAC,IAAI,CAAC;AACzB,CAAC"}

View File

@@ -0,0 +1,443 @@
import { AnySchema, AnyObjectSchema, SchemaOutput } from '../server/zod-compat.js';
import { ClientCapabilities, GetTaskRequest, GetTaskPayloadRequest, ListTasksResultSchema, CancelTaskResultSchema, JSONRPCRequest, Progress, RequestId, Result, ServerCapabilities, RequestMeta, RequestInfo, GetTaskResult, TaskCreationParams, RelatedTaskMetadata, Task, Request, Notification } from '../types.js';
import { Transport, TransportSendOptions } from './transport.js';
import { AuthInfo } from '../server/auth/types.js';
import { TaskStore, TaskMessageQueue, CreateTaskOptions } from '../experimental/tasks/interfaces.js';
import { ResponseMessage } from './responseMessage.js';
/**
* Callback for progress notifications.
*/
export type ProgressCallback = (progress: Progress) => void;
/**
* Additional initialization options.
*/
export type ProtocolOptions = {
/**
* Whether to restrict emitted requests to only those that the remote side has indicated that they can handle, through their advertised capabilities.
*
* Note that this DOES NOT affect checking of _local_ side capabilities, as it is considered a logic error to mis-specify those.
*
* Currently this defaults to false, for backwards compatibility with SDK versions that did not advertise capabilities correctly. In future, this will default to true.
*/
enforceStrictCapabilities?: boolean;
/**
* An array of notification method names that should be automatically debounced.
* Any notifications with a method in this list will be coalesced if they
* occur in the same tick of the event loop.
* e.g., ['notifications/tools/list_changed']
*/
debouncedNotificationMethods?: string[];
/**
* Optional task storage implementation. If provided, enables task-related request handlers
* and provides task storage capabilities to request handlers.
*/
taskStore?: TaskStore;
/**
* Optional task message queue implementation for managing server-initiated messages
* that will be delivered through the tasks/result response stream.
*/
taskMessageQueue?: TaskMessageQueue;
/**
* Default polling interval (in milliseconds) for task status checks when no pollInterval
* is provided by the server. Defaults to 5000ms if not specified.
*/
defaultTaskPollInterval?: number;
/**
* Maximum number of messages that can be queued per task for side-channel delivery.
* If undefined, the queue size is unbounded.
* When the limit is exceeded, the TaskMessageQueue implementation's enqueue() method
* will throw an error. It's the implementation's responsibility to handle overflow
* appropriately (e.g., by failing the task, dropping messages, etc.).
*/
maxTaskQueueSize?: number;
};
/**
* The default request timeout, in miliseconds.
*/
export declare const DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
/**
* Options that can be given per request.
*/
export type RequestOptions = {
/**
* If set, requests progress notifications from the remote end (if supported). When progress notifications are received, this callback will be invoked.
*
* For task-augmented requests: progress notifications continue after CreateTaskResult is returned and stop automatically when the task reaches a terminal status.
*/
onprogress?: ProgressCallback;
/**
* Can be used to cancel an in-flight request. This will cause an AbortError to be raised from request().
*/
signal?: AbortSignal;
/**
* A timeout (in milliseconds) for this request. If exceeded, an McpError with code `RequestTimeout` will be raised from request().
*
* If not specified, `DEFAULT_REQUEST_TIMEOUT_MSEC` will be used as the timeout.
*/
timeout?: number;
/**
* If true, receiving a progress notification will reset the request timeout.
* This is useful for long-running operations that send periodic progress updates.
* Default: false
*/
resetTimeoutOnProgress?: boolean;
/**
* Maximum total time (in milliseconds) to wait for a response.
* If exceeded, an McpError with code `RequestTimeout` will be raised, regardless of progress notifications.
* If not specified, there is no maximum total timeout.
*/
maxTotalTimeout?: number;
/**
* If provided, augments the request with task creation parameters to enable call-now, fetch-later execution patterns.
*/
task?: TaskCreationParams;
/**
* If provided, associates this request with a related task.
*/
relatedTask?: RelatedTaskMetadata;
} & TransportSendOptions;
/**
* Options that can be given per notification.
*/
export type NotificationOptions = {
/**
* May be used to indicate to the transport which incoming request to associate this outgoing notification with.
*/
relatedRequestId?: RequestId;
/**
* If provided, associates this notification with a related task.
*/
relatedTask?: RelatedTaskMetadata;
};
/**
* Options that can be given per request.
*/
export type TaskRequestOptions = Omit<RequestOptions, 'relatedTask'>;
/**
* Request-scoped TaskStore interface.
*/
export interface RequestTaskStore {
/**
* Creates a new task with the given creation parameters.
* The implementation generates a unique taskId and createdAt timestamp.
*
* @param taskParams - The task creation parameters from the request
* @returns The created task object
*/
createTask(taskParams: CreateTaskOptions): Promise<Task>;
/**
* Gets the current status of a task.
*
* @param taskId - The task identifier
* @returns The task object
* @throws If the task does not exist
*/
getTask(taskId: string): Promise<Task>;
/**
* Stores the result of a task and sets its final status.
*
* @param taskId - The task identifier
* @param status - The final status: 'completed' for success, 'failed' for errors
* @param result - The result to store
*/
storeTaskResult(taskId: string, status: 'completed' | 'failed', result: Result): Promise<void>;
/**
* Retrieves the stored result of a task.
*
* @param taskId - The task identifier
* @returns The stored result
*/
getTaskResult(taskId: string): Promise<Result>;
/**
* Updates a task's status (e.g., to 'cancelled', 'failed', 'completed').
*
* @param taskId - The task identifier
* @param status - The new status
* @param statusMessage - Optional diagnostic message for failed tasks or other status information
*/
updateTaskStatus(taskId: string, status: Task['status'], statusMessage?: string): Promise<void>;
/**
* Lists tasks, optionally starting from a pagination cursor.
*
* @param cursor - Optional cursor for pagination
* @returns An object containing the tasks array and an optional nextCursor
*/
listTasks(cursor?: string): Promise<{
tasks: Task[];
nextCursor?: string;
}>;
}
/**
* Extra data given to request handlers.
*/
export type RequestHandlerExtra<SendRequestT extends Request, SendNotificationT extends Notification> = {
/**
* An abort signal used to communicate if the request was cancelled from the sender's side.
*/
signal: AbortSignal;
/**
* Information about a validated access token, provided to request handlers.
*/
authInfo?: AuthInfo;
/**
* The session ID from the transport, if available.
*/
sessionId?: string;
/**
* Metadata from the original request.
*/
_meta?: RequestMeta;
/**
* The JSON-RPC ID of the request being handled.
* This can be useful for tracking or logging purposes.
*/
requestId: RequestId;
taskId?: string;
taskStore?: RequestTaskStore;
taskRequestedTtl?: number | null;
/**
* The original HTTP request.
*/
requestInfo?: RequestInfo;
/**
* Sends a notification that relates to the current request being handled.
*
* This is used by certain transports to correctly associate related messages.
*/
sendNotification: (notification: SendNotificationT) => Promise<void>;
/**
* Sends a request that relates to the current request being handled.
*
* This is used by certain transports to correctly associate related messages.
*/
sendRequest: <U extends AnySchema>(request: SendRequestT, resultSchema: U, options?: TaskRequestOptions) => Promise<SchemaOutput<U>>;
/**
* Closes the SSE stream for this request, triggering client reconnection.
* Only available when using StreamableHTTPServerTransport with eventStore configured.
* Use this to implement polling behavior during long-running operations.
*/
closeSSEStream?: () => void;
/**
* Closes the standalone GET SSE stream, triggering client reconnection.
* Only available when using StreamableHTTPServerTransport with eventStore configured.
* Use this to implement polling behavior for server-initiated notifications.
*/
closeStandaloneSSEStream?: () => void;
};
/**
* Implements MCP protocol framing on top of a pluggable transport, including
* features like request/response linking, notifications, and progress.
*/
export declare abstract class Protocol<SendRequestT extends Request, SendNotificationT extends Notification, SendResultT extends Result> {
private _options?;
private _transport?;
private _requestMessageId;
private _requestHandlers;
private _requestHandlerAbortControllers;
private _notificationHandlers;
private _responseHandlers;
private _progressHandlers;
private _timeoutInfo;
private _pendingDebouncedNotifications;
private _taskProgressTokens;
private _taskStore?;
private _taskMessageQueue?;
private _requestResolvers;
/**
* Callback for when the connection is closed for any reason.
*
* This is invoked when close() is called as well.
*/
onclose?: () => void;
/**
* Callback for when an error occurs.
*
* Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band.
*/
onerror?: (error: Error) => void;
/**
* A handler to invoke for any request types that do not have their own handler installed.
*/
fallbackRequestHandler?: (request: JSONRPCRequest, extra: RequestHandlerExtra<SendRequestT, SendNotificationT>) => Promise<SendResultT>;
/**
* A handler to invoke for any notification types that do not have their own handler installed.
*/
fallbackNotificationHandler?: (notification: Notification) => Promise<void>;
constructor(_options?: ProtocolOptions | undefined);
private _oncancel;
private _setupTimeout;
private _resetTimeout;
private _cleanupTimeout;
/**
* Attaches to the given transport, starts it, and starts listening for messages.
*
* The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
*/
connect(transport: Transport): Promise<void>;
private _onclose;
private _onerror;
private _onnotification;
private _onrequest;
private _onprogress;
private _onresponse;
get transport(): Transport | undefined;
/**
* Closes the connection.
*/
close(): Promise<void>;
/**
* A method to check if a capability is supported by the remote side, for the given method to be called.
*
* This should be implemented by subclasses.
*/
protected abstract assertCapabilityForMethod(method: SendRequestT['method']): void;
/**
* A method to check if a notification is supported by the local side, for the given method to be sent.
*
* This should be implemented by subclasses.
*/
protected abstract assertNotificationCapability(method: SendNotificationT['method']): void;
/**
* A method to check if a request handler is supported by the local side, for the given method to be handled.
*
* This should be implemented by subclasses.
*/
protected abstract assertRequestHandlerCapability(method: string): void;
/**
* A method to check if task creation is supported for the given request method.
*
* This should be implemented by subclasses.
*/
protected abstract assertTaskCapability(method: string): void;
/**
* A method to check if task handler is supported by the local side, for the given method to be handled.
*
* This should be implemented by subclasses.
*/
protected abstract assertTaskHandlerCapability(method: string): void;
/**
* Sends a request and returns an AsyncGenerator that yields response messages.
* The generator is guaranteed to end with either a 'result' or 'error' message.
*
* @example
* ```typescript
* const stream = protocol.requestStream(request, resultSchema, options);
* for await (const message of stream) {
* switch (message.type) {
* case 'taskCreated':
* console.log('Task created:', message.task.taskId);
* break;
* case 'taskStatus':
* console.log('Task status:', message.task.status);
* break;
* case 'result':
* console.log('Final result:', message.result);
* break;
* case 'error':
* console.error('Error:', message.error);
* break;
* }
* }
* ```
*
* @experimental Use `client.experimental.tasks.requestStream()` to access this method.
*/
protected requestStream<T extends AnySchema>(request: SendRequestT, resultSchema: T, options?: RequestOptions): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void>;
/**
* Sends a request and waits for a response.
*
* Do not use this method to emit notifications! Use notification() instead.
*/
request<T extends AnySchema>(request: SendRequestT, resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>>;
/**
* Gets the current status of a task.
*
* @experimental Use `client.experimental.tasks.getTask()` to access this method.
*/
protected getTask(params: GetTaskRequest['params'], options?: RequestOptions): Promise<GetTaskResult>;
/**
* Retrieves the result of a completed task.
*
* @experimental Use `client.experimental.tasks.getTaskResult()` to access this method.
*/
protected getTaskResult<T extends AnySchema>(params: GetTaskPayloadRequest['params'], resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>>;
/**
* Lists tasks, optionally starting from a pagination cursor.
*
* @experimental Use `client.experimental.tasks.listTasks()` to access this method.
*/
protected listTasks(params?: {
cursor?: string;
}, options?: RequestOptions): Promise<SchemaOutput<typeof ListTasksResultSchema>>;
/**
* Cancels a specific task.
*
* @experimental Use `client.experimental.tasks.cancelTask()` to access this method.
*/
protected cancelTask(params: {
taskId: string;
}, options?: RequestOptions): Promise<SchemaOutput<typeof CancelTaskResultSchema>>;
/**
* Emits a notification, which is a one-way message that does not expect a response.
*/
notification(notification: SendNotificationT, options?: NotificationOptions): Promise<void>;
/**
* Registers a handler to invoke when this protocol object receives a request with the given method.
*
* Note that this will replace any previous request handler for the same method.
*/
setRequestHandler<T extends AnyObjectSchema>(requestSchema: T, handler: (request: SchemaOutput<T>, extra: RequestHandlerExtra<SendRequestT, SendNotificationT>) => SendResultT | Promise<SendResultT>): void;
/**
* Removes the request handler for the given method.
*/
removeRequestHandler(method: string): void;
/**
* Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
*/
assertCanSetRequestHandler(method: string): void;
/**
* Registers a handler to invoke when this protocol object receives a notification with the given method.
*
* Note that this will replace any previous notification handler for the same method.
*/
setNotificationHandler<T extends AnyObjectSchema>(notificationSchema: T, handler: (notification: SchemaOutput<T>) => void | Promise<void>): void;
/**
* Removes the notification handler for the given method.
*/
removeNotificationHandler(method: string): void;
/**
* Cleans up the progress handler associated with a task.
* This should be called when a task reaches a terminal status.
*/
private _cleanupTaskProgressHandler;
/**
* Enqueues a task-related message for side-channel delivery via tasks/result.
* @param taskId The task ID to associate the message with
* @param message The message to enqueue
* @param sessionId Optional session ID for binding the operation to a specific session
* @throws Error if taskStore is not configured or if enqueue fails (e.g., queue overflow)
*
* Note: If enqueue fails, it's the TaskMessageQueue implementation's responsibility to handle
* the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer
* simply propagates the error.
*/
private _enqueueTaskMessage;
/**
* Clears the message queue for a task and rejects any pending request resolvers.
* @param taskId The task ID whose queue should be cleared
* @param sessionId Optional session ID for binding the operation to a specific session
*/
private _clearTaskQueue;
/**
* Waits for a task update (new messages or status change) with abort signal support.
* Uses polling to check for updates at the task's configured poll interval.
* @param taskId The task ID to wait for
* @param signal Abort signal to cancel the wait
* @returns Promise that resolves when an update occurs or rejects if aborted
*/
private _waitForTaskUpdate;
private requestTaskStore;
}
export declare function mergeCapabilities(base: ServerCapabilities, additional: Partial<ServerCapabilities>): ServerCapabilities;
export declare function mergeCapabilities(base: ClientCapabilities, additional: Partial<ClientCapabilities>): ClientCapabilities;
//# sourceMappingURL=protocol.d.ts.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,45 @@
import { Result, Task, McpError } from '../types.js';
/**
* Base message type
*/
export interface BaseResponseMessage {
type: string;
}
/**
* Task status update message
*/
export interface TaskStatusMessage extends BaseResponseMessage {
type: 'taskStatus';
task: Task;
}
/**
* Task created message (first message for task-augmented requests)
*/
export interface TaskCreatedMessage extends BaseResponseMessage {
type: 'taskCreated';
task: Task;
}
/**
* Final result message (terminal)
*/
export interface ResultMessage<T extends Result> extends BaseResponseMessage {
type: 'result';
result: T;
}
/**
* Error message (terminal)
*/
export interface ErrorMessage extends BaseResponseMessage {
type: 'error';
error: McpError;
}
/**
* Union type representing all possible messages that can be yielded during request processing.
* Note: Progress notifications are handled through the existing onprogress callback mechanism.
* Side-channeled messages (server requests/notifications) are handled through registered handlers.
*/
export type ResponseMessage<T extends Result> = TaskStatusMessage | TaskCreatedMessage | ResultMessage<T> | ErrorMessage;
export type AsyncGeneratorValue<T> = T extends AsyncGenerator<infer U> ? U : never;
export declare function toArrayAsync<T extends AsyncGenerator<unknown>>(it: T): Promise<AsyncGeneratorValue<T>[]>;
export declare function takeResult<T extends Result, U extends AsyncGenerator<ResponseMessage<T>>>(it: U): Promise<T>;
//# sourceMappingURL=responseMessage.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"responseMessage.d.ts","sourceRoot":"","sources":["../../../src/shared/responseMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC1D,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC3D,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,CAAE,SAAQ,mBAAmB;IACxE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,CAAC,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,mBAAmB;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,QAAQ,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,IAAI,iBAAiB,GAAG,kBAAkB,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;AAEzH,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AAEnF,wBAAsB,YAAY,CAAC,CAAC,SAAS,cAAc,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC,CAO9G;AAED,wBAAsB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAUlH"}

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toArrayAsync = toArrayAsync;
exports.takeResult = takeResult;
async function toArrayAsync(it) {
const arr = [];
for await (const o of it) {
arr.push(o);
}
return arr;
}
async function takeResult(it) {
for await (const o of it) {
if (o.type === 'result') {
return o.result;
}
else if (o.type === 'error') {
throw o.error;
}
}
throw new Error('No result in stream.');
}
//# sourceMappingURL=responseMessage.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"responseMessage.js","sourceRoot":"","sources":["../../../src/shared/responseMessage.ts"],"names":[],"mappings":";;AAkDA,oCAOC;AAED,gCAUC;AAnBM,KAAK,UAAU,YAAY,CAAoC,EAAK;IACvE,MAAM,GAAG,GAA6B,EAAE,CAAC;IACzC,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvB,GAAG,CAAC,IAAI,CAAC,CAA2B,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,GAAG,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,UAAU,CAAiE,EAAK;IAClG,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,CAAC,CAAC,MAAM,CAAC;QACpB,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,CAAC,CAAC,KAAK,CAAC;QAClB,CAAC;IACL,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;AAC5C,CAAC"}

View File

@@ -0,0 +1,13 @@
import { JSONRPCMessage } from '../types.js';
/**
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
*/
export declare class ReadBuffer {
private _buffer?;
append(chunk: Buffer): void;
readMessage(): JSONRPCMessage | null;
clear(): void;
}
export declare function deserializeMessage(line: string): JSONRPCMessage;
export declare function serializeMessage(message: JSONRPCMessage): string;
//# sourceMappingURL=stdio.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../../src/shared/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwB,MAAM,aAAa,CAAC;AAEnE;;GAEG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3B,WAAW,IAAI,cAAc,GAAG,IAAI;IAepC,KAAK,IAAI,IAAI;CAGhB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAE/D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAEhE"}

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReadBuffer = void 0;
exports.deserializeMessage = deserializeMessage;
exports.serializeMessage = serializeMessage;
const types_js_1 = require("../types.js");
/**
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
*/
class ReadBuffer {
append(chunk) {
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
}
readMessage() {
if (!this._buffer) {
return null;
}
const index = this._buffer.indexOf('\n');
if (index === -1) {
return null;
}
const line = this._buffer.toString('utf8', 0, index).replace(/\r$/, '');
this._buffer = this._buffer.subarray(index + 1);
return deserializeMessage(line);
}
clear() {
this._buffer = undefined;
}
}
exports.ReadBuffer = ReadBuffer;
function deserializeMessage(line) {
return types_js_1.JSONRPCMessageSchema.parse(JSON.parse(line));
}
function serializeMessage(message) {
return JSON.stringify(message) + '\n';
}
//# sourceMappingURL=stdio.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../../src/shared/stdio.ts"],"names":[],"mappings":";;;AAgCA,gDAEC;AAED,4CAEC;AAtCD,0CAAmE;AAEnE;;GAEG;AACH,MAAa,UAAU;IAGnB,MAAM,CAAC,KAAa;QAChB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/E,CAAC;IAED,WAAW;QACP,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAChD,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,KAAK;QACD,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;IAC7B,CAAC;CACJ;AAzBD,gCAyBC;AAED,SAAgB,kBAAkB,CAAC,IAAY;IAC3C,OAAO,+BAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAuB;IACpD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;AAC1C,CAAC"}

View File

@@ -0,0 +1,31 @@
/**
* Tool name validation utilities according to SEP: Specify Format for Tool Names
*
* Tool names SHOULD be between 1 and 128 characters in length (inclusive).
* Tool names are case-sensitive.
* Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits
* (0-9), underscore (_), dash (-), and dot (.).
* Tool names SHOULD NOT contain spaces, commas, or other special characters.
*/
/**
* Validates a tool name according to the SEP specification
* @param name - The tool name to validate
* @returns An object containing validation result and any warnings
*/
export declare function validateToolName(name: string): {
isValid: boolean;
warnings: string[];
};
/**
* Issues warnings for non-conforming tool names
* @param name - The tool name that triggered the warnings
* @param warnings - Array of warning messages
*/
export declare function issueToolNameWarning(name: string, warnings: string[]): void;
/**
* Validates a tool name and issues warnings for non-conforming names
* @param name - The tool name to validate
* @returns true if the name is valid, false otherwise
*/
export declare function validateAndWarnToolName(name: string): boolean;
//# sourceMappingURL=toolNameValidation.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"toolNameValidation.d.ts","sourceRoot":"","sources":["../../../src/shared/toolNameValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB,CA0DA;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAY3E;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAO7D"}

View File

@@ -0,0 +1,97 @@
"use strict";
/**
* Tool name validation utilities according to SEP: Specify Format for Tool Names
*
* Tool names SHOULD be between 1 and 128 characters in length (inclusive).
* Tool names are case-sensitive.
* Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits
* (0-9), underscore (_), dash (-), and dot (.).
* Tool names SHOULD NOT contain spaces, commas, or other special characters.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateToolName = validateToolName;
exports.issueToolNameWarning = issueToolNameWarning;
exports.validateAndWarnToolName = validateAndWarnToolName;
/**
* Regular expression for valid tool names according to SEP-986 specification
*/
const TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
/**
* Validates a tool name according to the SEP specification
* @param name - The tool name to validate
* @returns An object containing validation result and any warnings
*/
function validateToolName(name) {
const warnings = [];
// Check length
if (name.length === 0) {
return {
isValid: false,
warnings: ['Tool name cannot be empty']
};
}
if (name.length > 128) {
return {
isValid: false,
warnings: [`Tool name exceeds maximum length of 128 characters (current: ${name.length})`]
};
}
// Check for specific problematic patterns (these are warnings, not validation failures)
if (name.includes(' ')) {
warnings.push('Tool name contains spaces, which may cause parsing issues');
}
if (name.includes(',')) {
warnings.push('Tool name contains commas, which may cause parsing issues');
}
// Check for potentially confusing patterns (leading/trailing dashes, dots, slashes)
if (name.startsWith('-') || name.endsWith('-')) {
warnings.push('Tool name starts or ends with a dash, which may cause parsing issues in some contexts');
}
if (name.startsWith('.') || name.endsWith('.')) {
warnings.push('Tool name starts or ends with a dot, which may cause parsing issues in some contexts');
}
// Check for invalid characters
if (!TOOL_NAME_REGEX.test(name)) {
const invalidChars = name
.split('')
.filter(char => !/[A-Za-z0-9._-]/.test(char))
.filter((char, index, arr) => arr.indexOf(char) === index); // Remove duplicates
warnings.push(`Tool name contains invalid characters: ${invalidChars.map(c => `"${c}"`).join(', ')}`, 'Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)');
return {
isValid: false,
warnings
};
}
return {
isValid: true,
warnings
};
}
/**
* Issues warnings for non-conforming tool names
* @param name - The tool name that triggered the warnings
* @param warnings - Array of warning messages
*/
function issueToolNameWarning(name, warnings) {
if (warnings.length > 0) {
console.warn(`Tool name validation warning for "${name}":`);
for (const warning of warnings) {
console.warn(` - ${warning}`);
}
console.warn('Tool registration will proceed, but this may cause compatibility issues.');
console.warn('Consider updating the tool name to conform to the MCP tool naming standard.');
console.warn('See SEP: Specify Format for Tool Names (https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986) for more details.');
}
}
/**
* Validates a tool name and issues warnings for non-conforming names
* @param name - The tool name to validate
* @returns true if the name is valid, false otherwise
*/
function validateAndWarnToolName(name) {
const result = validateToolName(name);
// Always issue warnings for any validation issues (both invalid names and warnings)
issueToolNameWarning(name, result.warnings);
return result.isValid;
}
//# sourceMappingURL=toolNameValidation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"toolNameValidation.js","sourceRoot":"","sources":["../../../src/shared/toolNameValidation.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAYH,4CA6DC;AAOD,oDAYC;AAOD,0DAOC;AAxGD;;GAEG;AACH,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAElD;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,IAAY;IAIzC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,eAAe;IACf,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO;YACH,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,CAAC,2BAA2B,CAAC;SAC1C,CAAC;IACN,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACpB,OAAO;YACH,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,CAAC,gEAAgE,IAAI,CAAC,MAAM,GAAG,CAAC;SAC7F,CAAC;IACN,CAAC;IAED,wFAAwF;IACxF,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IAC/E,CAAC;IAED,oFAAoF;IACpF,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,QAAQ,CAAC,IAAI,CAAC,uFAAuF,CAAC,CAAC;IAC3G,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,QAAQ,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IAC1G,CAAC;IAED,+BAA+B;IAC/B,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,IAAI;aACpB,KAAK,CAAC,EAAE,CAAC;aACT,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aAC5C,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,oBAAoB;QAEpF,QAAQ,CAAC,IAAI,CACT,0CAA0C,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EACtF,8EAA8E,CACjF,CAAC;QAEF,OAAO;YACH,OAAO,EAAE,KAAK;YACd,QAAQ;SACX,CAAC;IACN,CAAC;IAED,OAAO;QACH,OAAO,EAAE,IAAI;QACb,QAAQ;KACX,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,IAAY,EAAE,QAAkB;IACjE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,qCAAqC,IAAI,IAAI,CAAC,CAAC;QAC5D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,OAAO,OAAO,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,6EAA6E,CAAC,CAAC;QAC5F,OAAO,CAAC,IAAI,CACR,oIAAoI,CACvI,CAAC;IACN,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CAAC,IAAY;IAChD,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAEtC,oFAAoF;IACpF,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE5C,OAAO,MAAM,CAAC,OAAO,CAAC;AAC1B,CAAC"}

View File

@@ -0,0 +1,89 @@
import { JSONRPCMessage, MessageExtraInfo, RequestId } from '../types.js';
export type FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;
/**
* Normalizes HeadersInit to a plain Record<string, string> for manipulation.
* Handles Headers objects, arrays of tuples, and plain objects.
*/
export declare function normalizeHeaders(headers: HeadersInit | undefined): Record<string, string>;
/**
* Creates a fetch function that includes base RequestInit options.
* This ensures requests inherit settings like credentials, mode, headers, etc. from the base init.
*
* @param baseFetch - The base fetch function to wrap (defaults to global fetch)
* @param baseInit - The base RequestInit to merge with each request
* @returns A wrapped fetch function that merges base options with call-specific options
*/
export declare function createFetchWithInit(baseFetch?: FetchLike, baseInit?: RequestInit): FetchLike;
/**
* Options for sending a JSON-RPC message.
*/
export type TransportSendOptions = {
/**
* If present, `relatedRequestId` is used to indicate to the transport which incoming request to associate this outgoing message with.
*/
relatedRequestId?: RequestId;
/**
* The resumption token used to continue long-running requests that were interrupted.
*
* This allows clients to reconnect and continue from where they left off, if supported by the transport.
*/
resumptionToken?: string;
/**
* A callback that is invoked when the resumption token changes, if supported by the transport.
*
* This allows clients to persist the latest token for potential reconnection.
*/
onresumptiontoken?: (token: string) => void;
};
/**
* Describes the minimal contract for an MCP transport that a client or server can communicate over.
*/
export interface Transport {
/**
* Starts processing messages on the transport, including any connection steps that might need to be taken.
*
* This method should only be called after callbacks are installed, or else messages may be lost.
*
* NOTE: This method should not be called explicitly when using Client, Server, or Protocol classes, as they will implicitly call start().
*/
start(): Promise<void>;
/**
* Sends a JSON-RPC message (request or response).
*
* If present, `relatedRequestId` is used to indicate to the transport which incoming request to associate this outgoing message with.
*/
send(message: JSONRPCMessage, options?: TransportSendOptions): Promise<void>;
/**
* Closes the connection.
*/
close(): Promise<void>;
/**
* Callback for when the connection is closed for any reason.
*
* This should be invoked when close() is called as well.
*/
onclose?: () => void;
/**
* Callback for when an error occurs.
*
* Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band.
*/
onerror?: (error: Error) => void;
/**
* Callback for when a message (request or response) is received over the connection.
*
* Includes the requestInfo and authInfo if the transport is authenticated.
*
* The requestInfo can be used to get the original request information (headers, etc.)
*/
onmessage?: <T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void;
/**
* The session ID generated for this connection.
*/
sessionId?: string;
/**
* Sets the protocol version used for the connection (called when the initialize response is received).
*/
setProtocolVersion?: (version: string) => void;
}
//# sourceMappingURL=transport.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/shared/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE1E,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAErF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYzF;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,SAAiB,EAAE,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAenG;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAC/B;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C,CAAC;AACF;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB;;;;;;OAMG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7E;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,SAAS,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAErF;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClD"}

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.normalizeHeaders = normalizeHeaders;
exports.createFetchWithInit = createFetchWithInit;
/**
* Normalizes HeadersInit to a plain Record<string, string> for manipulation.
* Handles Headers objects, arrays of tuples, and plain objects.
*/
function normalizeHeaders(headers) {
if (!headers)
return {};
if (headers instanceof Headers) {
return Object.fromEntries(headers.entries());
}
if (Array.isArray(headers)) {
return Object.fromEntries(headers);
}
return { ...headers };
}
/**
* Creates a fetch function that includes base RequestInit options.
* This ensures requests inherit settings like credentials, mode, headers, etc. from the base init.
*
* @param baseFetch - The base fetch function to wrap (defaults to global fetch)
* @param baseInit - The base RequestInit to merge with each request
* @returns A wrapped fetch function that merges base options with call-specific options
*/
function createFetchWithInit(baseFetch = fetch, baseInit) {
if (!baseInit) {
return baseFetch;
}
// Return a wrapped fetch that merges base RequestInit with call-specific init
return async (url, init) => {
const mergedInit = {
...baseInit,
...init,
// Headers need special handling - merge instead of replace
headers: init?.headers ? { ...normalizeHeaders(baseInit.headers), ...normalizeHeaders(init.headers) } : baseInit.headers
};
return baseFetch(url, mergedInit);
};
}
//# sourceMappingURL=transport.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../src/shared/transport.ts"],"names":[],"mappings":";;AAQA,4CAYC;AAUD,kDAeC;AAzCD;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,OAAgC;IAC7D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,IAAI,OAAO,YAAY,OAAO,EAAE,CAAC;QAC7B,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,EAAE,GAAI,OAAkC,EAAE,CAAC;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,mBAAmB,CAAC,YAAuB,KAAK,EAAE,QAAsB;IACpF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,8EAA8E;IAC9E,OAAO,KAAK,EAAE,GAAiB,EAAE,IAAkB,EAAqB,EAAE;QACtE,MAAM,UAAU,GAAgB;YAC5B,GAAG,QAAQ;YACX,GAAG,IAAI;YACP,2DAA2D;YAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO;SAC3H,CAAC;QACF,OAAO,SAAS,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC;AACN,CAAC"}

View File

@@ -0,0 +1,25 @@
export type Variables = Record<string, string | string[]>;
export declare class UriTemplate {
/**
* Returns true if the given string contains any URI template expressions.
* A template expression is a sequence of characters enclosed in curly braces,
* like {foo} or {?bar}.
*/
static isTemplate(str: string): boolean;
private static validateLength;
private readonly template;
private readonly parts;
get variableNames(): string[];
constructor(template: string);
toString(): string;
private parse;
private getOperator;
private getNames;
private encodeValue;
private expandPart;
expand(variables: Variables): string;
private escapeRegExp;
private partToRegExp;
match(uri: string): Variables | null;
}
//# sourceMappingURL=uriTemplate.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"uriTemplate.d.ts","sourceRoot":"","sources":["../../../src/shared/uriTemplate.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AAO1D,qBAAa,WAAW;IACpB;;;;OAIG;IACH,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAMvC,OAAO,CAAC,MAAM,CAAC,cAAc;IAK7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyF;IAE/G,IAAI,aAAa,IAAI,MAAM,EAAE,CAE5B;gBAEW,QAAQ,EAAE,MAAM;IAM5B,QAAQ,IAAI,MAAM;IAIlB,OAAO,CAAC,KAAK;IA8Cb,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,UAAU;IAsDlB,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM;IA4BpC,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;IAkDpB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;CAuCvC"}

View File

@@ -0,0 +1,243 @@
"use strict";
// Claude-authored implementation of RFC 6570 URI Templates
Object.defineProperty(exports, "__esModule", { value: true });
exports.UriTemplate = void 0;
const MAX_TEMPLATE_LENGTH = 1000000; // 1MB
const MAX_VARIABLE_LENGTH = 1000000; // 1MB
const MAX_TEMPLATE_EXPRESSIONS = 10000;
const MAX_REGEX_LENGTH = 1000000; // 1MB
class UriTemplate {
/**
* Returns true if the given string contains any URI template expressions.
* A template expression is a sequence of characters enclosed in curly braces,
* like {foo} or {?bar}.
*/
static isTemplate(str) {
// Look for any sequence of characters between curly braces
// that isn't just whitespace
return /\{[^}\s]+\}/.test(str);
}
static validateLength(str, max, context) {
if (str.length > max) {
throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`);
}
}
get variableNames() {
return this.parts.flatMap(part => (typeof part === 'string' ? [] : part.names));
}
constructor(template) {
UriTemplate.validateLength(template, MAX_TEMPLATE_LENGTH, 'Template');
this.template = template;
this.parts = this.parse(template);
}
toString() {
return this.template;
}
parse(template) {
const parts = [];
let currentText = '';
let i = 0;
let expressionCount = 0;
while (i < template.length) {
if (template[i] === '{') {
if (currentText) {
parts.push(currentText);
currentText = '';
}
const end = template.indexOf('}', i);
if (end === -1)
throw new Error('Unclosed template expression');
expressionCount++;
if (expressionCount > MAX_TEMPLATE_EXPRESSIONS) {
throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`);
}
const expr = template.slice(i + 1, end);
const operator = this.getOperator(expr);
const exploded = expr.includes('*');
const names = this.getNames(expr);
const name = names[0];
// Validate variable name length
for (const name of names) {
UriTemplate.validateLength(name, MAX_VARIABLE_LENGTH, 'Variable name');
}
parts.push({ name, operator, names, exploded });
i = end + 1;
}
else {
currentText += template[i];
i++;
}
}
if (currentText) {
parts.push(currentText);
}
return parts;
}
getOperator(expr) {
const operators = ['+', '#', '.', '/', '?', '&'];
return operators.find(op => expr.startsWith(op)) || '';
}
getNames(expr) {
const operator = this.getOperator(expr);
return expr
.slice(operator.length)
.split(',')
.map(name => name.replace('*', '').trim())
.filter(name => name.length > 0);
}
encodeValue(value, operator) {
UriTemplate.validateLength(value, MAX_VARIABLE_LENGTH, 'Variable value');
if (operator === '+' || operator === '#') {
return encodeURI(value);
}
return encodeURIComponent(value);
}
expandPart(part, variables) {
if (part.operator === '?' || part.operator === '&') {
const pairs = part.names
.map(name => {
const value = variables[name];
if (value === undefined)
return '';
const encoded = Array.isArray(value)
? value.map(v => this.encodeValue(v, part.operator)).join(',')
: this.encodeValue(value.toString(), part.operator);
return `${name}=${encoded}`;
})
.filter(pair => pair.length > 0);
if (pairs.length === 0)
return '';
const separator = part.operator === '?' ? '?' : '&';
return separator + pairs.join('&');
}
if (part.names.length > 1) {
const values = part.names.map(name => variables[name]).filter(v => v !== undefined);
if (values.length === 0)
return '';
return values.map(v => (Array.isArray(v) ? v[0] : v)).join(',');
}
const value = variables[part.name];
if (value === undefined)
return '';
const values = Array.isArray(value) ? value : [value];
const encoded = values.map(v => this.encodeValue(v, part.operator));
switch (part.operator) {
case '':
return encoded.join(',');
case '+':
return encoded.join(',');
case '#':
return '#' + encoded.join(',');
case '.':
return '.' + encoded.join('.');
case '/':
return '/' + encoded.join('/');
default:
return encoded.join(',');
}
}
expand(variables) {
let result = '';
let hasQueryParam = false;
for (const part of this.parts) {
if (typeof part === 'string') {
result += part;
continue;
}
const expanded = this.expandPart(part, variables);
if (!expanded)
continue;
// Convert ? to & if we already have a query parameter
if ((part.operator === '?' || part.operator === '&') && hasQueryParam) {
result += expanded.replace('?', '&');
}
else {
result += expanded;
}
if (part.operator === '?' || part.operator === '&') {
hasQueryParam = true;
}
}
return result;
}
escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
partToRegExp(part) {
const patterns = [];
// Validate variable name length for matching
for (const name of part.names) {
UriTemplate.validateLength(name, MAX_VARIABLE_LENGTH, 'Variable name');
}
if (part.operator === '?' || part.operator === '&') {
for (let i = 0; i < part.names.length; i++) {
const name = part.names[i];
const prefix = i === 0 ? '\\' + part.operator : '&';
patterns.push({
pattern: prefix + this.escapeRegExp(name) + '=([^&]+)',
name
});
}
return patterns;
}
let pattern;
const name = part.name;
switch (part.operator) {
case '':
pattern = part.exploded ? '([^/,]+(?:,[^/,]+)*)' : '([^/,]+)';
break;
case '+':
case '#':
pattern = '(.+)';
break;
case '.':
pattern = '\\.([^/,]+)';
break;
case '/':
pattern = '/' + (part.exploded ? '([^/,]+(?:,[^/,]+)*)' : '([^/,]+)');
break;
default:
pattern = '([^/]+)';
}
patterns.push({ pattern, name });
return patterns;
}
match(uri) {
UriTemplate.validateLength(uri, MAX_TEMPLATE_LENGTH, 'URI');
let pattern = '^';
const names = [];
for (const part of this.parts) {
if (typeof part === 'string') {
pattern += this.escapeRegExp(part);
}
else {
const patterns = this.partToRegExp(part);
for (const { pattern: partPattern, name } of patterns) {
pattern += partPattern;
names.push({ name, exploded: part.exploded });
}
}
}
pattern += '$';
UriTemplate.validateLength(pattern, MAX_REGEX_LENGTH, 'Generated regex pattern');
const regex = new RegExp(pattern);
const match = uri.match(regex);
if (!match)
return null;
const result = {};
for (let i = 0; i < names.length; i++) {
const { name, exploded } = names[i];
const value = match[i + 1];
const cleanName = name.replace('*', '');
if (exploded && value.includes(',')) {
result[cleanName] = value.split(',');
}
else {
result[cleanName] = value;
}
}
return result;
}
}
exports.UriTemplate = UriTemplate;
//# sourceMappingURL=uriTemplate.js.map

File diff suppressed because one or more lines are too long