@@ -8,15 +8,15 @@ const {
88 mockRegisterPrompt,
99 mockRegisterResource,
1010 mockRegisterCapabilities,
11- mockConnect,
11+ mockServeStdio,
12+ mockNotify,
1213 mcpTool,
1314 cliTool,
1415 allTool,
1516 uiTool,
16- mockPrompt,
17- mockZodSchema
17+ mockPrompt
1818} = vi . hoisted ( ( ) => {
19- const zodSchema = { shape : { } , optional : ( ) => zodSchema } ;
19+ const stdioHandle = { close : vi . fn ( ) . mockResolvedValue ( undefined ) } ;
2020
2121const createMockTool = ( overrides : Record < string , unknown > ) => {
2222const fn = vi . fn ( ) . mockResolvedValue ( { status : 'complete' , result : 'test' } ) ;
@@ -40,8 +40,11 @@ const {
4040mockRegisterPrompt : vi . fn ( ) ,
4141mockRegisterResource : vi . fn ( ) ,
4242mockRegisterCapabilities : vi . fn ( ) ,
43- mockConnect : vi . fn ( ) . mockResolvedValue ( undefined ) ,
44- mockZodSchema : zodSchema ,
43+ mockServeStdio : vi . fn ( ( factory : ( ) => unknown ) => {
44+ factory ( ) ;
45+ return stdioHandle ;
46+ } ) ,
47+ mockNotify : vi . fn ( ) . mockResolvedValue ( undefined ) ,
4548mcpTool : createMockTool ( {
4649support : 1 ,
4750toolName : 'mcp_tool' ,
@@ -83,28 +86,37 @@ const {
8386} ;
8487} ) ;
8588
86- vi . mock ( '@modelcontextprotocol/sdk/ server/mcp.js ' , ( ) => ( {
89+ vi . mock ( '@modelcontextprotocol/server' , ( ) => ( {
8790McpServer : vi . fn ( function ( ) {
8891return {
8992registerTool : mockRegisterTool ,
9093registerPrompt : mockRegisterPrompt ,
9194registerResource : mockRegisterResource ,
92- server : { registerCapabilities : mockRegisterCapabilities } ,
93- connect : mockConnect
95+ server : { registerCapabilities : mockRegisterCapabilities }
9496} ;
9597} )
9698} ) ) ;
9799
98- vi . mock ( '@modelcontextprotocol/sdk/ server/stdio.js ' , ( ) => ( {
99- StdioServerTransport : vi . fn ( )
100+ vi . mock ( '@modelcontextprotocol/server/stdio' , ( ) => ( {
101+ serveStdio : mockServeStdio
100102} ) ) ;
101103
102- vi . mock ( '@internals/tools' , ( ) => ( {
103- tools : [ mcpTool , cliTool , allTool , uiTool ] ,
104- prompts : [ mockPrompt ] ,
105- jsonSchemaToZod : vi . fn ( ( ) => mockZodSchema ) ,
106- ToolSupport : { None : 0 , MCP : 1 , CLI : 2 , All : 3 }
107- } ) ) ;
104+ vi . mock ( '@internals/tools' , async ( ) => {
105+ const { default : z } = await import ( 'zod' ) ;
106+ return {
107+ tools : [ mcpTool , cliTool , allTool , uiTool ] ,
108+ prompts : [ mockPrompt ] ,
109+ jsonSchemaToZod : vi . fn ( ( ) => z . object ( { } ) ) ,
110+ ToolSupport : { None : 0 , MCP : 1 , CLI : 2 , All : 3 }
111+ } ;
112+ } ) ;
113+
114+ const createRequestContext = ( ) => ( {
115+ mcpReq : {
116+ _meta : { } ,
117+ notify : mockNotify
118+ }
119+ } ) ;
108120
109121describe ( 'MCP server' , ( ) => {
110122beforeEach ( ( ) => {
@@ -151,12 +163,12 @@ describe('MCP server', () => {
151163) ;
152164} ) ;
153165
154- it ( 'should handle tools without inputSchema' , async ( ) => {
166+ it ( 'should register tools without inputSchema using an empty Zod object ' , async ( ) => {
155167const { startMcpServer } = await import ( './index.js' ) ;
156168await startMcpServer ( ) ;
157169// mcpTool has no inputSchema — should still register without error
158170const mcpToolCall = mockRegisterTool . mock . calls . find ( call => call [ 0 ] === 'mcp_tool' ) ;
159- expect ( mcpToolCall [ 1 ] . inputSchema ) . toEqual ( { } ) ;
171+ expect ( mcpToolCall [ 1 ] . inputSchema . shape ) . toEqual ( { } ) ;
160172} ) ;
161173
162174it ( 'should register prompts' , async ( ) => {
@@ -182,17 +194,19 @@ describe('MCP server', () => {
182194expect ( result ) . toEqual ( { messages : [ ] } ) ;
183195} ) ;
184196
185- it ( 'should connect to stdio transport ' , async ( ) => {
197+ it ( 'should serve an MCP server factory over stdio ' , async ( ) => {
186198const { startMcpServer } = await import ( './index.js' ) ;
187199await startMcpServer ( ) ;
188- expect ( mockConnect ) . toHaveBeenCalledTimes ( 1 ) ;
200+ expect ( mockServeStdio ) . toHaveBeenCalledWith ( expect . any ( Function ) , {
201+ onerror : expect . any ( Function )
202+ } ) ;
189203} ) ;
190204
191205it ( 'should return string result as text content' , async ( ) => {
192206const { startMcpServer } = await import ( './index.js' ) ;
193207await startMcpServer ( ) ;
194208const handler = mockRegisterTool . mock . calls [ 0 ] [ 2 ] ;
195- const result = await handler ( { } ) ;
209+ const result = await handler ( { } , createRequestContext ( ) ) ;
196210expect ( result ) . toEqual ( {
197211structuredContent : { status : 'complete' , result : 'test' } ,
198212content : [ { type : 'text' , text : 'test' } ]
@@ -205,7 +219,7 @@ describe('MCP server', () => {
205219const handler = mockRegisterTool . mock . calls [ 0 ] [ 2 ] ;
206220const errorResult = { status : 'error' , message : 'failed' } ;
207221mcpTool . mockResolvedValueOnce ( errorResult ) ;
208- const result = await handler ( { } ) ;
222+ const result = await handler ( { } , createRequestContext ( ) ) ;
209223expect ( result . content [ 0 ] . text ) . toBe ( JSON . stringify ( errorResult ) ) ;
210224} ) ;
211225
@@ -215,10 +229,37 @@ describe('MCP server', () => {
215229const handler = mockRegisterTool . mock . calls [ 0 ] [ 2 ] ;
216230const objResult = { status : 'complete' , result : { key : 'value' } } ;
217231mcpTool . mockResolvedValueOnce ( objResult ) ;
218- const result = await handler ( { } ) ;
232+ const result = await handler ( { } , createRequestContext ( ) ) ;
219233expect ( result . content [ 0 ] . text ) . toBe ( JSON . stringify ( objResult ) ) ;
220234} ) ;
221235
236+ it ( 'should report progress through the v2 request context' , async ( ) => {
237+ const { startMcpServer } = await import ( './index.js' ) ;
238+ await startMcpServer ( ) ;
239+ const handler = mockRegisterTool . mock . calls [ 0 ] [ 2 ] ;
240+ const params : Record < string , unknown > = { } ;
241+ await handler ( params , {
242+ mcpReq : {
243+ _meta : { progressToken : 'progress-token' } ,
244+ notify : mockNotify
245+ }
246+ } ) ;
247+
248+ if ( typeof params . onProgress !== 'function' ) {
249+ throw new TypeError ( 'Expected an onProgress callback' ) ;
250+ }
251+ params . onProgress ( 'Loading metadata' ) ;
252+
253+ expect ( mockNotify ) . toHaveBeenCalledWith ( {
254+ method : 'notifications/progress' ,
255+ params : {
256+ progressToken : 'progress-token' ,
257+ progress : 1 ,
258+ message : 'Loading metadata'
259+ }
260+ } ) ;
261+ } ) ;
262+
222263it ( 'should advertise the io.modelcontextprotocol/ui extension capability' , async ( ) => {
223264const { startMcpServer } = await import ( './index.js' ) ;
224265await startMcpServer ( ) ;
@@ -359,15 +400,17 @@ describe('MCP server', () => {
359400expect ( mcpToolCall ! [ 1 ] . _meta ) . toBeUndefined ( ) ;
360401} ) ;
361402
362- it ( 'should exit on connection error' , async ( ) => {
363- mockConnect . mockRejectedValueOnce ( new Error ( 'Connection failed' ) ) ;
403+ it ( 'should exit on stdio error' , async ( ) => {
364404const exitSpy = vi . spyOn ( process , 'exit' ) . mockImplementation ( ( ) => undefined as never ) ;
365405const errorSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ) => { } ) ;
366406
367407const { startMcpServer } = await import ( './index.js' ) ;
368408await startMcpServer ( ) ;
409+ const options = mockServeStdio . mock . calls [ 0 ] [ 1 ] ;
410+ const error = new Error ( 'Connection failed' ) ;
411+ options . onerror ( error ) ;
369412
370- expect ( errorSpy ) . toHaveBeenCalledWith ( expect . any ( Error ) ) ;
413+ expect ( errorSpy ) . toHaveBeenCalledWith ( error ) ;
371414expect ( exitSpy ) . toHaveBeenCalledWith ( 1 ) ;
372415} ) ;
373416} ) ;
0 commit comments