{"id":25939026,"url":"https://github.com/modelcontextprotocol/rust-sdk","last_synced_at":"2026-05-13T15:03:17.791Z","repository":{"id":278815043,"uuid":"934753959","full_name":"modelcontextprotocol/rust-sdk","owner":"modelcontextprotocol","description":"The official Rust SDK for the Model Context Protocol","archived":false,"fork":false,"pushed_at":"2025-05-07T07:22:24.000Z","size":209,"stargazers_count":993,"open_issues_count":35,"forks_count":140,"subscribers_count":19,"default_branch":"main","last_synced_at":"2025-05-07T08:29:05.309Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/modelcontextprotocol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-18T10:55:26.000Z","updated_at":"2025-05-07T07:22:28.000Z","dependencies_parsed_at":null,"dependency_job_id":"0599bc95-8196-465e-8854-700783748ba5","html_url":"https://github.com/modelcontextprotocol/rust-sdk","commit_stats":null,"previous_names":["modelcontextprotocol/rust-sdk"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelcontextprotocol%2Frust-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelcontextprotocol%2Frust-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelcontextprotocol%2Frust-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/modelcontextprotocol%2Frust-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/modelcontextprotocol","download_url":"https://codeload.github.com/modelcontextprotocol/rust-sdk/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254053178,"owners_count":22006717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-04T04:02:19.885Z","updated_at":"2026-05-13T15:03:17.781Z","avatar_url":"https://github.com/modelcontextprotocol.png","language":"Rust","funding_links":[],"categories":["SDKs","Rust","📚 Projects (1974 total)","MCP Ecosystem","Mcp Server Directories \u0026 Lists","Data Science","📦 Other","MCP Frameworks and libraries","MCP Servers \u0026 Protocol"],"sub_categories":["Official","MCP Servers","Core \u0026 Frameworks","Machine Learning","Rust","Codex Resources"],"readme":"\u003cdiv align = \"right\"\u003e\n\u003ca href=\"docs/readme/README.zh-cn.md\"\u003e简体中文\u003c/a\u003e\n\u003c/div\u003e\n\n# RMCP\n[![Crates.io Version](https://img.shields.io/crates/v/rmcp)](https://crates.io/crates/rmcp)\n[![docs.rs](https://img.shields.io/docsrs/rmcp)](https://docs.rs/rmcp/latest/rmcp)\n[![CI](https://github.com/modelcontextprotocol/rust-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/modelcontextprotocol/rust-sdk/actions/workflows/ci.yml)\n[![License](https://img.shields.io/crates/l/rmcp)](LICENSE)\n\nAn official Rust Model Context Protocol SDK implementation with tokio async runtime.\n\n\u003e **Migrating to 1.x?** See the [migration guide](https://github.com/modelcontextprotocol/rust-sdk/discussions/716) for breaking changes and upgrade instructions.\n\nThis repository contains the following crates:\n\n- [rmcp](crates/rmcp): The core crate providing the RMCP protocol implementation - see [rmcp](crates/rmcp/README.md)\n- [rmcp-macros](crates/rmcp-macros): A procedural macro crate for generating RMCP tool implementations - see [rmcp-macros](crates/rmcp-macros/README.md)\n\nFor the full MCP specification, see [modelcontextprotocol.io](https://modelcontextprotocol.io/specification/2025-11-25).\n\n## Table of Contents\n\n- [Usage](#usage)\n- [Tools](#tools)\n- [Resources](#resources)\n- [Prompts](#prompts)\n- [Sampling](#sampling)\n- [Roots](#roots)\n- [Logging](#logging)\n- [Completions](#completions)\n- [Notifications](#notifications)\n- [Subscriptions](#subscriptions)\n- [Tasks](#tasks-long-running-tool-invocations)\n- [Examples](#examples)\n- [OAuth Support](#oauth-support)\n- [Related Resources](#related-resources)\n- [Related Projects](#related-projects)\n- [Development](#development)\n\n## Usage\n\n### Import the crate\n\n```toml\nrmcp = { version = \"0.16.0\", features = [\"server\"] }\n## or dev channel\nrmcp = { git = \"https://github.com/modelcontextprotocol/rust-sdk\", branch = \"main\" }\n```\n### Third Dependencies\n\nBasic dependencies:\n- [tokio](https://github.com/tokio-rs/tokio)\n- [serde](https://github.com/serde-rs/serde)\nJson Schema generation (version 2020-12):\n- [schemars](https://github.com/GREsau/schemars)\n\n### Build a Client\n\n\u003cdetails\u003e\n\u003csummary\u003eStart a client\u003c/summary\u003e\n\n```rust, ignore\nuse rmcp::{ServiceExt, transport::{TokioChildProcess, ConfigureCommandExt}};\nuse tokio::process::Command;\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let client = ().serve(TokioChildProcess::new(Command::new(\"npx\").configure(|cmd| {\n        cmd.arg(\"-y\").arg(\"@modelcontextprotocol/server-everything\");\n    }))?).await?;\n    Ok(())\n}\n```\n\u003c/details\u003e\n\n### Build a Server\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild a transport\u003c/summary\u003e\n\n```rust, ignore\nuse tokio::io::{stdin, stdout};\nlet transport = (stdin(), stdout());\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild a service\u003c/summary\u003e\n\nYou can easily build a service by using [`ServerHandler`](crates/rmcp/src/handler/server.rs) or [`ClientHandler`](crates/rmcp/src/handler/client.rs).\n\n```rust, ignore\nlet service = common::counter::Counter::new();\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eStart the server\u003c/summary\u003e\n\n```rust, ignore\n// this call will finish the initialization process\nlet server = service.serve(transport).await?;\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eInteract with the server\u003c/summary\u003e\n\nOnce the server is initialized, you can send requests or notifications:\n\n```rust, ignore\n// request\nlet roots = server.list_roots().await?;\n\n// or send notification\nserver.notify_cancelled(...).await?;\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWaiting for service shutdown\u003c/summary\u003e\n\n```rust, ignore\nlet quit_reason = server.waiting().await?;\n// or cancel it\nlet quit_reason = server.cancel().await?;\n```\n\u003c/details\u003e\n\n---\n\n## Tools\n\nTools let servers expose callable functions to clients. Each tool has a name, description, and a JSON Schema for its parameters. Clients discover tools via `list_tools` and invoke them via `call_tool`.\n\n**MCP Spec:** [Tools](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)\n\n### Server-side\n\nThe `#[tool]`, `#[tool_router]`, and `#[tool_handler]` macros handle all the wiring. For a tools-only server you can use `#[tool_router(server_handler)]` to skip the separate `ServerHandler` impl:\n\n```rust,ignore\nuse rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router, ServiceExt, transport::stdio};\n\n#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]\nstruct AddParams {\n    a: i32,\n    b: i32,\n}\n\n#[derive(Clone)]\nstruct Calculator;\n\n#[tool_router(server_handler)]\nimpl Calculator {\n    #[tool(description = \"Add two numbers\")]\n    fn add(\u0026self, Parameters(AddParams { a, b }): Parameters\u003cAddParams\u003e) -\u003e String {\n        (a + b).to_string()\n    }\n}\n\n#[tokio::main]\nasync fn main() -\u003e anyhow::Result\u003c()\u003e {\n    let service = Calculator.serve(stdio()).await?;\n    service.waiting().await?;\n    Ok(())\n}\n```\n\nWhen you need custom server metadata or multiple capabilities (tools + prompts), use explicit `#[tool_handler]`:\n\n```rust,ignore\nuse rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router, tool_handler, ServerHandler, ServiceExt};\n\n#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]\nstruct AddParams {\n    a: i32,\n    b: i32,\n}\n\n#[derive(Clone)]\nstruct Calculator;\n\n#[tool_router]\nimpl Calculator {\n    #[tool(description = \"Add two numbers\")]\n    fn add(\u0026self, Parameters(AddParams { a, b }): Parameters\u003cAddParams\u003e) -\u003e String {\n        (a + b).to_string()\n    }\n}\n\n#[tool_handler(name = \"calculator\", version = \"1.0.0\", instructions = \"A simple calculator\")]\nimpl ServerHandler for Calculator {}\n```\n\nSee [`crates/rmcp-macros`](crates/rmcp-macros/README.md) for full macro documentation.\n\n### Client-side\n\n```rust,ignore\nuse rmcp::model::CallToolRequestParams;\n\n// List all tools\nlet tools = client.list_all_tools().await?;\n\n// Call a tool by name\nlet result = client.call_tool(CallToolRequestParams::new(\"add\")).await?;\n```\n\n**Example:** [`examples/servers/src/common/calculator.rs`](examples/servers/src/common/calculator.rs) (server), [`examples/servers/src/calculator_stdio.rs`](examples/servers/src/calculator_stdio.rs) (stdio runner)\n\n---\n\n## Resources\n\nResources let servers expose data (files, database records, API responses) that clients can read. Each resource is identified by a URI and returns content as text or binary (base64-encoded) data. Resource templates allow servers to declare URI patterns with dynamic parameters.\n\n**MCP Spec:** [Resources](https://modelcontextprotocol.io/specification/2025-11-25/server/resources)\n\n### Server-side\n\nImplement `list_resources()`, `read_resource()`, and optionally `list_resource_templates()` on the `ServerHandler` trait. Enable the resources capability in `get_info()`.\n\n```rust\nuse rmcp::{\n    ErrorData as McpError, RoleServer, ServerHandler, ServiceExt,\n    model::*,\n    service::RequestContext,\n    transport::stdio,\n};\nuse serde_json::json;\n\n#[derive(Clone)]\nstruct MyServer;\n\nimpl ServerHandler for MyServer {\n    fn get_info(\u0026self) -\u003e ServerInfo {\n        ServerInfo {\n            capabilities: ServerCapabilities::builder()\n                .enable_resources()\n                .build(),\n            ..Default::default()\n        }\n    }\n\n    async fn list_resources(\n        \u0026self,\n        _request: Option\u003cPaginatedRequestParams\u003e,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cListResourcesResult, McpError\u003e {\n        Ok(ListResourcesResult {\n            resources: vec![\n                RawResource::new(\"file:///config.json\", \"config\").no_annotation(),\n                RawResource::new(\"memo://insights\", \"insights\").no_annotation(),\n            ],\n            next_cursor: None,\n            meta: None,\n        })\n    }\n\n    async fn read_resource(\n        \u0026self,\n        request: ReadResourceRequestParams,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cReadResourceResult, McpError\u003e {\n        match request.uri.as_str() {\n            \"file:///config.json\" =\u003e Ok(ReadResourceResult {\n                contents: vec![ResourceContents::text(r#\"{\"key\": \"value\"}\"#, \u0026request.uri)],\n            }),\n            \"memo://insights\" =\u003e Ok(ReadResourceResult {\n                contents: vec![ResourceContents::text(\"Analysis results...\", \u0026request.uri)],\n            }),\n            _ =\u003e Err(McpError::resource_not_found(\n                \"resource_not_found\",\n                Some(json!({ \"uri\": request.uri })),\n            )),\n        }\n    }\n\n    async fn list_resource_templates(\n        \u0026self,\n        _request: Option\u003cPaginatedRequestParams\u003e,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cListResourceTemplatesResult, McpError\u003e {\n        Ok(ListResourceTemplatesResult {\n            resource_templates: vec![],\n            next_cursor: None,\n            meta: None,\n        })\n    }\n}\n```\n\n### Client-side\n\n```rust\nuse rmcp::model::{ReadResourceRequestParams};\n\n// List all resources (handles pagination automatically)\nlet resources = client.list_all_resources().await?;\n\n// Read a specific resource by URI\nlet result = client.read_resource(ReadResourceRequestParams {\n    meta: None,\n    uri: \"file:///config.json\".into(),\n}).await?;\n\n// List resource templates\nlet templates = client.list_all_resource_templates().await?;\n```\n\n### Notifications\n\nServers can notify clients when the resource list changes or when a specific resource is updated:\n\n```rust\n// Notify that the resource list has changed (clients should re-fetch)\ncontext.peer.notify_resource_list_changed().await?;\n\n// Notify that a specific resource was updated\ncontext.peer.notify_resource_updated(ResourceUpdatedNotificationParam {\n    uri: \"file:///config.json\".into(),\n}).await?;\n```\n\nClients handle these via `ClientHandler`:\n\n```rust\nimpl ClientHandler for MyClient {\n    async fn on_resource_list_changed(\n        \u0026self,\n        _context: NotificationContext\u003cRoleClient\u003e,\n    ) {\n        // Re-fetch the resource list\n    }\n\n    async fn on_resource_updated(\n        \u0026self,\n        params: ResourceUpdatedNotificationParam,\n        _context: NotificationContext\u003cRoleClient\u003e,\n    ) {\n        // Re-read the updated resource at params.uri\n    }\n}\n```\n\n**Example:** [`examples/servers/src/common/counter.rs`](examples/servers/src/common/counter.rs) (server), [`examples/clients/src/everything_stdio.rs`](examples/clients/src/everything_stdio.rs) (client)\n\n---\n\n## Prompts\n\nPrompts are reusable message templates that servers expose to clients. They accept typed arguments and return conversation messages. The `#[prompt]` macro handles argument validation and routing automatically.\n\n**MCP Spec:** [Prompts](https://modelcontextprotocol.io/specification/2025-11-25/server/prompts)\n\n### Server-side\n\nUse the `#[prompt_router]`, `#[prompt]`, and `#[prompt_handler]` macros to define prompts declaratively. Arguments are defined as structs deriving `JsonSchema`.\n\n```rust\nuse rmcp::{\n    ErrorData as McpError, RoleServer, ServerHandler, ServiceExt,\n    handler::server::{router::prompt::PromptRouter, wrapper::Parameters},\n    model::*,\n    prompt, prompt_handler, prompt_router,\n    schemars::JsonSchema,\n    service::RequestContext,\n    transport::stdio,\n};\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug, Serialize, Deserialize, JsonSchema)]\npub struct CodeReviewArgs {\n    #[schemars(description = \"Programming language of the code\")]\n    pub language: String,\n    #[schemars(description = \"Focus areas for the review\")]\n    pub focus_areas: Option\u003cVec\u003cString\u003e\u003e,\n}\n\n#[derive(Clone)]\npub struct MyServer {\n    prompt_router: PromptRouter\u003cSelf\u003e,\n}\n\n#[prompt_router]\nimpl MyServer {\n    fn new() -\u003e Self {\n        Self { prompt_router: Self::prompt_router() }\n    }\n\n    /// Simple prompt without parameters\n    #[prompt(name = \"greeting\", description = \"A simple greeting\")]\n    async fn greeting(\u0026self) -\u003e Vec\u003cPromptMessage\u003e {\n        vec![PromptMessage::new_text(\n            PromptMessageRole::User,\n            \"Hello! How can you help me today?\",\n        )]\n    }\n\n    /// Prompt with typed arguments\n    #[prompt(name = \"code_review\", description = \"Review code in a given language\")]\n    async fn code_review(\n        \u0026self,\n        Parameters(args): Parameters\u003cCodeReviewArgs\u003e,\n    ) -\u003e Result\u003cGetPromptResult, McpError\u003e {\n        let focus = args.focus_areas\n            .unwrap_or_else(|| vec![\"correctness\".into()]);\n\n        Ok(GetPromptResult {\n            description: Some(format!(\"Code review for {}\", args.language)),\n            messages: vec![\n                PromptMessage::new_text(\n                    PromptMessageRole::User,\n                    format!(\"Review my {} code. Focus on: {}\", args.language, focus.join(\", \")),\n                ),\n            ],\n        })\n    }\n}\n\n#[prompt_handler]\nimpl ServerHandler for MyServer {\n    fn get_info(\u0026self) -\u003e ServerInfo {\n        ServerInfo {\n            capabilities: ServerCapabilities::builder().enable_prompts().build(),\n            ..Default::default()\n        }\n    }\n}\n```\n\nPrompt functions support several return types:\n- `Vec\u003cPromptMessage\u003e` -- simple message list\n- `GetPromptResult` -- messages with an optional description\n- `Result\u003cT, McpError\u003e` -- either of the above, with error handling\n\n### Client-side\n\n```rust\nuse rmcp::model::GetPromptRequestParams;\n\n// List all prompts\nlet prompts = client.list_all_prompts().await?;\n\n// Get a prompt with arguments\nlet result = client.get_prompt(GetPromptRequestParams {\n    meta: None,\n    name: \"code_review\".into(),\n    arguments: Some(rmcp::object!({\n        \"language\": \"Rust\",\n        \"focus_areas\": [\"performance\", \"safety\"]\n    })),\n}).await?;\n```\n\n### Notifications\n\n```rust\n// Server: notify that available prompts have changed\ncontext.peer.notify_prompt_list_changed().await?;\n```\n\n**Example:** [`examples/servers/src/prompt_stdio.rs`](examples/servers/src/prompt_stdio.rs) (server), [`examples/clients/src/everything_stdio.rs`](examples/clients/src/everything_stdio.rs) (client)\n\n---\n\n## Sampling\n\nSampling flips the usual direction: the server asks the client to run an LLM completion. The server sends a `create_message` request, the client processes it through its LLM, and returns the result.\n\n**MCP Spec:** [Sampling](https://modelcontextprotocol.io/specification/2025-11-25/client/sampling)\n\n### Server-side (requesting sampling)\n\nAccess the client's sampling capability through `context.peer.create_message()`:\n\n```rust\nuse rmcp::model::*;\n\n// Inside a ServerHandler method (e.g., call_tool):\nlet response = context.peer.create_message(CreateMessageRequestParams {\n    meta: None,\n    task: None,\n    messages: vec![SamplingMessage::user_text(\"Explain this error: connection refused\")],\n    model_preferences: Some(ModelPreferences {\n        hints: Some(vec![ModelHint { name: Some(\"claude\".into()) }]),\n        cost_priority: Some(0.3),\n        speed_priority: Some(0.8),\n        intelligence_priority: Some(0.7),\n    }),\n    system_prompt: Some(\"You are a helpful assistant.\".into()),\n    include_context: Some(ContextInclusion::None),\n    temperature: Some(0.7),\n    max_tokens: 150,\n    stop_sequences: None,\n    metadata: None,\n    tools: None,\n    tool_choice: None,\n}).await?;\n\n// Extract the response text\nlet text = response.message.content\n    .first()\n    .and_then(|c| c.as_text())\n    .map(|t| \u0026t.text);\n```\n\n### Client-side (handling sampling)\n\nOn the client side, implement `ClientHandler::create_message()`. This is where you'd call your actual LLM:\n\n```rust\nuse rmcp::{ClientHandler, model::*, service::{RequestContext, RoleClient}};\n\n#[derive(Clone, Default)]\nstruct MyClient;\n\nimpl ClientHandler for MyClient {\n    async fn create_message(\n        \u0026self,\n        params: CreateMessageRequestParams,\n        _context: RequestContext\u003cRoleClient\u003e,\n    ) -\u003e Result\u003cCreateMessageResult, ErrorData\u003e {\n        // Forward to your LLM, or return a mock response:\n        let response_text = call_your_llm(\u0026params.messages).await;\n\n        Ok(CreateMessageResult {\n            message: SamplingMessage::assistant_text(response_text),\n            model: \"my-model\".into(),\n            stop_reason: Some(CreateMessageResult::STOP_REASON_END_TURN.into()),\n        })\n    }\n}\n```\n\n**Example:** [`examples/servers/src/sampling_stdio.rs`](examples/servers/src/sampling_stdio.rs) (server), [`examples/clients/src/sampling_stdio.rs`](examples/clients/src/sampling_stdio.rs) (client)\n\n---\n\n## Roots\n\nRoots tell servers which directories or projects the client is working in. A root is a URI (typically `file://`) pointing to a workspace or repository. Servers can query roots to know where to look for files and how to scope their work.\n\n**MCP Spec:** [Roots](https://modelcontextprotocol.io/specification/2025-11-25/client/roots)\n\n### Server-side\n\nAsk the client for its root list, and handle change notifications:\n\n```rust\nuse rmcp::{ServerHandler, model::*, service::{NotificationContext, RoleServer}};\n\nimpl ServerHandler for MyServer {\n    // Query the client for its roots\n    async fn call_tool(\n        \u0026self,\n        request: CallToolRequestParams,\n        context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cCallToolResult, ErrorData\u003e {\n        let roots = context.peer.list_roots().await?;\n        // Use roots.roots to understand workspace boundaries\n        // ...\n    }\n\n    // Called when the client's root list changes\n    async fn on_roots_list_changed(\n        \u0026self,\n        _context: NotificationContext\u003cRoleServer\u003e,\n    ) {\n        // Re-fetch roots to stay current\n    }\n}\n```\n\n### Client-side\n\nClients declare roots capability and implement `list_roots()`:\n\n```rust\nuse rmcp::{ClientHandler, model::*};\n\nimpl ClientHandler for MyClient {\n    async fn list_roots(\n        \u0026self,\n        _context: RequestContext\u003cRoleClient\u003e,\n    ) -\u003e Result\u003cListRootsResult, ErrorData\u003e {\n        Ok(ListRootsResult {\n            roots: vec![\n                Root {\n                    uri: \"file:///home/user/project\".into(),\n                    name: Some(\"My Project\".into()),\n                },\n            ],\n        })\n    }\n}\n```\n\nClients notify the server when roots change:\n\n```rust\n// After adding or removing a workspace root:\nclient.notify_roots_list_changed().await?;\n```\n\n---\n\n## Logging\n\nServers can send structured log messages to clients. The client sets a minimum severity level, and the server sends messages through the peer notification interface.\n\n**MCP Spec:** [Logging](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging)\n\n### Server-side\n\nEnable the logging capability, handle level changes from the client, and send log messages via the peer:\n\n```rust\nuse rmcp::{ServerHandler, model::*, service::RequestContext};\n\nimpl ServerHandler for MyServer {\n    fn get_info(\u0026self) -\u003e ServerInfo {\n        ServerInfo {\n            capabilities: ServerCapabilities::builder()\n                .enable_logging()\n                .build(),\n            ..Default::default()\n        }\n    }\n\n    // Client sets the minimum log level\n    async fn set_level(\n        \u0026self,\n        request: SetLevelRequestParams,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003c(), ErrorData\u003e {\n        // Store request.level and filter future log messages accordingly\n        Ok(())\n    }\n}\n\n// Send a log message from any handler with access to the peer:\ncontext.peer.notify_logging_message(LoggingMessageNotificationParam {\n    level: LoggingLevel::Info,\n    logger: Some(\"my-server\".into()),\n    data: serde_json::json!({\n        \"message\": \"Processing completed\",\n        \"items_processed\": 42\n    }),\n}).await?;\n```\n\nAvailable log levels (from least to most severe): `Debug`, `Info`, `Notice`, `Warning`, `Error`, `Critical`, `Alert`, `Emergency`.\n\n### Client-side\n\nClients handle incoming log messages via `ClientHandler`:\n\n```rust\nimpl ClientHandler for MyClient {\n    async fn on_logging_message(\n        \u0026self,\n        params: LoggingMessageNotificationParam,\n        _context: NotificationContext\u003cRoleClient\u003e,\n    ) {\n        println!(\"[{}] {}: {}\", params.level,\n            params.logger.unwrap_or_default(), params.data);\n    }\n}\n```\n\nClients can also set the server's log level:\n\n```rust\nclient.set_level(SetLevelRequestParams {\n    level: LoggingLevel::Warning,\n    meta: None,\n}).await?;\n```\n\n---\n\n## Completions\n\nCompletions give auto-completion suggestions for prompt or resource template arguments. As a user fills in arguments, the client can ask the server for suggestions based on what's already been entered.\n\n**MCP Spec:** [Completions](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/completion)\n\n### Server-side\n\nEnable the completions capability and implement the `complete()` handler. Use `request.context` to inspect previously filled arguments:\n\n```rust\nuse rmcp::{ErrorData as McpError, ServerHandler, model::*, service::RequestContext, RoleServer};\n\nimpl ServerHandler for MyServer {\n    fn get_info(\u0026self) -\u003e ServerInfo {\n        ServerInfo {\n            capabilities: ServerCapabilities::builder()\n                .enable_completions()\n                .enable_prompts()\n                .build(),\n            ..Default::default()\n        }\n    }\n\n    async fn complete(\n        \u0026self,\n        request: CompleteRequestParams,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003cCompleteResult, McpError\u003e {\n        let values = match \u0026request.r#ref {\n            Reference::Prompt(prompt_ref) if prompt_ref.name == \"sql_query\" =\u003e {\n                match request.argument.name.as_str() {\n                    \"operation\" =\u003e vec![\"SELECT\", \"INSERT\", \"UPDATE\", \"DELETE\"],\n                    \"table\" =\u003e vec![\"users\", \"orders\", \"products\"],\n                    \"columns\" =\u003e {\n                        // Adapt suggestions based on previously filled arguments\n                        if let Some(ctx) = \u0026request.context {\n                            if let Some(op) = ctx.get_argument(\"operation\") {\n                                match op.to_uppercase().as_str() {\n                                    \"SELECT\" | \"UPDATE\" =\u003e {\n                                        vec![\"id\", \"name\", \"email\", \"created_at\"]\n                                    }\n                                    _ =\u003e vec![],\n                                }\n                            } else { vec![] }\n                        } else { vec![] }\n                    }\n                    _ =\u003e vec![],\n                }\n            }\n            _ =\u003e vec![],\n        };\n\n        // Filter by the user's partial input\n        let filtered: Vec\u003cString\u003e = values.into_iter()\n            .map(String::from)\n            .filter(|v| v.to_lowercase().contains(\u0026request.argument.value.to_lowercase()))\n            .collect();\n\n        Ok(CompleteResult {\n            completion: CompletionInfo {\n                values: filtered,\n                total: None,\n                has_more: Some(false),\n            },\n        })\n    }\n}\n```\n\n### Client-side\n\n```rust\nuse rmcp::model::*;\n\nlet result = client.complete(CompleteRequestParams {\n    meta: None,\n    r#ref: Reference::Prompt(PromptReference {\n        name: \"sql_query\".into(),\n    }),\n    argument: ArgumentInfo {\n        name: \"operation\".into(),\n        value: \"SEL\".into(),\n    },\n    context: None,\n}).await?;\n\n// result.completion.values contains suggestions like [\"SELECT\"]\n```\n\n**Example:** [`examples/servers/src/completion_stdio.rs`](examples/servers/src/completion_stdio.rs)\n\n---\n\n## Notifications\n\nNotifications are fire-and-forget messages -- no response is expected. They cover progress updates, cancellation, and lifecycle events. Both sides can send and receive them.\n\n**MCP Spec:** [Notifications](https://modelcontextprotocol.io/specification/2025-11-25/basic/notifications)\n\n### Progress notifications\n\nServers can report progress during long-running operations:\n\n```rust\nuse rmcp::model::*;\n\n// Inside a tool handler:\nfor i in 0..total_items {\n    process_item(i).await;\n\n    context.peer.notify_progress(ProgressNotificationParam {\n        progress_token: ProgressToken(NumberOrString::Number(i as i64)),\n        progress: i as f64,\n        total: Some(total_items as f64),\n        message: Some(format!(\"Processing item {}/{}\", i + 1, total_items)),\n    }).await?;\n}\n```\n\n### Cancellation\n\nEither side can cancel an in-progress request:\n\n```rust\n// Send a cancellation\ncontext.peer.notify_cancelled(CancelledNotificationParam {\n    request_id: the_request_id,\n    reason: Some(\"User requested cancellation\".into()),\n}).await?;\n```\n\nHandle cancellation in `ServerHandler` or `ClientHandler`:\n\n```rust\nimpl ServerHandler for MyServer {\n    async fn on_cancelled(\n        \u0026self,\n        params: CancelledNotificationParam,\n        _context: NotificationContext\u003cRoleServer\u003e,\n    ) {\n        // Abort work for params.request_id\n    }\n}\n```\n\n### Initialized notification\n\nClients send `initialized` after the handshake completes:\n\n```rust\n// Sent automatically by rmcp during the serve() handshake.\n// Servers handle it via:\nimpl ServerHandler for MyServer {\n    async fn on_initialized(\n        \u0026self,\n        _context: NotificationContext\u003cRoleServer\u003e,\n    ) {\n        // Server is ready to receive requests\n    }\n}\n```\n\n### List-changed notifications\n\nWhen available tools, prompts, or resources change, tell the client:\n\n```rust\ncontext.peer.notify_tool_list_changed().await?;\ncontext.peer.notify_prompt_list_changed().await?;\ncontext.peer.notify_resource_list_changed().await?;\n```\n\n**Example:** [`examples/servers/src/common/progress_demo.rs`](examples/servers/src/common/progress_demo.rs)\n\n---\n\n## Subscriptions\n\nClients can subscribe to specific resources. When a subscribed resource changes, the server sends a notification and the client can re-read it.\n\n**MCP Spec:** [Resources - Subscriptions](https://modelcontextprotocol.io/specification/2025-11-25/server/resources#subscriptions)\n\n### Server-side\n\nEnable subscriptions in the resources capability and implement the `subscribe()` / `unsubscribe()` handlers:\n\n```rust\nuse rmcp::{ErrorData as McpError, ServerHandler, model::*, service::RequestContext, RoleServer};\nuse std::sync::Arc;\nuse tokio::sync::Mutex;\nuse std::collections::HashSet;\n\n#[derive(Clone)]\nstruct MyServer {\n    subscriptions: Arc\u003cMutex\u003cHashSet\u003cString\u003e\u003e\u003e,\n}\n\nimpl ServerHandler for MyServer {\n    fn get_info(\u0026self) -\u003e ServerInfo {\n        ServerInfo {\n            capabilities: ServerCapabilities::builder()\n                .enable_resources()\n                .enable_resources_subscribe()\n                .build(),\n            ..Default::default()\n        }\n    }\n\n    async fn subscribe(\n        \u0026self,\n        request: SubscribeRequestParams,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003c(), McpError\u003e {\n        self.subscriptions.lock().await.insert(request.uri);\n        Ok(())\n    }\n\n    async fn unsubscribe(\n        \u0026self,\n        request: UnsubscribeRequestParams,\n        _context: RequestContext\u003cRoleServer\u003e,\n    ) -\u003e Result\u003c(), McpError\u003e {\n        self.subscriptions.lock().await.remove(\u0026request.uri);\n        Ok(())\n    }\n}\n```\n\nWhen a subscribed resource changes, notify the client:\n\n```rust\n// Check if the resource has subscribers, then notify\ncontext.peer.notify_resource_updated(ResourceUpdatedNotificationParam {\n    uri: \"file:///config.json\".into(),\n}).await?;\n```\n\n### Client-side\n\n```rust\nuse rmcp::model::*;\n\n// Subscribe to updates for a resource\nclient.subscribe(SubscribeRequestParams {\n    meta: None,\n    uri: \"file:///config.json\".into(),\n}).await?;\n\n// Unsubscribe when no longer needed\nclient.unsubscribe(UnsubscribeRequestParams {\n    meta: None,\n    uri: \"file:///config.json\".into(),\n}).await?;\n```\n\nHandle update notifications in `ClientHandler`:\n\n```rust\nimpl ClientHandler for MyClient {\n    async fn on_resource_updated(\n        \u0026self,\n        params: ResourceUpdatedNotificationParam,\n        _context: NotificationContext\u003cRoleClient\u003e,\n    ) {\n        // Re-read the resource at params.uri\n    }\n}\n```\n\n---\n\n## Tasks (long-running tool invocations)\n\n`rmcp` supports the [task-based tool invocation](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks)\nflow defined in SEP-1319. Annotate a tool with `execution(task_support = \"required\" | \"optional\")`\nand add `#[task_handler]` to your `ServerHandler` impl — `enqueue_task`, `tasks/list`, `tasks/get`,\n`tasks/result`, and `tasks/cancel` are generated for you on top of an `OperationProcessor`.\n\n```rust, ignore\n#[tool(\n    description = \"Sum two numbers after a 2-second delay\",\n    execution(task_support = \"required\")\n)]\nasync fn slow_sum(/* ... */) -\u003e Result\u003cCallToolResult, McpError\u003e { /* ... */ }\n\n#[tool_handler]\n#[task_handler]\nimpl ServerHandler for TaskDemo {}\n```\n\nSee [`servers_task_stdio`](examples/servers/src/task_stdio.rs) and the matching\n[`clients_task_stdio`](examples/clients/src/task_stdio.rs) for a runnable end-to-end example.\n\n## Examples\n\nSee [examples](examples/README.md).\n\n## OAuth Support\n\nSee [Oauth_support](docs/OAUTH_SUPPORT.md) for details.\n\n## Related Resources\n\n- [MCP Specification](https://modelcontextprotocol.io/specification/2025-11-25)\n- [Schema](https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-11-25/schema.ts)\n\n## Related Projects\n\n### Extending `rmcp`\n\n- [rmcp-actix-web](https://gitlab.com/lx-industries/rmcp-actix-web) - An `actix_web` backend for `rmcp`\n- [rmcp-openapi](https://gitlab.com/lx-industries/rmcp-openapi) - Transform OpenAPI definition endpoints into MCP tools\n\n### Built with `rmcp`\n\n- [goose](https://github.com/block/goose) - An open-source, extensible AI agent that goes beyond code suggestions\n- [apollo-mcp-server](https://github.com/apollographql/apollo-mcp-server) - MCP server that connects AI agents to GraphQL APIs via Apollo GraphOS\n- [rustfs-mcp](https://github.com/rustfs/rustfs/tree/main/crates/mcp) - High-performance MCP server providing S3-compatible object storage operations for AI/LLM integration\n- [containerd-mcp-server](https://github.com/jokemanfire/mcp-containerd) - A containerd-based MCP server implementation\n- [rmcp-openapi-server](https://gitlab.com/lx-industries/rmcp-openapi/-/tree/main/crates/rmcp-openapi-server) - High-performance MCP server that exposes OpenAPI definition endpoints as MCP tools\n- [nvim-mcp](https://github.com/linw1995/nvim-mcp) - A MCP server to interact with Neovim\n- [terminator](https://github.com/mediar-ai/terminator) - AI-powered desktop automation MCP server with cross-platform support and \u003e95% success rate\n- [stakpak-agent](https://github.com/stakpak/agent) - Security-hardened terminal agent for DevOps with MCP over mTLS, streaming, secret tokenization, and async task management\n- [video-transcriber-mcp-rs](https://github.com/nhatvu148/video-transcriber-mcp-rs) - High-performance MCP server for transcribing videos from 1000+ platforms using whisper.cpp\n- [NexusCore MCP](https://github.com/sjkim1127/Nexuscore_MCP) - Advanced malware analysis \u0026 dynamic instrumentation MCP server with Frida integration and stealth unpacking capabilities\n- [spreadsheet-mcp](https://github.com/PSU3D0/spreadsheet-mcp) - Token-efficient MCP server for spreadsheet analysis with automatic region detection, recalculation, screenshot, and editing support for LLM agents\n- [hyper-mcp](https://github.com/hyper-mcp-rs/hyper-mcp) - A fast, secure MCP server that extends its capabilities through WebAssembly (WASM) plugins\n- [rudof-mcp](https://github.com/rudof-project/rudof/tree/master/rudof_mcp) - RDF validation and data processing MCP server with ShEx/SHACL validation, SPARQL queries, and format conversion. Supports stdio and streamable HTTP transports with full MCP capabilities (tools, prompts, resources, logging, completions, tasks)\n- [McpMux](https://github.com/mcpmux/mcp-mux) - Desktop app to configure MCP servers once at McpMux, connect every AI client (Cursor, Claude Desktop, VS Code, Windsurf) through a single encrypted local gateway with Spaces for project organization, FeatureSets to switch toolsets per client, and a built-in server registry\n- [systemprompt-template](https://github.com/systempromptio/systemprompt-template) - Single-binary Rust runtime providing MCP governance — authentication, authorisation, rate-limiting, audit trails, and cost tracking for AI agents. Self-hosted, air-gap capable, 3,300+ req/s with sub-5ms governance overhead\n\n\n## Development\n\n### Tips for Contributors\n\nSee [docs/CONTRIBUTE.MD](docs/CONTRIBUTE.MD) to get some tips for contributing.\n\n### Using Dev Container\n\nIf you want to use dev container, see [docs/DEVCONTAINER.md](docs/DEVCONTAINER.md) for instructions on using Dev Container for development.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodelcontextprotocol%2Frust-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmodelcontextprotocol%2Frust-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmodelcontextprotocol%2Frust-sdk/lists"}