Commit graph

582 commits

Author SHA1 Message Date
Maciek Kisiel
40475813dd mcp: fix multi-select enum elicitation 2026-02-17 14:42:18 +01:00
Maciek Kisiel
8b526cdb2e chore: setup dependabot to update github actions. 2026-02-17 14:39:59 +01:00
Maciej Kisiel
d3aeecfdbc chore: add an issue template for enhancements 2026-02-17 13:02:38 +01:00
Robert Findley
3cf9f9908d
mcp: implement sampling with tools (#699)
Summary

Add support for tool use within sampling requests, as described in the
MCP 2025-11-25 spec's sampling.tools capability.
  New types

  Content types:
- ToolUseContent — represents a tool invocation request from the
assistant
- ToolResultContent — represents the result of a tool invocation, with
nested []Content for the result blocks

  Capability types:
  - SamplingCapabilities gains Tools and Context sub-fields
  - SamplingToolsCapabilities, SamplingContextCapabilities

  Tool-enabled sampling (parallel tool calls):
- CreateMessageWithToolsParams with SamplingMessageV2 (supports array
content)
  - CreateMessageWithToolsResult (supports array content)
  - ToolChoice — controls tool invocation mode (auto, required, none)
  - ServerSession.CreateMessageWithTools
  - ClientOptions.CreateMessageWithToolsHandler

  Design

Following the TypeScript SDK's pattern, tool-enabled sampling uses
separate types from basic sampling to avoid breaking the existing API.
The basic CreateMessage/CreateMessageResult path
   is unchanged.

Both paths share the same wire method (sampling/createMessage) and go
through the method info table. The table uses the broader
CreateMessageWithToolsResult type internally;
CreateMessage downconverts (erroring if multiple content blocks are
returned). This is documented as a rough edge to unify in v2.

Setting CreateMessageWithToolsHandler automatically infers the
sampling.tools capability. It is a panic to set both
CreateMessageHandler and CreateMessageWithToolsHandler.

  References

-
https://modelcontextprotocol.io/specification/2025-11-25/client/sampling
- TypeScript SDK: CreateMessageResultWithToolsSchema /
CreateMessageRequestParamsWithTools

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 12:07:08 +01:00
Maciej Kisiel
14869a46ee
chore: update issue templates (#797)
Testing issue templates.
2026-02-17 11:49:32 +01:00
Maciek Kisiel
be7e6f49ab chore: bump node.js version for conformance test runs. 2026-02-17 09:57:34 +01:00
Nithin P
23ab1bfd26 jsonrpc2: fix Content-Length header parsing to be case-insensitive 2026-02-16 12:20:41 +01:00
Paul Carleton
67bd3f2e2b
mcp: add automatic DNS rebinding protection for localhost servers (#760)
## Summary

Add DNS rebinding protection that is automatically enabled when requests
arrive via localhost (127.0.0.1, [::1]). This protects against malicious
websites using DNS rebinding to interact with local MCP servers.

(note: this was a claude assisted PR, mostly wanted to see how difficult
passing this test would be for this SDK)

## Design Goal: Secure by Default

The primary goal is to make it difficult to run a localhost server
without these protections by mistake. There are other approaches that
could provide secure defaults (e.g., a helper for `ListenAndServe` that
does the localhost check), but using `http.LocalAddrContextKey` for
runtime detection seemed like the most backwards compatible approach and
least likely to be disabled by accident.

With this implementation:
- **No code changes required** - existing servers get protection
automatically
- **No opt-in needed** - protection activates based on the connection's
local address
- **Explicit opt-out** - users must deliberately set
`DisableLocalhostProtection: true`

## Changes

- Add `DisableLocalhostProtection` option to `StreamableHTTPOptions`
- Add `isLocalhostAddr` and `isLocalhostHost` helper functions  
- Validate Host header at start of `ServeHTTP`, rejecting non-localhost
Host headers with 403 Forbidden

## How it works

The protection uses `http.LocalAddrContextKey` to detect the
connection's local address at runtime. When a request arrives via
localhost (127.0.0.1 or [::1]), the handler validates that the Host
header also matches a localhost value. If not, the request is rejected
with 403 Forbidden.

This approach means:
- Protection is enabled for requests arriving via localhost, regardless
of whether the server listens on `127.0.0.1` or `0.0.0.0`
- Requests arriving via non-localhost IPs (e.g., external network
requests) are **not** affected

### Edge case: Reverse proxies

If a reverse proxy (e.g., Envoy, nginx) runs on the same host and
forwards requests to the MCP server via localhost while preserving the
original Host header, those requests would be rejected. In this case,
users should either:
1. Set `DisableLocalhostProtection: true`
2. Configure the proxy to rewrite the Host header to localhost

## Testing

- Added unit tests for `isLocalhostAddr` and `isLocalhostHost` helper
functions
- Added integration tests for the full protection flow
- Verified against the MCP conformance test suite:
  - `localhost-host-rebinding-rejected`: PASS
  - `localhost-host-valid-accepted`: PASS

## Related

- Spec:
https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise
- TypeScript SDK implementation: `localhostHostValidation()` middleware
- Conformance test: `dns-rebinding-protection` scenario

---------

Co-authored-by: Maciek Kisiel <mkisiel@google.com>
2026-02-13 16:23:32 +01:00
Maciej Kisiel
c952ab0a1c
chore: add ROADMAP.md (#788)
Roadmap is required for Tier 1 SDK classification.
2026-02-13 10:39:53 +01:00
Theodor Nesfeldt Engøy
6a81e5da2a
examples: bind auth-middleware server to localhost by default (#784)
The auth-middleware example currently defaults to `-http :8080`, which
binds to all interfaces (LAN-exposed) by default.

This changes the default to `localhost:8080` and adds a short comment
noting `-http 0.0.0.0:8080` if you intentionally want to expose it.

Rationale: this example serves token-generation endpoints and
authenticated MCP routes; binding loopback by default avoids accidental
exposure while keeping the opt-in remote behavior.

---------

Co-authored-by: Theodor N. Engøy <theodornengoy@Mac.home>
Co-authored-by: Theodor N. Engøy <theodornengoy@eduroam-193-157-246-146.wlan.uio.no>
2026-02-11 10:02:17 +01:00
original-owl
86f8415122
mcp: use synctest for timing-dependent tests (#756)
Converted timing-based tests to use Go 1.25's synctest for instant,
deterministic execution with simulated time.

Moved converted tests to *_go125_test.go files. Tests using real I/O
(exec.Command, httptest) remain timing-based as they require actual
system operations.

Fixes #680

---------

Co-authored-by: i586147 <lakshana.goswami@sap.com>
2026-02-10 10:40:11 +01:00
Suraj Bobade
41b54dced5
mcp: use omitzero json tag for byte array field in ResourceContents, instead of omitempty
/mcp/content.go: use omitzero json tag for byte array, instead of omitempty earlier.

This change removes requirement of custom MarshalJSON

Fixes #781
2026-02-09 10:41:27 +01:00
Maciek Kisiel
6a19c95fea mcp: finalize cleanup of Go 1.23, make Go version support explicit 2026-02-06 12:50:01 +01:00
Maciek Kisiel
d0922abcd3 mcp: allow SSE messages with empty data (SEP-1699) 2026-02-05 15:36:53 +01:00
Alexandru Bumbacea
1fb6e204d7
mcp: improve http transports error handling and make transport work with any size message (#734)
We have a use case for messages larger than 1MB, hence making this
configurable would help.
In order to discover the source of our problems we had to spend some
time investigating, as errors were silently discarded. So we also
included changes to improving surfacing errors .

---------

Co-authored-by: Maciej Kisiel <macio92@gmail.com>
2026-02-05 12:59:37 +01:00
Maciek Kisiel
8a39cf6ce0 conformance: add the remaining client auth test to baseline.yml and trigger conformance tests automatically 2026-02-03 18:04:15 +01:00
Maciek Kisiel
487ef65ef6 conformance: mark the new dns-rebinding-protection scenario as failing 2026-02-03 17:03:31 +01:00
awschmeder
c6e7337e7e fix: add JSON tags to ElicitationCapabilities fields
The Form and URL fields in ElicitationCapabilities were missing JSON
struct tags, causing them to serialize as "Form" and "URL" (capitalized)
instead of the lowercase "form" and "url" required by the MCP protocol.

This caused initialization failures when clients advertised elicitation
capabilities, as servers would not recognize the incorrectly-cased keys.

Discovered via downstream issue: https://github.com/docker/cagent/issues/1574
2026-02-03 16:27:48 +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
Suraj Bobade
df4f0d56a4
mcp: use rand.Text and slog.DiscardHandler over internal implementation (#773)
Address TODOs in code requiring go 1.24 as minimum version

mcp/logging.go: use slog.DiscardHandler, over local implementation
mcp/util.go: use rand.Text, over local implementation

Fixes #772
2026-02-03 14:23:59 +01:00
lif
cf88bda0c5
fix: enforce retry limit when SSE stream makes no progress (#742)
Fixes #679

## Changes
- Track last event ID to detect progress in SSE streams
- Only reset retry counter when Last-Event-ID advances
- Fail connection after exceeding maxRetries without progress

This implements Option 2 from the issue: progress-based retry limiting
that prevents infinite retry loops while allowing long-running streams
to continue as long as progress is made.

---------

Signed-off-by: majiayu000 <1835304752@qq.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Maciej Kisiel <mkisiel@google.com>
2026-02-03 10:17:32 +01:00
Maciek Kisiel
634735bfa7 conformance: create everything-client with basic scenarios and configure GitHub action to run it
- implement all non-auth scenarios (client OAuth is not yet supported)
- sse-retry is also implemented, but failing due to a bug in the SDK
- configure client-conformance job in the conformance GitHub action
- create a client-conformance.sh script and rename conformance.sh to server-conformance.sh
2026-02-03 09:37:23 +01:00
Maciek Kisiel
367175d97b conformance: prepare the conformance test suite
- move the conformance related files to a dedicated directory
- simplify parts of the conformance script
- create a scaffolding for a new GitHub action to run conformance tests
- use the new --output-dir flag from the conformance suite for saving
  results on demand
2026-02-03 09:37:23 +01:00
Maciej Kisiel
bba5acdc8b
mcp: update JSON marshaling to not HTML-escape messages (#769)
It's causing some servers to fail: #767. I checked with Typescript
implementation and it doesn't do any escaping.

It's still a behavior change, so we'll expose a way to bring back the previous behavior via an env var.

Fixes #238 
Fixes #767
2026-01-30 17:13:30 +01:00
Maciek Kisiel
66d6190c4e chore: update dependencies to newest versions, require Go 1.24 2026-01-29 10:30:43 +01:00
FanOne
b29ab52878
mcp: validation only for accept action (#766)
Fix: #758

- validate schema only for accept action
- add unit test
2026-01-28 17:06:06 +01:00
adam jones
fe6d3cf74a
chore: update licensing to Apache 2.0 for new contributions (#750)
As part of MCP joining the Linux Foundation, this PR updates licensing
to Apache 2.0 for new contributions while grandfathering existing MIT
code. See modelcontextprotocol/modelcontextprotocol#1994

---------

Co-authored-by: Maciek Kisiel <mkisiel@google.com>
2026-01-28 09:17:41 +01:00
Maciek Kisiel
6b75899fd7 mcp: fix a race condition in logging v1.3.0 v1.3.0-pre.1
Reading the buffer outside of a critical section can lead to data corruption.
2026-01-25 19:48:35 +01:00
Sam Morrow
52e03de6c8
mcp: add Allow header to 405 responses per RFC 9110 §15.5.6 (#757)
405 Method Not Allowed responses MUST include an Allow header listing
supported methods, per RFC 9110 Section 15.5.6. This fixes issues with
strict HTTP gateways (like Apigee) that treat 405 responses without an
Allow header as malformed, returning 502 Bad Gateway errors.

## Changes

- **SSEHandler**: Add `Allow: GET, POST` header for unsupported methods
- **StreamableHTTPHandler**: Add Allow header for GET-without-session case:
  - Stateless mode: `Allow: POST, DELETE` (GET is never valid)
  - Stateful mode: `Allow: GET, POST, DELETE` (GET is valid once you have
a session)
- Add tests to verify Allow header presence in all 405 responses

## RFC 9110 Reference

[Section 15.5.6 (405 Method Not
Allowed)](https://httpwg.org/specs/rfc9110.html#status.405):

> The origin server MUST generate an Allow header field in a 405
response containing a list of the target resource's currently supported
methods.

## Testing

All existing tests pass, plus new tests added:
- `TestSSE405AllowHeader` - verifies SSE handler compliance
- `TestStreamable405AllowHeader` - verifies Streamable handler
compliance in both stateful and stateless modes

---------

Co-authored-by: omgitsads <4619+omgitsads@users.noreply.github.com>
2026-01-22 09:43:45 +01:00
Sam Morrow
c2c7edc3f0
perf: add schema caching to avoid repeated reflection (#685)
## Summary

This PR adds schema caching that dramatically reduces the cost of tool
registration in stateless server patterns.

**Contributors:** Thanks to @omgitsads and @almaleksia for collaborating
on and researching this solution!

## Problem

In stateless deployments like `github/github-mcp-server`, a new
`*mcp.Server` is created for each incoming request. This means `AddTool`
is called repeatedly for the same tools, causing:

1. **For typed handlers**: `jsonschema.ForType()` reflection is called
every time
2. **For pre-defined schemas**: `schema.Resolve()` is called every time

## Solution

Add a `schemaCache` that stores:
- Schemas by `reflect.Type` (for auto-generated schemas from typed
handlers)
- Resolved schemas by schema pointer (for pre-defined schemas)

The cache is:
- Concurrent-safe using `sync.Map`
- Unbounded (typical MCP servers have &lt;100 tools)
- Global (lives across server instances)

## Benchmark Results

```
BenchmarkAddToolTypedHandler-22          977080    1223 ns/op     1208 B/op    21 allocs/op
BenchmarkAddToolTypedHandlerNoCache-22    6764  161463 ns/op    39262 B/op  1072 allocs/op
```

| Metric | With Cache | Without Cache | Improvement |
|--------|------------|---------------|-------------|
| Time | 1,223 ns/op | 161,463 ns/op | **132x faster** |
| Allocations | 21 allocs | 1,072 allocs | **51x fewer** |
| Memory | 1,208 B/op | 39,262 B/op | **32x less** |

## Files Changed

- `mcp/schema_cache.go` - New cache implementation
- `mcp/server.go` - Modified `setSchema` to use cache
- `mcp/schema_cache_test.go` - Unit tests for caching behavior
- `mcp/schema_cache_benchmark_test.go` - Benchmarks

## Impact for Integrators

**Automatic** - no code changes required. Integrators using:
- Typed handlers (`AddTool[In, Out]`) → cache by type
- Pre-defined schemas (`Tool{InputSchema: schema}`) → cache by pointer

Both patterns benefit from caching after the first call.

---

## Real-World Performance Validation

The following benchmarks were conducted using `github/github-mcp-server`
(a production MCP server with ~130 tools) to validate the performance
impact in a real-world scenario.

### Test Environment
- **Server:** github-mcp-server (stateless HTTP deployment)
- **Tools registered per request:** ~130 tools
- **Test methodology:** Python benchmark client, 30 iterations
(latency), 100 iterations (stress)
- **Date:** December 4, 2025

### Configurations Tested

| Configuration | Description |
|--------------|-------------|
| **main (mcp-go)** | Original implementation using mcp-go library |
| **go-sdk (no cache)** | go-sdk WITHOUT schema caching (broken) |
| **go-sdk (with cache)** | go-sdk WITH this PR&#39;s schema caching fix
|

### Latency Test Results (n=30)

#### Operation: `initialize`

| Configuration | P50 | P99 | Status |
|--------------|-----|-----|--------|
| main (mcp-go) | 11.48ms | 14.00ms | ✅ Baseline |
| go-sdk (no cache) | 20.47ms | 25.30ms | 🔴 **+78% REGRESSION** |
| go-sdk (with cache) | 10.36ms | 14.33ms | ✅ **FIXED (-10%)** |

#### Operation: `tools/list`

| Configuration | P50 | P99 | Status |
|--------------|-----|-----|--------|
| main (mcp-go) | 13.46ms | 22.47ms | ✅ Baseline |
| go-sdk (no cache) | 22.98ms | 29.11ms | 🔴 **+71% REGRESSION** |
| go-sdk (with cache) | 14.15ms | 15.91ms | ✅ **FIXED (+5%)** |

#### Operation: `prompts/list`

| Configuration | P50 | P99 | Status |
|--------------|-----|-----|--------|
| main (mcp-go) | 11.56ms | 13.81ms | ✅ Baseline |
| go-sdk (no cache) | 21.00ms | 26.42ms | 🔴 **+82% REGRESSION** |
| go-sdk (with cache) | 10.77ms | 15.25ms | ✅ **FIXED (-7%)** |

### Stress Test Results (n=100)

| Configuration | P50 (initialize) | P50 (tools/list) | P50
(prompts/list) |

|--------------|------------------|------------------|-------------------|
| main (mcp-go) | 12.06ms | 14.30ms | 11.34ms |
| go-sdk (no cache) | 20.06ms | 23.44ms | 19.59ms |
| go-sdk (with cache) | **11.83ms** | **14.58ms** | **11.05ms** |

### Memory/Allocation Comparison (from pprof)

| Configuration | Total Allocations | Comparison |
|--------------|-------------------|------------|
| main (mcp-go) | 355.91 MB | Baseline |
| go-sdk (no cache) | **1208.70 MB** | 🔴 **3.4x MORE allocations** |

#### Top Allocation Sources - go-sdk WITHOUT cache (broken)

| Function | Size | % | Issue |
|----------|------|---|-------|
| `jsonschema.UnmarshalJSON` | 324.60 MB | 27% | 🚨 Schema re-parsing |
| `encoding/json.Unmarshal` | 341.10 MB | 28% | JSON deserialization |
| `jsonschema.resolve` | 219.51 MB | 18% | 🚨 Schema re-resolution |
| `jsonschema.MarshalJSON` | 92.52 MB | 8% | Schema JSON encoding |

### Root Cause Analysis

The `google/jsonschema-go` library regenerates JSON schemas on every
request instead of caching them. In a server with ~130 tools, this
causes:
- **70-80% latency regression** on all MCP operations
- **3.4x more memory allocations** per request
- **~70% of all allocations** are schema-related operations

### Key Findings

1. ✅ **REGRESSION CONFIRMED:** go-sdk without schema caching is 70-80%
slower than mcp-go
2. ✅ **FIX VERIFIED:** Schema caching restores performance to baseline
(or better)
3. ✅ **MEMORY IMPACT:** 3.4x reduction in allocations with caching
4. ✅ **PRODUCTION READY:** Fixed version performs equivalently to mcp-go
baseline

---------

Co-authored-by: Adam Holt <4619+omgitsads@users.noreply.github.com>
Co-authored-by: Ksenia Bobrova <1885174+almaleksia@users.noreply.github.com>
2026-01-20 20:16:46 -05:00
Claude
3381035a50 .github/workflows: add nightly conformance tests
Add a conformance test job to the nightly GitHub workflow that runs
the SDK conformance tests using ./scripts/conformance.sh.

The job:
- Runs on ubuntu-latest
- Sets up Go 1.25
- Sets up Node.js 20 (required for npx)
- Executes the conformance test script

This ensures the SDK is regularly tested against the official MCP
conformance test suite to catch regressions early.
2026-01-19 13:33:00 +01:00
Jonathan Amsterdam
13488f7da1
Export GetError and SetError methods (#753)
Exported GetError and SetError. 

Fixes #64

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-15 11:46:13 -05:00
zhaoxin
b4f957ff3c
mcp: add DisableListening option to StreamableClientTransport (#729)
mcp: add DisableStandaloneSSE option to control standalone SSE stream

Currently, StreamableClientTransport always establishes a standalone SSE
connection after initialization to receive server-initiated messages.
While this is useful for receiving notifications like
ToolListChangedNotification, it causes problems in several real-world
scenarios:

1. **Unnecessary resource usage**: Many use cases (for me), especially
in scientific computing scenarios, only require simple request-response
communication. Clients don't need server-initiated notifications, making
the persistent SSE connection wasteful.

2. **Server compatibility issues**: Some third-party MCP servers don't
properly handle GET requests for SSE streams. When the client
automatically tries to establish the SSE connection, it fails or hangs,
blocking the entire connection process. This is particularly problematic
when using third-party servers that cannot be modified. This issue is
similar to what was discussed in issue #634.

3. **Lack of user control**: The MCP specification states that the
standalone SSE stream is optional ("The client MAY issue an HTTP GET"),
but the current SDK implementation doesn't provide a way to opt out.
This implementation is also inconsistent with other MCP SDKs like
github.com/mark3labs/mcp-go, which provides getListeningEnabled control
(defaults to false, requires explicit enablement).

Fixes #728

Co-authored-by: zhaoxin <zhaoxin@pjlab.org.cn>
2026-01-11 07:56:47 -05:00
FanOne
a225d4dc7d
delete the old logger in client (#744)
Fix #708   
Link PR #738

deleted the old logger in client.
2026-01-05 12:29:40 -05:00
FanOne
61be548803
feat: deprecated logger in client & add Logger in ClientOption (#738)
Fix #708

1. deprecated logger in client
2. add Logger in ClientOption
3. add unit test for Logger
2026-01-05 07:41:31 -08:00
Liu Shuang
e66c23fae4
mcp: fix connectStandaloneSSE checking Content-Type header (#736)
Issue encountered: the MCP server request was unexpectedly redirected to
an HTML page. Because the client didn’t validate the response
Content-Type, it still entered handleSSE, which then kept treating the
HTML as an SSE stream and repeatedly re-requesting the same
page—creating an infinite request loop.
2026-01-04 13:16:06 -08:00
Jonathan Amsterdam
9c4d349ec0
go.mod: upgrade to jsonschema v0.4.2 (#732)
Visible changes:

- Inference on structs populates PropertyOrder

- An empty map for Schema.Properties marshals as "{}"
2026-01-04 09:18:01 -05:00
ZAIN ULHASSAN
27b3354f80
mcp: fix SSEClientTransport to report HTTP errors properly (#740)
Fix SSEClientTransport.Connect to check HTTP status code before attempting to parse SSE events.

Fixes #714
2025-12-31 08:16:20 -08:00
La002
1d5938cad4
docs: adds SDK version support matrix (#737)
Adds a version compatibility table to the README documenting which SDK
versions support which MCP spec versions.

Fixes #674 

---------

Co-authored-by: i586147 <lakshana.goswami@sap.com>
2025-12-30 17:14:02 -05:00
Robert Findley
411d5a049a
mcp: switch icon theme to named string type (#733) v1.2.0 v1.2.0-pre.2
To be consistent with logging level, use a named string type for icon
themes. This improves readability / discoverability, and is more
consistent.

Updates #552
For #725
2025-12-19 16:44:10 -05:00
Jonathan Hefner
76e6854375
docs: add GitHub Pages redirect to latest release documentation (#702)
**Context:** I am working on getting documentation up for each MCP SDK
at `https://modelcontextprotocol.github.io/*-sdk/`. I realize the Go
ecosystem has its own idioms for generating and hosting documentation,
so I am proposing we make https://modelcontextprotocol.github.io/go-sdk/
redirect to the latest tag of
https://github.com/modelcontextprotocol/go-sdk/blob/v1.1.0/docs/README.md.

---

Adds a redirect page at https://modelcontextprotocol.github.io/go-sdk/
that automatically redirects visitors to the `docs/README.md` of the
latest release tag. Uses Jekyll templating with
`site.github.latest_release` to dynamically resolve the current release
version at build time.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 15:21:03 -08:00
Robert Findley
875d1d30c2
mcp: don't break the streamable client connection for transient errors (#723) v1.2.0-pre.1
When POST requests in the streamableClientConn return a transient error,
return this error to the caller rather than permanently breaking the
connection.

This is achieved by using the special sentinel ErrRejected error to the
jsonrpc2 layer. In doing so, the change revealed a pre-existing bug:
ErrRejected had the same code as ErrConnectionClosing, and
jsonrpc2.WireError implements errors.Is, so the two sentinel values
could be conflated. This is fixed by using a new internal code.

There's more to do for #683: we should also retry transient errors in
handleSSE.

For #683
2025-12-12 13:49:41 -05:00
Nikola Petrov
e009bacfcb
oauthex: fix content type check in getJSON (#721)
The content type header contains full mimetype string which might
contain parameters. The getJSON function doesn't account for this and so
some responses fail to parse although they are perfectly acceptable. For
example
```
curl -vv 'https://github.com/.well-known/oauth-authorization-server/login/oauth'
```
returns a content type header with the value
```
content-type: application/json; charset=utf-8
```
which is perfectly valid but fails in the current logic.
2025-12-12 09:44:26 -05:00
Robert Findley
3430e2242e
mcp: add Capabilities fields to ServerOptions and ClientOptions (#713)
This allows users to configure default capabilities, disable defaults,
or suppress listChanged notifications by setting ListChanged: false.

Key changes:
- Add ServerOptions.Capabilities and ClientOptions.Capabilities fields.
- Merge user-provided capabilities with dynamically-added ones (tools,
  prompts, resources, sampling, elicitation).
- Check ListChanged before sending list-changed notifications.
- Deprecate HasPrompts, HasResources, HasTools on ServerOptions.
- Remove unreleased ElicitationModes field from ClientOptions.
- Add clone methods to capability structs using shallowClone helper.
- Add synctest-based tests for notification behavior (go1.25).
- Add TestClientCapabilitiesOverWire and TestServerCapabilitiesOverWire
- Document the new Capabilities API in docs/

Updates #706
2025-12-11 20:17:46 -05:00
Robert Findley
bae0929820
mcp: address review comments on streamable cleanup (#716)
Follow up on some post-submit comments from jba.
2025-12-11 15:16:04 -05:00
Robert Findley
b3399e6730
mcp: use args struct for CloseSSEStream (#718)
Change CloseSSEStream from func(time.Duration) to
func(CloseSSEStreamArgs) for forward compatibility, allowing new fields
to be added without breaking existing callers.
2025-12-11 15:13:38 -05:00
Robert Findley
1a964ae23d
mcp: debounce server change notifications (#717)
Debounce and batch notifications for changes in server feature sets
(adding or removing tools, prompts, resources, or resource templates).

This PR handles server-side change notifications only. We could do the
same for client-side notifications (e.g. roots changed), but at the
moment we don't need to.

Fixes #649.
2025-12-11 15:11:32 -05:00
Robert Findley
a5e369ed12
mcp: fix broken client root capabilities (#698)
To address #607, add ClientCapabilities.RootsV2, and populate it when
constructing sending and receiving InitializeParams.

Fixes #607
2025-12-11 10:16:56 -05:00
Rob Findley
307e32c2b3 oauthex: validate URL schemes in auth server metadata and DCR
Use URL scheme validation for remaining URL fields in auth
datastructures (AuthServerMeta and ClientRegistrationMetadata), to
prevent XSS attacks.

By comparison, the typescript SDK doesn't validate
introspection_endpoint, and I don't know why. Otherwise the
implementations align.

Fixes #526
2025-12-11 10:09:43 -05:00
Rob Findley
6f6e196c27 mcp: fix stray log.Printf in notifySessions
I encountered these logs, and they were distracting and noisy. We
shouldn't log to stderr unless configured by the user. Thread through
the logger.

Also, use ensureLogger in the streamable client to simplify call sites.

Updates #708
Updates #684
2025-12-10 18:15:54 -05:00