Skip to content

Commit d9abbf4

Browse files
feat: Context API for request handlers
Replaces the flat RequestHandlerExtra type with a structured context system using 4 plain types: BaseContext, ServerContext, ClientContext, TaskContext. - Nested structure groups related fields (mcpReq, http, task, notification) - Separate ServerContext/ClientContext types for server and client handlers - Server-specific convenience methods (logging, sampling, elicitation) - Plain object (POJO) construction with closures - no classes - Type-safe contexts without runtime instanceof checks Based on the design from PR #1241, simplified from class hierarchy to plain types. BREAKING CHANGE: RequestHandlerExtra is replaced by BaseContext with nested structure. See docs/migration.md for migration guide.
1 parent 92f5ce5 commit d9abbf4

38 files changed

Lines changed: 1992 additions & 1457 deletions

.changeset/hot-trees-sing.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@modelcontextprotocol/express': patch
3+
'@modelcontextprotocol/hono': patch
4+
'@modelcontextprotocol/node': patch
5+
'@modelcontextprotocol/eslint-config': patch
6+
'@modelcontextprotocol/test-integration': patch
7+
'@modelcontextprotocol/client': patch
8+
'@modelcontextprotocol/server': patch
9+
'@modelcontextprotocol/core': patch
10+
---
11+
12+
add context API to tool, prompt, resource callbacks, linting

CLAUDE.md

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ When making breaking changes, document them in **both**:
2929
- `docs/migration.md` — human-readable guide with before/after code examples
3030
- `docs/migration-SKILL.md` — LLM-optimized mapping tables for mechanical migration
3131

32-
Include what changed, why, and how to migrate. Search for related sections and group
33-
related changes together rather than adding new standalone sections.
32+
Include what changed, why, and how to migrate. Search for related sections and group related changes together rather than adding new standalone sections.
3433

3534
## Code Style Guidelines
3635

@@ -146,37 +145,63 @@ When a request arrives from the remote side:
146145
2. **`Protocol.connect()`** routes to `_onrequest()`, `_onresponse()`, or `_onnotification()`
147146
3. **`Protocol._onrequest()`**:
148147
- Looks up handler in `_requestHandlers` map (keyed by method name)
149-
- Creates `RequestHandlerExtra` with `signal`, `sessionId`, `sendNotification`, `sendRequest`
148+
- Creates a context object (`ServerContext` or `ClientContext`) via `createRequestContext()`
150149
- Invokes handler, sends JSON-RPC response back via transport
151150
4. **Handler** was registered via `setRequestHandler('method', handler)`
152151

153152
### Handler Registration
154153

155154
```typescript
156155
// In Client (for server→client requests like sampling, elicitation)
157-
client.setRequestHandler('sampling/createMessage', async (request, extra) => {
156+
client.setRequestHandler('sampling/createMessage', async (request, ctx) => {
158157
// Handle sampling request from server
159158
return { role: "assistant", content: {...}, model: "..." };
160159
});
161160

162161
// In Server (for client→server requests like tools/call)
163-
server.setRequestHandler('tools/call', async (request, extra) => {
162+
server.setRequestHandler('tools/call', async (request, ctx) => {
164163
// Handle tool call from client
165164
return { content: [...] };
166165
});
167166
```
168167

169-
### Request Handler Extra
170-
171-
The `extra` parameter in handlers (`RequestHandlerExtra`) provides:
172-
173-
- `signal`: AbortSignal for cancellation
174-
- `sessionId`: Transport session identifier
175-
- `authInfo`: Validated auth token info (if authenticated)
176-
- `requestId`: JSON-RPC message ID
177-
- `sendNotification(notification)`: Send related notification back
178-
- `sendRequest(request, schema)`: Send related request (for bidirectional flows)
179-
- `taskStore`: Task storage interface (if tasks enabled)
168+
### Request Handler Context
169+
170+
The `ctx` parameter in handlers provides a structured context with grouped fields:
171+
172+
**Common structure (both Client and Server)**:
173+
174+
- `ctx.sessionId`: Transport session identifier (top-level)
175+
- `ctx.mcpReq`: MCP protocol context
176+
- `id`: JSON-RPC message ID
177+
- `method`: The method being called
178+
- `_meta`: Request metadata
179+
- `signal`: AbortSignal for cancellation
180+
- `send(request, schema, options?)`: Send request (for bidirectional flows)
181+
- `ctx.http`: HTTP request context (optional, present for HTTP transports)
182+
- `authInfo`: Validated auth token info (if authenticated)
183+
- `ctx.task`: Task context (when tasks are enabled)
184+
- `id`: Current task ID (updates after `store.createTask()`)
185+
- `store`: Request-scoped task store (`RequestTaskStore`)
186+
- `requestedTtl`: Requested TTL for the task
187+
- `ctx.notification`: Notification context
188+
- `send(notification)`: Send notification back
189+
190+
**Server-specific additions**:
191+
192+
- `ctx.http`: Extended with additional fields
193+
- `req`: Raw fetch Request object (access to URL, headers, etc.)
194+
- `closeSSE?()`: Close SSE stream for polling
195+
- `closeStandaloneSSE?()`: Close standalone SSE stream
196+
- `ctx.mcpReq`: Extended with server-to-client request methods
197+
- `requestSampling(params, options?)`: Request sampling from client
198+
- `elicitInput(params, options?)`: Request user input from client
199+
- `ctx.notification`: Extended with logging methods
200+
- `log(params)`: Send logging notification
201+
- `debug(message, extraLogData?)`: Send debug log
202+
- `info(message, extraLogData?)`: Send info log
203+
- `warning(message, extraLogData?)`: Send warning log
204+
- `error(message, extraLogData?)`: Send error log
180205

181206
### Capability Checking
182207

@@ -207,7 +232,7 @@ const result = await server.createMessage({
207232
});
208233

209234
// Client must have registered handler:
210-
client.setRequestHandler('sampling/createMessage', async (request, extra) => {
235+
client.setRequestHandler('sampling/createMessage', async (request, ctx) => {
211236
// Client-side LLM call
212237
return { role: "assistant", content: {...} };
213238
});
@@ -218,8 +243,8 @@ client.setRequestHandler('sampling/createMessage', async (request, extra) => {
218243
### Request Handler Registration (Low-Level Server)
219244

220245
```typescript
221-
server.setRequestHandler('tools/call', async (request, extra) => {
222-
// extra contains sessionId, authInfo, sendNotification, etc.
246+
server.setRequestHandler('tools/call', async (request, ctx) => {
247+
// ctx provides mcpReq, http, task, notification
223248
return {
224249
/* result */
225250
};
@@ -229,7 +254,7 @@ server.setRequestHandler('tools/call', async (request, extra) => {
229254
### Tool Registration (High-Level McpServer)
230255

231256
```typescript
232-
mcpServer.tool('tool-name', { param: z.string() }, async ({ param }, extra) => {
257+
mcpServer.tool('tool-name', { param: z.string() }, async ({ param }, ctx) => {
233258
return { content: [{ type: 'text', text: 'result' }] };
234259
});
235260
```

0 commit comments

Comments
 (0)