1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//! Shared schema helpers for the pmcp-backed MCP tool handlers.
//!
//! KAIZEN-0189 / R17 #3: pmcp 1.10's `ToolHandler::metadata()` defaults to
//! `None`, which causes `ServerCoreBuilder::tool()` to fall back to an empty
//! `ToolInfo { description: None, input_schema: json!({}) }`. MCP clients
//! then see every tool as "no description, no schema" in `tools/list`.
//!
//! The fix is for each `ToolHandler` impl to override `metadata()` and return
//! a `ToolInfo` whose `input_schema` reflects the actual `#[derive(Deserialize)]`
//! args struct. Since the 24 core tools here do **not** share a `schema()`
//! method on their inner types, we declare each tool's schema inline via the
//! `build_tool_info` helper below and keep every schema adjacent to the handler
//! file that uses it.
//!
//! Companion PR #351 applied the same pattern to the 4 `pmat_*` handlers in
//! `agent_context_handlers.rs`, where the inner `McpTool::schema()` already
//! existed. Together these three PRs (KAIZEN-0174 / KAIZEN-0189) ensure every
//! MCP tool advertises a non-empty `inputSchema`.
use ToolInfo;
use ;
/// Build a `ToolInfo` with a non-empty description and JSON-Schema.
///
/// Every `ToolHandler::metadata()` override in this crate should funnel
/// through this helper so the behavior stays consistent and auditable.
/// Convenience for "paths: string[]"-shaped tools that take only a paths
/// array plus optional fields. Extra properties can be added before calling.
/// Convert MCP `paths` arguments to `PathBuf`s, rejecting ones that do not exist.
///
/// # Why this exists (GH #639)
///
/// Every path-taking tool did `params.paths.into_iter().map(PathBuf::from)` and
/// analysed whatever came back. A path that does not exist walks to zero files,
/// so `tools/call analyze_complexity {"paths": ["/typo"]}` returned
/// `isError: false` with `{"status":"completed","total_files":0,"violations":[]}`
/// — indistinguishable from a clean repository. Unlike the CLI, an MCP client
/// has no exit code to fall back on, so a mistyped path read as a passing
/// quality check.
///
/// The CLI was given the same guard in v3.28.1
/// (`cli::ensure_analysis_path_exists`); this is its MCP counterpart, so the two
/// surfaces now agree. It also makes the surface self-consistent: `quality_gate`
/// already errored on a nonexistent `file`.
///
/// # Errors
///
/// Returns a validation error naming every missing path. An empty `paths` list
/// is allowed through — tools interpret that as "use the default scope", which
/// is a separate contract.