Commit graph rust-sdk/crates/rmcp/src/handler
Author SHA1 Message Date
Jack Amadeo
07abfdfd99
feat: Implement SEP-2663 Tasks Extension (#1020)
* feat!: implement SEP-2663 Tasks extension, removing the experimental 2025-11-25 tasks design

Reshape tasks from the experimental SEP-1319/1686 core-protocol feature into
the official io.modelcontextprotocol/tasks extension (SEP-2663):

Model:
- Re-model Task (statusMessage, ttlMs nullable, pollIntervalMs) and add
  DetailedTask with status-discriminated payloads (inputRequests/result/error
  inlined per spec)
- CreateTaskResult now flattens Task with resultType: "task";
  add ResultType::TASK and CallToolResponse::Task
- Add tasks/update (UpdateTaskParams with MRTR InputResponses); tasks/get and
  tasks/cancel reworked; remove tasks/list, tasks/result
- notifications/tasks now carries a full DetailedTask; removed from client
  notifications (SEP-2260)

Capabilities:
- Remove core TasksCapability et al; tasks are declared via the extensions
  map (enable_tasks() builders, supports_tasks() accessors)
- Remove tool-level execution.taskSupport and the _meta.task /
  TaskMetadata / with_task() opt-in: task creation is server-directed and
  gated on the per-request client capability

Runtime:
- Replace OperationProcessor with TaskManager: durable-before-response task
  creation, input_required round-trips via tasks/update, cooperative
  cancellation, TTL expiry
- Client peer helpers get_task/update_task/cancel_task
- Emit Mcp-Name routing header from params.taskId for tasks/* methods
  (SEP-2243/2663)

Macros:
- Remove #[task_handler] and the tool execution() attribute

Tests/examples/docs updated; schema snapshots regenerated.

BREAKING CHANGE: the 2025-11-25 experimental tasks API is removed without a
compatibility shim. Clients that do not declare the tasks extension always
receive synchronous results.

* feat: gate tasks/* methods on the client tasks-extension capability (SEP-2663)

When the server advertises io.modelcontextprotocol/tasks but the client did
not declare it (per-request _meta clientCapabilities, or initialize-time
capabilities in session mode), tasks/get, tasks/update, and tasks/cancel now
return -32021 Missing Required Client Capability with the required extension
in the error data, instead of falling through to the handler's -32601
default. Servers that do not advertise the extension keep returning -32601.

Also add regression tests confirming that unknown taskIds yield -32602 and
that a legacy 2025-11-25 'task' param on tools/call is silently ignored.

* feat: pass SEP-2663 Tasks extension conformance suite

Conformance fixtures (conformance/src/bin/server.rs):
- Add the required fixture tools: greet (sync-only), slow_compute,
  failing_job (task support: required), protocol_error_job, confirm_delete,
  multi_input, and test_tool_with_task (MRTR -> task escalation), all backed
  by TaskManager with server-directed task creation gated on the client's
  tasks-extension capability
- Advertise io.modelcontextprotocol/tasks in server capabilities and wire
  get_task/update_task/cancel_task handlers
- Task-required tools reject with -32021 when the client did not declare the
  extension

SDK wire-shape fixes surfaced by the suite:
- Add resultType: "complete" to GetTaskResult and introduce TaskAckResult
  so tasks/update and tasks/cancel acks carry the SEP-2322 discriminator
  (spec: every non-CreateTaskResult response on the tasks surface is
  resultType complete); dispatch now returns ServerResult::task_ack
- CreateTaskResult gets a strict deserializer requiring resultType: "task"
  so it does not shadow task-shaped results in untagged unions
- Client update_task/cancel_task accept both TaskAckResult and EmptyResult

All 9 runnable Tasks extension server scenarios now pass (35/35 checks;
tasks-status-notifications remains upstream-skipped), so the corresponding
entries are removed from conformance/expected-failures-extensions.yaml.
2025-11-25 server suite (40/40), 2026-07-28 server suite (114/114), draft
client suite, and extensions client suite all pass their baselines.

* fix: address PR 1020 review feedback on DetailedTask schema and cooperative cancel

- DetailedTask's JsonSchema now derives from the actual flattened wire shape
  (DetailedTaskWire: base Task + optional inputRequests/result/error) instead
  of approximating with the base Task schema, so generated schemas for
  GetTaskResult and notifications/tasks document the status-specific payload
  fields. Golden message schemas regenerated.

- TaskManager::cancel_task no longer aborts the underlying future. It records
  the observable cancelled state and acks immediately (spec's eventually
  consistent semantics), but lets the operation keep running so it can observe
  is_cancel_requested() or the error from a woken request_input() and perform
  cleanup; any late result is discarded. New unit tests cover both the
  cooperative-cleanup path and waking parked input requests.

* fix: make tasks/cancel truly cooperative per SEP-2663 (FEEDBACK_2)

- TaskManager::cancel_task no longer forces terminal 'cancelled'. It records
  the cancellation intent, acks immediately, and wakes parked request_input
  awaits, but the operation decides its own terminal state: a post-cancel
  error settles as 'cancelled', while an operation that finishes its work
  settles as 'completed' — per the spec, 'the task may still reach a
  non-cancelled terminal status'.
- Add TaskContext::cancelled(), a watch-based await for use with
  tokio::select! as the cooperative cancellation exit path.
- Correct the unsupported-notification method string from
  'notifications/tasks/status' to 'notifications/tasks' and document that
  task status notifications are not yet routable through subscriptions/listen
  (SubscriptionFilter has no taskIds field; upstream check still skipped).
- Update conformance slow_compute fixture, task_demo example, and tests to
  honor cancellation via ctx.cancelled(); lifecycle conformance still 8/8
  (all 9 scenarios remain green, 35/35 checks).

* fix: sweep and evict expired tasks from TaskManager (FEEDBACK_3)

TTL handling previously only ran from get_task and only flipped overdue
non-terminal tasks to 'failed', never removing entries — an unbounded leak
for long-lived servers, and it made ttl_ms: None = 'unlimited retention'
meaningless since everything was retained forever.

- Rename expire_overdue to sweep_expired and run it from every entry point
  (spawn, get_task, update_task, cancel_task).
- Track terminal_at on each entry; terminal tasks are evicted after being
  retained for one further ttl_ms window past their terminal transition, so
  well-behaved pollers can observe the final state before late tasks/get
  calls return -32602 (spec: servers may delete expired tasks at any time,
  and returning task-not-found for purged tasks is compliant behavior).
- ttl_ms: None entries are never evicted (spec: unlimited retention);
  document the retention model on TaskManager, including that there is no
  background sweeper.
- New tests: retention-window eviction, sweep of abandoned tasks via other
  entry points, unlimited-TTL retention, and error-code assertions
  (-32602) for unknown ids across get/update/cancel.

Reviewed the second feedback item (dedicated task-not-found error code)
against SEP-2663 §Protocol Errors and it is incorrect: the SEP explicitly
specifies -32602 (Invalid params) for invalid or nonexistent taskIds, which
is what unknown_task already returns. Kept -32602; strengthened tests to
assert the code.

* fix: address Copilot review feedback on PR #1020

- TaskManager: drop the JoinHandle as soon as the operation settles instead
  of retaining it for the whole retention window, and only store it at spawn
  time if the task is still non-terminal (avoids keeping a completed handle
  that the completion path could never clear).
- Use RequestContext::client_capabilities() (which applies the initialize-time
  fallback for session peers) instead of raw context.meta.client_capabilities()
  in the task_demo example, the README snippet, and the test server — the
  meta-only form would wrongly treat session-declared tasks clients as
  unsupported.

All 9 Tasks extension conformance scenarios remain green (35/35 checks).

* fix: enforce SEP-2663 task-result gating in tools/call dispatch

A handler could return CallToolResponse::Task without checking whether the
request declared the io.modelcontextprotocol/tasks extension, sending a
CreateTaskResult to a client that cannot parse it. The SDK dispatch now
rejects that case with -32021 Missing Required Client Capability before the
response leaves the server, using RequestContext::client_capabilities()
(per-request _meta with initialize-time fallback).

Adds a regression test with a deliberately misbehaving handler that always
materializes a task; all Tasks extension conformance scenarios remain green
(35/35 checks).

* fix: align TTL boundary comparisons in sweep_expired

Copilot review: ttlMs: 0 never expired immediately because phase 1 used a
strict 'elapsed > ttl_ms' comparison, and phase 2 retained terminal tasks
at exactly the TTL boundary ('elapsed <= ttl_ms'). Treat elapsed >= ttl_ms
as expired in phase 1 and evict when elapsed >= ttl_ms in phase 2, so both
phases agree at the boundary and ttlMs: 0 expires/evicts on the first sweep.

* fix: strict TaskAckResult deserializer and TASK_REQUIRED_TOOLS consistency

- TaskAckResult carried only resultType (+ optional _meta) with a derived
  Deserialize, so inside the untagged ServerResult union it greedily matched
  any result object containing a resultType key, shadowing CustomResult and
  losing data (verified with a probe). Replace with a strict deserializer:
  deny_unknown_fields and require resultType == "complete". Regression
  tests cover both the non-matching shapes and the genuine ack shape.

- Conformance fixtures: confirm_delete and multi_input rejected non-tasks
  clients inline, contradicting the TASK_REQUIRED_TOOLS doc/constant. Move
  them into TASK_REQUIRED_TOOLS (they park on in-task elicitation and have
  no synchronous fallback) so the upfront -32021 gate is the single source
  of truth, and drop the now-unreachable inline checks.

All 9 Tasks extension conformance scenarios remain green (35/35 checks).

* fix: let the operation decide its terminal state via TaskExit

After tasks/cancel, any operation error was coerced to terminal
'cancelled', masking real failures (e.g. an unrelated error landing just
after a late cancel request) and contradicting the documented 'operation
decides its own terminal state' contract.

Change TaskFuture's error type from McpError to a new TaskExit enum:

- TaskExit::Cancelled — an explicit cooperative-cancellation exit;
  settles as terminal 'cancelled'.
- TaskExit::Error(McpError) — a real failure; settles as terminal
  'failed' with the error inlined, even after tasks/cancel was received.

From<McpError> for TaskExit keeps '?' ergonomic in task bodies, and
request_input() now returns TaskExit directly (its wake-on-cancel path
yields TaskExit::Cancelled), so parked operations that propagate it with
'?' settle as 'cancelled' automatically.

Update the conformance fixtures, task_demo example, and tests; add a
regression test asserting a post-cancel unrelated error settles as
'failed' with its error payload preserved.

All 9 Tasks extension conformance scenarios remain green (35/35 checks).

* fix: allow TaskExit enum to be exhaustive

* fix: close spawn/shutdown race, sweep in running_task_count, clarify TTL retention docs

Addresses branch-review findings:

- spawn() raced with shutdown(): if shutdown drained the task map between
  the entry insert and the JoinHandle store, the handle was dropped and the
  operation kept running detached. If the entry is gone at store time, the
  handle is now aborted instead.
- running_task_count() now runs the TTL sweep like every other entry point,
  so it no longer reports overdue tasks as running (matching the documented
  sweep-on-every-entry-point behavior). Regression test added.
- Document that terminal-task retention intentionally extends one ttl_ms
  window past the terminal transition (observation grace period) beyond the
  creation-based lifetime ttlMs advertises on the wire — compliant since
  SEP-2663 allows deleting expired tasks at any time after the TTL.
2026-07-22 14:16:52 -04:00
Dale Seo
ff1a715b6e
feat: add subscription listen streams (SEP-2575) (#1000)
* feat: add subscription listen streams

* refactor: reuse legacy request helper
2026-07-21 09:53:04 -04:00
Dale Seo
2e2c79173c
feat: add modern client lifecycle modes (SEP-2575) (#995)
* feat: add modern client lifecycle modes

* refactor: drop modern naming and dead cleanup

* fix: gate request metadata on inline lifecycle only
2026-07-16 17:12:16 -04:00
Dale Seo
3549e86d28
feat!: add server discovery and negotiation (SEP-2575) (#973)
* feat!: add server discovery and negotiation

* feat!: make protocol unions extensible

* ci: add discovery conformance coverage
2026-07-16 13:47:30 -04:00
Dale Seo
839922d8fd
feat!: align metadata models with draft schema (#993) 2026-07-15 08:58:46 -04:00
Dale Seo
dd30a70f84
feat!: add MRTR behavior support (SEP-2322) (#929)
* feat!: add MRTR behavior support

* feat: harden SEP-2322 MRTR support

* ci: diff public API only on features common to base and head

cargo public-api builds both revisions with the same feature set, so a
feature introduced (or removed) by a PR broke the build of the other
revision. Restrict the all-features diff to features present in both the
base and head revisions.
2026-07-10 12:02:26 -04:00
Brandon Bennett
9e3de344f4
feat: relax outputSchema to accept non-object JSON Schema types (SEP-2106) (#895)
* fix: address PR review - schema_for_output no longer validates or returns Result

- Add strip_output() that strips title/description without validating type (Dale #1)
- Change schema_for_output to return Arc<JsonObject> instead of Result (Dale #2)
- Cache only Arc<JsonObject> success values, not Result (Dale #3)
- Remove dead unwrap_or_else panic paths in with_output_schema, ToolBase, and macros
- Tighten test assertions from contains to assert_eq on type field (Dale #4)
- Update test_schema_for_output_rejects_primitive to accept_primitive (SEP-2106)

Co-authored-by: Orca <help@stably.ai>

* test(rmcp): add non-object output schema tests for SEP-2106

Add tests verifying schema_for_output accepts non-object types:
- test_tool_builder_methods: primitive (i32), array (Vec<String>), option
- test_structured_output: tool returning Json<Vec<T>> and Json<i32>
- test_json_schema_detection: Json<Vec<T>>, Result<Json<Vec<T>>,E>, Json<String>
- tool_traits: ToolBase::output_schema with Vec<AddOutput> output type

* test(rmcp): add missing edge case tests from code review

Add tests identified during code review:
- description stripping for primitive types
- composition types (Option<String> with anyOf/oneOf/null)
- cache correctness (Arc::ptr_eq for repeated calls)
- schema_for_input rejecting array types (not just primitives)
- schema_for_output accepting unit type ()

* feat!: mark schema_for_output return-type change as breaking

This introduces SEP-2106: schema_for_output no longer validates or
returns Result. The public signature changed, so bump major.

* fix: address Dale's PR review - direct schema.get assertions, remove ArrayTool

- Replace loose schema_str.contains(...) assertions with direct
  schema.get("type") equality checks in test_tool_builder_methods.rs
  and test_structured_output.rs
- Remove redundant ArrayTool fixture and its round-trip
  serde_json::from_str test from tool_traits.rs since schema is
  already Arc<JsonObject>
- Drop dead schema_str variable in test_structured_output.rs

---------

Co-authored-by: Brandon Bennett <brandonbennett@macbookair.myfiosgateway.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brandon Bennett <brandonbennett@Pursuits-Air.lan>
2026-07-08 14:19:28 -04:00
Dale Seo
f4ff56b81d
feat!: add MRTR model types (SEP-2322) (#915)
* feat!: add SEP-2322 MRTR model types

* feat!: remove URLElicitationRequiredError (SEP-2322)
2026-07-08 14:03:33 -04:00
actsalan
80a74795e9
fix: negotiate protocol version in handler (#930)
* fix: negotiate protocol version in handler (fixes #916)

* fix: use server pinned version as fallback in default initialize handler
2026-06-29 13:29:06 -04:00
Dale Seo
e1af378949
chore: consolidate repeated rmcp tests (#931) 2026-06-26 16:15:20 -04:00
Dale Seo
d1cabb458f
feat: deprecate roots/sampling/logging types (#923) 2026-06-25 21:11:59 -04:00
Dale Seo
77932141e3
feat!: align model types with MCP 2025-11-25 spec (#927)
* feat!: align model types with MCP 2025-11-25 spec

* fix: complete 2025-11-25 model spec conformance
2026-06-24 20:41:13 -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
Dale Seo
2536a05992
fix: update peer info on duplicate initialize (#862) 2026-06-10 15:42:09 -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
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
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
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
Dale Seo
d485249048
fix: use cfg-gated Send+Sync supertraits to avoid semver break (#757) 2026-03-18 11:37:32 -04:00
Dale Seo
1a4a52a173
feat: add local feature for !Send tool handler support (#740)
* feat: add local feature for !Send tool handler support

* fix: gate streamable HTTP transport on not(local) feature
2026-03-11 17:22:56 -04:00
EvianZhang
6c336a90c1
feat: add trait-based tool declaration (#677)
* feat: add trait-based tool declaration

* fix: typo

* fix: add docs, make more idomatic patterns, allow for empty parameters and return types

* fix: format code

* fix: add default trait

* fix: docs typo
2026-02-25 12:23:37 -05:00
Mark Wotton
92b1459647
fix(schema): remove AddNullable from draft2020_12 settings (#664)
* fix(schema): remove AddNullable from draft2020_12 settings

The `nullable` keyword is an OpenAPI 3.0 extension, not part of
JSON Schema 2020-12. Using AddNullable with draft2020_12 settings
causes validation failures with strict JSON Schema validators.

JSON Schema 2020-12 represents nullable types using:
- {"type": ["string", "null"]} (type array with null)
- {"anyOf": [{"type": "string"}, {"type": "null"}]}

Fixes #663

* test(schema): update complex schema nullable expectation

* test(schema): align macro optional-field expectations with draft2020
2026-02-19 11:19:45 -05:00
Peter
61ffba84b5
fix: sort list_all() output in ToolRouter and PromptRouter for deterministic ordering (#665)
ToolRouter::list_all() and PromptRouter::list_all() iterate over a
HashMap, which returns items in non-deterministic order. Since list_all()
backs the tools/list and prompts/list MCP protocol responses, this causes
MCP clients to receive differently-ordered results across calls and
process restarts, leading to intermittent tool discovery failures.

Sort the output alphabetically by name to guarantee stable ordering.
2026-02-17 09:37:17 -05:00
Dale Seo
08a5b0551b
fix: align task response types with MCP spec (#658) 2026-02-13 17:56:44 -05:00
Pavel Bezglasny
187597bf7e
feat(elicitation): add support URL elicitation. SEP-1036 (#605) 2026-02-07 21:39:30 -05:00
Luca Chang
df6c3f0665
fix(tasks): expose execution.taskSupport on tools (#635)
* fix(tasks): expose execution.taskSupport on tools

* feat: implement taskSupport validation on server
2026-02-03 19:17:36 -05:00
Dale Seo
9e881a645b
Implement SEP-1319: Decouple Request Payload from RPC Methods (#617)
* feat: implement SEP-1319 Decouple Request Payload from RPC Methods

* test: update tests

* fix: update handler trait methods to use new types

* fix: update examples

* fix: correct deprecation version

* fix: update wrapper macros to use new *Params type names
2026-01-16 12:16:52 -05:00
Taylor Ninesling
81f858836d
feat: provide blanket implementations for ClientHandler and ServerHandler traits (#609)
* feat!: implement ServerHandler for Box<H> and Arc<H> where H is a ServerHandler

* feat!: implement ClientHandler for Box<H> and Arc<H> where H is a ClientHandler

* test: test Box and Arc have blanket implementations for handler traits

* refactor: deduplicate blanket implementations with macros
2026-01-14 10:53:05 -05:00
jokemanfire
621c9f619e
feat(task): add task support (SEP-1686) (#536)
Signed-off-by: jokemanfire <hu.dingyang@zte.com.cn>
2025-12-22 09:01:00 -05:00
Michael Bolin
e0faf1ed51
feat: add support for custom requests (#590)
#580 and #556 introduced support for custom notifications,
so this PR takes the next logical step and adds support for custom requests:

- Introduces `CustomRequest` and `CustomResult` model types, wires them into the client/server
  request and result unions, and allows `ClientRequest::method()` to return the dynamic method
  name.
- Implements serde and meta handling for `CustomRequest` so `_meta` is carried through
  extensions; adds default `on_custom_request` handlers that return `METHOD_NOT_FOUND` unless
  overridden.
- Updates JSON schema fixtures to include the new request/result shapes and `EmptyObject`
  strictness.
- Adds tests for custom request roundtrips and end-to-end client↔server handling.
- Focused integration test in `crates/rmcp/tests/test_custom_request.rs`.

For additional testing, I used this locally to update Codex to use a custom
request instead of a custom notification so that it gets an "ack" from the MCP
server to ensure it has processed the update before sending more messages:
https://github.com/openai/codex/pull/8142.
2025-12-18 12:41:06 -05:00
Michael Bolin
2e3cc4a973
feat: add support for custom server notifications (#580)
https://github.com/modelcontextprotocol/rust-sdk/pull/556 introduced support for
custom client notifications, so this PR makes the complementary change, adding
support for custom server notifications.

MCP clients, particularly ones that offer "experimental" capabilities,
may wish to handle custom server notifications that are not part of the
standard MCP specification. This change introduces a new
`CustomServerNotification` type that allows a client to process
such custom notifications.

- introduces `CustomServerNotification` to carry arbitrary methods/params while
  still preserving meta/extensions; wires it into the `ServerNotification` union
  and `serde` so `params` can be decoded with `params_as`
- allows client handlers to receive custom notifications via a new
  `on_custom_notification` hook
- adds integration coverage that sends a custom server notification end-to-end
  and asserts the client sees the method and payload

Test:

```shell
cargo test -p rmcp --features client test_custom_server_notification_reaches_client
```
2025-12-16 12:48:03 -05:00
Dale Seo
8d33b155b6
refactor: merge cached_schema_for_type into schema_for_type (#581) 2025-12-09 20:04:52 -05:00
Dale Seo
df84555065
Implements outputSchema validation (#566)
* feat: implement output schema validation

* fix: calculator example comply MCP spec

* refactor: merge cached_schema_for_output into schema_for_output
2025-12-08 16:13:19 -05:00
Pavel Bezglasny
81411fc12d
feat(meta): add _meta field to prompts, resources and paginated result (#558) 2025-12-04 09:38:47 +08:00
Michael Bolin
4ef1a163f4
feat: add support for custom client notifications (#556)
MCP servers, particularly ones that offer "experimental" capabilities,
may wish to handle custom client notifications that are not part of the
standard MCP specification. This change introduces a new
`CustomClientNotification` type that allows a server to process
such custom notifications.

- introduces `CustomClientNotification` to carry arbitrary methods/params while
  still preserving meta/extensions; wires it into the `ClientNotification` union
  and `serde` so `params` can be decoded with `params_as`
- allows server handlers to receive custom notifications via a new
  `on_custom_notification` hook
- adds integration coverage that sends a custom client notification end-to-end
  and asserts the server sees the method and payload

Test:

```shell
cargo test -p rmcp --features client test_custom_client_notification_reaches_server
```
2025-12-01 15:58:18 -05:00
Tanish Desai
97ef6c97e4
Implementation of SEP-986: Specify Format for Tool Names (#551)
* feat: implement SEP-986 tool name validation and error reporting

Adds validation for MCP tool naming conventions as specified in SEP-986.
Ensures Rust SDK enforces standardized tool name formats, provides clear
errors for invalid names, and improves consistency across implementations.

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

* fix(doctests): correct import paths in tool_name_validation

Update doctest examples to use the correct module path
rmcp::handler::server::tool_name_validation instead of
rmcp::model::tool_name_validation.

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

* fix: only warn when warnings exist

Prevent empty warnings array from triggering warning output.

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

* fix: remove internal check

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

* refactor: remove doc comments from validation functions

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

* refactor: remove doc comments from add_route functions

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

* refactor: make tool name validation helpers private

Made ToolNameValidationResult and its functions and fields private.
Made validate_tool_name and issue_tool_name_warning private.

Signed-off-by: tanish111 <tanishdesai37@gmail.com>

---------

Signed-off-by: tanish111 <tanishdesai37@gmail.com>
2025-11-24 10:40:03 -05:00
jokemanfire
76cdf48b68
fix(shemars): use JSON Schema 2020-12 as Default Dialect (#549)
TODO: Not fully compatible with 2020-12 yet.
2025-11-19 09:55:39 +08:00
fucktx
6fbd941a32
fix:crates/rmcp/src/handler/client/progress.rs dispacher-> dispatcher (#429) 2025-09-10 17:03:35 -04:00
Andrei G
452fe2c50f
feat: implement context-aware completion (MCP 2025-06-18) (#396)
* feat: implement MCP completion specification 2025-06-18

Complete implementation of MCP completion specification with performance optimizations:

Core Features:
- Add CompletionContext for context-aware completion with previously resolved arguments
- Implement CompletionProvider trait with async support and dyn compatibility
- Create DefaultCompletionProvider with optimized fuzzy matching algorithm
- Add comprehensive validation and helper methods to CompletionInfo
- Update ServerHandler to handle completion/complete requests
- Add client convenience methods for prompt and resource completion

Performance Optimizations:
- Zero-allocation fuzzy matching using index-based scoring
- Top-k selection with select_nth_unstable instead of full sorting
- Pre-allocated vectors to avoid reallocations during matching
- Char-based case-insensitive matching to minimize string operations
- 5-8x performance improvement for large candidate sets

API Design:
- Context-aware completion supporting multi-argument scenarios
- Type-safe validation with MAX_VALUES limit (100 per MCP spec)
- Helper methods: with_all_values, with_pagination, validate
- Reference convenience methods: for_prompt, for_resource
- Client methods: complete_prompt_argument, complete_resource_argument

Testing:
- 17 comprehensive tests covering all functionality
- Schema compliance tests for MCP 2025-06-18 specification
- Performance tests with <100ms target for 1000 candidates
- Edge case and validation tests

Schema Updates:
- Add CompletionContext to JSON schema
- Update CompleteRequestParam with optional context field
- Maintain backward compatibility with existing API

* test: add comprehensive fuzzy matching tests for completion

Add three new test cases to enhance coverage of fuzzy matching algorithm:

- test_fuzzy_matching_with_typos_and_missing_chars: Tests subsequence matching
  with real-world scenarios including abbreviated patterns, case-insensitive
  matching, and complex file/package name completion

- test_fuzzy_matching_scoring_priority: Validates scoring system prioritizes
  exact matches > prefix matches > substring matches > subsequence matches

- test_fuzzy_matching_edge_cases: Covers boundary conditions including
  single character queries, oversized queries, and repeated characters

These tests ensure robust fuzzy search functionality for MCP completion
specification implementation with proper handling of user typos and
incomplete input patterns.

* feat: improve completion algorithms, add comprehensive tests and example

- Enhance fuzzy matching algorithm with acronym support for multi-word entries
- Add comprehensive scoring system for better relevance ranking
- Implement multi-level matching: exact, prefix, word prefix, acronym, substring
- Add context-aware completion scoring with proper priority ordering
- Optimize performance through efficient character-by-character matching
- Support case-insensitive acronym matching
- Improve code quality with clippy fixes and async fn syntax
- Add comprehensive test suite covering edge cases and acronym matching
- Create completion example server demonstrating weather-related prompts

* fix(test): typos

* refactor: improve completion API and replace example with SQL query builder

- Remove DefaultCompletionProvider from library core
- Move completion logic to examples following review feedback
- Update CompletionContext.argument_names() to return Iterator for better performance
- Replace tech search example with SQL query builder demonstrating progressive completion
- Add context-aware completion that adapts based on filled arguments
- Use proper Option types for optional SQL fields (columns, where_clause, values)
- Demonstrate real-world value of argument_names() method for dynamic completion flow

The SQL query builder showcases:
• Progressive field availability based on operation type
• Context validation using argument_names()
• Proper Optional field handling
• Smart completion that guides user through multi-step form

* fix: fmt
2025-09-08 14:44:49 +08:00
4t145
1b70f5b032
feat: add title field for data types (#410)
* feat(model): add title field to various structs

* fix(test): fix test json schema

* fix(model): allow boxed type  in `ts_union` macro
2025-09-08 09:15:22 +08:00
unship
eb46b4ce3b
refactor: simplify remove_route method signature (#401) 2025-08-29 18:20:03 +08:00
Michael Assaf
9349f5cb26
feat: Add prompt support (#351)
* feat: add prompt support with typed argument handling

- Implement #[prompt], #[prompt_router], and #[prompt_handler] macros
- Add automatic JSON schema generation from Rust types for arguments
- Support flexible async handler signatures with automatic adaptation
- Create PromptRouter for efficient prompt dispatch
- Include comprehensive tests and example implementation

This enables MCP servers to provide reusable prompt templates that
LLMs can discover and invoke with strongly-typed parameters, similar
to the existing tool system but optimized for prompt use cases.

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: unify parameter handling between tools and prompts

- Replace Arguments<T> with Parameters<T> for consistent API
- Create shared common module for tool/prompt utilities
- Modernize async handling with futures::future::BoxFuture
- Move cached_schema_for_type to common module for reuse
- Update error types from rmcp::Error to rmcp::ErrorData
- Add comprehensive trait implementations for parameter extraction

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>

* refactor: extract Parameters wrapper to shared module and unify trait usage

- Move Parameters type from common.rs to dedicated wrapper/parameters.rs module
- Unify extractor traits by using FromContextPart for both tools and prompts
- Remove duplicate FromToolCallContextPart and FromPromptContextPart traits
- Add structured output support to Json wrapper with IntoCallToolResult impl
- Improve error messages with more descriptive panic for schema serialization
- Update all imports across codebase to use new module path
- Clean up trailing whitespace and formatting inconsistencies

This consolidates parameter extraction logic and reduces code duplication
between tool and prompt handlers while maintaining backward compatibility.

* chore: remove committed .egg-info directory and update gitignore

* docs: fix documentation formatting
2025-08-21 11:22:19 +08:00
Andrei G
81ff3159f9
feat: Add MCP Elicitation support (#332)
* feat: implement MCP elicitation support for interactive user input

Adds comprehensive elicitation functionality according to MCP 2025-06-18 specification:

Core Features:
- ElicitationAction enum (Accept, Decline, Cancel)
- CreateElicitationRequestParam and CreateElicitationResult structures
- Protocol version V_2025_06_18 with elicitation methods
- Full JSON-RPC integration with method constants

Capabilities Integration:
- ElicitationCapability with schema validation support
- ClientCapabilities builder pattern integration
- enable_elicitation() and enable_elicitation_schema_validation() methods

Handler Support:
- create_elicitation method in ClientHandler and ServerHandler traits
- Integration with existing request/response union types
- Async/await compatible implementation

Service Layer:
- Basic create_elicitation method via macro expansion
- Four convenience methods for common scenarios:
  * elicit_confirmation() - yes/no questions
  * elicit_text_input() - string input with optional requirements
  * elicit_choice() - selection from multiple options
  * elicit_structured_input() - complex data via JSON Schema

Comprehensive Testing:
- 11 test cases covering all functionality aspects
- JSON serialization/deserialization validation
- MCP specification compliance verification
- Error handling and edge cases
- Performance benchmarks
- Capabilities integration tests

All tests pass and code follows project standards.

* feat: add typed elicitation API with enhanced error handling

- Add new 'elicitation' feature that depends on 'client' and 'schemars'
- Implement elicit<T>() method for type-safe elicitation with automatic schema generation
- Remove convenience methods (elicit_confirmation, elicit_text_input, elicit_choice)
- Add ElicitationError enum with detailed error variants:
  - Service: underlying service errors
  - UserDeclined: user cancelled or declined request
  - ParseError: response parsing failed with context
  - NoContent: no response content provided
- Update documentation with comprehensive examples and error handling
- Add comprehensive tests for typed elicitation and error handling

* fix: correct elicitation direction to comply with MCP 2025-06-18

- Remove CreateElicitationRequest from ClientRequest - clients cannot initiate elicitation
- Move elicit methods from client to server - servers now request user input
- Add comprehensive direction tests verifying Server→Client→Server flow
- Maintain CreateElicitationResult in ClientResult for proper responses
- Update handlers to reflect correct message routing
- Add elicitation feature flag for typed schema generation

Fixes elicitation direction to match specification where servers request
interactive user input from clients, not the reverse.

* feat: add elicitation capability checking for server methods

- Add supports_elicitation() method to check client capabilities
- Add CapabilityNotSupported error variant to ElicitationError
- Update elicit_structured_input() to check capabilities before execution
- Update elicit<T>() method to check capabilities before execution
- Add comprehensive tests for capability checking functionality
- Tests verify that servers check client capabilities before sending elicitation requests
- Ensures compliance with MCP 2025-06-18 specification requirement

* fix: json rpc message schema

* fix: doc tests

* fix: cargo nightly fmt checks

* fix: clippy

* refactor: separate elicitation methods into dedicated impl block for RoleServer

- Move (supports_elicitation, elicit_structured_input, elicit) to separate impl block
- Move ElicitationError definition to elicitation methods section
- Keep base methods (create_message, list_roots, notify_*) in main impl block with macro
- Add section comments to distinguish general and elicitation-specific methods

* revert: rollback LATEST protocol version to V_2025_03_26

* fix: remove protocol version assertions
- Remove assertions for V_2025_06_18 protocol version

* fix: fmt checks

* feat: add timeout support for elicitation methods

- Add peer_req_with_timeout macro variants for timeout-enabled methods
- Implement create_elicitation_with_timeout() method
- Implement elicit_with_timeout() for typed elicitation with timeout
- Refactor elicit() to use elicit_with_timeout() internally
- Add 8 comprehensive timeout tests covering validation, error handling, and realistic scenarios
- Fix elicitation feature dependencies in Cargo.toml
- Add proper feature gates for elicitation-specific code

* feat: add timeout validation to prevent DoS attacks

- Add InvalidTimeout error variant for comprehensive validation
- Implement validate_timeout function with security limits (1ms-300s)
- Integrate validation into peer_req_with_timeout macros
- Add comprehensive security tests for timeout validation
- Prevent DoS attacks through unreasonable timeout values

* feat: separate UserDeclined and UserCancelled elicitation errors

According to MCP specification and PR feedback, decline and cancel
actions should be handled differently:

- UserDeclined: explicit user rejection (clicked "Decline", "No", etc.)
- UserCancelled: dismissal without explicit choice (closed dialog, Escape, etc.)

Changes:
- Split ElicitationError::UserDeclined into two distinct error types
- Update error handling logic to map each ElicitationAction correctly
- Improve documentation with proper action semantics
- Add comprehensive tests for new error types and action mapping
- Update examples to demonstrate proper error handling

This provides better error granularity allowing servers to handle
explicit declines vs cancellations appropriately as per MCP spec.

* feat: add compile-time type safety for elicitation methods

Add ElicitationSafe trait and elicit_safe\! macro to ensure elicit<T>()
methods are only used with types that generate appropriate JSON object
schemas, addressing type safety concerns from PR feedback.

Features:
- ElicitationSafe marker trait for compile-time constraints
- elicit_safe\! macro for opt-in type safety declaration
- Updated elicit<T> and elicit_with_timeout<T> to require ElicitationSafe bound
- Comprehensive documentation with examples and rationale
- Full test coverage for new type safety features

This prevents common mistakes like:
- elicit::<String>() - primitives not suitable for object schemas
- elicit::<Vec<i32>>() - arrays don't match client expectations

Breaking change: Existing code must add elicit_safe\!(TypeName) declarations
for types used with elicit methods. This is an intentional safety improvement.

* Revert "feat: add timeout validation to prevent DoS attacks"

This reverts commit 829624212b4fb55ec32566329af7ec195c987ef5.

* fix: correct doctest example in elicit_safe macro documentation

- Remove invalid async/await usage in doctest example
- Comment out the actual usage line to show intent without compilation errors
- Maintain clear documentation of the macro's purpose and usage

* refactor: remove redundant elicitation direction tests

- Remove test_elicitation_not_in_client_request (duplicated functionality)
- Remove redundant ServerRequest match in test_elicitation_direction_server_to_client
- Direction compliance is already verified by the remaining comprehensive test
- Reduces test fragility and maintenance burden

* feat: add elicitation example with user name collection

- Add elicitation server example demonstrating real MCP usage
- Implement greet_user tool with context.peer.elicit::<T>() API
- Show type-safe elicitation with elicit_safe! macro
- Include reset_name tool and MCP Inspector instructions
- Update examples documentation and dependencies

* fix: add Default impl to ElicitationServer for clippy

Resolves clippy::new_without_default warning by implementing
Default trait for ElicitationServer struct.
2025-08-19 11:48:26 +08:00
4t145
1555c0fa33
fix(tool): remove unnecessary schema validation (#375) 2025-08-15 11:12:37 +08:00
Jean-Marc Le Roux
fbc7ab70ca
feat: Add support for Tool.outputSchema and CallToolResult.structuredContent (#316)
* feat: add output_schema field to Tool struct

- Add optional output_schema field to Tool struct for defining tool
output structure
- Update Tool::new() to initialize output_schema as None

* feat: add structured_content field to CallToolResult

- Add optional structured_content field for JSON object results
- Make content field optional to support either structured or
unstructured results
- Add CallToolResult::structured() and structured_error() constructor
methods

* feat: implement validation for mutually exclusive content/structuredContent

- Add validate() method to ensure content and structured_content are
mutually exclusive
- Implement custom Deserialize to enforce validation during
deserialization
- Update documentation to clarify the mutual exclusivity requirement

* feat: add output_schema support to #[tool] macro

- Add output_schema field to ToolAttribute and ResolvedToolAttribute
structs
- Implement automatic output schema generation from return types
- Support explicit output_schema attribute for manual specification
- Generate schemas for Result<T, E> where T is not CallToolResult
- Update tool generation to include output_schema in Tool struct

* feat: implement IntoCallToolResult for structured content

- Add Structured<T> wrapper type for explicit structured content
- Implement IntoCallToolResult for Structured<T> with JSON serialization
- Add support for Result<Structured<T>, E> conversions
- Enable tools to return structured content through the trait system

* fix: update simple-chat-client example for optional content field

- Handle Option<Vec<Content>> in CallToolResult.content
- Add proper unwrapping for the optional content field
- Fix compilation error in chat.rs

* fix: update examples and tests for optional content field

- Add output_schema field to Tool initialization in sampling_stdio
example
- Update test_tool_macros tests to handle Option<Vec<Content>>
- Use as_ref() before calling first() on optional content field

* feat: implement basic schema validation in conversion logic

- Add validate_against_schema function for basic type validation
- Add note that full JSON Schema validation requires dedicated library
- Document that actual validation should happen in tool handler

* feat: add structured output support for tools

- Add output_schema field to Tool struct for defining output JSON schemas
- Add structured_content field to CallToolResult (mutually exclusive with content)
- Implement Structured<T> wrapper for type-safe structured outputs
- Update #[tool] macro to automatically generate output schemas from return types
- Add validation of structured outputs against their schemas
- Update all examples and tests for breaking change (CallToolResult.content now Option)
- Add comprehensive documentation and rustdoc
- Add structured_output example demonstrating the feature

BREAKING CHANGE: CallToolResult.content is now Option<Vec<Content>> instead of Vec<Content>

Closes #312

* fix: correct structured output doctest to use Parameters wrapper

The #[tool] macro requires Parameters<T> wrapper for tool inputs.
This fixes the pre-existing broken doctest in the structured output
documentation example.

* feat: replace Structured<T> with Json<T> for structured output

- Remove Structured<T> type definition and implementations
- Reuse existing Json<T> wrapper for structured content
- Update IntoCallToolResult implementations to use Json<T>
- Add JsonSchema implementation for Json<T> delegating to T
- Update all examples and tests to use Json<T> instead of Structured<T>
- Update documentation and exports

BREAKING CHANGE: Structured<T> has been replaced with Json<T>. Users must update their code to use Json<T> for structured tool outputs.

* feat: add output_schema() method to IntoCallToolResult trait

- Add output_schema() method with default None implementation
- Implement output_schema() for Json<T> to return cached schema
- Implement output_schema() for Result<Json<T>, E> delegating to Json<T>
- Enable trait-based schema generation for structured outputs

* feat: update macro to detect Json<T> wrapper for output schemas

- Add extract_json_inner_type() helper to detect Json<T> types
- Update schema generation to only occur for Json<T> wrapped types
- Remove generic Result<T, E> detection in favor of specific Json<T> detection
- Add comprehensive tests to verify schema generation behavior

* feat: add builder methods to Tool struct for setting schemas

- Add with_output_schema<T>() method to set output schema from type
- Add with_input_schema<T>() method to set input schema from type
- Both methods use cached_schema_for_type internally
- Add comprehensive tests for builder methods

* fix: address clippy warnings

- Add Default implementation for StructuredOutputServer
- Fix collapsible else-if in simple-chat-client
- No functional changes

* style: apply cargo fmt

Apply automatic formatting changes to:
- examples/simple-chat-client/src/chat.rs - fix line wrapping
- crates/rmcp-macros/src/tool.rs - format method chaining
- examples/servers/src/structured_output.rs - reorder imports and format function signatures

* chore: fix formatting

* chore: fix rustdoc redundant link warning

* refactor: validate_against_schema

* feat: enforce structured_content usage when output_schema is defined

This commit implements strict validation to ensure tools with output_schema
consistently use structured_content for both success and error responses.

Changes:
- Enhanced ToolRouter::call() validation to require structured_content when output_schema is present
- Added validation that tools with output_schema cannot use regular content field
- Added comprehensive tests covering the new strict validation behavior
- Created example demonstrating proper structured output usage
- Updated TODO.md to track validation improvements

This ensures consistent response format and better type safety for MCP clients.

* chore: remove TODO.md

* refactor: simplify output schema extraction logic in tool macro

- Extract complex nested logic into dedicated helper function
- Replace deeply nested if-else chains with functional approach
- Use early returns and ? operator for cleaner code flow
- Reduce 46 lines to 7 lines in main logic while improving readability

* chore: run cargo fmt

* fix: enforce structured_content usage when output_schema is defined

Structured content is returned as a JSON object in the structuredContent
field of a result.For backwards compatibility, a tool that returns
structured content SHOULD also return the serialized JSON in a
TextContent block.

https://modelcontextprotocol.io/specification/2025-06-18/server/tools#structured-content

Tools may also provide an output schema for validation of structured
results. If an output schema is provided:

- Servers MUST provide structured results that conform to this schema.
- Clients SHOULD validate structured results against this schema.

https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema

* chore: cargo fmt
2025-07-31 16:32:24 +08:00