Skip to main content

aprender_mcp/tools/
version.rs

1//! `apr.version` — M1 stub tool that reports the aprender-mcp crate version.
2
3#![allow(clippy::disallowed_methods)] // serde_json::json! macro expands to .unwrap() internally
4
5use crate::types::{InputSchema, ToolCallResult, ToolDefinition};
6
7/// Tool name registered with MCP clients.
8pub const NAME: &str = "apr.version";
9
10/// Crate version baked in at compile time.
11pub const VERSION: &str = env!("CARGO_PKG_VERSION");
12
13/// Return the MCP tool definition for `apr.version`.
14///
15/// FALSIFY-MCP-008: the `inputSchema` is parsed from the build-time codegen
16/// constant `crate::schemas::APR_VERSION_SCHEMA`, which `build.rs` emits from
17/// `contracts/apr-mcp-tool-schemas-v1.yaml`. The contract is the single
18/// source of truth — the live `tools/list` response and the YAML must agree
19/// byte-for-byte after JSON canonicalization (asserted by
20/// `tests/falsify_mcp_008.rs`).
21#[must_use]
22pub fn version_tool_definition() -> ToolDefinition {
23    let input_schema: InputSchema = serde_json::from_str(crate::schemas::APR_VERSION_SCHEMA)
24        .expect(
25            "FALSIFY-MCP-008: apr.version codegen constant must parse as InputSchema; \
26             regenerate by editing contracts/apr-mcp-tool-schemas-v1.yaml and rebuilding",
27        );
28    ToolDefinition {
29        name: NAME.to_string(),
30        description: crate::schemas::APR_VERSION_DESCRIPTION.to_string(),
31        input_schema,
32    }
33}
34
35/// Execute the `apr.version` tool.
36#[must_use]
37pub fn call(_args: &serde_json::Value) -> ToolCallResult {
38    let payload = serde_json::json!({
39        "server": crate::SERVER_NAME,
40        "version": VERSION,
41        "protocol_version": crate::PROTOCOL_VERSION,
42    });
43    ToolCallResult::success(payload.to_string())
44}
45
46/// HELIX-IDEA-002 — unified-signature shim for the inventory dispatcher.
47/// `apr.version` is sync; `cancel`, `sink`, `token` are accepted only for
48/// ABI uniformity with cancel-aware tools.
49pub fn dispatch(
50    args: &serde_json::Value,
51    _cancel: &std::sync::mpsc::Receiver<()>,
52    _sink: Option<&crate::server::NotificationSink>,
53    _token: Option<serde_json::Value>,
54) -> ToolCallResult {
55    call(args)
56}
57
58crate::register_mcp_tool!(
59    name: NAME,
60    definition: version_tool_definition,
61    dispatch: dispatch,
62);
63
64#[cfg(test)]
65#[allow(clippy::disallowed_methods)] // serde_json::json! expands to code that hits unwrap()
66mod tests {
67    use super::*;
68
69    #[test]
70    fn definition_has_correct_name() {
71        let def = version_tool_definition();
72        assert_eq!(def.name, "apr.version");
73        assert!(def.input_schema.required.is_empty());
74        assert_eq!(def.input_schema.schema_type, "object");
75    }
76
77    #[test]
78    fn call_returns_version_payload() {
79        let result = call(&serde_json::json!({}));
80        assert!(result.is_error.is_none());
81        let text = &result.content[0].text;
82        let parsed: serde_json::Value = serde_json::from_str(text).expect("valid json");
83        assert_eq!(parsed["server"], "aprender-mcp");
84        assert_eq!(parsed["version"], VERSION);
85        assert_eq!(parsed["protocol_version"], "2024-11-05");
86    }
87}