| Filename | Latest commit message | Latest commit date |
|---|---|---|
* 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. |
||
| .. | ||
| src | ||
| templates | ||
| Cargo.toml | ||
| README.md | ||
MCP Server Examples
This directory contains Model Context Protocol (MCP) server examples implemented in Rust. These examples demonstrate how to create MCP servers using different transport methods and how to implement various server capabilities including tools, resources, prompts, and authentication.
Example List
Counter Standard I/O Server (counter_stdio.rs)
A basic MCP server that communicates using standard input/output transport.
- Provides a simple counter tool with increment, decrement, and get_value operations
- Demonstrates basic tool implementation and stdio transport
Memory Standard I/O Server (memory_stdio.rs)
A minimal server example using stdio transport.
- Lightweight server implementation
- Demonstrates basic server setup patterns
- Good starting point for custom server development
Counter Streamable HTTP Server (counter_streamhttp.rs)
A server using streamable HTTP transport for MCP communication, with axum.
- Runs on HTTP with streaming capabilities
- Provides counter tools via HTTP streaming
- Demonstrates streamable HTTP transport configuration
Modern Subscription Server (subscriptions_streamhttp.rs)
A stateless 2026-07-28 server that opens subscriptions/listen response
streams, acknowledges the accepted filter, and emits tagged tool-list changes.
- Run with
cargo run -p mcp-server-examples --example servers_subscriptions_streamhttp
Counter Streamable HTTP Server with Hyper (counter_hyper_streamable_http.rs)
A server using streamable HTTP transport for MCP communication, with hyper.
- Runs on HTTP with streaming capabilities
- Provides counter tools via HTTP streaming
- Demonstrates streamable HTTP transport configuration
Elicitation Demo (elicitation_stdio.rs)
A working MCP server demonstrating elicitation for user name collection.
- Real MCP server using rmcp library
context.peer.elicit::<T>()API usage- Type-safe elicitation with
elicit_safe!macro - JSON Schema validation with schemars
- Tools:
greet_user(collects name),reset_name(clears stored name)
Elicitation with Enum Inference config (elicitation_enum_inference.rs)
A demonstration of elicitation using enum inference configuration for MCP server.
- Runs on HTTP with streaming capabilities
- Uses schemars for elicitation schema generation
- Shows how to configure enum inference for elicitation prompts
Prompt Standard I/O Server (prompt_stdio.rs)
A server demonstrating the prompt framework capabilities.
- Shows how to implement prompts in MCP servers
- Provides code review and debugging prompts
- Demonstrates prompt argument handling with JSON schema
- Uses standard I/O transport
- Good example of prompt implementation patterns
Task Demo Server (task_stdio.rs)
A minimal stdio server demonstrating the MCP Tasks extension
(SEP-2663, io.modelcontextprotocol/tasks).
slow_sumis materialized as a task (CreateTaskResult,resultType: "task") whenever the client declares the tasks extension capability; other clients get a normal synchronous responsequick_echois a regular synchronous tool for contrast- Serves
tasks/get/tasks/update/tasks/cancelvia aTaskManager - Pair with
examples/clients/src/task_stdio.rsto see the full lifecycle (create → poll → inline result)
MRTR Demo (mrtr.rs)
An end-to-end walkthrough of SEP-2322 Multi Round-Trip Requests, running a server and client in one process over an in-memory stream.
- Server answers
tools/callwith anInputRequiredResultasking the client to elicit a value - Client uses
call_toolto auto-fulfil the elicitation and retry, thencall_tool_oncefor manual control - Seals/opens the untrusted
requestStatewithRequestStateCodec(HMAC integrity) - Both sides negotiate
2026-07-28, the minimum version for MRTR - Run with
cargo run -p mcp-server-examples --example servers_mrtr
Progress Demo Server (progress_demo.rs)
A server that demonstrates progress notifications during long-running operations.
- Provides a stream_processor tool that generates progress notifications
- Demonstrates progress notifications during long-running operations
- Can be run with
cargo run -p mcp-server-examples --example servers_progress_demo -- {stdio|http|all}
Simple Auth Streamable HTTP Server (simple_auth_streamhttp.rs)
A server demonstrating simple token-based authentication with streamable HTTP transport.
- Uses bearer token authentication via Authorization header
- Provides
/api/token/{id}endpoint to get demo tokens - Protected MCP endpoint at
/mcp - Shows how to add auth middleware to streamable HTTP services
Complex Auth Streamable HTTP Server (complex_auth_streamhttp.rs)
A full OAuth 2.0 authorization server implementation with streamable HTTP MCP transport.
- Complete OAuth 2.0 authorization code flow
- Client registration endpoint
- Authorization server metadata discovery
- Protected MCP endpoint with token validation
- Demonstrates building a production-like auth server
How to Run
Each example can be run using Cargo:
# Run the counter standard I/O server
cargo run -p mcp-server-examples --example servers_counter_stdio
# Run the memory standard I/O server
cargo run -p mcp-server-examples --example servers_memory_stdio
# Run the counter streamable HTTP server
cargo run -p mcp-server-examples --example servers_counter_streamhttp
# Run the elicitation standard I/O server
cargo run -p mcp-server-examples --example servers_elicitation_stdio
# Run the prompt standard I/O server
cargo run -p mcp-server-examples --example servers_prompt_stdio
# Run the simple auth streamable HTTP server
cargo run -p mcp-server-examples --example servers_simple_auth_streamhttp
# Run the complex auth streamable HTTP server
cargo run -p mcp-server-examples --example servers_complex_auth_streamhttp
Testing with MCP Inspector
Many of these servers can be tested using the MCP Inspector tool: See inspector
Dependencies
These examples use the following main dependencies:
rmcp: Rust implementation of the MCP server librarytokio: Asynchronous runtimeserdeandserde_json: For JSON serialization and deserializationtracingandtracing-subscriber: For logginganyhow: Error handlingaxum: Web framework for HTTP-based transportstokio-util: Utilities for async programmingschemars: JSON Schema generation (used in elicitation examples)
Common Module
The common/ directory contains shared code used across examples:
counter.rs: Counter tool implementation with MCP server traitscalculator.rs: Calculator tool examplesgeneric_service.rs: Generic service implementations
This modular approach allows for code reuse and demonstrates how to structure larger MCP server applications.