use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: &'static str,
pub description: &'static str,
pub input_schema: serde_json::Value,
pub http_route: &'static str,
pub http_method: &'static str,
}
pub fn tool_definitions() -> Vec<ToolDefinition> {
vec![
ToolDefinition {
name: "cluster_concepts",
description:
"Cluster doc-comment themes from a set of source contents. Returns ConceptCluster entities labelled by nearest vocab word.",
input_schema: serde_json::json!({
"type": "object",
"properties": {
"contents": {
"type": "array",
"items": { "type": "string" },
"description": "Raw chunk source contents."
},
"file": {
"type": "string",
"description": "Anchor filename for emitted entity ids."
}
},
"required": ["contents"]
}),
http_route: "/analyze/concept-cluster",
http_method: "POST",
},
ToolDefinition {
name: "ner_extract",
description:
"Extract NaturalLanguagePhrase entities from doc-comment text via the optional ONNX NER model. Returns an empty list when the model is not installed.",
input_schema: serde_json::json!({
"type": "object",
"properties": {
"text": { "type": "string", "description": "Source text or pre-extracted doc text." },
"file": { "type": "string" },
"extract_doc_comments_first": {
"type": "boolean",
"description": "When true, pull /// and //! lines from `text` before running NER."
}
},
"required": ["text"]
}),
http_route: "/analyze/ner",
http_method: "POST",
},
ToolDefinition {
name: "ingest_scip",
description:
"Ingest SCIP-derived entity references and edges. Returns the materialised RawEntity list and edge tuples.",
input_schema: serde_json::json!({
"type": "object",
"properties": {
"refs": { "type": "array", "description": "ScipEntityRef objects." },
"edges": { "type": "array", "description": "ScipEdge objects." }
}
}),
http_route: "/analyze/scip-ingest",
http_method: "POST",
},
ToolDefinition {
name: "complexity_hotspots",
description: "Return the top-N chunks by cyclomatic complexity for an index.",
input_schema: serde_json::json!({
"type": "object",
"properties": {
"index_id": { "type": "string" },
"top_n": { "type": "integer", "default": 20 }
},
"required": ["index_id"]
}),
http_route: "/analyze/{index_id}/complexity_hotspots",
http_method: "GET",
},
ToolDefinition {
name: "find_smells",
description: "List chunks with one or more code-smell findings.",
input_schema: serde_json::json!({
"type": "object",
"properties": { "index_id": { "type": "string" } },
"required": ["index_id"]
}),
http_route: "/analyze/{index_id}/smells",
http_method: "GET",
},
ToolDefinition {
name: "analyze_quality",
description: "Aggregate quality stats for an index (avg cyclomatic, %A grade, smell count).",
input_schema: serde_json::json!({
"type": "object",
"properties": { "index_id": { "type": "string" } },
"required": ["index_id"]
}),
http_route: "/analyze/{index_id}/quality",
http_method: "GET",
},
]
}
pub fn placeholder() -> &'static str {
"trusty-analyzer-mcp: transport layer pending; tool definitions available via tool_definitions()"
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tool_definitions_includes_new_analysis_tools() {
let defs = tool_definitions();
let names: Vec<&str> = defs.iter().map(|d| d.name).collect();
assert!(names.contains(&"cluster_concepts"));
assert!(names.contains(&"ner_extract"));
assert!(names.contains(&"ingest_scip"));
}
#[test]
fn tool_definitions_serialise() {
let defs = tool_definitions();
let json = serde_json::to_string(&defs).expect("serialise");
assert!(json.contains("cluster_concepts"));
}
#[test]
fn tool_definitions_have_non_empty_descriptions() {
for def in tool_definitions() {
assert!(
!def.description.is_empty(),
"{} missing description",
def.name
);
assert!(
!def.http_route.is_empty(),
"{} missing http_route",
def.name
);
}
}
}