Skip to main content

Crate mcp_skill_framework

Crate mcp_skill_framework 

Source
Expand description

§mcp-skill-framework

A small framework for building Model Context Protocol servers on top of rmcp as a uniform layer of self-contained tools called skills.

Each tool you expose is a Skill: a type with a name, a description, a JSON-schema’d argument struct, and an async call body. The framework adds the cross-cutting machinery every skill ends up wanting:

  • Declarative input validation (validation) — assert ranges, enums, mutual exclusion, regex, and length as data (Rules). The dispatcher evaluates them before your body runs and returns a structured {"validation_failed": [...]} payload a calling model can correct from.
  • Capability probes (SkillCapability) — answer “can this host actually run this tool?” (a binary on $PATH, a reachable socket) separately from whether the operator enabled it. capability::resolve combines each tool’s probe with its family’s (“family Unavailable wins”), and routes_gated blocks unavailable tools at dispatch with a reason + hint the caller can act on.
  • Family metadata (FamilyMeta) — group related skills, describe them, and probe their shared host requirement as a unit.
  • Rich descriptions (describe) — render a skill or family (description, use cases, worked examples, validation rules, argument schema) for an on-demand introspection tool.
  • A ready-made dispatcher (route_skill) — adapt a Skill into an rmcp tool route with the validation gate already wired in.

The Skill trait is generic over a server-state type S — whatever shared state your tools need (HTTP clients, DB handles, config). The framework never inspects S; it just hands each call a &S.

§Quickstart

use std::sync::Arc;
use futures::future::BoxFuture;
use mcp_skill_framework::{schema_for, text_result, Rule, Skill, SkillCtx, SkillExample};
use rmcp::model::{CallToolResult, JsonObject};
use rmcp::ErrorData as McpError;
use serde::Deserialize;

struct App; // your shared server state

#[derive(Deserialize, schemars::JsonSchema)]
struct FormatArgs {
    /// Total seconds to format.
    seconds: i64,
    /// Output style: `human` or `hms`.
    #[serde(default)]
    style: Option<String>,
}

struct Format;
impl Skill<App> for Format {
    fn name(&self) -> &'static str { "duration_format" }
    fn description(&self) -> &'static str { "Format seconds as `human` or `hms`." }
    fn schema(&self) -> Arc<JsonObject> { schema_for::<FormatArgs>() }
    fn validation_rules(&self) -> &'static [Rule] {
        &[Rule::OneOf { field: "style", values: &["human", "hms"] }]
    }
    fn examples(&self) -> &'static [SkillExample] {
        &[SkillExample { title: "HH:MM:SS", args: r#"{"seconds": 9045, "style": "hms"}"#, note: None }]
    }
    fn call<'a>(&self, ctx: SkillCtx<'a, App>) -> BoxFuture<'a, Result<CallToolResult, McpError>> {
        Box::pin(async move {
            let (_app, a) = ctx.parse::<FormatArgs>()?;
            Ok(text_result(format!("{} seconds", a.seconds)))
        })
    }
}

// Register with an rmcp ToolRouter:
// let router = rmcp::handler::server::router::tool::ToolRouter::new()
//     .with_route(mcp_skill_framework::route_skill(Box::new(Format)));

Re-exports§

pub use capability::Capabilities;
pub use capability::SkillCapability;
pub use dispatch::route_skill;
pub use dispatch::route_skill_gated;
pub use dispatch::routes_gated;
pub use dispatch::with_extra_property;
pub use family::FamilyMeta;
pub use skill::schema_for;
pub use skill::NoArgs;
pub use skill::Skill;
pub use skill::SkillCtx;
pub use skill::SkillExample;
pub use validation::evaluate;
pub use validation::FieldViolation;
pub use validation::Rule;
pub use validation::ValidationResult;

Modules§

capability
Capability probes — “can this host actually run this tool?”
describe
Human-readable rendering of skills and families — “the description” layer.
dispatch
Turning skills into rmcp tool routes.
family
Family metadata — group related skills and describe them as a unit.
skill
The skill contract — the uniform capability unit of an MCP server.
validation
Structured input validation for the Skill contract.

Functions§

internal
Build an internal_error MCP error from anything Display. Use for failures that aren’t the caller’s fault (an upstream API, a socket, a parse of data you fetched).
invalid
Build an invalid_params MCP error from anything Display. Use for bad caller input that the declarative validation layer didn’t catch.
text_result
Wrap a string as a successful single-text-content tool result. The idiomatic return for a skill body that produced one textual answer (often a JSON string built with serde_json).