Skip to content

Commit 2b9b1b7

Browse files
Merge pull request #33 from modelcontextprotocol/justin/custom-requests
Fix typing for custom requests, notification, and result types
2 parents 844d69d + 8be664b commit 2b9b1b7

File tree

4 files changed

+212
-8
lines changed

4 files changed

+212
-8
lines changed

src/client/index.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* eslint-disable @typescript-eslint/no-unused-vars */
2+
/* eslint-disable no-constant-binary-expression */
3+
/* eslint-disable @typescript-eslint/no-unused-expressions */
4+
import { Client } from "./index.js";
5+
import { z } from "zod";
6+
import { RequestSchema, NotificationSchema, ResultSchema } from "../types.js";
7+
8+
/*
9+
Test that custom request/notification/result schemas can be used with the Client class.
10+
*/
11+
test("should typecheck", () => {
12+
const GetWeatherRequestSchema = RequestSchema.extend({
13+
method: z.literal("weather/get"),
14+
params: z.object({
15+
city: z.string(),
16+
}),
17+
});
18+
19+
const GetForecastRequestSchema = RequestSchema.extend({
20+
method: z.literal("weather/forecast"),
21+
params: z.object({
22+
city: z.string(),
23+
days: z.number(),
24+
}),
25+
});
26+
27+
const WeatherForecastNotificationSchema = NotificationSchema.extend({
28+
method: z.literal("weather/alert"),
29+
params: z.object({
30+
severity: z.enum(["warning", "watch"]),
31+
message: z.string(),
32+
}),
33+
});
34+
35+
const WeatherRequestSchema = GetWeatherRequestSchema.or(
36+
GetForecastRequestSchema,
37+
);
38+
const WeatherNotificationSchema = WeatherForecastNotificationSchema;
39+
const WeatherResultSchema = ResultSchema.extend({
40+
temperature: z.number(),
41+
conditions: z.string(),
42+
});
43+
44+
type WeatherRequest = z.infer<typeof WeatherRequestSchema>;
45+
type WeatherNotification = z.infer<typeof WeatherNotificationSchema>;
46+
type WeatherResult = z.infer<typeof WeatherResultSchema>;
47+
48+
// Create a typed Client for weather data
49+
const weatherClient = new Client<
50+
WeatherRequest,
51+
WeatherNotification,
52+
WeatherResult
53+
>({
54+
name: "WeatherClient",
55+
version: "1.0.0",
56+
});
57+
58+
// Typecheck that only valid weather requests/notifications/results are allowed
59+
false &&
60+
weatherClient.request(
61+
{
62+
method: "weather/get",
63+
params: {
64+
city: "Seattle",
65+
},
66+
},
67+
WeatherResultSchema,
68+
);
69+
70+
false &&
71+
weatherClient.notification({
72+
method: "weather/alert",
73+
params: {
74+
severity: "warning",
75+
message: "Storm approaching",
76+
},
77+
});
78+
});

src/client/index.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,46 @@ import {
66
ClientResult,
77
Implementation,
88
InitializeResultSchema,
9+
Notification,
910
PROTOCOL_VERSION,
11+
Request,
12+
Result,
1013
ServerCapabilities,
1114
} from "../types.js";
1215

