Files
kebab/crates/kebab-mcp/src/error.rs
th-kim0823 8ca8e18d12 feat(kebab-mcp): schema tool (fb-30)
First tool wired — `schema` (no input args, returns schema.v1 JSON
mirroring `kebab schema --json`). Establishes the per-tool module
pattern (crates/kebab-mcp/src/tools/<name>.rs) + error helper that maps
anyhow::Error to MCP CallToolResult.error with error.v1 content.

Dispatch pattern: manual dispatch — explicit `list_tools` + `call_tool`
overrides on `impl ServerHandler for KebabHandler` with a
`match request.name.as_ref()` arm per tool. No proc-macro magic.
Tasks 5-7 should add a new arm + new tools/<name>.rs following the same
pattern; also add a `Tool::new(...)` entry in `list_tools`.

API shapes confirmed from rmcp 1.6 source:
- Content = Annotated<RawContent>; text via `Content::text(s)`; pattern
  match via `&content.raw` → `RawContent::Text(t)` → `t.text`
- CallToolResult::success(Vec<Content>) / ::error(Vec<Content>)
- ListToolsResult::with_all_items(Vec<Tool>)
- schema_for_empty_input() from rmcp::handler::server::common

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 15:38:00 +09:00

23 lines
859 B
Rust

//! Map `anyhow::Error` returned by kebab-app facades to MCP
//! `CallToolResult` with `isError: true` + error.v1 JSON content.
use rmcp::model::{CallToolResult, Content};
use kebab_app::classify;
/// Convert an `anyhow::Error` to a `CallToolResult` with `isError: true`
/// and the serialised `error.v1` envelope as the text content.
pub fn to_tool_error(err: &anyhow::Error) -> CallToolResult {
let v1 = classify(err, false);
let body = serde_json::to_string(&v1).unwrap_or_else(|_| {
r#"{"schema_version":"error.v1","code":"generic","message":"serialize failed"}"#
.to_string()
});
CallToolResult::error(vec![Content::text(body)])
}
/// Wrap a successful wire-schema JSON string as a `CallToolResult`.
pub fn to_tool_success(json: String) -> CallToolResult {
CallToolResult::success(vec![Content::text(json)])
}