Commit graph go-sdk/mcp
Author SHA1 Message Date
Guglielmo Colombo
6de85ec3ed
mcp: do not enforce TTL on cached tool (#1053)
This PR removes the TTL enforcing on the cached tools, when used to
generate the http-parameters for CallTool, according to updated
[spec](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2972)

> Clients **MUST** construct `Mcp-Param-*` headers using the most
recently obtained `inputSchema` for the tool.
2026-07-07 13:43:10 +02:00
Guglielmo Colombo
d651e2d2e1
mcp: make NotificationSubscriptions a mandatory field (#1050)
Fixes #1037
2026-07-07 11:39:18 +02:00
Guglielmo Colombo
ec564f9ccb
mcp: cap negotiated protocolVersion in legacy initialize to protocolVersion20251125 (#1051)
Fixes #1033
2026-07-06 15:40:27 +00:00
Guglielmo Colombo
224a240875
mcp: do not exclude Notifications from carrying InitializeParams in Meta (#1049)
Fixes #1043
2026-07-06 17:10:26 +02:00
Yaroslav
ede1894174
fix: extra wrapper for input response map (#1045)
fixes #1036
2026-07-02 09:22:05 +02:00
Guglielmo Colombo
65ece59643
mcp: Allow registration of custom JSON-RPC methods (#956)
## Description

This PR introduces support for custom (non-standard) JSON-RPC methods in
the MCP Go SDK.
This feature enables servers to handle arbitrary JSON-RPC methods and
clients to invoke them, all while maintaining full type safety and
integrating seamlessly with the SDK's existing middleware and session
management.

- Server-Side Registration: Added AddReceivingCustomMethod to allow
servers to register handlers for custom methods. Incoming requests are
automatically unmarshaled into user-defined parameter structs.
- Client-Side Calling: Added AddSendingCustomMethod which returns a
strongly-typed caller function that clients can use to invoke custom
methods and receive strongly-typed results.
- Helper Structs: Provided ParamsBase and ResultBase for users to embed
in their custom structs. This allows them to easily satisfy the SDK's
internal Params and Result interfaces.

Fixes #954
2026-06-25 23:13:17 +02:00
Guglielmo Colombo
a817f61412
mcp: add Cacheable fields to DiscoverResult (#1022)
This PR add the Cacheable fields to the DiscoverResult, as it was added
later
(8e4c2322ca)
2026-06-24 14:08:43 +02:00
Guglielmo Colombo
7f4aa4a0ce
mcp: add 2026-07-28 to the supported protocol versions (#1020)
This PR adds the protocol version `2026-07-28` to the list of supported
protocol versions and fixes legacy tests hardcoding the legacy protocol
version for those use cases.
2026-06-24 12:02:56 +02:00
Guglielmo Colombo
b9e6fc5a66
mcp: add documentation for new protocol version (#1019)
Add documentation for new protocol version in the `internal/docs`
2026-06-23 17:07:38 +02:00
Guglielmo Colombo
01847b35b3
mcp: refactor notification of subscribed sessions (#1018)
This PR improves the notification of subscribed sessions removing
duplicated part
2026-06-23 09:50:33 +00:00
Guglielmo Colombo
b6649db4f9
mcp: Deprecate roots, sampling and logging (SEP-2577) (#1017)
Fixes #1013
2026-06-23 11:11:08 +02:00
Guglielmo Colombo
cd7d80c591
mcp: Implement subscriptions/listen rpc (SEP-2575) (#1007)
Fixes #966
2026-06-22 18:57:36 +02:00
Guglielmo Colombo
3486f6bcaf
mco: update JSON-RPC error codes to align with updated MCP specification (#1016)
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.
2026-06-22 10:27:31 +02:00
Guglielmo Colombo
17029489ff
mcp: bump protocol version from 2026-06-30 to 2026-07-28 (#1015)
The new official date for the protocol version has been moved to
'2026-07-28'. Update all the references in the code of the old version.
2026-06-22 09:53:04 +02:00
Guglielmo Colombo
b85574f685
mcp: Enable legacy initialize fallback on any error (#1014)
To guarantee a better compatibility with legacy server, enable the
fallback to the legacy initialize rpc in case of any error returned by
the server, in case of `server/discover` request.
2026-06-18 15:52:11 +02:00
Guglielmo Colombo
6333aa7c63
mcp: add ttl for list results (SEP-2549) (#1008)
Fixes #994
2026-06-18 15:20:21 +02:00
Guglielmo Colombo
71ffcb412a
mcp: add MCPGPDEBUG for opt-in Content-Type check (#1012)
Copy of existing
[PR](https://github.com/modelcontextprotocol/go-sdk/pull/972) patching
1.6.0 version
2026-06-17 11:41:35 +02:00
Guglielmo Colombo
96074c9918
fix: prevent duplicate in-flight request IDs (#1011)
Fixes #1004
2026-06-16 14:13:52 +00:00
Guglielmo Colombo
79e1a13dcf
mcp: tool.InputSchema and tool.OutputSchema validation (SEP-2106) (#1009)
Fixes #990
2026-06-16 13:55:47 +02:00
Guglielmo Colombo
91c010f664
mcp: fix http-standardization (SEP-2243) (#1010)
Fixes #905
2026-06-16 09:20:53 +00:00
Piyush Bag
bcbfe0ce36
fix(jsonrpc2): decode requests when method key is present (#1000)
## Summary

Fixes #976.

`DecodeMessage` used `msg.Method != ""` to classify messages. A wire
payload with `"method":""` was decoded as a **response**, so stdio
servers dropped it without a correlated JSON-RPC error.

## Approach

Decode into a `wireDecode` struct whose `method` field is
`json.RawMessage`. Treat the message as a **request** when the `method`
key is **present on the wire** (including `""`), which matches JSON-RPC
semantics.

This revision:

- Uses a **single JSON parse** per message (dedicated decode struct, no
secondary fields-map unmarshal)
- Documents why `json.RawMessage` is used for `method`
- Adds regression coverage so normal responses and id-only payloads
without a `method` key stay on the response path

## Test plan

- [x] `go test ./internal/jsonrpc2/... -v`
- [x] `go test ./mcp/... -run TestIOConnRead_EmptyMethod -v`
- [x] `go test ./... -count=1`
- [x] `go test -race ./internal/jsonrpc2/... -count=1`
- [x] Server conformance (`npx @modelcontextprotocol/conformance server
--url http://127.0.0.1:3000/mcp`) — 40 passed, 0 failed
- [x] Client conformance (`./scripts/client-conformance.sh`) — 217
passed, 0 failed

## Scope note

This PR addresses the primary repro in #976 (empty `method` with valid
`id`). Other cases listed in the issue (`id: null`, structurally invalid
envelopes) are unchanged.

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
2026-06-16 09:57:18 +02:00
Guglielmo Colombo
4dc99b48e1
mcp: Remove resumability and introduce MissingRequiredClientCapability error data (#1005)
Fixes #966
2026-06-15 09:47:42 +02:00
Guglielmo Colombo
88c58c3006
mcp: remove outdated comment (#1003) 2026-06-10 16:13:27 +02:00
Guglielmo Colombo
dd978160ab
mcp: Implement server-side support for discover method (SEP-2575) (#987)
## 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.
2026-06-08 17:52:10 +02:00
Guglielmo Colombo
00d3642840
mcp: add support for logging level (SEP-2575) (#997)
## Description

This PR adds the support for logging feature in meta.

Fixes #966
2026-06-08 15:56:38 +02:00
Guglielmo Colombo
d9714c61f1
mcp: Implement retry on advised supportedVersions in UnsupportedProtocolVersion error (#989)
## Description

This PR introduces a retry mechanism for the discover method according
to SEP-2575 (Issue #966)
2026-06-08 15:09:07 +02:00
Guglielmo Colombo
c60a318d22
mcp: Implement support for SEP-2575 on client side (#975)
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
2026-06-02 14:17:01 +02:00
Yufeng He
6d2bbff7d8
fix: add implementation description metadata (#981)
## Summary
- add the optional `description` field to `mcp.Implementation`
- keep the field omitted when empty
- cover JSON marshal/unmarshal behavior with a focused regression test

Fixes #977.

## To verify
- `go test ./mcp -run TestImplementationDescriptionJSON -count=1`
- `go test ./mcp -run TestServerRequest_PerRequestAccessors -count=1`
- `go test ./mcp -count=1`
- `go test ./...`
- `git diff --check`

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
2026-05-31 10:35:46 +02:00
Tomas Dabašinskas
8805aa85f7
mcp: add configurable keepalive failure threshold (#982)
mcp: add configurable keepalive failure threshold

Introduce `KeepAliveFailureThreshold` option in both `ClientOptions` and
`ServerOptions` to control how many consecutive keepalive ping failures
are tolerated before closing the session.

This aligns with the MCP spec's guidance that "multiple failed pings MAY
trigger a connection reset," allowing operators to tune resilience
against transient network hiccups without immediately tearing down
otherwise healthy sessions.

A threshold of 0 or 1 (the default) closes on the first failure,
preserving existing behavior. Higher values let isolated misses pass
while still closing the session once consecutive failures reach the
threshold. A successful ping resets the counter.

Tolerated failures are logged at WARN level; the final failure that
closes the session is logged at ERROR level.

This is rework of #979.
2026-05-31 10:25:49 +02:00
Yaroslav
189a85ad92
feat: multi-round-trip request implementation (SEP-2322) (#950)
### Context

Design (`design/mrtr.md`) and implementation proposal for Multi
Round-Trip Requests (MRTR) per
[SEP-2322](de6d76fba3/seps/2322-MRTR.md).

### Changes

- Introduce `InputRequest` and `InputResponse` sealed interfaces with
corresponding `InputRequestMap` and `InputResponseMap` types for custom
map JSON codec implementation.
- Extend `CallToolParams`, `GetPromptParams`, and `ReadResourceParams`
with `InputResponses`, `RequestState` and an unexported `resultType`
("complete" or "input_required") for retry round-trips. The type is set
by the SDK based on input requests presence.
- Add client-side MRTR middleware enabled by default that automatically
fulfills input requests and retries.
- Added MRTROptions on ClientOptions to allow disabling the middleware
and configuring the number of retries.
- Add server-side MRTR middleware for backward compatibility:
transparently bridges input requests to direct
Elicit/CreateMessage/ListRoots calls for older clients
2026-05-29 09:29:34 +02:00
Guglielmo Colombo
4cbdd6a514
mcp: Implement stateless server (SEP-2275) (#965)
## Description

This PR lays the foundational server-side groundwork for the `>=
2026-06-30` sessionless and stateless feature introduced by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)
and SEP-2567, tracked in
[design/stateless.md](614460a225/design/stateless.md).

### SEP-2575: Stateless MCP

*   **Per-request protocol detection in `ServerSession.handle()`**
Unmarshal `_meta` from the raw JSON-RPC params determines whether each
request follows the new protocol.

*   **Per-request typed accessors on `ServerRequest[P]`**
    ```go
    func (r *ServerRequest[P]) ProtocolVersion() string
    func (r *ServerRequest[P]) ClientInfo() *Implementation
    func (r *ServerRequest[P]) ClientCapabilities() *ClientCapabilities
    ```

* **Reject client->server `initialize`, `initialized` and `ping` for
new-protocol requests**

*   **Per-request `_meta` field name constants**
Three constants for the wire-protocol field names
(`MetaKeyProtocolVersion`, `MetaKeyClientInfo`,
`MetaKeyClientCapabilities`)

* **Stop synthesizing fake `InitializeParams` for new-protocol
requests**

Fixes: #966
2026-05-26 14:25:51 +00:00
Pavel Bazin
c044cdb734
fix: do not omitempty ReadOnlyHint in ToolAnnotations (#908)
`ReadOnlyHint` is a `bool` with omitempty, so the zero value (false) is
indistinguishable from unset, and **drops out** of marshaled JSON.

The current behavior causes an issue with the OpenAI MCP app submission
because they require explicit hints.

Consumers that explicitly set `ReadOnlyHint: false` on write tools lose
the field on the wire. Removing `omitempty` ensures `false` is always
serialized, which matches the MCP spec default.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
Co-authored-by: guglielmoc <guglielmoc@google.com>
2026-05-25 15:52:15 +02:00
Yufeng He
bed83e104b
fix: reject duplicate initialize requests (#962)
## Summary
- reject a second `initialize` request on an already initialized server
session
- keep the original `ServerSession.InitializeParams()` instead of
replacing it with later client parameters
- add a raw JSON-RPC regression test for the duplicate-initialize path

Fixes #961.

## To verify
- `go test ./mcp -run TestServerRejectsDuplicateInitialize -count=1`
- `go test ./mcp -count=1`
- `go test ./...`

---------

Co-authored-by: guglielmoc <guglielmoc@google.com>
2026-05-25 15:15:26 +02:00
Maciej Kisiel
1253d17add
mcp: do not call DELETE for sessionless & doc adjustments (#960) 2026-05-15 21:47:34 +02:00
Maciej Kisiel
8eb54632ad
mcp: remove session header handling in stateless mode (#952) 2026-05-15 09:51:14 +02:00
Maciej Kisiel
9f5e89fea2
mcp: refactor streamable handler control flow (#949) 2026-05-08 14:29:01 +02:00
Guglielmo Colombo
909372d8d1
mcp: HTTP Header Standardization for x-mcp-header (#915)
## Description

Implements
[SEP-2243](https://modelcontextprotocol.io/seps/2243-http-standardization)
(HTTP Header Standardization) for x-mcp-param custom header.

Fixes https://github.com/modelcontextprotocol/go-sdk/issues/905

---------

Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-05-08 12:39:18 +02:00
Justin Chang
d10c315008
mcp: write SSE comment on standalone stream so HTTP/2 reverse proxies flush HEADERS frame (#938)
Fixes #937.

### What this changes

Adds a single line — an SSE comment (`: ok\n\n`) — after `WriteHeader`
and before `Flush` on the standalone SSE GET stream. SSE comments are
explicitly ignored by clients (any line starting with `:` per the SSE
spec), but they produce an HTTP/2 DATA frame, which is what HTTP/2
reverse proxies need before they will forward the HEADERS frame.

### Why the existing fix isn't sufficient

Issue #410 was closed by #413, which added `WriteHeader(StatusOK)` +
`Flush()` to the same site. #870 refactored the `Flush()` to use
`http.NewResponseController`. Both correctly address HTTP/1.1: `Flush()`
writes the headers as text on the TCP stream and any HTTP/1.1-aware
proxy forwards them.

In HTTP/2, headers and body are separate frame types (HEADERS, DATA).
Reverse proxies batch them for efficiency and won't forward the HEADERS
frame on its own. `Flush()` on the response writer only pushes the
in-process buffer to the HTTP/2 stack; the proxy still holds the HEADERS
frame waiting for DATA. The standalone SSE stream never sends body data
on connect (the whole point — it's the long-poll listener for
server-pushed notifications), so without a deliberate "kick" the headers
never reach the client until the proxy tears down the stream on timeout.

The SSE-comment trick is the established mitigation for SSE servers
behind HTTP/2 proxies. It's used in Caddy
(https://github.com/caddyserver/caddy/issues/4247), referenced in the Go
issue tracker (https://github.com/golang/go/issues/31125), and is
independent of the proxy implementation.

### The diff

```go
 if s.id == "" {
     // Issue #410: the standalone SSE stream is likely not to receive messages
     // for a long time. Ensure that headers are flushed.
+    //
+    // On HTTP/2, headers and body travel as separate frames (HEADERS and
+    // DATA). Reverse proxies (e.g. Envoy, Caddy, net/http/httputil)
+    // commonly buffer the HEADERS frame until they have a DATA frame to
+    // coalesce it with — there is no HTTP/2 equivalent of HTTP/1.1's
+    // Transfer-Encoding: chunked signal that says "this is streaming, send
+    // headers now". Calling Flush() alone is not sufficient: it pushes
+    // the kernel buffer to the proxy, but the proxy still holds the
+    // HEADERS frame.
+    //
+    // Write an SSE comment (lines starting with ":" are ignored by
+    // clients per RFC) so a DATA frame is produced, which forces the
+    // proxy to forward both frames. See:
+    //   https://github.com/golang/go/issues/31125
+    //   https://github.com/caddyserver/caddy/issues/4247
     w.WriteHeader(http.StatusOK)
+    fmt.Fprint(w, ": ok\n\n")
     rc := http.NewResponseController(w)
     // Ignore returned error as flushing is best-effort.
     _ = rc.Flush()
 }
```

`fmt` is already imported in this file; no other dependencies change.

### Test

Added `TestStandaloneSSEEmitsCommentForHTTP2Flush`. It initializes a
streamable session via raw POST, then opens the standalone SSE GET and
asserts the first body byte starts with `:` (the SSE comment marker,
which is what produces the DATA frame HTTP/2 proxies need).

The test catches the bug behaviorally without requiring a full HTTP/2
reverse-proxy setup: it verifies the SDK's contract (emit body bytes
after headers on the standalone SSE stream). HTTP/2-specific behavior is
then a property of any conforming proxy.

I confirmed the test fails on `main` without the patch — it times out at
2s waiting for the first body byte — and passes in <1ms with the patch.
Full streamable test suite still passes.

### Reproduction matrix (from #937)

| Configuration | TTFB | Result |
|---|---|---|
| HTTP/2 + 30s request timeout | ~31s | Headers only on stream tear-down
|
| HTTP/2 + no request timeout | never | Headers never arrive |
| HTTP/1.1 + 30s request timeout | ~300ms | Headers immediate |
| HTTP/1.1 + no request timeout | ~270ms | Headers immediate |
| HTTP/2 POST (response has body) | ~350ms | Works (DATA frame
coalesces) |
| **HTTP/2 + this PR** | ~1ms | Headers immediate |

### Risk

- Behavior-preserving for HTTP/1.1 clients (a leading `:` line is a
valid SSE comment and is ignored by every conforming SSE client).
- No protocol-level concern: SSE explicitly defines comment lines as a
no-op for the consumer.
- The DATA frame adds 7 bytes per standalone SSE connection, sent once
on connect.

Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-05-07 14:28:38 +02:00
wucm667
9604499eed
mcp: use golang.org/x/time/rate for LoggingHandler rate limiting (#927)
Replaces the simple time-interval check in `LoggingHandler` with a
standard `rate.Limiter` from `golang.org/x/time/rate`, as suggested by
the existing TODO comment.

This provides a well-tested implementation and removes the need for
manual `lastMessageSent` tracking. The rate (`1/MinInterval`) and burst
(1) match the previous behavior exactly.

/cc @jba

Closes #924

---------

Signed-off-by: wucm667 <stevenwucongmin@gmail.com>
Co-authored-by: Jonathan Amsterdam <jba@users.noreply.github.com>
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-05-06 10:31:01 +02:00
wucm667
06c5f290f2
fix(mcp): prevent SIGSEGV in AddTool when schema is nil (#918)
## Bug

`mcp.AddTool` crashes the server with SIGSEGV when input/output struct
tags trigger a nil schema.

## Root Cause

In `mcp/server.go`, the function `setSchema` calls
`internalSchema.Resolve(...)` without checking if `internalSchema` is
nil. If the schema inference (e.g., via `jsonschema.ForType`) or a
provided schema results in a nil value, this dereference causes a panic.

## Fix

Added nil checks for `internalSchema` before calling `Resolve` in two
locations within `setSchema`:
1. After schema generation via `jsonschema.ForType`.
2. After processing a provided schema (which might be nil or fail to
unmarshal).

If `internalSchema` is nil, the function now returns a descriptive error
instead of crashing.

## Verification

- `go test ./mcp/...` passes.
- The fix prevents the crash by handling the nil case gracefully.

Closes #916.

---------

Signed-off-by: wucm667 <stevenwucongmin@gmail.com>
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
2026-05-06 09:29:29 +02:00
Yaroslav
2b7c9937e6
mcp: align resource not found error code with sep-2164 (#931)
Change the error code returned by ResourceNotFoundError from -32002 to
-32602 (jsonrpc.CodeInvalidParams), aligning with
[SEP-2164](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2164).

* Not gated with a protocol version check, because the previous error
code was never formally specified and the behaviour varied across SDKs.
* In case clients which rely on the old error code exist, the previous
behaviour can be restored with `MCPGODEBUG=customresnotfounderrcode=1`.
* `CodeResourceNotFound` deprecated since its only purpose going forward
is backward compatibility. The new code should use
`jsonrpc.CodeInvalidParams` directly.
* `CodeResourceNotFound` changed to `var` so it can stay in sync with
`ResourceNotFoundError` and existing code doing `code ==
mcp.CodeResourceNotFound` keeps working in both the default and compat
modes.
2026-05-05 16:20:02 +02:00
Scott Leggett
4f1b3e5062
fix: handle oauth2.RetrieveError during token refresh (#917)
Previously, an expired refresh token in the oauth2.Token returned from
OAuthHandler.TokenSource() would cause the connection to fail.

From the client perspective, this meant that the MCP connection was in a
hard-failed state with no way to re-authorize.

The change in this commit causes Authorize() to be called in the event
of both an oauth2.RetrieveError from the token endpoint, as well as in
the pre-existing case of a 401/403 HTTP response from the MCP server.
Clients will handle this in their existing Authorize() flows to get a
new valid token for the connection.

Supersedes and closes: #909.

Co-authored-by: Yaroslav <yarolegovich@gmail.com>
2026-05-04 13:07:03 +02:00
Dayna Blackwell
8e45782dbd
mcp: close HTTP response body from DELETE request in streamableClientConn.Close (#929)
## Summary

The DELETE request that terminates a streamable HTTP session discards
the `*http.Response` without closing its body. Per the `net/http`
documentation, this prevents the underlying TCP connection from being
reused and leaks it.

## The bug

```go
// mcp/streamable.go:2239 (before)
} else if _, err := c.client.Do(req); err != nil {
    c.closeErr = err
}
```

On the success path (no error), the response is silently discarded. The
default client is `http.DefaultClient` with keep-alives enabled, so the
leaked connection stays open until GC or process exit.

## The fix

```go
// mcp/streamable.go:2239 (after)
} else if resp, err := c.client.Do(req); err != nil {
    c.closeErr = err
} else {
    resp.Body.Close()
}
```

All 10 other `client.Do` call sites in this file close the response
body. This one was missed.

## Test plan

- [x] `go build ./...` passes
- [x] `go test ./... -short` passes

Fixes #928
2026-05-04 12:14:20 +02:00
Maciej Kisiel
f5f2015413
MCPGODEBUG update for 1.6.0 (#893) 2026-04-30 09:54:32 +02:00
Guglielmo Colombo
e01639a318
feat: HTTP Header Standardization for method and name (#907)
## Description

Implements SEP-2243
(https://modelcontextprotocol.io/seps/2243-http-standardization) (HTTP
Header Standardization) for Mcp-Method and Mcp-Name standard headers
(x-mcp-param custom headers will be covvered in separate PR).

Fixes #905
2026-04-29 17:33:39 +02:00
Ravish Gupta
db50910c70
mcp: do not re-prompt OAuth after cancelled Authorize (#885)
When `OAuthHandler.Authorize` returns a context-cancelled error, the
cancellation notification that the call layer sends in response to the
same ctx cancellation is routed back through the streamable client
transport, re-triggering the `401` → `Authorize` path and prompting the
user a second time for a request they have already abandoned.

Fix in two layers:

1. **`mcp/transport.go`** — bound the cancellation `Notify` with a fresh
`context.Background()`-derived timeout instead of reusing the cancelled
caller context. This guarantees the caller never blocks on a doomed
notify against a degraded connection.
2. **`mcp/streamable.go`** — when `Authorize` returns a
context-cancelled error, mark the streamable client connection as failed
via `c.fail()`. The existing failure check in `Write()` then
short-circuits the cancellation notification without re-invoking the
OAuth handler, matching the bug reporter's expectation that cancelling
`Authorize` abandons the connection.

Includes a regression test
(`TestStreamableClientOAuth_CancelledAuthorize_NoReprompt`) with a
blocking `OAuthHandler` that asserts `Authorize` is invoked exactly
once. The test was verified to fail on unfixed code (`got 2` calls) and
pass with the fix.

Fixes #882

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 16:58:57 +02:00
John Pugliesi
5f2cd8fd14
mcp: preserve transport errors in Write error chain (#888)
mcp: preserve transport errors in Write error chain

Custom RoundTrippers that return typed errors (e.g. for auth failures)
have those errors flattened to strings by `%v`, making `errors.As`
unusable for callers inspecting transport-level errors.

This is inconsistent with the OAuth handler path (line 1807) which
already uses `%w` to preserve errors for client inspection.

Relates to #579

Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-04-24 13:20:45 +02:00
Maciej Kisiel
41e1f941bb
mcp: remove default cross origin protection (#906)
Fixes #902
2026-04-24 11:35:22 +02:00
Kalvin C
35f1b07e48
mcp: accept parameterized Content-Type types (#890)
Use a shared helper for Content-Type parsing in streamable transport
request validation and client response handling.

Follow up to https://github.com/modelcontextprotocol/go-sdk/pull/853,
where we did this for `Accept` headers.

This allows the streamable mcp server to validate the following header
`Content-Type: application/json;charset=utf-8`

Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-04-23 08:51:47 -04:00
Maciej Kisiel
b37e497f0a
mcp: don't close session when keepalive ping returns method-not-found (#900)
Since ping is optional per the MCP spec, a peer that doesn't implement
it will respond with a -32601 (method not found) error. Previously,
startKeepalive treated all ping errors identically, closing the session
even when the peer was perfectly healthy.

Now, a method-not-found response stops the keepalive goroutine without
closing the session.

Fixes #897
2026-04-22 16:35:27 +02:00