1
- import { mergeCapabilities , Protocol , ProtocolOptions , RequestOptions } from '../shared/protocol.js' ;
1
+ import { mergeCapabilities , Protocol , ProtocolOptions , RequestHandlerExtra , RequestOptions } from '../shared/protocol.js' ;
2
2
import {
3
3
ClientCapabilities ,
4
4
CreateMessageRequest ,
@@ -29,11 +29,18 @@ import {
29
29
SUPPORTED_PROTOCOL_VERSIONS ,
30
30
LoggingLevel ,
31
31
SetLevelRequestSchema ,
32
- LoggingLevelSchema
32
+ LoggingLevelSchema ,
33
+ JSONRPCRequest ,
34
+ MessageExtraInfo
33
35
} from '../types.js' ;
34
36
import Ajv from 'ajv' ;
37
+ import { Context } from './context.js' ;
38
+ import { LifespanContext , RequestContext } from '../shared/requestContext.js' ;
39
+ import { ZodLiteral , ZodObject } from 'zod' ;
40
+ import { z } from 'zod' ;
41
+ import { Transport } from 'src/shared/transport.js' ;
35
42
36
- export type ServerOptions = ProtocolOptions & {
43
+ export type ServerOptions < LifespanContextT extends LifespanContext | undefined = undefined > = ProtocolOptions & {
37
44
/**
38
45
* Capabilities to advertise as being supported by this server.
39
46
*/
@@ -43,6 +50,11 @@ export type ServerOptions = ProtocolOptions & {
43
50
* Optional instructions describing how to use the server and its features.
44
51
*/
45
52
instructions ?: string ;
53
+
54
+ /**
55
+ * Optional lifespan context type.
56
+ */
57
+ lifespan ?: LifespanContextT ;
46
58
} ;
47
59
48
60
/**
@@ -73,12 +85,14 @@ export type ServerOptions = ProtocolOptions & {
73
85
export class Server <
74
86
RequestT extends Request = Request ,
75
87
NotificationT extends Notification = Notification ,
76
- ResultT extends Result = Result
88
+ ResultT extends Result = Result ,
89
+ LifespanContextT extends LifespanContext | undefined = undefined
77
90
> extends Protocol < ServerRequest | RequestT , ServerNotification | NotificationT , ServerResult | ResultT > {
78
91
private _clientCapabilities ?: ClientCapabilities ;
79
92
private _clientVersion ?: Implementation ;
80
93
private _capabilities : ServerCapabilities ;
81
94
private _instructions ?: string ;
95
+ private _lifespan ?: LifespanContextT ;
82
96
83
97
/**
84
98
* Callback for when initialization has fully completed (i.e., the client has sent an `initialized` notification).
@@ -90,12 +104,12 @@ export class Server<
90
104
*/
91
105
constructor (
92
106
private _serverInfo : Implementation ,
93
- options ?: ServerOptions
107
+ options ?: ServerOptions < LifespanContextT >
94
108
) {
95
109
super ( options ) ;
96
110
this . _capabilities = options ?. capabilities ?? { } ;
97
111
this . _instructions = options ?. instructions ;
98
-
112
+ this . _lifespan = options ?. lifespan ;
99
113
this . setRequestHandler ( InitializeRequestSchema , request => this . _oninitialize ( request ) ) ;
100
114
this . setNotificationHandler ( InitializedNotificationSchema , ( ) => this . oninitialized ?.( ) ) ;
101
115
@@ -125,6 +139,13 @@ export class Server<
125
139
return currentLevel ? this . LOG_LEVEL_SEVERITY . get ( level ) ! < this . LOG_LEVEL_SEVERITY . get ( currentLevel ) ! : false ;
126
140
} ;
127
141
142
+ // Runtime type guard: ensure extra is our Context
143
+ private isContextExtra (
144
+ extra : RequestHandlerExtra < ServerRequest | RequestT , ServerNotification | NotificationT >
145
+ ) : extra is Context < LifespanContextT , RequestT , NotificationT , ResultT > {
146
+ return extra instanceof Context ;
147
+ }
148
+
128
149
/**
129
150
* Registers new capabilities. This can only be called before connecting to a transport.
130
151
*
@@ -277,6 +298,65 @@ export class Server<
277
298
return this . _capabilities ;
278
299
}
279
300
301
+ /**
302
+ * Registers a handler where `extra` is typed as `Context` for ergonomic server-side usage.
303
+ * Internally, this wraps the handler and forwards to the base implementation.
304
+ */
305
+ public override setRequestHandler <
306
+ T extends ZodObject < {
307
+ method : ZodLiteral < string > ;
308
+ } >
309
+ > (
310
+ requestSchema : T ,
311
+ handler : (
312
+ request : z . infer < T > ,
313
+ extra : Context < LifespanContextT , RequestT , NotificationT , ResultT >
314
+ ) => ServerResult | ResultT | Promise < ServerResult | ResultT >
315
+ ) : void {
316
+ super . setRequestHandler ( requestSchema , ( request , extra ) => {
317
+ if ( ! this . isContextExtra ( extra ) ) {
318
+ throw new Error ( 'Internal error: Expected Context for request handler extra' ) ;
319
+ }
320
+ return handler ( request , extra ) ;
321
+ } ) ;
322
+ }
323
+
324
+ protected override createRequestExtra (
325
+ request : JSONRPCRequest ,
326
+ abortController : AbortController ,
327
+ capturedTransport : Transport | undefined ,
328
+ extra ?: MessageExtraInfo
329
+ ) : RequestHandlerExtra < ServerRequest | RequestT , ServerNotification | NotificationT > {
330
+ const base = super . createRequestExtra ( request , abortController , capturedTransport , extra ) as RequestHandlerExtra <
331
+ ServerRequest | RequestT ,
332
+ ServerNotification | NotificationT
333
+ > ;
334
+
335
+ // Wrap base in Context to add server utilities while preserving shape
336
+ const requestCtx = new RequestContext <
337
+ LifespanContextT ,
338
+ ServerRequest | RequestT ,
339
+ ServerNotification | NotificationT ,
340
+ ServerResult | ResultT
341
+ > ( {
342
+ signal : base . signal ,
343
+ authInfo : base . authInfo ,
344
+ requestInfo : base . requestInfo ,
345
+ requestId : base . requestId ,
346
+ _meta : base . _meta ,
347
+ sessionId : base . sessionId ,
348
+ lifespanContext : this . _lifespan as LifespanContextT ,
349
+ protocol : this
350
+ } ) ;
351
+
352
+ const ctx = new Context < LifespanContextT , RequestT , NotificationT , ResultT > ( {
353
+ server : this ,
354
+ requestCtx
355
+ } ) ;
356
+
357
+ return ctx ;
358
+ }
359
+
280
360
async ping ( ) {
281
361
return this . request ( { method : 'ping' } , EmptyResultSchema ) ;
282
362
}
0 commit comments