@thotischner/observability-mcp 3.1.1 → 3.2.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.
- package/dist/conformance/mcp-2025-11-25.test.js +27 -0
- package/dist/connectors/loki.js +24 -15
- package/dist/connectors/loki.test.js +15 -0
- package/dist/connectors/prometheus.d.ts +1 -0
- package/dist/connectors/prometheus.js +75 -3
- package/dist/connectors/prometheus.test.js +81 -0
- package/dist/context.d.ts +11 -2
- package/dist/context.js +10 -2
- package/dist/context.test.js +6 -0
- package/dist/enrich/ip-dataset.d.ts +25 -0
- package/dist/enrich/ip-dataset.js +113 -0
- package/dist/enrich/ip-dataset.test.d.ts +1 -0
- package/dist/enrich/ip-dataset.test.js +85 -0
- package/dist/index.js +67 -8
- package/dist/tools/enrich-ips.d.ts +30 -0
- package/dist/tools/enrich-ips.js +60 -0
- package/dist/tools/enrich-ips.test.d.ts +1 -0
- package/dist/tools/enrich-ips.test.js +38 -0
- package/dist/tools/query-logs.d.ts +5 -2
- package/dist/tools/query-logs.js +31 -13
- package/dist/tools/query-metrics.d.ts +7 -3
- package/dist/tools/query-metrics.js +33 -12
- package/dist/tools/query-raw-gate.test.d.ts +1 -0
- package/dist/tools/query-raw-gate.test.js +52 -0
- package/dist/tools/registry-names.d.ts +1 -1
- package/dist/tools/registry-names.js +2 -0
- package/dist/tools/topology.js +14 -0
- package/dist/tools/topology.test.js +15 -0
- package/dist/tools/validation.d.ts +17 -0
- package/dist/tools/validation.js +27 -0
- package/dist/tools/validation.test.js +24 -1
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
package/dist/tools/validation.js
CHANGED
|
@@ -73,6 +73,33 @@ export function validateLogLabels(labels) {
|
|
|
73
73
|
}
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate a structured `labels` filter map for query_metrics. The rules are
|
|
78
|
+
* identical to the log-label validator (valid Prometheus label names, bounded
|
|
79
|
+
* map size + value length, fail-closed) — metric labels compile to PromQL
|
|
80
|
+
* label-equality matchers and the values are escaped for PromQL at injection
|
|
81
|
+
* time, exactly as the log path escapes for LogQL.
|
|
82
|
+
*/
|
|
83
|
+
export const validateMetricLabels = validateLogLabels;
|
|
84
|
+
/**
|
|
85
|
+
* Validate a raw PromQL/LogQL passthrough string. The capability gate (is raw
|
|
86
|
+
* query allowed at all) lives at the handler; this only bounds the shape:
|
|
87
|
+
* non-empty string, length-capped so a crafted query can't build a
|
|
88
|
+
* pathological request. The query is sent verbatim to the backend (that is the
|
|
89
|
+
* point of a passthrough), so there is no syntax check — an invalid query just
|
|
90
|
+
* yields the backend's own parse error.
|
|
91
|
+
*/
|
|
92
|
+
export function validateRawQuery(raw) {
|
|
93
|
+
if (raw === undefined)
|
|
94
|
+
return null;
|
|
95
|
+
if (typeof raw !== "string")
|
|
96
|
+
return "raw_query must be a string.";
|
|
97
|
+
if (raw.trim().length === 0)
|
|
98
|
+
return "raw_query must not be empty.";
|
|
99
|
+
if (raw.length > 8192)
|
|
100
|
+
return "raw_query too long (max 8192 chars).";
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
76
103
|
const AGGREGATE_OPS = new Set(["count_over_time", "sum", "topk"]);
|
|
77
104
|
/**
|
|
78
105
|
* Validate the query_logs `aggregate` spec. Fail-closed, like the labels
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { validateDuration, validateServiceName, sanitizeLabelValue, validateLogLabels, validateLogAggregate, errorResponse } from "./validation.js";
|
|
3
|
+
import { validateDuration, validateServiceName, sanitizeLabelValue, validateLogLabels, validateLogAggregate, validateMetricLabels, validateRawQuery, errorResponse } from "./validation.js";
|
|
4
4
|
describe("validateLogAggregate (Q-LOG2)", () => {
|
|
5
5
|
it("accepts undefined and valid specs", () => {
|
|
6
6
|
assert.equal(validateLogAggregate(undefined), null);
|
|
@@ -54,6 +54,29 @@ describe("validateLogLabels (Q-LOG1)", () => {
|
|
|
54
54
|
assert.ok(validateLogLabels(many));
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
+
describe("validateRawQuery (R4, issue #415 #3)", () => {
|
|
58
|
+
it("accepts undefined (not requested) and a normal query", () => {
|
|
59
|
+
assert.equal(validateRawQuery(undefined), null);
|
|
60
|
+
assert.equal(validateRawQuery('sum(rate(http_requests_total[5m]))'), null);
|
|
61
|
+
});
|
|
62
|
+
it("rejects non-strings, empty/whitespace, and over-long", () => {
|
|
63
|
+
assert.ok(validateRawQuery(42));
|
|
64
|
+
assert.ok(validateRawQuery(""));
|
|
65
|
+
assert.ok(validateRawQuery(" "));
|
|
66
|
+
assert.ok(validateRawQuery("x".repeat(8193)));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("validateMetricLabels (R3, issue #415 #4)", () => {
|
|
70
|
+
it("accepts undefined and a valid map (same rules as log labels)", () => {
|
|
71
|
+
assert.equal(validateMetricLabels(undefined), null);
|
|
72
|
+
assert.equal(validateMetricLabels({ status: "500", route: "/checkout" }), null);
|
|
73
|
+
});
|
|
74
|
+
it("rejects invalid label names and bad values, fail-closed", () => {
|
|
75
|
+
assert.ok(validateMetricLabels({ "a-b": "x" }));
|
|
76
|
+
assert.ok(validateMetricLabels({ status: 500 }));
|
|
77
|
+
assert.ok(validateMetricLabels("nope"));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
57
80
|
describe("validateDuration", () => {
|
|
58
81
|
it("accepts valid durations", () => {
|
|
59
82
|
assert.equal(validateDuration("5m"), null);
|
package/dist/types.d.ts
CHANGED
|
@@ -102,6 +102,11 @@ export interface MetricQuery {
|
|
|
102
102
|
duration: string;
|
|
103
103
|
step?: string;
|
|
104
104
|
groupBy?: string;
|
|
105
|
+
labels?: Record<string, string>;
|
|
106
|
+
/** Raw PromQL, run verbatim over query_range — bypasses the curated metric
|
|
107
|
+
* catalog/selector. When set, `metric`/`groupBy`/`labels`/`service` are
|
|
108
|
+
* ignored. Gated by the OMCP_RAW_QUERY capability at the tool layer. */
|
|
109
|
+
rawQuery?: string;
|
|
105
110
|
}
|
|
106
111
|
export interface LogQuery {
|
|
107
112
|
service: string;
|
|
@@ -115,6 +120,11 @@ export interface LogQuery {
|
|
|
115
120
|
* environment, …) become first-class selectors instead of brittle
|
|
116
121
|
* free-text regex. */
|
|
117
122
|
labels?: Record<string, string>;
|
|
123
|
+
/** Raw LogQL log-selector query, run verbatim — bypasses the curated
|
|
124
|
+
* stream-selector/pipeline construction. When set, `service`/`labels`/
|
|
125
|
+
* `level`/`query` are ignored. Gated by the OMCP_RAW_QUERY capability at
|
|
126
|
+
* the tool layer. For metric LogQL (count_over_time/…) use `aggregate`. */
|
|
127
|
+
rawQuery?: string;
|
|
118
128
|
}
|
|
119
129
|
/** Server-side log aggregation (Q-LOG2). Pushes count/group/topk down to
|
|
120
130
|
* the backend's metric-query path so an agent gets a *number*, not a
|
package/package.json
CHANGED