cubeapm-mcp 1.0.0 → 1.1.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 +72 -8
  2. package/dist/index.js +75 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -68,7 +68,8 @@ You can now ask Claude questions like:
68
68
 
69
69
  | Environment Variable | Default | Description |
70
70
  |---------------------|---------|-------------|
71
- | `CUBEAPM_HOST` | `localhost` | CubeAPM server hostname or IP |
71
+ | `CUBEAPM_URL` | - | Full URL to CubeAPM (e.g., `https://cube.example.com`). Takes precedence over HOST/PORT settings. |
72
+ | `CUBEAPM_HOST` | `localhost` | CubeAPM server hostname or IP (used if CUBEAPM_URL not set) |
72
73
  | `CUBEAPM_QUERY_PORT` | `3140` | Port for querying APIs (traces, metrics, logs) |
73
74
  | `CUBEAPM_INGEST_PORT` | `3130` | Port for ingestion APIs |
74
75
 
@@ -89,7 +90,22 @@ You can now ask Claude questions like:
89
90
  }
90
91
  ```
91
92
 
92
- **Production:**
93
+ **Production (with full URL):**
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "cubeapm": {
98
+ "command": "npx",
99
+ "args": ["-y", "cubeapm-mcp"],
100
+ "env": {
101
+ "CUBEAPM_URL": "https://cubeapm.internal.company.com"
102
+ }
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ **Production (with host/port):**
93
109
  ```json
