ci / checks (push) Failing after 14s
The crate maps MCP tool catalogs to clap commands and back, and has no rhema dependencies — it was in-tree only because the workspace glob reached it. Its own repository gives it its own release cadence and its own crates.io token, scoped to this crate alone.
2.3 KiB
2.3 KiB
mcp-to-cli
MCP servers advertise their tools as (name, description, JSON Schema)
triples. mcp-to-cli turns each CLI-safe tool into a clap subcommand
whose flags come from the schema's top-level properties, and turns
parsed argv back into the {property: value} object the server
expects.
It is pure mapping: no I/O, no processes, no MCP protocol. clap,
serde_json, and thiserror are the whole dependency list.
Use
use mcp_to_cli::{Payload, ToolInfo, args, command};
fn main() {
let catalog = [ToolInfo {
name: "create_pull_request".into(),
description: "Create a new pull request".into(),
input_schema: serde_json::json!({
"type": "object",
"properties": {
"title": {"type": "string"},
"draft": {"type": "boolean", "default": false}
},
"required": ["title"]
}),
}];
let cmd = command("github", &catalog);
let matches = cmd
.try_get_matches_from(["github", "create_pull_request", "--title", "Ship it"])
.expect("argv parses");
match args(&cmd, &matches).expect("argv maps") {
Payload::Mapped(value) => println!("{value}"), // {"title":"Ship it"}
Payload::Overridden(raw) => println!("{raw}"), // the --json string, verbatim
Payload::Schema { tool } => println!("print the schema of {tool}"),
_ => unreachable!(),
}
}
The mapping is conservative, with an escape hatch at every level of schema complexity:
- enums become clap
possible_values; - scalar arrays become repeatable flags;
- every shape the flag form cannot express collapses to a
--<property>_jsonflag; --jsoncarries the whole argument object verbatim, for scripts;- schema defaults are never pre-filled: an omitted flag omits the key, and the server stays the validator.
Discovery rides along: a bare invocation answers with the tool list
(list_json), and --schema asks for one tool's input_schema
instead of calling it (Payload::Schema).
Examples
cargo run --example github # the real GitHub create_pull_request schema
cargo run --example blender # a Blender MCP server's tools
cargo run --example catalog # every tool of a JSON catalog
cargo test drives the mapping matrix against vendored real schemas.