Commit 6332c79
authored
fix: disable standalone SSE stream in streamable HTTP transport to prevent HTTP MCP server startup failures (#4428)
HTTP MCP servers (e.g. Unwrap) show `status: error` at gateway startup
and advertise no tools to Copilot CLI, despite the endpoint being
reachable.
## Root cause
The SDK's `StreamableClientTransport` automatically issues a **GET
request for a persistent SSE stream** synchronously inside
`client.Connect()`, immediately after the `initialize` POST succeeds:
1. POST `initialize` → server responds ✓
2. `sessionUpdated()` → `connectStandaloneSSE()` → GET request
3. Server returns 5xx (or any non-4xx/non-405) → `c.fail()` marks
connection as broken
4. `client.Connect()` tries to send `initialized` notification →
`c.Write()` sees failure → returns error
5. All three transports fail → `recordError()` → `/health` reports
`status: error`
Additionally, `MaxRetries: 0` in the SDK defaults to **5 retries**, not
0, contrary to the existing comment.
## Changes
- **`internal/mcp/http_transport.go` / `connection.go`**: Set
`DisableStandaloneSSE: true` and `MaxRetries: -1` on
`StreamableClientTransport` in both `tryStreamableHTTPTransport` and
`reconnectSDKTransport`. The gateway is a request-response proxy and has
no use for server-initiated SSE notifications from backends.
```go
return &sdk.StreamableClientTransport{
Endpoint: url,
HTTPClient: httpClient,
MaxRetries: -1, // Disable retries (-1 = 0 retries; SDK treats 0 as "use default: 5")
DisableStandaloneSSE: true,
}
```
- **`internal/mcp/http_connection_test.go`**: Adds regression test
`TestNewHTTPConnection_StreamableTransport_BadSSEEndpoint` — server
returns valid initialize result for POST but 500 for GET; connection
must succeed via streamable transport with zero GET requests issued.
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `example.com`
> - Triggering command: `/tmp/go-build900524329/b513/launcher.test
/tmp/go-build900524329/b513/launcher.test
-test.testlogfile=/tmp/go-build900524329/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build900524329/b410/vet.cfg g_.a o_.o x_amd64/vet 01.o
roundrobin 03.o x_amd64/vet -W wkO_kTNLT .cfg x_amd64/vet . --gdwarf2
--64 x_amd64/vet` (dns block)
> - `invalid-host-that-does-not-exist-12345.com`
> - Triggering command: `/tmp/go-build900524329/b495/config.test
/tmp/go-build900524329/b495/config.test
-test.testlogfile=/tmp/go-build900524329/b495/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build900524329/b399/vet.cfg g_.a 9742532/b151/ x_amd64/vet
--gdwarf-5 grpcsync
p=/opt/hostedtoolcache/go/1.25.9/tmp/go-build511290287/b107/vet.cfg
x_amd64/vet -W ABSZSCNGR .cfg x_amd64/vet . --gdwarf2 --64 x_amd64/vet`
(dns block)
> - `nonexistent.local`
> - Triggering command: `/tmp/go-build900524329/b513/launcher.test
/tmp/go-build900524329/b513/launcher.test
-test.testlogfile=/tmp/go-build900524329/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build900524329/b410/vet.cfg g_.a o_.o x_amd64/vet 01.o
roundrobin 03.o x_amd64/vet -W wkO_kTNLT .cfg x_amd64/vet . --gdwarf2
--64 x_amd64/vet` (dns block)
> - `slow.example.com`
> - Triggering command: `/tmp/go-build900524329/b513/launcher.test
/tmp/go-build900524329/b513/launcher.test
-test.testlogfile=/tmp/go-build900524329/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build900524329/b410/vet.cfg g_.a o_.o x_amd64/vet 01.o
roundrobin 03.o x_amd64/vet -W wkO_kTNLT .cfg x_amd64/vet . --gdwarf2
--64 x_amd64/vet` (dns block)
> - `this-host-does-not-exist-12345.com`
> - Triggering command: `/tmp/go-build3852249779/b001/mcp.test
/tmp/go-build3852249779/b001/mcp.test
-test.testlogfile=/tmp/go-build3852249779/b001/testlog.txt
-test.paniconexit0 -test.run=TestNewHTTPConnection|TestHTTPConnection
-test.v=true -test.timeout=1m0s
0/internal/tracetransform/instrumentation.go x_amd64/compile OUTPUT
9742532/b015/ 168.63.129.16 x_amd64/compile -w p/go-build security
64=/_/GOROOT OUTPUT 64/src/runtime/c-unsafeptr=false 168.63.129.16 git`
(dns block)
> - Triggering command: `/tmp/go-build900524329/b522/mcp.test
/tmp/go-build900524329/b522/mcp.test
-test.testlogfile=/tmp/go-build900524329/b522/testlog.txt
-test.paniconexit0 -test.timeout=10m0s 9742�� olang.org/protob-errorsas
.cfg x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet -o 9742532/b408/_pk-s
-trimpath x_amd64/vet -p g/grpc/binarylog/usr/bin/runc -lang=go1.25
x_amd64/vet` (dns block)
> - Triggering command: `/tmp/go-build3289280713/b001/mcp.test
/tmp/go-build3289280713/b001/mcp.test
-test.testlogfile=/tmp/go-build3289280713/b001/testlog.txt
-test.paniconexit0 -test.run=TestNewHTTPConnection -test.v=true
-test.timeout=1m0s y bin/bash ntime.v2.task/mogit .cfg x_amd64/vet
/usr/bin/runc.original --ve�� 30977974af8f49f3088e082efdf88695
runtime-runc/moby /usr/local/bin/iptables io.containerd.ru/bin/sh .cfg
x_amd64/vet b83/log.json` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/github/gh-aw-mcpg/settings/copilot/coding_agent)
(admins only)
>
> </details>3 files changed
Lines changed: 72 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
377 | 377 | | |
378 | 378 | | |
379 | 379 | | |
380 | | - | |
381 | | - | |
382 | | - | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
383 | 384 | | |
384 | 385 | | |
385 | 386 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
582 | 583 | | |
583 | 584 | | |
584 | 585 | | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
445 | 445 | | |
446 | 446 | | |
447 | 447 | | |
448 | | - | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
449 | 457 | | |
450 | 458 | | |
451 | 459 | | |
| |||
0 commit comments