@@ -2,9 +2,18 @@ import type { Context } from 'hono';
22import { Hono } from 'hono' ;
33import { vi } from 'vitest' ;
44
5- import { createMcpHonoApp } from '../src/hono.js' ;
5+ import { McpServer , SessionCompat } from '@modelcontextprotocol/server' ;
6+
7+ import { createMcpHonoApp , mcpHonoHandler } from '../src/hono.js' ;
68import { hostHeaderValidation } from '../src/middleware/hostHeaderValidation.js' ;
79
10+ const INIT_MESSAGE = {
11+ jsonrpc : '2.0' ,
12+ method : 'initialize' ,
13+ params : { clientInfo : { name : 'test-client' , version : '1.0' } , protocolVersion : '2025-11-25' , capabilities : { } } ,
14+ id : 'init-1'
15+ } ;
16+
817describe ( '@modelcontextprotocol/hono' , ( ) => {
918 test ( 'hostHeaderValidation blocks invalid Host and allows valid Host' , async ( ) => {
1019 const app = new Hono ( ) ;
@@ -106,4 +115,48 @@ describe('@modelcontextprotocol/hono', () => {
106115 expect ( res . status ) . toBe ( 200 ) ;
107116 expect ( await res . json ( ) ) . toEqual ( { preset : true } ) ;
108117 } ) ;
118+
119+ describe ( 'mcpHonoHandler' , ( ) => {
120+ function makeApp ( options ?: Parameters < typeof mcpHonoHandler > [ 1 ] ) {
121+ const mcp = new McpServer ( { name : 'test-server' , version : '1.0.0' } ) ;
122+ const app = createMcpHonoApp ( { host : '0.0.0.0' , allowedHosts : [ 'localhost' ] } ) ;
123+ app . all ( '/mcp' , mcpHonoHandler ( mcp , { enableJsonResponse : true , ...options } ) ) ;
124+ return app ;
125+ }
126+
127+ async function postJson ( app : Hono , body : unknown , headers : Record < string , string > = { } ) : Promise < Response > {
128+ return app . request ( 'http://localhost/mcp' , {
129+ method : 'POST' ,
130+ headers : {
131+ Host : 'localhost' ,
132+ 'Content-Type' : 'application/json' ,
133+ Accept : 'application/json, text/event-stream' ,
134+ ...headers
135+ } ,
136+ body : JSON . stringify ( body )
137+ } ) ;
138+ }
139+
140+ test ( 'serves initialize via mcp.dispatch() (stateless, no transport class)' , async ( ) => {
141+ const res = await postJson ( makeApp ( ) , INIT_MESSAGE ) ;
142+ expect ( res . status ) . toBe ( 200 ) ;
143+ const body = ( await res . json ( ) ) as { result : { serverInfo : { name : string } } } ;
144+ expect ( body . result ) . toMatchObject ( { serverInfo : { name : 'test-server' } } ) ;
145+ expect ( res . headers . get ( 'mcp-session-id' ) ) . toBeNull ( ) ;
146+ } ) ;
147+
148+ test ( 'serves session lifecycle via SessionCompat' , async ( ) => {
149+ const app = makeApp ( { session : new SessionCompat ( ) } ) ;
150+ const initRes = await postJson ( app , INIT_MESSAGE ) ;
151+ const sid = initRes . headers . get ( 'mcp-session-id' ) ;
152+ expect ( sid ) . toBeTruthy ( ) ;
153+ const pingRes = await postJson (
154+ app ,
155+ { jsonrpc : '2.0' , method : 'ping' , params : { } , id : 'p-1' } ,
156+ { 'mcp-session-id' : sid as string , 'mcp-protocol-version' : '2025-11-25' }
157+ ) ;
158+ expect ( pingRes . status ) . toBe ( 200 ) ;
159+ expect ( await pingRes . json ( ) ) . toMatchObject ( { jsonrpc : '2.0' , id : 'p-1' , result : { } } ) ;
160+ } ) ;
161+ } ) ;
109162} ) ;
0 commit comments