Commit graph

385 commits

Author SHA1 Message Date
Robert Findley
8f11a86898
examples/client: add a loadtest command v0.3.1
Add a loadtest client example, to help confirm performance of our
streamable transport implementation.

For #190
2025-08-29 15:28:05 -04:00
Sam Thanawalla
ddaf35ed77
mcp/streamable: use event store to fix unbounded memory issues (#335)
This CL utilizes the event store to write outgoing messages and removes
the unbounded outgoing data structure.

It also adds a new interface [EventStore.Open]

For #190
2025-08-29 12:54:51 -04:00
Huabing (Robin) Zhao
f6118aaace
mcp: fix the type of the Complete handler
The Complete handler was returning an abstract Result type, rather than concrete CompleteResult type.

Fixes #375
2025-08-29 10:06:23 -04:00
Takeshi Yoneda
a76bae3a11 address review comments
Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
2025-08-27 16:03:32 -04:00
Takeshi Yoneda
c24d9856c4 mcp/streamable: fixes broken DELETE request on connection close
Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
2025-08-27 16:03:32 -04:00
Sam Thanawalla
392f719bd1
mcp/examples: move server example into example folder (#357)
For better organization.
2025-08-26 09:13:30 -04:00
Jonathan Amsterdam
03c5113471
examples/server/memory: fix misleading code (#369)
Tools shouldn't both set StructuredContent and return a typed output.
Remove the assignment.
2025-08-25 16:48:10 -04:00
Jim Clark
c1c22925da
relax requirements on resource URIs (#365)
remove checks for URI scheme; nothing in the spec requires one

# Background

When adding resource templates, we are currently doing checks for
schemes on absolute uris. It is still common for MCP servers to use
custom uris that would not pass these checks. For example, if the
official GitHub MCP server were to use this sdk, it would show failures
like:

```
"repo://{owner}/{repo}/contents{/path*}": parse "repo://{owner}/{repo}/contents{/path*}": invalid character "{" in host name
"repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}": parse "repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}": invalid character "{" in host name
"repo://{owner}/{repo}/sha/{sha}/contents{/path*}": parse "repo://{owner}/{repo}/sha/{sha}/contents{/path*}": invalid character "{" in host name
invalid resource template uri "repo://{owner}/{repo}/refs/pull/{prNumber}/head/contents{/path*}": parse "repo://{owner}/{repo}/refs/pull/{prNumber}/head/contents{/path*}": invalid character "{" in host name
"repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}": parse "repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}": invalid character "{" in host name
```

The wikipedia MCP would also show problems with missing schemes.

```
"/search/{query}": <nil>
"/article/{title}": <nil>
"/summary/{title}": <nil>
"/summary/{title}/query/{query}/length/{max_length}": <nil>
"/summary/{title}/section/{section_title}/length/{max_length}": <nil>
"/sections/{title}": <nil>
"/links/{title}": <nil>
"/facts/{title}/topic/{topic_within_article}/count/{count}": <nil>
"/coordinates/{title}": <nil>
```
2025-08-25 15:00:36 -04:00
Jonathan Amsterdam
73dd76b614
go.mod: update to fixed jsonschema (#370)
Update to a version that doesn't copy descriptions of subschemas.

Fixes #366.
2025-08-25 14:00:24 -04:00
Rob Findley
f37e549e25 mcp: polish package doc for v0.3.0 v0.3.0
Do a pass through the package doc for v0.3.0.
2025-08-22 16:03:15 -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
Rob Findley
9754a2aa86 examples: add an everything example, and simplify hello
Our 'hello' example should be as simple as possible. On the other hand,
we should have an 'everything' example that exercises (almost)
everything we offer.

Using the everything example, I did some final testing using the
inspector. This turned up a couple rough edges related to JSON null that
I addressed by preferring empty slices.

For #33
2025-08-22 14:00:37 -04:00
Jonathan Amsterdam
c631641296
mcp: check output schema type (#358)
Server.AddTool now checks that an output schema, if any, has type
"object".

Also add doc and a test.
2025-08-22 11:35:50 -04:00
Sam Thanawalla
d16ce9c25f
mcp/examples: moves middleware examples into example folder (#356)
For better organization.
2025-08-22 11:18:05 -04:00
Jonathan Amsterdam
b891d95361
mcp: example middleware wrapping ToolHandler (#351)
Add to the middleware example to show how to wrap a ToolHandler.

This middleware is very close to, but not the same as, wrapping a
ToolHandler directly. The only difference is that the middleware wraps
Server.callTool, which looks up the tool by name in the server's list
and then calls the handler. That lookup (plus other intervening
middleware, of course) is all that distinguishes this way of wrapping
from a more direct wrapping.
2025-08-22 11:05:03 -04:00
Jonathan Amsterdam
18c96d6912
mcp: enforce input schema type "object" (#349)
The spec makes it clear that input schemas must have type "object".
Enforce that.
Allow "any" as an input argument type by special-casing it.

Fixes #283.
2025-08-22 11:04:44 -04:00
Jonathan Amsterdam
4587941994
mcp: add Request.GetExtra (#350) 2025-08-22 11:04:01 -04:00
Sam Thanawalla
42f419fff4
mcp/examples: move elicitation example into example folder (#354)
Rename and move the example into the examples folder.
2025-08-22 11:01:48 -04:00
Jonathan Amsterdam
e3e8aaf024
mcp: align PromptHandler args with other handlers (#348)
The second arg is a GetPromptRequest.

Fixes #300.
2025-08-22 10:44:05 -04:00
Rob Findley
5fd06ae8b7 mcp: unexport InitializeClientRequest
This request is synthetic, and should not be observed by the user.
Unexport it and rename InitializeServerRequest to just
InitializeRequest.
2025-08-22 10:40:57 -04:00
Kartik Verma
c058c6a93b
feat/Issue-13: elicitation support (#188)
fixes: #13 

 Implements elicitation functionality from MCP 2025-06-18 specification.

 Changes
 - Add ElicitParams and ElicitResult protocol types 
- Add ServerSession.Elicit() method and ClientOptions.ElicitationHandler
 - Schema validation enforces top-level properties only
 - Support for accept/decline/cancel actions with progress tokens

Tests
 - Integration test in TestEndToEnd
 - Schema validation, error handling, and capability declaration tests
2025-08-22 09:29:51 -04:00
Jonathan Amsterdam
62db914082
mcp: export ToolFor (#347)
This is needed so clients can modify tool schemas, and not merely to
wrap ToolHandlers.

(A consequence of returning a copy of Tool instead of modifying it.)
2025-08-21 17:54:17 -04:00
Robert Findley
dd8e3af18d
README: soften the warning, and reference roadmap (#345)
We can warn a little less loudly against using the SDK.
2025-08-21 17:41:38 -04:00
Jonathan Amsterdam
bf79d784b9
mcp: remove references to ClientRequest[T] for concrete T (#343)
See related PR about ServerRequest[T].
2025-08-21 15:24:58 -04:00
Robert Findley
d8e18b34cf
mcp: fix bugs handling pointer types (#344)
Some bad merging in #338 led to a bad bug in the handling of structured
output for pointer output types. Add a conformance test and fix the bug.
2025-08-21 14:43:22 -04:00
Jonathan Amsterdam
6378df6f24
all: remove ServerRequest[T] for concrete T (#342)
Replace all occurrences of ServerRequest[*CallToolParams] and other
concrete instantiations with CallToolRequest and the like.

Make the XXXRequest types aliases, to preserve the convenience of
generics for the internal machinery (see shared.go, for example.)

I will expand the aliases in a followup PR.
2025-08-21 12:10:53 -04:00
Robert Findley
43ad1eb8b4
mcp: treat pointers equivalently to non-pointers when deriving schema
As reported in #199 and #200, the fact that we return a possibly "null"
schema for pointer types breaks various clients, which expect schemas to
be of type "object". This is an unfortunate footgun. For now, assume
that the user wants us to treat pointers equivalently to non-pointers.
If we want to change this behavior in the future, we can do so behind an
option.

+ a test

Also fix the handling of nil results in the case where the output schema
is non-nil: we must provide structured content in this case. (This was
causing the test to fail).

Fixes #199
Fixes #200
2025-08-21 10:57:28 -04:00
Jonathan Amsterdam
5b1f328fd4
mcp: uncomment test, change struct{} to any (#341)
Address comments on #325.
2025-08-21 10:18:52 -04:00
Jonathan Amsterdam
32cf71de3a
mcp: change tool handler design (#325)
Change the design of tool handlers by removing genericity from the common path.
- `ToolHandler` gets the args as a `json.RawMessage`. Using Server.AddTool does no unmarshaling or schema validation.
- `ToolHandlerFor`, installed with `AddTool`, are the only generic pieces.

TODO:
uncomment and fix some tests
2025-08-20 19:36:34 -04:00
Rob Findley
79f063bc98 mcp: handle the Mcp-Protocol-Version header correctly
Handle the protocol version header according to section 2.7 of the spec
(and the other SDKs).

Fixes #198
2025-08-20 16:48:08 -04:00
Hongxiang Jiang
3f10c19b45
mcp: replace type struct{} with any for user-defined fields (#334)
This change adopts a clearer convention:
- any is used for fields where the structure is defined by the client or
server implementer.
- an named empty struct type is the place holder type to be defined by
the MCP spec in the future. 

The "Experimental" fields in "ClientCapabilities" and
"ServerCapabilities" now use "map[string]any". This allows clients and
servers to negotiate custom capabilities that contain complex data,
rather than being restricted to a simple on/off flag.

The "Metadata" field in "CreateMessageParams" was also changed to "any"
to properly serve its purpose as a flexible container for server-defined
data.
2025-08-20 15:01:44 -04:00
Jonathan Amsterdam
73b8a7f134
mcp: make request headers available to tools (#333)
Add HTTP request headers to RequestExtra, so tools and other
user-defined handlers can access them.

Fixes #331.
2025-08-20 14:58:44 -04:00
Sam Thanawalla
48abccbf6b
mcp/server: expose InitializeParams to ServerSession (#336)
This CL enables the server session to see what capabilities the client
has by introducing the InitializeParams() function.

This CL also adds a test to ensure ClientCapabilities is accurate.

Fixes #141
2025-08-20 14:39:17 -04:00
Robert Findley
62d815922f
mcp: minor cleanup of some TODOs (#332)
Do a pass through TODOs to delete or address minor TODOs.
2025-08-20 12:55:19 -04:00
Shusaku Yasoda
719f68d0bf
improve error handling for tools and server methods (#311)
- Fix tool error handling to properly distinguish between protocol
errors and tool execution errors
- Return structured JSON-RPC errors directly for protocol-level issues
- Embed regular tool errors in CallToolResult as per MCP specification
- Improve server error responses for unknown prompts and tools
- Add comprehensive tests for both structured and regular error handling
2025-08-20 12:31:11 -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
1a54234c27 mcp: fix reconnect semantics for hanging GET
A few problems with reconnection cropped up in the review of PR #307.
We should allow for the hanging GET to fail with StatusMethodNotAllowed.
This simply means that the server does not support sending notifications
or requests over the GET, which is allowed in the spec.

Also, we should fix the initial delay of the hanging GET request: it
should start with 0 delay. Fix the math for this and subsequent
attempts. Incidentally, this makes the tests take 3s on my machine, down
from 9s.

Also address some comments from #307.
2025-08-20 09:59:52 -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
0eb03e0d76 mcp: factor out reusable functions from TestStreamableServerTransport
The next CL will test stateless and distributable server transport
configurations, using the HTTP testing strategy of
TestStreamableServerTransport.
2025-08-20 09:59:52 -04:00
cryo
732b97fdaf
mcp: add syntax and scheme validation to AddResourceTemplate (#253)
Add template validation and scheme check in `AddResourceTemplate`.
2025-08-19 17:43:26 -04:00
Robert Findley
46ba813860
.github: add a staticcheck action (#326)
Add a staticcheck step to the lint job of the Test workflow, and fix
reported diagnostics.
2025-08-19 14:58:01 -04:00
Rob Findley
3db848abec mcp: implement a concurrency model for calls
Implement the concurrency model described in #26: notifications are
synchronous, but calls are asynchronous (except for 'initialize'). To
achieve this, implement jsonrpc2.Async(ctx) to signal asynchronous
handling. This is simpler to use than returning ErrAsyncResponse and
calling Respond, and since this is an internal detail we don't need to
worry too much about whether it's idiomatic.

Add tests that verify both features, for both client and server.

Also:
- replace req.ID.IsValid with req.IsCall
- remove the methodHandler type as we can just use MethodHandler

Fixes #26
2025-08-19 11:21:02 -04:00
cryo
112ca4e3b1 mcp: Ensure keepalive goroutine is started exactly once 2025-08-19 09:46:54 -04:00
Jonathan Amsterdam
52734fd414
mcp: rename logging symbols (#321)
Fixes #279.
2025-08-19 07:49:56 -04:00
Matt L
75f9999590
feat(mcp): export initialized result in client session (#320)
Export ServerCapabilities and related subtypes and update protocol,
server, and tests to use exported types. Add InitializeResult method on
ClientSession for accessing the initialize result from sessions.

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

Co-authored-by: Shashank Pachava <spachava753@gmail.com>
2025-08-18 14:49:02 -04:00
Sam Thanawalla
767dacb820
mcp/streamable: remove StreamableReconnectOptions (#319)
Moves MaxRetries into the parent struct and assume a negative integer
signifies to not retry.

Fixes #308
2025-08-18 13:53:34 -04:00
Manuel Martinez
eb5eb06341
ci: add go vet to lint step (#316)
- Adds `go vet` call to the lint step in GA workflow.
- Enforces a compatible `go-version` with the minimum in the matrix for
the `test` step.
- Fixes some minor `.yml` formatting

Fixes #281

---

There's some discussion in #281 about adding
[staticcheck](https://staticcheck.dev/docs/) as well but when I ran it,
it found some hits (see below) that are presumably not severe, and I
wouldn't want to block deployments before getting some feedback from
maintainers.

```
internal/jsonrpc2/conn.go:695:4: this value of writeErr is never used (SA4006)
internal/jsonrpc2/conn.go:697:5: this value of err is never used (SA4006)
internal/jsonrpc2/conn.go:700:4: this value of err is never used (SA4006)
mcp/mcp_test.go:501:2: var resource3 is unused (U1000)
mcp/mcp_test.go:509:5: var embeddedResources is unused (U1000)
mcp/mcp_test.go:513:6: func handleEmbeddedResource is unused (U1000)
mcp/streamable.go:124:3: unnecessary assignment to the blank identifier (S1005)
mcp/streamable.go:291:2: field lastStreamID is unused (U1000)
mcp/streamable.go:293:2: field opts is unused (U1000)
mcp/streamable_test.go:542:29: argument ctx is overwritten before first use (SA4009)
```
2025-08-18 12:11:25 -04:00
Rob Findley
1afdb1f273 mcp: several fixes for streamable client reconnection
Do a pass through the streamable client reconnection logic, and fix
several bugs.

- Establish the initial GET even if MaxRetries is 0 (#256). This was
  broken because the GET bypasses the initial request and going straight
  to the SSE GET reconnection logic.
- Release the stream ownership when POST requests exit.
- Don't reconnect POST requests if we've received the expected response.
- Move unexported reconnection config to constants. Otherwise it is too
  hard to set ReconnectOptions (you have to use the DefaultOptions and
  mutate).

Fixes #256
2025-08-15 16:48:09 -04:00
Hugh Palmer
0a8fe40c51 mcp: changed streamID's from int64 to random strings (#266)
- Changed StreamID's to store randomly generated strings.
- Updated all tests.
- Resolved conflicts
2025-08-15 09:38:18 -04:00
Rob Findley
54c99810f6 mcp: add a test for streamable accept headers
Add a test following up on the fix for #290.
2025-08-14 15:42:57 -04:00