Skip to content

Commit e232939

Browse files
committed
save commit
1 parent 7d6210c commit e232939

File tree

8 files changed

+87
-49
lines changed

8 files changed

+87
-49
lines changed

src/client/index.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ describe('outputSchema validation', () => {
822822
server.setRequestHandler(CallToolRequestSchema, async request => {
823823
if (request.params.name === 'test-tool') {
824824
return {
825+
content: [],
825826
structuredContent: { result: 'success', count: 42 }
826827
};
827828
}
@@ -897,6 +898,7 @@ describe('outputSchema validation', () => {
897898
if (request.params.name === 'test-tool') {
898899
// Return invalid structured content (count is string instead of number)
899900
return {
901+
content: [],
900902
structuredContent: { result: 'success', count: 'not a number' }
901903
};
902904
}
@@ -1124,6 +1126,7 @@ describe('outputSchema validation', () => {
11241126
server.setRequestHandler(CallToolRequestSchema, async request => {
11251127
if (request.params.name === 'complex-tool') {
11261128
return {
1129+
content: [],
11271130
structuredContent: {
11281131
name: 'John Doe',
11291132
age: 30,
@@ -1209,6 +1212,7 @@ describe('outputSchema validation', () => {
12091212
if (request.params.name === 'strict-tool') {
12101213
// Return structured content with extra property
12111214
return {
1215+
content: [],
12121216
structuredContent: {
12131217
name: 'John',
12141218
extraField: 'not allowed'

src/client/index.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
type ClientNotification,
88
type ClientRequest,
99
type ClientResult,
10-
type CompatibilityCallToolResultSchema,
1110
type CompleteRequest,
1211
CompleteResultSchema,
1312
EmptyResultSchema,
@@ -27,18 +26,19 @@ import {
2726
ListToolsResultSchema,
2827
type LoggingLevel,
2928
McpError,
30-
type Notification,
3129
type ReadResourceRequest,
3230
ReadResourceResultSchema,
33-
type Request,
34-
type Result,
3531
type ServerCapabilities,
3632
SUPPORTED_PROTOCOL_VERSIONS,
3733
type SubscribeRequest,
3834
type Tool,
3935
type UnsubscribeRequest,
4036
ElicitResultSchema,
41-
ElicitRequestSchema
37+
ElicitRequestSchema,
38+
type RequestGeneric,
39+
type NotificationGeneric,
40+
type ResultGeneric,
41+
Result
4242
} from '../types.js';
4343
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
4444
import type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator } from '../validation/types.js';
@@ -149,9 +149,9 @@ export type ClientOptions = ProtocolOptions & {
149149
* ```
150150
*/
151151
export class Client<
152-
RequestT extends Request = Request,
153-
NotificationT extends Notification = Notification,
154-
ResultT extends Result = Result
152+
RequestT extends RequestGeneric = RequestGeneric,
153+
NotificationT extends NotificationGeneric = NotificationGeneric,
154+
ResultT extends ResultGeneric = Result
155155
> extends Protocol<ClientRequest | RequestT, ClientNotification | NotificationT, ClientResult | ResultT> {
156156
private _serverCapabilities?: ServerCapabilities;
157157
private _serverVersion?: Implementation;
@@ -461,7 +461,7 @@ export class Client<
461461

462462
async callTool(
463463
params: CallToolRequest['params'],
464-
resultSchema: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema = CallToolResultSchema,
464+
resultSchema: typeof CallToolResultSchema = CallToolResultSchema,
465465
options?: RequestOptions
466466
) {
467467
const result = await this.request({ method: 'tools/call', params }, resultSchema, options);

src/server/index.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ import {
2020
LoggingLevelSchema,
2121
type LoggingMessageNotification,
2222
McpError,
23-
type Notification,
24-
type Request,
2523
type ResourceUpdatedNotification,
26-
type Result,
2724
type ServerCapabilities,
2825
type ServerNotification,
2926
type ServerRequest,
3027
type ServerResult,
3128
SetLevelRequestSchema,
32-
SUPPORTED_PROTOCOL_VERSIONS
29+
SUPPORTED_PROTOCOL_VERSIONS,
30+
type RequestGeneric,
31+
type NotificationGeneric,
32+
type ResultGeneric,
33+
Result
3334
} from '../types.js';
3435
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
3536
import type { JsonSchemaType, jsonSchemaValidator } from '../validation/types.js';
@@ -104,9 +105,9 @@ export type ServerOptions = ProtocolOptions & {
104105
* @deprecated Use `McpServer` instead for the high-level API. Only use `Server` for advanced use cases.
105106
*/
106107
export class Server<
107-
RequestT extends Request = Request,
108-
NotificationT extends Notification = Notification,
109-
ResultT extends Result = Result
108+
RequestT extends RequestGeneric = RequestGeneric,
109+
NotificationT extends NotificationGeneric = NotificationGeneric,
110+
ResultT extends ResultGeneric = Result
110111
> extends Protocol<ServerRequest | RequestT, ServerNotification | NotificationT, ServerResult | ResultT> {
111112
private _clientCapabilities?: ClientCapabilities;
112113
private _clientVersion?: Implementation;

src/server/mcp.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4273,6 +4273,7 @@ describe('elicitInput()', () => {
42734273

42744274
expect(checkAvailability).toHaveBeenCalledWith('ABC Restaurant', '2024-12-25', 2);
42754275
expect(findAlternatives).not.toHaveBeenCalled();
4276+
42764277
expect(result.content).toEqual([
42774278
{
42784279
type: 'text',

src/shared/protocol.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ZodType, z } from 'zod';
2-
import { ClientCapabilities, ErrorCode, McpError, Notification, Request, Result, ServerCapabilities } from '../types.js';
2+
import { ClientCapabilities, ErrorCode, McpError, NotificationGeneric, RequestGeneric, Result, ServerCapabilities } from '../types.js';
33
import { Protocol, mergeCapabilities } from './protocol.js';
44
import { Transport } from './transport.js';
55
import { MockInstance } from 'vitest';
@@ -18,14 +18,14 @@ class MockTransport implements Transport {
1818
}
1919

2020
describe('protocol tests', () => {
21-
let protocol: Protocol<Request, Notification, Result>;
21+
let protocol: Protocol<RequestGeneric, NotificationGeneric, Result>;
2222
let transport: MockTransport;
2323
let sendSpy: MockInstance;
2424

2525
beforeEach(() => {
2626
transport = new MockTransport();
2727
sendSpy = vi.spyOn(transport, 'send');
28-
protocol = new (class extends Protocol<Request, Notification, Result> {
28+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
2929
protected assertCapabilityForMethod(): void {}
3030
protected assertNotificationCapability(): void {}
3131
protected assertRequestHandlerCapability(): void {}
@@ -479,7 +479,7 @@ describe('protocol tests', () => {
479479

480480
it('should NOT debounce a notification that has parameters', async () => {
481481
// ARRANGE
482-
protocol = new (class extends Protocol<Request, Notification, Result> {
482+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
483483
protected assertCapabilityForMethod(): void {}
484484
protected assertNotificationCapability(): void {}
485485
protected assertRequestHandlerCapability(): void {}
@@ -500,7 +500,7 @@ describe('protocol tests', () => {
500500

501501
it('should NOT debounce a notification that has a relatedRequestId', async () => {
502502
// ARRANGE
503-
protocol = new (class extends Protocol<Request, Notification, Result> {
503+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
504504
protected assertCapabilityForMethod(): void {}
505505
protected assertNotificationCapability(): void {}
506506
protected assertRequestHandlerCapability(): void {}
@@ -519,7 +519,7 @@ describe('protocol tests', () => {
519519

520520
it('should clear pending debounced notifications on connection close', async () => {
521521
// ARRANGE
522-
protocol = new (class extends Protocol<Request, Notification, Result> {
522+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
523523
protected assertCapabilityForMethod(): void {}
524524
protected assertNotificationCapability(): void {}
525525
protected assertRequestHandlerCapability(): void {}
@@ -543,7 +543,7 @@ describe('protocol tests', () => {
543543

544544
it('should debounce multiple synchronous calls when params property is omitted', async () => {
545545
// ARRANGE
546-
protocol = new (class extends Protocol<Request, Notification, Result> {
546+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
547547
protected assertCapabilityForMethod(): void {}
548548
protected assertNotificationCapability(): void {}
549549
protected assertRequestHandlerCapability(): void {}
@@ -570,7 +570,7 @@ describe('protocol tests', () => {
570570

571571
it('should debounce calls when params is explicitly undefined', async () => {
572572
// ARRANGE
573-
protocol = new (class extends Protocol<Request, Notification, Result> {
573+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
574574
protected assertCapabilityForMethod(): void {}
575575
protected assertNotificationCapability(): void {}
576576
protected assertRequestHandlerCapability(): void {}
@@ -595,7 +595,7 @@ describe('protocol tests', () => {
595595

596596
it('should send non-debounced notifications immediately and multiple times', async () => {
597597
// ARRANGE
598-
protocol = new (class extends Protocol<Request, Notification, Result> {
598+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
599599
protected assertCapabilityForMethod(): void {}
600600
protected assertNotificationCapability(): void {}
601601
protected assertRequestHandlerCapability(): void {}
@@ -628,7 +628,7 @@ describe('protocol tests', () => {
628628

629629
it('should handle sequential batches of debounced notifications correctly', async () => {
630630
// ARRANGE
631-
protocol = new (class extends Protocol<Request, Notification, Result> {
631+
protocol = new (class extends Protocol<RequestGeneric, NotificationGeneric, Result> {
632632
protected assertCapabilityForMethod(): void {}
633633
protected assertNotificationCapability(): void {}
634634
protected assertRequestHandlerCapability(): void {}

src/shared/protocol.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import {
1919
ProgressNotificationSchema,
2020
Request,
2121
RequestId,
22-
Result,
2322
ServerCapabilities,
2423
RequestMeta,
2524
MessageExtraInfo,
26-
RequestInfo
25+
RequestInfo,
26+
type RequestGeneric,
27+
type NotificationGeneric,
28+
type ResultGeneric
2729
} from '../types.js';
2830
import { Transport, TransportSendOptions } from './transport.js';
2931
import { AuthInfo } from '../server/auth/types.js';
@@ -171,7 +173,11 @@ type TimeoutInfo = {
171173
* Implements MCP protocol framing on top of a pluggable transport, including
172174
* features like request/response linking, notifications, and progress.
173175
*/
174-
export abstract class Protocol<SendRequestT extends Request, SendNotificationT extends Notification, SendResultT extends Result> {
176+
export abstract class Protocol<
177+
SendRequestT extends RequestGeneric,
178+
SendNotificationT extends NotificationGeneric,
179+
SendResultT extends ResultGeneric
180+
> {
175181
private _transport?: Transport;
176182
private _requestMessageId = 0;
177183
private _requestHandlers: Map<

src/spec.types.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ type MakeUnknownsNotOptional<T> =
6262
}
6363
: T;
6464

65+
/**
66+
* Spec Patches
67+
*
68+
* Temporary spec type patches, to be used until the spec is updated to fix the issues.
69+
*/
70+
6571
// Targeted fix: in spec, treat ClientCapabilities.elicitation?: object as Record<string, unknown>
6672
type FixSpecClientCapabilities<T> = T extends { elicitation?: object }
6773
? Omit<T, 'elicitation'> & { elicitation?: Record<string, unknown> }
@@ -75,6 +81,14 @@ type FixSpecInitializeRequest<T> = T extends { params: infer P } ? Omit<T, 'para
7581

7682
type FixSpecClientRequest<T> = T extends { params: infer P } ? Omit<T, 'params'> & { params: FixSpecInitializeRequestParams<P> } : T;
7783

84+
// ElicitResult - spec does not allow undefined values in the content object
85+
type SpecElicitResultPatched = Omit<SpecTypes.ElicitResult, 'content'> & {
86+
action: SpecTypes.ElicitResult['action'];
87+
content?: {
88+
[key: string]: string | number | boolean | string[] | undefined;
89+
};
90+
};
91+
7892
const sdkTypeChecks = {
7993
RequestParams: (sdk: SDKTypes.RequestParams, spec: SpecTypes.RequestParams) => {
8094
sdk = spec;
@@ -205,7 +219,7 @@ const sdkTypeChecks = {
205219
sdk = spec;
206220
spec = sdk;
207221
},
208-
ElicitResult: (sdk: SDKTypes.ElicitResult, spec: SpecTypes.ElicitResult) => {
222+
ElicitResult: (sdk: SDKTypes.ElicitResult, spec: SpecElicitResultPatched) => {
209223
sdk = spec;
210224
spec = sdk;
211225
},

src/types.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const BaseRequestParamsSchema = z.object({
4242
/**
4343
* See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage.
4444
*/
45-
_meta: RequestMetaSchema.optional()
45+
_meta: z.intersection(RequestMetaSchema.optional(), z.record(z.string(), z.unknown()).optional()).optional()
4646
});
4747

4848
export const RequestSchema = z.object({
@@ -56,9 +56,11 @@ export const RequestSchema = z.object({
5656
* Used in {@link JSONRPCRequestSchema} for generic shape matching.
5757
*/
5858
export const RequestSchemaGeneric = RequestSchema.extend({
59-
params: z.intersection(RequestSchema.shape.params, z.record(z.string(), z.unknown()).optional())
59+
params: z.intersection(RequestSchema.shape.params.optional(), z.record(z.string(), z.unknown()).optional())
6060
});
6161

62+
export type RequestGeneric = ExpandRecursively<z.infer<typeof RequestSchemaGeneric>>;
63+
6264
const NotificationsParamsSchema = z.object({
6365
/**
6466
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
@@ -81,20 +83,26 @@ export const NotificationSchemaGeneric = NotificationSchema.extend({
8183
params: z.intersection(NotificationSchema.shape.params, z.record(z.string(), z.unknown()).optional())
8284
});
8385

84-
export const ResultSchema = z.object({
85-
/**
86-
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
87-
* for notes on _meta usage.
88-
*/
89-
_meta: z.record(z.string(), z.unknown()).optional()
90-
});
86+
export type NotificationGeneric = z.infer<typeof NotificationSchemaGeneric>;
87+
88+
export const ResultSchema = z
89+
.object({
90+
/**
91+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
92+
* for notes on _meta usage.
93+
*/
94+
_meta: z.record(z.string(), z.unknown()).optional()
95+
})
96+
.catchall(z.unknown());
9197

9298
/**
9399
* Generic result schema that allows any additional fields to be added to the result.
94100
*
95101
* Used in {@link JSONRPCResponseSchema} for generic shape matching.
96102
*/
97103
export const ResultSchemaGeneric = z.intersection(ResultSchema, z.record(z.string(), z.unknown()).optional());
104+
105+
export type ResultGeneric = ExpandRecursively<z.infer<typeof ResultSchemaGeneric>>;
98106
/**
99107
* A uniquely identifying ID for a request in JSON-RPC.
100108
*/
@@ -1035,14 +1043,18 @@ export const CallToolResultSchema = ResultSchema.extend({
10351043
isError: z.boolean().optional()
10361044
});
10371045

1038-
/**
1039-
* CallToolResultSchema extended with backwards compatibility to protocol version 2024-10-07.
1040-
*/
1041-
export const CompatibilityCallToolResultSchema = CallToolResultSchema.or(
1042-
ResultSchema.extend({
1043-
toolResult: z.unknown()
1044-
})
1045-
);
1046+
// const LegacyCallToolResultSchema = ResultSchema.extend({
1047+
// toolResult: z.unknown()
1048+
// });
1049+
1050+
// export type LegacyCallToolResult = z.infer<typeof LegacyCallToolResultSchema>;
1051+
1052+
// export const isLegacyCallToolResult = (value: unknown): value is LegacyCallToolResult => LegacyCallToolResultSchema.safeParse(value).success;
1053+
1054+
// /**
1055+
// * CallToolResultSchema extended with backwards compatibility to protocol version 2024-10-07.
1056+
// */
1057+
// export const CompatibilityCallToolResultSchema = CallToolResultSchema.or(LegacyCallToolResultSchema);
10461058

10471059
/**
10481060
* Parameters for a `tools/call` request.
@@ -1389,7 +1401,7 @@ export const ElicitResultSchema = ResultSchema.extend({
13891401
* The submitted form data, only present when action is "accept".
13901402
* Contains values matching the requested schema.
13911403
*/
1392-
content: z.record(z.union([z.string(), z.number(), z.boolean(), z.array(z.string())])).optional()
1404+
content: z.record(z.union([z.string(), z.number(), z.boolean(), z.array(z.string()), z.undefined()])).optional()
13931405
});
13941406

13951407
/* Autocomplete */
@@ -1733,7 +1745,7 @@ export type ListToolsRequest = Infer<typeof ListToolsRequestSchema>;
17331745
export type ListToolsResult = Infer<typeof ListToolsResultSchema>;
17341746
export type CallToolRequestParams = Infer<typeof CallToolRequestParamsSchema>;
17351747
export type CallToolResult = Infer<typeof CallToolResultSchema>;
1736-
export type CompatibilityCallToolResult = Infer<typeof CompatibilityCallToolResultSchema>;
1748+
// export type CompatibilityCallToolResult = Infer<typeof CompatibilityCallToolResultSchema>;
17371749
export type CallToolRequest = Infer<typeof CallToolRequestSchema>;
17381750
export type ToolListChangedNotification = Infer<typeof ToolListChangedNotificationSchema>;
17391751

0 commit comments

Comments
 (0)