Commit graph go-sdk/mcp/mcp_test.go
Author SHA1 Message Date
Jonathan Amsterdam
b1780319e4 internal/mcp: future-proof prompt handling
Pass GetPromptParams as needed, in case that type ever gets additional
properties.

Change-Id: I83fa37bb2af5633a8c2bcdc47a85c3030dfe70fb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/674895
Auto-Submit: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-22 15:06:03 -07:00
Jonathan Amsterdam
edda031578 internal/mcp: implement logging
Provide a ServerSession method that directly implements the spec
method.

Also provide a custom slog.Handler that writes a notification on each
log call. It uses a slog.JSONHandler to format the message as JSON, then
adds the additional fields of LoggingMessageParams.

I decided that the direct method should handle levels, since that is
in the spec, but that rate-limiting is an extra feature, so is only
provided in the slog handler.

Change-Id: I1190ca124e992e61fb633ac52ee3492c8e6d99c9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/674498
Auto-Submit: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-22 15:02:13 -07:00
Jonathan Amsterdam
ce5b6cd926 internal/mcp: add notifications
Add notification logic for intialization and list changes.

Change-Id: I1d392bb49d5995f2046c400da955a033ab2715ce
Reviewed-on: https://go-review.googlesource.com/c/tools/+/673775
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-21 16:01:46 -07:00
Jonathan Amsterdam
9bcc6c758f internal/mcp: sampling
Implement sampling (the CreateMessage method).

Change-Id: Ie44a671dd62831122b25cd7386b5bfcdece7ff60
Reviewed-on: https://go-review.googlesource.com/c/tools/+/673176
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-19 11:15:58 -07:00
Jonathan Amsterdam
bac1b78b4a internal/mcp: client middleware
Implement middleware for clients.

Make the middleware types generic so we can factor out code
from both peers.

I was able to make the request handler generic, with one weird trick
(see handleRequest).

The first three declarations of shared.go, and a few others as well,
are copied unchanged from middleware.go.

Change-Id: Ib3239d8fcfda022ddb5b554c38cff4cdbc3704c1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/673635
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-19 11:15:45 -07:00
Jonathan Amsterdam
8361df724d internal/mcp: add the FileResourceHandler method
Provide secure reading of files on the local filesystem.

There are subtleties in path manipulation on Windows that I don't fully
understand, so I defer that work for now.

Change-Id: Ic11f4617dde4ac0b31ccd5b0a772bb2f56db026f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/673196
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-19 11:15:23 -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
Rob Findley
a8b98da480 internal/mcp: reinstate ClientSession
Add a logical client session type back to the SDK, to bring it closer to
the design. Also, remove 'ConnectionOptions', which is better
implemented using middleware, and reimplement logging as a
LoggingTransport middleware, which is also added to the design.

Change-Id: I908b4f837d513ffc60ae5b6171a31519cffafebc
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672795
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-05-14 12:41:58 -07:00
Rob Findley
dbf57af24b internal/mcp: rename LocalTransport->InMemoryTransport, and document
Update the design to mention our in-memory transport, since it is
generally useful, and update the implementation to use distinct types
for InMemoryTransport and StdIOTransport (the fact that they are both
implemented as an io.ReadWriteCloser is an implementation detail).

Change-Id: I8228ccf2092a56a730c113c4db105fb492cce112
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672715
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
Commit-Queue: Robert Findley <rfindley@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
TryBot-Bypass: Robert Findley <rfindley@google.com>
2025-05-14 10:11:26 -07:00
Rob Findley
ac828d5a82 internal/mcp: rename ServerConnection->ServerSession
Bring the Server implementation up to date with the spec by renaming
ServerConnection to ServerSession. Also update variable names, which
were in various states of disarray from the multiple renamings of this
type.

