@@ -44,6 +44,7 @@ import {
4444 ProtocolErrorCode ,
4545 SUPPORTED_PROTOCOL_VERSIONS
4646} from '../types/index.js' ;
47+ import { deprecate } from '../util/deprecate.js' ;
4748import type { AnySchema , SchemaOutput } from '../util/schema.js' ;
4849import { parseSchema } from '../util/schema.js' ;
4950import type { TaskContext , TaskManagerHost , TaskManagerOptions , TaskRequestOptions } from './taskManager.js' ;
@@ -227,6 +228,25 @@ export type BaseContext = {
227228 * Task context, available when task storage is configured.
228229 */
229230 task ?: TaskContext ;
231+
232+ /** @deprecated Use `ctx.mcpReq.signal`. Removed in v3. */
233+ signal : AbortSignal ;
234+ /** @deprecated Use `ctx.mcpReq.id`. Removed in v3. */
235+ requestId : RequestId ;
236+ /** @deprecated Use `ctx.mcpReq._meta`. Removed in v3. */
237+ _meta ?: RequestMeta ;
238+ /** @deprecated Use `ctx.http?.authInfo`. Removed in v3. */
239+ authInfo ?: AuthInfo ;
240+ /** @deprecated Use `ctx.mcpReq.notify`. Removed in v3. */
241+ sendNotification : ( notification : Notification ) => Promise < void > ;
242+ /** @deprecated Use `ctx.mcpReq.send`. Removed in v3. */
243+ sendRequest : < T extends AnySchema > ( request : Request , resultSchema : T , options ?: RequestOptions ) => Promise < SchemaOutput < T > > ;
244+ /** @deprecated Use `ctx.task?.store`. Removed in v3. */
245+ taskStore ?: TaskContext [ 'store' ] ;
246+ /** @deprecated Use `ctx.task?.id`. Removed in v3. */
247+ taskId ?: TaskContext [ 'id' ] ;
248+ /** @deprecated Use `ctx.task?.requestedTtl`. Removed in v3. */
249+ taskRequestedTtl ?: TaskContext [ 'requestedTtl' ] ;
230250} ;
231251
232252/**
@@ -279,6 +299,54 @@ export type ServerContext = BaseContext & {
279299 */
280300export type ClientContext = BaseContext ;
281301
302+ /**
303+ * Flat-field shape of the v1 handler context. v2 nests these under
304+ * {@linkcode BaseContext.mcpReq | ctx.mcpReq} / {@linkcode BaseContext.http | ctx.http }.
305+ * Kept as deprecated forwarding properties on the context object so v1 handlers
306+ * compile and run unchanged.
307+ *
308+ * @deprecated Use the nested fields on `ctx.mcpReq` / `ctx.http` instead. Will be removed in v3.
309+ */
310+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- phantom params kept for v1 source compatibility
311+ export type RequestHandlerExtra < _Req = unknown , _Notif = unknown > = ServerContext ;
312+
313+ // --- v1-compat: flat ctx.* getters (internal) ---
314+
315+ function legacyFieldGetter ( key : string , target : string , value : ( ) => unknown ) : PropertyDescriptor {
316+ return {
317+ enumerable : false ,
318+ configurable : true ,
319+ get ( ) {
320+ deprecate ( `ctx.${ key } ` , `ctx.${ key } is deprecated. Use ${ target } instead. Removed in v3.` ) ;
321+ return value ( ) ;
322+ }
323+ } ;
324+ }
325+
326+ /**
327+ * Attaches v1's flat `extra.*` fields to the context object as deprecated
328+ * forwarding getters. v2 nests these under `ctx.mcpReq` / `ctx.http`.
329+ *
330+ * @internal
331+ */
332+ function attachLegacyContextFields (
333+ ctx : BaseContext ,
334+ sendRequest : < T extends AnySchema > ( r : Request , s : T , o ?: RequestOptions ) => Promise < SchemaOutput < T > > ,
335+ sendNotification : ( n : Notification ) => Promise < void >
336+ ) : void {
337+ Object . defineProperties ( ctx , {
338+ signal : legacyFieldGetter ( 'signal' , 'ctx.mcpReq.signal' , ( ) => ctx . mcpReq . signal ) ,
339+ requestId : legacyFieldGetter ( 'requestId' , 'ctx.mcpReq.id' , ( ) => ctx . mcpReq . id ) ,
340+ _meta : legacyFieldGetter ( '_meta' , 'ctx.mcpReq._meta' , ( ) => ctx . mcpReq . _meta ) ,
341+ authInfo : legacyFieldGetter ( 'authInfo' , 'ctx.http?.authInfo' , ( ) => ctx . http ?. authInfo ) ,
342+ sendNotification : legacyFieldGetter ( 'sendNotification' , 'ctx.mcpReq.notify' , ( ) => sendNotification ) ,
343+ sendRequest : legacyFieldGetter ( 'sendRequest' , 'ctx.mcpReq.send' , ( ) => sendRequest ) ,
344+ taskStore : legacyFieldGetter ( 'taskStore' , 'ctx.task?.store' , ( ) => ctx . task ?. store ) ,
345+ taskId : legacyFieldGetter ( 'taskId' , 'ctx.task?.id' , ( ) => ctx . task ?. id ) ,
346+ taskRequestedTtl : legacyFieldGetter ( 'taskRequestedTtl' , 'ctx.task?.requestedTtl' , ( ) => ctx . task ?. requestedTtl )
347+ } ) ;
348+ }
349+
282350/**
283351 * Information about a request's timeout state
284352 */
@@ -604,8 +672,11 @@ export abstract class Protocol<ContextT extends BaseContext> {
604672 } ,
605673 http : extra ?. authInfo ? { authInfo : extra . authInfo } : undefined ,
606674 task : taskContext
607- } ;
675+ // Deprecated flat fields (signal, requestId, sendNotification, sendRequest, task*) are
676+ // attached as getters by attachLegacyContextFields() below.
677+ } as BaseContext ;
608678 const ctx = this . buildContext ( baseCtx , extra ) ;
679+ attachLegacyContextFields ( ctx , sendRequest , sendNotification ) ;
609680
610681 // Starting with Promise.resolve() puts any synchronous errors into the monad as well.
611682 Promise . resolve ( )
0 commit comments