diff --git a/README.md b/README.md index 52fda9b3..bac86aee 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,41 @@
-简体中文 +简体中文(待更新)
# RMCP - -[![Crates.io Version](https://img.shields.io/crates/v/rmcp)](https://crates.io/crates/rmcp) -![Release status](https://github.com/modelcontextprotocol/rust-sdk/actions/workflows/release.yml/badge.svg) -[![docs.rs](https://img.shields.io/docsrs/rmcp)](https://docs.rs/rmcp/latest/rmcp) +Wait for the first release. + + + ![Coverage](coverage.svg) An official rust Model Context Protocol SDK implementation with tokio async runtime. + +This repository contains the following crates: + +- [rmcp](crates/rmcp): The core crate providing the RMCP protocol implementation( If you want to get more information, please visit [rmcp](crates/rmcp/README.md)) +- [rmcp-macros](crates/rmcp-macros): A procedural macro crate for generating RMCP tool implementations(If you want to get more information, please visit [rmcp-macros](crates/rmcp-macros/README.md)) + ## Usage -### Import +### Import the crate ```toml rmcp = { version = "0.1", features = ["server"] } ## or dev channel rmcp = { git = "https://github.com/modelcontextprotocol/rust-sdk", branch = "main" } ``` +### Third Dependencies +Basic dependencies: +- [tokio required](https://github.com/tokio-rs/tokio) +- [serde required](https://github.com/serde-rs/serde) -### Quick start -Start a client: + +### Build a Client +
+Start a client ```rust, ignore use rmcp::{ServiceExt, transport::{TokioChildProcess, ConfigureCommandExt}}; @@ -37,39 +49,22 @@ async fn main() -> Result<(), Box> { Ok(()) } ``` +
+ +### Build a Server
-1. Build a transport +Build a transport ```rust, ignore use tokio::io::{stdin, stdout}; let transport = (stdin(), stdout()); ``` -#### Transport -The transport type must implemented [`Transport`] trait, which allow it send message concurrently and receive message sequentially. -There are 3 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`] | -| sse | [`sse_client::SseClientTransport`] | [`sse_server::SseServer`] | - -#### [IntoTransport](`IntoTransport`) trait -[`IntoTransport`] is a helper trait that implicitly convert 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. - -For example, you can see how we build a transport through TCP stream or http upgrade so easily. [examples](examples/README.md)
-2. Build a service +Build a service You can easily build a service by using [`ServerHandler`](crates/rmcp/src/handler/server.rs) or [`ClientHandler`](crates/rmcp/src/handler/client.rs). @@ -79,7 +74,7 @@ let service = common::counter::Counter::new();
-3. Serve them together +Start the server ```rust, ignore // this call will finish the initialization process @@ -88,7 +83,7 @@ let server = service.serve(transport).await?;
-4. Interact with the server +Interact with the server Once the server is initialized, you can send requests or notifications: @@ -102,7 +97,7 @@ server.notify_cancelled(...).await?;
-5. Waiting for service shutdown +Waiting for service shutdown ```rust, ignore let quit_reason = server.waiting().await?; @@ -111,104 +106,19 @@ let quit_reason = server.cancel().await?; ```
-### Use macros to declaring tool - -Use `toolbox` and `tool` macros to create tool quickly. - -
-Example: Calculator Tool - -Check this [file](examples/servers/src/common/calculator.rs). -```rust, ignore -use rmcp::{ServerHandler, model::ServerInfo, schemars, tool}; - -use super::counter::Counter; - -#[derive(Debug, serde::Deserialize, schemars::JsonSchema)] -pub struct SumRequest { - #[schemars(description = "the left hand side number")] - pub a: i32, - #[schemars(description = "the right hand side number")] - pub b: i32, -} -#[derive(Debug, Clone)] -pub struct Calculator; - -// create a static toolbox to store the tool attributes -#[tool(tool_box)] -impl Calculator { - // async function - #[tool(description = "Calculate the sum of two numbers")] - async fn sum(&self, #[tool(aggr)] SumRequest { a, b }: SumRequest) -> String { - (a + b).to_string() - } - - // sync function - #[tool(description = "Calculate the difference of two numbers")] - fn sub( - &self, - #[tool(param)] - // this macro will transfer the schemars and serde's attributes - #[schemars(description = "the left hand side number")] - a: i32, - #[tool(param)] - #[schemars(description = "the right hand side number")] - b: i32, - ) -> String { - (a - b).to_string() - } -} - -// impl call_tool and list_tool by querying static toolbox -#[tool(tool_box)] -impl ServerHandler for Calculator { - fn get_info(&self) -> ServerInfo { - ServerInfo { - instructions: Some("A simple calculator".into()), - ..Default::default() - } - } -} -``` - - -The only thing you should do is to make the function's return type implement `IntoCallToolResult`. - -And you can just implement `IntoContents`, and the return value will be marked as success automatically. - -If you return a type of `Result` where `T` and `E` both implemented `IntoContents`, it's also OK. -
- -### Manage Multi Services -For many cases you need to manage several service in a collection, you can call `into_dyn` to convert services into the same type. -```rust, ignore -let service = service.into_dyn(); -``` -### OAuth Support -See [docs/OAUTH_SUPPORT.md](docs/OAUTH_SUPPORT.md) for details. - -### Examples +## Examples See [examples](examples/README.md) -### Features +## OAuth Support + +See [oauth_support](docs/OAUTH_SUPPORT.md) for details. -- `client`: use client side sdk -- `server`: use server side sdk -- `macros`: macros default -- `schemars`: implement `JsonSchema` for all model structs -#### Transports -- `transport-io`: Server stdio transport -- `transport-sse-server`: Server SSE transport -- `transport-child-process`: Client stdio transport -- `transport-sse-client`: Client sse transport -- `transport-streamable-http-server` streamable http server transport -- `transport-streamable-client-server` streamable http server transport ## Related Resources diff --git a/crates/rmcp-macros/README.md b/crates/rmcp-macros/README.md new file mode 100644 index 00000000..223e164f --- /dev/null +++ b/crates/rmcp-macros/README.md @@ -0,0 +1,54 @@ +# 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: + +- `#[tool]`: Used to mark functions as RMCP tools, automatically generating necessary metadata and invocation mechanisms + +## Usage + +### Tool Macro + +Mark a function as a tool: + +```rust ignore +#[tool] +fn calculator(&self, #[tool(param)] a: i32, #[tool(param)] b: i32) -> Result { + // Implement tool functionality + Ok(CallToolResult::success(vec![Content::text((a + b).to_string())])) +} + +``` + +Use on an impl block to automatically register multiple tools: + +```rust ignore +#[tool(tool_box)] +impl MyHandler { + #[tool] + fn tool1(&self) -> Result { + // Tool 1 implementation + } + + #[tool] + fn tool2(&self) -> Result { + // Tool 2 implementation + } +} +``` + + + +## Advanced Features + +- Support for parameter aggregation (`#[tool(aggr)]`) +- Support for custom tool names and descriptions +- Automatic generation of tool descriptions from documentation comments +- JSON Schema generation for tool parameters + +## License + +Please refer to the LICENSE file in the project root directory. \ No newline at end of file diff --git a/crates/rmcp/README.md b/crates/rmcp/README.md new file mode 100644 index 00000000..b3f67d12 --- /dev/null +++ b/crates/rmcp/README.md @@ -0,0 +1,206 @@ +# 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. + +wait for the first release. + + + + +## Quick Start + +### Server Implementation + +Creating a server with tools is simple using the `#[tool]` macro: + +```rust, ignore +use rmcp::{Error as McpError, ServiceExt, model::*, tool, transport::stdio}; +use std::sync::Arc; +use tokio::sync::Mutex; + +#[derive(Clone)] +pub struct Counter { + counter: Arc>, +} + +#[tool(tool_box)] +impl Counter { + fn new() -> Self { + Self { + counter: Arc::new(Mutex::new(0)), + } + } + + #[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(), + )])) + } + + #[tool(description = "Get the current counter value")] + async fn get(&self) -> Result { + let counter = self.counter.lock().await; + Ok(CallToolResult::success(vec![Content::text( + counter.to_string(), + )])) + } +} + +// Implement the server handler +#[tool(tool_box)] +impl rmcp::ServerHandler for Counter { + fn get_info(&self) -> ServerInfo { + ServerInfo { + instructions: Some("A simple calculator".into()), + capabilities: ServerCapabilities::builder().enable_tools().build(), + ..Default::default() + } + } +} + +// Run the server +#[tokio::main] +async fn main() -> Result<(), Box> { + // Create and run the server with STDIO transport + let service = Counter::new().serve(stdio()).await.inspect_err(|e| { + println!("Error starting server: {}", e); + })?; + service.waiting().await?; + + Ok(()) +} +``` + +### Client Implementation + +Creating a client to interact with a server: + +```rust, ignore +use rmcp::{ + model::CallToolRequestParam, + service::ServiceExt, + transport::{TokioChildProcess, ConfigureCommandExt} +}; +use tokio::process::Command; + +#[tokio::main] +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?; + + // Get server information + let server_info = service.peer_info(); + println!("Connected to server: {server_info:#?}"); + + // List available tools + let tools = service.list_tools(Default::default()).await?; + println!("Available tools: {tools:#?}"); + + // Call a tool + let result = service + .call_tool(CallToolRequestParam { + name: "increment".into(), + arguments: None, + }) + .await?; + println!("Result: {result:#?}"); + + // Gracefully close the connection + service.cancel().await?; + + Ok(()) +} +``` + +## Transport Options + +RMCP supports multiple transport mechanisms, each suited for different use cases: + +### `transport-async-rw` +Low-level interface for asynchronous read/write operations. This is the foundation for many other transports. + +### `transport-io` +For working directly with I/O streams (`tokio::io::AsyncRead` and `tokio::io::AsyncWrite`). + +### `transport-child-process` +Run MCP servers as child processes and communicate via standard I/O. + +Example: +```rust +use rmcp::transport::TokioChildProcess; +use tokio::process::Command; + +let transport = TokioChildProcess::new(Command::new("mcp-server"))?; +let service = client.serve(transport).await?; +``` + + + +## Manage Multi Services + +For many cases you need to manage several service in a collection, you can call `into_dyn` to convert services into the same type. +```rust, ignore +let service = service.into_dyn(); +``` + +## Feature Flags + +RMCP uses feature flags to control which components are included: + +- `client`: Enable client functionality +- `server`: Enable server functionality and the tool system +- `macros`: Enable the `#[tool]` macro (enabled by default) +- Transport-specific features: + - `transport-async-rw`: Async read/write support + - `transport-io`: I/O stream support + - `transport-child-process`: Child process support + - `transport-sse-client` / `transport-sse-server`: SSE support + - `transport-streamable-http-client` / `transport-streamable-http-server`: HTTP streaming +- `auth`: OAuth2 authentication support +- `schemars`: JSON Schema generation (for tool definitions) + + +## Transports + +- `transport-io`: Server stdio transport +- `transport-sse-server`: Server SSE transport +- `transport-child-process`: Client stdio transport +- `transport-sse-client`: Client sse transport +- `transport-streamable-http-server` streamable http server transport +- `transport-streamable-client-server` streamable http server transport + +
+Transport +The transport type must implemented [`Transport`] trait, which allow it send message concurrently and receive message sequentially. +There are 3 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`] | +| sse | [`sse_client::SseClientTransport`] | [`sse_server::SseServer`] | + +#### [IntoTransport](`IntoTransport`) trait +[`IntoTransport`] is a helper trait that implicitly convert 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. + +
+ +## License + +This project is licensed under the terms specified in the repository's LICENSE file. \ No newline at end of file