Skip to content

Commit a31b3a7

Browse files
refactor: move notification.send → mcpReq.notify, notification.log → mcpReq.log
All request-scoped operations now live under mcpReq: send(), notify(), log(), elicitInput(), requestSampling(). The notification group is removed.
1 parent f81f1e8 commit a31b3a7

9 files changed

Lines changed: 42 additions & 57 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,15 @@ The `ctx` parameter in handlers provides a structured context:
179179
- `_meta?`: Request metadata
180180
- `signal`: AbortSignal for cancellation
181181
- `send(request, schema, options?)`: Send related request (for bidirectional flows)
182+
- `notify(notification)`: Send related notification back
182183
- `http?`: HTTP transport info (undefined for stdio)
183184
- `authInfo?`: Validated auth token info
184185
- `task?`: Task context (`{ id?, store, requestedTtl? }`) when task storage is configured
185-
- `notification`: Outbound notifications
186-
- `send(notification)`: Send related notification back
187186

188-
**`ServerContext`** extends each `BaseContext` group via type intersection:
187+
**`ServerContext`** extends `BaseContext.mcpReq` and `BaseContext.http?` via type intersection:
189188

190-
- `mcpReq` adds: `elicitInput(params, options?)`, `requestSampling(params, options?)`
189+
- `mcpReq` adds: `log(level, data, logger?)`, `elicitInput(params, options?)`, `requestSampling(params, options?)`
191190
- `http?` adds: `req?` (HTTP request info), `closeSSE?`, `closeStandaloneSSE?`
192-
- `notification` adds: `log(level, data, logger?)`
193191

194192
**`ClientContext`** is currently identical to `BaseContext`.
195193

docs/migration-SKILL.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ Request/notification params remain fully typed. Remove unused schema imports aft
367367
| `extra.requestId` | `ctx.mcpReq.id` |
368368
| `extra._meta` | `ctx.mcpReq._meta` |
369369
| `extra.sendRequest(...)` | `ctx.mcpReq.send(...)` |
370-
| `extra.sendNotification(...)` | `ctx.notification.send(...)` |
370+
| `extra.sendNotification(...)` | `ctx.mcpReq.notify(...)` |
371371
| `extra.authInfo` | `ctx.http?.authInfo` |
372372
| `extra.sessionId` | `ctx.sessionId` |
373373
| `extra.requestInfo` | `ctx.http?.req` (only `ServerContext`) |
@@ -381,15 +381,15 @@ Request/notification params remain fully typed. Remove unused schema imports aft
381381

