Commit graph

357 commits

Author SHA1 Message Date
ContextVM-org
5d00e20f2a
Add progress-aware request timeout reset (#858)
* feat: add progress-aware request timeouts

* Update crates/rmcp/src/service.rs

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>

* refactor(rmcp): move helpers and simplify response waiting

---------

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
2026-06-17 15:07:37 -04:00
Greg Virgin
4b82e41522
docs(server): document Err vs Ok(CallToolResult::error) visibility contract on ServerHandler::call_tool (#854)
* docs(server): document Err vs Ok(CallToolResult::error) visibility contract

The MCP spec separates two failure modes that surface very differently in
clients:

  - Err(ErrorData) is a JSON-RPC protocol error. Most MCP clients render
    it opaquely ("Tool result missing due to internal error") - the
    caller does not see the message text.
  - Ok(CallToolResult::error(content)) is a tool-level error. Clients
    render the content; the caller reads the message.

The right shape for "the tool didn't work" is the latter, but Err is
what most handlers reach for because it looks like the natural Rust
return value. This commit adds rustdoc on both ServerHandler::call_tool
and CallToolResult::error pointing handlers at the correct shape, with
a worked example showing protocol errors (-32602 invalid_params) vs
tool errors (empty result, downstream failure).

This is the docs half of the visibility-contract ask. A follow-up may
introduce a typed ToolOutcome sum type to enforce the distinction at
compile time; this PR is the lower-risk version that unblocks the
class immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: update crates/rmcp/src/handler/server.rs

* docs: update crates/rmcp/src/model.rs

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
2026-06-16 22:13:33 -04:00
Dale Seo
95a8e961e0
feat: standardize resource-not-found error code (SEP-2164) (#899)
* feat: implement SEP-2164 resource not found errors

* test: update protocol version utility expectations

* feat: gate not-found code at server boundary

---------

Co-authored-by: Michael Neale <michael.neale@gmail.com>
2026-06-16 21:55:25 -04:00
0xWeakSheep
5a78773faa
fix: return tool errors for invalid arguments (#894) 2026-06-11 10:18:02 -04:00
Michael Neale
53c6daadd9
fix(auth): apply offline_access to reauth paths (#897)
* fix(auth): apply offline_access to reauth paths

* Update crates/rmcp/src/transport/auth.rs

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>

---------

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
2026-06-11 06:43:49 -04:00
Dale Seo
2536a05992
fix: update peer info on duplicate initialize (#862) 2026-06-10 15:42:09 -04:00
Michael Neale
2d3d1879ad
feat: validate OAuth authorization response issuer (#896)
* feat: validate OAuth authorization response issuer

* fix: tighten issuer validation callbacks
2026-06-10 13:08:31 +10:00
Stefano Amorelli
f1ef2ec86c
feat: specify OIDC application_type during dynamic client registration (SEP-837) (#883)
* feat(auth): specify OIDC application_type during client registration

SEP-837 [1] requires an MCP client to specify an application_type during
OIDC Dynamic Client Registration. When it is omitted, OIDC servers
default the client to "web", which conflicts with the loopback redirect
URIs that CLI and desktop clients use, so the registration can be
rejected.

I make register_client always send an application_type. It defaults to
"native" to match the loopback redirect this SDK uses, and I added
OAuthClientConfig::with_application_type so web clients can opt in. Tests
cover the serialized request body and the config default. Implements [2].

[1]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/draft/basic/authorization.mdx#L395
[2]: https://github.com/modelcontextprotocol/rust-sdk/issues/880

Signed-off-by: Stefano Amorelli <stefano@amorelli.tech>

* chore(auth): declare application_type in client metadata document

I set application_type to "native" in the hosted client metadata
document so the URL-based client id flow and dynamic registration agree
on the client type that SEP-837 [1] expects.

[1]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/draft/basic/authorization.mdx#L395

Signed-off-by: Stefano Amorelli <stefano@amorelli.tech>

---------

Signed-off-by: Stefano Amorelli <stefano@amorelli.tech>
2026-06-04 10:53:18 -04:00
Rohit Ghumare
82b04a31f5
feat: deprecate roots, sampling, and logging (SEP-2577) (#884)
SEP-2577 deprecates the Roots, Sampling, and Logging features. The
deprecation is advisory: the features stay fully functional and there is
no wire-level change. Mark the corresponding Rust APIs as deprecated so
downstream users get compiler warnings and migration guidance.

- Forward attributes through the service `method!` macros and deprecate
  `Peer::create_message`, `Peer::list_roots`, `Peer::set_level`, and
  `Peer::notify_logging_message`.
- Forward per-field attributes through the capability `builder!` macro and
  deprecate the generated `enable_roots`, `enable_sampling`, and
  `enable_logging` builders, plus the hand-written
  `enable_roots_list_changed`, `enable_sampling_tools`, and
  `enable_sampling_context`.
- Document the deprecation on the capability types and fields, and in the
  README feature sections.
- Allow `deprecated` at the crate's own call sites so the build stays
  warning-clean, and refresh the message schema snapshots.
2026-06-04 08:43:39 -04:00
Dale Seo
254f04a764
fix: strip and validate tool outputSchema and inputSchema (#860)
* fix: remove unnecessary fields from tools' outputSchema

* fix: validate input schema root type per MCP spec
2026-06-02 12:01:33 -04:00
Federico Poli
53e4410d99
fix: remove unnecessary fields from tools' inputSchema (#856) 2026-05-28 11:28:57 -04:00
Dale Seo
c330fede90
fix: reject init header/body version mismatch (#853) 2026-05-18 20:21:22 -04:00
Dale Seo
d328751dc9
fix: align protocol version negotiation (#855)
* fix: align protocol version negotiation

* ci: relax semver-checks to allow minor changes
2026-05-18 16:54:08 -04:00
Alex Hancock
cc66e3091e
fix: accept 200 with empty body in response to notifications in addition to 202 (#849) 2026-05-14 14:52:11 -04:00
github-actions[bot]
3529c3675f
chore: release v1.6.1 (#831)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-13 09:36:22 -04:00
Yutaka Nishimura
d83b1566d0
fix(rmcp): flatten Resource variant of PromptMessageContent (#843)
The Resource variant of PromptMessageContent was missing #[serde(flatten)],
causing the embedded resource content block to serialize as a double-nested
shape `{ "type": "resource", "resource": { "resource": {...} } }` instead of
the spec-compliant flat shape `{ "type": "resource", "resource": {uri, mimeType, text} }`.

This caused Zod-based MCP clients (e.g. Claude Code) to reject prompts/get
responses containing embedded resource messages with InvalidUnion errors.

The Image and ResourceLink variants already use #[serde(flatten)] correctly;
only Resource was missing it.

Fix: add #[serde(flatten)] so EmbeddedResource (=Annotated<RawEmbeddedResource>)
fields _meta / annotations / resource are flattened to the content-block level,
matching the MCP spec for prompts embedded resources.

Regression test: test_prompt_message_resource_serialization_is_flat verifies
content.resource.uri is reachable and content.resource.resource is absent.

Schema snapshots regenerated via UPDATE_SCHEMA=1.
2026-05-12 14:46:12 -04:00
Dale Seo
321ab14f67
fix: reply -32700 on stdio parse errors instead of closing (#833)
* fix: reply -32700 on stdio parse errors instead of closing

* fix: make JsonRpcError id optional per MCP spec
2026-05-07 12:27:15 -04:00
Xuntao Chi
0f776ab1d6
chore(rmcp): remove dependency on chrono default features (#829) 2026-05-06 14:53:22 -04:00
lutz-grex
2f8d3b7355
Fix/issue 817 idle timeout log level (#824)
* fix(transport): downgrade idle timeout log from error to debug

Idle keep-alive timeout is normal zombie-session cleanup, not a transport failure.

Route it through a dedicated WorkerQuitReason::IdleTimeout variant.

Log it at debug level instead of treating it as a fatal error.

Remove the unused LocalSessionWorkerError::KeepAliveTimeout variant.

Closes #817

* fix(session): tolerate dead worker in close_session

Swallow SessionServiceTerminated in close_session when the worker has already exited.
This prevents a spurious ERROR log during the post-exit cleanup path in
spawn_session_worker.

* fix(transport): address PR review feedback

- deprecate KeepAliveTimeout
- harden tests
2026-05-04 20:14:06 -04:00
github-actions[bot]
014fb2e6cd
chore: release v1.6.0 (#818)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-01 09:38:25 -04:00
Dale Seo
c1e0eadd5d
fix: add init_timeout for streamable-http sessions (#811) 2026-05-01 09:20:50 -04:00
Dale Seo
ef74147113
fix(http): fall back to :authority for HTTP/2 (#827) 2026-05-01 09:17:06 -04:00
Dale Seo
4cf78736e7
feat(http): log Host/Origin rejections (#826) 2026-05-01 14:42:47 +02:00
Dale Seo
9753d61510
feat(http): add Origin header validation (#823) 2026-04-23 15:41:06 -04:00
lutz-grex
63583b164f
feat(router): support runtime disabling of tools (#809)
* feat(router): support runtime disabling of tools

Add methods to disable/enable tools at runtime.
Disabled tools are hidden from listing, lookup,
and execution, including in composed routers.

Closes #477

* fix(router): simplify disable tool api

* feat(router): auto-send tools/list_changed on disable/enable

* refactor(router): simplify disable_route and notifier call
2026-04-22 08:10:29 -04:00
Guy Lichtman
8f696e6788
feat: optional session store (resumabillity support) (#775)
* feat: optional session store

* fix: docs

* fix: pr review comments

* fix: add non_exhaustive

* fix: support for non_exhaustive StreamableHttpServerConfig

* fix: add SessionState::new
2026-04-21 17:06:18 -04:00
github-actions[bot]
020a38b6ad
chore: release v1.5.0 (#804)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-16 12:47:24 -04:00
jh-block
01a6666429
fix: treat resource metadata JSON parse failure as soft error (#810)
In fetch_resource_metadata_from_url, a JSON parse failure on the
response body caused a fatal AuthError::MetadataError, preventing
discover_metadata() from falling through to direct
.well-known/oauth-authorization-server discovery (Strategy B).

MCP servers that return HTTP 200 with non-JSON content (e.g. HTML)
at their base URL caused the OAuth flow to abort entirely, even
when the server had a valid .well-known/oauth-authorization-server
endpoint.

Return Ok(None) on parse failure, consistent with how HTTP errors
are already handled in the same function.
2026-04-16 12:16:19 -04:00
Dale Seo
3e56d52764
fix: include http_request_id in request-wise priming event IDs (#799)
* fix: include http_request_id in request-wise priming event IDs

* refactor: use Option::into_iter and usize::from for priming

* fix: retain event cache for completed request-wise channels

* fix: track completed_at for cache eviction and resume

* fix: log resume failures at warn level

* test: add completed_cache_ttl eviction test

* fix: return empty stream on failed resume

* test: add resume after completion test
2026-04-16 12:02:35 -04:00
WeekendsuperHero
6603c1ff15
fix(macros): respect local feature in #[prompt] macro — omit + Send bound (#803)
* refactor(prompt): update return type handling

* fix(prompt): add omit send and test
2026-04-14 09:56:19 -04:00
Dale Seo
c99903a67a
fix(http): drain SSE stream for connection reuse (#790)
* fix(http): reduce latency on subsequent StreamableHttp calls

* refactor: rely on stream drain for connection reuse

* refactor: clean up comments and naming

* fix: restore pool_max_idle_per_host(0) for Linux
2026-04-13 16:33:30 -04:00
Will Pfleger
ad3997268d
feat(transport): add constructors for non_exhaustive error types (#806)
AuthRequiredError, InsufficientScopeError, and DynamicTransportError
were marked #[non_exhaustive] in #715/#768 but don't have constructors
usable by external crates. Add new() for the error types and
from_parts() for DynamicTransportError (the existing new() requires a
Transport type parameter, making it unusable for test fixtures).

Fixes #805
2026-04-13 16:31:26 -04:00
dependabot[bot]
a743f15654
chore(deps): update which requirement from 7 to 8 (#807)
Updates the requirements on [which](https://github.com/harryfei/which-rs) to permit the latest version.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.0...8.0.2)

---
updated-dependencies:
- dependency-name: which
  dependency-version: 8.0.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-13 16:10:42 -04:00
Dale Seo
a64be23152
feat: add 2025-11-25 protocol version support (#802) 2026-04-10 15:21:48 -04:00
github-actions[bot]
4628720f89
chore: release v1.4.0 (#779)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-10 10:40:38 -04:00
Anar Azadaliyev
65d2b29da5
fix(server): remove initialized notification gate to support Streamable HTTP (#788)
* fix(server): remove initialized notification gate to support Streamable HTTP

The server's init handshake loop fatally rejected any request arriving
before the `notifications/initialized` message. This breaks Streamable
HTTP clients where each JSON-RPC message is a separate POST with no
ordering guarantee — `tools/list` can easily arrive before `initialized`.

Remove the ~40-line wait loop and enter `serve_inner` immediately after
sending `InitializeResult`. The `initialized` notification is now
handled as a regular notification by the main service loop, matching the
TypeScript SDK behavior (validated in typescript-sdk#578).

Also remove the now-unreachable `ExpectedInitializedNotification` error
variant from `ServerInitializeError`.

Closes #783

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(server): keep ExpectedInitializedNotification as deprecated

Retain the variant for semver compatibility — removing it would be a
breaking change caught by cargo-semver-checks. Mark it deprecated with
a note that it is never constructed and will be removed in a future
major release.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Anar Azadaliyev <anar.azadaliye@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 19:23:59 -04:00
Dale Seo
8a8c036ccb
chore: update Rust toolchain to 1.92 (#797) 2026-04-09 14:09:59 -04:00
Eren Atas
45a4cc5316
feat: add Default and constructors to ServerSseMessage (#794)
* feat: add Default and constructors to ServerSseMessage

* fix: add tests, missing feature gates, small test issues
2026-04-08 16:39:16 -04:00
Matthew Zeng
5f432834a1
feat: add meta to elicitation results (#792) 2026-04-08 15:07:04 -04:00
Dale Seo
be321a4abe
feat(macros): auto-generate get_info and default router (#785)
* feat(macros): auto-generate get_info and default router

* docs: simplify examples and docs with new defaults

* feat(macros): add tool_router(server_handler) to elide separate #[tool_handler] impl

* docs: add Tools section to README and simplify calculator examples with server_handler
2026-04-08 15:06:26 -04:00
Dale Seo
5891b45162
refactor: unify IntoCallToolResult Result impls (#787) 2026-04-08 10:39:18 -04:00
Dale Seo
929441e443
fix: default session keep_alive to 5 minutes (#780) 2026-04-08 10:36:19 -04:00
Axel
cabf71aa74
feat(transport): add which_command for cross-platform executable resolution (#774)
* feat(transport): add which_command for cross-platform executable resolution

Adds a `which_command()` helper that resolves executable paths via the
`which` crate before constructing a `tokio::process::Command`. This fixes
Windows failures where `.cmd` shim scripts (e.g. `npx.cmd`) are not
found by `Command::new()` without a fully-qualified path.

Closes #456

* refactor(transport): move which_command behind opt-in feature flag

Address review feedback: the `which` dependency is now gated behind a
separate `which-command` feature flag instead of being bundled into
`transport-child-process`. Users on Linux/macOS who don't need
cross-platform executable resolution no longer pull in the extra crate.

Also fixes the doc example import path to use the re-exported
`rmcp::transport::which_command`.
2026-04-07 06:58:23 -04:00
jokemanfire
8e22aa2de2
fix(http): add host check (#764)
Signed-off-by: jokemanfire <hu.dingyang@zte.com.cn>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-01 19:28:56 -04:00
lif
cf6988ac7c
fix: exclude local feature from docs.rs build (#782)
The `local` feature relaxes Send+Sync bounds, which causes items
gated behind `cfg(not(feature = "local"))` to be excluded when
docs.rs builds with all-features. Replace `all-features = true`
with an explicit feature list that omits `local`.

Signed-off-by: majiayu000 <1835304752@qq.com>
2026-04-01 17:40:11 -04:00
Will Pfleger
b74f5ca35b
feat(auth): add StoredCredentials::new() constructor (#778)
StoredCredentials is #[non_exhaustive] but has no constructor, making
it impossible for external crates implementing CredentialStore to
construct instances without a serde roundtrip workaround. Add a new()
constructor matching the pattern used for other #[non_exhaustive]
types in this crate.

Fixes #777
2026-03-27 14:47:41 -04:00
github-actions[bot]
ac749e3ced
chore: release v1.3.0 (#747)
* chore: release v2.0.0

* chore: version 1.3.0

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
2026-03-26 11:31:13 -04:00
Guy Lichtman
0b36a84f05
feat: add "theme" to Icon (#766)
* feat: add theme field to Icon

* fix: update IconThem crates/rmcp/src/model.rs (non_exhaustive)

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>

* fix: update IconThem crates/rmcp/src/model.rs (eq, hash)

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>

* fix: update docs with full descriptions of theme from mcp spec

---------

Co-authored-by: Dale Seo <5466341+DaleSeo@users.noreply.github.com>
2026-03-26 10:26:44 -04:00
Dale Seo
6a3b32d3ab
chore: add #[non_exhaustive] to remaining public structs (#768)
* chore: add #[non_exhaustive] to remaining public structs

* chore: add #[non_exhaustive] to remaining public types

* chore: enable exhaustive_structs/enums clippy lints

* test: add untagged ServerResult deserialization regression tests
2026-03-26 10:23:58 -04:00
Will Pfleger
ee1c63c53f
feat(transport): add Unix domain socket client for streamable HTTP (#749)
* feat(transport): add Unix domain socket client for streamable HTTP

MCP hosts in Kubernetes environments with Envoy sidecars need to route
HTTP through Unix domain sockets because DNS-based URIs only resolve
via the proxy. Adds UnixSocketHttpClient implementing StreamableHttpClient
using hyper over tokio::net::UnixStream, gated behind the
transport-streamable-http-client-unix-socket feature.

Also extracts RESERVED_HEADERS, extract_scope_from_header, and
validate_custom_header into common/http_header.rs to share header
validation logic between the reqwest and unix socket implementations.

* fix(transport): address review feedback for unix socket transport

- Document one-connection-per-request behavior on UnixSocketHttpClient
- Reject empty socket paths and bare '@' in constructor with assert
- Add explicit dep:http to unix-socket feature for self-documenting deps
- Document MCP-Protocol-Version exception on RESERVED_HEADERS constant
- Fix test catch-all to echo request id instead of hardcoding 1
- Remove leftover sleep(100ms) in test_unix_socket_custom_headers
- Add blank line before macro comment in Cargo.toml

* fix(transport): fix CI failures for unix socket transport

- Use std::io::Error::other() instead of Error::new(ErrorKind::Other)
  to satisfy clippy::io_other_error on newer nightly
- Use #[tokio::test(flavor = "current_thread")] for unix socket tests
  since axum's serve(UnixListener) requires spawn_local
- Gate validate_custom_header behind client-side-sse feature since it
  references http::HeaderName which isn't available with default features

* fix(transport): fix CI failures for unix socket transport

axum::serve(UnixListener) uses spawn_local on Linux, which panics
outside a LocalSet. Replace with manual hyper HTTP/1.1 server that
accepts connections directly from the UnixListener, avoiding the
spawn_local requirement entirely.

* fix(transport): skip unix socket tests when local feature is enabled

The local feature causes ().serve(transport) to use spawn_local, which
requires a LocalSet. Gate the integration tests with not(feature = "local")
to match every other integration test in the repo.
2026-03-24 09:50:32 -04:00