@@ -454,10 +454,135 @@ async function testMultiSession(): Promise<void> {
454454 }
455455}
456456
457- // --------------- Test 6: CLI Exec Mode ---------------
457+ // --------------- Test 6: Stdio Transport ---------------
458+
459+ async function testStdio ( ) : Promise < void > {
460+ console . log ( "\n🧪 Test 6: Stdio transport" ) ;
461+
462+ const { spawn } = await import ( "node:child_process" ) ;
463+ const { resolve } = await import ( "node:path" ) ;
464+ const cliPath = resolve ( import . meta. dirname ?? "." , "../dist/cli.js" ) ;
465+
466+ // Helper: send a JSON-RPC message over stdio and collect the response
467+ const stdioCall = ( messages : object [ ] ) : Promise < string [ ] > => {
468+ return new Promise ( ( resolvePromise , reject ) => {
469+ const args = [ "--stdio" ] ;
470+ if ( API_KEY ) args . push ( "--apikey" , API_KEY ) ;
471+
472+ const child = spawn ( "node" , [ cliPath , ...args ] , {
473+ stdio : [ "pipe" , "pipe" , "pipe" ] ,
474+ } ) ;
475+
476+ let stdout = "" ;
477+ child . stdout ! . on ( "data" , ( chunk : Buffer ) => {
478+ stdout += chunk . toString ( ) ;
479+ } ) ;
480+
481+ const timeout = setTimeout ( ( ) => {
482+ child . kill ( ) ;
483+ reject ( new Error ( "stdio test timed out" ) ) ;
484+ } , 15000 ) ;
485+
486+ child . on ( "close" , ( ) => {
487+ clearTimeout ( timeout ) ;
488+ resolvePromise ( stdout . split ( "\n" ) . filter ( ( l ) => l . trim ( ) ) ) ;
489+ } ) ;
490+
491+ // Send all messages then close stdin
492+ for ( const msg of messages ) {
493+ child . stdin ! . write ( JSON . stringify ( msg ) + "\n" ) ;
494+ }
495+ child . stdin ! . end ( ) ;
496+ } ) ;
497+ } ;
498+
499+ // Test: initialize
500+ try {
501+ const lines = await stdioCall ( [
502+ {
503+ jsonrpc : "2.0" ,
504+ id : 1 ,
505+ method : "initialize" ,
506+ params : {
507+ protocolVersion : PROTOCOL_VERSION ,
508+ capabilities : { } ,
509+ clientInfo : { name : "stdio-test" , version : "1.0.0" } ,
510+ } ,
511+ } ,
512+ ] ) ;
513+ assert ( lines . length > 0 , "stdio: initialize returns response" ) ;
514+ const resp = JSON . parse ( lines [ 0 ] ) ;
515+ assert ( resp ?. result ?. serverInfo ?. name !== undefined , "stdio: server info present" ) ;
516+ assert ( resp ?. result ?. capabilities ?. tools !== undefined , "stdio: tools capability present" ) ;
517+ } catch ( err : any ) {
518+ assert ( false , "stdio: initialize succeeds" , err . message ) ;
519+ }
520+
521+ // Test: initialize + list tools
522+ try {
523+ const lines = await stdioCall ( [
524+ {
525+ jsonrpc : "2.0" ,
526+ id : 1 ,
527+ method : "initialize" ,
528+ params : {
529+ protocolVersion : PROTOCOL_VERSION ,
530+ capabilities : { } ,
531+ clientInfo : { name : "stdio-test" , version : "1.0.0" } ,
532+ } ,
533+ } ,
534+ { jsonrpc : "2.0" , method : "notifications/initialized" } ,
535+ { jsonrpc : "2.0" , id : 2 , method : "tools/list" } ,
536+ ] ) ;
537+ // Find tools/list response
538+ const toolsResp = lines . map ( ( l ) => JSON . parse ( l ) ) . find ( ( m : any ) => m . id === 2 ) ;
539+ const tools = toolsResp ?. result ?. tools ?? [ ] ;
540+ assert ( tools . length >= 8 , `stdio: tools/list returns ${ tools . length } tools` ) ;
541+ } catch ( err : any ) {
542+ assert ( false , "stdio: tools/list succeeds" , err . message ) ;
543+ }
544+
545+ // Test: tool call (geocode) via stdio
546+ if ( API_KEY ) {
547+ try {
548+ const lines = await stdioCall ( [
549+ {
550+ jsonrpc : "2.0" ,
551+ id : 1 ,
552+ method : "initialize" ,
553+ params : {
554+ protocolVersion : PROTOCOL_VERSION ,
555+ capabilities : { } ,
556+ clientInfo : { name : "stdio-test" , version : "1.0.0" } ,
557+ } ,
558+ } ,
559+ { jsonrpc : "2.0" , method : "notifications/initialized" } ,
560+ {
561+ jsonrpc : "2.0" ,
562+ id : 2 ,
563+ method : "tools/call" ,
564+ params : { name : "maps_geocode" , arguments : { address : "Tokyo Tower" } } ,
565+ } ,
566+ ] ) ;
567+ const geocodeResp = lines . map ( ( l ) => JSON . parse ( l ) ) . find ( ( m : any ) => m . id === 2 ) ;
568+ const content = geocodeResp ?. result ?. content ?? [ ] ;
569+ assert ( content . length > 0 , "stdio: geocode returns content" ) ;
570+ if ( content . length > 0 ) {
571+ const parsed = JSON . parse ( content [ 0 ] . text ) ;
572+ assert ( typeof parsed ?. location ?. lat === "number" , "stdio: geocode returns lat" ) ;
573+ }
574+ } catch ( err : any ) {
575+ assert ( false , "stdio: geocode succeeds" , err . message ) ;
576+ }
577+ } else {
578+ console . log ( " ⏭️ stdio tool call skipped (no GOOGLE_MAPS_API_KEY)" ) ;
579+ }
580+ }
581+
582+ // --------------- Test 7: CLI Exec Mode ---------------
458583
459584async function testExecMode ( ) : Promise < void > {
460- console . log ( "\n🧪 Test 6 : CLI exec mode" ) ;
585+ console . log ( "\n🧪 Test 7 : CLI exec mode" ) ;
461586
462587 const { execFileSync } = await import ( "node:child_process" ) ;
463588 const { resolve } = await import ( "node:path" ) ;
@@ -545,7 +670,8 @@ async function main() {
545670 console . log ( ` API Key: ${ API_KEY ? "✅ provided" : "⚠️ not set (some tests skipped)" } ` ) ;
546671 console . log ( "═══════════════════════════════════════════" ) ;
547672
548- // Test exec mode first (no server needed)
673+ // Test stdio and exec mode first (no server needed)
674+ await testStdio ( ) ;
549675 await testExecMode ( ) ;
550676
551677 try {
0 commit comments