From e59360cb0059c4cde2d9f3d44ee426e806fb3492 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 14:12:31 +0100 Subject: [PATCH 1/9] chore: upgrade zod to v4.1.9 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a2dc15d2..8be5d4a36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8", + "zod": "^4.1.9", "zod-to-json-schema": "^3.24.1" }, "devDependencies": { @@ -6673,9 +6673,9 @@ } }, "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index ad0db7075..ef385d63d 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^3.23.8", + "zod": "^4.1.9", "zod-to-json-schema": "^3.24.1" }, "devDependencies": { From b12f1165503b6dc6e8152c077b4b366656658942 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 14:13:20 +0100 Subject: [PATCH 2/9] refactor: apply automated zod v3 to v4 migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated transformations applied via zod-v3-to-v4: - String validators: z.string().url() → z.url() - Number validators: z.number().int() → z.int() - Object methods: .passthrough() → z.looseObject(), .strict() → z.strictObject() - Schema composition: .merge() → .extend() where applicable - Error messages: { message } → { error } - Defaults: .default() → .prefault() - Object stripping: .strip() removed/consolidated --- src/examples/server/simpleSseServer.ts | 4 +- .../server/simpleStatelessStreamableHttp.ts | 4 +- src/examples/server/simpleStreamableHttp.ts | 4 +- .../sseAndStreamableHttpCompatibleServer.ts | 4 +- .../stateManagementStreamableHttp.test.ts | 2 +- .../taskResumability.test.ts | 6 +- src/server/auth/handlers/authorize.ts | 6 +- src/server/auth/handlers/token.ts | 4 +- src/shared/auth.ts | 66 ++- src/types.ts | 406 +++++++----------- 10 files changed, 206 insertions(+), 300 deletions(-) diff --git a/src/examples/server/simpleSseServer.ts b/src/examples/server/simpleSseServer.ts index b99334369..7b57844bf 100644 --- a/src/examples/server/simpleSseServer.ts +++ b/src/examples/server/simpleSseServer.ts @@ -28,8 +28,8 @@ const getServer = () => { 'start-notification-stream', 'Starts sending periodic notifications', { - interval: z.number().describe('Interval in milliseconds between notifications').default(1000), - count: z.number().describe('Number of notifications to send').default(10) + interval: z.number().describe('Interval in milliseconds between notifications').prefault(1000), + count: z.number().describe('Number of notifications to send').prefault(10) }, async ({ interval, count }, extra): Promise => { const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/examples/server/simpleStatelessStreamableHttp.ts b/src/examples/server/simpleStatelessStreamableHttp.ts index f71e5db6c..b3d136ed9 100644 --- a/src/examples/server/simpleStatelessStreamableHttp.ts +++ b/src/examples/server/simpleStatelessStreamableHttp.ts @@ -42,8 +42,8 @@ const getServer = () => { 'start-notification-stream', 'Starts sending periodic notifications for testing resumability', { - interval: z.number().describe('Interval in milliseconds between notifications').default(100), - count: z.number().describe('Number of notifications to send (0 for 100)').default(10) + interval: z.number().describe('Interval in milliseconds between notifications').prefault(100), + count: z.number().describe('Number of notifications to send (0 for 100)').prefault(10) }, async ({ interval, count }, extra): Promise => { const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/examples/server/simpleStreamableHttp.ts b/src/examples/server/simpleStreamableHttp.ts index 5872cb4ac..eff9ef7cb 100644 --- a/src/examples/server/simpleStreamableHttp.ts +++ b/src/examples/server/simpleStreamableHttp.ts @@ -291,8 +291,8 @@ const getServer = () => { 'start-notification-stream', 'Starts sending periodic notifications for testing resumability', { - interval: z.number().describe('Interval in milliseconds between notifications').default(100), - count: z.number().describe('Number of notifications to send (0 for 100)').default(50) + interval: z.number().describe('Interval in milliseconds between notifications').prefault(100), + count: z.number().describe('Number of notifications to send (0 for 100)').prefault(50) }, async ({ interval, count }, extra): Promise => { const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/examples/server/sseAndStreamableHttpCompatibleServer.ts b/src/examples/server/sseAndStreamableHttpCompatibleServer.ts index 50e2e5125..e66b3ab53 100644 --- a/src/examples/server/sseAndStreamableHttpCompatibleServer.ts +++ b/src/examples/server/sseAndStreamableHttpCompatibleServer.ts @@ -33,8 +33,8 @@ const getServer = () => { 'start-notification-stream', 'Starts sending periodic notifications for testing resumability', { - interval: z.number().describe('Interval in milliseconds between notifications').default(100), - count: z.number().describe('Number of notifications to send (0 for 100)').default(50) + interval: z.number().describe('Interval in milliseconds between notifications').prefault(100), + count: z.number().describe('Number of notifications to send (0 for 100)').prefault(50) }, async ({ interval, count }, extra): Promise => { const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); diff --git a/src/integration-tests/stateManagementStreamableHttp.test.ts b/src/integration-tests/stateManagementStreamableHttp.test.ts index 629b01519..82d1187ee 100644 --- a/src/integration-tests/stateManagementStreamableHttp.test.ts +++ b/src/integration-tests/stateManagementStreamableHttp.test.ts @@ -56,7 +56,7 @@ describe('Streamable HTTP Transport Session Management', () => { 'greet', 'A simple greeting tool', { - name: z.string().describe('Name to greet').default('World') + name: z.string().describe('Name to greet').prefault('World') }, async ({ name }) => { return { diff --git a/src/integration-tests/taskResumability.test.ts b/src/integration-tests/taskResumability.test.ts index d397ffab3..227c1d0e6 100644 --- a/src/integration-tests/taskResumability.test.ts +++ b/src/integration-tests/taskResumability.test.ts @@ -28,7 +28,7 @@ describe('Transport resumability', () => { 'send-notification', 'Sends a single notification', { - message: z.string().describe('Message to send').default('Test notification') + message: z.string().describe('Message to send').prefault('Test notification') }, async ({ message }, { sendNotification }) => { // Send notification immediately @@ -51,8 +51,8 @@ describe('Transport resumability', () => { 'run-notifications', 'Sends multiple notifications over time', { - count: z.number().describe('Number of notifications to send').default(10), - interval: z.number().describe('Interval between notifications in ms').default(50) + count: z.number().describe('Number of notifications to send').prefault(10), + interval: z.number().describe('Interval between notifications in ms').prefault(50) }, async ({ count, interval }, { sendNotification }) => { // Send notifications at specified intervals diff --git a/src/server/auth/handlers/authorize.ts b/src/server/auth/handlers/authorize.ts index 14c7121ae..0e9386313 100644 --- a/src/server/auth/handlers/authorize.ts +++ b/src/server/auth/handlers/authorize.ts @@ -21,7 +21,9 @@ const ClientAuthorizationParamsSchema = z.object({ redirect_uri: z .string() .optional() - .refine(value => value === undefined || URL.canParse(value), { message: 'redirect_uri must be a valid URL' }) + .refine(value => value === undefined || URL.canParse(value), { + error: 'redirect_uri must be a valid URL' + }) }); // Parameters that must be validated for a successful authorization request. Failure can be reported to the redirect URI. @@ -31,7 +33,7 @@ const RequestAuthorizationParamsSchema = z.object({ code_challenge_method: z.literal('S256'), scope: z.string().optional(), state: z.string().optional(), - resource: z.string().url().optional() + resource: z.url().optional() }); export function authorizationHandler({ provider, rateLimit: rateLimitConfig }: AuthorizationHandlerOptions): RequestHandler { diff --git a/src/server/auth/handlers/token.ts b/src/server/auth/handlers/token.ts index c387ff7bf..a7480a66b 100644 --- a/src/server/auth/handlers/token.ts +++ b/src/server/auth/handlers/token.ts @@ -32,13 +32,13 @@ const AuthorizationCodeGrantSchema = z.object({ code: z.string(), code_verifier: z.string(), redirect_uri: z.string().optional(), - resource: z.string().url().optional() + resource: z.url().optional() }); const RefreshTokenGrantSchema = z.object({ refresh_token: z.string(), scope: z.string().optional(), - resource: z.string().url().optional() + resource: z.url().optional() }); export function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHandlerOptions): RequestHandler { diff --git a/src/shared/auth.ts b/src/shared/auth.ts index c5ddbda16..aeac022f6 100644 --- a/src/shared/auth.ts +++ b/src/shared/auth.ts @@ -3,13 +3,11 @@ import { z } from 'zod'; /** * Reusable URL validation that disallows javascript: scheme */ -export const SafeUrlSchema = z - .string() - .url() +export const SafeUrlSchema = z.url() .superRefine((val, ctx) => { if (!URL.canParse(val)) { ctx.addIssue({ - code: z.ZodIssueCode.custom, + code: "custom", message: 'URL must be parseable', fatal: true }); @@ -22,36 +20,35 @@ export const SafeUrlSchema = z const u = new URL(url); return u.protocol !== 'javascript:' && u.protocol !== 'data:' && u.protocol !== 'vbscript:'; }, - { message: 'URL cannot use javascript:, data:, or vbscript: scheme' } + { + error: 'URL cannot use javascript:, data:, or vbscript: scheme' + } ); /** * RFC 9728 OAuth Protected Resource Metadata */ -export const OAuthProtectedResourceMetadataSchema = z - .object({ - resource: z.string().url(), +export const OAuthProtectedResourceMetadataSchema = z.looseObject({ + resource: z.url(), authorization_servers: z.array(SafeUrlSchema).optional(), - jwks_uri: z.string().url().optional(), + jwks_uri: z.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(), + resource_policy_uri: z.url().optional(), + resource_tos_uri: z.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() - }) - .passthrough(); + }); /** * RFC 8414 OAuth 2.0 Authorization Server Metadata */ -export const OAuthMetadataSchema = z - .object({ +export const OAuthMetadataSchema = z.looseObject({ issuer: z.string(), authorization_endpoint: SafeUrlSchema, token_endpoint: SafeUrlSchema, @@ -70,15 +67,13 @@ export const OAuthMetadataSchema = z 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() - }) - .passthrough(); + }); /** * OpenID Connect Discovery 1.0 Provider Metadata * see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata */ -export const OpenIdProviderMetadataSchema = z - .object({ +export const OpenIdProviderMetadataSchema = z.looseObject({ issuer: z.string(), authorization_endpoint: SafeUrlSchema, token_endpoint: SafeUrlSchema, @@ -114,8 +109,7 @@ export const OpenIdProviderMetadataSchema = z require_request_uri_registration: z.boolean().optional(), op_policy_uri: SafeUrlSchema.optional(), op_tos_uri: SafeUrlSchema.optional() - }) - .passthrough(); + }); /** * OpenID Connect Discovery metadata that may include OAuth 2.0 fields @@ -131,16 +125,14 @@ export const OpenIdProviderDiscoveryMetadataSchema = OpenIdProviderMetadataSchem /** * OAuth 2.1 token response */ -export const OAuthTokensSchema = z - .object({ +export const 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.number().optional(), scope: z.string().optional(), refresh_token: z.string().optional() - }) - .strip(); + }); /** * OAuth 2.1 error response @@ -159,8 +151,7 @@ export const OptionalSafeUrlSchema = SafeUrlSchema.optional().or(z.literal('').t /** * RFC 7591 OAuth 2.0 Dynamic Client Registration metadata */ -export const OAuthClientMetadataSchema = z - .object({ +export const OAuthClientMetadataSchema = z.object({ redirect_uris: z.array(SafeUrlSchema), token_endpoint_auth_method: z.string().optional(), grant_types: z.array(z.string()).optional(), @@ -177,20 +168,17 @@ export const OAuthClientMetadataSchema = z 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 */ -export const OAuthClientInformationSchema = z - .object({ +export const 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) @@ -200,22 +188,18 @@ export const OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge( /** * RFC 7591 OAuth 2.0 Dynamic Client Registration error response */ -export const OAuthClientRegistrationErrorSchema = z - .object({ +export const OAuthClientRegistrationErrorSchema = z.object({ error: z.string(), error_description: z.string().optional() - }) - .strip(); + }); /** * RFC 7009 OAuth 2.0 Token Revocation request */ -export const OAuthTokenRevocationRequestSchema = z - .object({ +export const OAuthTokenRevocationRequestSchema = z.object({ token: z.string(), token_type_hint: z.string().optional() - }) - .strip(); + }); export type OAuthMetadata = z.infer; export type OpenIdProviderMetadata = z.infer; diff --git a/src/types.ts b/src/types.ts index e6d3fe46e..a9ad6cc94 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,98 +11,84 @@ export const JSONRPC_VERSION = '2.0'; /** * A progress token, used to associate progress notifications with the original request. */ -export const ProgressTokenSchema = z.union([z.string(), z.number().int()]); +export const ProgressTokenSchema = z.union([z.string(), z.int()]); /** * An opaque token used to represent a cursor for pagination. */ export const CursorSchema = z.string(); -const RequestMetaSchema = z - .object({ +const RequestMetaSchema = z.looseObject({ /** * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. */ progressToken: z.optional(ProgressTokenSchema) - }) - .passthrough(); + }); -const BaseRequestParamsSchema = z - .object({ +const BaseRequestParamsSchema = z.looseObject({ _meta: z.optional(RequestMetaSchema) - }) - .passthrough(); + }); export const RequestSchema = z.object({ method: z.string(), params: z.optional(BaseRequestParamsSchema) }); -const BaseNotificationParamsSchema = z - .object({ +const BaseNotificationParamsSchema = z.looseObject({ /** * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); export const NotificationSchema = z.object({ method: z.string(), params: z.optional(BaseNotificationParamsSchema) }); -export const ResultSchema = z - .object({ +export const ResultSchema = z.looseObject({ /** * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); /** * A uniquely identifying ID for a request in JSON-RPC. */ -export const RequestIdSchema = z.union([z.string(), z.number().int()]); +export const RequestIdSchema = z.union([z.string(), z.int()]); /** * A request that expects a response. */ -export const JSONRPCRequestSchema = z - .object({ +export const JSONRPCRequestSchema = z.strictObject({ jsonrpc: z.literal(JSONRPC_VERSION), id: RequestIdSchema }) - .merge(RequestSchema) - .strict(); + .extend(RequestSchema.shape); export const isJSONRPCRequest = (value: unknown): value is JSONRPCRequest => JSONRPCRequestSchema.safeParse(value).success; /** * A notification which does not expect a response. */ -export const JSONRPCNotificationSchema = z - .object({ +export const JSONRPCNotificationSchema = z.strictObject({ jsonrpc: z.literal(JSONRPC_VERSION) }) - .merge(NotificationSchema) - .strict(); + .extend(NotificationSchema.shape); export const isJSONRPCNotification = (value: unknown): value is JSONRPCNotification => JSONRPCNotificationSchema.safeParse(value).success; /** * A successful (non-error) response to a request. */ -export const JSONRPCResponseSchema = z - .object({ +export const JSONRPCResponseSchema = z.strictObject({ jsonrpc: z.literal(JSONRPC_VERSION), id: RequestIdSchema, result: ResultSchema - }) - .strict(); + }); export const isJSONRPCResponse = (value: unknown): value is JSONRPCResponse => JSONRPCResponseSchema.safeParse(value).success; @@ -125,15 +111,14 @@ export enum ErrorCode { /** * A response to a request that indicates an error occurred. */ -export const JSONRPCErrorSchema = z - .object({ +export const JSONRPCErrorSchema = z.strictObject({ jsonrpc: z.literal(JSONRPC_VERSION), id: RequestIdSchema, - error: z.object({ + error: z.strictObject({ /** * The error type that occurred. */ - code: z.number().int(), + code: z.int(), /** * A short description of the error. The message SHOULD be limited to a concise single sentence. */ @@ -143,8 +128,7 @@ export const JSONRPCErrorSchema = z */ data: z.optional(z.unknown()) }) - }) - .strict(); + }); export const isJSONRPCError = (value: unknown): value is JSONRPCError => JSONRPCErrorSchema.safeParse(value).success; @@ -187,8 +171,7 @@ export const CancelledNotificationSchema = NotificationSchema.extend({ /** * Icon schema for use in tools, prompts, resources, and implementations. */ -export const IconSchema = z - .object({ +export const IconSchema = z.looseObject({ /** * URL or data URI for the icon. */ @@ -204,15 +187,13 @@ export const IconSchema = z * If not provided, the client should assume that the icon can be used at any size. */ sizes: z.optional(z.array(z.string())) - }) - .passthrough(); + }); /** * Base schema to add `icons` property. * */ -export const IconsSchema = z - .object({ +export const IconsSchema = z.looseObject({ /** * Optional set of sized icons that the client can display in a user interface. * @@ -225,14 +206,12 @@ export const IconsSchema = z * - `image/webp` - WebP images (modern, efficient format) */ icons: z.array(IconSchema).optional() - }) - .passthrough(); + }); /** * Base metadata interface for common properties across resources, tools, prompts, and implementations. */ -export const BaseMetadataSchema = z - .object({ +export const BaseMetadataSchema = z.looseObject({ /** Intended for programmatic or logical use, but used as a display name in past specs or fallback */ name: z.string(), /** @@ -244,8 +223,7 @@ export const BaseMetadataSchema = z * if present). */ title: z.optional(z.string()) - }) - .passthrough(); + }); /* Initialization */ /** @@ -262,35 +240,31 @@ export const ImplementationSchema = BaseMetadataSchema.extend({ /** * Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities. */ -export const ClientCapabilitiesSchema = z - .object({ +export const ClientCapabilitiesSchema = z.looseObject({ /** * Experimental, non-standard capabilities that the client supports. */ - experimental: z.optional(z.object({}).passthrough()), + experimental: z.optional(z.looseObject({})), /** * Present if the client supports sampling from an LLM. */ - sampling: z.optional(z.object({}).passthrough()), + sampling: z.optional(z.looseObject({})), /** * Present if the client supports eliciting user input. */ - elicitation: z.optional(z.object({}).passthrough()), + elicitation: z.optional(z.looseObject({})), /** * Present if the client supports listing roots. */ roots: z.optional( - z - .object({ - /** - * Whether the client supports issuing notifications for changes to the roots list. - */ - listChanged: z.optional(z.boolean()) - }) - .passthrough() + z.looseObject({ + /** + * Whether the client supports issuing notifications for changes to the roots list. + */ + listChanged: z.optional(z.boolean()) + }) ) - }) - .passthrough(); + }); /** * This request is sent from the client to the server when it first connects, asking it to begin initialization. @@ -312,66 +286,58 @@ export const isInitializeRequest = (value: unknown): value is InitializeRequest /** * Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. */ -export const ServerCapabilitiesSchema = z - .object({ +export const ServerCapabilitiesSchema = z.looseObject({ /** * Experimental, non-standard capabilities that the server supports. */ - experimental: z.optional(z.object({}).passthrough()), + experimental: z.optional(z.looseObject({})), /** * Present if the server supports sending log messages to the client. */ - logging: z.optional(z.object({}).passthrough()), + logging: z.optional(z.looseObject({})), /** * Present if the server supports sending completions to the client. */ - completions: z.optional(z.object({}).passthrough()), + completions: z.optional(z.looseObject({})), /** * Present if the server offers any prompt templates. */ prompts: z.optional( - z - .object({ - /** - * Whether this server supports issuing notifications for changes to the prompt list. - */ - listChanged: z.optional(z.boolean()) - }) - .passthrough() + z.looseObject({ + /** + * Whether this server supports issuing notifications for changes to the prompt list. + */ + listChanged: z.optional(z.boolean()) + }) ), /** * Present if the server offers any resources to read. */ resources: z.optional( - z - .object({ - /** - * Whether this server supports clients subscribing to resource updates. - */ - subscribe: z.optional(z.boolean()), - - /** - * Whether this server supports issuing notifications for changes to the resource list. - */ - listChanged: z.optional(z.boolean()) - }) - .passthrough() + z.looseObject({ + /** + * Whether this server supports clients subscribing to resource updates. + */ + subscribe: z.optional(z.boolean()), + + /** + * Whether this server supports issuing notifications for changes to the resource list. + */ + listChanged: z.optional(z.boolean()) + }) ), /** * Present if the server offers any tools to call. */ tools: z.optional( - z - .object({ - /** - * Whether this server supports issuing notifications for changes to the tool list. - */ - listChanged: z.optional(z.boolean()) - }) - .passthrough() + z.looseObject({ + /** + * Whether this server supports issuing notifications for changes to the tool list. + */ + listChanged: z.optional(z.boolean()) + }) ) - }) - .passthrough(); + }); /** * After receiving an initialize request from the client, the server sends this response. @@ -410,8 +376,7 @@ export const PingRequestSchema = RequestSchema.extend({ }); /* Progress notifications */ -export const ProgressSchema = z - .object({ +export const ProgressSchema = z.looseObject({ /** * The progress thus far. This should increase every time progress is made, even if the total is unknown. */ @@ -424,8 +389,7 @@ export const ProgressSchema = z * An optional message describing the current progress. */ message: z.optional(z.string()) - }) - .passthrough(); + }); /** * An out-of-band notification used to inform the receiver of a progress update for a long-running request. @@ -463,8 +427,7 @@ export const PaginatedResultSchema = ResultSchema.extend({ /** * The contents of a specific resource or sub-resource. */ -export const ResourceContentsSchema = z - .object({ +export const ResourceContentsSchema = z.looseObject({ /** * The URI of this resource. */ @@ -477,9 +440,8 @@ export const ResourceContentsSchema = z * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); export const TextResourceContentsSchema = ResourceContentsSchema.extend({ /** @@ -504,7 +466,9 @@ const Base64Schema = z.string().refine( return false; } }, - { message: 'Invalid Base64 string' } + { + error: 'Invalid Base64 string' + } ); export const BlobResourceContentsSchema = ResourceContentsSchema.extend({ @@ -539,7 +503,7 @@ export const ResourceSchema = BaseMetadataSchema.extend({ * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) + _meta: z.optional(z.looseObject({})) }).merge(IconsSchema); /** @@ -567,7 +531,7 @@ export const ResourceTemplateSchema = BaseMetadataSchema.extend({ * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) + _meta: z.optional(z.looseObject({})) }).merge(IconsSchema); /** @@ -668,8 +632,7 @@ export const ResourceUpdatedNotificationSchema = NotificationSchema.extend({ /** * Describes an argument that a prompt can accept. */ -export const PromptArgumentSchema = z - .object({ +export const PromptArgumentSchema = z.looseObject({ /** * The name of the argument. */ @@ -682,8 +645,7 @@ export const PromptArgumentSchema = z * Whether this argument must be provided. */ required: z.optional(z.boolean()) - }) - .passthrough(); + }); /** * A prompt or prompt template that the server offers. @@ -701,7 +663,7 @@ export const PromptSchema = BaseMetadataSchema.extend({ * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) + _meta: z.optional(z.looseObject({})) }).merge(IconsSchema); /** @@ -731,15 +693,14 @@ export const GetPromptRequestSchema = RequestSchema.extend({ /** * Arguments to use for templating the prompt. */ - arguments: z.optional(z.record(z.string())) + arguments: z.optional(z.record(z.string(), z.string())) }) }); /** * Text provided to or from an LLM. */ -export const TextContentSchema = z - .object({ +export const TextContentSchema = z.looseObject({ type: z.literal('text'), /** * The text content of the message. @@ -750,15 +711,13 @@ export const TextContentSchema = z * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); /** * An image provided to or from an LLM. */ -export const ImageContentSchema = z - .object({ +export const ImageContentSchema = z.looseObject({ type: z.literal('image'), /** * The base64-encoded image data. @@ -773,15 +732,13 @@ export const ImageContentSchema = z * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); /** * An Audio provided to or from an LLM. */ -export const AudioContentSchema = z - .object({ +export const AudioContentSchema = z.looseObject({ type: z.literal('audio'), /** * The base64-encoded audio data. @@ -796,24 +753,21 @@ export const AudioContentSchema = z * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); /** * The contents of a resource, embedded into a prompt or tool call result. */ -export const EmbeddedResourceSchema = z - .object({ +export const EmbeddedResourceSchema = z.looseObject({ type: z.literal('resource'), resource: z.union([TextResourceContentsSchema, BlobResourceContentsSchema]), /** * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); /** * A resource that the server is capable of reading, included in a prompt or tool call result. @@ -838,12 +792,10 @@ export const ContentBlockSchema = z.union([ /** * Describes a message returned as part of a prompt. */ -export const PromptMessageSchema = z - .object({ +export const PromptMessageSchema = z.looseObject({ role: z.enum(['user', 'assistant']), content: ContentBlockSchema - }) - .passthrough(); + }); /** * The server's response to a prompts/get request from the client. @@ -874,8 +826,7 @@ export const PromptListChangedNotificationSchema = NotificationSchema.extend({ * Clients should never make tool use decisions based on ToolAnnotations * received from untrusted servers. */ -export const ToolAnnotationsSchema = z - .object({ +export const ToolAnnotationsSchema = z.looseObject({ /** * A human-readable title for the tool. */ @@ -917,8 +868,7 @@ export const ToolAnnotationsSchema = z * Default: true */ openWorldHint: z.optional(z.boolean()) - }) - .passthrough(); + }); /** * Definition for a tool the client can call. @@ -931,25 +881,21 @@ export const ToolSchema = BaseMetadataSchema.extend({ /** * A JSON Schema object defining the expected parameters for the tool. */ - inputSchema: z - .object({ - type: z.literal('object'), - properties: z.optional(z.object({}).passthrough()), - required: z.optional(z.array(z.string())) - }) - .passthrough(), + inputSchema: z.looseObject({ + type: z.literal('object'), + properties: z.optional(z.looseObject({})), + required: z.optional(z.array(z.string())) + }), /** * An optional JSON Schema object defining the structure of the tool's output returned in * the structuredContent field of a CallToolResult. */ outputSchema: z.optional( - z - .object({ - type: z.literal('object'), - properties: z.optional(z.object({}).passthrough()), - required: z.optional(z.array(z.string())) - }) - .passthrough() + z.looseObject({ + type: z.literal('object'), + properties: z.optional(z.looseObject({})), + required: z.optional(z.array(z.string())) + }) ), /** * Optional additional tool information. @@ -960,7 +906,7 @@ export const ToolSchema = BaseMetadataSchema.extend({ * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) + _meta: z.optional(z.looseObject({})) }).merge(IconsSchema); /** @@ -987,14 +933,14 @@ export const CallToolResultSchema = ResultSchema.extend({ * If the Tool does not define an outputSchema, this field MUST be present in the result. * For backwards compatibility, this field is always present, but it may be empty. */ - content: z.array(ContentBlockSchema).default([]), + content: z.array(ContentBlockSchema).prefault([]), /** * An object containing structured tool output. * * If the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema. */ - structuredContent: z.object({}).passthrough().optional(), + structuredContent: z.looseObject({}).optional(), /** * Whether the tool call ended in an error. @@ -1029,7 +975,7 @@ export const CallToolRequestSchema = RequestSchema.extend({ method: z.literal('tools/call'), params: BaseRequestParamsSchema.extend({ name: z.string(), - arguments: z.optional(z.record(z.unknown())) + arguments: z.optional(z.record(z.string(), z.unknown())) }) }); @@ -1084,20 +1030,17 @@ export const LoggingMessageNotificationSchema = NotificationSchema.extend({ /** * Hints to use for model selection. */ -export const ModelHintSchema = z - .object({ +export const ModelHintSchema = z.looseObject({ /** * A hint for a model name. */ name: z.string().optional() - }) - .passthrough(); + }); /** * The server's preferences for model selection, requested of the client during sampling. */ -export const ModelPreferencesSchema = z - .object({ +export const ModelPreferencesSchema = z.looseObject({ /** * Optional hints to use for model selection. */ @@ -1114,18 +1057,15 @@ export const ModelPreferencesSchema = z * How much to prioritize intelligence and capabilities when selecting a model. */ intelligencePriority: z.optional(z.number().min(0).max(1)) - }) - .passthrough(); + }); /** * Describes a message issued to or received from an LLM API. */ -export const SamplingMessageSchema = z - .object({ +export const SamplingMessageSchema = z.looseObject({ role: z.enum(['user', 'assistant']), content: z.union([TextContentSchema, ImageContentSchema, AudioContentSchema]) - }) - .passthrough(); + }); /** * A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. @@ -1146,12 +1086,12 @@ export const CreateMessageRequestSchema = RequestSchema.extend({ /** * The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested. */ - maxTokens: z.number().int(), + maxTokens: z.int(), stopSequences: z.optional(z.array(z.string())), /** * Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. */ - metadata: z.optional(z.object({}).passthrough()), + metadata: z.optional(z.looseObject({})), /** * The server's preferences for which model to select. */ @@ -1179,54 +1119,46 @@ export const CreateMessageResultSchema = ResultSchema.extend({ /** * Primitive schema definition for boolean fields. */ -export const BooleanSchemaSchema = z - .object({ +export const BooleanSchemaSchema = z.looseObject({ type: z.literal('boolean'), title: z.optional(z.string()), description: z.optional(z.string()), default: z.optional(z.boolean()) - }) - .passthrough(); + }); /** * Primitive schema definition for string fields. */ -export const StringSchemaSchema = z - .object({ +export const StringSchemaSchema = z.looseObject({ type: z.literal('string'), title: z.optional(z.string()), description: z.optional(z.string()), minLength: z.optional(z.number()), maxLength: z.optional(z.number()), format: z.optional(z.enum(['email', 'uri', 'date', 'date-time'])) - }) - .passthrough(); + }); /** * Primitive schema definition for number fields. */ -export const NumberSchemaSchema = z - .object({ +export const NumberSchemaSchema = z.looseObject({ type: z.enum(['number', 'integer']), title: z.optional(z.string()), description: z.optional(z.string()), minimum: z.optional(z.number()), maximum: z.optional(z.number()) - }) - .passthrough(); + }); /** * Primitive schema definition for enum fields. */ -export const EnumSchemaSchema = z - .object({ +export const EnumSchemaSchema = z.looseObject({ type: z.literal('string'), title: z.optional(z.string()), description: z.optional(z.string()), enum: z.array(z.string()), enumNames: z.optional(z.array(z.string())) - }) - .passthrough(); + }); /** * Union of all primitive schema definitions. @@ -1247,13 +1179,11 @@ export const ElicitRequestSchema = RequestSchema.extend({ /** * The schema for the requested user input. */ - requestedSchema: z - .object({ - type: z.literal('object'), - properties: z.record(z.string(), PrimitiveSchemaDefinitionSchema), - required: z.optional(z.array(z.string())) - }) - .passthrough() + requestedSchema: z.looseObject({ + type: z.literal('object'), + properties: z.record(z.string(), PrimitiveSchemaDefinitionSchema), + required: z.optional(z.array(z.string())) + }) }) }); @@ -1275,15 +1205,13 @@ export const ElicitResultSchema = ResultSchema.extend({ /** * A reference to a resource or resource template definition. */ -export const ResourceTemplateReferenceSchema = z - .object({ +export const ResourceTemplateReferenceSchema = z.looseObject({ type: z.literal('ref/resource'), /** * The URI or URI template of the resource. */ uri: z.string() - }) - .passthrough(); + }); /** * @deprecated Use ResourceTemplateReferenceSchema instead @@ -1293,15 +1221,13 @@ export const ResourceReferenceSchema = ResourceTemplateReferenceSchema; /** * Identifies a prompt. */ -export const PromptReferenceSchema = z - .object({ +export const PromptReferenceSchema = z.looseObject({ type: z.literal('ref/prompt'), /** * The name of the prompt or prompt template */ name: z.string() - }) - .passthrough(); + }); /** * A request from the client to the server, to ask for completion options. @@ -1313,18 +1239,16 @@ export const CompleteRequestSchema = RequestSchema.extend({ /** * The argument's information */ - argument: z - .object({ - /** - * The name of the argument - */ - name: z.string(), - /** - * The value of the argument to use for completion matching. - */ - value: z.string() - }) - .passthrough(), + argument: z.looseObject({ + /** + * The name of the argument + */ + name: z.string(), + /** + * The value of the argument to use for completion matching. + */ + value: z.string() + }), context: z.optional( z.object({ /** @@ -1340,30 +1264,27 @@ export const CompleteRequestSchema = RequestSchema.extend({ * The server's response to a completion/complete request */ export const CompleteResultSchema = ResultSchema.extend({ - completion: z - .object({ - /** - * An array of completion values. Must not exceed 100 items. - */ - values: z.array(z.string()).max(100), - /** - * The total number of completion options available. This can exceed the number of values actually sent in the response. - */ - total: z.optional(z.number().int()), - /** - * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. - */ - hasMore: z.optional(z.boolean()) - }) - .passthrough() + completion: z.looseObject({ + /** + * An array of completion values. Must not exceed 100 items. + */ + values: z.array(z.string()).max(100), + /** + * The total number of completion options available. This can exceed the number of values actually sent in the response. + */ + total: z.optional(z.int()), + /** + * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + hasMore: z.optional(z.boolean()) + }) }); /* Roots */ /** * Represents a root directory or file that the server can operate on. */ -export const RootSchema = z - .object({ +export const RootSchema = z.looseObject({ /** * The URI identifying the root. This *must* start with file:// for now. */ @@ -1377,9 +1298,8 @@ export const RootSchema = z * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. */ - _meta: z.optional(z.object({}).passthrough()) - }) - .passthrough(); + _meta: z.optional(z.looseObject({})) + }); /** * Sent from the server to request a list of root URIs from the client. From b37056ba728e18e486adaf5ac65079dd1b87853b Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 14:14:13 +0100 Subject: [PATCH 3/9] chore: remove zod-to-json-schema dependency Remove the external zod-to-json-schema library in preparation for migrating to Zod v4's native toJSONSchema() method. --- package-lock.json | 12 +----------- package.json | 3 +-- src/server/mcp.ts | 1 - 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8be5d4a36..7a92ef4af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,8 +19,7 @@ "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^4.1.9", - "zod-to-json-schema": "^3.24.1" + "zod": "^4.1.9" }, "devDependencies": { "@eslint/js": "^9.8.0", @@ -6680,15 +6679,6 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", - "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } } } } diff --git a/package.json b/package.json index ef385d63d..360dfb151 100644 --- a/package.json +++ b/package.json @@ -73,8 +73,7 @@ "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", - "zod": "^4.1.9", - "zod-to-json-schema": "^3.24.1" + "zod": "^4.1.9" }, "devDependencies": { "@eslint/js": "^9.8.0", diff --git a/src/server/mcp.ts b/src/server/mcp.ts index cef1722d6..678adda4c 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -1,5 +1,4 @@ import { Server, ServerOptions } from './index.js'; -import { zodToJsonSchema } from 'zod-to-json-schema'; import { z, ZodRawShape, ZodObject, ZodString, AnyZodObject, ZodTypeAny, ZodType, ZodTypeDef, ZodOptional } from 'zod'; import { Implementation, From 9c5d1d6b89ebef1878459325add973333d6fb17e Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 14:14:45 +0100 Subject: [PATCH 4/9] refactor: use native z.toJSONSchema() instead of external library Replace zodToJsonSchema() calls with Zod v4's native z.toJSONSchema() method. Use default options (JSON Schema Draft 2020-12) for simplicity and standard compliance. Removed unnecessary options: - target: 'openapi-3.0' - default draft-2020-12 is appropriate for MCP - strictUnions: true - option doesn't exist in Zod v4's native implementation --- src/server/mcp.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 678adda4c..a4e79a054 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -102,18 +102,14 @@ export class McpServer { title: tool.title, description: tool.description, inputSchema: tool.inputSchema - ? (zodToJsonSchema(tool.inputSchema, { - strictUnions: true - }) as Tool['inputSchema']) + ? (z.toJSONSchema(tool.inputSchema) as Tool['inputSchema']) : EMPTY_OBJECT_JSON_SCHEMA, annotations: tool.annotations, _meta: tool._meta }; if (tool.outputSchema) { - toolDefinition.outputSchema = zodToJsonSchema(tool.outputSchema, { - strictUnions: true - }) as Tool['outputSchema']; + toolDefinition.outputSchema = z.toJSONSchema(tool.outputSchema) as Tool['outputSchema']; } return toolDefinition; From caed2d5e9af093cf918fe46dcb142af218dfd295 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 15:11:08 +0100 Subject: [PATCH 5/9] fix: update zod type imports and inference for v4 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace removed/renamed types with v4 equivalents: - AnyZodObject → ZodObject - z.objectOutputType<> → z.infer> - Remove ZodTypeDef import (will be addressed with PromptArgsRawShape fix) This resolves 4 compilation errors (Category B & C). --- src/server/mcp.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/mcp.ts b/src/server/mcp.ts index a4e79a054..7fa8f5e99 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -1,5 +1,5 @@ import { Server, ServerOptions } from './index.js'; -import { z, ZodRawShape, ZodObject, ZodString, AnyZodObject, ZodTypeAny, ZodType, ZodTypeDef, ZodOptional } from 'zod'; +import { z, ZodRawShape, ZodObject, ZodString, ZodTypeAny, ZodType, ZodOptional } from 'zod'; import { Implementation, Tool, @@ -1008,7 +1008,7 @@ export class ResourceTemplate { */ export type ToolCallback = Args extends ZodRawShape ? ( - args: z.objectOutputType, + args: z.infer>, extra: RequestHandlerExtra ) => CallToolResult | Promise : (extra: RequestHandlerExtra) => CallToolResult | Promise; @@ -1016,8 +1016,8 @@ export type ToolCallback = Arg export type RegisteredTool = { title?: string; description?: string; - inputSchema?: AnyZodObject; - outputSchema?: AnyZodObject; + inputSchema?: ZodObject; + outputSchema?: ZodObject; annotations?: ToolAnnotations; _meta?: Record; callback: ToolCallback; @@ -1138,7 +1138,7 @@ type PromptArgsRawShape = { export type PromptCallback = Args extends PromptArgsRawShape ? ( - args: z.objectOutputType, + args: z.infer>, extra: RequestHandlerExtra ) => GetPromptResult | Promise : (extra: RequestHandlerExtra) => GetPromptResult | Promise; From 9563135693f093d3888534b9bf36d9f49c584c49 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 15:13:58 +0100 Subject: [PATCH 6/9] fix: update PromptArgsRawShape for zod v4 type system Replace generic ZodType with concrete ZodString type. This fixes compatibility with Zod v4's internal type system changes. Resolves 8 compilation errors (Category D & F): - 5 errors in mcp.ts type constraints - 3 cascading errors in example servers --- src/server/mcp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 7fa8f5e99..e56269955 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -1133,7 +1133,7 @@ export type RegisteredResourceTemplate = { }; type PromptArgsRawShape = { - [k: string]: ZodType | ZodOptional>; + [k: string]: ZodString | ZodOptional; }; export type PromptCallback = Args extends PromptArgsRawShape From f46f2acb0ba61ba1ffa3d83072a1251e9c8ec0a9 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 16:12:03 +0100 Subject: [PATCH 7/9] fix: rewrite Completable using immutable wrapper for Zod v4 The previous class-based approach that extended ZodType no longer works in Zod v4 due to architectural changes. This implements an immutable wrapper approach that creates a new schema object with completion metadata while preserving all validation behavior. - Creates new schema via Object.create() + property copying - Adds completion metadata to new _def object (not mutating shared state) - Maintains _def === _zod.def invariant - Preserves all schema properties including getters - Follows Zod's immutability pattern Also updates mcp.ts to check _def.typeName instead of instanceof. All 755 tests pass. --- src/server/completable.ts | 97 +++++++++++++++++---------------------- src/server/mcp.ts | 7 +-- 2 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/server/completable.ts b/src/server/completable.ts index 67d91c383..f23e2c977 100644 --- a/src/server/completable.ts +++ b/src/server/completable.ts @@ -1,4 +1,4 @@ -import { ZodTypeAny, ZodTypeDef, ZodType, ParseInput, ParseReturnType, RawCreateParams, ZodErrorMap, ProcessedCreateParams } from 'zod'; +import { ZodTypeAny } from 'zod'; export enum McpZodTypeKind { Completable = 'McpCompletable' @@ -11,69 +11,56 @@ export type CompleteCallback = ( } ) => T['_input'][] | Promise; -export interface CompletableDef extends ZodTypeDef { +export interface CompletableDef { type: T; complete: CompleteCallback; typeName: McpZodTypeKind.Completable; } -export class Completable extends ZodType, T['_input']> { - _parse(input: ParseInput): ParseReturnType { - const { ctx } = this._processInputParams(input); - const data = ctx.data; - return this._def.type._parse({ - data, - path: ctx.path, - parent: ctx - }); - } - - unwrap() { - return this._def.type; - } - - static create = ( - type: T, - params: RawCreateParams & { - complete: CompleteCallback; - } - ): Completable => { - return new Completable({ - type, - typeName: McpZodTypeKind.Completable, - complete: params.complete, - ...processCreateParams(params) - }); - }; -} - /** * Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP. + * + * Uses an immutable wrapper approach that creates a new schema object with completion metadata + * while preserving all validation behavior of the underlying schema. */ -export function completable(schema: T, complete: CompleteCallback): Completable { - return Completable.create(schema, { ...schema._def, complete }); -} +export function completable( + schema: T, + complete: CompleteCallback +): T & { _def: T['_def'] & CompletableDef } { + // Create new schema object inheriting from original + const wrapped = Object.create(Object.getPrototypeOf(schema)); -// Not sure why this isn't exported from Zod: -// https://github.com/colinhacks/zod/blob/f7ad26147ba291cb3fb257545972a8e00e767470/src/types.ts#L130 -function processCreateParams(params: RawCreateParams): ProcessedCreateParams { - if (!params) return {}; - const { errorMap, invalid_type_error, required_error, description } = params; - if (errorMap && (invalid_type_error || required_error)) { - throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); - } - if (errorMap) return { errorMap: errorMap, description }; - const customMap: ZodErrorMap = (iss, ctx) => { - const { message } = params; - - if (iss.code === 'invalid_enum_value') { - return { message: message ?? ctx.defaultError }; + // Copy all properties including getters/setters (except _def and _zod which we'll redefine) + Object.getOwnPropertyNames(schema).forEach(key => { + if (key !== '_def' && key !== '_zod') { + const descriptor = Object.getOwnPropertyDescriptor(schema, key); + if (descriptor) { + Object.defineProperty(wrapped, key, descriptor); + } } - if (typeof ctx.data === 'undefined') { - return { message: message ?? required_error ?? ctx.defaultError }; - } - if (iss.code !== 'invalid_type') return { message: ctx.defaultError }; - return { message: message ?? invalid_type_error ?? ctx.defaultError }; + }); + + // Create new def with added completion metadata + const newDef = { + ...schema._def, + typeName: McpZodTypeKind.Completable, + type: schema, + complete }; - return { errorMap: customMap, description }; + + // Set _def as read-only property (matching Zod's design) + Object.defineProperty(wrapped, '_def', { + value: newDef, + writable: false, + enumerable: false, + configurable: false + }); + + // Update _zod to maintain _def === _zod.def invariant + wrapped._zod = { + ...schema._zod, + def: newDef + }; + + return wrapped as T & { _def: T['_def'] & CompletableDef }; } diff --git a/src/server/mcp.ts b/src/server/mcp.ts index e56269955..4b9299337 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -32,7 +32,7 @@ import { ToolAnnotations, LoggingMessageNotification } from '../types.js'; -import { Completable, CompletableDef } from './completable.js'; +import { CompletableDef, McpZodTypeKind } from './completable.js'; import { UriTemplate, Variables } from '../shared/uriTemplate.js'; import { RequestHandlerExtra } from '../shared/protocol.js'; import { Transport } from '../shared/transport.js'; @@ -238,11 +238,12 @@ export class McpServer { } const field = prompt.argsSchema.shape[request.params.argument.name]; - if (!(field instanceof Completable)) { + const defLike = (field as unknown as { _def?: { typeName?: unknown } })._def; + if (!defLike || defLike.typeName !== McpZodTypeKind.Completable) { return EMPTY_COMPLETION_RESULT; } - const def: CompletableDef = field._def; + const def: CompletableDef = (field as unknown as { _def: CompletableDef })._def; const suggestions = await def.complete(request.params.argument.value, request.params.context); return createCompletionResult(suggestions); } From 24bbfe195188c5b0ee06a4c16b1cc2e50099bfc9 Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 16:14:51 +0100 Subject: [PATCH 8/9] refactor: extract CompletableSchema type for clarity Extracts the intersection type to a named CompletableSchema type alias, making the function signature more readable and reusable. --- src/server/completable.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/server/completable.ts b/src/server/completable.ts index f23e2c977..6f1ae0ae6 100644 --- a/src/server/completable.ts +++ b/src/server/completable.ts @@ -17,6 +17,11 @@ export interface CompletableDef { typeName: McpZodTypeKind.Completable; } +/** + * A Zod schema that has been wrapped with completion capabilities. + */ +export type CompletableSchema = T & { _def: T['_def'] & CompletableDef }; + /** * Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP. * @@ -26,7 +31,7 @@ export interface CompletableDef { export function completable( schema: T, complete: CompleteCallback -): T & { _def: T['_def'] & CompletableDef } { +): CompletableSchema { // Create new schema object inheriting from original const wrapped = Object.create(Object.getPrototypeOf(schema)); @@ -62,5 +67,5 @@ export function completable( def: newDef }; - return wrapped as T & { _def: T['_def'] & CompletableDef }; + return wrapped as CompletableSchema; } From d22ef6e2c1b5fef296b65300332770cc21b302fd Mon Sep 17 00:00:00 2001 From: Felix Weinberger Date: Thu, 16 Oct 2025 16:22:11 +0100 Subject: [PATCH 9/9] style: fix lint errors and format code - Remove unused ZodTypeAny import - Replace any with ZodRawShape in RegisteredTool interface - Apply prettier formatting to modified files --- src/server/completable.ts | 5 +- src/server/mcp.ts | 6 +- src/shared/auth.ts | 215 +++++----- src/types.ts | 796 +++++++++++++++++++------------------- 4 files changed, 511 insertions(+), 511 deletions(-) diff --git a/src/server/completable.ts b/src/server/completable.ts index 6f1ae0ae6..9a136ded4 100644 --- a/src/server/completable.ts +++ b/src/server/completable.ts @@ -28,10 +28,7 @@ export type CompletableSchema = T & { _def: T['_def'] & Co * Uses an immutable wrapper approach that creates a new schema object with completion metadata * while preserving all validation behavior of the underlying schema. */ -export function completable( - schema: T, - complete: CompleteCallback -): CompletableSchema { +export function completable(schema: T, complete: CompleteCallback): CompletableSchema { // Create new schema object inheriting from original const wrapped = Object.create(Object.getPrototypeOf(schema)); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 4b9299337..f2442962d 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -1,5 +1,5 @@ import { Server, ServerOptions } from './index.js'; -import { z, ZodRawShape, ZodObject, ZodString, ZodTypeAny, ZodType, ZodOptional } from 'zod'; +import { z, ZodRawShape, ZodObject, ZodString, ZodType, ZodOptional } from 'zod'; import { Implementation, Tool, @@ -1017,8 +1017,8 @@ export type ToolCallback = Arg export type RegisteredTool = { title?: string; description?: string; - inputSchema?: ZodObject; - outputSchema?: ZodObject; + inputSchema?: ZodObject; + outputSchema?: ZodObject; annotations?: ToolAnnotations; _meta?: Record; callback: ToolCallback; diff --git a/src/shared/auth.ts b/src/shared/auth.ts index aeac022f6..1c484f637 100644 --- a/src/shared/auth.ts +++ b/src/shared/auth.ts @@ -3,11 +3,12 @@ import { z } from 'zod'; /** * Reusable URL validation that disallows javascript: scheme */ -export const SafeUrlSchema = z.url() +export const SafeUrlSchema = z + .url() .superRefine((val, ctx) => { if (!URL.canParse(val)) { ctx.addIssue({ - code: "custom", + code: 'custom', message: 'URL must be parseable', fatal: true }); @@ -29,87 +30,87 @@ export const SafeUrlSchema = z.url() * RFC 9728 OAuth Protected Resource Metadata */ export const OAuthProtectedResourceMetadataSchema = z.looseObject({ - resource: z.url(), - authorization_servers: z.array(SafeUrlSchema).optional(), - jwks_uri: z.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.url().optional(), - resource_tos_uri: z.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() - }); + resource: z.url(), + authorization_servers: z.array(SafeUrlSchema).optional(), + jwks_uri: z.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.url().optional(), + resource_tos_uri: z.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 */ export const OAuthMetadataSchema = z.looseObject({ - issuer: z.string(), - authorization_endpoint: SafeUrlSchema, - token_endpoint: SafeUrlSchema, - registration_endpoint: 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: SafeUrlSchema.optional(), - revocation_endpoint: 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() - }); + issuer: z.string(), + authorization_endpoint: SafeUrlSchema, + token_endpoint: SafeUrlSchema, + registration_endpoint: 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: SafeUrlSchema.optional(), + revocation_endpoint: 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() +}); /** * OpenID Connect Discovery 1.0 Provider Metadata * see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata */ export const OpenIdProviderMetadataSchema = z.looseObject({ - issuer: z.string(), - authorization_endpoint: SafeUrlSchema, - token_endpoint: SafeUrlSchema, - userinfo_endpoint: SafeUrlSchema.optional(), - jwks_uri: SafeUrlSchema, - registration_endpoint: 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: SafeUrlSchema.optional(), - op_tos_uri: SafeUrlSchema.optional() - }); + issuer: z.string(), + authorization_endpoint: SafeUrlSchema, + token_endpoint: SafeUrlSchema, + userinfo_endpoint: SafeUrlSchema.optional(), + jwks_uri: SafeUrlSchema, + registration_endpoint: 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: SafeUrlSchema.optional(), + op_tos_uri: SafeUrlSchema.optional() +}); /** * OpenID Connect Discovery metadata that may include OAuth 2.0 fields @@ -126,13 +127,13 @@ export const OpenIdProviderDiscoveryMetadataSchema = OpenIdProviderMetadataSchem * OAuth 2.1 token response */ export const 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.number().optional(), - scope: z.string().optional(), - refresh_token: z.string().optional() - }); + 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.number().optional(), + scope: z.string().optional(), + refresh_token: z.string().optional() +}); /** * OAuth 2.1 error response @@ -152,33 +153,33 @@ export const OptionalSafeUrlSchema = SafeUrlSchema.optional().or(z.literal('').t * RFC 7591 OAuth 2.0 Dynamic Client Registration metadata */ export const OAuthClientMetadataSchema = z.object({ - redirect_uris: z.array(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: SafeUrlSchema.optional(), - logo_uri: OptionalSafeUrlSchema, - scope: z.string().optional(), - contacts: z.array(z.string()).optional(), - tos_uri: OptionalSafeUrlSchema, - policy_uri: z.string().optional(), - jwks_uri: SafeUrlSchema.optional(), - jwks: z.any().optional(), - software_id: z.string().optional(), - software_version: z.string().optional(), - software_statement: z.string().optional() - }); + redirect_uris: z.array(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: SafeUrlSchema.optional(), + logo_uri: OptionalSafeUrlSchema, + scope: z.string().optional(), + contacts: z.array(z.string()).optional(), + tos_uri: OptionalSafeUrlSchema, + policy_uri: z.string().optional(), + jwks_uri: SafeUrlSchema.optional(), + jwks: z.any().optional(), + software_id: z.string().optional(), + software_version: z.string().optional(), + software_statement: z.string().optional() +}); /** * RFC 7591 OAuth 2.0 Dynamic Client Registration client information */ export const 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() - }); + client_id: z.string(), + client_secret: z.string().optional(), + client_id_issued_at: z.number().optional(), + client_secret_expires_at: z.number().optional() +}); /** * RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata) @@ -189,17 +190,17 @@ export const OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge( * RFC 7591 OAuth 2.0 Dynamic Client Registration error response */ export const OAuthClientRegistrationErrorSchema = z.object({ - error: z.string(), - error_description: z.string().optional() - }); + error: z.string(), + error_description: z.string().optional() +}); /** * RFC 7009 OAuth 2.0 Token Revocation request */ export const OAuthTokenRevocationRequestSchema = z.object({ - token: z.string(), - token_type_hint: z.string().optional() - }); + token: z.string(), + token_type_hint: z.string().optional() +}); export type OAuthMetadata = z.infer; export type OpenIdProviderMetadata = z.infer; diff --git a/src/types.ts b/src/types.ts index a9ad6cc94..26ed661ac 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,15 +19,15 @@ export const ProgressTokenSchema = z.union([z.string(), z.int()]); export const CursorSchema = z.string(); const RequestMetaSchema = z.looseObject({ - /** - * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. - */ - progressToken: z.optional(ProgressTokenSchema) - }); + /** + * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. + */ + progressToken: z.optional(ProgressTokenSchema) +}); const BaseRequestParamsSchema = z.looseObject({ - _meta: z.optional(RequestMetaSchema) - }); + _meta: z.optional(RequestMetaSchema) +}); export const RequestSchema = z.object({ method: z.string(), @@ -35,12 +35,12 @@ export const RequestSchema = z.object({ }); const BaseNotificationParamsSchema = z.looseObject({ - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); export const NotificationSchema = z.object({ method: z.string(), @@ -48,12 +48,12 @@ export const NotificationSchema = z.object({ }); export const ResultSchema = z.looseObject({ - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); /** * A uniquely identifying ID for a request in JSON-RPC. @@ -63,7 +63,8 @@ export const RequestIdSchema = z.union([z.string(), z.int()]); /** * A request that expects a response. */ -export const JSONRPCRequestSchema = z.strictObject({ +export const JSONRPCRequestSchema = z + .strictObject({ jsonrpc: z.literal(JSONRPC_VERSION), id: RequestIdSchema }) @@ -74,7 +75,8 @@ export const isJSONRPCRequest = (value: unknown): value is JSONRPCRequest => JSO /** * A notification which does not expect a response. */ -export const JSONRPCNotificationSchema = z.strictObject({ +export const JSONRPCNotificationSchema = z + .strictObject({ jsonrpc: z.literal(JSONRPC_VERSION) }) .extend(NotificationSchema.shape); @@ -85,10 +87,10 @@ export const isJSONRPCNotification = (value: unknown): value is JSONRPCNotificat * A successful (non-error) response to a request. */ export const JSONRPCResponseSchema = z.strictObject({ - jsonrpc: z.literal(JSONRPC_VERSION), - id: RequestIdSchema, - result: ResultSchema - }); + jsonrpc: z.literal(JSONRPC_VERSION), + id: RequestIdSchema, + result: ResultSchema +}); export const isJSONRPCResponse = (value: unknown): value is JSONRPCResponse => JSONRPCResponseSchema.safeParse(value).success; @@ -112,23 +114,23 @@ export enum ErrorCode { * A response to a request that indicates an error occurred. */ export const JSONRPCErrorSchema = z.strictObject({ - jsonrpc: z.literal(JSONRPC_VERSION), - id: RequestIdSchema, - error: z.strictObject({ - /** - * The error type that occurred. - */ - code: z.int(), - /** - * A short description of the error. The message SHOULD be limited to a concise single sentence. - */ - message: z.string(), - /** - * Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). - */ - data: z.optional(z.unknown()) - }) - }); + jsonrpc: z.literal(JSONRPC_VERSION), + id: RequestIdSchema, + error: z.strictObject({ + /** + * The error type that occurred. + */ + code: z.int(), + /** + * A short description of the error. The message SHOULD be limited to a concise single sentence. + */ + message: z.string(), + /** + * Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + */ + data: z.optional(z.unknown()) + }) +}); export const isJSONRPCError = (value: unknown): value is JSONRPCError => JSONRPCErrorSchema.safeParse(value).success; @@ -172,58 +174,58 @@ export const CancelledNotificationSchema = NotificationSchema.extend({ * Icon schema for use in tools, prompts, resources, and implementations. */ export const IconSchema = z.looseObject({ - /** - * URL or data URI for the icon. - */ - src: z.string(), - /** - * Optional MIME type for the icon. - */ - mimeType: z.optional(z.string()), - /** - * Optional array of strings that specify sizes at which the icon can be used. - * Each string should be in WxH format (e.g., `"48x48"`, `"96x96"`) or `"any"` for scalable formats like SVG. - * - * If not provided, the client should assume that the icon can be used at any size. - */ - sizes: z.optional(z.array(z.string())) - }); + /** + * URL or data URI for the icon. + */ + src: z.string(), + /** + * Optional MIME type for the icon. + */ + mimeType: z.optional(z.string()), + /** + * Optional array of strings that specify sizes at which the icon can be used. + * Each string should be in WxH format (e.g., `"48x48"`, `"96x96"`) or `"any"` for scalable formats like SVG. + * + * If not provided, the client should assume that the icon can be used at any size. + */ + sizes: z.optional(z.array(z.string())) +}); /** * Base schema to add `icons` property. * */ export const IconsSchema = z.looseObject({ - /** - * Optional set of sized icons that the client can display in a user interface. - * - * Clients that support rendering icons MUST support at least the following MIME types: - * - `image/png` - PNG images (safe, universal compatibility) - * - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) - * - * Clients that support rendering icons SHOULD also support: - * - `image/svg+xml` - SVG images (scalable but requires security precautions) - * - `image/webp` - WebP images (modern, efficient format) - */ - icons: z.array(IconSchema).optional() - }); + /** + * Optional set of sized icons that the client can display in a user interface. + * + * Clients that support rendering icons MUST support at least the following MIME types: + * - `image/png` - PNG images (safe, universal compatibility) + * - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + * + * Clients that support rendering icons SHOULD also support: + * - `image/svg+xml` - SVG images (scalable but requires security precautions) + * - `image/webp` - WebP images (modern, efficient format) + */ + icons: z.array(IconSchema).optional() +}); /** * Base metadata interface for common properties across resources, tools, prompts, and implementations. */ export const BaseMetadataSchema = z.looseObject({ - /** Intended for programmatic or logical use, but used as a display name in past specs or fallback */ - name: z.string(), - /** - * Intended for UI and end-user contexts — optimized to be human-readable and easily understood, - * even by those unfamiliar with domain-specific terminology. - * - * If not provided, the name should be used for display (except for Tool, - * where `annotations.title` should be given precedence over using `name`, - * if present). - */ - title: z.optional(z.string()) - }); + /** Intended for programmatic or logical use, but used as a display name in past specs or fallback */ + name: z.string(), + /** + * Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + * even by those unfamiliar with domain-specific terminology. + * + * If not provided, the name should be used for display (except for Tool, + * where `annotations.title` should be given precedence over using `name`, + * if present). + */ + title: z.optional(z.string()) +}); /* Initialization */ /** @@ -241,30 +243,30 @@ export const ImplementationSchema = BaseMetadataSchema.extend({ * Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities. */ export const ClientCapabilitiesSchema = z.looseObject({ - /** - * Experimental, non-standard capabilities that the client supports. - */ - experimental: z.optional(z.looseObject({})), - /** - * Present if the client supports sampling from an LLM. - */ - sampling: z.optional(z.looseObject({})), - /** - * Present if the client supports eliciting user input. - */ - elicitation: z.optional(z.looseObject({})), - /** - * Present if the client supports listing roots. - */ - roots: z.optional( - z.looseObject({ - /** - * Whether the client supports issuing notifications for changes to the roots list. - */ - listChanged: z.optional(z.boolean()) - }) - ) - }); + /** + * Experimental, non-standard capabilities that the client supports. + */ + experimental: z.optional(z.looseObject({})), + /** + * Present if the client supports sampling from an LLM. + */ + sampling: z.optional(z.looseObject({})), + /** + * Present if the client supports eliciting user input. + */ + elicitation: z.optional(z.looseObject({})), + /** + * Present if the client supports listing roots. + */ + roots: z.optional( + z.looseObject({ + /** + * Whether the client supports issuing notifications for changes to the roots list. + */ + listChanged: z.optional(z.boolean()) + }) + ) +}); /** * This request is sent from the client to the server when it first connects, asking it to begin initialization. @@ -287,57 +289,57 @@ export const isInitializeRequest = (value: unknown): value is InitializeRequest * Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. */ export const ServerCapabilitiesSchema = z.looseObject({ - /** - * Experimental, non-standard capabilities that the server supports. - */ - experimental: z.optional(z.looseObject({})), - /** - * Present if the server supports sending log messages to the client. - */ - logging: z.optional(z.looseObject({})), - /** - * Present if the server supports sending completions to the client. - */ - completions: z.optional(z.looseObject({})), - /** - * Present if the server offers any prompt templates. - */ - prompts: z.optional( - z.looseObject({ - /** - * Whether this server supports issuing notifications for changes to the prompt list. - */ - listChanged: z.optional(z.boolean()) - }) - ), - /** - * Present if the server offers any resources to read. - */ - resources: z.optional( - z.looseObject({ - /** - * Whether this server supports clients subscribing to resource updates. - */ - subscribe: z.optional(z.boolean()), - - /** - * Whether this server supports issuing notifications for changes to the resource list. - */ - listChanged: z.optional(z.boolean()) - }) - ), - /** - * Present if the server offers any tools to call. - */ - tools: z.optional( - z.looseObject({ - /** - * Whether this server supports issuing notifications for changes to the tool list. - */ - listChanged: z.optional(z.boolean()) - }) - ) - }); + /** + * Experimental, non-standard capabilities that the server supports. + */ + experimental: z.optional(z.looseObject({})), + /** + * Present if the server supports sending log messages to the client. + */ + logging: z.optional(z.looseObject({})), + /** + * Present if the server supports sending completions to the client. + */ + completions: z.optional(z.looseObject({})), + /** + * Present if the server offers any prompt templates. + */ + prompts: z.optional( + z.looseObject({ + /** + * Whether this server supports issuing notifications for changes to the prompt list. + */ + listChanged: z.optional(z.boolean()) + }) + ), + /** + * Present if the server offers any resources to read. + */ + resources: z.optional( + z.looseObject({ + /** + * Whether this server supports clients subscribing to resource updates. + */ + subscribe: z.optional(z.boolean()), + + /** + * Whether this server supports issuing notifications for changes to the resource list. + */ + listChanged: z.optional(z.boolean()) + }) + ), + /** + * Present if the server offers any tools to call. + */ + tools: z.optional( + z.looseObject({ + /** + * Whether this server supports issuing notifications for changes to the tool list. + */ + listChanged: z.optional(z.boolean()) + }) + ) +}); /** * After receiving an initialize request from the client, the server sends this response. @@ -377,19 +379,19 @@ export const PingRequestSchema = RequestSchema.extend({ /* Progress notifications */ export const ProgressSchema = z.looseObject({ - /** - * The progress thus far. This should increase every time progress is made, even if the total is unknown. - */ - progress: z.number(), - /** - * Total number of items to process (or total progress required), if known. - */ - total: z.optional(z.number()), - /** - * An optional message describing the current progress. - */ - message: z.optional(z.string()) - }); + /** + * The progress thus far. This should increase every time progress is made, even if the total is unknown. + */ + progress: z.number(), + /** + * Total number of items to process (or total progress required), if known. + */ + total: z.optional(z.number()), + /** + * An optional message describing the current progress. + */ + message: z.optional(z.string()) +}); /** * An out-of-band notification used to inform the receiver of a progress update for a long-running request. @@ -428,20 +430,20 @@ export const PaginatedResultSchema = ResultSchema.extend({ * The contents of a specific resource or sub-resource. */ export const ResourceContentsSchema = z.looseObject({ - /** - * The URI of this resource. - */ - uri: z.string(), - /** - * The MIME type of this resource, if known. - */ - mimeType: z.optional(z.string()), - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * The URI of this resource. + */ + uri: z.string(), + /** + * The MIME type of this resource, if known. + */ + mimeType: z.optional(z.string()), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); export const TextResourceContentsSchema = ResourceContentsSchema.extend({ /** @@ -633,19 +635,19 @@ export const ResourceUpdatedNotificationSchema = NotificationSchema.extend({ * Describes an argument that a prompt can accept. */ export const PromptArgumentSchema = z.looseObject({ - /** - * The name of the argument. - */ - name: z.string(), - /** - * A human-readable description of the argument. - */ - description: z.optional(z.string()), - /** - * Whether this argument must be provided. - */ - required: z.optional(z.boolean()) - }); + /** + * The name of the argument. + */ + name: z.string(), + /** + * A human-readable description of the argument. + */ + description: z.optional(z.string()), + /** + * Whether this argument must be provided. + */ + required: z.optional(z.boolean()) +}); /** * A prompt or prompt template that the server offers. @@ -701,73 +703,73 @@ export const GetPromptRequestSchema = RequestSchema.extend({ * Text provided to or from an LLM. */ export const TextContentSchema = z.looseObject({ - type: z.literal('text'), - /** - * The text content of the message. - */ - text: z.string(), + type: z.literal('text'), + /** + * The text content of the message. + */ + text: z.string(), - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); /** * An image provided to or from an LLM. */ export const ImageContentSchema = z.looseObject({ - type: z.literal('image'), - /** - * The base64-encoded image data. - */ - data: Base64Schema, - /** - * The MIME type of the image. Different providers may support different image types. - */ - mimeType: z.string(), + type: z.literal('image'), + /** + * The base64-encoded image data. + */ + data: Base64Schema, + /** + * The MIME type of the image. Different providers may support different image types. + */ + mimeType: z.string(), - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); /** * An Audio provided to or from an LLM. */ export const AudioContentSchema = z.looseObject({ - type: z.literal('audio'), - /** - * The base64-encoded audio data. - */ - data: Base64Schema, - /** - * The MIME type of the audio. Different providers may support different audio types. - */ - mimeType: z.string(), + type: z.literal('audio'), + /** + * The base64-encoded audio data. + */ + data: Base64Schema, + /** + * The MIME type of the audio. Different providers may support different audio types. + */ + mimeType: z.string(), - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); /** * The contents of a resource, embedded into a prompt or tool call result. */ export const EmbeddedResourceSchema = z.looseObject({ - type: z.literal('resource'), - resource: z.union([TextResourceContentsSchema, BlobResourceContentsSchema]), - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + type: z.literal('resource'), + resource: z.union([TextResourceContentsSchema, BlobResourceContentsSchema]), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); /** * A resource that the server is capable of reading, included in a prompt or tool call result. @@ -793,9 +795,9 @@ export const ContentBlockSchema = z.union([ * Describes a message returned as part of a prompt. */ export const PromptMessageSchema = z.looseObject({ - role: z.enum(['user', 'assistant']), - content: ContentBlockSchema - }); + role: z.enum(['user', 'assistant']), + content: ContentBlockSchema +}); /** * The server's response to a prompts/get request from the client. @@ -827,48 +829,48 @@ export const PromptListChangedNotificationSchema = NotificationSchema.extend({ * received from untrusted servers. */ export const ToolAnnotationsSchema = z.looseObject({ - /** - * A human-readable title for the tool. - */ - title: z.optional(z.string()), + /** + * A human-readable title for the tool. + */ + title: z.optional(z.string()), - /** - * If true, the tool does not modify its environment. - * - * Default: false - */ - readOnlyHint: z.optional(z.boolean()), + /** + * If true, the tool does not modify its environment. + * + * Default: false + */ + readOnlyHint: z.optional(z.boolean()), - /** - * If true, the tool may perform destructive updates to its environment. - * If false, the tool performs only additive updates. - * - * (This property is meaningful only when `readOnlyHint == false`) - * - * Default: true - */ - destructiveHint: z.optional(z.boolean()), + /** + * If true, the tool may perform destructive updates to its environment. + * If false, the tool performs only additive updates. + * + * (This property is meaningful only when `readOnlyHint == false`) + * + * Default: true + */ + destructiveHint: z.optional(z.boolean()), - /** - * If true, calling the tool repeatedly with the same arguments - * will have no additional effect on the its environment. - * - * (This property is meaningful only when `readOnlyHint == false`) - * - * Default: false - */ - idempotentHint: z.optional(z.boolean()), + /** + * If true, calling the tool repeatedly with the same arguments + * will have no additional effect on the its environment. + * + * (This property is meaningful only when `readOnlyHint == false`) + * + * Default: false + */ + idempotentHint: z.optional(z.boolean()), - /** - * If true, this tool may interact with an "open world" of external - * entities. If false, the tool's domain of interaction is closed. - * For example, the world of a web search tool is open, whereas that - * of a memory tool is not. - * - * Default: true - */ - openWorldHint: z.optional(z.boolean()) - }); + /** + * If true, this tool may interact with an "open world" of external + * entities. If false, the tool's domain of interaction is closed. + * For example, the world of a web search tool is open, whereas that + * of a memory tool is not. + * + * Default: true + */ + openWorldHint: z.optional(z.boolean()) +}); /** * Definition for a tool the client can call. @@ -882,20 +884,20 @@ export const ToolSchema = BaseMetadataSchema.extend({ * A JSON Schema object defining the expected parameters for the tool. */ inputSchema: z.looseObject({ - type: z.literal('object'), - properties: z.optional(z.looseObject({})), - required: z.optional(z.array(z.string())) - }), + type: z.literal('object'), + properties: z.optional(z.looseObject({})), + required: z.optional(z.array(z.string())) + }), /** * An optional JSON Schema object defining the structure of the tool's output returned in * the structuredContent field of a CallToolResult. */ outputSchema: z.optional( z.looseObject({ - type: z.literal('object'), - properties: z.optional(z.looseObject({})), - required: z.optional(z.array(z.string())) - }) + type: z.literal('object'), + properties: z.optional(z.looseObject({})), + required: z.optional(z.array(z.string())) + }) ), /** * Optional additional tool information. @@ -1031,41 +1033,41 @@ export const LoggingMessageNotificationSchema = NotificationSchema.extend({ * Hints to use for model selection. */ export const ModelHintSchema = z.looseObject({ - /** - * A hint for a model name. - */ - name: z.string().optional() - }); + /** + * A hint for a model name. + */ + name: z.string().optional() +}); /** * The server's preferences for model selection, requested of the client during sampling. */ export const ModelPreferencesSchema = z.looseObject({ - /** - * Optional hints to use for model selection. - */ - hints: z.optional(z.array(ModelHintSchema)), - /** - * How much to prioritize cost when selecting a model. - */ - costPriority: z.optional(z.number().min(0).max(1)), - /** - * How much to prioritize sampling speed (latency) when selecting a model. - */ - speedPriority: z.optional(z.number().min(0).max(1)), - /** - * How much to prioritize intelligence and capabilities when selecting a model. - */ - intelligencePriority: z.optional(z.number().min(0).max(1)) - }); + /** + * Optional hints to use for model selection. + */ + hints: z.optional(z.array(ModelHintSchema)), + /** + * How much to prioritize cost when selecting a model. + */ + costPriority: z.optional(z.number().min(0).max(1)), + /** + * How much to prioritize sampling speed (latency) when selecting a model. + */ + speedPriority: z.optional(z.number().min(0).max(1)), + /** + * How much to prioritize intelligence and capabilities when selecting a model. + */ + intelligencePriority: z.optional(z.number().min(0).max(1)) +}); /** * Describes a message issued to or received from an LLM API. */ export const SamplingMessageSchema = z.looseObject({ - role: z.enum(['user', 'assistant']), - content: z.union([TextContentSchema, ImageContentSchema, AudioContentSchema]) - }); + role: z.enum(['user', 'assistant']), + content: z.union([TextContentSchema, ImageContentSchema, AudioContentSchema]) +}); /** * A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. @@ -1120,45 +1122,45 @@ export const CreateMessageResultSchema = ResultSchema.extend({ * Primitive schema definition for boolean fields. */ export const BooleanSchemaSchema = z.looseObject({ - type: z.literal('boolean'), - title: z.optional(z.string()), - description: z.optional(z.string()), - default: z.optional(z.boolean()) - }); + type: z.literal('boolean'), + title: z.optional(z.string()), + description: z.optional(z.string()), + default: z.optional(z.boolean()) +}); /** * Primitive schema definition for string fields. */ export const StringSchemaSchema = z.looseObject({ - type: z.literal('string'), - title: z.optional(z.string()), - description: z.optional(z.string()), - minLength: z.optional(z.number()), - maxLength: z.optional(z.number()), - format: z.optional(z.enum(['email', 'uri', 'date', 'date-time'])) - }); + type: z.literal('string'), + title: z.optional(z.string()), + description: z.optional(z.string()), + minLength: z.optional(z.number()), + maxLength: z.optional(z.number()), + format: z.optional(z.enum(['email', 'uri', 'date', 'date-time'])) +}); /** * Primitive schema definition for number fields. */ export const NumberSchemaSchema = z.looseObject({ - type: z.enum(['number', 'integer']), - title: z.optional(z.string()), - description: z.optional(z.string()), - minimum: z.optional(z.number()), - maximum: z.optional(z.number()) - }); + type: z.enum(['number', 'integer']), + title: z.optional(z.string()), + description: z.optional(z.string()), + minimum: z.optional(z.number()), + maximum: z.optional(z.number()) +}); /** * Primitive schema definition for enum fields. */ export const EnumSchemaSchema = z.looseObject({ - type: z.literal('string'), - title: z.optional(z.string()), - description: z.optional(z.string()), - enum: z.array(z.string()), - enumNames: z.optional(z.array(z.string())) - }); + type: z.literal('string'), + title: z.optional(z.string()), + description: z.optional(z.string()), + enum: z.array(z.string()), + enumNames: z.optional(z.array(z.string())) +}); /** * Union of all primitive schema definitions. @@ -1180,10 +1182,10 @@ export const ElicitRequestSchema = RequestSchema.extend({ * The schema for the requested user input. */ requestedSchema: z.looseObject({ - type: z.literal('object'), - properties: z.record(z.string(), PrimitiveSchemaDefinitionSchema), - required: z.optional(z.array(z.string())) - }) + type: z.literal('object'), + properties: z.record(z.string(), PrimitiveSchemaDefinitionSchema), + required: z.optional(z.array(z.string())) + }) }) }); @@ -1206,12 +1208,12 @@ export const ElicitResultSchema = ResultSchema.extend({ * A reference to a resource or resource template definition. */ export const ResourceTemplateReferenceSchema = z.looseObject({ - type: z.literal('ref/resource'), - /** - * The URI or URI template of the resource. - */ - uri: z.string() - }); + type: z.literal('ref/resource'), + /** + * The URI or URI template of the resource. + */ + uri: z.string() +}); /** * @deprecated Use ResourceTemplateReferenceSchema instead @@ -1222,12 +1224,12 @@ export const ResourceReferenceSchema = ResourceTemplateReferenceSchema; * Identifies a prompt. */ export const PromptReferenceSchema = z.looseObject({ - type: z.literal('ref/prompt'), - /** - * The name of the prompt or prompt template - */ - name: z.string() - }); + type: z.literal('ref/prompt'), + /** + * The name of the prompt or prompt template + */ + name: z.string() +}); /** * A request from the client to the server, to ask for completion options. @@ -1240,15 +1242,15 @@ export const CompleteRequestSchema = RequestSchema.extend({ * The argument's information */ argument: z.looseObject({ - /** - * The name of the argument - */ - name: z.string(), - /** - * The value of the argument to use for completion matching. - */ - value: z.string() - }), + /** + * The name of the argument + */ + name: z.string(), + /** + * The value of the argument to use for completion matching. + */ + value: z.string() + }), context: z.optional( z.object({ /** @@ -1265,19 +1267,19 @@ export const CompleteRequestSchema = RequestSchema.extend({ */ export const CompleteResultSchema = ResultSchema.extend({ completion: z.looseObject({ - /** - * An array of completion values. Must not exceed 100 items. - */ - values: z.array(z.string()).max(100), - /** - * The total number of completion options available. This can exceed the number of values actually sent in the response. - */ - total: z.optional(z.int()), - /** - * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. - */ - hasMore: z.optional(z.boolean()) - }) + /** + * An array of completion values. Must not exceed 100 items. + */ + values: z.array(z.string()).max(100), + /** + * The total number of completion options available. This can exceed the number of values actually sent in the response. + */ + total: z.optional(z.int()), + /** + * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + hasMore: z.optional(z.boolean()) + }) }); /* Roots */ @@ -1285,21 +1287,21 @@ export const CompleteResultSchema = ResultSchema.extend({ * Represents a root directory or file that the server can operate on. */ export const RootSchema = z.looseObject({ - /** - * The URI identifying the root. This *must* start with file:// for now. - */ - uri: z.string().startsWith('file://'), - /** - * An optional name for the root. - */ - name: z.optional(z.string()), + /** + * The URI identifying the root. This *must* start with file:// for now. + */ + uri: z.string().startsWith('file://'), + /** + * An optional name for the root. + */ + name: z.optional(z.string()), - /** - * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) - * for notes on _meta usage. - */ - _meta: z.optional(z.looseObject({})) - }); + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: z.optional(z.looseObject({})) +}); /** * Sent from the server to request a list of root URIs from the client.