@@ -67,7 +67,7 @@ import { PROTOCOL_VERSION_META_KEY } from '../types/constants.js';
6767import { ProtocolErrorCode } from '../types/enums.js' ;
6868import { ProtocolError , UnsupportedProtocolVersionError } from '../types/errors.js' ;
6969import { isJSONRPCErrorResponse , isJSONRPCNotification , isJSONRPCRequest , isJSONRPCResultResponse } from '../types/guards.js' ;
70- import type { MessageClassification } from '../types/types.js' ;
70+ import type { JSONRPCNotification , JSONRPCRequest , MessageClassification } from '../types/types.js' ;
7171import { envelopeClaimVersion , hasEnvelopeClaim , requestMetaOf , validateEnvelopeMeta } from './envelope.js' ;
7272import { isModernProtocolVersion } from './protocolEras.js' ;
7373
@@ -126,17 +126,32 @@ export interface InboundLegacyRoute {
126126 requestedVersion ?: string ;
127127}
128128
129- /** The request claims the per-request envelope mechanism and is served on the modern path. */
130- export interface InboundModernRoute {
131- kind : 'modern' ;
132- /** Whether the classified message is a request or a notification. */
133- messageKind : 'request' | 'notification' ;
134- /**
135- * The classification handed to the per-request transport and validated by
136- * the protocol layer against the serving instance's negotiated era.
137- */
138- classification : MessageClassification ;
139- }
129+ /**
130+ * The request claims the per-request envelope mechanism and is served on the
131+ * modern path. Discriminated by `messageKind` so the typed `message` narrows
132+ * with it — the classifier has already proved the JSON-RPC shape via the
133+ * `isJSONRPCRequest` / `isJSONRPCNotification` guards, so consumers never
134+ * cast the body again.
135+ */
136+ export type InboundModernRoute =
137+ | {
138+ kind : 'modern' ;
139+ messageKind : 'request' ;
140+ /** The classified body — guard-proved {@linkcode JSONRPCRequest} shape. */
141+ message : JSONRPCRequest ;
142+ /**
143+ * The classification handed to the per-request transport and validated by
144+ * the protocol layer against the serving instance's negotiated era.
145+ */
146+ classification : MessageClassification ;
147+ }
148+ | {
149+ kind : 'modern' ;
150+ messageKind : 'notification' ;
151+ /** The classified body — guard-proved {@linkcode JSONRPCNotification} shape. */
152+ message : JSONRPCNotification ;
153+ classification : MessageClassification ;
154+ } ;
140155
141156/** The named steps of the inbound validation ladder, in evaluation order. */
142157export type InboundValidationRung =
@@ -461,9 +476,9 @@ function classifyBatch(body: readonly unknown[]): InboundClassificationOutcome {
461476 return { kind : 'legacy' , reason : 'batch' } ;
462477}
463478
464- function classifyRequestBody ( request : InboundHttpRequest , body : Record < string , unknown > ) : InboundClassificationOutcome {
465- const params = body [ ' params' ] ;
466- const method = body [ ' method' ] as string ;
479+ function classifyRequestBody ( request : InboundHttpRequest , body : JSONRPCRequest ) : InboundClassificationOutcome {
480+ const params = body . params ;
481+ const method = body . method ;
467482 const headerVersion = request . protocolVersionHeader ;
468483 const headerNamesModern = headerVersion !== undefined && isModernProtocolVersion ( headerVersion ) ;
469484
@@ -523,7 +538,7 @@ function classifyRequestBody(request: InboundHttpRequest, body: Record<string, u
523538 `the body names method ${ method } but the Mcp-Method header names ${ request . mcpMethodHeader } `
524539 ) ;
525540 }
526- return { kind : 'modern' , messageKind : 'request' , classification : classificationForClaim ( claimedVersion ) } ;
541+ return { kind : 'modern' , messageKind : 'request' , message : body , classification : classificationForClaim ( claimedVersion ) } ;
527542 }
528543
529544 // No claim: legacy-era traffic — unless the protocol-version header names a
@@ -554,9 +569,9 @@ function classifyRequestBody(request: InboundHttpRequest, body: Record<string, u
554569 return { kind : 'legacy' , reason : 'no-claim' , ...( headerVersion !== undefined && { requestedVersion : headerVersion } ) } ;
555570}
556571
557- function classifyNotificationBody ( request : InboundHttpRequest , body : Record < string , unknown > ) : InboundClassificationOutcome {
558- const params = body [ ' params' ] ;
559- const method = body [ ' method' ] as string ;
572+ function classifyNotificationBody ( request : InboundHttpRequest , body : JSONRPCNotification ) : InboundClassificationOutcome {
573+ const params = body . params ;
574+ const method = body . method ;
560575 const headerVersion = request . protocolVersionHeader ;
561576 const headerNamesModern = headerVersion !== undefined && isModernProtocolVersion ( headerVersion ) ;
562577
@@ -603,7 +618,7 @@ function classifyNotificationBody(request: InboundHttpRequest, body: Record<stri
603618 `the notification body names method ${ method } but the Mcp-Method header names ${ request . mcpMethodHeader } `
604619 ) ;
605620 }
606- return { kind : 'modern' , messageKind : 'notification' , classification } ;
621+ return { kind : 'modern' , messageKind : 'notification' , message : body , classification } ;
607622 }
608623
609624 // Notifications carry no body claim under the current spec, so the
@@ -623,6 +638,7 @@ function classifyNotificationBody(request: InboundHttpRequest, body: Record<stri
623638 return {
624639 kind : 'modern' ,
625640 messageKind : 'notification' ,
641+ message : body ,
626642 classification : { era : 'modern' , revision : headerVersion }
627643 } ;
628644 }
0 commit comments