94
110
  {
95
111
  "mcpServers": {
@@ -160,20 +176,68 @@ You can now ask Claude questions like:
160
176
  |------|-------------|
161
177
  | `ingest_metrics_prometheus` | Send metrics in Prometheus text exposition format |
162
178
 
179
+ ## CubeAPM Query Patterns
180
+
181
+ ### Metrics Naming Conventions
182
+
183
+ CubeAPM uses specific naming conventions that differ from standard OpenTelemetry:
184
+
185
+ | What | CubeAPM Convention |
186
+ |------|-------------------|
187
+ | Metric prefix | `cube_apm_*` (e.g., `cube_apm_calls_total`, `cube_apm_latency_bucket`) |
188
+ | Service label | `service` (NOT `server` or `service_name`) |
189
+ | Common labels | `env`, `service`, `span_kind`, `status_code`, `http_code` |
190
+
191
+ ### Histogram Queries (P50, P90, P95, P99)
192
+
193
+ CubeAPM uses **VictoriaMetrics-style histograms** with `vmrange` labels instead of Prometheus `le` buckets:
194
+
195
+ ```promql
196
+ # ✅ Correct - Use histogram_quantiles() with vmrange
197
+ histogram_quantiles("phi", 0.95, sum by (vmrange, service) (
198
+ increase(cube_apm_latency_bucket{service="MyService", span_kind="server"}[5m])
199
+ ))
200
+
201
+ # ❌ Wrong - Standard Prometheus syntax won't work
202
+ histogram_quantile(0.95, sum by (le) (rate(http_request_duration_bucket[5m])))
203
+ ```
204
+
205
+ > **Note:** Latency values are returned in **seconds** (0.05 = 50ms)
206
+
207
+ ### Logs Label Discovery
208
+
209
+ Log labels vary by source. Use `*` query first to discover available labels:
210
+
211
+ | Source | Common Labels |
212
+ |--------|---------------|
213
+ | Lambda functions | `faas.name`, `faas.arn`, `env`, `aws.lambda_request_id` |
214
+ | Services | `service_name`, `level`, `host` |
215
+
216
+ ```logsql
217
+ # Discover all labels
218
+ *
219
+
220
+ # Lambda function logs
221
+ {faas.name="my-lambda-prod"}
222
+
223
+ # Search with text filter
224
+ {faas.name=~".*-prod"} AND "error"
225
+ ```
226
+
163
227
  ## Example Queries
164
228
 
165
229
  ### Logs
166
230
  ```
167
- "Find all ERROR logs from auth-service in the last 30 minutes"
168
- "Show me logs containing 'connection refused' from the database service"
169
- "Query logs where trace_id matches xyz123"
231
+ "Show me logs from webhook-lambda-prod"
232
+ "Find all logs containing 'timeout' in the last hour"
233
+ "Query logs from Lambda functions in production"
170
234
  ```
171
235
 
172
236
  ### Metrics
173
237
  ```
174
- "What's the average request duration for the API gateway?"
175
- "Show me CPU usage for all services over the last 6 hours"
176
- "Graph the error rate for checkout-service with 1-minute resolution"
238
+ "What's the P95 latency for Kratos-Prod service?"
239
+ "Show me error rate for all services"
240
+ "List all available services in CubeAPM"
177
241
  ```
178
242
 
179
243
  ### Traces
package/dist/index.js CHANGED
@@ -3,11 +3,20 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
5
  // Configuration from environment variables
6
+ // CUBEAPM_URL takes precedence - use full URL like https://prod-cube.example.com
7
+ // Falls back to building URL from CUBEAPM_HOST + ports for backward compatibility
8
+ const CUBEAPM_URL = process.env.CUBEAPM_URL;
6
9
  const CUBEAPM_HOST = process.env.CUBEAPM_HOST || "localhost";
7
10
  const CUBEAPM_QUERY_PORT = process.env.CUBEAPM_QUERY_PORT || "3140";
8
11
  const CUBEAPM_INGEST_PORT = process.env.CUBEAPM_INGEST_PORT || "3130";
9
- const queryBaseUrl = `http://${CUBEAPM_HOST}:${CUBEAPM_QUERY_PORT}`;
10
- const ingestBaseUrl = `http://${CUBEAPM_HOST}:${CUBEAPM_INGEST_PORT}`;
12
+ // If CUBEAPM_URL is provided, use it directly (removes trailing slash if present)
13
+ // Otherwise build from host:port
14
+ const queryBaseUrl = CUBEAPM_URL
15
+ ? CUBEAPM_URL.replace(/\/$/, '')
16
+ : `http://${CUBEAPM_HOST}:${CUBEAPM_QUERY_PORT}`;
17
+ const ingestBaseUrl = CUBEAPM_URL
18
+ ? CUBEAPM_URL.replace(/\/$/, '')
19
+ : `http://${CUBEAPM_HOST}:${CUBEAPM_INGEST_PORT}`;
11
20
  // Create the MCP server
12
21
  const server = new McpServer({
13
22
  name: "cubeapm-mcp",
@@ -16,7 +25,27 @@ const server = new McpServer({
16
25
  // ============================================
17
26
  // LOGS APIs
18
27
  // ============================================
19
- server.tool("query_logs", "Query logs from CubeAPM using LogsQL syntax. Returns log entries matching the query.", {
28
+ server.tool("query_logs", `Query logs from CubeAPM using LogsQL syntax (VictoriaLogs compatible). Returns log entries matching the query.
29
+
30
+ IMPORTANT - Log Label Discovery:
31
+ - Labels vary by source! Use "*" query first to discover available labels
32
+ - Lambda logs use: faas.name, faas.arn, env, aws.lambda_request_id
33
+ - Service logs may use: service_name, level, host
34
+ - NOT all sources have "service_name" - check the _stream field in results
35
+
36
+ LogsQL Query Syntax:
37
+ - Wildcard (discover labels): *
38
+ - Stream filters: {faas.name="my-lambda-prod"}
39
+ - Text search: {faas.name="webhook-lambda-prod"} AND "error"
40
+ - Regex match: {faas.name=~".*-prod"}
41
+ - Negation: {faas.name!="unwanted-service"}
42
+ - Combine filters: {env="UNSET", faas.name=~".*-lambda.*"}
43
+
44
+ Example queries:
45
+ - All logs (discover structure): *
46
+ - Lambda logs: {faas.name="webhook-lambda-prod"}
47
+ - Search errors: {faas.name=~".*"} AND "error"
48
+ - By environment: {env="production"}`, {
20
49
  query: z.string().describe("The log search query including stream filters (LogsQL syntax)"),
21
50
  start: z.string().describe("Start time in RFC3339 format (e.g., 2024-01-01T00:00:00Z) or Unix timestamp in seconds"),
22
51
  end: z.string().describe("End time in RFC3339 format or Unix timestamp in seconds"),
@@ -59,7 +88,24 @@ server.tool("query_logs", "Query logs from CubeAPM using LogsQL syntax. Returns
59
88
  // ============================================
60
89
  // METRICS APIs
61
90
  // ============================================
62
- server.tool("query_metrics_instant", "Execute an instant query against CubeAPM metrics at a single point in time. Uses PromQL-compatible syntax.", {
91
+ server.tool("query_metrics_instant", `Execute an instant query against CubeAPM metrics at a single point in time. Uses PromQL-compatible syntax (VictoriaMetrics flavor).
92
+
93
+ IMPORTANT - CubeAPM Metric Naming Conventions:
94
+ - Metrics use "cube_apm_" prefix (e.g., cube_apm_calls_total, cube_apm_latency_bucket)
95
+ - Service label is "service" (NOT "server" or "service_name")
96
+ - Common labels: env, service, span_kind (server|client|consumer|producer), status_code, http_code
97
+
98
+ HISTOGRAM QUERIES (P50, P90, P95, P99):
99
+ - CubeAPM uses VictoriaMetrics-style histograms with "vmrange" label (NOT Prometheus "le" buckets)
100
+ - Use histogram_quantiles() function instead of histogram_quantile()
101
+ - Syntax: histogram_quantiles("phi", 0.95, sum by (vmrange) (increase(cube_apm_latency_bucket{...}[5m])))
102
+ - Latency values are returned in SECONDS (0.05 = 50ms)
103
+
104
+ Example queries:
105
+ - P95 latency: histogram_quantiles("phi", 0.95, sum by (vmrange, service) (increase(cube_apm_latency_bucket{service="MyService", span_kind="server"}[5m])))
106
+ - Error rate: sum by (service) (increase(cube_apm_calls_total{status_code="ERROR"}[1h])) / sum by (service) (increase(cube_apm_calls_total[1h])) * 100
107
+ - List services: count by (service) (cube_apm_calls_total{env="UNSET"})
108
+ - Request count: sum by (service) (increase(cube_apm_calls_total{span_kind=~"server|consumer"}[1h]))`, {
63
109
  query: z.string().describe("The metrics query expression (PromQL syntax)"),
64
110
  time: z.string().describe("Evaluation timestamp in RFC3339 format or Unix timestamp in seconds"),
65
111
  step: z.number().optional().describe("Time window in seconds for processing data"),
@@ -95,7 +141,21 @@ server.tool("query_metrics_instant", "Execute an instant query against CubeAPM m
95
141
  ],
96
142
  };
97
143
  });
98
- server.tool("query_metrics_range", "Execute a range query against CubeAPM metrics over a time range. Returns time series data.", {
144
+ server.tool("query_metrics_range", `Execute a range query against CubeAPM metrics over a time range. Returns time series data. Uses PromQL-compatible syntax (VictoriaMetrics flavor).
145
+
146
+ IMPORTANT - CubeAPM Metric Naming Conventions:
147
+ - Metrics use "cube_apm_" prefix (e.g., cube_apm_calls_total, cube_apm_latency_bucket)
148
+ - Service label is "service" (NOT "server" or "service_name")
149
+ - Common labels: env, service, span_kind (server|client|consumer|producer), status_code, http_code
150
+
151
+ HISTOGRAM QUERIES (P50, P90, P95, P99):
152
+ - CubeAPM uses VictoriaMetrics-style histograms with "vmrange" label (NOT Prometheus "le" buckets)
153
+ - Use histogram_quantiles() function instead of histogram_quantile()
154
+ - Syntax: histogram_quantiles("phi", 0.95, sum by (vmrange, service) (increase(cube_apm_latency_bucket{...}[5m])))
155
+ - Latency values are returned in SECONDS (0.05 = 50ms)
156
+
157
+ Example range query for P95 over time:
158
+ histogram_quantiles("phi", 0.95, sum by (vmrange, service) (increase(cube_apm_latency_bucket{service="MyService", span_kind="server"}[5m])))`, {
99
159
  query: z.string().describe("The metrics query expression (PromQL syntax)"),
100
160
  start: z.string().describe("Start timestamp in RFC3339 format or Unix timestamp"),
101
161
  end: z.string().describe("End timestamp in RFC3339 format or Unix timestamp"),
@@ -138,7 +198,16 @@ server.tool("query_metrics_range", "Execute a range query against CubeAPM metric
138
198
  // ============================================
139
199
  // TRACES APIs
140
200
  // ============================================
141
- server.tool("search_traces", "Search for traces in CubeAPM matching the specified criteria. Returns trace snippets with key spans.", {
201
+ server.tool("search_traces", `Search for traces in CubeAPM matching the specified criteria. Returns trace snippets with key spans.
202
+
203
+ Tips for effective trace searches:
204
+ - Service names are case-sensitive (e.g., "Kratos-Prod" not "kratos")
205
+ - Use the service parameter to filter by service name
206
+ - Use the env parameter to filter by environment
207
+ - Time range is required (start/end in RFC3339 or Unix timestamp)
208
+
209
+ To discover available service names, first query metrics:
210
+ count by (service) (cube_apm_calls_total{env="UNSET"})`, {
142
211
  query: z.string().optional().describe("The traces search query"),
143
212
  env: z.string().optional().describe("Environment name to filter by"),
144
213
  service: z.string().optional().describe("Service name to filter by"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cubeapm-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server for CubeAPM - Query traces, metrics, and logs from your observability platform using AI assistants",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",