Skip to content

Commit 68ff665

Browse files
committed
context API - backwards compatible introduction
1 parent 107767a commit 68ff665

File tree

8 files changed

+411
-208
lines changed

8 files changed

+411
-208
lines changed

src/server/context.ts

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ElicitRequest,
55
ElicitResult,
66
ElicitResultSchema,
7+
JSONRPCRequest,
78
LoggingMessageNotification,
89
Notification,
910
Request,
@@ -12,16 +13,15 @@ import {
1213
RequestMeta,
1314
Result,
1415
ServerNotification,
15-
ServerRequest,
16-
ServerResult
16+
ServerRequest
1717
} from '../types.js';
1818
import { RequestHandlerExtra, RequestOptions, RequestTaskStore } from '../shared/protocol.js';
1919
import { Server } from './index.js';
20-
import { RequestContext } from './requestContext.js';
2120
import { AuthInfo } from './auth/types.js';
2221
import { AnySchema, SchemaOutput } from './zod-compat.js';
2322

24-
export interface ContextInterface<RequestT extends Request = Request, NotificationT extends Notification = Notification> extends RequestHandlerExtra<ServerRequest | RequestT, NotificationT | ServerNotification> {
23+
export interface ContextInterface<RequestT extends Request = Request, NotificationT extends Notification = Notification>
24+
extends RequestHandlerExtra<ServerRequest | RequestT, NotificationT | ServerNotification> {
2525
elicit(params: ElicitRequest['params'], options?: RequestOptions): Promise<ElicitResult>;
2626
requestSampling: (params: CreateMessageRequest['params'], options?: RequestOptions) => Promise<CreateMessageResult>;
2727
log(params: LoggingMessageNotification['params'], sessionId?: string): Promise<void>;
@@ -35,32 +35,61 @@ export interface ContextInterface<RequestT extends Request = Request, Notificati
3535
*
3636
* Implements the RequestHandlerExtra interface for backwards compatibility.
3737
*/
38-
export class Context<
39-
RequestT extends Request = Request,
40-
NotificationT extends Notification = Notification,
41-
ResultT extends Result = Result
42-
> implements ContextInterface<RequestT, NotificationT>
38+
export class Context<RequestT extends Request = Request, NotificationT extends Notification = Notification, ResultT extends Result = Result>
39+
implements ContextInterface<RequestT, NotificationT>
4340
{
4441
private readonly server: Server<RequestT, NotificationT, ResultT>;
4542

4643
/**
4744
* The request context.
4845
* A type-safe context that is passed to request handlers.
4946
*/
50-
public readonly requestCtx: RequestContext<
51-
RequestT | ServerRequest,
52-
NotificationT | ServerNotification,
53-
ResultT | ServerResult
54-
>;
47+
private readonly requestCtx: RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT>;
48+
49+
/**
50+
* The MCP context - Contains information about the current MCP request and session.
51+
*/
52+
public readonly mcpContext: {
53+
/**
54+
* The JSON-RPC ID of the request being handled.
55+
* This can be useful for tracking or logging purposes.
56+
*/
57+
requestId: RequestId;
58+
/**
59+
* The method of the request.
60+
*/
61+
method: string;
62+
/**
63+
* The metadata of the request.
64+
*/
65+
_meta?: RequestMeta;
66+
/**
67+
* The session ID of the request.
68+
*/
69+
sessionId?: string;
70+
};
5571

5672
constructor(args: {
5773
server: Server<RequestT, NotificationT, ResultT>;
58-
requestCtx: RequestContext<RequestT | ServerRequest, NotificationT | ServerNotification, ResultT | ServerResult>;
74+
request: JSONRPCRequest;
75+
requestCtx: RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT>;
5976
}) {
6077
this.server = args.server;
6178
this.requestCtx = args.requestCtx;
79+
this.mcpContext = {
80+
requestId: args.requestCtx.requestId,
81+
method: args.request.method,
82+
_meta: args.requestCtx._meta,
83+
sessionId: args.requestCtx.sessionId
84+
};
6285
}
6386

87+
/**
88+
* The JSON-RPC ID of the request being handled.
89+
* This can be useful for tracking or logging purposes.
90+
*
91+
* @deprecated Use {@link mcpContext.requestId} instead.
92+
*/
6493
public get requestId(): RequestId {
6594
return this.requestCtx.requestId;
6695
}
@@ -77,12 +106,18 @@ export class Context<
77106
return this.requestCtx.requestInfo;
78107
}
79108

109+
/**
110+
* @deprecated Use {@link mcpContext._meta} instead.
111+
*/
80112
public get _meta(): RequestMeta | undefined {
81113
return this.requestCtx._meta;
82114
}
83115

116+
/**
117+
* @deprecated Use {@link mcpContext.sessionId} instead.
118+
*/
84119
public get sessionId(): string | undefined {
85-
return this.requestCtx.sessionId;
120+
return this.mcpContext.sessionId;
86121
}
87122

88123
public get taskId(): string | undefined {
@@ -97,12 +132,12 @@ export class Context<
97132
return this.requestCtx.taskRequestedTtl ?? undefined;
98133
}
99134

100-
public closeSSEStream = (): void => {
101-
return this.requestCtx?.closeSSEStream();
135+
public get closeSSEStream(): (() => void) | undefined {
136+
return this.requestCtx.closeSSEStream;
102137
}
103138

104-
public closeStandaloneSSEStream = (): void => {
105-
return this.requestCtx?.closeStandaloneSSEStream();
139+
public get closeStandaloneSSEStream(): (() => void) | undefined {
140+
return this.requestCtx.closeStandaloneSSEStream;
106141
}
107142

108143
/**
@@ -111,7 +146,7 @@ export class Context<
111146
* This is used by certain transports to correctly associate related messages.
112147
*/
113148
public sendNotification = (notification: NotificationT | ServerNotification): Promise<void> => {
114-
return this.requestCtx.sendNotification(notification);
149+
return this.server.notification(notification);
115150
};
116151

117152
/**
@@ -124,7 +159,7 @@ export class Context<
124159
resultSchema: U,
125160
options?: RequestOptions
126161
): Promise<SchemaOutput<U>> => {
127-
return this.requestCtx.sendRequest(request, resultSchema, { ...options, relatedRequestId: this.requestId });
162+
return this.server.request(request, resultSchema, { ...options, relatedRequestId: this.requestId });
128163
};
129164

130165
/**
@@ -219,4 +254,4 @@ export class Context<
219254
sessionId
220255
);
221256
}
222-
}
257+
}

src/server/index.ts

Lines changed: 27 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ import { assertToolsCallTaskCapability, assertClientRequestTaskCapability } from
6262
import { Context } from './context.js';
6363
import { TaskStore } from '../experimental/index.js';
6464
import { Transport } from '../shared/transport.js';
65-
import { RequestContext } from './requestContext.js';
6665

6766
export type ServerOptions = ProtocolOptions & {
6867
/**
@@ -232,16 +231,24 @@ export class Server<
232231
// Wrap the handler to ensure the extra is a Context and return a decorated handler that can be passed to the base implementation
233232

234233
// Factory function to create a handler decorator that ensures the extra is a Context and returns a decorated handler that can be passed to the base implementation
235-
const handlerDecoratorFactory = (innerHandler: (request: SchemaOutput<T>, extra: Context<RequestT, NotificationT, ResultT>) => ServerResult | ResultT | Promise<ServerResult | ResultT>) => {
236-
const decoratedHandler = (request: SchemaOutput<T>, extra: RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT>) => {
234+
const handlerDecoratorFactory = (
235+
innerHandler: (
236+
request: SchemaOutput<T>,
237+
extra: Context<RequestT, NotificationT, ResultT>
238+
) => ServerResult | ResultT | Promise<ServerResult | ResultT>
239+
) => {
240+
const decoratedHandler = (
241+
request: SchemaOutput<T>,
242+
extra: RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT>
243+
) => {
237244
if (!this.isContextExtra(extra)) {
238245
throw new Error('Internal error: Expected Context for request handler extra');
239246
}
240247
return innerHandler(request, extra);
241-
}
248+
};
242249

243250
return decoratedHandler;
244-
}
251+
};
245252

246253
const shape = getObjectShape(requestSchema);
247254
const methodSchema = shape?.method;
@@ -496,45 +503,23 @@ export class Server<
496503
return this._capabilities;
497504
}
498505

499-
protected createRequestExtra(
500-
args: {
501-
request: JSONRPCRequest,
502-
taskStore: TaskStore | undefined,
503-
relatedTaskId: string | undefined,
504-
taskCreationParams: TaskCreationParams | undefined,
505-
abortController: AbortController,
506-
capturedTransport: Transport | undefined,
507-
extra?: MessageExtraInfo
508-
}
509-
): RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT> {
510-
const base = super.createRequestExtra(args) as RequestHandlerExtra<
511-
ServerRequest | RequestT,
512-
ServerNotification | NotificationT
513-
>;
514-
515-
// Wrap base in Context to add server utilities while preserving shape
516-
const requestCtx = new RequestContext<
517-
ServerRequest | RequestT,
518-
ServerNotification | NotificationT,
519-
ServerResult | ResultT
520-
>({
521-
signal: base.signal,
522-
authInfo: base.authInfo,
523-
requestInfo: base.requestInfo,
524-
requestId: base.requestId,
525-
_meta: base._meta,
526-
sessionId: base.sessionId,
527-
protocol: this,
528-
closeSSEStream: base.closeSSEStream ?? undefined,
529-
closeStandaloneSSEStream: base.closeStandaloneSSEStream ?? undefined
530-
});
531-
532-
const ctx = new Context<RequestT, NotificationT, ResultT>({
506+
protected createRequestExtra(args: {
507+
request: JSONRPCRequest;
508+
taskStore: TaskStore | undefined;
509+
relatedTaskId: string | undefined;
510+
taskCreationParams: TaskCreationParams | undefined;
511+
abortController: AbortController;
512+
capturedTransport: Transport | undefined;
513+
extra?: MessageExtraInfo;
514+
}): RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT> {
515+
const base = super.createRequestExtra(args) as RequestHandlerExtra<ServerRequest | RequestT, ServerNotification | NotificationT>;
516+
517+
// Expose a Context instance to handlers, which implements RequestHandlerExtra
518+
return new Context<RequestT, NotificationT, ResultT>({
533519
server: this,
534-
requestCtx
520+
request: args.request,
521+
requestCtx: base
535522
});
536-
537-
return ctx;
538523
}
539524

540525
async ping() {

src/server/mcp.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { validateAndWarnToolName } from '../shared/toolNameValidation.js';
6363
import { ExperimentalMcpServerTasks } from '../experimental/tasks/mcp-server.js';
6464
import type { ToolTaskHandler } from '../experimental/tasks/interfaces.js';
6565
import { ZodOptional } from 'zod';
66+
import { ContextInterface } from './context.js';
6667

6768
/**
6869
* High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
@@ -324,7 +325,7 @@ export class McpServer {
324325
private async executeToolHandler(
325326
tool: RegisteredTool,
326327
args: unknown,
327-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
328+
extra: ContextInterface<ServerRequest, ServerNotification>
328329
): Promise<CallToolResult | CreateTaskResult> {
329330
const handler = tool.handler as AnyToolHandler<ZodRawShapeCompat | undefined>;
330331
const isTaskHandler = 'createTask' in handler;
@@ -1270,7 +1271,7 @@ export class ResourceTemplate {
12701271

12711272
export type BaseToolCallback<
12721273
SendResultT extends Result,
1273-
Extra extends RequestHandlerExtra<ServerRequest, ServerNotification>,
1274+
Extra extends ContextInterface<ServerRequest, ServerNotification>,
12741275
Args extends undefined | ZodRawShapeCompat | AnySchema
12751276
> = Args extends ZodRawShapeCompat
12761277
? (args: ShapeOutput<Args>, extra: Extra) => SendResultT | Promise<SendResultT>
@@ -1290,7 +1291,7 @@ export type BaseToolCallback<
12901291
*/
12911292
export type ToolCallback<Args extends undefined | ZodRawShapeCompat | AnySchema = undefined> = BaseToolCallback<
12921293
CallToolResult,
1293-
RequestHandlerExtra<ServerRequest, ServerNotification>,
1294+
ContextInterface<ServerRequest, ServerNotification>,
12941295
Args
12951296
>;
12961297

@@ -1409,15 +1410,15 @@ export type ResourceMetadata = Omit<Resource, 'uri' | 'name'>;
14091410
* Callback to list all resources matching a given template.
14101411
*/
14111412
export type ListResourcesCallback = (
1412-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1413+
extra: ContextInterface<ServerRequest, ServerNotification>
14131414
) => ListResourcesResult | Promise<ListResourcesResult>;
14141415

14151416
/**
14161417
* Callback to read a resource at a given URI.
14171418
*/
14181419
export type ReadResourceCallback = (
14191420
uri: URL,
1420-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1421+
extra: ContextInterface<ServerRequest, ServerNotification>
14211422
) => ReadResourceResult | Promise<ReadResourceResult>;
14221423

14231424
export type RegisteredResource = {
@@ -1445,7 +1446,7 @@ export type RegisteredResource = {
14451446
export type ReadResourceTemplateCallback = (
14461447
uri: URL,
14471448
variables: Variables,
1448-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1449+
extra: ContextInterface<ServerRequest, ServerNotification>
14491450
) => ReadResourceResult | Promise<ReadResourceResult>;
14501451

14511452
export type RegisteredResourceTemplate = {
@@ -1470,8 +1471,8 @@ export type RegisteredResourceTemplate = {
14701471
type PromptArgsRawShape = ZodRawShapeCompat;
14711472

14721473
export type PromptCallback<Args extends undefined | PromptArgsRawShape = undefined> = Args extends PromptArgsRawShape
1473-
? (args: ShapeOutput<Args>, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>
1474-
: (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>;
1474+
? (args: ShapeOutput<Args>, extra: ContextInterface<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>
1475+
: (extra: ContextInterface<ServerRequest, ServerNotification>) => GetPromptResult | Promise<GetPromptResult>;
14751476

14761477
export type RegisteredPrompt = {
14771478
title?: string;

0 commit comments

Comments
 (0)