systemprompt_analytics/repository/tools/
list_queries.rs1use crate::Result;
5use chrono::{DateTime, Utc};
6
7use super::ToolAnalyticsRepository;
8use crate::models::cli::ToolListRow;
9
10#[derive(Debug)]
11pub struct ToolListParams<'a> {
12 pub start: DateTime<Utc>,
13 pub end: DateTime<Utc>,
14 pub limit: i64,
15 pub server_filter: Option<&'a str>,
16 pub sort_order: &'a str,
17}
18
19impl ToolAnalyticsRepository {
20 pub async fn list_tools(&self, params: ToolListParams<'_>) -> Result<Vec<ToolListRow>> {
21 if let Some(server) = params.server_filter {
22 let pattern = format!("%{}%", server);
23 self.list_tools_with_filter(¶ms, &pattern).await
24 } else {
25 self.list_tools_unfiltered(¶ms).await
26 }
27 }
28
29 async fn list_tools_with_filter(
30 &self,
31 params: &ToolListParams<'_>,
32 pattern: &str,
33 ) -> Result<Vec<ToolListRow>> {
34 let ToolListParams {
35 start,
36 end,
37 limit,
38 sort_order,
39 ..
40 } = *params;
41 match sort_order {
42 "success_rate" => {
43 self.filtered_by_success_rate(start, end, pattern, limit)
44 .await
45 },
46 "avg_time" => self.filtered_by_avg_time(start, end, pattern, limit).await,
47 _ => self.filtered_by_count(start, end, pattern, limit).await,
48 }
49 }
50
51 async fn filtered_by_success_rate(
52 &self,
53 start: DateTime<Utc>,
54 end: DateTime<Utc>,
55 pattern: &str,
56 limit: i64,
57 ) -> Result<Vec<ToolListRow>> {
58 sqlx::query_as!(
59 ToolListRow,
60 r#"
61 SELECT
62 tool_name as "tool_name!",
63 server_name as "server_name!",
64 COUNT(*)::bigint as "execution_count!",
65 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
66 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
67 MAX(created_at) as "last_used!"
68 FROM mcp_tool_executions
69 WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
70 GROUP BY tool_name, server_name
71 ORDER BY CASE WHEN COUNT(*) > 0
72 THEN COUNT(*) FILTER (WHERE status = 'success')::float / COUNT(*)::float
73 ELSE 0 END DESC
74 LIMIT $4
75 "#,
76 start,
77 end,
78 pattern,
79 limit
80 )
81 .fetch_all(&*self.pool)
82 .await
83 .map_err(Into::into)
84 }
85
86 async fn filtered_by_avg_time(
87 &self,
88 start: DateTime<Utc>,
89 end: DateTime<Utc>,
90 pattern: &str,
91 limit: i64,
92 ) -> Result<Vec<ToolListRow>> {
93 sqlx::query_as!(
94 ToolListRow,
95 r#"
96 SELECT
97 tool_name as "tool_name!",
98 server_name as "server_name!",
99 COUNT(*)::bigint as "execution_count!",
100 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
101 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
102 MAX(created_at) as "last_used!"
103 FROM mcp_tool_executions
104 WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
105 GROUP BY tool_name, server_name
106 ORDER BY COALESCE(AVG(execution_time_ms), 0) DESC
107 LIMIT $4
108 "#,
109 start,
110 end,
111 pattern,
112 limit
113 )
114 .fetch_all(&*self.pool)
115 .await
116 .map_err(Into::into)
117 }
118
119 async fn filtered_by_count(
120 &self,
121 start: DateTime<Utc>,
122 end: DateTime<Utc>,
123 pattern: &str,
124 limit: i64,
125 ) -> Result<Vec<ToolListRow>> {
126 sqlx::query_as!(
127 ToolListRow,
128 r#"
129 SELECT
130 tool_name as "tool_name!",
131 server_name as "server_name!",
132 COUNT(*)::bigint as "execution_count!",
133 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
134 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
135 MAX(created_at) as "last_used!"
136 FROM mcp_tool_executions
137 WHERE created_at >= $1 AND created_at < $2 AND server_name ILIKE $3
138 GROUP BY tool_name, server_name
139 ORDER BY COUNT(*) DESC
140 LIMIT $4
141 "#,
142 start,
143 end,
144 pattern,
145 limit
146 )
147 .fetch_all(&*self.pool)
148 .await
149 .map_err(Into::into)
150 }
151
152 async fn list_tools_unfiltered(&self, params: &ToolListParams<'_>) -> Result<Vec<ToolListRow>> {
153 let ToolListParams {
154 start,
155 end,
156 limit,
157 sort_order,
158 ..
159 } = *params;
160 match sort_order {
161 "success_rate" => self.unfiltered_by_success_rate(start, end, limit).await,
162 "avg_time" => self.unfiltered_by_avg_time(start, end, limit).await,
163 _ => self.unfiltered_by_count(start, end, limit).await,
164 }
165 }
166
167 async fn unfiltered_by_success_rate(
168 &self,
169 start: DateTime<Utc>,
170 end: DateTime<Utc>,
171 limit: i64,
172 ) -> Result<Vec<ToolListRow>> {
173 sqlx::query_as!(
174 ToolListRow,
175 r#"
176 SELECT
177 tool_name as "tool_name!",
178 server_name as "server_name!",
179 COUNT(*)::bigint as "execution_count!",
180 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
181 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
182 MAX(created_at) as "last_used!"
183 FROM mcp_tool_executions
184 WHERE created_at >= $1 AND created_at < $2
185 GROUP BY tool_name, server_name
186 ORDER BY CASE WHEN COUNT(*) > 0
187 THEN COUNT(*) FILTER (WHERE status = 'success')::float / COUNT(*)::float
188 ELSE 0 END DESC
189 LIMIT $3
190 "#,
191 start,
192 end,
193 limit
194 )
195 .fetch_all(&*self.pool)
196 .await
197 .map_err(Into::into)
198 }
199
200 async fn unfiltered_by_avg_time(
201 &self,
202 start: DateTime<Utc>,
203 end: DateTime<Utc>,
204 limit: i64,
205 ) -> Result<Vec<ToolListRow>> {
206 sqlx::query_as!(
207 ToolListRow,
208 r#"
209 SELECT
210 tool_name as "tool_name!",
211 server_name as "server_name!",
212 COUNT(*)::bigint as "execution_count!",
213 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
214 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
215 MAX(created_at) as "last_used!"
216 FROM mcp_tool_executions
217 WHERE created_at >= $1 AND created_at < $2
218 GROUP BY tool_name, server_name
219 ORDER BY COALESCE(AVG(execution_time_ms), 0) DESC
220 LIMIT $3
221 "#,
222 start,
223 end,
224 limit
225 )
226 .fetch_all(&*self.pool)
227 .await
228 .map_err(Into::into)
229 }
230
231 async fn unfiltered_by_count(
232 &self,
233 start: DateTime<Utc>,
234 end: DateTime<Utc>,
235 limit: i64,
236 ) -> Result<Vec<ToolListRow>> {
237 sqlx::query_as!(
238 ToolListRow,
239 r#"
240 SELECT
241 tool_name as "tool_name!",
242 server_name as "server_name!",
243 COUNT(*)::bigint as "execution_count!",
244 COUNT(*) FILTER (WHERE status = 'success')::bigint as "success_count!",
245 COALESCE(AVG(execution_time_ms)::float8, 0) as "avg_time!",
246 MAX(created_at) as "last_used!"
247 FROM mcp_tool_executions
248 WHERE created_at >= $1 AND created_at < $2
249 GROUP BY tool_name, server_name
250 ORDER BY COUNT(*) DESC
251 LIMIT $3
252 "#,
253 start,
254 end,
255 limit
256 )
257 .fetch_all(&*self.pool)
258 .await
259 .map_err(Into::into)
260 }
261}