11package main
22
33import (
4- "bytes "
4+ "bufio "
55 "crypto/rand"
66 "encoding/json"
77 "fmt"
@@ -376,8 +376,8 @@ func buildJSONRPCRequest(method, toolName string, arguments map[string]any) (str
376376 return string (jsonData ), nil
377377}
378378
379- // executeServerCommand runs the specified command, sends the JSON request to stdin,
380- // and returns the response from stdout
379+ // executeServerCommand runs the specified command, performs the MCP initialization
380+ // handshake, sends the JSON request to stdin, and returns the response from stdout.
381381func executeServerCommand (cmdStr , jsonRequest string ) (string , error ) {
382382 // Split the command string into command and arguments
383383 cmdParts := strings .Fields (cmdStr )
@@ -393,28 +393,96 @@ func executeServerCommand(cmdStr, jsonRequest string) (string, error) {
393393 return "" , fmt .Errorf ("failed to create stdin pipe: %w" , err )
394394 }
395395
396- // Setup stdout and stderr pipes
397- var stdout , stderr bytes.Buffer
398- cmd .Stdout = & stdout
396+ // Setup stdout pipe for line-by-line reading
397+ stdoutPipe , err := cmd .StdoutPipe ()
398+ if err != nil {
399+ return "" , fmt .Errorf ("failed to create stdout pipe: %w" , err )
400+ }
401+
402+ // Stderr still uses a buffer
403+ var stderr strings.Builder
399404 cmd .Stderr = & stderr
400405
401406 // Start the command
402407 if err := cmd .Start (); err != nil {
403408 return "" , fmt .Errorf ("failed to start command: %w" , err )
404409 }
405410
406- // Write the JSON request to stdin
411+ // Use a scanner with a large buffer for reading JSON-RPC responses
412+ scanner := bufio .NewScanner (stdoutPipe )
413+ scanner .Buffer (make ([]byte , 0 , 1024 * 1024 ), 1024 * 1024 ) // 1MB max line size
414+
415+ // Step 1: Send MCP initialize request
416+ initReq , err := buildInitializeRequest ()
417+ if err != nil {
418+ return "" , fmt .Errorf ("failed to build initialize request: %w" , err )
419+ }
420+ if _ , err := io .WriteString (stdin , initReq + "\n " ); err != nil {
421+ return "" , fmt .Errorf ("failed to write initialize request: %w" , err )
422+ }
423+
424+ // Step 2: Read initialize response
425+ if ! scanner .Scan () {
426+ scanErr := scanner .Err ()
427+ return "" , fmt .Errorf ("failed to read initialize response: %v, stderr: %s" , scanErr , stderr .String ())
428+ }
429+ // Initialize response is discarded — we only need the handshake to complete
430+
431+ // Step 3: Send initialized notification
432+ if _ , err := io .WriteString (stdin , buildInitializedNotification ()+ "\n " ); err != nil {
433+ return "" , fmt .Errorf ("failed to write initialized notification: %w" , err )
434+ }
435+
436+ // Step 4: Send the actual request
407437 if _ , err := io .WriteString (stdin , jsonRequest + "\n " ); err != nil {
408- return "" , fmt .Errorf ("failed to write to stdin : %w" , err )
438+ return "" , fmt .Errorf ("failed to write request : %w" , err )
409439 }
410- _ = stdin .Close ()
411440
412- // Wait for the command to complete
441+ // Step 5: Read the actual response
442+ if ! scanner .Scan () {
443+ scanErr := scanner .Err ()
444+ return "" , fmt .Errorf ("failed to read response: %v, stderr: %s" , scanErr , stderr .String ())
445+ }
446+ response := scanner .Text ()
447+
448+ // Close stdin and wait for process to exit
449+ _ = stdin .Close ()
413450 if err := cmd .Wait (); err != nil {
414451 return "" , fmt .Errorf ("command failed: %w, stderr: %s" , err , stderr .String ())
415452 }
416453
417- return stdout .String (), nil
454+ return response , nil
455+ }
456+
457+ // buildInitializeRequest creates the MCP initialize handshake request.
458+ func buildInitializeRequest () (string , error ) {
459+ id , err := rand .Int (rand .Reader , big .NewInt (10000 ))
460+ if err != nil {
461+ return "" , fmt .Errorf ("failed to generate random ID: %w" , err )
462+ }
463+ msg := map [string ]any {
464+ "jsonrpc" : "2.0" ,
465+ "id" : int (id .Int64 ()),
466+ "method" : "initialize" ,
467+ "params" : map [string ]any {
468+ "protocolVersion" : "2024-11-05" ,
469+ "capabilities" : map [string ]any {},
470+ "clientInfo" : map [string ]any {
471+ "name" : "mcpcurl" ,
472+ "version" : "0.1.0" ,
473+ },
474+ },
475+ }
476+ data , err := json .Marshal (msg )
477+ if err != nil {
478+ return "" , fmt .Errorf ("failed to marshal initialize request: %w" , err )
479+ }
480+ return string (data ), nil
481+ }
482+
483+ // buildInitializedNotification creates the MCP initialized notification.
484+ func buildInitializedNotification () string {
485+ return `{"jsonrpc":"2.0","method":"notifications/initialized"}`
418486}
419487
420488func printResponse (response string , prettyPrint bool ) error {
0 commit comments