use std::sync::Arc;
use mcp_skill_framework::prelude::*;
use mcp_skill_framework::rmcp::{
handler::server::router::tool::ToolRouter,
model::{Implementation, ProtocolVersion, ServerCapabilities, ServerInfo},
tool_handler,
transport::stdio,
ServerHandler, ServiceExt,
};
use serde::Deserialize;
use serde_json::json;
#[derive(Clone)]
struct Server {
tool_router: ToolRouter<Server>,
greeting: String,
}
#[derive(Deserialize, schemars::JsonSchema)]
struct GreetArgs {
name: String,
}
struct Greet;
impl Skill<Server> for Greet {
fn name(&self) -> &'static str {
"greet"
}
fn description(&self) -> &'static str {
"Greet someone using the server's configured greeting."
}
fn schema(&self) -> Arc<JsonObject> {
schema_for::<GreetArgs>()
}
fn call<'a>(
&self,
ctx: SkillCtx<'a, Server>,
) -> BoxFuture<'a, Result<CallToolResult, McpError>> {
Box::pin(async move {
let (server, a) = ctx.parse::<GreetArgs>()?;
Ok(text_result(format!("{}, {}!", server.greeting, a.name)))
})
}
}
#[derive(Deserialize, schemars::JsonSchema)]
struct RoundArgs {
value: f64,
#[serde(default)]
mode: Option<String>,
}
struct Round;
impl Skill<Server> for Round {
fn name(&self) -> &'static str {
"round"
}
fn description(&self) -> &'static str {
"Round a number `nearest` (default), `up`, or `down`."
}
fn schema(&self) -> Arc<JsonObject> {
schema_for::<RoundArgs>()
}
fn validation_rules(&self) -> &'static [Rule] {
&[Rule::OneOf {
field: "mode",
values: &["nearest", "up", "down"],
}]
}
fn call<'a>(
&self,
ctx: SkillCtx<'a, Server>,
) -> BoxFuture<'a, Result<CallToolResult, McpError>> {
Box::pin(async move {
let (_server, a) = ctx.parse::<RoundArgs>()?;
let out = match a.mode.as_deref().unwrap_or("nearest") {
"up" => a.value.ceil(),
"down" => a.value.floor(),
_ => a.value.round(),
};
Ok(text_result(json!({ "result": out }).to_string()))
})
}
}
fn skills() -> Vec<Box<dyn Skill<Server>>> {
vec![Box::new(Greet), Box::new(Round)]
}
#[tool_handler(router = self.tool_router)]
impl ServerHandler for Server {
fn get_info(&self) -> ServerInfo {
let mut implementation = Implementation::from_build_env();
implementation.name = "mcp-skill-framework-example".to_string();
implementation.version = env!("CARGO_PKG_VERSION").to_string();
ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
.with_server_info(implementation)
.with_protocol_version(ProtocolVersion::V_2024_11_05)
.with_instructions(
"Example MCP server built with mcp-skill-framework. Tools: greet, round.",
)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut router = ToolRouter::new();
for route in skills().into_iter().map(route_skill) {
router.add_route(route);
}
let server = Server {
tool_router: router,
greeting: "Hello".to_string(),
};
eprintln!(
"mcp-skill-framework example: serving {} tools over stdio…",
server.tool_router.list_all().len()
);
let service = server.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}