diff --git a/src/client/index.ts b/src/client/index.ts index 5770f9d7f..7c242e46c 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -7,7 +7,6 @@ import { type ClientNotification, type ClientRequest, type ClientResult, - type CompatibilityCallToolResultSchema, type CompleteRequest, CompleteResultSchema, EmptyResultSchema, @@ -27,18 +26,18 @@ import { ListToolsResultSchema, type LoggingLevel, McpError, - type Notification, type ReadResourceRequest, ReadResourceResultSchema, - type Request, - type Result, type ServerCapabilities, SUPPORTED_PROTOCOL_VERSIONS, type SubscribeRequest, type Tool, type UnsubscribeRequest, ElicitResultSchema, - ElicitRequestSchema + ElicitRequestSchema, + type RequestGeneric, + type NotificationGeneric, + type Result } from '../types.js'; import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js'; import type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator } from '../validation/types.js'; @@ -149,8 +148,8 @@ export type ClientOptions = ProtocolOptions & { * ``` */ export class Client< - RequestT extends Request = Request, - NotificationT extends Notification = Notification, + RequestT extends RequestGeneric = RequestGeneric, + NotificationT extends NotificationGeneric = NotificationGeneric, ResultT extends Result = Result > extends Protocol { private _serverCapabilities?: ServerCapabilities; @@ -461,7 +460,7 @@ export class Client< async callTool( params: CallToolRequest['params'], - resultSchema: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema = CallToolResultSchema, + resultSchema: typeof CallToolResultSchema = CallToolResultSchema, options?: RequestOptions ) { const result = await this.request({ method: 'tools/call', params }, resultSchema, options); diff --git a/src/server/index.ts b/src/server/index.ts index 47b5f538f..9c6fb1385 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -20,16 +20,16 @@ import { LoggingLevelSchema, type LoggingMessageNotification, McpError, - type Notification, - type Request, type ResourceUpdatedNotification, - type Result, type ServerCapabilities, type ServerNotification, type ServerRequest, type ServerResult, SetLevelRequestSchema, - SUPPORTED_PROTOCOL_VERSIONS + SUPPORTED_PROTOCOL_VERSIONS, + type RequestGeneric, + type NotificationGeneric, + type Result } from '../types.js'; import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js'; import type { JsonSchemaType, jsonSchemaValidator } from '../validation/types.js'; @@ -104,8 +104,8 @@ export type ServerOptions = ProtocolOptions & { * @deprecated Use `McpServer` instead for the high-level API. Only use `Server` for advanced use cases. */ export class Server< - RequestT extends Request = Request, - NotificationT extends Notification = Notification, + RequestT extends RequestGeneric = RequestGeneric, + NotificationT extends NotificationGeneric = NotificationGeneric, ResultT extends Result = Result > extends Protocol { private _clientCapabilities?: ClientCapabilities; diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index e2291481a..a56d3787d 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4273,6 +4273,7 @@ describe('elicitInput()', () => { expect(checkAvailability).toHaveBeenCalledWith('ABC Restaurant', '2024-12-25', 2); expect(findAlternatives).not.toHaveBeenCalled(); + expect(result.content).toEqual([ { type: 'text', diff --git a/src/shared/protocol.test.ts b/src/shared/protocol.test.ts index b47de8c55..b57a62ade 100644 --- a/src/shared/protocol.test.ts +++ b/src/shared/protocol.test.ts @@ -1,5 +1,5 @@ import { ZodType, z } from 'zod'; -import { ClientCapabilities, ErrorCode, McpError, Notification, Request, Result, ServerCapabilities } from '../types.js'; +import { ClientCapabilities, ErrorCode, McpError, NotificationGeneric, RequestGeneric, Result, ServerCapabilities } from '../types.js'; import { Protocol, mergeCapabilities } from './protocol.js'; import { Transport } from './transport.js'; import { MockInstance } from 'vitest'; @@ -18,14 +18,14 @@ class MockTransport implements Transport { } describe('protocol tests', () => { - let protocol: Protocol; + let protocol: Protocol; let transport: MockTransport; let sendSpy: MockInstance; beforeEach(() => { transport = new MockTransport(); sendSpy = vi.spyOn(transport, 'send'); - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -479,7 +479,7 @@ describe('protocol tests', () => { it('should NOT debounce a notification that has parameters', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -500,7 +500,7 @@ describe('protocol tests', () => { it('should NOT debounce a notification that has a relatedRequestId', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -519,7 +519,7 @@ describe('protocol tests', () => { it('should clear pending debounced notifications on connection close', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -543,7 +543,7 @@ describe('protocol tests', () => { it('should debounce multiple synchronous calls when params property is omitted', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -570,7 +570,7 @@ describe('protocol tests', () => { it('should debounce calls when params is explicitly undefined', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -595,7 +595,7 @@ describe('protocol tests', () => { it('should send non-debounced notifications immediately and multiple times', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} @@ -628,7 +628,7 @@ describe('protocol tests', () => { it('should handle sequential batches of debounced notifications correctly', async () => { // ARRANGE - protocol = new (class extends Protocol { + protocol = new (class extends Protocol { protected assertCapabilityForMethod(): void {} protected assertNotificationCapability(): void {} protected assertRequestHandlerCapability(): void {} diff --git a/src/shared/protocol.ts b/src/shared/protocol.ts index 48cad896f..91c5ade58 100644 --- a/src/shared/protocol.ts +++ b/src/shared/protocol.ts @@ -19,11 +19,13 @@ import { ProgressNotificationSchema, Request, RequestId, - Result, ServerCapabilities, RequestMeta, MessageExtraInfo, - RequestInfo + RequestInfo, + type RequestGeneric, + type NotificationGeneric, + type Result } from '../types.js'; import { Transport, TransportSendOptions } from './transport.js'; import { AuthInfo } from '../server/auth/types.js'; @@ -171,7 +173,11 @@ type TimeoutInfo = { * Implements MCP protocol framing on top of a pluggable transport, including * features like request/response linking, notifications, and progress. */ -export abstract class Protocol { +export abstract class Protocol< + SendRequestT extends RequestGeneric, + SendNotificationT extends NotificationGeneric, + SendResultT extends Result +> { private _transport?: Transport; private _requestMessageId = 0; private _requestHandlers: Map< diff --git a/src/spec.types.test.ts b/src/spec.types.test.ts index 2417e6b1d..ad43b4eb2 100644 --- a/src/spec.types.test.ts +++ b/src/spec.types.test.ts @@ -62,6 +62,12 @@ type MakeUnknownsNotOptional = } : T; +/** + * Spec Patches + * + * Temporary spec type patches, to be used until the spec is updated to fix the issues. + */ + // Targeted fix: in spec, treat ClientCapabilities.elicitation?: object as Record type FixSpecClientCapabilities = T extends { elicitation?: object } ? Omit & { elicitation?: Record } @@ -75,6 +81,14 @@ type FixSpecInitializeRequest = T extends { params: infer P } ? Omit = T extends { params: infer P } ? Omit & { params: FixSpecInitializeRequestParams

} : T; +// ElicitResult - spec does not allow undefined values in the content object +type SpecElicitResultPatched = Omit & { + action: SpecTypes.ElicitResult['action']; + content?: { + [key: string]: string | number | boolean | string[] | undefined; + }; +}; + const sdkTypeChecks = { RequestParams: (sdk: SDKTypes.RequestParams, spec: SpecTypes.RequestParams) => { sdk = spec; @@ -169,19 +183,19 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ProgressNotification: (sdk: RemovePassthrough>, spec: SpecTypes.ProgressNotification) => { + ProgressNotification: (sdk: WithJSONRPC, spec: SpecTypes.ProgressNotification) => { sdk = spec; spec = sdk; }, - SubscribeRequest: (sdk: RemovePassthrough>, spec: SpecTypes.SubscribeRequest) => { + SubscribeRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.SubscribeRequest) => { sdk = spec; spec = sdk; }, - UnsubscribeRequest: (sdk: RemovePassthrough>, spec: SpecTypes.UnsubscribeRequest) => { + UnsubscribeRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.UnsubscribeRequest) => { sdk = spec; spec = sdk; }, - PaginatedRequest: (sdk: RemovePassthrough>, spec: SpecTypes.PaginatedRequest) => { + PaginatedRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.PaginatedRequest) => { sdk = spec; spec = sdk; }, @@ -189,7 +203,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ListRootsRequest: (sdk: RemovePassthrough>, spec: SpecTypes.ListRootsRequest) => { + ListRootsRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.ListRootsRequest) => { sdk = spec; spec = sdk; }, @@ -205,7 +219,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ElicitResult: (sdk: SDKTypes.ElicitResult, spec: SpecTypes.ElicitResult) => { + ElicitResult: (sdk: SDKTypes.ElicitResult, spec: SpecElicitResultPatched) => { sdk = spec; spec = sdk; }, @@ -261,7 +275,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ClientNotification: (sdk: RemovePassthrough>, spec: SpecTypes.ClientNotification) => { + ClientNotification: (sdk: WithJSONRPC, spec: SpecTypes.ClientNotification) => { sdk = spec; spec = sdk; }, @@ -285,7 +299,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ListToolsRequest: (sdk: RemovePassthrough>, spec: SpecTypes.ListToolsRequest) => { + ListToolsRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.ListToolsRequest) => { sdk = spec; spec = sdk; }, @@ -297,42 +311,36 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - CallToolRequest: (sdk: RemovePassthrough>, spec: SpecTypes.CallToolRequest) => { + CallToolRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.CallToolRequest) => { sdk = spec; spec = sdk; }, - ToolListChangedNotification: ( - sdk: RemovePassthrough>, - spec: SpecTypes.ToolListChangedNotification - ) => { + ToolListChangedNotification: (sdk: WithJSONRPC, spec: SpecTypes.ToolListChangedNotification) => { sdk = spec; spec = sdk; }, ResourceListChangedNotification: ( - sdk: RemovePassthrough>, + sdk: WithJSONRPC, spec: SpecTypes.ResourceListChangedNotification ) => { sdk = spec; spec = sdk; }, PromptListChangedNotification: ( - sdk: RemovePassthrough>, + sdk: WithJSONRPC, spec: SpecTypes.PromptListChangedNotification ) => { sdk = spec; spec = sdk; }, RootsListChangedNotification: ( - sdk: RemovePassthrough>, + sdk: WithJSONRPC, spec: SpecTypes.RootsListChangedNotification ) => { sdk = spec; spec = sdk; }, - ResourceUpdatedNotification: ( - sdk: RemovePassthrough>, - spec: SpecTypes.ResourceUpdatedNotification - ) => { + ResourceUpdatedNotification: (sdk: WithJSONRPC, spec: SpecTypes.ResourceUpdatedNotification) => { sdk = spec; spec = sdk; }, @@ -344,25 +352,19 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - SetLevelRequest: (sdk: RemovePassthrough>, spec: SpecTypes.SetLevelRequest) => { + SetLevelRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.SetLevelRequest) => { sdk = spec; spec = sdk; }, - PingRequest: (sdk: RemovePassthrough>, spec: SpecTypes.PingRequest) => { + PingRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.PingRequest) => { sdk = spec; spec = sdk; }, - InitializedNotification: ( - sdk: RemovePassthrough>, - spec: SpecTypes.InitializedNotification - ) => { + InitializedNotification: (sdk: WithJSONRPC, spec: SpecTypes.InitializedNotification) => { sdk = spec; spec = sdk; }, - ListResourcesRequest: ( - sdk: RemovePassthrough>, - spec: SpecTypes.ListResourcesRequest - ) => { + ListResourcesRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.ListResourcesRequest) => { sdk = spec; spec = sdk; }, @@ -371,7 +373,7 @@ const sdkTypeChecks = { spec = sdk; }, ListResourceTemplatesRequest: ( - sdk: RemovePassthrough>, + sdk: WithJSONRPCRequest, spec: SpecTypes.ListResourceTemplatesRequest ) => { sdk = spec; @@ -381,10 +383,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ReadResourceRequest: ( - sdk: RemovePassthrough>, - spec: SpecTypes.ReadResourceRequest - ) => { + ReadResourceRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.ReadResourceRequest) => { sdk = spec; spec = sdk; }, @@ -420,7 +419,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ListPromptsRequest: (sdk: RemovePassthrough>, spec: SpecTypes.ListPromptsRequest) => { + ListPromptsRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.ListPromptsRequest) => { sdk = spec; spec = sdk; }, @@ -428,7 +427,7 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - GetPromptRequest: (sdk: RemovePassthrough>, spec: SpecTypes.GetPromptRequest) => { + GetPromptRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.GetPromptRequest) => { sdk = spec; spec = sdk; }, @@ -543,28 +542,22 @@ const sdkTypeChecks = { sdk = spec; spec = sdk; }, - ClientRequest: ( - sdk: RemovePassthrough>, - spec: FixSpecClientRequest - ) => { + ClientRequest: (sdk: WithJSONRPCRequest, spec: FixSpecClientRequest) => { sdk = spec; spec = sdk; }, - ServerRequest: (sdk: RemovePassthrough>, spec: SpecTypes.ServerRequest) => { + ServerRequest: (sdk: WithJSONRPCRequest, spec: SpecTypes.ServerRequest) => { sdk = spec; spec = sdk; }, LoggingMessageNotification: ( - sdk: RemovePassthrough>>, + sdk: MakeUnknownsNotOptional>, spec: SpecTypes.LoggingMessageNotification ) => { sdk = spec; spec = sdk; }, - ServerNotification: ( - sdk: MakeUnknownsNotOptional>>, - spec: SpecTypes.ServerNotification - ) => { + ServerNotification: (sdk: MakeUnknownsNotOptional>, spec: SpecTypes.ServerNotification) => { sdk = spec; spec = sdk; }, diff --git a/src/types.ts b/src/types.ts index 66cc34941..ee50809d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -28,17 +28,12 @@ export const ProgressTokenSchema = z.union([z.string(), z.number().int()]); */ export const CursorSchema = z.string(); -const RequestMetaSchema = z - .object({ - /** - * 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: ProgressTokenSchema.optional() - }) +const RequestMetaSchema = z.object({ /** - * Passthrough required here because we want to allow any additional fields to be added to the request meta. + * 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. */ - .passthrough(); + progressToken: ProgressTokenSchema.optional() +}); /** * Common params for any request. @@ -47,14 +42,25 @@ const BaseRequestParamsSchema = z.object({ /** * See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage. */ - _meta: RequestMetaSchema.optional() + _meta: z.intersection(RequestMetaSchema.optional(), z.record(z.string(), z.unknown()).optional()).optional() }); export const RequestSchema = z.object({ method: z.string(), - params: BaseRequestParamsSchema.passthrough().optional() + params: BaseRequestParamsSchema.optional() +}); + +/** + * Generic request schema that allows any additional fields to be added to the params. + * + * Used in {@link JSONRPCRequestSchema} for generic shape matching. + */ +export const RequestSchemaGeneric = RequestSchema.extend({ + params: z.intersection(RequestSchema.shape.params.optional(), z.record(z.string(), z.unknown()).optional()) }); +export type RequestGeneric = ExpandRecursively>; + const NotificationsParamsSchema = z.object({ /** * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) @@ -65,9 +71,20 @@ const NotificationsParamsSchema = z.object({ export const NotificationSchema = z.object({ method: z.string(), - params: NotificationsParamsSchema.passthrough().optional() + params: NotificationsParamsSchema.optional() +}); + +/** + * Generic notification schema that allows any additional fields to be added to the params. + * + * Used in {@link JSONRPCNotificationSchema} for generic shape matching. + */ +export const NotificationSchemaGeneric = NotificationSchema.extend({ + params: z.intersection(NotificationSchema.shape.params, z.record(z.string(), z.unknown()).optional()) }); +export type NotificationGeneric = z.infer; + export const ResultSchema = z .object({ /** @@ -76,10 +93,7 @@ export const ResultSchema = z */ _meta: z.record(z.string(), z.unknown()).optional() }) - /** - * Passthrough required here because we want to allow any additional fields to be added to the result. - */ - .passthrough(); + .catchall(z.unknown()); /** * A uniquely identifying ID for a request in JSON-RPC. @@ -94,7 +108,7 @@ export const JSONRPCRequestSchema = z jsonrpc: z.literal(JSONRPC_VERSION), id: RequestIdSchema }) - .merge(RequestSchema) + .merge(RequestSchemaGeneric) .strict(); export const isJSONRPCRequest = (value: unknown): value is JSONRPCRequest => JSONRPCRequestSchema.safeParse(value).success; @@ -106,7 +120,7 @@ export const JSONRPCNotificationSchema = z .object({ jsonrpc: z.literal(JSONRPC_VERSION) }) - .merge(NotificationSchema) + .merge(NotificationSchemaGeneric) .strict(); export const isJSONRPCNotification = (value: unknown): value is JSONRPCNotification => JSONRPCNotificationSchema.safeParse(value).success; @@ -159,7 +173,7 @@ export const JSONRPCErrorSchema = z /** * 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()) + data: z.unknown().optional() }) }) .strict(); @@ -347,14 +361,14 @@ export const ServerCapabilitiesSchema = z.object({ /** * Present if the server offers any prompt templates. */ - prompts: z.optional( - z.object({ + prompts: z + .object({ /** * Whether this server supports issuing notifications for changes to the prompt list. */ - listChanged: z.optional(z.boolean()) + listChanged: z.boolean().optional() }) - ), + .optional(), /** * Present if the server offers any resources to read. */ @@ -429,11 +443,11 @@ export const ProgressSchema = z.object({ /** * Total number of items to process (or total progress required), if known. */ - total: z.optional(z.number()), + total: z.number().optional(), /** * An optional message describing the current progress. */ - message: z.optional(z.string()) + message: z.string().optional() }); export const ProgressNotificationParamsSchema = NotificationsParamsSchema.merge(ProgressSchema).extend({ @@ -470,7 +484,7 @@ export const PaginatedResultSchema = ResultSchema.extend({ * An opaque token representing the pagination position after the last returned result. * If present, there may be more results available. */ - nextCursor: z.optional(CursorSchema) + nextCursor: CursorSchema.optional() }); /* Resources */ @@ -485,7 +499,7 @@ export const ResourceContentsSchema = z.object({ /** * The MIME type of this resource, if known. */ - mimeType: z.optional(z.string()), + mimeType: z.string().optional(), /** * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) * for notes on _meta usage. @@ -540,18 +554,18 @@ export const ResourceSchema = BaseMetadataSchema.extend({ * * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. */ - description: z.optional(z.string()), + description: z.string().optional(), /** * The MIME type of this resource, if known. */ - mimeType: z.optional(z.string()), + mimeType: z.string().optional(), /** * 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.record(z.string(), z.unknown()).optional() }).merge(IconsSchema); /** @@ -568,18 +582,18 @@ export const ResourceTemplateSchema = BaseMetadataSchema.extend({ * * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. */ - description: z.optional(z.string()), + description: z.string().optional(), /** * The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type. */ - mimeType: z.optional(z.string()), + mimeType: z.string().optional(), /** * 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.record(z.string(), z.unknown()).optional() }).merge(IconsSchema); /** @@ -694,11 +708,11 @@ export const PromptArgumentSchema = z.object({ /** * A human-readable description of the argument. */ - description: z.optional(z.string()), + description: z.string().optional(), /** * Whether this argument must be provided. */ - required: z.optional(z.boolean()) + required: z.boolean().optional() }); /** @@ -708,16 +722,16 @@ export const PromptSchema = BaseMetadataSchema.extend({ /** * An optional description of what this prompt provides */ - description: z.optional(z.string()), + description: z.string().optional(), /** * A list of arguments to use for templating the prompt. */ - arguments: z.optional(z.array(PromptArgumentSchema)), + arguments: z.array(PromptArgumentSchema).optional(), /** * 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.record(z.string(), z.unknown()).optional() }).merge(IconsSchema); /** @@ -862,7 +876,7 @@ export const GetPromptResultSchema = ResultSchema.extend({ /** * An optional description for the prompt. */ - description: z.optional(z.string()), + description: z.string().optional(), messages: z.array(PromptMessageSchema) }); @@ -942,7 +956,7 @@ export const ToolSchema = BaseMetadataSchema.extend({ inputSchema: z.object({ type: z.literal('object'), properties: z.record(z.string(), AssertObjectSchema).optional(), - required: z.optional(z.array(z.string())) + required: z.array(z.string()).optional() }), /** * An optional JSON Schema object defining the structure of the tool's output returned in @@ -952,17 +966,17 @@ export const ToolSchema = BaseMetadataSchema.extend({ .object({ type: z.literal('object'), properties: z.record(z.string(), AssertObjectSchema).optional(), - required: z.optional(z.array(z.string())), + required: z.array(z.string()).optional(), /** * Not in the MCP specification, but added to support the Ajv validator while removing .passthrough() which previously allowed additionalProperties to be passed through. */ - additionalProperties: z.optional(z.boolean()) + additionalProperties: z.boolean().optional() }) .optional(), /** * Optional additional tool information. */ - annotations: z.optional(ToolAnnotationsSchema), + annotations: ToolAnnotationsSchema.optional(), /** * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) @@ -1018,18 +1032,9 @@ export const CallToolResultSchema = ResultSchema.extend({ * server does not support tool calls, or any other exceptional conditions, * should be reported as an MCP error response. */ - isError: z.optional(z.boolean()) + isError: z.boolean().optional() }); -/** - * CallToolResultSchema extended with backwards compatibility to protocol version 2024-10-07. - */ -export const CompatibilityCallToolResultSchema = CallToolResultSchema.or( - ResultSchema.extend({ - toolResult: z.unknown() - }) -); - /** * Parameters for a `tools/call` request. */ @@ -1041,7 +1046,7 @@ export const CallToolRequestParamsSchema = BaseRequestParamsSchema.extend({ /** * Arguments to pass to the tool. */ - arguments: z.optional(z.record(z.string(), z.unknown())) + arguments: z.record(z.string(), z.unknown()).optional() }); /** @@ -1125,19 +1130,19 @@ export const ModelPreferencesSchema = z.object({ /** * Optional hints to use for model selection. */ - hints: z.optional(z.array(ModelHintSchema)), + hints: z.array(ModelHintSchema).optional(), /** * How much to prioritize cost when selecting a model. */ - costPriority: z.optional(z.number().min(0).max(1)), + costPriority: z.number().min(0).max(1).optional(), /** * How much to prioritize sampling speed (latency) when selecting a model. */ - speedPriority: z.optional(z.number().min(0).max(1)), + speedPriority: z.number().min(0).max(1).optional(), /** * How much to prioritize intelligence and capabilities when selecting a model. */ - intelligencePriority: z.optional(z.number().min(0).max(1)) + intelligencePriority: z.number().min(0).max(1).optional() }); /** @@ -1197,7 +1202,7 @@ export const CreateMessageResultSchema = ResultSchema.extend({ /** * The reason why sampling stopped. */ - stopReason: z.optional(z.enum(['endTurn', 'stopSequence', 'maxTokens']).or(z.string())), + stopReason: z.enum(['endTurn', 'stopSequence', 'maxTokens']).or(z.string()).optional(), role: z.enum(['user', 'assistant']), content: z.discriminatedUnion('type', [TextContentSchema, ImageContentSchema, AudioContentSchema]) }); @@ -1375,7 +1380,7 @@ export const ElicitResultSchema = ResultSchema.extend({ * The submitted form data, only present when action is "accept". * Contains values matching the requested schema. */ - content: z.record(z.union([z.string(), z.number(), z.boolean(), z.array(z.string())])).optional() + content: z.record(z.union([z.string(), z.number(), z.boolean(), z.array(z.string()), z.undefined()])).optional() }); /* Autocomplete */ @@ -1457,22 +1462,20 @@ export function assertCompleteRequestResourceTemplate(request: CompleteRequest): * 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.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.number().int().optional(), + /** + * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + hasMore: z.boolean().optional() + }) }); /* Roots */ @@ -1721,7 +1724,6 @@ export type ListToolsRequest = Infer; export type ListToolsResult = Infer; export type CallToolRequestParams = Infer; export type CallToolResult = Infer; -export type CompatibilityCallToolResult = Infer; export type CallToolRequest = Infer; export type ToolListChangedNotification = Infer;