loopctl-mcp-server 2.44.0 → 2.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/index.js +73 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -188,6 +188,7 @@ autonomous agent) can self-remediate without a human. Full agent-tenant lifecycl
188
188
  | `get_cost_summary` | orch | Get cost/token usage summary for a project, optionally broken down by `agent`, `epic`, or `model`. |
189
189
  | `get_story_token_usage` | orch | Get all token usage records for a single story. |
190
190
  | `get_cost_anomalies` | orch | Get cost anomaly alerts — stories or agents exceeding expected budgets. Optionally filter by project. |
191
+ | `get_ingestion_anomalies` | orch | Get ingestion-health anomalies — capture_silence (a source_type stopped producing articles) and high_reject_rate (writes rejected at high rate, persisting no article row). Check whether knowledge capture is still landing AND being accepted. |
191
192
  | `set_token_budget` | orch | Set a token budget (in millicents) for a project, epic, story, or agent scope. Requires orchestrator role. |
192
193
 
193
194
  ### Knowledge Wiki Tools (agent key)
package/index.js CHANGED
@@ -824,6 +824,35 @@ async function getCostAnomalies({ project_id, page, page_size }) {
824
824
  return toContent(result);
825
825
  }
826
826
 
827
+ async function getIngestionAnomalies({
828
+ source_type,
829
+ anomaly_type,
830
+ resolved,
831
+ include_archived,
832
+ page,
833
+ page_size,
834
+ }) {
835
+ const params = new URLSearchParams();
836
+ if (source_type) params.set("source_type", source_type);
837
+ if (anomaly_type) params.set("anomaly_type", anomaly_type);
838
+ if (resolved != null) params.set("resolved", String(resolved));
839
+ if (include_archived != null) params.set("include_archived", String(include_archived));
840
+ if (page != null) params.set("page", String(page));
841
+ if (page_size != null) params.set("page_size", String(page_size));
842
+
843
+ const query = params.toString() ? `?${params}` : "";
844
+ // Orchestrator-gated endpoint (RequireRole :orchestrator). Pass the ORCH key
845
+ // explicitly so a misconfigured single agent-role LOOPCTL_API_KEY fails loudly
846
+ // rather than drifting into an undiagnosable 403.
847
+ const result = await apiCall(
848
+ "GET",
849
+ `/api/v1/ingestion-anomalies${query}`,
850
+ undefined,
851
+ process.env.LOOPCTL_ORCH_KEY
852
+ );
853
+ return toContent(result);
854
+ }
855
+
827
856
  async function setTokenBudget({ scope_type, scope_id, budget_millicents, alert_threshold_pct }) {
828
857
  const body = { scope_type, scope_id, budget_millicents };
829
858
  if (alert_threshold_pct != null) body.alert_threshold_pct = alert_threshold_pct;
@@ -2751,6 +2780,47 @@ const TOOLS = [
2751
2780
  required: [],
2752
2781
  },
2753
2782
  },
2783
+ {
2784
+ name: "get_ingestion_anomalies",
2785
+ description:
2786
+ "Get ingestion-health anomalies — capture_silence (a source_type that was producing " +
2787
+ "articles has gone silent) and high_reject_rate (writes attempted but rejected at high " +
2788
+ "rate — 409 title_conflict / validation drops that persist no article row). Use to check " +
2789
+ "whether knowledge capture is still landing AND being accepted. Paginated (page/page_size); " +
2790
+ "advance `page` to enumerate all. Filter by source_type, anomaly_type, resolved status, or " +
2791
+ "include archived.",
2792
+ inputSchema: {
2793
+ type: "object",
2794
+ properties: {
2795
+ source_type: {
2796
+ type: "string",
2797
+ description: 'Optional: filter to one article source_type (e.g. "session_log").',
2798
+ },
2799
+ anomaly_type: {
2800
+ type: "string",
2801
+ // Keep in sync with Loopctl.Knowledge.IngestionAnomaly @anomaly_types (the
2802
+ // server-side Ecto.Enum + the ingestion_anomalies_anomaly_type_check DB CHECK).
2803
+ enum: ["capture_silence", "high_reject_rate"],
2804
+ description:
2805
+ 'Optional: filter by anomaly type — "capture_silence" (writes stopped) or ' +
2806
+ '"high_reject_rate" (writes rejected at high rate).',
2807
+ },
2808
+ resolved: {
2809
+ type: "string",
2810
+ enum: ["false", "true", "all"],
2811
+ description:
2812
+ 'Which anomalies to return: "false" = unresolved only (default), "true" = resolved only, "all" = both.',
2813
+ },
2814
+ include_archived: {
2815
+ type: "boolean",
2816
+ description: "Optional: include archived (retired-source) anomalies (default false).",
2817
+ },
2818
+ page: { type: "integer", description: "Page number (default 1)." },
2819
+ page_size: { type: "integer", description: "Anomalies per page (default 20, max 100)." },
2820
+ },
2821
+ required: [],
2822
+ },
2823
+ },
2754
2824
  {
2755
2825
  name: "set_token_budget",
2756
2826
  description:
@@ -5019,6 +5089,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5019
5089
  case "get_cost_anomalies":
5020
5090
  return await getCostAnomalies(args);
5021
5091
 
5092
+ case "get_ingestion_anomalies":
5093
+ return await getIngestionAnomalies(args);
5094
+
5022
5095
  case "set_token_budget":
5023
5096
  return await setTokenBudget(args);
5024
5097
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopctl-mcp-server",
3
- "version": "2.44.0",
3
+ "version": "2.46.0",
4
4
  "description": "MCP server for loopctl — structural trust for AI development loops",
5
5
  "type": "module",
6
6
  "main": "index.js",