1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! # mcp-skill-framework
//!
//! A small framework for building [Model Context Protocol][mcp] 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 ([`Rule`]s). 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
//!
//! ```no_run
//! 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)));
//! ```
//!
//! [mcp]: https://modelcontextprotocol.io
pub use ;
pub use ;
pub use FamilyMeta;
pub use ;
pub use ;
use ;
use ErrorData as McpError;
/// 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`]).
/// Build an `invalid_params` MCP error from anything `Display`. Use for bad
/// caller input that the declarative [`validation`] layer didn't catch.
/// 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).