# Problem
WireError.Is backs errors.Is checks used across the SDK against
sentinels such as ErrMethodNotFound, ErrRejected, and ErrClientClosing,
but had no direct test. Add a table-driven test for it.
While doing so, errors.Is(err, (*WireError)(nil)) was found to panic on
a nil pointer dereference, because Is read w.Code without a nil check.
Guard against a nil *WireError target so the comparison returns false.
```go
errors.Is(err, (*WireError)(nil)) // panic: nil pointer dereference
```
# Approach
Is read w.Code after a successful type assertion without checking
whether w itself was nil. The fix guards against a nil *WireError target
so the comparison returns false instead of panicking.
# Test plan
- go test ./internal/jsonrpc2/ passes.
- The new typed nil WireError case panics without the one-line fix and
passes with it, confirming the test exercises the guarded path.
Co-authored-by: Yaroslav <yarolegovich@gmail.com>
This PR updates the error codes to align with the updated specifications
in
https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2907
ErrServerClosing and ErrClientClosing are reverted to their 1.6.0
version codes, as they do not overlap anymore with the newly introduced
error codes.
## Summary
Part 3 of the SEP-2575 ("Make MCP Stateless") implementation. Builds on
PR #975 which landed the client-side discover/fallback flow and the
per-request `_meta` plumbing. This PR adds the **server-side**
counterparts:
- `server/discover` handler (replaces the
stub),ProtocolVersionSupporter`
- transport hook for per-transport version filtering,
- transport-agnostic version validation and method-removal enforcement
in `ServerSession.handle`,
- HTTP-level wire compliance for SEP-2575 error codes/status codes,
- bug fix in `internal/jsonrpc2`,`processResult` with an `MCPGODEBUG`
opt-out for backward compatibility.
Implements the client half of the SEP-2575 / `2026-06-30` protocol. This
CL is client-only; server-side `server/discover` is still a
`MethodNotFound` stub.
### Changes
- **`server/discover` probe in `Client.Connect()`** (`client.go`). When
the caller opts into `protocolVersion >= 2026-06-30`, the client probes
the server with `server/discover` before falling back to the legacy
`initialize`
handshake.
- **Per-request `_meta` injection** (`shared.go`, `injectRequestMeta`).
For`>= 2026-06-30` sessions every outgoing request gets the SEP-2575
triple (`protocolVersion`, `clientInfo`, `clientCapabilities`).
- **Server-side fix**: `ServerSession.handle` no longer pre-populates
`state.InitializeParams` from a `server/discover` request's `_meta`, so
a subsequent fallback `initialize` is not rejected as a duplicate.
### Fallback rules
Client falls back to the legacy `initialize` handshake when discover
returns:
- `MethodNotFound` (-32601),
- `UnsupportedProtocolVersionError` (-32004),
- an HTTP `Bad Request` (substring-matched on the error message, to
interop
with pre-2026-06-30 servers).
Fixes#966
When POST requests in the streamableClientConn return a transient error,
return this error to the caller rather than permanently breaking the
connection.
This is achieved by using the special sentinel ErrRejected error to the
jsonrpc2 layer. In doing so, the change revealed a pre-existing bug:
ErrRejected had the same code as ErrConnectionClosing, and
jsonrpc2.WireError implements errors.Is, so the two sentinel values
could be conflated. This is fixed by using a new internal code.
There's more to do for #683: we should also retry transient errors in
handleSSE.
For #683
Several improvements for the stateless streamable mode, plus support for
a 'distributed' (or rather, distributable) version of the stateless
server.
- Add a 'Stateless' option to StreamableHTTPOptions and
StreamableServerTransport, which controls stateless behavior.
GetSessionID may still return a non-empty session ID.
- Audit validation of stateless mode to allow requests with a session
id. Propagate this session ID to the temporary connection.
- Peek at requests to allow 'initialize' requests to go through to the
session, so that version negotiation can occur (FIXME: add tests).
Fixes#284
For #148
Audit all cases where params must not be null, and enforce this using
methodInfo via a new methodFlags bitfield that parameterizes method
requirements. Write extensive conformance tests catching all the
(server-side) crashes that were possible.
We should go further and validate schema against the spec, but that is
more indirect, more complicated, and potentially slower. For now we
adopt this more explicit approach.
Still TODO in a subsequent CL: verify the client side of this with
client conformance tests.
Additionally, improve some error messages that were redundant or leaked
internal implementation details.
Fixes#195
Produced with a patched version of modernize containing only
the efaceany pass:
$ go run ./gopls/internal/analysis/modernize/cmd/modernize/main.go -test -fix ./... ./gopls/...
This is very safe and forms the bulk of the modernize diff,
isolating the other changes for ease of review.
Change-Id: Iec9352bac5a639a5c03368427531c7842c6e9ad0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/650759
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
This makes it possible for users of the package to us the optional error
data field in error construction and error handling logic.
Updates golang/go#64882
Change-Id: Iaa554f42ff42a0b7355605ba0b49ac67f623da15
Reviewed-on: https://go-review.googlesource.com/c/tools/+/562575
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This eliminates a race between a successful Accept call and a
concurrent Close call, which previously could have shut down the
'run' goroutine before Accept sent to the newConns channel, causing
Accept to deadlock.
In fact, it eliminates the long-running 'run' goroutine entirely
(replacing it with a time.Timer), and in the process avoids leaking
O(N) closed connections when only the very first one is long-lived.
It also eliminates a potential double-Close bug: if the run method had
called l.wrapped.Close due to an idle timeout, a subsequent call to
Close would invoke l.wrapped.Close again. The io.Closer method
explicitly documents doubled Close calls as undefined behavior, and
many Closer implementations (especially test fakes) panic or deadlock
in that case.
It also eliminates a timer leak if the Listener rapidly oscillates
between active and idle: previously the implementation used
time.After, but it now uses an explicit time.Timer which can be
stopped (and garbage-collected) when the listener becomes active.
Idleness is now tracked based on the connection's Close method rather
than Read: we have no guarantee in general that a caller will ever
actually invoke Read (if, for example, they Close the connection as
soon as it is dialed), but we can reasonably expect a caller to at
least try to ensure that Close is always called.
We now also verify, using a finalizer on a best-effort basis, that the
Close method on each connection is called. We use the finalizer to
verify the Close call — rather than to close the connection implicitly
— because closing the connection in a finalizer would delay the start
of the idle timer by an arbitrary and unbounded duration after the
last connection is actually no longer in use.
Fixesgolang/go#46047.
Fixesgolang/go#51435.
For golang/go#46520.
For golang/go#49387.
Change-Id: If173a3ed7a44aff14734b72c8340122e8d020f26
Reviewed-on: https://go-review.googlesource.com/c/tools/+/388597
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Otherwise, the Await method on the corresponding AsyncCall will never
unblock, leading to a deadlock (detected by the test changes in
CL 388597).
For golang/go#46047
For golang/go#46520
For golang/go#49387
Change-Id: I7954f80786059772ddd7f8c98d8752d56d074919
Reviewed-on: https://go-review.googlesource.com/c/tools/+/388775
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>