墨丘利欧
用于构建的Rust库 主控程序 服务器。在MCP中, *客户* 是连接到的LLM主机应用程序(IDE、聊天界面) *服务器* 使模型能够访问工具。 mercutio 在处理传输的同时,处理服务器端协议(解析消息、管理初始化握手、调度工具调用)。核心是一个纯状态机:向它提供JSON-RPC消息,它返回要发送回的内容。
这 无io 设计意味着您可以在stdio、HTTP、WebSockets或其他任何东西上运行它,而无需与库对抗。
定义工具
使用 tool_registry! 定义你的工具。字段文档注释变成LLM看到的JSON模式描述:
mercutio::tool_registry! {
enum MyTools {
GetWeather("get_weather", "Gets current weather for a city") {
/// City name, e.g. "San Francisco".
city: String,
},
SetReminder("set_reminder", "Sets a reminder") {
/// What to remind about.
message: String,
/// Minutes from now.
minutes: u32,
},
}
}Sans IO使用情况
核心API是一个状态机。传入解析后的消息,在输出上匹配:
use mercutio::{McpServer, Output};
let mut server = McpServer::::builder()
.name("my-server")
.version("1.0")
.build();
loop {
let line = read_line_somehow();
let msg = mercutio::parse_line(&line)?;
match server.handle(msg) {
Output::Send(response) => send(response.into_inner()),
Output::ToolCall { tool, responder } => {
let result = match tool {
MyTools::GetWeather(input) => format!("Weather in {}: sunny", input.city),
MyTools::SetReminder(input) => format!("Reminder: {} in {} min", input.message, input.minutes),
};
send(responder.respond(Ok::(result)).into_inner());
}
Output::ProtocolError(_) => break,
Output::None => {}
}
}运输
如果你不想自己连接I/O io-* 特征标志提供现成的传输。这些使用处理程序特性来处理工具调用:
use mercutio::{ToolOutput, io::{McpSessionId, ToolHandler}};
struct MyHandler;
impl ToolHandler for MyHandler {
type Error = std::convert::Infallible;
async fn handle(
&self,
_session_id: Option,
tool: MyTools,
) -> Result {
match tool {
MyTools::GetWeather(input) => {
Ok(format!("Weather in {}: sunny", input.city).into())
}
MyTools::SetReminder(input) => {
Ok(format!("Reminder: {} in {} min", input.message, input.minutes).into())
}
}
}
}ToolHandler 拿 &self 对于并发上下文; MutToolHandler 拿 &mut self 独家访问。会话ID为 Some 对于HTTP(多个客户端共享一个服务器), None 对于stdio(一个进程=一个会话)。闭合通过毯式装置工作: |_session_id, tool| async move { ... }.
东京
使用Tokio异步stdin/stdout:
let server = McpServer::::builder().name("my-server").version("1.0").build();
mercutio::io::tokio::run_stdio(server, MyHandler).await?;io标准库
同步stdin/stdout(无异步运行时):
let server = McpServer::::builder().name("my-server").version("1.0").build();
mercutio::io::stdlib::run_stdio(server, |_session_id, tool| handle_tool(tool))?;io axum
带有会话管理的HTTP传输:
let mut builder = McpServer::::builder();
builder.name("my-server").version("1.0");
let router = mercutio::io::axum::mcp_router(builder, MyHandler);
let app = axum::Router::new().nest("/mcp", router);对于自定义会话存储,请使用 McpRouter::builder() 随着 .storage().
示例
支持两种传输的完整服务器:
use clap::{Parser, Subcommand};
use mercutio::{McpServer, ToolOutput, io::{McpSessionId, ToolHandler}};
mercutio::tool_registry! {
enum MyTools {
Greet("greet", "Greets someone") { name: String },
}
}
struct MyHandler;
impl ToolHandler for MyHandler {
type Error = std::convert::Infallible;
async fn handle(&self, _: Option, tool: MyTools) -> Result {
match tool {
MyTools::Greet(input) => Ok(format!("Hello, {}!", input.name).into()),
}
}
}
#[derive(Parser)]
struct Args {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Mcp,
McpHttp { bind: std::net::SocketAddr },
}
#[tokio::main]
async fn main() -> anyhow::Result {
let args = Args::parse();
let mut builder = McpServer::::builder();
builder.name("greeter").version("1.0");
match args.command {
Command::Mcp => {
mercutio::io::tokio::run_stdio(builder.build(), MyHandler).await?;
}
Command::McpHttp { bind } => {
let router = mercutio::io::axum::mcp_router(builder, MyHandler);
let listener = tokio::net::TcpListener::bind(bind).await?;
axum::serve(listener, router).await?;
}
}
Ok(())
}功能开关
| 特性 | 描述 |
|---|---|
io-stdlib | 同步stdin/stdout传输 |
io-tokio | 异步stdin/stdout传输(东京) |
io-axum | 带有会话管理的HTTP传输(Axum) |
