include!(concat!(env!("OUT_DIR"), "/mcp_tool_schemas_gen.rs"));
use pmcp::types::ToolInfo;
use serde_json::Value;
#[must_use]
pub fn lookup_raw_schema(name: &str) -> &'static str {
MCP_TOOL_SCHEMAS
.iter()
.find_map(|(n, json)| if *n == name { Some(*json) } else { None })
.unwrap_or_else(|| {
panic!(
"KAIZEN-0178: no MCP tool schema registered for `{name}`. \
Add `mcp_tool_schemas/{name}.json` and rebuild."
)
})
}
#[must_use]
pub fn raw_schema_to_tool_info(raw: &str) -> ToolInfo {
let parsed: Value =
serde_json::from_str(raw).expect("KAIZEN-0178: generated schema is valid JSON");
let name = parsed
.get("name")
.and_then(Value::as_str)
.expect("KAIZEN-0178: schema has name")
.to_string();
let description = parsed
.get("description")
.and_then(Value::as_str)
.map(String::from);
let input_schema = parsed
.get("inputSchema")
.cloned()
.expect("KAIZEN-0178: schema has inputSchema");
ToolInfo::new(name, description, input_schema)
}
#[must_use]
pub fn tool_info_for(name: &str) -> ToolInfo {
raw_schema_to_tool_info(lookup_raw_schema(name))
}
#[macro_export]
macro_rules! tool_metadata {
($name:tt) => {{
let raw: &'static str = __kaizen0178_schema_const!($name);
$crate::mcp_pmcp::tool_schemas_generated::raw_schema_to_tool_info(raw)
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_is_not_empty() {
assert!(
!MCP_TOOL_SCHEMAS.is_empty(),
"at least the 2 PoC schemas (pmat_query_code, quality_gate) must be generated"
);
}
#[test]
fn registry_is_sorted_and_unique() {
for pair in MCP_TOOL_SCHEMAS.windows(2) {
assert!(
pair[0].0 < pair[1].0,
"tool schemas must be sorted and unique: {} vs {}",
pair[0].0,
pair[1].0
);
}
}
#[test]
fn every_schema_parses_and_has_object_input() {
for (name, raw) in MCP_TOOL_SCHEMAS {
let info = raw_schema_to_tool_info(raw);
assert_eq!(info.name, *name, "ToolInfo.name must match registry key");
assert!(
info.description.as_ref().is_some_and(|s| !s.is_empty()),
"{name}: description must be present and non-empty"
);
assert_eq!(
info.input_schema.get("type").and_then(Value::as_str),
Some("object"),
"{name}: inputSchema.type must be \"object\""
);
}
}
#[test]
fn poc_pmat_tool_present() {
let info = tool_info_for("pmat_query_code");
assert_eq!(info.name, "pmat_query_code");
let required = info
.input_schema
.get("required")
.and_then(Value::as_array)
.expect("pmat_query_code has required array");
assert!(
required.iter().any(|v| v.as_str() == Some("query")),
"`query` must remain a required property"
);
}
#[test]
fn poc_core_tool_present() {
let info = tool_info_for("quality_gate");
assert_eq!(info.name, "quality_gate");
assert!(
info.input_schema
.get("properties")
.and_then(|p| p.get("paths"))
.is_some(),
"quality_gate must expose `paths` property"
);
}
#[test]
#[should_panic(expected = "KAIZEN-0178: no MCP tool schema registered")]
fn unknown_tool_panics() {
let _ = tool_info_for("does_not_exist");
}
#[test]
fn macro_expands_to_tool_info() {
let info = tool_metadata!("quality_gate");
assert_eq!(info.name, "quality_gate");
}
}