fastmcp-rs 0.2.0

Rust prototype for the FastMCP server
Documentation
use std::net::SocketAddr;

use fastmcp_rs::{
    FastMcpServer,
    http::start_http,
    mcp_register_tools,
    mcp_response,
    mcp_text,
    tool::ToolResponse,
};
use serde_json::Value;

// Attribute-style HTTP tool: returns basic info about URL length (no external fetch)
#[fastmcp_rs_macros::mcp_tool(
    name = "url_info",
    description = "Returns simple info about a URL string",
    parameters_json(
        "type": "object",
        "required": ["url"],
        "properties": { "url": { "type": "string" } }
    )
)]
async fn url_info(_: fastmcp_rs::InvocationContext, payload: Value) -> fastmcp_rs::Result<ToolResponse> {
    let url = payload.get("url").and_then(Value::as_str).ok_or_else(|| fastmcp_rs::FastMcpError::InvalidInvocation("expected string 'url'".into()))?;
    let info = format!("url='{}' length={}", url, url.len());
    Ok(mcp_response!([ mcp_text!(info) ]))
}

#[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("HttpAttr")
        .instructions("Call url_info over HTTP")
        .build()
        .into_shared();

    mcp_register_tools!(server, [ url_info_tool() ]);

    let addr = SocketAddr::from(([127, 0, 0, 1], 0));
    let handle = start_http(server.clone(), addr).await?;
    tracing::info!("HTTP server listening on {}", handle.addr());

    // Keep running until Ctrl+C
    tokio::signal::ctrl_c().await?;
    handle.shutdown().await;
    Ok(())
}