diff --git a/crates/rmcp-macros/README.md b/crates/rmcp-macros/README.md
index 62ea2f5e..ca137b13 100644
--- a/crates/rmcp-macros/README.md
+++ b/crates/rmcp-macros/README.md
@@ -1,208 +1,65 @@
-# rmcp-macros
-
-`rmcp-macros` is a procedural macro library for the Rust Model Context Protocol (RMCP) SDK, providing macros that facilitate the development of RMCP applications.
+
-## Features
+
-This library primarily provides the following macros:
+# rmcp-macros
-- `#[tool]`: Mark an async/sync function as an RMCP tool and generate metadata + schema glue
-- `#[tool_router]`: Collect all `#[tool]` functions in an impl block into a router value
-- `#[tool_handler]`: Implement the `call_tool` and `list_tools` entry points by delegating to a router expression
-- `#[task_handler]`: Wire up the task lifecycle (list/enqueue/get/cancel) on top of an `OperationProcessor`
+[](https://crates.io/crates/rmcp-macros)
+[](https://docs.rs/rmcp-macros)
-## Usage
+
-### tool
+`rmcp-macros` is a procedural macro library for the Rust Model Context Protocol (RMCP) SDK, providing macros that facilitate the development of RMCP applications.
-This macro is used to mark a function as a tool handler.
+## Available Macros
-This will generate a function that return the attribute of this tool, with type `rmcp::model::Tool`.
+| Macro | Description |
+|-------|-------------|
+| [`#[tool]`][tool] | Mark a function as an MCP tool handler |
+| [`#[tool_router]`][tool_router] | Generate a tool router from an impl block |
+| [`#[tool_handler]`][tool_handler] | Generate `call_tool` and `list_tools` handler methods |
+| [`#[prompt]`][prompt] | Mark a function as an MCP prompt handler |
+| [`#[prompt_router]`][prompt_router] | Generate a prompt router from an impl block |
+| [`#[prompt_handler]`][prompt_handler] | Generate `get_prompt` and `list_prompts` handler methods |
+| [`#[task_handler]`][task_handler] | Wire up the task lifecycle on top of an `OperationProcessor` |
-#### Tool attributes
+[tool]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.tool.html
+[tool_router]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.tool_router.html
+[tool_handler]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.tool_handler.html
+[prompt]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.prompt.html
+[prompt_router]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.prompt_router.html
+[prompt_handler]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.prompt_handler.html
+[task_handler]: https://docs.rs/rmcp-macros/latest/rmcp_macros/attr.task_handler.html
-| field | type | usage |
-| :- | :- | :- |
-| `name` | `String` | The name of the tool. If not provided, it defaults to the function name. |
-| `description` | `String` | A description of the tool. The document of this function will be used. |
-| `input_schema` | `Expr` | A JSON Schema object defining the expected parameters for the tool. If not provide, if will use the json schema of its argument with type `Parameters` |
-| `annotations` | `ToolAnnotationsAttribute` | Additional tool information. Defaults to `None`. |
+## Quick Example
-#### Tool example
+```rust,ignore
+use rmcp::{tool, tool_router, tool_handler, ServerHandler, model::*};
-```rust
-#[tool(name = "my_tool", description = "This is my tool", annotations(title = "我的工具", read_only_hint = true))]
-pub async fn my_tool(param: Parameters) {
- // handling tool request
+#[derive(Clone)]
+struct MyServer {
+ tool_router: rmcp::handler::server::tool::ToolRouter,
}
-```
-
-### tool_router
-
-This macro is used to generate a tool router based on functions marked with `#[rmcp::tool]` in an implementation block.
-It creates a function that returns a `ToolRouter` instance.
-
-In most case, you need to add a field for handler to store the router information and initialize it when creating handler, or store it with a static variable.
-
-#### Router attributes
-
-| field | type | usage |
-| :- | :- | :- |
-| `router` | `Ident` | The name of the router function to be generated. Defaults to `tool_router`. |
-| `vis` | `Visibility` | The visibility of the generated router function. Defaults to empty. |
-
-#### Router example
-
-```rust
#[tool_router]
-impl MyToolHandler {
- #[tool]
- pub fn my_tool() {
-
- }
-
- pub fn new() -> Self {
- Self {
- // the default name of tool router will be `tool_router`
- tool_router: Self::tool_router(),
- }
+impl MyServer {
+ #[tool(description = "Say hello")]
+ async fn hello(&self) -> String {
+ "Hello, world!".into()
}
}
-```
-
-Or specify the visibility and router name, which would be helpful when you want to combine multiple routers into one:
-
-```rust
-mod a {
- #[tool_router(router = tool_router_a, vis = "pub")]
- impl MyToolHandler {
- #[tool]
- fn my_tool_a() {
-
- }
- }
-}
-
-mod b {
- #[tool_router(router = tool_router_b, vis = "pub")]
- impl MyToolHandler {
- #[tool]
- fn my_tool_b() {
-
- }
- }
-}
-
-impl MyToolHandler {
- fn new() -> Self {
- Self {
- tool_router: self::tool_router_a() + self::tool_router_b(),
- }
- }
-}
-```
-
-### tool_handler
-
-This macro will generate the handler for `tool_call` and `list_tools` methods in the implementation block, by using an existing `ToolRouter` instance.
-
-#### Handler attributes
-
-| field | type | usage |
-| :- | :- | :- |
-| `router` | `Expr` | The expression to access the `ToolRouter` instance. Defaults to `self.tool_router`. |
-
-#### Handler example
-```rust
#[tool_handler]
-impl ServerHandler for MyToolHandler {
- // ...implement other handler
-}
-```
-
-or using a custom router expression:
-
-```rust
-#[tool_handler(router = self.get_router().await)]
-impl ServerHandler for MyToolHandler {
- // ...implement other handler
-}
-```
-
-#### Handler expansion
-
-This macro will be expended to something like this:
-
-```rust
-impl ServerHandler for MyToolHandler {
- async fn call_tool(
- &self,
- request: CallToolRequestParam,
- context: RequestContext,
- ) -> Result {
- let tcc = ToolCallContext::new(self, request, context);
- self.tool_router.call(tcc).await
- }
-
- async fn list_tools(
- &self,
- _request: Option,
- _context: RequestContext,
- ) -> Result {
- let items = self.tool_router.list_all();
- Ok(ListToolsResult::with_all_items(items))
- }
-}
-```
-
-### task_handler
-
-This macro wires the task lifecycle endpoints (`list_tasks`, `enqueue_task`, `get_task`, `cancel_task`) to an implementation of `OperationProcessor`. It keeps the handler lean by delegating scheduling, status tracking, and cancellation semantics to the processor.
-
-#### Task handler attributes
-
-| field | type | usage |
-| :- | :- | :- |
-| `processor` | `Expr` | Expression that yields an `Arc` (or compatible trait object). Defaults to `self.processor.clone()`. |
-
-#### Task handler example
-
-```rust
-#[derive(Clone)]
-pub struct TaskHandler {
- processor: Arc + Send + Sync>,
-}
-
-#[task_handler(processor = self.processor.clone())]
-impl ServerHandler for TaskHandler {}
-```
-
-#### Task handler expansion
-
-At expansion time the macro implements the task-specific handler methods by forwarding to the processor expression, roughly equivalent to:
-
-```rust
-impl ServerHandler for TaskHandler {
- async fn list_tasks(&self, request: TaskListRequest, ctx: RequestContext) -> Result {
- self.processor.list_tasks(request, ctx).await
- }
-
- async fn enqueue_task(&self, request: TaskEnqueueRequest, ctx: RequestContext) -> Result {
- self.processor.enqueue_task(request, ctx).await
+impl ServerHandler for MyServer {
+ fn get_info(&self) -> ServerInfo {
+ ServerInfo::default()
}
-
- // get_task and cancel_task are generated in the same manner.
}
```
-
-## Advanced Features
-
-- Support for custom tool names and descriptions
-- Automatic generation of tool descriptions from documentation comments
-- JSON Schema generation for tool parameters
+See the [full documentation](https://docs.rs/rmcp-macros) for detailed usage of each macro.
## License
diff --git a/crates/rmcp-macros/src/lib.rs b/crates/rmcp-macros/src/lib.rs
index ea79f465..ce9047e4 100644
--- a/crates/rmcp-macros/src/lib.rs
+++ b/crates/rmcp-macros/src/lib.rs
@@ -1,3 +1,5 @@
+#![doc = include_str!("../README.md")]
+
#[allow(unused_imports)]
use proc_macro::TokenStream;
diff --git a/crates/rmcp/README.md b/crates/rmcp/README.md
index e28df000..ed366a39 100644
--- a/crates/rmcp/README.md
+++ b/crates/rmcp/README.md
@@ -1,9 +1,17 @@
-# RMCP: Rust Model Context Protocol
+
-`rmcp` is the official Rust implementation of the Model Context Protocol (MCP), a protocol designed for AI assistants to communicate with other services. This library can be used to build both servers that expose capabilities to AI assistants and clients that interact with such servers.
+
+# RMCP: Rust Model Context Protocol
+[](https://crates.io/crates/rmcp)
+[](https://docs.rs/rmcp)
+
+
+`rmcp` is the official Rust implementation of the Model Context Protocol (MCP), a protocol designed for AI assistants to communicate with other services. This library can be used to build both servers that expose capabilities to AI assistants and clients that interact with such servers.
## Quick Start
@@ -11,12 +19,15 @@
Creating a server with tools is simple using the `#[tool]` macro:
-```rust, ignore
+```rust,no_run
use rmcp::{
- handler::server::router::tool::ToolRouter, model::*, tool, tool_handler, tool_router,
- transport::stdio, ErrorData as McpError, ServiceExt,
+ ServerHandler, ServiceExt,
+ handler::server::tool::ToolRouter,
+ model::*,
+ tool, tool_handler, tool_router,
+ transport::stdio,
+ ErrorData as McpError,
};
-use std::future::Future;
use std::sync::Arc;
use tokio::sync::Mutex;
@@ -55,7 +66,7 @@ impl Counter {
// Implement the server handler
#[tool_handler]
-impl rmcp::ServerHandler for Counter {
+impl ServerHandler for Counter {
fn get_info(&self) -> ServerInfo {
ServerInfo {
instructions: Some("A simple counter that tallies the number of times the increment tool has been used".into()),
@@ -73,11 +84,54 @@ async fn main() -> Result<(), Box> {
println!("Error starting server: {}", e);
})?;
service.waiting().await?;
-
Ok(())
}
```
+### Structured Output
+
+Tools can return structured JSON data with schemas. Use the [`Json`] wrapper:
+
+```rust
+# use rmcp::{tool, tool_router, handler::server::{tool::ToolRouter, wrapper::Parameters}, Json};
+# use schemars::JsonSchema;
+# use serde::{Serialize, Deserialize};
+#
+#[derive(Serialize, Deserialize, JsonSchema)]
+struct CalculationRequest {
+ a: i32,
+ b: i32,
+ operation: String,
+}
+
+#[derive(Serialize, Deserialize, JsonSchema)]
+struct CalculationResult {
+ result: i32,
+ operation: String,
+}
+
+# #[derive(Clone)]
+# struct Calculator {
+# tool_router: ToolRouter,
+# }
+#
+# #[tool_router]
+# impl Calculator {
+#[tool(name = "calculate", description = "Perform a calculation")]
+async fn calculate(&self, params: Parameters) -> Result, String> {
+ let result = match params.0.operation.as_str() {
+ "add" => params.0.a + params.0.b,
+ "multiply" => params.0.a * params.0.b,
+ _ => return Err("Unknown operation".to_string()),
+ };
+
+ Ok(Json(CalculationResult { result, operation: params.0.operation }))
+}
+# }
+```
+
+The `#[tool]` macro automatically generates an output schema from the `CalculationResult` type.
+
## Tasks
RMCP implements the task lifecycle from SEP-1686 so long-running or asynchronous tool calls can be queued and polled safely.
@@ -93,11 +147,11 @@ To expose task support, enable the `tasks` capability when building `ServerCapab
Creating a client to interact with a server:
-```rust, ignore
+```rust,no_run
use rmcp::{
- model::CallToolRequestParam,
- service::ServiceExt,
- transport::{TokioChildProcess, ConfigureCommandExt}
+ ServiceExt,
+ model::CallToolRequestParams,
+ transport::{ConfigureCommandExt, TokioChildProcess},
};
use tokio::process::Command;
@@ -105,12 +159,12 @@ use tokio::process::Command;
async fn main() -> Result<(), Box> {
// Connect to a server running as a child process
let service = ()
- .serve(TokioChildProcess::new(Command::new("uvx").configure(
- |cmd| {
- cmd.arg("mcp-server-git");
- },
- ))?)
- .await?;
+ .serve(TokioChildProcess::new(Command::new("uvx").configure(
+ |cmd| {
+ cmd.arg("mcp-server-git");
+ },
+ ))?)
+ .await?;
// Get server information
let server_info = service.peer_info();
@@ -122,9 +176,10 @@ async fn main() -> Result<(), Box> {
// Call a tool
let result = service
- .call_tool(CallToolRequestParam {
- name: "increment".into(),
- arguments: None,
+ .call_tool(CallToolRequestParams {
+ meta: None,
+ name: "git_status".into(),
+ arguments: serde_json::json!({ "repo_path": "." }).as_object().cloned(),
task: None,
})
.await?;
@@ -132,11 +187,12 @@ async fn main() -> Result<(), Box> {
// Gracefully close the connection
service.cancel().await?;
-
Ok(())
}
```
+For more examples, see the [examples directory](https://github.com/anthropics/mcp-rust-sdk/tree/main/examples) in the repository.
+
## Transport Options
RMCP supports multiple transport mechanisms, each suited for different use cases:
@@ -151,7 +207,7 @@ For working directly with I/O streams (`tokio::io::AsyncRead` and `tokio::io::As
Run MCP servers as child processes and communicate via standard I/O.
Example:
-```rust
+```rust,ignore
use rmcp::transport::TokioChildProcess;
use tokio::process::Command;
@@ -159,8 +215,6 @@ let transport = TokioChildProcess::new(Command::new("mcp-server"))?;
let service = client.serve(transport).await?;
```
-
-
## Access with peer interface when handling message
You can get the [`Peer`](crate::service::Peer) struct from [`NotificationContext`](crate::service::NotificationContext) and [`RequestContext`](crate::service::RequestContext).
@@ -212,7 +266,7 @@ RMCP uses feature flags to control which components are included:
- `transport-async-rw`: Async read/write support
- `transport-io`: I/O stream support
- `transport-child-process`: Child process support
- - `transport-streamable-http-client` / `transport-streamable-http-server`: HTTP streaming (client agnostic, see [`StreamableHttpClientTransport`] for details)
+ - `transport-streamable-http-client` / `transport-streamable-http-server`: HTTP streaming (client agnostic, see [`StreamableHttpClientTransport`](crate::transport::StreamableHttpClientTransport) for details)
- `transport-streamable-http-client-reqwest`: a default `reqwest` implementation of the streamable http client
- `auth`: OAuth2 authentication support
- `schemars`: JSON Schema generation (for tool definitions)
@@ -227,25 +281,26 @@ RMCP uses feature flags to control which components are included:
Transport
-The transport type must implemented [`Transport`] trait, which allow it send message concurrently and receive message sequentially.
+
+The transport type must implement the [`Transport`](crate::transport::Transport) trait, which allows it to send messages concurrently and receive messages sequentially.
There are 2 pairs of standard transport types:
-| transport | client | server |
-|:-: |:-: |:-: |
-| std IO | [`child_process::TokioChildProcess`] | [`io::stdio`] |
-| streamable http | [`streamable_http_client::StreamableHttpClientTransport`] | [`streamable_http_server::session::create_session`] |
+| transport | client | server |
+|:---------------:|:-----------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------:|
+| std IO | [`TokioChildProcess`](crate::transport::TokioChildProcess) | [`stdio`](crate::transport::stdio) |
+| streamable http | [`StreamableHttpClientTransport`](crate::transport::StreamableHttpClientTransport) | [`StreamableHttpService`](crate::transport::StreamableHttpService) |
-#### [IntoTransport](`IntoTransport`) trait
-[`IntoTransport`] is a helper trait that implicitly convert a type into a transport type.
+#### [`IntoTransport`](crate::transport::IntoTransport) trait
+[`IntoTransport`](crate::transport::IntoTransport) is a helper trait that implicitly converts a type into a transport type.
-These types is automatically implemented [`IntoTransport`] trait
-1. A type that already implement both [`futures::Sink`] and [`futures::Stream`] trait, or a tuple `(Tx, Rx)` where `Tx` is [`futures::Sink`] and `Rx` is [`futures::Stream`].
-2. A type that implement both [`tokio::io::AsyncRead`] and [`tokio::io::AsyncWrite`] trait. or a tuple `(R, W)` where `R` is [`tokio::io::AsyncRead`] and `W` is [`tokio::io::AsyncWrite`].
-3. A type that implement [Worker](`worker::Worker`) trait.
-4. A type that implement [`Transport`] trait.
+These types automatically implement [`IntoTransport`](crate::transport::IntoTransport):
+1. A type that implements both `futures::Sink` and `futures::Stream`, or a tuple `(Tx, Rx)` where `Tx` is `futures::Sink` and `Rx` is `futures::Stream`.
+2. A type that implements both `tokio::io::AsyncRead` and `tokio::io::AsyncWrite`, or a tuple `(R, W)` where `R` is `tokio::io::AsyncRead` and `W` is `tokio::io::AsyncWrite`.
+3. A type that implements the [`Worker`](crate::transport::worker::Worker) trait.
+4. A type that implements the [`Transport`](crate::transport::Transport) trait.
## License
-This project is licensed under the terms specified in the repository's LICENSE file.
\ No newline at end of file
+This project is licensed under the terms specified in the repository's LICENSE file.
diff --git a/crates/rmcp/src/lib.rs b/crates/rmcp/src/lib.rs
index 5f543d27..29d21b2d 100644
--- a/crates/rmcp/src/lib.rs
+++ b/crates/rmcp/src/lib.rs
@@ -1,145 +1,7 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, allow(unused_attributes))]
-//! The official Rust SDK for the Model Context Protocol (MCP).
-//!
-//! The MCP is a protocol that allows AI assistants to communicate with other
-//! services. `rmcp` is the official Rust implementation of this protocol.
-//!
-//! There are two ways in which the library can be used, namely to build a
-//! server or to build a client.
-//!
-//! ## Server
-//!
-//! A server is a service that exposes capabilities. For example, a common
-//! use-case is for the server to make multiple tools available to clients such
-//! as Claude Desktop or the Cursor IDE.
-//!
-//! For example, to implement a server that has a tool that can count, you would
-//! make an object for that tool and add an implementation with the `#[tool_router]` macro:
-//!
-//! ```rust
-//! use std::sync::Arc;
-//! use rmcp::{ErrorData as McpError, model::*, tool, tool_router, handler::server::tool::ToolRouter};
-//! use tokio::sync::Mutex;
-//!
-//! #[derive(Clone)]
-//! pub struct Counter {
-//! counter: Arc>,
-//! tool_router: ToolRouter,
-//! }
-//!
-//! #[tool_router]
-//! impl Counter {
-//! fn new() -> Self {
-//! Self {
-//! counter: Arc::new(Mutex::new(0)),
-//! tool_router: Self::tool_router(),
-//! }
-//! }
-//!
-//! #[tool(description = "Increment the counter by 1")]
-//! async fn increment(&self) -> Result {
-//! let mut counter = self.counter.lock().await;
-//! *counter += 1;
-//! Ok(CallToolResult::success(vec![Content::text(
-//! counter.to_string(),
-//! )]))
-//! }
-//! }
-//! ```
-//!
-//! ### Structured Output
-//!
-//! Tools can also return structured JSON data with schemas. Use the [`Json`] wrapper:
-//!
-//! ```rust
-//! # use rmcp::{tool, tool_router, handler::server::{tool::ToolRouter, wrapper::Parameters}, Json};
-//! # use schemars::JsonSchema;
-//! # use serde::{Serialize, Deserialize};
-//! #
-//! #[derive(Serialize, Deserialize, JsonSchema)]
-//! struct CalculationRequest {
-//! a: i32,
-//! b: i32,
-//! operation: String,
-//! }
-//!
-//! #[derive(Serialize, Deserialize, JsonSchema)]
-//! struct CalculationResult {
-//! result: i32,
-//! operation: String,
-//! }
-//!
-//! # #[derive(Clone)]
-//! # struct Calculator {
-//! # tool_router: ToolRouter,
-//! # }
-//! #
-//! # #[tool_router]
-//! # impl Calculator {
-//! #[tool(name = "calculate", description = "Perform a calculation")]
-//! async fn calculate(&self, params: Parameters) -> Result, String> {
-//! let result = match params.0.operation.as_str() {
-//! "add" => params.0.a + params.0.b,
-//! "multiply" => params.0.a * params.0.b,
-//! _ => return Err("Unknown operation".to_string()),
-//! };
-//!
-//! Ok(Json(CalculationResult { result, operation: params.0.operation }))
-//! }
-//! # }
-//! ```
-//!
-//! The `#[tool]` macro automatically generates an output schema from the `CalculationResult` type.
-//!
-//! Next also implement [ServerHandler] for your server type and start the server inside
-//! `main` by calling `.serve(...)`. See the examples directory in the repository for more information.
-//!
-//! ## Client
-//!
-//! A client can be used to interact with a server. Clients can be used to get a
-//! list of the available tools and to call them. For example, we can `uv` to
-//! start a MCP server in Python and then list the tools and call `git status`
-//! as follows:
-//!
-//! ```rust
-//! use anyhow::Result;
-//! use rmcp::{model::CallToolRequestParams, service::ServiceExt};
-//! #[cfg(feature = "transport-child-process")]
-//! #[cfg_attr(docsrs, doc(cfg(feature = "transport-child-process")))]
-//! use rmcp::transport::{TokioChildProcess, ConfigureCommandExt};
-//! use tokio::process::Command;
-//!
-//! #[cfg(feature = "transport-child-process")]
-//! #[cfg_attr(docsrs, doc(cfg(feature = "transport-child-process")))]
-//! async fn client() -> Result<()> {
-//! let service = ().serve(TokioChildProcess::new(Command::new("uvx").configure(|cmd| {
-//! cmd.arg("mcp-server-git");
-//! }))?).await?;
-//!
-//! // Initialize
-//! let server_info = service.peer_info();
-//! println!("Connected to server: {server_info:#?}");
-//!
-//! // List tools
-//! let tools = service.list_tools(Default::default()).await?;
-//! println!("Available tools: {tools:#?}");
-//!
-//! // Call tool 'git_status' with arguments = {"repo_path": "."}
-//! let tool_result = service
-//! .call_tool(CallToolRequestParams {
-//! meta: None,
-//! name: "git_status".into(),
-//! arguments: serde_json::json!({ "repo_path": "." }).as_object().cloned(),
-//! task: None,
-//! })
-//! .await?;
-//! println!("Tool result: {tool_result:#?}");
-//!
-//! service.cancel().await?;
-//! Ok(())
-//! }
-//! ```
+#![doc = include_str!("../README.md")]
+
mod error;
#[allow(deprecated)]
pub use error::{Error, ErrorData, RmcpError};