Skip to content

Commit 1270ff8

Browse files
committed
changes
1 parent 57ce0b1 commit 1270ff8

File tree

6 files changed

+128
-19
lines changed

6 files changed

+128
-19
lines changed

apps/framework-cli/src/mcp/server.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl ServerHandler for MooseMcpHandler {
108108
self.redis_client.clone(),
109109
)
110110
.await),
111-
"diagnose_infrastructure" => Ok(infra_issues::handle_call(
111+
"get_issues" => Ok(infra_issues::handle_call(
112112
param.arguments.as_ref(),
113113
self.redis_client.clone(),
114114
&self.clickhouse_config,
@@ -271,7 +271,7 @@ mod tests {
271271
"get_infra_map",
272272
"query_olap",
273273
"get_stream_sample",
274-
"diagnose_infrastructure",
274+
"get_issues",
275275
];
276276

277277
let logs_tool = logs::tool_definition();

apps/framework-cli/src/mcp/tools/infra_issues/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ pub fn tool_definition() -> Tool {
137137
});
138138

139139
Tool {
140-
name: "diagnose_infrastructure".into(),
140+
name: "get_issues".into(),
141141
description: Some(
142-
"Proactively diagnose infrastructure issues by intelligently checking relevant diagnostic sources based on infrastructure type. For ClickHouse, automatically checks: stuck mutations, S3Queue ingestion errors (for S3Queue tables), replication health (for replicated tables), data parts issues, background merge problems, system errors, and stopped operations. Returns structured, actionable information about errors and warnings with suggested remediation steps.".into()
142+
"Proactively scan for infrastructure health issues (stuck mutations, replication errors, S3Queue failures, merge problems). Auto-checks relevant diagnostics based on infrastructure type. Use when investigating errors or performance issues. Returns actionable problems with remediation suggestions.".into()
143143
),
144144
input_schema: Arc::new(schema.as_object().unwrap().clone()),
145145
annotations: None,
146146
icons: None,
147147
output_schema: None,
148-
title: Some("Diagnose Infrastructure Issues".into()),
148+
title: Some("Get Infrastructure Issues".into()),
149149
}
150150
}
151151

apps/framework-cli/src/mcp/tools/infra_map.rs

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,39 +25,61 @@ pub enum InfraMapError {
2525
ToonSerialization(#[from] super::toon_serializer::ToonSerializationError),
2626
}
2727

28+
/// Fuzzy match using case-insensitive substring matching
29+
fn fuzzy_match(query: &str, text: &str) -> bool {
30+
let query_lower = query.to_lowercase();
31+
let text_lower = text.to_lowercase();
32+
text_lower.contains(&query_lower)
33+
}
34+
2835
/// Returns the tool definition for the MCP server
2936
pub fn tool_definition() -> Tool {
3037
let schema = json!({
3138
"type": "object",
32-
"properties": {}
39+
"properties": {
40+
"search": {
41+
"type": "string",
42+
"description": "Focus on specific components (fuzzy, case-insensitive). Examples: 'user' → UserEvents, user_table, INGRESS_User; 'bar' → Bar topic, BarAggregated table. Omit for complete map."
43+
}
44+
}
3345
});
3446

3547
Tool {
3648
name: "get_infra_map".into(),
3749
description: Some(
38-
"Retrieve the complete Moose infrastructure map showing all components and their connections. Returns tables, topics, API endpoints, sync processes, functions, SQL resources, workflows, and their relationships in a compact TOON table format.".into()
50+
"🔍 START HERE: Get complete project topology showing all components (tables, topics, APIs, functions, workflows) with source file locations and data flow connections. Essential first step to understand project structure, locate files, and verify code changes are reflected. Use 'search' to focus on specific components (e.g., search='User' shows UserEvents topic, user tables, and all related connections).".into()
3951
),
4052
input_schema: Arc::new(schema.as_object().unwrap().clone()),
4153
annotations: None,
4254
icons: None,
4355
output_schema: None,
44-
title: Some("Get Moose Infrastructure Map".into()),
56+
title: Some("Get Infrastructure Map".into()),
4557
}
4658
}
4759

4860
/// Handle the tool call with the given arguments
4961
pub async fn handle_call(
50-
_arguments: Option<&Map<String, Value>>,
62+
arguments: Option<&Map<String, Value>>,
5163
redis_client: Arc<RedisClient>,
5264
) -> CallToolResult {
53-
match execute_get_infra_map(redis_client).await {
65+
// Parse optional search parameter
66+
let search = arguments
67+
.and_then(|args| args.get("search"))
68+
.and_then(|v| v.as_str())
69+
.filter(|s| !s.is_empty())
70+
.map(|s| s.to_string());
71+
72+
match execute_get_infra_map(redis_client, search).await {
5473
Ok(content) => create_success_result(content),
5574
Err(e) => create_error_result(format!("Error retrieving infrastructure map: {}", e)),
5675
}
5776
}
5877

5978
/// Main function to retrieve infrastructure map
60-
async fn execute_get_infra_map(redis_client: Arc<RedisClient>) -> Result<String, InfraMapError> {
79+
async fn execute_get_infra_map(
80+
redis_client: Arc<RedisClient>,
81+
search: Option<String>,
82+
) -> Result<String, InfraMapError> {
6183
// Load infrastructure map from Redis
6284
let infra_map_opt = InfrastructureMap::load_from_redis(&redis_client).await?;
6385

@@ -72,11 +94,14 @@ async fn execute_get_infra_map(redis_client: Arc<RedisClient>) -> Result<String,
7294
};
7395

7496
// Format and return the infrastructure map
75-
format_infrastructure_map(&infra_map)
97+
format_infrastructure_map(&infra_map, &search)
7698
}
7799

78100
/// Format infrastructure map with component lineage information
79-
fn format_infrastructure_map(infra_map: &InfrastructureMap) -> Result<String, InfraMapError> {
101+
fn format_infrastructure_map(
102+
infra_map: &InfrastructureMap,
103+
search: &Option<String>,
104+
) -> Result<String, InfraMapError> {
80105
let mut output = String::from("# Moose Infrastructure Map\n\n");
81106
output.push_str("This view shows infrastructure components and their connections.\n");
82107
output.push_str(
@@ -86,11 +111,95 @@ fn format_infrastructure_map(infra_map: &InfrastructureMap) -> Result<String, In
86111
// Build compressed map
87112
let compressed_map = build_compressed_map(infra_map);
88113

114+
// Apply search filter if provided
115+
let filtered_map = if let Some(query) = search {
116+
let mut filtered = compressed_map.clone();
117+
118+
// Filter components: keep if query matches id OR name
119+
filtered.components.retain(|component| {
120+
fuzzy_match(query, &component.id) || fuzzy_match(query, &component.name)
121+
});
122+
123+
// Build set of matching component IDs for connection filtering
124+
let component_ids: std::collections::HashSet<_> =
125+
filtered.components.iter().map(|c| c.id.as_str()).collect();
126+
127+
// Filter connections: keep if source OR destination is in filtered components
128+
filtered.connections.retain(|conn| {
129+
component_ids.contains(conn.from.as_str()) || component_ids.contains(conn.to.as_str())
130+
});
131+
132+
// Recalculate stats
133+
filtered.stats.total_components = filtered.components.len() as u32;
134+
filtered.stats.total_connections = filtered.connections.len() as u32;
135+
filtered.stats.by_type.clear();
136+
for component in &filtered.components {
137+
*filtered
138+
.stats
139+
.by_type
140+
.entry(component.component_type)
141+
.or_insert(0) += 1;
142+
}
143+
144+
filtered
145+
} else {
146+
compressed_map
147+
};
148+
89149
// Serialize to TOON
90-
let compressed_json = serde_json::to_value(&compressed_map)?;
150+
let compressed_json = serde_json::to_value(&filtered_map)?;
91151
output.push_str("```toon\n");
92152
output.push_str(&serialize_to_toon_compressed(&compressed_json)?);
93153
output.push_str("\n```\n");
94154

155+
// Add search info if filtered
156+
if search.is_some() {
157+
output.push_str(&format!(
158+
"\n**Search:** '{}' | **Matched:** {} component(s), {} connection(s)\n",
159+
search.as_ref().unwrap(),
160+
filtered_map.stats.total_components,
161+
filtered_map.stats.total_connections
162+
));
163+
}
164+
95165
Ok(output)
96166
}
167+
168+
#[cfg(test)]
169+
mod tests {
170+
use super::*;
171+
172+
#[test]
173+
fn test_fuzzy_match() {
174+
// Basic substring matching
175+
assert!(fuzzy_match("user", "user_table"));
176+
assert!(fuzzy_match("user", "UserEvents"));
177+
assert!(fuzzy_match("table", "user_table"));
178+
179+
// Case insensitive
180+
assert!(fuzzy_match("USER", "user_table"));
181+
assert!(fuzzy_match("User", "UserEvents"));
182+
assert!(fuzzy_match("user", "INGRESS_User"));
183+
184+
// Partial matches
185+
assert!(fuzzy_match("eve", "UserEvents"));
186+
assert!(fuzzy_match("gres", "INGRESS_User"));
187+
188+
// No match
189+
assert!(!fuzzy_match("order", "user_table"));
190+
assert!(!fuzzy_match("xyz", "UserEvents"));
191+
}
192+
193+
#[test]
194+
fn test_fuzzy_match_empty_query() {
195+
// Empty query matches everything (substring logic)
196+
assert!(fuzzy_match("", "user_table"));
197+
assert!(fuzzy_match("", ""));
198+
}
199+
200+
#[test]
201+
fn test_fuzzy_match_exact() {
202+
assert!(fuzzy_match("user_table", "user_table"));
203+
assert!(fuzzy_match("Bar", "Bar"));
204+
}
205+
}

apps/framework-cli/src/mcp/tools/logs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,13 @@ pub fn tool_definition() -> Tool {
172172
Tool {
173173
name: "get_logs".into(),
174174
description: Some(
175-
"Retrieve and filter Moose dev server logs. Access recent log entries from the current dev server session, filter by log level (ERROR, WARN, INFO, DEBUG, TRACE), and search with regex patterns (e.g., 'error|warn', '(?i)connection', 'user_\\d+').".into()
175+
"Debug issues by checking dev server logs. Filter by level (ERROR/WARN/INFO/DEBUG/TRACE) or search with regex. Use when troubleshooting errors, connection issues, or unexpected behavior.".into()
176176
),
177177
input_schema: Arc::new(schema.as_object().unwrap().clone()),
178178
annotations: None,
179179
icons: None,
180180
output_schema: None,
181-
title: Some("Get Moose Dev Server Logs".into()),
181+
title: Some("Get Dev Server Logs".into()),
182182
}
183183
}
184184

apps/framework-cli/src/mcp/tools/query_olap.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ pub fn tool_definition() -> Tool {
291291
Tool {
292292
name: "query_olap".into(),
293293
description: Some(
294-
"Execute read-only SQL queries against the ClickHouse OLAP database. Queries automatically use the configured database as the default context, so you can query tables without fully-qualifying them. Use this tool to explore data, check table schemas, and analyze stored information. Supports SELECT queries, system table queries (e.g., system.tables, system.columns), and metadata commands (SHOW, DESCRIBE, EXPLAIN).".into()
294+
"Query ClickHouse to explore data, verify ingestion, check schemas. Use SELECT to analyze stored data, SHOW/DESCRIBE for table info, system.tables/system.columns for metadata. Read-only, safe for production. Tables auto-scoped to project database.".into()
295295
),
296296
input_schema: Arc::new(schema.as_object().unwrap().clone()),
297297
annotations: None,

apps/framework-cli/src/mcp/tools/sample_stream.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,13 @@ pub fn tool_definition() -> Tool {
9393
Tool {
9494
name: "get_stream_sample".into(),
9595
description: Some(
96-
"Retrieve sample messages from a Redpanda/Kafka streaming topic. Get the last N messages from any topic/stream for debugging and exploration. Returns messages as JSON arrays with message payloads.".into()
96+
"Sample recent messages from streaming topics to verify data flow, debug transformations, or inspect payloads. Get last N messages from any topic. Use after get_infra_map to see available topics.".into()
9797
),
9898
input_schema: Arc::new(schema.as_object().unwrap().clone()),
9999
annotations: None,
100100
icons: None,
101101
output_schema: None,
102-
title: Some("Get Stream Sample".into()),
102+
title: Some("Sample Stream Messages".into()),
103103
}
104104
}
105105

0 commit comments

Comments
 (0)