fastmcp-rs 0.2.0

Rust prototype for the FastMCP server
Documentation
use fastmcp_rs::{
    FastMcpServer,
    run_stdio,
    mcp_register_tools,
    mcp_prompt_text,
    mcp_register_prompts,
    mcp_response,
    mcp_text,
    tool::ToolResponse,
};
use serde_json::{Value, json};
use tokio::signal;

// Attribute-style tools: add and multiply
#[fastmcp_rs_macros::mcp_tool(
    name = "add",
    description = "Adds two numbers",
    parameters_json(
        "type": "object",
        "required": ["a", "b"],
        "properties": { "a": { "type": "number" }, "b": { "type": "number" } }
    ),
    annotations(category = json!("math"))
)]
async fn add(_: fastmcp_rs::InvocationContext, payload: Value) -> fastmcp_rs::Result<ToolResponse> {
    let a = payload.get("a").and_then(Value::as_f64).ok_or_else(|| fastmcp_rs::FastMcpError::InvalidInvocation("expected float 'a'".into()))?;
    let b = payload.get("b").and_then(Value::as_f64).ok_or_else(|| fastmcp_rs::FastMcpError::InvalidInvocation("expected float 'b'".into()))?;
    Ok(mcp_response!([ mcp_text!(format!("{} + {} = {}", a, b, a + b)) ]))
}

#[fastmcp_rs_macros::mcp_tool(
    name = "multiply",
    description = "Multiplies two numbers",
    parameters_json(
        "type": "object",
        "required": ["a", "b"],
        "properties": { "a": { "type": "number" }, "b": { "type": "number" } }
    ),
    annotations(category = json!("math"))
)]
async fn multiply(_: fastmcp_rs::InvocationContext, payload: Value) -> fastmcp_rs::Result<ToolResponse> {
    let a = payload.get("a").and_then(Value::as_f64).ok_or_else(|| fastmcp_rs::FastMcpError::InvalidInvocation("expected float 'a'".into()))?;
    let b = payload.get("b").and_then(Value::as_f64).ok_or_else(|| fastmcp_rs::FastMcpError::InvalidInvocation("expected float 'b'".into()))?;
    Ok(mcp_response!([ mcp_text!(format!("{} × {} = {}", a, b, a * b)) ]))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let server = FastMcpServer::builder()
        .name("MathOps")
        .instructions("Use add or multiply tools")
        .build()
        .into_shared();

    // Register via generated factory functions
    mcp_register_tools!(server, [ add_tool(), multiply_tool() ]);

    mcp_register_prompts!(server, [
        mcp_prompt_text!{
            name: "calc",
            role: "user",
            text: "Please compute {{ a }} op {{ b }}",
            description: "Requests a math operation",
            parameters: json!({
                "type": "object",
                "required": ["a", "b", "op"],
                "properties": {
                    "a": { "type": "number" },
                    "b": { "type": "number" },
                    "op": { "type": "string", "enum": ["add", "multiply"] }
                }
            })
        }
    ]);

    tokio::select! {
        result = run_stdio(server.clone()) => result?,
        _ = signal::ctrl_c() => {
            tracing::info!("received Ctrl+C, shutting down");
        }
    }

    Ok(())
}