Change-Id: I4aec883db6d0500fb91e6daa51b702614877cf8a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672655
Auto-Submit: Robert Findley <rfindley@google.com>
TryBot-Bypass: Robert Findley <rfindley@google.com>
Commit-Queue: Robert Findley <rfindley@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-05-14 10:10:28 -07:00
Jonathan Amsterdam
81a8ece2dd internal/mcp: resource tweaks
Get the name and signature of the handler right.

Change-Id: I743aaaee45cc07a741ab8ad16e90e1b1d5519b64
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672336
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-14 08:50:33 -07:00
Jonathan Amsterdam
6394485a3d internal/mcp: change CallTool signature
CallTool takes a tool name, arguments, and an options struct.

Also, pass the params to ToolHandler.

Update the design doc accordingly.

Change-Id: I21b7995b607bea976ad708ebb6ac0716018f39cb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672596
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-14 06:23:21 -07:00
Jonathan Amsterdam
4721f501df internal/mcp: change function signatures match design doc
To ensure backward compatibility, every method in the spec now takes a
*Params struct and returns a *Result struct.

CallTools is an exception: we'll change that in the next CL.

Change-Id: I58835239c67582401b8c9c2d070702d270131c08
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672595
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-14 06:23:12 -07:00
Jonathan Amsterdam
02fa1b3ea6 internal/mcp: pointerize all the things
Use pointers for the protocol's struct types, to future-proof against
them getting large.

Change-Id: I55b204080a41c8140539697ba264e502e912e613
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672575
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-14 06:22:56 -07:00
Rob Findley
dac5be2c50 internal/mcp: simplify content
Now that the protocol package is merged into the mcp package, clean up
the higher level 'Content' union types, in favor of types constructors.

Change-Id: I37bedb0ff0a296eef799ba65c37ca7f94ee1dbbe
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672415
Reviewed-by: Jonathan Amsterdam <jba@google.com>
TryBot-Bypass: Jonathan Amsterdam <jba@google.com>
2025-05-13 15:03:25 -07:00
Rob Findley
4f6510b9ae internal/mcp: merge the protocol package into mcp
In accordance with the updated design, merge the protocol package into
the mcp package. To do so, rename protocol.Content to WireContent and
protocol.ResourceContents to WireResource.

Also, remove some stray logging in readResource.

Change-Id: I76f61ce8d67dd3ac520ef2e1959ade0a0785a480
Reviewed-on: https://go-review.googlesource.com/c/tools/+/672375
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-05-13 14:43:46 -07:00
Jonathan Amsterdam
1ae23e7031 internal/mcp: implement resources
Implementation of resources.

This follows the design sketched in design/design.md, except for
Server.FileResourceHandler, which will be in a forthcoming CL.

As the spec recommends, the server verifies an incoming URI from the
client against its list of resources, to make sure the URI came from one
of them.

Some things that aren't in the design doc:
- I provide an exported function that creates a "resource not found"
  jsonrpc2.WireEror error for server authors to return.
  A wrinkle: the MCP spec defines the error code for
  "resource not found" as -32002 [1]. That code is reserved for use
  by implementations of JSON-RPC, not applications. That matters to us
  because our JSON-RPC implementation already uses that code for the
  "server closing" error. I filed a bug against MCP about it [2] and
  chose a different code for the "resource not found" error.

- As a slight convenience, I fill in some fields in the
  ReadResourceResult if the handler doesn't set them.

[1] https://modelcontextprotocol.io/specification/2025-03-26/server/resources#error-handling
[2] https://github.com/modelcontextprotocol/modelcontextprotocol/issues/509

Change-Id: I06153da982ea00748a3691ded923448a880cea2a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/671363
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-13 11:39:49 -07:00
Jonathan Amsterdam
5004d3e1bb internal/mcp: implement roots
Add support for roots.

Change-Id: Ia50abc88f0047238272d698f30ce615b1a8fd486
Reviewed-on: https://go-review.googlesource.com/c/tools/+/671360
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-13 07:52:49 -07:00
Jonathan Amsterdam
37ae90648b internal/mcp/protocol: featureSet[T]
What the spec calls "lists" of features (tools, prompts and so on)
are actually sets. Define a common data structure we can use to
implement all of them, and use it for server tools and prompts.

