Commit graph

333 commits

Author SHA1 Message Date
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
Riley Norton
a834f3cf9a
mcp: allow correct 'Accept' header values
Allow other header values for the 'Accept' header that imply
application/json and text/event-stream for requests to the Streamable
HTTP Transport.

Fixes #290

Unit tests here seemed like unnecessary complexity and wouldn't fit
cleanly with the style of the `streamable_test.go` tests. Happy to add
those if desired though.
2025-08-13 17:12:13 -04:00
Robert Findley
87f222477b
mcp: add a test for streamable sampling during a tool call
Add a test that attempts (and fails) to reproduce the bug reported in
issue #285.

For #285
2025-08-13 10:11:01 -04:00
Jonathan Amsterdam
6e03217c83
mcp: introduce Requests (#267)
Handlers and client/server methods take a single request argument
combining the session and params. This slightly simplifies the signature
for handlers, since there is no longer an often-ignored session
argument.

But more importantly, it opens the door to adding more information in
requests, such as auth info and HTTP request headers.

For #243.
2025-08-12 17:49:50 -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
Sam Thanawalla
679f777825
Update pull_request_template.md (#287)
pull_request_template: update template to reflect correct form
2025-08-12 14:14:57 -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
Kartik Verma
cb27392b2d
.gitignore: add
Add `.gitignore` for some common files.
2025-08-11 08:33:50 -04:00
Jonathan Amsterdam
be1ddf5f80 jsonrpc: paper over race in jsonrpc2 test
In jsonprc2_test.go, binder.Bind starts a goroutine.
That goroutine begins to run during jsonrpc2.bindConnection, and
can race with the setting of conn.write in bindConnection.

This PR adds a sleep, which is a poor way to deal with the race, but
the least invasive change. Better ones include running the test
function after Dial returns, or adding a Connection.Ready method
to detect when initialization is complete.
2025-08-09 16:00:45 -04:00
Jonathan Amsterdam
cccc086518 mcp: clarify Server.Run use
Make it clear that it's not needed for an HTTP server.
2025-08-09 15:59:26 -04:00
Robert Findley
4e413da771
mcp: rename mcp{Params,Result} to is{Params,Result} (#270)
For some reason, when making these marker methods, the standard naming
convention escaped me.
2025-08-09 11:06:59 -04:00
Robert Findley
388e000aa2
.github: update go 1.25 builds to rc3 (#271)
Now that rc3 is out, update our 1.25 builds to use it.
2025-08-09 11:04:44 -04:00
Rob Findley
0375535db4 mcp: implement support for JSON responses in the MCP streamable server
Add a new (currently unexported) jsonResponse option to
StreamableServerTransportOptions, and use it to control the response
content type, serving application/json if set.

Additionally:
- A transportOptions field is added to the StreamableHTTPOptions.
- A messages iterator is added to encapsulate the iteration of stream
  messages, since the handling of JSON and SSE responses are otherwise
  very different.
- The serving flow is refactored to avoid returning (statusCode,
  message), primarily because this seemed liable to lead to redundant
  calls to WriteHeader, because only local logic knows whether or not
  any data has been written to the response.
- The serving flow is refactored to delegate to responseJSON and
  responseSSE, according to the currently unexported jsonResponse
  option.
- A bug is fixed where all GET streams were considered persistent: the
  terminal condition req.Method == http.MethodPost && nOutstanding == 0
  was not right: GET requests may implement stream resumption.

Updates #211
2025-08-08 12:38:43 -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
Jonathan Amsterdam
e2cf95ca22
auth: add OAuth authenticating middleware for server (#261)
Add auth.RequireBearerToken and associated types.

This piece of middleware authenticates clients using OAuth 2.0, as
specified in the MCP spec.

For #237.

Usage:
```
st := mcp.NewStreamableServerTransport(...)
http.Handle(path, auth.RequireBearerToken(verifier, nil)(st.ServeHTTP))
```
2025-08-08 11:07:03 -04:00
Rob Findley
8186bf39c7 mcp: lock down Params and Result
Add unexported methods to the Params and Result interface, so that
they're harder to implement outside the mcp package.

It looks like these are the only two interfaces we need to lock down:
others are either intentionally open (Transport, Connection), or already
closed (Session).

Fixes #263
2025-08-07 15:59:31 -04:00
Sam Thanawalla
e9e0da8dac
jsonschema: remove jsonschema code and depend on google/jsonschema-go (#262)
This CL prunes the jsonschema directory and updates the go.mod.

For #28
2025-08-07 15:30:06 -04:00
Rob Findley
5ab63feefa mcp: don't mark the server sesion as initialized prematurely
Update ServerSession so that the session is not marked as initialized
until 'notifications/initialized' is actually received from the client.

Include a new test of this lifecycle strictness.

Fixes #225
2025-08-07 14:59:36 -04:00
Rob Findley
4608401cc4 mcp: simplify ServerSession.initialize
Make the simplifications described in #222. I don't know why we
originally had such fine granularity of locking: perhaps we were going
to execute 'oninitialized' callbacks.

Also update the TODO with a bit more information about jsonrpc2
behavior.

Fixes #222
2025-08-07 14:59:36 -04:00
Sam Thanawalla
5bd02a3c04
jsonschema: copy wrapf into jsonschema/util.go (#254)
jsonschema depends on wrapf in mcp/util.go, copy this into jsonschema
for the code move.
2025-08-06 13:02:10 -04:00
Sam Thanawalla
b392875dc5
internal/util: move FieldJSONInfo into jsonschema/util (#251)
This CL effectively removes any dependencies on mcp from jsonschema.
This will enable us to move the repo.
This CL also deletes unused marshalStructWithMap and
unmarshalStructWithMap which had a dependency on FieldJSONInfo.

Fixes: #250
2025-08-06 12:07:57 -04:00
Rob Findley
e00c485969 examples: add a 'listeverything' client example, and reorganize
Add a client example that lists all features on a stdio server. Since
this is our first client example, organize examples by client / server
-- otherwise it is too difficult to find client examples.

For #33
2025-08-06 11:46:59 -04:00
Jonathan Amsterdam
c132621eb0
jsonschema: marshal to bools (#247)
Marshal the empty schema to true, and its negation to false.

This is technically a breaking behavior change, but a properly written
consumer of JSON Schema will not notice.

For #244.

Fixes #230.
2025-08-06 11:41:02 -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
Jonathan Amsterdam
032f03bbea
jsonschema: add ForType function (#242)
Add ForType, which is like For but takes a reflect.Type.

Fixes #233.
2025-08-05 16:32:06 -04:00
davidleitw
be0bd77f48
mcp: test nil params crash vulnerability
Test that explicit nulls don't cause panics.
2025-08-05 12:28:36 -04:00
Rob Findley
f4a9396942 mcp: fix a bug causing premature termination of streams
As reported in #227, there was a race where streams could be terminated
before the final reply was written. Fix this by checking the number of
outstanding requests in the same critical section that acquires outgoing
messages to write.

Fixes #227
2025-08-05 12:14:02 -04:00
Rob Findley
272a5ac76c .github: add race and 1.25 tests
This CL makes the following changes to our Github workflows, motivated
by #227:

- Add -race tests.
- Add go1.25rc2 to the build matrix.
- Check out code before setting up Go, so that the setup step can use
  dependency caching.
2025-08-05 12:14:02 -04:00
winterfx
31c18fba2d
docs: fix middleware example function signature (#236)
- Correct MethodHandler type parameter to use fully qualified
mcp.ServerSession
- Update parameter types to use proper mcp.Params and mcp.Result instead
of any
- Ensure example code matches the actual API
2025-08-05 12:05:51 -04:00
cryo
90a0b1fb23
mcp: fix comments (#241)
Update inaccurate doc comment for ClientSession.
2025-08-05 10:46:39 -04:00
Jim Clark
cb17593a5d
update Subscribe/Unsubscribe handlers in ServerOpts (#231)
## Background

Since the Server has a map of ServersSessions->subscriptions, and takes
care of routing ResourceUpdate messages to ServerSessions, I think the
decision was made to leave *ServerSession out of the
SubscribeHandler/UnsubscribeHandler signatures. However, resource
subscriptions are not specific to the a particular client and this
information is kept private in the Session today.

## Proposal

Align the Subscribe/Unsubscribe handlers with the other two handlers
that are client specific (ProgressNotificationHandler, and
RootListChangeHanlder), and include *ServerSession in the signature.

It comes in practice because because a gateway server that needs to
forward subscriptions still needs this information. The routing logic in
Server is convenient and means that most servers won't need to know
which ServerSessions are subscribed. But I think there are still use
cases where the user will need to know.
2025-08-04 14:46:49 -04:00
Jonathan Amsterdam
1e40ede369
jsonschema: options for inference (#185)
- Add ForOptions to hold options for schema inference.

- Replace ForLax with ForOptions.IgnoreBadTypes.

- Add an option to provide schemas for arbitrary types.

- Provide a default mapping from types to schemas that includes stdlib
types with MarshalJSON methods.

- Add Schema.CloneSchemas. This is needed to make copies of the schemas
in the above map: a schema cannot appear twice in a parent schema,
because schema addresses matter when resolving internal references.
2025-08-04 10:50:57 -04:00
Sam Thanawalla
faaf77dc05
mcp/server: advertise completions only if installed (#213)
Previously, completions would always be advertised as a capability even
if the CompletionHandler was not installed by the server. This CL fixes
that.
2025-08-04 10:12:01 -04:00
cryo
0cde9623d3
mcp: fix comments (#221)
### change:
Fix typos in some comments.
2025-08-01 16:20:55 -04:00
Robert Findley
b34ba2149d
mcp: handle bad or missing params (#210)
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
2025-07-31 10:57:59 -04:00
Sam Thanawalla
a9a503f74a
mcp/streamable: add persistent SSE GET listener (#206)
This CL adds the optional persistent SSE GET listener as specified in
section 2.2.
https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#listening-for-messages-from-the-server

This enables server initiated SSE streams.

For #10
2025-07-30 17:00:50 -04:00
Robert Findley
56734edf46
mcp: reject notifications with unexpected ID field (#204)
Use the new 'isRequest' field on methodInfo to also reject notifications
with an unexpected ID field.

For #196
2025-07-30 15:19:51 -04:00
Robert Findley
9f48a04971
mcp: remove unused functions (cleanup) (#207)
Remove a few functions that were flagged by gopls as unused. We can
always add them back if necessary.
2025-07-30 14:59:47 -04:00
Francis Zhou
bacca7a1d2
Implement json.Unmarshaler to CreateMessageResult (#191)
When calling `ServerSession.CreateMessage`, will get an json unmarshal
error because `CreateMessageResult.Content` is an interface, and it
doesn't implement `json.Unmarshaller`.

Implement the `json.Unmarshaller` to `CreateMessageResult`, let it able
to unmarshal the client response without error
2025-07-30 13:25:06 -04:00
CSK
982a0bceca
feat(devcontainer): Adds Dev Container configuration (#154)
This fixes #151, which makes it easier for users to easily use GitHub
Codespaces, providing them with a VS Code like environment with their
preferred settings/themes and the required dependencies, all within the
browser.
2025-07-30 12:54:41 -04:00
Jonathan Amsterdam
0fa4a6c65b
CONTRIBUTING.md: timeout policy (#187)
Explain that we'll close issues or PRs after a period of inactivity.

This will reduce clutter on the issue and PR pages.
2025-07-30 12:53:56 -04:00
Robert Findley
64493be392
mcp: reject requests with bad IDs (#202)
Add an isRequest field to methodInfo, used it to reject non-notification
requests that lack a valid ID. Furthermore, lift this validation to the
transport layer for HTTP server transports, so that we can preemptively
reject bad HTTP requests.

Fixes #194
Fixes #197
2025-07-30 12:26:40 -04:00
Jonathan Amsterdam
3ac4ca9c5b
mcp: streamable.go: clarifications (#189)
Clarify or remove TODOs.

Make some code clearer.
2025-07-29 10:49:16 -04:00
Jonathan Amsterdam
64b5b9125e
mcp: support application/json in streamable client (#181)
The client handles POST responses with content type application/json.

For: #10
Fixes #129
2025-07-29 10:28:49 -04:00
Sam Thanawalla
2b6f7b5185
mcp/server: add initial capability server option (#182)
This CL adds an option for servers to advertise support for features
that have not been added yet. This includes Prompts, Tools, and
Resources as these could be dynamically added after initialization.

Fixes: #135.
2025-07-29 05:20:08 -04:00
Jonathan Amsterdam
a5aa370ea0
mcp: various cleanups to streamable code (#184)
- move done field outside of mutex hat
- refactor handler into a function that returns `error` to avoid repeated `http.Error` calls
- use an atomic for the signal channel to simplify locking
2025-07-28 13:58:37 -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