Commit graph go-sdk/mcp/transport.go
Author SHA1 Message Date
Guglielmo Colombo
b58aa8d8d8
mcp: ignore notification in stdio batches (#1277)
Fixes #1256
2026-09-18 09:26:01 +02:00
José M. Requena Plens
3210c2b34c
mcp: carry a cancelled notification's reason as the request's cancel cause (#1255)
The canceller read `CancelledParams.Reason` off the wire and dropped it:
`Connection.Cancel` cancelled the request's context with a nil cause, so
a handler that looked at `context.Cause` saw a bare `context.Canceled`
and nothing of what the peer said. The specification asks
implementations to log cancellation reasons, and the handler is the one
place that can act on one; #1100 had already given the request context a
cause, so the reason has a natural place to travel.

`Connection.CancelCause` is `Cancel` with a cause. The canceller now
cancels with an error that carries the peer's reason and unwraps to
`context.Canceled`, so `errors.Is` keeps classifying the cancellation as
one while `context.Cause` reads `request cancelled by the peer:
<reason>`. It also logs the id and the reason at debug level through the
logger the connection already carries, which is the SDK's own answer to
the SHOULD. No exported API changes: the cause type stays unexported and
is read through `context.Cause` and `Error()`.

`TestCancellation` now asserts the cause the SDK's own client produces
(its context's error, which the client sends as the reason), and
`TestCancellationReason` writes the call and the cancelled notification
to the connection directly with a reason the client would never send, on
a session pinned to 2025-11-25 so the hand-written request needs no
`_meta`. Both fail without the change.

Fixes #1254

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
2026-09-14 11:33:30 +00:00
Yaroslav
3632967404
mcp: add an sse event size cap (#1205)
update sse I/O code to limit the amount of memory a single
non-terminated event can take

---------

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
2026-09-02 11:35:23 +02:00
Guglielmo Colombo
21c18c6229
mcp: allow restricting the protocol versions a server supports (#1199)
Fixes #945
2026-08-28 16:06:10 +02:00
Guglielmo Colombo
039119057f
mcp: allow asynchronous cancel notification (#1151)
Fixes #1150
2026-08-07 10:43:17 +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
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
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
Ravish Gupta
4c00594be6
mcp: log out-of-band errors instead of dropping them (#887)
## Summary

Two error sites in the MCP runtime were previously unobservable:

- **`mcp/shared.go` `startKeepalive`** — when a keepalive ping failed,
the session was closed silently with no indication of why.
- **`mcp/transport.go` `connect`** — `jsonrpc2.OnInternalError` printed
via the package-level `log.Printf`, bypassing the `*slog.Logger`
configured on `ServerOptions`/`ClientOptions`.

Both sites now report through the existing `*slog.Logger` that `Server`
and `Client` already guarantee non-nil via `ensureLogger`. The logger is
threaded into the unexported `startKeepalive` and `connect` helpers as a
parameter — **no new public API surface**.

## Context

Per @jba's [suggestion on
#865](https://github.com/modelcontextprotocol/go-sdk/pull/865#issuecomment-4156166474):

> Maybe we can just use the logger in all the places we currently drop
the error. Then it's still reported, but we don't have to add any API.

This PR is that smaller alternative. It addresses the concrete
observability gap in #218 without expanding `ServerOptions`. PR #865
(the `ErrorHandler` callback proposal) remains open pending community
input on whether a structured callback API is also wanted on top of
logging.

`notifySessions` already logs at `Warn` level on delivery errors and is
intentionally left untouched.

## Test plan

- [x] New regression test `TestKeepAliveFailure_Logged` asserts the
keepalive failure produces a log line on the configured logger.
- [x] Verified the test fails when the new `logger.Error(...)` call is
removed (sanity check that it pins the behavior).
- [x] `go test ./mcp/... -count=1` passes.
- [x] `go vet ./...` clean.
- [x] `go build ./...` clean.

Refs #218

---------

Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-04-14 16:53:11 +02:00
Maciej Kisiel
16d990b041
mcp: protect ioConn.protocolVersion with a mutex (#832)
This is done to prevent concurrent reads and writes, which violate Go's
memory model. The concurrent write may be triggered e.g. by setting the
log level on
the client side.

Fixes #828
2026-03-03 15:13:10 +01:00
Maciej Kisiel
7b8d81c264
all: use case-sensitive JSON unmarshaling (#807)
As reported in
https://github.com/modelcontextprotocol/go-sdk/issues/805, case
insensitive unmarshaling can pose security issues. This change adjusts
all non-test and non-example unmarshaling to use a case sensitive mode.

This introduces a new external dependency
`github.com/segmentio/encoding`.
2026-02-18 16:38:36 +01:00
FanOne
7ecd8049c6
mcp: export session missing error (#771)
Fix: #715

- export session missing error
- modify `errSessionMissing` to `ErrSessionMissing`
- add unit test about session missing error
2026-02-03 15:46:49 +01:00
Robert Findley
272e0cd4f5
mcp: better handling for streamable context cancellation (#677)
After walking through our handling of streamable client context
cancellation (due to encountering a shutdown deadlock), I think I've
settled on a more coherent strategy for handling call cancellation:

- In our call handler, retire the request if the call exits due to
cancellation: the caller will never see the actual result anyway.
- In connectSSE, use the actual request context (the same context used
in Write) for the client request, so that it terminates when the context
is cancelled. Thread through the initialization context for the
standalone SSE request.

Also, a couple minor improvements:
- Use a detached context for the background context of the client
connection. We want to preserve context values (see #513), but it is not
right to cancel the connection after Connect has already returned, if
the context times out.
- Don't use Last-Event-ID != "" as the signal for whether the connectSSE
call is initial: if the standalone SSE stream disconnects without an
event ID, we'll still reconnect it, and don't want to do so without a
delay.

+ tests, updating the streamable client connection test harness to
accomodate the new aspects being exercised.

Fixes #662
2025-12-03 12:26:50 -05:00
isfzhang
1bec3c60a0
fix: handle Windows CRLF in MCP client(#664) (#665)
This change updates the MCP client stdio JSON decoder to correctly
handle
both Unix (`\n`) and Windows (`\r\n`) line endings. 

Fixes #664

Co-authored-by: qysz <i_zf@qq.com>
2025-12-01 12:16:39 -05:00
Robert Findley
2ce44d45e9
all: expose JSON-RPC error codes, and support URLElicitationRequired errors (#655)
Expose the jsonrpc.Error type to allow access to underlying JSON-RPC
error codes. Also, expose common JSON-RPC error codes in the jsonrpc
package, and MCP error codes in the mcp package.

Also, implement support for the MCP URL elicitation required error
(-32042), enabling servers to request out-of-band user authorization and
clients to automatically handle these requests with retry logic.

Server-side:
- Add CodeURLElicitationRequired constant
- Add URLElicitationRequiredError() constructor.

Client-side:
- Add urlElicitationMiddleware to intercept URLElicitationRequired
  errors and retry calls.
- Update callElicitationCompleteHandler to signal waiting operations.
- Fix capability advertisement to support both form and URL modes.

Fixes #452
Fixes #623
2025-12-01 11:29:17 -05:00
Robert Findley
104d39c609
mcp: allow disabling stream replay; disable by default (#585)
This CL implements two changes that must be logically connected.

The first is to provide a mechanism for disabling stream replay.
Previously, our documentation stated that if
StreamableServerTransport.EventStore was nil, a default in-memory event
store would be used. In that case, there would be no way to completely
disable stream replay, and as described in #576, there are many cases
where replay is undesirable.

But of course changing the behavior of the transport zero value
implicitly affects its default behavior, and so this CL also implements
the proposal #580: stream replay is disabled by default.

Implementing this change required a significant refactoring, as
previously we were relying on the event store for serialized message
delivery from the JSON-RPC layer to the MCP layer: the connection would
write to the event store, and independently the stream (be it an
incoming POST or replay GET) would iterate and serve messages in the
stream.

In order to achieve the goals of this CL, it was necessary to decouple
storage from delivery. The 'stream' abstraction now tracks a delivery
callback that writes straight to the HTTP response. It would have been
convenient to store the ongoing http.ResponseWriter directly in the
stream (this is how typescript does it), but due to the design of our
EventStore API, only the HTTP handler knows the next event index, so a
'deliver' abstraction was an unfortunate requirement (suggestions for
how to further simplify this are welcome).

More simplification is possible: in particular, as a result of this
refactoring it should be entirely possible to clean up streams once
we've received all responses. Any replay would only need access to the
EventStore, if at all. This is left to a follow-up CL, to limit this
already significant change.

Furthermore, a nice consequence of this refactoring is that, when not
using event storage, servers can get synchronous feedback that message
delivery failed, which should avoid unnecessary work. We can
additionally cancel ongoing requests on early client termination, but
that is also left to a follow-up CL.

Throughout, the terminology 'standalone SSE stream' replaced 'hanging
GET' when referring to the non-replay GET request issued by the client.
This is consistent with other SDKs.

Fixes #576
Updates #580
2025-10-16 12:11:49 -04:00
Friedrich Große
010bdbc64d
mcp: fix goroutine leaks in unit tests (#496)
This PR fixes goroutine leaks in all unit tests.

To find the leaks I integrated https://github.com/uber-go/goleak and I
suggest to keep using it to catch any future regressions.

I used the folowing script to more easily find individual tests which
were leaking goroutines:

```
for t in $(go test -run=Nothing -list=. ./mcp | grep -v ok); do go test ./mcp -run="$t" > /dev/null && echo -n . || echo -e "\nDetected leak: $t"; done
```
2025-10-16 11:13:30 -04:00
Robert Findley
e8685b3022
mcp: don't close stdout when shutting down the stdio transport (#562)
We need to close stdin when the stdio transport is shut down, to unblock
its read loop. However, we don't technically need to close stdout.

Fixes #548
2025-10-07 11:08:38 -04:00
Konstantin Alikhanov
49047aebe8
Add IOTransport for separate reader/writer (#376)
This commit adds ability to create a mcp.Transport with specific
io.ReadCloser and io.WriteCloser.
Ii is needed when you need create an mcp.Client for mcp.Server launched
in separate process and you need to communicate with it with it's own
stdin/stdout.
2025-09-30 16:24:07 -04:00
Robert Findley
6dbf6c6450
all: minor cleanup (#545)
Also fix a flaky test case in TestStreamableServerTransport.
2025-09-30 15:53:00 -04:00
Chuck Ha
b835e07c26
mcp: fix inconsistent logging new lines (#541)
This change keeps the logging consistent with a new line after each log
whether an error or not.

Fixes #540

Co-authored-by: Chuck Ha <cha@nvidia.com>
2025-09-29 14:19:05 -04:00
Robert Findley
ac2f175214
docs: add feature and troubleshooting documentation (#463)
Add a framework for feature documentation, and start populating it with
our SDK documentation.

This framework is as follows:
- internal/docs/**.src.md is the markdown source for the docs/
  directory.
- The x/example/internal/cmd/weave tool is used to compile these docs to
  the top-level docs, supporting both linked code samples and generated
  tables of contents.
- The readme-check workflow is updated to check these docs as well.
- The structure of these docs follows the MCP spec.
- Wherever possible, example code is linked from actual Go documentation
  examples, so that it is testable.

Some minor modifications to the weave tool were made to support this
framework.

Additionally, partially fill out this documentation with content on
base protocol and client features, as well as troubleshooting help.

Along the way, a bug was encountered that our LoggingTransport was not
concurrency safe. This is fixed with a mutex.

Fixes https://github.com/modelcontextprotocol/go-sdk/issues/466
Fixes https://github.com/modelcontextprotocol/go-sdk/issues/409
Updates https://github.com/modelcontextprotocol/go-sdk/issues/442
2025-09-16 11:13:40 -04:00
Sam Thanawalla
b66679350e
transport.go: disable stdio batching for newer protocols (#453)
This PR disables batching support for stdio by storing the negotiated
protocol version and comparing it to protocolVersion20250618.

Fixes: #21
2025-09-11 13:33:46 -04:00
Rob Findley
b1c75f04d8 mcp: remove deprecated transport constructors
In #272, we made transports open structs and deprecated their
constructors. However, we left the constructors in place to allow go:fix
directives to facilitate migration.

Now let's remove the constructors before cutting the release.

Fixes #305
2025-09-10 11:21:46 -04:00
Rob Findley
2c40bdc4d7 mcp: systematically improve streamable client errors
The streamable client connection can break for a variety of reasons,
asynchronously to the client's request. Decorate these failures with
additional context to clarify why they occurred.

Add a test for the failure message of #393.

Fixes #393
2025-09-04 14:09:37 -04:00
Hongxiang Jiang
fb69971650
mcp: cleanup transport after keepalive ping fails
An onClose function is passed to the ServerSession and ClientSession to
help cleanup resources from the caller.

The onClose function will be executed as part of the ServerSession and
ClientSession closure.

Fixes #258
2025-08-22 15:26:52 -04:00
Jonathan Amsterdam
8e6ab130d8
mcp: pass TokenInfo to server handler (#292)
If there is a TokenInfo in the request context of a
StreamableServerTransport, then propagate it through to the
ServerRequest that is passed to server methods like callTool.

Fixes #317.
2025-08-20 10:50:16 -04:00
Rob Findley
98e67fdf62 mcp: improvements for 'stateless' streamable servers; 'distributed' mode
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
2025-08-20 09:59:52 -04:00
Rob Findley
bb6dadecca mcp: fix cancellation for HTTP transport
In #202, I added the checkRequest helper to validate incoming requests,
and invoked it in the stremable transports to preemptively reject
invalid HTTP requests, so that a jsonrpc error could be translated to an
HTTP error.

However, this introduced a bug: since cancellation was handled in the
jsonrpc2 layer, we never had to validate it in the mcp layer, and
therefore never added methodInfo. As a result, it was reported as an
invalid request in the http layer.

Add a test, and a fix. The simplest fix was to create stubs that are
placeholders for cancellation.

This was discovered in the course of investigating #285.
2025-08-14 11:32:51 -04:00
Robert Findley
e097918484
mcp: make transports open structs
As described in #272, there is really no reason for transports to be
closed structs with constructors, since their state must be established
by a call to Connect. Making them open structs simplifies their APIs,
and means that all transports can be extended in the future: we don't
have to create empty Options structs just for the purpose of future
compatibility.

For now, the related constructors and options structs are simply
deprecated (with go:fix directives where possible). A future CL will
remove them prior to the v1.0.0 release.

For #272
2025-08-12 15:14:21 -04:00
Robert Findley
dae88535ba
mcp: support stateless streamable sessions (#277)
Support stateless streamable sessions by adding a GetSessionID function
to StreamableHTTPOptions. If GetSessionID returns "", the session is
stateless, and no validation is performed. This is implemented by
providing the session a trivial initialization state.

To implement this, some parts of #232 (distributed sessions) are copied
over, since they add an API for creating an already-initialized session.

In total, the following new API is added:
- StreamableHTTPOptions.GetSessionID
- ServerSessionOptions (a new parameter to Server.Connect)
- ServerSessionState
- ClientSessionOptions (a new parameter to Client.Connect, for symmetry)

For #10
2025-08-11 16:43:46 -04:00
Robert Findley
119a58314d
mcp: address some comments from #234 (#248)
I inadvertently merged #234 without pushing my latest patches. This CL
adds the missing changes.
2025-08-11 12:45:14 -04:00
Rob Findley
6ea7a6c653 mcp: simplify and fix the streamable client transport
Make several cleanups of the streamable client transport, encountered
during work on JSON support for the streamable server:

- The 'Close' condition is differentiated from asynchronous failures. A
  failure should unblock Read with an error, at which point the JSON-RPC
  connection will be broken and closed.
- Fields are reordered in streamableClientConn to make guards more
  apparent.
- The handling of sessionID is simplified: we simply set the session ID
  whenever we receive response headers. No need to have special handling
  for the first request, as the serializeation of session initialization
  is implemented in Client.Connect.
- Since the above bullet makes Write a trivial wrapper around
  postMessage, the two methods are merged.
- A bug is fixed where JSON responses were handled synchronously in
  Write. This lead to deadlock when a hanging client->server request is
  waiting on a server->client request. Now JSON is handled symmetrically
  to SSE: the Write returns once response headers are received.
  asynchronous to Write.
- The httpConnection interface is renamed to clientConnection, and
  receive the entire InitializeResult.
- streamableClientConn receivers are renamed to be consistently 'c'.
2025-08-08 12:38:43 -04:00
Rob Findley
c03cd684ab internal/jsonrpc2: allow asynchronous writes
This CL introduces a fundamental change to the jsonrpc2 library:
connection writes, which were previously serialized by the jsonrpc2
library itself, are now allowed to be concurrent.

The change in semantics of the jsonrpc2 library should hopefully be easy
to review, since moving the synchronization to the Writer implementation
is equivalent to the previous logic. However, this change is critical
for the streamable client transport, because it allows for concurrent
http requests to the server.

Consider that a write is a POST to the server, and we don't know that
write succeeded until we get the response header. Previously, we had the
following problem: if the client POSTs a request, and the server blocks
its response on a request made through the hanging GET, the client was
unable to respond because the initial POST is still blocked. We could
update our streamable server transport to force a flush of the response
headers, but we can't guarantee that other servers behave the same way.
Fundamentally, writes in the spec are asynchronous, and we need to
support that.
2025-08-08 12:38:43 -04:00
Rishabh Nrupnarayan
bb9087d252
mcp: handle bad trailing stdio input graciously (#179) (#192)
You can make MCP server log error, send response and then abruptly exit, with a json input malformed at the end.

While using json.RawMessage, once a valid first json is decoded, trailing bad input is silently ignored.
Without graciously handling this input, mcp server is currently sending response as well as encountering error.
It should just report error without further processing of request.

Fixes #179.
2025-08-06 11:40:39 -04:00
Rob Findley
a02c2ffd0e mcp: unblock reads of ioConn as soon as Close is called
Use a read loop and incoming channel so that we can select on incoming
messages and unblock calls of ioConn.Read as soon as Close is called.
This avoids the scenario of #224, where a close of the StdioTransport
does not gracefully shut down the JSON-RPC connection.

Fixes #224
2025-08-05 20:24:59 -04:00
Koichi Shiraishi
619bc41581
internal/jsonrpc2,mcp: fix typos (#183)
- add links to doc strings
- use correct symbol and package names
2025-07-28 12:18:03 -04:00
Jonathan Amsterdam
261a63ccc0
mcp: don't panic on bad JSON RPC input (#177)
Log internal errors from the jsonprc2 package, instead of panicking.

Fixes #175.
2025-07-28 10:29:59 -04:00
Jonathan Amsterdam
bfa5e30cd1
jsonrpc: package with JSON-RPC symbols (#117)
Move the aliases to the internal/jsonrpc2 package into their own
package.

Fixes #115.
2025-07-10 14:33:23 -04:00
Jonathan Amsterdam
c037ba51d2
mcp: negotiate protocol version (#112)
Negotiate the protocol version properly, and set the header on HTTP
transports.

We follow the logic of the Typescript SDK.

- On initialization, the client sends the latest version it can support,
and accepts the version that the server returns if the client supports
it. If not, the connection fails.

- The server accepts the client's version unless it doesn't support it,
in which case it replies with its latest version.

Fixes #103.
2025-07-10 11:06:55 -04:00
Jonathan Amsterdam
9b6327b3b7
mcp: add Session.ID (#67)
Support retrieving the session ID from client and server sessions.

Fixes #65.
2025-07-01 13:49:23 -04:00
Robert Findley
5aabc33c9b
all: minor cleanup and README improvements (#30)
Clean up some inaccurate comments, and update the README to strengthen
warnings about the current status.
2025-06-25 13:12:39 -04:00
Rob Findley
a590cfe063 internal/mcp: rename StdIO->Stdio
In CL 682976, Alan Donovan correctly points out that the term in the MCP
spec is 'stdio', and therefore the correct capitalization is Stdio.

Change-Id: Ib5bbe04f1a61de886298b4d0adc13aefd6aadd9c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/683075
Reviewed-by: Alan Donovan <adonovan@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-20 13:34:58 -07:00
Rob Findley
4012cc3d2e internal/mcp: use JSONRPC aliases consistently
Use JSONRPCMessage, JSONRPCRequest, and JSONRPCResponse aliases
throughout the internal/mcp package, so that no jsonrpc2 names are
exposed in the public API.

Change-Id: Ibce794907538706448c694eafccf599a5892ab17
Reviewed-on: https://go-review.googlesource.com/c/tools/+/682796
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-06-19 06:55:37 -07:00
Rob Findley
1fead0ba64 internal/mcp: rename Stream->Connection
After implementing the Streamable transport, it is apparent to me that
the name 'Stream' is confusing, as it can refer either to a stream in
the streamable transport, or to a JSON-RPC stream.

But the name 'Connection' is available, and a natural name for the
result of the Transport.Connect interface method. Therefore, rename
Stream to Connection, and update the streamable implementation to refer
to its HTTP connections as 'streams', consistent with other SDKs.

Change-Id: Ica55f84caabcbfa5a2fe029cc0c2db57da574875
Reviewed-on: https://go-review.googlesource.com/c/tools/+/682376
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-06-17 17:55:11 -07:00
Rob Findley
d2f2f87bbe internal/mcp: implement streamable HTTP transport
Add an (almost) complete implementation of the streamable HTTP
transport, including:
- A StreamableHTTPHandler for serving sessions.
- A StreamableHTTPServerTransport that supports session resumption.
- A minimal StreamableHTTPClientTransport.

Additionally, include the following minor changes:
- Refactor SSE event reading to use an iterator.
- Start to to aliases for JSON-RPC types (incomplete)
- Fix handling for null tool arguments (again)

Still to do:
- The streamable client does not support retry/resumption, or the
  side-channel GET requests.
- Support configurable session and event retention (garbage collection).
- Expose the HTTP client used for HTTP requests (for auth).

Change-Id: Ieba216ef901169b4660d601386fa0c7c7bb4c4b2
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667515
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-17 17:54:33 -07:00
Rob Findley
82be9ce756 internal/jsonrpc2_v2: refactor Reader/Writer and remove event dependency
In preparation for extracting the jsonrpc2_v2 package, update the Reader
and Writer interfaces to not report byte counts, and remove the
internal/event dependency.

Change-Id: Ie6b373736fe297c8427cd4ece7ddad33e275864b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/678956
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-10 07:56:09 -07:00
Jonathan Amsterdam
0d0c48a468 internal/mcp: middleware on the sending side
Implement sending middleware.

Sending middleware is useful for adding tracing, progress tokens,
and so on.

Change-Id: I0acab2550f0212d549c4adbcf1ebf425a9486df6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/676355
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-02 13:16:02 -07:00
Rob Findley
04ca8ac03b internal/mcp: generic handling of CallToolParams
Make Tool APIs future-proof by pushing generics into CallToolParams, and
using CallToolParams throughout. This has several advantages:

- We no longer need the awkward CallToolOptions type, which is almost
  but not quite like CallToolParams.
- NewTool no longer needs to define its own handler type: it accepts a
  ToolHandler[TArgs]. This also means its signature is future-proof.

Additionally, provide a `CallTool` helper (the analog of NewTool) to
serve as a more strongly typed variant of `ClientSession.CallTool`.

+ design, protocol generation

Change-Id: I80172a25bc50734dae84a541aa4776ed9161694e
Reviewed-on: https://go-review.googlesource.com/c/tools/+/676495
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-02 07:45:36 -07:00
Jonathan Amsterdam
1323fb5216 internal/mcp: dispatcher middleware
Implement the dispatcher middleware framework, allowing users to
wrap method dispatching with their own code.

The Dispatcher functions have an untyped params arg, but expect its
value to be a typed, unmarshaled XXXParams struct. That required
splitting the elegant dispatch[P, R] function into two parts, one
to do the unmarshaling and one to call the method. The nicest way
I could find to do that was to map from method names to a pair
of functions, one to unmarshal and one to call. We could also write
it as two switches on method name, which would require less machinery
but would duplicate the list of method names, already a tad fragile
because they are literal strings.

I also exported the initialize params and result, so user Dispatchers
could access them. All Params and Result types will have to be exported
for that reason.

Change-Id: Ie2c95cc17b4ef3d3d095dccdadb294e5f1590796
Reviewed-on: https://go-review.googlesource.com/c/tools/+/673515
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-16 11:24:52 -07:00