Closes#1271.
`Server.handle` refuses a call made before `initialize` on a legacy
session, and that refusal lives in the `default` branch of its method
switch, so any method with a `case` of its own skips it. Three are
served on a session that never handshook: `logging/setLevel`,
`resources/subscribe` and `resources/unsubscribe`.
`resources/subscribe` is the one that reaches state rather than merely
answering: it starts a watcher and registers a subscriber, so a client
that subscribes before `initialize` is delivered
`notifications/resources/updated` for the lifetime of a session the
server never agreed to.
The check now runs after the switch, for every call carrying no
new-protocol `_meta`, with `initialize` and `ping` exempt. That is the
same condition the `default` branch already applied and the same error
it produced; the change is that the three methods above stop skipping
it.
`ping` stays allowed because the lifecycle page exempts it.
`server/discover` is deliberately left alone: a legacy request there
already receives method-not-found, which tells the client more than an
initialization error would. The two notification cases stay outside,
since the gate covers calls and a notification carries no refusal.
The SEP-2575 path is untouched. A session without an `initialize`
handshake is exactly what that proposal is for, so the exemption is the
request's own `_meta.protocolVersion` rather than anything about the
session, and a new-protocol client is served as it is today.
Tests: `TestServerHandle_LegacyCallBeforeInitialize` drives each
affected method on a fresh session and asserts the refusal, that
`resources/subscribe` runs no handler and leaves no subscriber, and that
`initialize` and `ping` are served.
`TestServerHandle_NewProtocolCallWithoutInitialize` asserts a
new-protocol session with no handshake still gets `tools/list`, and that
the three methods keep their existing method-not-found rather than the
initialization refusal. Without the change, the setLevel, subscribe and
unsubscribe rows fail.
`go test ./mcp/ -count=1` is green, `gofmt -l` is empty and `go vet
./mcp/` is clean. This does not depend on #1266 and rebases onto it
cleanly, verified with the suite green on the combination.
One decision worth your view: the refusal is a plain error, which on the
wire carries code 0, exactly as the `default` branch produced before
this change. The specification prescribes no code for it. If you would
rather it were `-32600` or `-32603`, say so and I will change it here.
---------
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
CallToolResult currently decodes structuredContent through an any field,
which converts JSON numbers to float64 and silently rounds integers
outside the IEEE-754 safe range.
This change decodes structuredContent separately with UseNumber,
preserving wire numbers as json.Number while retaining the existing
decoded object, array, and primitive shapes. The field documentation now
describes that representation. Users who depend on the previous float64
behavior can temporarily restore it with
MCPGODEBUG=structuredcontentfloat64=1.
An end-to-end in-memory client/server regression covers 9007199254740993
from a low-level Server.AddTool result. Additional coverage verifies the
compatibility option restores the legacy rounded float64 value.
Verification:
- gofmt -l .
- go test ./...
- go vet ./...
Fixes#1236
---------
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
`validateRequestMeta` decided whether a request follows the 2026-07-28
protocol by comparing `_meta.protocolVersion` against `2026-07-28` with
`<`. This replaces that comparison with a membership test against
`supportedProtocolVersions`, which is the rule `mcp/streamable.go:354`
already applies to the `Mcp-Protocol-Version` header.
Nothing changes for a version the SDK supports: one predating 2026-07-28
still selects the legacy handshake, and one at 2026-07-28 or later is
still validated as the full `_meta` triple. What changes is a version
the SDK does not support at all. It now reports `usesNewProtocol`, so it
reaches the `CodeUnsupportedProtocolVersion` check in
`(*ServerSession).handle` and is answered with the error the versioning
page requires.
Driving a stdio server with `_meta.protocolVersion` set to `1900-01-01`,
the worked example on that page, before:
```json
{"jsonrpc":"2.0","id":1,"error":{"code":0,"message":"method \"tools/list\" is invalid during session initialization"}}
```
and after:
```json
{"jsonrpc":"2.0","id":1,"error":{"code":-32022,"message":"unsupported protocol version","data":{"supported":["2026-07-28","2025-11-25","2025-06-18","2025-03-26","2024-11-05"],"requested":"1900-01-01"}}}
```
There are two judgement calls in the diff, and I would rather state them
than leave them to be found.
The first is that an unsupported version returns early with
`initializeParams` carrying only that version, and the rest of the
`_meta` triple is left unvalidated. The shape of `_meta` is defined by
the revision the request names, and that is a revision this SDK does not
know, so there is nothing to validate it against; answering "missing
`io.modelcontextprotocol/clientCapabilities`" would send the client off
to fix the wrong thing while its real problem is the version. The partly
populated value cannot escape: `handle` runs the unsupported check
before anything reads the capabilities, and `Server.protocolVersions` is
always a subset of `supportedProtocolVersions`, so a version that fails
the membership test in `validateRequestMeta` always fails it in `handle`
too.
The second is that an empty `protocolVersion` still means "no version
declared" rather than "unsupported version". `mcp/streamable.go` already
reads the field that way when it decides whether a request carries a
version at all (`metaVersion != ""`), and rejecting the empty string
here would put the two layers back into disagreement, in the other
direction. The `!ok` and `== ""` cases are therefore handled together.
**Behaviour an existing user would see change.** A request whose
`_meta.protocolVersion` is a string not in `SupportedProtocolVersions()`
is now refused with `-32022`, where before a value sorting below
`2026-07-28` was treated as a legacy request: served if the session was
already initialized, refused with the session-initialization error if it
was not. No published protocol revision is in that set, so a client
speaking any released version of MCP sees no difference. A deployment
that put a sentinel or placeholder version in `_meta` and relied on it
being ignored would see those requests refused, which I believe is the
right answer but is a visible change.
This is not limited to stdio, and I want to be precise about where the
header gate does and does not stand in front of it. The gate at
`mcp/streamable.go:354` refuses an unsupported `Mcp-Protocol-Version`
only when that version **also** sorts below `2026-07-28`, so an unknown
version sorting above it (`2027-01-01`, say) passes the gate and reaches
this classification; the deprecated HTTP+SSE transport in `mcp/sse.go`
has no header check at all; and stdio, the in-memory transport and any
custom transport have none either. The window the old comparison left
open is therefore reachable on every transport, and a value sorting
above `2026-07-28` with an incomplete `_meta` triple also changes
answer, from `-32602` to `-32022`.
**The test that pins it** is
`TestServerUnknownProtocolVersion_NewProtocol` in `mcp/server_test.go`.
It drives `(*ServerSession).handle` with a `tools/list` request whose
`_meta.protocolVersion` names a version the SDK does not know, and
asserts that the returned error is a `*jsonrpc.Error` with code
`CodeUnsupportedProtocolVersion`, `Data.Requested` equal to the version
asked for, and `Data.Supported` equal to `SupportedProtocolVersions()`.
Four cases: `1900-01-01`, `2025-11-24`, `1.0`, and `1900-01-01` again
with the rest of the triple omitted, which is the case that pins the
first judgement call above. Before the change all four fail with `handle
returned method "tools/list" is invalid during session initialization,
want a *jsonrpc.Error`. `TestValidateRequestMeta` in
`mcp/shared_test.go` gains four cases at the unit level: two that fail
before the change, and two that guard the classifications which must not
move, a supported pre-2026-07-28 version and an empty one.
`go test ./...` and `gofmt -l .` are both clean.
Fixes#1260.
Fixes#1225. See the [issue
comment](https://github.com/modelcontextprotocol/go-sdk/issues/1225#issuecomment-5500818546).
## The bug
`2026-07-28` requires `resultType` on every result. `CallToolResult`,
`GetPromptResult`, and `ReadResourceResult` keep `resultType` in an
unexported field. Each of these results can be `complete` or
`input_required`, so it cannot embed `completeResultWithType` the way
the seven types in #1060 do.
Only `handleMultiRoundTripResult` sets that field. It runs inside
`Server.callTool`, `getPrompt`, and `readResource`. Some middleware
returns a result directly instead of calling `next` — for example, an
auth, rate-limit, or entitlement gate. That middleware never reaches
`handleMultiRoundTripResult`. The response then ships without the field,
and strict clients reject it.
## The fix
The choice between `complete` and `input_required` now happens inside
`setMultiRoundTripResultType`. Both call sites that need this choice
call that function. `setCompleteResultType` now labels results it did
not label before, so it is renamed `annotateResultType`. The new
function picks a branch with a type switch.
Reading matters here. `setResultType` is unexported, so setting
`InputRequests` is the only way for middleware to ask for input. If the
code always labels results `complete`, the response ships as
`{"resultType":"complete","inputRequests":{...}}`. A client that trusts
the discriminator then drops the elicitation request.
Recomputing the label every time is safe. A result that did reach the
dispatcher already carries the same label from that test. This also
covers one edge case: a handler that returns `InputRequests` again after
the shim's single reinvocation. Before this fix, that case shipped with
no `resultType`.
Embedding `completeResultWithType` instead is not an option: it labels
everything `complete` and overwrites `input_required`, breaking
multi-round-trip flows.
**Skip nil results.** Middleware can return a typed nil value, which
satisfies the `Result` interface. `handle` now checks for that case
before it calls the labeling function. Before this change, that same
case also panicked, one frame later in `annotateServerInfo` and then
`GetMeta`.
## Checks
- `TestServerSessionHandle_SetsResultTypeWhenMiddlewareShortCircuits`
fails on all three methods before the first change. Its fourth case
fails if labeling overwrites a populated `InputRequests`.
- `TestServerSessionHandle_ResultTypeGate` records which requests carry
`resultType`, including one known gap. See "Separate from this PR"
below.
- `TestServerSessionHandle_NilResultFromMiddleware` panics without the
nil check.
- The complete-path cases added to
`TestServerSessionHandle_SetsResultTypeOnNewProtocol` also pass before
this change. They close a gap in #1060, which added only three cases,
and only for `input_required`.
- The exported API is unchanged. `go doc -all ./mcp`, with comments
stripped, is identical before and after this change.
- With the reproduction from the issue, over the streamable transport:
the dispatched `tools/call` and the short-circuited `tools/call` both
now return `resultType: "complete"`.
I could not run `./scripts/conformance.sh`. CONTRIBUTING.md names that
script, but the repo only has `client-conformance.sh` and
`server-conformance.sh`. Both scripts need GNU `timeout`, and macOS does
not include it.
## Also here
Ten comments named an exported `ResultType` field and
`ResultTypeComplete` / `ResultTypeInputRequired` constants. None of
those exist. Two of the comments were `[CallToolResult.ResultType]` doc
links, and those links cannot resolve. They now point at
`[CallToolResult.NeedsInput]` instead, because an unexported field is
also not a valid link target.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
## Summary
`ClientSessionOptions.protocolVersion` was unexported and documented as
test-only, so external consumers of `Client.Connect` could not request a
specific protocol revision.
This exports the field as `ProtocolVersion` with the same semantics:
- empty → request the latest supported version (unchanged default)
- non-empty → send that version in initialize/discover
- existing negotiate-down path is unchanged
## Usage
```go
cs, err := client.Connect(ctx, transport, &mcp.ClientSessionOptions{
ProtocolVersion: "2025-11-25",
})
```
## Testing
- `go test ./mcp/ -count=1` (pass)
Fixes#1113
Co-authored-by: arimu1 <19286898+arimu1@users.noreply.github.com>
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
## Summary
- Add `resultType: "complete"` to non-MRTR server result responses for
2026-07-28 requests.
- Cover `server/discover`, list methods, and completion responses, and
update expected conformance/example output.
## Why
Fixes#1032. The 2026-07-28 schema requires `resultType` on result
objects, but only the MRTR-capable results were setting it.
## Validation
- `go test ./mcp -run
'Test(ServerSessionHandle|CompleteResult|ClientConnectDiscover|InMemory_E2E_Discover)'
-count=1` — passed\n- `go test ./mcp -count=1` — passed\n- `go test
./...` — passed\n- `go vet ./...` — passed
---------
Co-authored-by: ychampion <ychampion@users.noreply.github.com>
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
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.
## 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.
## 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
## 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>
## 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>
This PR fixes a race condition where `ServerSession.keepaliveCancel` was
accessed in `Close` concurrently with its initialization in
`ServerSession.startKeepalive`. It also removes a now redundant test.
The fix moves keepalive initialization into `ServerSession.Connect`
similar to the implementation in `ClientSession.Connect`. This is safe
because the MCP spec allows keepalive pings before the initialization
messages. From the MCP
[spec](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle)
(emphasis added):
> The client SHOULD NOT send requests **other than pings** before the
server has responded to the initialize request.
The PR includes a reproducer test case, which fails with the following
error without this fix:
```
> go test -count=10 -race -run ^TestStreamableStatelessKeepaliveRace$ github.com/modelcontextprotocol/go-sdk/mcp 8s
==================
WARNING: DATA RACE
Write at 0x00c00007fbf0 by goroutine 1095:
github.com/modelcontextprotocol/go-sdk/mcp.startKeepalive()
/Users/benjamin/Documents/go-sdk/mcp/shared.go:590 +0x58
github.com/modelcontextprotocol/go-sdk/mcp.(*ServerSession).startKeepalive()
/Users/benjamin/Documents/go-sdk/mcp/server.go:1526 +0x130
github.com/modelcontextprotocol/go-sdk/mcp.(*ServerSession).initialized()
/Users/benjamin/Documents/go-sdk/mcp/server.go:1059 +0xf8
github.com/modelcontextprotocol/go-sdk/mcp.init.serverSessionMethod[go.shape.*uint8,go.shape.interface { GetMeta() map[string]interface {}; SetMeta(map[string]interface {}); github.com/modelcontextprotocol/go-sdk/mcp.isResult() }].func27()
/Users/benjamin/Documents/go-sdk/mcp/shared.go:332 +0x98
github.com/modelcontextprotocol/go-sdk/mcp.newServerMethodInfo[go.shape.*github.com/modelcontextprotocol/go-sdk/mcp.InitializedParams,go.shape.interface { GetMeta() map[string]interface {}; SetMeta(map[string]interface {}); github.com/modelcontextprotocol/go-sdk/mcp.isResult() },go.shape.struct { github.com/modelcontextprotocol/go-sdk/mcp.Meta "json:\"_meta,omitempty\"" }].func2()
/Users/benjamin/Documents/go-sdk/mcp/shared.go:272 +0x84
github.com/modelcontextprotocol/go-sdk/mcp.defaultReceivingMethodHandler[go.shape.*uint8]()
/Users/benjamin/Documents/go-sdk/mcp/shared.go:152 +0xe8
github.com/modelcontextprotocol/go-sdk/mcp.defaultReceivingMethodHandler[*github.com/modelcontextprotocol/go-sdk/mcp.ServerSession]()
/Users/benjamin/Documents/go-sdk/mcp/shared.go:146 +0x60
github.com/modelcontextprotocol/go-sdk/mcp.handleReceive[go.shape.*uint8]()
/Users/benjamin/Documents/go-sdk/mcp/shared.go:169 +0x228
github.com/modelcontextprotocol/go-sdk/mcp.(*ServerSession).handle()
/Users/benjamin/Documents/go-sdk/mcp/server.go:1445 +0x370
github.com/modelcontextprotocol/go-sdk/mcp.connect[go.shape.*uint8,go.shape.*uint8].func1.1()
/Users/benjamin/Documents/go-sdk/mcp/transport.go:170 +0x5c
github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2.HandlerFunc.Handle()
/Users/benjamin/Documents/go-sdk/internal/jsonrpc2/jsonrpc2.go:84 +0x48
github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2.(*Connection).handleAsync.func3()
/Users/benjamin/Documents/go-sdk/internal/jsonrpc2/conn.go:717 +0xd8
Previous read at 0x00c00007fbf0 by goroutine 1089:
github.com/modelcontextprotocol/go-sdk/mcp.(*ServerSession).Close()
/Users/benjamin/Documents/go-sdk/mcp/server.go:1502 +0x34
github.com/modelcontextprotocol/go-sdk/mcp.(*StreamableHTTPHandler).ServeHTTP.deferwrap1()
/Users/benjamin/Documents/go-sdk/mcp/streamable.go:519 +0x34
runtime.deferreturn()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/runtime/panic.go:589 +0x5c
github.com/modelcontextprotocol/go-sdk/mcp.TestStreamableStatelessKeepaliveRace.mustNotPanic.func2()
/Users/benjamin/Documents/go-sdk/mcp/streamable_test.go:2156 +0x94
net/http.HandlerFunc.ServeHTTP()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/net/http/server.go:2322 +0x48
net/http.serverHandler.ServeHTTP()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/net/http/server.go:3340 +0x270
net/http.(*conn).serve()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/net/http/server.go:2109 +0x9b0
net/http.(*Server).Serve.gowrap3()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/net/http/server.go:3493 +0x48
Goroutine 1095 (running) created at:
github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2.(*Connection).handleAsync()
/Users/benjamin/Documents/go-sdk/internal/jsonrpc2/conn.go:715 +0x334
github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2.(*Connection).acceptRequest.func2.gowrap1()
/Users/benjamin/Documents/go-sdk/internal/jsonrpc2/conn.go:676 +0x34
Goroutine 1089 (running) created at:
net/http.(*Server).Serve()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/net/http/server.go:3493 +0x5dc
net/http/httptest.(*Server).goServe.func1()
/opt/homebrew/Cellar/go/1.25.4/libexec/src/net/http/httptest/server.go:311 +0x98
==================
--- FAIL: TestStreamableStatelessKeepaliveRace (0.14s)
testing.go:1617: race detected during execution of test
FAIL
FAIL github.com/modelcontextprotocol/go-sdk/mcp 1.727s
FAIL
```
---------
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
This implements [SEP-1303](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1303).
Input validation errors (missing required fields, wrong types) during
tool calls
are now returned as `CallToolResult` with `IsError=true` instead of
JSON-RPC
`-32602` errors. This allows LLMs to see the validation error and
self-correct,
per the MCP spec.
The existing pattern for handler-returned errors (lines 347-350 in
server.go)
already converts regular errors to tool results with `IsError=true`.
This change
applies the same pattern to argument schema validation and unmarshal
errors,
resolving the `TODO(#450)` in the code.
Thanks to @iwyrkore for filing the issue and proposing the approach, and
@findleyr for the cross-SDK analysis in the discussion.
Fixes#450
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
Update the SDK to use protocol version 2025-11-25 as the latest
supported version. Fix tests and examples that had hardcoded
expectations for the previous version (2025-06-18).
---------
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
This implements
[SEP-2133](https://modelcontextprotocol.io/community/seps/2133-extensions)
and resolves#777.
## Summary of changes
- Add `Extensions` field to `ServerCapabilities` and
`ClientCapabilities` structs per
[SEP-2133](https://modelcontextprotocol.io/community/seps/2133-extensions)
- Add `AddExtension` helper methods on both types that normalize nil
settings to empty maps.
- [The spec requires an object, not
null](https://modelcontextprotocol.io/community/seps/2133-extensions#negotiation:~:text=an%20empty%20object%20indicates%20no%20settings.)
- Include shallow-copy support via `maps.Clone` in the existing
`clone()` methods
- also include shallow-copy support for `Experimental` field.
- Add comprehensive tests: unit tests for nil-safety, capability
preservation, and over-the-wire round-trip
## What's tested
- [x] `go test ./mcp/...` passes
- [x] Server conformance tests pass (30 scenarios, 40 assertions, 0
failures)
- [x] Client conformance tests match existing baseline (auth scenarios
are known failures)
Implement SEP 986, enforcing restrictions on tool names. Since we're
already at v1 we couldn't turn this into a panic, so use an error log
instead. This is consistent with the typescript SDK. A note is left in
our 'rough edges' doc to panic in v2.
Fixes#621
As discussed at length in
https://github.com/modelcontextprotocol/go-sdk/issues/518, coupling our
schema fields to
jsonschema.Schema limits our user's ability to use a different schema
package, and makes it impossible for our client to talk to some
backends.
This CL relaxes the type of `*jsonschema.Schema` fields to be of type
`any`, which is a breaking change but is unlikely to affect most server
code. (It *is* a nontrivial change for clients).
Fixes https://github.com/modelcontextprotocol/go-sdk/issues/518
Recently, I refactored to fix validation and default application for
structured output. However, I used the wrong byte slice for the
unstructured output. Fix it to use the same bytes as structured output,
with a test.
Fixes#475
Our validation logic was avoiding double-unmarshalling as much as
possible, by parsing before validation and validating the Go type.
This only works if the Go type has the same structure as its JSON
representation, which may not be the case in the presence of types with
custom MarshalJSON or UnmarshalJSON methods (such as time.Time).
But even if the Go type doesn't use any custom marshalling, validation
is broken, because we can't differentiate zero values from missing
values.
Bite the bullet and use double-unmarshalling for both input and output
schemas. Coincidentally, this fixes three bugs:
- We were accepting case-insensitive JSON keys, since we parsed first,
even though they should have been rejected. A number of tests were
wrong.
- Defaults were overriding present-yet-zero values, as noted in an
incorrect test case.
- When "arguments" was missing, validation wasn't performed, no defaults
were applied, and unmarshalling failed even if all properties were
optional.
First unmarshalling to map[string]any allows us to fix all these bugs.
Unfortunately, it means a 3x increase in the number of reflection
operations (we need to unmarshal, apply defaults and validate,
re-marshal with the defaults, and then unmarshal into the Go type).
However, this is not likely to be a significant overhead, and we can
always optimize in the future.
Update github.com/google/jsonschema-go to pick up necessary improvements
supporting this change.
Additionally, fix the error codes for invalid tool parameters, to be
consistent with other SDKs (Invalid Params: -32602).
Fixes#447Fixes#449
Updates #450
toolForErr was ignoring the output schema if the output type was any.
That neglected the case where the user provided their own output schema.
Fixes#371.
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>
```
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.
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.
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>
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.
## 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.
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.
If tools are added to a Server when Run is called, it will send
toolCapabilities to the client. Otherwise it won't. Ditto for prompts
and resources.
For #56.
This CL simplifies the paginateList function in server.go to use a
generic helper for tools, resources, and prompts.
Change-Id: Ide0d2a90d715374280067e094d8870882e6bddfa
Reviewed-on: https://go-review.googlesource.com/c/tools/+/678055
Auto-Submit: 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>
This CL streamlines the client side pagination logic by adding a generic
helper that handles pagination. This also simplifies the existing tools
iterator method.
Change-Id: If4304deda1ccd22d0c4580d76deaefc64e5dfeb9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/676975
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Sam Thanawalla <samthanawalla@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>