382382
| Method | Description | Replaces |
383383
|--------|-------------|----------|
384-
| `ctx.notification.log(level, data, logger?)` | Send log notification (respects client's level filter) | `server.sendLoggingMessage(...)` from within handler |
384+
| `ctx.mcpReq.log(level, data, logger?)` | Send log notification (respects client's level filter) | `server.sendLoggingMessage(...)` from within handler |
385385
| `ctx.mcpReq.elicitInput(params, options?)` | Elicit user input (form or URL) | `server.elicitInput(...)` from within handler |
386386
| `ctx.mcpReq.requestSampling(params, options?)` | Request LLM sampling from client | `server.createMessage(...)` from within handler |
387387

388388
## 11. Client Behavioral Changes
389389

390390
`Client.listPrompts()`, `listResources()`, `listResourceTemplates()`, `listTools()` now return empty results when the server lacks the corresponding capability (instead of sending the request). Set `enforceStrictCapabilities: true` in `ClientOptions` to throw an error instead.
391391

392-
## 11. Runtime-Specific JSON Schema Validators (Enhancement)
392+
## 12. Runtime-Specific JSON Schema Validators (Enhancement)
393393

394394
The SDK now auto-selects the appropriate JSON Schema validator based on runtime:
395395
- Node.js → `AjvJsonSchemaValidator` (no change from v1)
@@ -409,7 +409,7 @@ new McpServer({ name: 'server', version: '1.0.0' }, {});
409409

410410
Access validators via `_shims` export: `import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';`
411411

412-
## 12. Migration Steps (apply in this order)
412+
## 13. Migration Steps (apply in this order)
413413

414414
1. Update `package.json`: `npm uninstall @modelcontextprotocol/sdk`, install the appropriate v2 packages
415415
2. Replace all imports from `@modelcontextprotocol/sdk/...` using the import mapping tables (sections 3-4), including `StreamableHTTPServerTransport``NodeStreamableHTTPServerTransport`

docs/migration.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ The `RequestHandlerExtra` type has been replaced with a structured context type
356356
| `extra.requestId` | `ctx.mcpReq.id` |
357357
| `extra._meta` | `ctx.mcpReq._meta` |
358358
| `extra.sendRequest(...)` | `ctx.mcpReq.send(...)` |
359-
| `extra.sendNotification(...)` | `ctx.notification.send(...)` |
359+
| `extra.sendNotification(...)` | `ctx.mcpReq.notify(...)` |
360360
| `extra.authInfo` | `ctx.http?.authInfo` |
361361
| `extra.requestInfo` | `ctx.http?.req` (only on `ServerContext`) |
362362
| `extra.closeSSEStream` | `ctx.http?.closeSSE` (only on `ServerContext`) |
@@ -383,17 +383,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
383383
server.setRequestHandler('tools/call', async (request, ctx) => {
384384
const headers = ctx.http?.req?.headers;
385385
const taskStore = ctx.task?.store;
386-
await ctx.notification.send({ method: 'notifications/progress', params: { progressToken: 'abc', progress: 50, total: 100 } });
386+
await ctx.mcpReq.notify({ method: 'notifications/progress', params: { progressToken: 'abc', progress: 50, total: 100 } });
387387
return { content: [{ type: 'text', text: 'result' }] };
388388
});
389389
```
390390

391391
Context fields are organized into 4 groups:
392392

393-
- **`mcpReq`** — request-level concerns: `id`, `method`, `_meta`, `signal`, `send()`, plus server-only `elicitInput()` and `requestSampling()`
393+
- **`mcpReq`** — request-level concerns: `id`, `method`, `_meta`, `signal`, `send()`, `notify()`, plus server-only `log()`, `elicitInput()`, and `requestSampling()`
394394
- **`http?`** — HTTP transport concerns (undefined for stdio): `authInfo`, plus server-only `req`, `closeSSE`, `closeStandaloneSSE`
395395
- **`task?`** — task lifecycle: `id`, `store`, `requestedTtl`
396-
- **`notification`** — outbound notifications: `send()`, plus server-only `log()`
397396

398397
`BaseContext` is the common base type shared by both `ServerContext` and `ClientContext`. `ServerContext` extends each group with server-specific additions via type intersection.
399398

@@ -402,7 +401,7 @@ Context fields are organized into 4 groups:
402401
```typescript
403402
server.setRequestHandler('tools/call', async (request, ctx) => {
404403
// Send a log message (respects client's log level filter)
405-
await ctx.notification.log('info', 'Processing tool call', 'my-logger');
404+
await ctx.mcpReq.log('info', 'Processing tool call', 'my-logger');
406405

407406
// Request client to sample an LLM
408407
const samplingResult = await ctx.mcpReq.requestSampling({

packages/core/src/shared/protocol.ts

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,13 @@ export type BaseContext<SendRequestT extends Request, SendNotificationT extends
303303
* This is used by certain transports to correctly associate related messages.
304304
*/
305305
send: <U extends AnySchema>(request: SendRequestT, resultSchema: U, options?: TaskRequestOptions) => Promise<SchemaOutput<U>>;
306+
307+
/**
308+
* Sends a notification that relates to the current request being handled.
309+
*
310+
* This is used by certain transports to correctly associate related messages.
311+
*/
312+
notify: (notification: SendNotificationT) => Promise<void>;
306313
};
307314

308315
/**
@@ -319,18 +326,6 @@ export type BaseContext<SendRequestT extends Request, SendNotificationT extends
319326
* Task context, available when task storage is configured.
320327
*/
321328
task?: TaskContext;
322-
323-
/**
324-
* Outbound notification sending.
325-
*/
326-
notification: {
327-
/**
328-
* Sends a notification that relates to the current request being handled.
329-
*
330-
* This is used by certain transports to correctly associate related messages.
331-
*/
332-
send: (notification: SendNotificationT) => Promise<void>;
333-
};
334329
};
335330

336331
/**
@@ -341,6 +336,12 @@ export type ServerContext<
341336
SendNotificationT extends Notification = ServerNotification
342337
> = BaseContext<SendRequestT, SendNotificationT> & {
343338
mcpReq: {
339+
/**
340+
* Send a log message notification to the client.
341+
* Respects the client's log level filter set via logging/setLevel.
342+
*/
343+
log: (level: LoggingLevel, data: unknown, logger?: string) => Promise<void>;
344+
344345
/**
345346
* Send an elicitation request to the client, requesting user input.
346347
*/
@@ -373,14 +374,6 @@ export type ServerContext<
373374
*/
374375
closeStandaloneSSE?: () => void;
375376
};
376-
377-
notification: {
378-
/**
379-
* Send a log message notification to the client.
380-
* Respects the client's log level filter set via logging/setLevel.
381-
*/
382-
log: (level: LoggingLevel, data: unknown, logger?: string) => Promise<void>;
383-
};
384377
};
385378

386379
/**
@@ -841,19 +834,17 @@ export abstract class Protocol<
841834
await taskStore.updateTaskStatus(effectiveTaskId, 'input_required');
842835
}
843836
return await this.request(r, resultSchema, requestOptions);
844-
}
845-
},
846-
http: extra?.authInfo ? { authInfo: extra.authInfo } : undefined,
847-
task,
848-
notification: {
849-
send: async notification => {
837+
},
838+
notify: async notification => {
850839
const notificationOptions: NotificationOptions = { relatedRequestId: request.id };
851840
if (relatedTaskId) {
852841
notificationOptions.relatedTask = { taskId: relatedTaskId };
853842
}
854843
await this.notification(notification, notificationOptions);
855844
}
856-
}
845+
},
846+
http: extra?.authInfo ? { authInfo: extra.authInfo } : undefined,
847+
task
857848
};
858849
const ctx = this.buildContext(baseCtx, extra);
859850

packages/core/test/shared/protocol.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,8 +2081,8 @@ describe('Task-based execution', () => {
20812081

20822082
// Set up a handler that uses sendRequest and sendNotification
20832083
serverProtocol.setRequestHandler('tools/call', async (_request, ctx) => {
2084-
// Send a notification using the ctx.notification.send
2085-
await ctx.notification.send({
2084+
// Send a notification using the ctx.mcpReq.notify
2085+
await ctx.mcpReq.notify({
20862086
method: 'notifications/message',
20872087
params: { level: 'info', data: 'test' }
20882088
});

packages/server/src/server/server.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export class Server<
167167
...ctx,
168168
mcpReq: {
169169
...ctx.mcpReq,
170+
log: (level, data, logger) => this.sendLoggingMessage({ level, data, logger }),
170171
elicitInput: (params, options) => this.elicitInput(params, options),
171172
requestSampling: (params, options) => this.createMessage(params, options)
172173
},
@@ -175,10 +176,6 @@ export class Server<
175176
req: transportInfo?.requestInfo,
176177
closeSSE: transportInfo?.closeSSEStream,
177178
closeStandaloneSSE: transportInfo?.closeStandaloneSSEStream
178-
},
179-
notification: {
180-
...ctx.notification,
181-
log: (level, data, logger) => this.sendLoggingMessage({ level, data, logger })
182179
}
183180
};
184181
}

test/conformance/src/everythingServer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ function createMcpServer() {
203203
inputSchema: {}
204204
},
205205
async (_args, ctx): Promise<CallToolResult> => {
206-
await ctx.notification.send({
206+
await ctx.mcpReq.notify({
207207
method: 'notifications/message',
208208
params: {
209209
level: 'info',
@@ -212,7 +212,7 @@ function createMcpServer() {
212212
});
213213
await new Promise(resolve => setTimeout(resolve, 50));
214214

215-
await ctx.notification.send({
215+
await ctx.mcpReq.notify({
216216
method: 'notifications/message',
217217
params: {
218218
level: 'info',
@@ -221,7 +221,7 @@ function createMcpServer() {
221221
});
222222
await new Promise(resolve => setTimeout(resolve, 50));
223223

224-
await ctx.notification.send({
224+
await ctx.mcpReq.notify({
225225
method: 'notifications/message',
226226
params: {
227227
level: 'info',
@@ -244,7 +244,7 @@ function createMcpServer() {
244244
async (_args, ctx): Promise<CallToolResult> => {
245245
const progressToken = ctx.mcpReq._meta?.progressToken ?? 0;
246246
console.log('Progress token:', progressToken);
247-
await ctx.notification.send({
247+
await ctx.mcpReq.notify({
248248
method: 'notifications/progress',
249249
params: {
250250
progressToken,
@@ -255,7 +255,7 @@ function createMcpServer() {
255255
});
256256
await new Promise(resolve => setTimeout(resolve, 50));
257257

258-
await ctx.notification.send({
258+
await ctx.mcpReq.notify({
259259
method: 'notifications/progress',
260260
params: {
261261
progressToken,
@@ -266,7 +266,7 @@ function createMcpServer() {
266266
});
267267
await new Promise(resolve => setTimeout(resolve, 50));
268268

269-
await ctx.notification.send({
269+
await ctx.mcpReq.notify({
270270
method: 'notifications/progress',
271271
params: {
272272
progressToken,

test/integration/test/server/mcp.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
121121
if (progressToken) {
122122
// Send progress notification for each step
123123
for (let i = 1; i <= steps; i++) {
124-
await ctx.notification.send({
124+
await ctx.mcpReq.notify({
125125
method: 'notifications/progress',
126126
params: {
127127
progressToken,
@@ -1444,7 +1444,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
14441444
});
14451445

14461446
mcpServer.registerTool('test-tool', {}, async ctx => {
1447-
await ctx.notification.send({
1447+
await ctx.mcpReq.notify({
14481448
method: 'notifications/message',
14491449
params: { level: 'debug', data: loggingMessage }
14501450
});

test/integration/test/taskResumability.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
7070
},
7171
async ({ message }, ctx) => {
7272
// Send notification immediately
73-
await ctx.notification.send({
73+
await ctx.mcpReq.notify({
7474
method: 'notifications/message',
7575
params: {
7676
level: 'info',
@@ -97,7 +97,7 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
9797
async ({ count, interval }, ctx) => {
9898
// Send notifications at specified intervals
9999
for (let i = 0; i < count; i++) {
100-
await ctx.notification.send({
100+
await ctx.mcpReq.notify({
101101
method: 'notifications/message',
102102
params: {
103103
level: 'info',

0 commit comments

Comments
 (0)