1316
/**
1417
* An MCP client on top of a pluggable transport.
1518
*
1619
* The client will automatically begin the initialization flow with the server when connect() is called.
20+
*
21+
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
22+
*
23+
* ```typescript
24+
* // Custom schemas
25+
* const CustomRequestSchema = RequestSchema.extend({...})
26+
* const CustomNotificationSchema = NotificationSchema.extend({...})
27+
* const CustomResultSchema = ResultSchema.extend({...})
28+
*
29+
* // Type aliases
30+
* type CustomRequest = z.infer<typeof CustomRequestSchema>
31+
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
32+
* type CustomResult = z.infer<typeof CustomResultSchema>
33+
*
34+
* // Create typed client
35+
* const client = new Client<CustomRequest, CustomNotification, CustomResult>({
36+
* name: "CustomClient",
37+
* version: "1.0.0"
38+
* })
39+
* ```
1740
*/
18-
export class Client extends Protocol<
19-
ClientRequest,
20-
ClientNotification,
21-
ClientResult
41+
export class Client<
42+
RequestT extends Request = Request,
43+
NotificationT extends Notification = Notification,
44+
ResultT extends Result = Result,
45+
> extends Protocol<
46+
ClientRequest | RequestT,
47+
ClientNotification | NotificationT,
48+
ClientResult | ResultT
2249
> {
2350
private _serverCapabilities?: ServerCapabilities;
2451
private _serverVersion?: Implementation;

src/server/index.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* eslint-disable @typescript-eslint/no-unused-vars */
2+
/* eslint-disable no-constant-binary-expression */
3+
/* eslint-disable @typescript-eslint/no-unused-expressions */
4+
import { Server } from "./index.js";
5+
import { z } from "zod";
6+
import { RequestSchema, NotificationSchema, ResultSchema } from "../types.js";
7+
8+
/*
9+
Test that custom request/notification/result schemas can be used with the Server class.
10+
*/
11+
test("should typecheck", () => {
12+
const GetWeatherRequestSchema = RequestSchema.extend({
13+
method: z.literal("weather/get"),
14+
params: z.object({
15+
city: z.string(),
16+
}),
17+
});
18+
19+
const GetForecastRequestSchema = RequestSchema.extend({
20+
method: z.literal("weather/forecast"),
21+
params: z.object({
22+
city: z.string(),
23+
days: z.number(),
24+
}),
25+
});
26+
27+
const WeatherForecastNotificationSchema = NotificationSchema.extend({
28+
method: z.literal("weather/alert"),
29+
params: z.object({
30+
severity: z.enum(["warning", "watch"]),
31+
message: z.string(),
32+
}),
33+
});
34+
35+
const WeatherRequestSchema = GetWeatherRequestSchema.or(
36+
GetForecastRequestSchema,
37+
);
38+
const WeatherNotificationSchema = WeatherForecastNotificationSchema;
39+
const WeatherResultSchema = ResultSchema.extend({
40+
temperature: z.number(),
41+
conditions: z.string(),
42+
});
43+
44+
type WeatherRequest = z.infer<typeof WeatherRequestSchema>;
45+
type WeatherNotification = z.infer<typeof WeatherNotificationSchema>;
46+
type WeatherResult = z.infer<typeof WeatherResultSchema>;
47+
48+
// Create a typed Server for weather data
49+
const weatherServer = new Server<
50+
WeatherRequest,
51+
WeatherNotification,
52+
WeatherResult
53+
>({
54+
name: "WeatherServer",
55+
version: "1.0.0",
56+
});
57+
58+
// Typecheck that only valid weather requests/notifications/results are allowed
59+
weatherServer.setRequestHandler(GetWeatherRequestSchema, (request) => {
60+
return {
61+
temperature: 72,
62+
conditions: "sunny",
63+
};
64+
});
65+
66+
weatherServer.setNotificationHandler(
67+
WeatherForecastNotificationSchema,
68+
(notification) => {
69+
console.log(`Weather alert: ${notification.params.message}`);
70+
},
71+
);
72+
});

src/server/index.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import {
66
InitializeRequest,
77
InitializeRequestSchema,
88
InitializeResult,
9+
Notification,
910
PROTOCOL_VERSION,
11+
Request,
12+
Result,
1013
ServerNotification,
1114
ServerRequest,
1215
ServerResult,
@@ -21,11 +24,35 @@ import {
2124
* An MCP server on top of a pluggable transport.
2225
*
2326
* This server will automatically respond to the initialization flow as initiated from the client.
27+
*
28+
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
29+
*
30+
* ```typescript
31+
* // Custom schemas
32+
* const CustomRequestSchema = RequestSchema.extend({...})
33+
* const CustomNotificationSchema = NotificationSchema.extend({...})
34+
* const CustomResultSchema = ResultSchema.extend({...})
35+
*
36+
* // Type aliases
37+
* type CustomRequest = z.infer<typeof CustomRequestSchema>
38+
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
39+
* type CustomResult = z.infer<typeof CustomResultSchema>
40+
*
41+
* // Create typed server
42+
* const server = new Server<CustomRequest, CustomNotification, CustomResult>({
43+
* name: "CustomServer",
44+
* version: "1.0.0"
45+
* })
46+
* ```
2447
*/
25-
export class Server extends Protocol<
26-
ServerRequest,
27-
ServerNotification,
28-
ServerResult
48+
export class Server<
49+
RequestT extends Request = Request,
50+
NotificationT extends Notification = Notification,
51+
ResultT extends Result = Result,
52+
> extends Protocol<
53+
ServerRequest | RequestT,
54+
ServerNotification | NotificationT,
55+
ServerResult | ResultT
2956
> {
3057
private _clientCapabilities?: ClientCapabilities;
3158
private _clientVersion?: Implementation;

0 commit comments

Comments
 (0)