Adopt rustfmt and clean up clippy warnings
Formats the whole backend to rustfmt defaults and fixes all 23 outstanding clippy warnings (collapsible ifs, clone-to-slice-ref, type-complexity, single-arm match, unnecessary is_none check), then wires cargo fmt --check and -D warnings into make lint so both stay enforced going forward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2cc8ac8138
commit
c3108677ce
+142
-44
@@ -381,7 +381,9 @@ impl OttylMcp {
|
||||
|
||||
// ---- logs / timeline / stats ----
|
||||
|
||||
#[tool(description = "Count or read log lines matching a FilterBar query against the running ottyl instance.")]
|
||||
#[tool(
|
||||
description = "Count or read log lines matching a FilterBar query against the running ottyl instance."
|
||||
)]
|
||||
async fn query_logs(
|
||||
&self,
|
||||
Parameters(params): Parameters<QueryLogsParams>,
|
||||
@@ -414,7 +416,9 @@ impl OttylMcp {
|
||||
}
|
||||
}
|
||||
|
||||
#[tool(description = "Read the log-count timeline (bucketed histogram) matching an optional FilterBar query — the same data backing the Log Timeline chart.")]
|
||||
#[tool(
|
||||
description = "Read the log-count timeline (bucketed histogram) matching an optional FilterBar query — the same data backing the Log Timeline chart."
|
||||
)]
|
||||
async fn get_timeline(
|
||||
&self,
|
||||
Parameters(params): Parameters<GetTimelineParams>,
|
||||
@@ -435,14 +439,18 @@ impl OttylMcp {
|
||||
json_result(self.get_json("/api/timeline", &query).await?)
|
||||
}
|
||||
|
||||
#[tool(description = "Read overall ingest stats: log count, sequence/timestamp bounds, ingest lag.")]
|
||||
#[tool(
|
||||
description = "Read overall ingest stats: log count, sequence/timestamp bounds, ingest lag."
|
||||
)]
|
||||
async fn get_stats(&self) -> Result<CallToolResult, McpError> {
|
||||
json_result(self.get_json("/api/stats", &[]).await?)
|
||||
}
|
||||
|
||||
// ---- params ----
|
||||
|
||||
#[tool(description = "List all currently declared params (name, regex, kind, etc.), including auto-derived ones.")]
|
||||
#[tool(
|
||||
description = "List all currently declared params (name, regex, kind, etc.), including auto-derived ones."
|
||||
)]
|
||||
async fn list_params(&self) -> Result<CallToolResult, McpError> {
|
||||
json_result(self.get_json("/api/params", &[]).await?)
|
||||
}
|
||||
@@ -452,13 +460,22 @@ impl OttylMcp {
|
||||
&self,
|
||||
Parameters(params): Parameters<DeclareParamParams>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let body = serde_json::to_value(¶ms).map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(self.send_json(reqwest::Method::POST, "/api/params", Some(&body)).await?)
|
||||
let body = serde_json::to_value(¶ms)
|
||||
.map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::POST, "/api/params", Some(&body))
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Clear all declared params and re-derive auto params from retained log lines.")]
|
||||
#[tool(
|
||||
description = "Clear all declared params and re-derive auto params from retained log lines."
|
||||
)]
|
||||
async fn clear_params(&self) -> Result<CallToolResult, McpError> {
|
||||
json_result(self.send_json(reqwest::Method::DELETE, "/api/params", None).await?)
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::DELETE, "/api/params", None)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
// ---- metrics ----
|
||||
@@ -468,13 +485,19 @@ impl OttylMcp {
|
||||
json_result(self.get_json("/api/metrics", &[]).await?)
|
||||
}
|
||||
|
||||
#[tool(description = "Declare a metric: named event counts/values bucketed by label, derived from matching log lines.")]
|
||||
#[tool(
|
||||
description = "Declare a metric: named event counts/values bucketed by label, derived from matching log lines."
|
||||
)]
|
||||
async fn declare_metric(
|
||||
&self,
|
||||
Parameters(params): Parameters<DeclareMetricParams>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let body = serde_json::to_value(¶ms).map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(self.send_json(reqwest::Method::POST, "/api/metrics", Some(&body)).await?)
|
||||
let body = serde_json::to_value(¶ms)
|
||||
.map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::POST, "/api/metrics", Some(&body))
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Update an existing metric declaration in place by name.")]
|
||||
@@ -509,7 +532,9 @@ impl OttylMcp {
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Read a metric's raw bucket values (labels, sequence, timestamp, value), optionally filtered by label and time range.")]
|
||||
#[tool(
|
||||
description = "Read a metric's raw bucket values (labels, sequence, timestamp, value), optionally filtered by label and time range."
|
||||
)]
|
||||
async fn get_metric_bucket_values(
|
||||
&self,
|
||||
Parameters(params): Parameters<GetMetricBucketValuesParams>,
|
||||
@@ -531,19 +556,27 @@ impl OttylMcp {
|
||||
query.push(("limit", limit.to_string()));
|
||||
}
|
||||
json_result(
|
||||
self.get_json(&format!("/api/metrics/{}", urlencoding_path(¶ms.name)), &query)
|
||||
.await?,
|
||||
self.get_json(
|
||||
&format!("/api/metrics/{}", urlencoding_path(¶ms.name)),
|
||||
&query,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
// ---- promql ----
|
||||
|
||||
#[tool(description = "Evaluate a PromQL-style query directly and get back raw series data (labels + timestamped points) — the same evaluation dashboards use to render charts.")]
|
||||
#[tool(
|
||||
description = "Evaluate a PromQL-style query directly and get back raw series data (labels + timestamped points) — the same evaluation dashboards use to render charts."
|
||||
)]
|
||||
async fn run_promql_query(
|
||||
&self,
|
||||
Parameters(params): Parameters<RunPromqlQueryParams>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
json_result(self.run_promql(¶ms.promql, params.start_ms, params.end_ms).await?)
|
||||
json_result(
|
||||
self.run_promql(¶ms.promql, params.start_ms, params.end_ms)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
// ---- dashboards / panels ----
|
||||
@@ -556,7 +589,10 @@ impl OttylMcp {
|
||||
let profile = self.resolve_profile(params.profile).await?;
|
||||
json_result(
|
||||
self.get_json(
|
||||
&format!("/api/settings/profiles/{}/dashboards", urlencoding_path(&profile)),
|
||||
&format!(
|
||||
"/api/settings/profiles/{}/dashboards",
|
||||
urlencoding_path(&profile)
|
||||
),
|
||||
&[],
|
||||
)
|
||||
.await?,
|
||||
@@ -572,14 +608,19 @@ impl OttylMcp {
|
||||
json_result(
|
||||
self.send_json(
|
||||
reqwest::Method::POST,
|
||||
&format!("/api/settings/profiles/{}/dashboards", urlencoding_path(&profile)),
|
||||
&format!(
|
||||
"/api/settings/profiles/{}/dashboards",
|
||||
urlencoding_path(&profile)
|
||||
),
|
||||
Some(&serde_json::json!({ "name": params.name })),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Read a dashboard's current panels (definitions only — use render_panel or render_dashboard for the data a panel would chart).")]
|
||||
#[tool(
|
||||
description = "Read a dashboard's current panels (definitions only — use render_panel or render_dashboard for the data a panel would chart)."
|
||||
)]
|
||||
async fn get_dashboard(
|
||||
&self,
|
||||
Parameters(params): Parameters<GetDashboardParams>,
|
||||
@@ -631,12 +672,18 @@ impl OttylMcp {
|
||||
.get_mut("panels")
|
||||
.and_then(Value::as_array_mut)
|
||||
.ok_or_else(|| mcp_error("unexpected dashboard shape", "missing panels array"))?;
|
||||
match panels.iter().position(|panel| panel.get("id") == new_panel.get("id")) {
|
||||
match panels
|
||||
.iter()
|
||||
.position(|panel| panel.get("id") == new_panel.get("id"))
|
||||
{
|
||||
Some(index) => panels[index] = new_panel,
|
||||
None => panels.push(new_panel),
|
||||
}
|
||||
|
||||
json_result(self.save_dashboard(&profile, ¶ms.dashboard_id, dashboard).await?)
|
||||
json_result(
|
||||
self.save_dashboard(&profile, ¶ms.dashboard_id, dashboard)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Remove a panel from a dashboard by panel_id.")]
|
||||
@@ -650,8 +697,13 @@ impl OttylMcp {
|
||||
.get_mut("panels")
|
||||
.and_then(Value::as_array_mut)
|
||||
.ok_or_else(|| mcp_error("unexpected dashboard shape", "missing panels array"))?;
|
||||
panels.retain(|panel| panel.get("id").and_then(Value::as_str) != Some(params.panel_id.as_str()));
|
||||
json_result(self.save_dashboard(&profile, ¶ms.dashboard_id, dashboard).await?)
|
||||
panels.retain(|panel| {
|
||||
panel.get("id").and_then(Value::as_str) != Some(params.panel_id.as_str())
|
||||
});
|
||||
json_result(
|
||||
self.save_dashboard(&profile, ¶ms.dashboard_id, dashboard)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
async fn save_dashboard(
|
||||
@@ -672,7 +724,9 @@ impl OttylMcp {
|
||||
.await
|
||||
}
|
||||
|
||||
#[tool(description = "Render one panel: resolve its promql from the dashboard definition, evaluate it, and return the chart's underlying series data (labels + timestamped points) as JSON — this is how an agent should 'see' a chart without a browser.")]
|
||||
#[tool(
|
||||
description = "Render one panel: resolve its promql from the dashboard definition, evaluate it, and return the chart's underlying series data (labels + timestamped points) as JSON — this is how an agent should 'see' a chart without a browser."
|
||||
)]
|
||||
async fn render_panel(
|
||||
&self,
|
||||
Parameters(params): Parameters<RenderPanelParams>,
|
||||
@@ -682,20 +736,30 @@ impl OttylMcp {
|
||||
let panel = dashboard
|
||||
.get("panels")
|
||||
.and_then(Value::as_array)
|
||||
.and_then(|panels| panels.iter().find(|panel| panel.get("id").and_then(Value::as_str) == Some(params.panel_id.as_str())))
|
||||
.ok_or_else(|| McpError::invalid_params("no panel with that panel_id in the dashboard", None))?;
|
||||
.and_then(|panels| {
|
||||
panels.iter().find(|panel| {
|
||||
panel.get("id").and_then(Value::as_str) == Some(params.panel_id.as_str())
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
McpError::invalid_params("no panel with that panel_id in the dashboard", None)
|
||||
})?;
|
||||
let promql = panel
|
||||
.get("promql")
|
||||
.and_then(Value::as_str)
|
||||
.ok_or_else(|| mcp_error("unexpected panel shape", "missing promql"))?;
|
||||
let series = self.run_promql(promql, params.start_ms, params.end_ms).await?;
|
||||
let series = self
|
||||
.run_promql(promql, params.start_ms, params.end_ms)
|
||||
.await?;
|
||||
json_result(serde_json::json!({
|
||||
"panel": panel,
|
||||
"series": series,
|
||||
}))
|
||||
}
|
||||
|
||||
#[tool(description = "Render every panel in a dashboard: evaluate each panel's promql and return all their chart data as JSON, so an agent can reason about the whole dashboard without a browser.")]
|
||||
#[tool(
|
||||
description = "Render every panel in a dashboard: evaluate each panel's promql and return all their chart data as JSON, so an agent can reason about the whole dashboard without a browser."
|
||||
)]
|
||||
async fn render_dashboard(
|
||||
&self,
|
||||
Parameters(params): Parameters<RenderDashboardParams>,
|
||||
@@ -709,8 +773,13 @@ impl OttylMcp {
|
||||
.unwrap_or_default();
|
||||
let mut rendered = Vec::with_capacity(panels.len());
|
||||
for panel in panels {
|
||||
let promql = panel.get("promql").and_then(Value::as_str).unwrap_or_default();
|
||||
let series = self.run_promql(promql, params.start_ms, params.end_ms).await?;
|
||||
let promql = panel
|
||||
.get("promql")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default();
|
||||
let series = self
|
||||
.run_promql(promql, params.start_ms, params.end_ms)
|
||||
.await?;
|
||||
rendered.push(serde_json::json!({ "panel": panel, "series": series }));
|
||||
}
|
||||
json_result(serde_json::json!({
|
||||
@@ -721,18 +790,26 @@ impl OttylMcp {
|
||||
|
||||
// ---- settings / theming ----
|
||||
|
||||
#[tool(description = "Read the running instance's global settings (theme, active profile, max_logs, ntfy config).")]
|
||||
#[tool(
|
||||
description = "Read the running instance's global settings (theme, active profile, max_logs, ntfy config)."
|
||||
)]
|
||||
async fn get_settings(&self) -> Result<CallToolResult, McpError> {
|
||||
json_result(self.get_json("/api/settings", &[]).await?)
|
||||
}
|
||||
|
||||
#[tool(description = "Update global settings. Only fields provided are changed; pass null for an Option field to clear it.")]
|
||||
#[tool(
|
||||
description = "Update global settings. Only fields provided are changed; pass null for an Option field to clear it."
|
||||
)]
|
||||
async fn update_settings(
|
||||
&self,
|
||||
Parameters(params): Parameters<UpdateSettingsParams>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let body = serde_json::to_value(¶ms).map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(self.send_json(reqwest::Method::PATCH, "/api/settings", Some(&body)).await?)
|
||||
let body = serde_json::to_value(¶ms)
|
||||
.map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::PATCH, "/api/settings", Some(&body))
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "List the names of the themes embedded in this build (see /api/themes).")]
|
||||
@@ -779,7 +856,10 @@ impl OttylMcp {
|
||||
"enabled": params.enabled,
|
||||
"condition": params.condition,
|
||||
});
|
||||
json_result(self.send_json(reqwest::Method::POST, "/api/settings/alerts", Some(&body)).await?)
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::POST, "/api/settings/alerts", Some(&body))
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Update an existing alert rule by id.")]
|
||||
@@ -817,21 +897,30 @@ impl OttylMcp {
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Send a test ntfy notification to verify alert delivery is configured correctly.")]
|
||||
#[tool(
|
||||
description = "Send a test ntfy notification to verify alert delivery is configured correctly."
|
||||
)]
|
||||
async fn test_alert_notification(
|
||||
&self,
|
||||
Parameters(params): Parameters<TestAlertNotificationParams>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let body = serde_json::to_value(¶ms).map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
let body = serde_json::to_value(¶ms)
|
||||
.map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::POST, "/api/settings/alerts/test", Some(&body))
|
||||
.await?,
|
||||
self.send_json(
|
||||
reqwest::Method::POST,
|
||||
"/api/settings/alerts/test",
|
||||
Some(&body),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
// ---- profiles ----
|
||||
|
||||
#[tool(description = "List all saved profiles with summary counts (params, metrics, dashboards).")]
|
||||
#[tool(
|
||||
description = "List all saved profiles with summary counts (params, metrics, dashboards)."
|
||||
)]
|
||||
async fn list_profiles(&self) -> Result<CallToolResult, McpError> {
|
||||
json_result(self.get_json("/api/settings/profiles", &[]).await?)
|
||||
}
|
||||
@@ -841,8 +930,12 @@ impl OttylMcp {
|
||||
&self,
|
||||
Parameters(params): Parameters<CreateProfileParams>,
|
||||
) -> Result<CallToolResult, McpError> {
|
||||
let body = serde_json::to_value(¶ms).map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(self.send_json(reqwest::Method::POST, "/api/settings/profiles", Some(&body)).await?)
|
||||
let body = serde_json::to_value(¶ms)
|
||||
.map_err(|err| mcp_error("failed to encode params", err))?;
|
||||
json_result(
|
||||
self.send_json(reqwest::Method::POST, "/api/settings/profiles", Some(&body))
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Delete a profile by name.")]
|
||||
@@ -860,7 +953,9 @@ impl OttylMcp {
|
||||
)
|
||||
}
|
||||
|
||||
#[tool(description = "Activate a profile: swaps in its param/metric declarations and dashboards as the running instance's live state.")]
|
||||
#[tool(
|
||||
description = "Activate a profile: swaps in its param/metric declarations and dashboards as the running instance's live state."
|
||||
)]
|
||||
async fn activate_profile(
|
||||
&self,
|
||||
Parameters(params): Parameters<ProfileNameParams>,
|
||||
@@ -868,7 +963,10 @@ impl OttylMcp {
|
||||
json_result(
|
||||
self.send_json(
|
||||
reqwest::Method::POST,
|
||||
&format!("/api/settings/profiles/{}/activate", urlencoding_path(¶ms.name)),
|
||||
&format!(
|
||||
"/api/settings/profiles/{}/activate",
|
||||
urlencoding_path(¶ms.name)
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await?,
|
||||
|
||||
Reference in New Issue
Block a user