aprender_mcp/tools/
validate.rs1#![allow(clippy::disallowed_methods)] use crate::tools::args::{self, try_arg};
14use crate::tools::subprocess::run_apr;
15use crate::types::{InputSchema, ToolCallResult, ToolDefinition};
16
17pub const NAME: &str = "apr.validate";
19
20#[must_use]
29pub fn validate_tool_definition() -> ToolDefinition {
30 let input_schema: InputSchema = serde_json::from_str(crate::schemas::APR_VALIDATE_SCHEMA)
31 .expect(
32 "FALSIFY-MCP-008: apr.validate codegen constant must parse as InputSchema; \
33 regenerate by editing contracts/apr-mcp-tool-schemas-v1.yaml and rebuilding",
34 );
35 ToolDefinition {
36 name: NAME.to_string(),
37 description: crate::schemas::APR_VALIDATE_DESCRIPTION.to_string(),
38 input_schema,
39 }
40}
41
42#[must_use]
44pub fn call(args: &serde_json::Value) -> ToolCallResult {
45 let model_path = try_arg!(args::required_str(args, "model_path"));
46 run_apr(&["validate", model_path, "--json"])
47}
48
49pub fn dispatch(
51 args: &serde_json::Value,
52 _cancel: &std::sync::mpsc::Receiver<()>,
53 _sink: Option<&crate::server::NotificationSink>,
54 _token: Option<serde_json::Value>,
55) -> ToolCallResult {
56 call(args)
57}
58
59crate::register_mcp_tool!(
60 name: NAME,
61 definition: validate_tool_definition,
62 dispatch: dispatch,
63);
64
65#[cfg(test)]
66#[allow(clippy::disallowed_methods)] mod tests {
68 use super::*;
69
70 #[test]
71 fn definition_has_correct_name_and_required_field() {
72 let def = validate_tool_definition();
73 assert_eq!(def.name, "apr.validate");
74 assert_eq!(def.input_schema.schema_type, "object");
75 assert_eq!(def.input_schema.required, vec!["model_path".to_string()]);
76 assert!(def.input_schema.properties.contains_key("model_path"));
77 }
78
79 #[test]
80 fn missing_model_path_returns_error() {
81 let result = call(&serde_json::json!({}));
82 assert_eq!(result.is_error, Some(true));
83 assert!(result.content[0].text.contains("model_path"));
84 }
85
86 #[test]
91 fn nonstring_model_path_is_reported_as_a_type_error_not_as_missing() {
92 let result = call(&serde_json::json!({ "model_path": 42 }));
93 assert_eq!(result.is_error, Some(true));
94 let text = &result.content[0].text;
95 assert!(
96 !text.contains("Missing"),
97 "model_path WAS supplied; reporting it as missing sends the caller \
98 to fix the wrong thing. got: {text}"
99 );
100 assert!(
101 text.contains("model_path"),
102 "must name the argument: {text}"
103 );
104 assert!(
105 text.contains("string"),
106 "must state the expected type: {text}"
107 );
108 assert!(text.contains("42"), "must quote what was received: {text}");
109 }
110}