Change-Id: I257d0776b87a746ff23cd49ebcc7b317f4ea65e1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/671359
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-12 11:03:35 -07:00
Rob Findley
347a1a0078 internal/mcp: rename ClientConnection to ServerConnection
It's too confusing that you send server requests through the Client, and
send client requests through the ClientConnection. Rename
ClientConnection to ServerConnection, so that the types on the
server side of the control diagram start with 'Server'.

Change-Id: Idfd27af464a9296a2341cd23b2075cd9d0eb6b45
Reviewed-on: https://go-review.googlesource.com/c/tools/+/670538
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Findley <rfindley@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-05-07 10:17:38 -07:00
Jonathan Amsterdam
e0e304e51e internal/mcp: rename Make* to New*
Change-Id: I8d3d2ca125b7c24dd265937c4fe910ea823ab811
Reviewed-on: https://go-review.googlesource.com/c/tools/+/670537
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-07 09:32:55 -07:00
Jonathan Amsterdam
f8098af1c5 internal/mcp: NewClient takes Transport
Pass the client Transport to NewClient instead of Connect.
Combine the ConnectionOptions with the ClientOptions.

Change-Id: I3acd6cf6d5bf46666742f2a73e2acaef2057ea77
Reviewed-on: https://go-review.googlesource.com/c/tools/+/670215
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-06 12:19:25 -07:00
Rob Findley
8efb54691d internal/mcp/protocol: move out of mcp/internal, as it's used in the API
The generated protocol package is part of the SDK API, so it shouldn't
be internal.

Change-Id: I5306af48019b1599707b939b348eb6ae4f766ca9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/670356
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-05-06 10:18:38 -07:00
Jonathan Amsterdam
15e0c9ef02 internal/mcp: move jsonschema out of mcp/internal
The jsonschema package is part of of the API of the mcp package,
so it should not be internal.

Change-Id: I9520797837329fc2cda09c2f46ec4d48d031b9d3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/669996
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-05 13:17:44 -07:00
Jonathan Amsterdam
3d33fafc5d jsonschema: pre-compile regexps
This is the first CL that deals with the process of preparing a schema for validation.

Perform some basic checks on the scheme. Along the way, compile regexps and store
them in the schema for use during validation.

Change-Id: I0e5e6ce28656dcecb6d2d4b2fdc98998fa05b6f1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/669696
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2025-05-05 12:29:58 -07:00
Jonathan Amsterdam
7a76f3502a mcp: remove ServerConnection
Remove ServerConnection.
- It's not in typescript or mcpgo APIs.
- Amortizing a client over multiple servers doesn't have any value.

Change-Id: I725b42569733d6836864d96b16898a10ae91e570
Reviewed-on: https://go-review.googlesource.com/c/tools/+/669575
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
2025-05-02 13:30:38 -07:00
Rob Findley
bf608eeeae internal/mcp: clean up handling of content
Add support for all content types, and formalize the conversion of
content to and from the wire format (as a discriminated union).

Change-Id: I93678ce98c02176a524d2c82425fb1267ad68bf0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/669135
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
2025-05-01 19:15:30 -07:00
Rob Findley
af4d138732 internal/mcp: support prompts
Add support for 'prompts/list' and 'prompts/get', and registering
prompts on the server using AddPrompts. Add a 'MakePrompt' helper to
construct prompts from a prompt handler using reflection.

Change-Id: I479cb9c9b99313cca99640c1f5ea1939363759e6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/669015
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-05-01 18:43:44 -07:00
Rob Findley
e082a340be internal/mcp: add tool and schema options
Implement variadic options for building tools and schemas, to allow
easier customization of tool input schemas.

Also, make fields required, unless they are marked as "omitempty".

Change-Id: I7a29258fae3ec5e7ea4906ed30ed6750979de962
Reviewed-on: https://go-review.googlesource.com/c/tools/+/668955
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-01 18:41:25 -07:00
Jonathan Amsterdam
3434e57007 internal/mcp/internal/jsonschema: schema definition
The first of several CLs providing JSON Schema validation.

