systemprompt_cli/commands/analytics/tools/
mod.rs1mod list;
12mod show;
13mod stats;
14mod trends;
15
16use anyhow::Result;
17use clap::Subcommand;
18use schemars::JsonSchema;
19use serde::{Deserialize, Serialize};
20use systemprompt_identifiers::{AgentName, McpServerId, McpToolName};
21
22use crate::context::CommandContext;
23use crate::shared::render_result;
24
25#[derive(Debug, Subcommand)]
26pub enum ToolsCommands {
27 #[command(about = "Aggregate tool statistics")]
28 Stats(stats::StatsArgs),
29
30 #[command(about = "List tools with metrics")]
31 List(list::ListArgs),
32
33 #[command(about = "Tool usage trends over time")]
34 Trends(trends::TrendsArgs),
35
36 #[command(about = "Deep dive into specific tool")]
37 Show(show::ShowArgs),
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
41pub struct ToolStatsOutput {
42 pub period: String,
43 pub total_tools: i64,
44 pub total_executions: i64,
45 pub successful: i64,
46 pub failed: i64,
47 pub timeout: i64,
48 pub success_rate: f64,
49 pub avg_execution_time_ms: i64,
50 pub p95_execution_time_ms: i64,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
54pub struct ToolListRow {
55 pub tool_name: McpToolName,
56 pub server_name: McpServerId,
57 pub execution_count: i64,
58 pub success_rate: f64,
59 pub avg_execution_time_ms: i64,
60 pub last_used: String,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
64pub struct ToolListOutput {
65 pub tools: Vec<ToolListRow>,
66 pub total: i64,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
70pub struct ToolTrendPoint {
71 pub timestamp: String,
72 pub execution_count: i64,
73 pub success_rate: f64,
74 pub avg_execution_time_ms: i64,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
78pub struct ToolTrendsOutput {
79 pub tool: Option<String>,
80 pub period: String,
81 pub group_by: String,
82 pub points: Vec<ToolTrendPoint>,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
86pub struct ToolShowOutput {
87 pub tool_name: McpToolName,
88 pub period: String,
89 pub summary: ToolStatsOutput,
90 pub status_breakdown: Vec<StatusBreakdownItem>,
91 pub top_errors: Vec<ErrorItem>,
92 pub usage_by_agent: Vec<AgentUsageItem>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
96pub struct StatusBreakdownItem {
97 pub status: String,
98 pub count: i64,
99 pub percentage: f64,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
103pub struct ErrorItem {
104 pub error_message: String,
105 pub count: i64,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
109pub struct AgentUsageItem {
110 pub agent_name: Option<AgentName>,
111 pub count: i64,
112 pub percentage: f64,
113}
114
115pub async fn execute(command: ToolsCommands, ctx: &CommandContext) -> Result<()> {
116 let db_ctx = ctx.database().await?;
117 match command {
118 ToolsCommands::Stats(args) => {
119 let result = stats::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
120 render_result(&result, &ctx.cli);
121 Ok(())
122 },
123 ToolsCommands::List(args) => {
124 let result = list::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
125 render_result(&result, &ctx.cli);
126 Ok(())
127 },
128 ToolsCommands::Trends(args) => {
129 let result = trends::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
130 render_result(&result, &ctx.cli);
131 Ok(())
132 },
133 ToolsCommands::Show(args) => {
134 let result = show::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
135 render_result(&result, &ctx.cli);
136 Ok(())
137 },
138 }
139}