@@ -28,6 +28,7 @@ import type {
2828 Result ,
2929 ServerCapabilities ,
3030 ServerContext ,
31+ StandardSchemaV1 ,
3132 TaskManagerOptions ,
3233 ToolResultContent ,
3334 ToolUseContent
@@ -54,6 +55,13 @@ import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims'
5455
5556import { ExperimentalServerTasks } from '../experimental/tasks/server.js' ;
5657
58+ /** Three-arg send used by `_createMessageVia`/`_elicitInputVia`; satisfied by both `_requestWithSchema` and `ctx.mcpReq.send`. */
59+ type SendWithSchema = < T extends StandardSchemaV1 > (
60+ request : { method : string ; params ?: Record < string , unknown > } ,
61+ resultSchema : T ,
62+ options ?: RequestOptions
63+ ) => Promise < StandardSchemaV1 . InferOutput < T > > ;
64+
5765/**
5866 * Extended tasks capability that includes runtime configuration (store, messageQueue).
5967 * The runtime-only fields are stripped before advertising capabilities to clients.
@@ -148,13 +156,18 @@ export class Server extends Protocol<ServerContext> {
148156 protected override buildContext ( ctx : BaseContext , transportInfo ?: MessageExtraInfo ) : ServerContext {
149157 // Only create http when there's actual HTTP transport info or auth info
150158 const hasHttpInfo = ctx . http || transportInfo ?. request || transportInfo ?. closeSSEStream || transportInfo ?. closeStandaloneSSEStream ;
159+ const sendOpts = ( options ?: RequestOptions ) : RequestOptions => ( { ...options , relatedRequestId : ctx . mcpReq . id } ) ;
151160 return {
152161 ...ctx ,
153162 mcpReq : {
154163 ...ctx . mcpReq ,
155- log : ( level , data , logger ) => this . sendLoggingMessage ( { level, data, logger } ) ,
156- elicitInput : ( params , options ) => this . elicitInput ( params , options ) ,
157- requestSampling : ( params , options ) => this . createMessage ( params , options )
164+ log : async ( level , data , logger ) => {
165+ if ( this . _capabilities . logging && ! this . isMessageIgnored ( level , ctx . sessionId ) ) {
166+ await ctx . mcpReq . notify ( { method : 'notifications/message' , params : { level, data, logger } } ) ;
167+ }
168+ } ,
169+ elicitInput : ( params , options ) => this . _elicitInputVia ( ctx . mcpReq . send , params , sendOpts ( options ) ) ,
170+ requestSampling : ( params , options ) => this . _createMessageVia ( ctx . mcpReq . send , params , sendOpts ( options ) )
158171 } ,
159172 http : hasHttpInfo
160173 ? {
@@ -458,14 +471,31 @@ export class Server extends Protocol<ServerContext> {
458471 params : CreateMessageRequest [ 'params' ] ,
459472 options ?: RequestOptions
460473 ) : Promise < CreateMessageResult | CreateMessageResultWithTools > {
461- // Capability check - only required when tools/toolChoice are provided
462- if ( ( params . tools || params . toolChoice ) && ! this . _clientCapabilities ?. sampling ?. tools ) {
474+ return this . _createMessageVia ( ( r , schema , opts ) => this . _requestWithSchema ( r , schema , opts ) , params , options ) ;
475+ }
476+
477+ /**
478+ * Shared body for {@linkcode createMessage} and `ctx.mcpReq.requestSampling`: capability check,
479+ * tool_use/tool_result pairing validation, and result-schema selection. The `send` argument
480+ * routes to either the connected driver (instance method) or `ctx.mcpReq.send` (per-request).
481+ *
482+ * NOTE: the capability check below reads `this._clientCapabilities`, which is a singleton
483+ * (set on the most recent `initialize`). For multi-session `handleHttp` deployments this
484+ * can read a different session's caps. The per-request `_meta.clientCapabilities` flow
485+ * fixes this in a follow-up; until then, do not rely on the cap gate for isolation.
486+ */
487+ private async _createMessageVia (
488+ send : SendWithSchema ,
489+ params : CreateMessageRequest [ 'params' ] ,
490+ options ?: RequestOptions
491+ ) : Promise < CreateMessageResult | CreateMessageResultWithTools > {
492+ if ( ! this . _clientCapabilities ?. sampling ) {
493+ throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support sampling capability.' ) ;
494+ }
495+ if ( ( params . tools || params . toolChoice ) && ! this . _clientCapabilities . sampling . tools ) {
463496 throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support sampling tools capability.' ) ;
464497 }
465498
466- // Message structure validation - always validate tool_use/tool_result pairs.
467- // These may appear even without tools/toolChoice in the current request when
468- // a previous sampling request returned tool_use and this is a follow-up with results.
469499 if ( params . messages . length > 0 ) {
470500 const lastMessage = params . messages . at ( - 1 ) ! ;
471501 const lastContent = Array . isArray ( lastMessage . content ) ? lastMessage . content : [ lastMessage . content ] ;
@@ -507,11 +537,10 @@ export class Server extends Protocol<ServerContext> {
507537 }
508538 }
509539
510- // Use different schemas based on whether tools are provided
511540 if ( params . tools ) {
512- return this . _requestWithSchema ( { method : 'sampling/createMessage' , params } , CreateMessageResultWithToolsSchema , options ) ;
541+ return send ( { method : 'sampling/createMessage' , params } , CreateMessageResultWithToolsSchema , options ) ;
513542 }
514- return this . _requestWithSchema ( { method : 'sampling/createMessage' , params } , CreateMessageResultSchema , options ) ;
543+ return send ( { method : 'sampling/createMessage' , params } , CreateMessageResultSchema , options ) ;
515544 }
516545
517546 /**
@@ -522,46 +551,49 @@ export class Server extends Protocol<ServerContext> {
522551 * @returns The result of the elicitation request.
523552 */
524553 async elicitInput ( params : ElicitRequestFormParams | ElicitRequestURLParams , options ?: RequestOptions ) : Promise < ElicitResult > {
554+ return this . _elicitInputVia ( ( r , schema , opts ) => this . _requestWithSchema ( r , schema , opts ) , params , options ) ;
555+ }
556+
557+ /**
558+ * Shared body for {@linkcode elicitInput} and `ctx.mcpReq.elicitInput`: form/url capability
559+ * sub-field check, mode defaulting, and post-receipt JSON-schema validation of `content`.
560+ */
561+ private async _elicitInputVia (
562+ send : SendWithSchema ,
563+ params : ElicitRequestFormParams | ElicitRequestURLParams ,
564+ options ?: RequestOptions
565+ ) : Promise < ElicitResult > {
525566 const mode = ( params . mode ?? 'form' ) as 'form' | 'url' ;
526567
527568 switch ( mode ) {
528569 case 'url' : {
529570 if ( ! this . _clientCapabilities ?. elicitation ?. url ) {
530571 throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support url elicitation.' ) ;
531572 }
532-
533573 const urlParams = params as ElicitRequestURLParams ;
534- return this . _requestWithSchema ( { method : 'elicitation/create' , params : urlParams } , ElicitResultSchema , options ) ;
574+ return send ( { method : 'elicitation/create' , params : urlParams } , ElicitResultSchema , options ) ;
535575 }
536576 case 'form' : {
537577 if ( ! this . _clientCapabilities ?. elicitation ?. form ) {
538578 throw new SdkError ( SdkErrorCode . CapabilityNotSupported , 'Client does not support form elicitation.' ) ;
539579 }
540-
541580 const formParams : ElicitRequestFormParams =
542581 params . mode === 'form' ? ( params as ElicitRequestFormParams ) : { ...( params as ElicitRequestFormParams ) , mode : 'form' } ;
543582
544- const result = await this . _requestWithSchema (
545- { method : 'elicitation/create' , params : formParams } ,
546- ElicitResultSchema ,
547- options
548- ) ;
583+ const result = await send ( { method : 'elicitation/create' , params : formParams } , ElicitResultSchema , options ) ;
549584
550585 if ( result . action === 'accept' && result . content && formParams . requestedSchema ) {
551586 try {
552587 const validator = this . _jsonSchemaValidator . getValidator ( formParams . requestedSchema as JsonSchemaType ) ;
553588 const validationResult = validator ( result . content ) ;
554-
555589 if ( ! validationResult . valid ) {
556590 throw new ProtocolError (
557591 ProtocolErrorCode . InvalidParams ,
558592 `Elicitation response content does not match requested schema: ${ validationResult . errorMessage } `
559593 ) ;
560594 }
561595 } catch ( error ) {
562- if ( error instanceof ProtocolError ) {
563- throw error ;
564- }
596+ if ( error instanceof ProtocolError ) throw error ;
565597 throw new ProtocolError (
566598 ProtocolErrorCode . InternalError ,
567599 `Error validating elicitation response: ${ error instanceof Error ? error . message : String ( error ) } `
0 commit comments