@@ -28,6 +28,7 @@ import type {
2828 Result ,
2929 ServerCapabilities ,
3030 ServerContext ,
31+ StandardSchemaV1 ,
3132 ToolResultContent ,
3233 ToolUseContent
3334} from '@modelcontextprotocol/core' ;
@@ -53,6 +54,13 @@ import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims'
5354
5455import { ExperimentalServerTasks } from '../experimental/tasks/server.js' ;
5556
57+ /** Three-arg send used by `_createMessageVia`/`_elicitInputVia`; satisfied by both `_requestWithSchema` and `ctx.mcpReq.send`. */
58+ type SendWithSchema = < T extends StandardSchemaV1 > (
59+ request : { method : string ; params ?: Record < string , unknown > } ,
60+ resultSchema : T ,
61+ options ?: RequestOptions
62+ ) => Promise < StandardSchemaV1 . InferOutput < T > > ;
63+
5664export type ServerOptions = ProtocolOptions & {
5765 /**
5866 * Capabilities to advertise as being supported by this server.
@@ -131,13 +139,18 @@ export class Server extends Protocol<ServerContext> {
131139 protected override buildContext ( ctx : BaseContext , transportInfo ?: MessageExtraInfo ) : ServerContext {
132140 // Only create http when there's actual HTTP transport info or auth info
133141 const hasHttpInfo = ctx . http || transportInfo ?. request || transportInfo ?. closeSSEStream || transportInfo ?. closeStandaloneSSEStream ;
142+ const sendOpts = ( options ?: RequestOptions ) : RequestOptions => ( { ...options , relatedRequestId : ctx . mcpReq . id } ) ;
134143 return {
135144 ...ctx ,
136145 mcpReq : {
137146 ...ctx . mcpReq ,
138- log : ( level , data , logger ) => this . sendLoggingMessage ( { level, data, logger } ) ,
139- elicitInput : ( params , options ) => this . elicitInput ( params , options ) ,
140- requestSampling : ( params , options ) => this . createMessage ( params , options )
147+ log : async ( level , data , logger ) => {
148+ if ( this . _capabilities . logging && ! this . isMessageIgnored ( level , ctx . sessionId ) ) {
149+ await ctx . mcpReq . notify ( { method : 'notifications/message' , params : { level, data, logger } } ) ;
150+ }
151+ } ,
152+ elicitInput : ( params , options ) => this . _elicitInputVia ( ctx . mcpReq . send , params , sendOpts ( options ) ) ,
153+ requestSampling : ( params , options ) => this . _createMessageVia ( ctx . mcpReq . send , params , sendOpts ( options ) )
141154 } ,
142155 http : hasHttpInfo
143156 ? {
@@ -441,14 +454,31 @@ export class Server extends Protocol<ServerContext> {
441454 params : CreateMessageRequest [ 'params' ] ,
442455 options ?: RequestOptions
443456 ) : Promise < CreateMessageResult | CreateMessageResultWithTools > {
444- // Capability check - only required when tools/toolChoice are provided
445- if ( ( params . tools || params . toolChoice ) && ! this . _clientCapabilities ?. sampling ?. tools ) {
457+ return this . _createMessageVia ( ( r , schema , opts ) => this . _requestWithSchema ( r , schema , opts ) , params , options ) ;
458+ }
459+
460+ /**
461+ * Shared body for {@linkcode createMessage} and `ctx.mcpReq.requestSampling`: capability check,
462+ * tool_use/tool_result pairing validation, and result-schema selection. The `send` argument
463+ * routes to either the connected driver (instance method) or `ctx.mcpReq.send` (per-request).
464+ *
465+ * NOTE: the capability check below reads `this._clientCapabilities`, which is a singleton
466+ * (set on the most recent `initialize`). For multi-session `handleHttp` deployments this
467+ * can read a different session's caps. The per-request `_meta.clientCapabilities` flow
468+ * fixes this in a follow-up; until then, do not rely on the cap gate for isolation.
469+ */
470+ private async _createMessageVia (
471+ send : SendWithSchema ,
472+ params : CreateMessageRequest [ 'params' ] ,
473+ options ?: RequestOptions
474+ ) : Promise < CreateMessageResult | CreateMessageResultWithTools > {
475+ if ( ! this . _clientCapabilities ?. sampling ) {
476+ throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support sampling capability.' ) ;
477+ }
478+ if ( ( params . tools || params . toolChoice ) && ! this . _clientCapabilities . sampling . tools ) {
446479 throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support sampling tools capability.' ) ;
447480 }
448481
449- // Message structure validation - always validate tool_use/tool_result pairs.
450- // These may appear even without tools/toolChoice in the current request when
451- // a previous sampling request returned tool_use and this is a follow-up with results.
452482 if ( params . messages . length > 0 ) {
453483 const lastMessage = params . messages . at ( - 1 ) ! ;
454484 const lastContent = Array . isArray ( lastMessage . content ) ? lastMessage . content : [ lastMessage . content ] ;
@@ -490,11 +520,10 @@ export class Server extends Protocol<ServerContext> {
490520 }
491521 }
492522
493- // Use different schemas based on whether tools are provided
494523 if ( params . tools ) {
495- return this . _requestWithSchema ( { method : 'sampling/createMessage' , params } , CreateMessageResultWithToolsSchema , options ) ;
524+ return send ( { method : 'sampling/createMessage' , params } , CreateMessageResultWithToolsSchema , options ) ;
496525 }
497- return this . _requestWithSchema ( { method : 'sampling/createMessage' , params } , CreateMessageResultSchema , options ) ;
526+ return send ( { method : 'sampling/createMessage' , params } , CreateMessageResultSchema , options ) ;
498527 }
499528
500529 /**
@@ -505,46 +534,49 @@ export class Server extends Protocol<ServerContext> {
505534 * @returns The result of the elicitation request.
506535 */
507536 async elicitInput ( params : ElicitRequestFormParams | ElicitRequestURLParams , options ?: RequestOptions ) : Promise < ElicitResult > {
537+ return this . _elicitInputVia ( ( r , schema , opts ) => this . _requestWithSchema ( r , schema , opts ) , params , options ) ;
538+ }
539+
540+ /**
541+ * Shared body for {@linkcode elicitInput} and `ctx.mcpReq.elicitInput`: form/url capability
542+ * sub-field check, mode defaulting, and post-receipt JSON-schema validation of `content`.
543+ */
544+ private async _elicitInputVia (
545+ send : SendWithSchema ,
546+ params : ElicitRequestFormParams | ElicitRequestURLParams ,
547+ options ?: RequestOptions
548+ ) : Promise < ElicitResult > {
508549 const mode = ( params . mode ?? 'form' ) as 'form' | 'url' ;
509550
510551 switch ( mode ) {
511552 case 'url' : {
512553 if ( ! this . _clientCapabilities ?. elicitation ?. url ) {
513554 throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support url elicitation.' ) ;
514555 }
515-
516556 const urlParams = params as ElicitRequestURLParams ;
517- return this . _requestWithSchema ( { method : 'elicitation/create' , params : urlParams } , ElicitResultSchema , options ) ;
557+ return send ( { method : 'elicitation/create' , params : urlParams } , ElicitResultSchema , options ) ;
518558 }
519559 case 'form' : {
520560 if ( ! this . _clientCapabilities ?. elicitation ?. form ) {
521561 throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support form elicitation.' ) ;
522562 }
523-
524563 const formParams : ElicitRequestFormParams =
525564 params . mode === 'form' ? ( params as ElicitRequestFormParams ) : { ...( params as ElicitRequestFormParams ) , mode : 'form' } ;
526565
527- const result = await this . _requestWithSchema (
528- { method : 'elicitation/create' , params : formParams } ,
529- ElicitResultSchema ,
530- options
531- ) ;
566+ const result = await send ( { method : 'elicitation/create' , params : formParams } , ElicitResultSchema , options ) ;
532567
533568 if ( result . action === 'accept' && result . content && formParams . requestedSchema ) {
534569 try {
535570 const validator = this . _jsonSchemaValidator . getValidator ( formParams . requestedSchema as JsonSchemaType ) ;
536571 const validationResult = validator ( result . content ) ;
537-
538572 if ( ! validationResult . valid ) {
539573 throw new ProtocolError (
540574 ProtocolErrorCode . InvalidParams ,
541575 `Elicitation response content does not match requested schema: ${ validationResult . errorMessage } `
542576 ) ;
543577 }
544578 } catch ( error ) {
545- if ( error instanceof ProtocolError ) {
546- throw error ;
547- }
579+ if ( error instanceof ProtocolError ) throw error ;
548580 throw new ProtocolError (
549581 ProtocolErrorCode . InternalError ,
550582 `Error validating elicitation response: ${ error instanceof Error ? error . message : String ( error ) } `
0 commit comments