Expand description
A WebAssembly-focused fork of the Rust SDK for the Model Context Protocol (MCP).
The MCP is a protocol that allows AI assistants to communicate with other
services. mcpkit-rs is a WebAssembly-enhanced fork of the official Rust
implementation, extending it with WASM runtime integration for tool execution.
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:
use std::sync::Arc;
use mcpkit_rs::{ErrorData as McpError, model::*, tool, tool_router, handler::server::tool::ToolRouter};
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct Counter {
counter: Arc<Mutex<i32>>,
tool_router: ToolRouter<Self>,
}
#[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<CallToolResult, McpError> {
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:
#[derive(Serialize, Deserialize, JsonSchema)]
struct CalculationRequest {
a: i32,
b: i32,
operation: String,
}
#[derive(Serialize, Deserialize, JsonSchema)]
struct CalculationResult {
result: i32,
operation: String,
}
#[tool(name = "calculate", description = "Perform a calculation")]
async fn calculate(&self, params: Parameters<CalculationRequest>) -> Result<Json<CalculationResult>, 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:
use anyhow::Result;
use mcpkit_rs::{model::CallToolRequestParams, service::ServiceExt};
#[cfg(feature = "transport-child-process")]
#[cfg_attr(docsrs, doc(cfg(feature = "transport-child-process")))]
use mcpkit_rs::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(())
}§mcpkit-rs: WebAssembly-focused MCP SDK Fork
mcpkit-rs is a WebAssembly-focused fork of the official Rust MCP SDK. This fork extends the original implementation with WebAssembly runtime integration, allowing tools to be executed in WASM environments. It provides a complete implementation of the Model Context Protocol (MCP) for building both servers that expose capabilities to AI assistants and clients that interact with such servers.
§Fork Information
This is a community fork that builds upon the official MCP Rust SDK with the following enhancements:
- WebAssembly Runtime Integration: Execute MCP tools in WASM environments using WasmEdge or other WASM runtimes
- WASM Tool Manifest Support: Define and load tools from WASM modules with manifest declarations
- Extended Transport Options: Additional transport mechanisms optimized for WASM deployment scenarios
- Full MCP Compatibility: Maintains complete compatibility with the MCP specification
For the official Rust SDK without WebAssembly extensions, please visit the original repository.
§Quick Start
§Server Implementation
Creating a server with tools is simple using the #[tool] macro:
use mcpkit_rs::{
ServerHandler, ServiceExt,
handler::server::tool::ToolRouter,
model::*,
tool, tool_handler, tool_router,
transport::stdio,
ErrorData as McpError,
};
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct Counter {
counter: Arc<Mutex<i32>>,
tool_router: ToolRouter<Self>,
}
#[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<CallToolResult, McpError> {
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<CallToolResult, McpError> {
let counter = self.counter.lock().await;
Ok(CallToolResult::success(vec![Content::text(
counter.to_string(),
)]))
}
}
// Implement the server handler
#[tool_handler]
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()),
capabilities: ServerCapabilities::builder().enable_tools().build(),
..Default::default()
}
}
}
// Run the server
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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(())
}§Structured Output
Tools can return structured JSON data with schemas. Use the Json wrapper:
#[derive(Serialize, Deserialize, JsonSchema)]
struct CalculationRequest {
a: i32,
b: i32,
operation: String,
}
#[derive(Serialize, Deserialize, JsonSchema)]
struct CalculationResult {
result: i32,
operation: String,
}
#[tool(name = "calculate", description = "Perform a calculation")]
async fn calculate(&self, params: Parameters<CalculationRequest>) -> Result<Json<CalculationResult>, 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
mcpkit-rs implements the task lifecycle from SEP-1686 so long-running or asynchronous tool calls can be queued and polled safely.
- Create: set the
taskfield onCallToolRequestParamto ask the server to enqueue the tool call. The response is aCreateTaskResultthat includes the generatedtask.task_id. - Inspect: use
tasks/get(GetTaskInfoRequest) to retrieve metadata such as status, timestamps, TTL, and poll interval. - Await results: call
tasks/result(GetTaskResultRequest) to block until the task completes and receive either the finalCallToolResultpayload or a protocol error. - Cancel: call
tasks/cancel(CancelTaskRequest) to request termination of a running task.
To expose task support, enable the tasks capability when building ServerCapabilities. The #[task_handler] macro and OperationProcessor utility provide reference implementations for enqueuing, tracking, and collecting task results.
§Client Implementation
Creating a client to interact with a server:
use mcpkit_rs::{
ServiceExt,
model::CallToolRequestParams,
transport::{ConfigureCommandExt, TokioChildProcess},
};
use tokio::process::Command;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 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(CallToolRequestParams {
meta: None,
name: "git_status".into(),
arguments: serde_json::json!({ "repo_path": "." }).as_object().cloned(),
task: None,
})
.await?;
println!("Result: {result:#?}");
// Gracefully close the connection
service.cancel().await?;
Ok(())
}For more examples, see the examples directory in the repository.
§Transport Options
mcpkit-rs 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:
use mcpkit_rs::transport::TokioChildProcess;
use tokio::process::Command;
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 struct from NotificationContext and RequestContext.
impl ServerHandler for Handler {
async fn on_progress(
&self,
notification: ProgressNotificationParam,
context: NotificationContext<RoleServer>,
) {
let peer = context.peer;
let _ = peer
.notify_logging_message(LoggingMessageNotificationParam {
level: LoggingLevel::Info,
logger: None,
data: serde_json::json!({
"message": format!("Progress: {}", notification.progress),
}),
})
.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.
let service = service.into_dyn();§Feature Flags
mcpkit-rs uses feature flags to control which components are included:
client: Enable client functionalityserver: Enable server functionality and the tool systemmacros: Enable the#[tool]macro (enabled by default)- Transport-specific features:
transport-async-rw: Async read/write supporttransport-io: I/O stream supporttransport-child-process: Child process supporttransport-streamable-http-client/transport-streamable-http-server: HTTP streaming (client agnostic, seeStreamableHttpClientTransportfor details)transport-streamable-http-client-reqwest: a defaultreqwestimplementation of the streamable http client
auth: OAuth2 authentication supportschemars: JSON Schema generation (for tool definitions)
§Transports
transport-io: Server stdio transporttransport-child-process: Client stdio transporttransport-streamable-http-serverstreamable http server transporttransport-streamable-http-clientstreamable http client transport
Transport
The transport type must implement the 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 | TokioChildProcess | stdio |
| streamable http | StreamableHttpClientTransport | StreamableHttpService |
§IntoTransport trait
IntoTransport is a helper trait that implicitly converts a type into a transport type.
These types automatically implement IntoTransport:
- A type that implements both
futures::Sinkandfutures::Stream, or a tuple(Tx, Rx)whereTxisfutures::SinkandRxisfutures::Stream. - A type that implements both
tokio::io::AsyncReadandtokio::io::AsyncWrite, or a tuple(R, W)whereRistokio::io::AsyncReadandWistokio::io::AsyncWrite. - A type that implements the
Workertrait. - A type that implements the
Transporttrait.
§License
This project is licensed under the terms specified in the repository’s LICENSE file.
Re-exports§
pub use error::ErrorData;pub use handler::client::ClientHandler;clientpub use handler::server::ServerHandler;serverpub use handler::server::policy::PolicyEnabledServer;policypub use handler::server::wrapper::Json;serverpub use service::Peer;clientorserverpub use service::Service;clientorserverpub use service::ServiceError;clientorserverpub use service::ServiceExt;clientorserverpub use service::RoleClient;clientpub use service::serve_client;clientpub use service::RoleServer;serverpub use service::serve_server;serverpub use schemars;schemarspub use serde;macrospub use serde_json;macros
Modules§
- bundle
distribution - Bundle distribution system for mcpkit-rs
- config
config - Configuration integration for mcpkit-rs
- handler
- model
- Basic data types in MCP specification
- service
clientorserver - task_
manager server - transport
- Transport
- wasm
wasm-tools - WASM tool execution support for MCP servers
Macros§
- const_
string - elicit_
safe elicitation - Macro to mark types as safe for elicitation by verifying they generate object schemas.
- object
macros - Use this macro just like
serde_json::json! - paste
macrosandserver
Enums§
- Rmcp
Error - This is an unified error type for the errors could be returned by the service.
Type Aliases§
- Error
Deprecated
Attribute Macros§
- prompt
macrosandserver - prompt
- prompt_
handler macrosandserver - prompt_handler
- prompt_
router macrosandserver - prompt_router
- task_
handler macrosandserver - task_handler
- tool
macrosandserver - tool
- tool_
handler macrosandserver - tool_handler
- tool_
router macrosandserver - tool_router