@@ -73,28 +73,54 @@ func (b *StdioBridge) loop(ctx context.Context) {
73
73
74
74
// Header include Mcp-Session-Id if needed
75
75
func (b * StdioBridge ) detectTransport (ctx context.Context ) (string , error ) {
76
- body , err := buildJSONRPCInitializeRequest ()
76
+ // 1) Probe for legacy SSE without sending any JSON-RPC
77
+ getReq , err := http .NewRequestWithContext (ctx , "GET" , b .baseURL .String (), nil )
77
78
if err != nil {
78
- logger .Errorf ("failed to marshal initialization request: %v" , err )
79
79
return "" , err
80
80
}
81
- req , err := http .NewRequestWithContext (ctx , "POST" , b .baseURL .String (), bytes .NewReader (body ))
81
+ getReq .Header .Set ("Accept" , "text/event-stream" )
82
+ copyHeaders (getReq .Header , b .headers )
83
+
84
+ getResp , err := http .DefaultClient .Do (getReq )
85
+ if err == nil {
86
+ ct := getResp .Header .Get ("Content-Type" )
87
+ err = getResp .Body .Close () // we’re only peeking at headers here
88
+ if err != nil {
89
+ return "" , fmt .Errorf ("failed to close GET response body: %w" , err )
90
+ }
91
+ if strings .HasPrefix (ct , "text/event-stream" ) {
92
+ // Legacy SSE: runLegacyReader will open the real stream.
93
+ return "legacy-sse" , nil
94
+ }
95
+ }
96
+
97
+ // 2) Not SSE -> treat as streamable HTTP and do a proper initialize
98
+ body , err := buildJSONRPCInitializeRequest () // keep if your server expects initialize for streamable
82
99
if err != nil {
83
- logger .Errorf ("failed to create HTTP request: %v" , err )
84
100
return "" , err
85
101
}
86
- req .Header .Set ("Content-Type" , "application/json" )
87
- req .Header .Set ("Accept" , "application/json, text/event-stream" )
88
- copyHeaders (req .Header , b .headers )
89
- resp , err := http .DefaultClient .Do (req )
102
+ postReq , err := http .NewRequestWithContext (ctx , "POST" , b .baseURL .String (), bytes .NewReader (body ))
90
103
if err != nil {
91
104
return "" , err
92
105
}
93
- defer resp .Body .Close ()
94
- if resp .StatusCode >= 400 && resp .StatusCode < 500 {
106
+ postReq .Header .Set ("Content-Type" , "application/json" )
107
+ postReq .Header .Set ("Accept" , "application/json, text/event-stream" )
108
+ copyHeaders (postReq .Header , b .headers )
109
+
110
+ postResp , err := http .DefaultClient .Do (postReq )
111
+ if err != nil {
112
+ return "" , err
113
+ }
114
+ defer postResp .Body .Close ()
115
+
116
+ // If a streamable server returns info + Mcp-Session-Id, great.
117
+ // Some legacy gateways might reply 4xx to POST at base URL.
118
+ if postResp .StatusCode >= 400 && postResp .StatusCode < 500 {
95
119
return "legacy-sse" , nil
96
120
}
97
- b .handleInitializeResponse (resp )
121
+
122
+ // Streamable: capture session headers and emit the response payload.
123
+ b .handleInitializeResponse (postResp )
98
124
return "streamable-http" , nil
99
125
}
100
126
@@ -104,7 +130,6 @@ func (b *StdioBridge) handleInitializeResponse(resp *http.Response) {
104
130
b .headers = make (http.Header )
105
131
}
106
132
b .headers .Set ("Mcp-Session-Id" , sid )
107
- logger .Infof ("Streamable HTTP session ID: %s" , sid )
108
133
}
109
134
data , err := io .ReadAll (resp .Body )
110
135
if err != nil {
@@ -307,15 +332,28 @@ func (b *StdioBridge) updatePostURL(path string) {
307
332
return
308
333
}
309
334
b .postURL = u
310
- logger .Infof ("POST URL updated to %s" , b .postURL )
311
335
}
312
336
313
337
func buildJSONRPCInitializeRequest () ([]byte , error ) {
314
338
req := map [string ]interface {}{
315
339
"jsonrpc" : "2.0" ,
316
340
"id" : 1 ,
317
341
"method" : "initialize" ,
318
- "params" : map [string ]interface {}{},
342
+ "params" : map [string ]interface {}{
343
+ "protocolVersion" : "2024-02-01" ,
344
+ "capabilities" : map [string ]interface {}{
345
+ "prompts" : true ,
346
+ "tools" : true ,
347
+ "resources" : map [string ]interface {}{
348
+ "subscribe" : true ,
349
+ "unsubscribe" : true ,
350
+ },
351
+ },
352
+ "clientInfo" : map [string ]interface {}{
353
+ "name" : "toolhive-stdio-bridge" ,
354
+ "version" : "0.1.0" ,
355
+ },
356
+ },
319
357
}
320
358
return json .Marshal (req )
321
359
}
0 commit comments