Replace the existing Schema type with one that has all the keywords
of the most current version of the JSON Schema spec, called 2020-12.

The 2020-12 spec can be represented type-safely in Go with one exception:
the "type" keyword can be either a string or a []string. To handle this
safely, both Type and Types fields are defined. Custom
JSONMarshal/Unmarshal methods handle the conversion. We need the
unmarshal anyway because "true" and "false" are valid schemas.

Unfortunately, the MCP spec uses the older "draft7" version of JSON
Schema, but the two are close enough for code generation to work.
I confirmed that the protocol generator produces the identical output.

Change-Id: I44f6ea2f3a3f8498979946f69dfb7f50afa1acad
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667855
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
2025-04-25 08:33:53 -07:00
Rob Findley
14b18b2645 internal/mcp: implement cancellation
Use the existing jsonrpc2 preemption mechanism to implement MCP
cancellation.

This was mostly straightforward, except where I got confused about ID
unmarshaling. Leave a note about using omitempty when it is available.

Change-Id: I13e073f08c5d5c2cc78d882da4e6ff47f09fb340
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667578
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-04-23 19:15:39 -07:00
Rob Findley
fe938f6eb7 internal/mcp: implement ping, and test request interleaving
Also validate that requests observe the lifecycle rules defined in the
spec.

Change-Id: I302282f81e053e76926e0bb295f7dbf8d526f02d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667575
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Commit-Queue: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
TryBot-Bypass: Robert Findley <rfindley@google.com>
2025-04-23 19:15:29 -07:00
Rob Findley
ef446f82d7 internal/mcp: implement HTTP SSE transport
Add support for SSE transport, as defined by version 2024-11-05 and
2025-03-26 of the MCP spec.

In order to achieve this, the jsonrpc2 stream must be abstracted so that
we can implement it across multiple HTTP requests. This required some
refactoring of the jsonrpc2_v2 package, so that we can avoid the
Dialer/Binder interfaces and construct the Connection directly from its
constituent primitives.

Change-Id: Id627b5b5a474d92f2c613cfad904d14611e7c768
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667475
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
2025-04-23 19:15:20 -07:00
Rob Findley
1204b93a85 internal/mcp: add batching support
Update the jsonrpc2 MCP framer to support both incoming and outgoing
batches. In order to achieve this, we must correlate the framed Reader
and Writer, which is not explicitly supported by the jsonrpc2 API, but
does work. A note is left to revisit the Framer interface.

Change-Id: If060cb5822da067833db20d58f5f112a0528da91
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667295
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-04-22 14:39:09 -07:00
Rob Findley
34cc770852 internal/mcp: an MCP SDK prototype
This CL contains an minimal prototype of an MCP SDK, using the
"new" jsonrpc2_v2 package (similar to x/exp/jsonrpc2).

Much is still yet to do, but this initial version addressed the
following aspects:

- Support for newline delimited JSONRPC2 framing, and message logging.
- A generated protocol package, containing type definitions to be used
  in transport.
- A minimal jsonschema package to be used for both reading the MCP spec,
  and for serving Tool input schemas.
- A transport abstraction, and two transport implementations: stdio, and
  local (for testing).
- Client and Server types, to be configured with features and then
  connected using their Connect methods, which return (respectively) a
  ServerConnection and ClientConnection object.
- A minimal binding API for tools.

A catalog of things not yet done is in doc.go, but we should review this
CL eagerly, as it contains the fundamental building blocks of an
eventual SDK.

It is the intention that this package is more-or-less standalone, as it
may eventually be carved out into a separate repository. As such, its
only dependency within x/tools is jsonrpc2_v2, and the only change
required to that package is to expose a callback when the connection is
closed.

Change-Id: Ib66018961014f85a8884d56ab721d5642c98443c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/667036
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-04-22 14:37:47 -07:00