cubeapm-mcp 1.1.0 → 1.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/README.md +34 -5
- package/dist/index.js +190 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/cubeapm-mcp)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://lobehub.com/mcp/technicalrhino-cubeapm-mcp)
|
|
5
6
|
|
|
6
7
|
A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for [CubeAPM](https://cubeapm.com) - enabling AI assistants like Claude to query your observability data including traces, metrics, and logs.
|
|
7
8
|
|
|
@@ -159,12 +160,16 @@ You can now ask Claude questions like:
|
|
|
159
160
|
| `search_traces` | Search traces by service, environment, or custom query |
|
|
160
161
|
| `get_trace` | Fetch complete trace details by trace ID |
|
|
161
162
|
|
|
162
|
-
**Search Parameters:**
|
|
163
|
-
- `query` -
|
|
164
|
-
- `env` - Environment filter (
|
|
165
|
-
- `service` - Service name filter
|
|
166
|
-
- `start` / `end` - Time range
|
|
163
|
+
**Search Parameters (Required):**
|
|
164
|
+
- `query` - Search query (default: `*` for wildcard)
|
|
165
|
+
- `env` - Environment filter (default: `UNSET`)
|
|
166
|
+
- `service` - Service name filter (**required**, case-sensitive)
|
|
167
|
+
- `start` / `end` - Time range (RFC3339 or Unix timestamp)
|
|
168
|
+
|
|
169
|
+
**Search Parameters (Optional):**
|
|
167
170
|
- `limit` - Maximum results (default: 20)
|
|
171
|
+
- `spanKind` - Filter by span type: `server`, `client`, `consumer`, `producer`
|
|
172
|
+
- `sortBy` - Sort by: `duration` (useful for finding slow traces)
|
|
168
173
|
|
|
169
174
|
**Get Trace Parameters:**
|
|
170
175
|
- `trace_id` - Hex-encoded trace ID
|
|
@@ -176,6 +181,30 @@ You can now ask Claude questions like:
|
|
|
176
181
|
|------|-------------|
|
|
177
182
|
| `ingest_metrics_prometheus` | Send metrics in Prometheus text exposition format |
|
|
178
183
|
|
|
184
|
+
## Prompts
|
|
185
|
+
|
|
186
|
+
Pre-defined templates for common observability tasks:
|
|
187
|
+
|
|
188
|
+
| Prompt | Description |
|
|
189
|
+
|--------|-------------|
|
|
190
|
+
| `investigate-service` | Comprehensive service investigation - checks errors, latency, and traces |
|
|
191
|
+
| `check-latency` | Get P50, P95, P99 latency percentiles for a service |
|
|
192
|
+
| `find-slow-traces` | Find slowest traces to identify performance bottlenecks |
|
|
193
|
+
|
|
194
|
+
**Usage Example:**
|
|
195
|
+
```
|
|
196
|
+
Use the investigate-service prompt for Kratos-Prod
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Resources
|
|
200
|
+
|
|
201
|
+
Readable resources exposing CubeAPM data and configuration:
|
|
202
|
+
|
|
203
|
+
| Resource URI | Description |
|
|
204
|
+
|--------------|-------------|
|
|
205
|
+
| `cubeapm://config` | Current CubeAPM connection configuration |
|
|
206
|
+
| `cubeapm://query-patterns` | Query patterns and naming conventions reference |
|
|
207
|
+
|
|
179
208
|
## CubeAPM Query Patterns
|
|
180
209
|
|
|
181
210
|
### Metrics Naming Conventions
|
package/dist/index.js
CHANGED
|
@@ -200,28 +200,42 @@ histogram_quantiles("phi", 0.95, sum by (vmrange, service) (increase(cube_apm_la
|
|
|
200
200
|
// ============================================
|
|
201
201
|
server.tool("search_traces", `Search for traces in CubeAPM matching the specified criteria. Returns trace snippets with key spans.
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
IMPORTANT - Required Parameters:
|
|
204
|
+
- query, env, service, start, end are ALL REQUIRED by CubeAPM API
|
|
205
|
+
- Use query="*" for wildcard search
|
|
206
|
+
- Use env="UNSET" if environment is not configured
|
|
204
207
|
- Service names are case-sensitive (e.g., "Kratos-Prod" not "kratos")
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
-
|
|
208
|
+
|
|
209
|
+
Optional filters:
|
|
210
|
+
- spanKind: server, client, consumer, producer
|
|
211
|
+
- sortBy: duration (to find slow traces)
|
|
208
212
|
|
|
209
213
|
To discover available service names, first query metrics:
|
|
210
|
-
count by (service) (cube_apm_calls_total{env="UNSET"})
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
count by (service) (cube_apm_calls_total{env="UNSET"})
|
|
215
|
+
|
|
216
|
+
Example: Find slow server spans in Shopify-Prod
|
|
217
|
+
query="*", env="UNSET", service="Shopify-Prod", spanKind="server", sortBy="duration"`, {
|
|
218
|
+
query: z.string().default("*").describe("The traces search query (use * for wildcard)"),
|
|
219
|
+
env: z.string().default("UNSET").describe("Environment name (use UNSET if not configured)"),
|
|
220
|
+
service: z.string().describe("Service name to filter by (REQUIRED, case-sensitive)"),
|
|
214
221
|
start: z.string().describe("Start timestamp in RFC3339 format or Unix seconds"),
|
|
215
222
|
end: z.string().describe("End timestamp in RFC3339 format or Unix seconds"),
|
|
216
223
|
limit: z.number().optional().default(20).describe("Maximum number of traces to return"),
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
spanKind: z.string().optional().describe("Filter by span kind: server, client, consumer, producer"),
|
|
225
|
+
sortBy: z.string().optional().describe("Sort results by: duration"),
|
|
226
|
+
}, async ({ query, env, service, start, end, limit, spanKind, sortBy }) => {
|
|
227
|
+
const params = new URLSearchParams({
|
|
228
|
+
query,
|
|
229
|
+
env,
|
|
230
|
+
service,
|
|
231
|
+
start,
|
|
232
|
+
end,
|
|
233
|
+
limit: String(limit)
|
|
234
|
+
});
|
|
235
|
+
if (spanKind)
|
|
236
|
+
params.append("spanKind", spanKind);
|
|
237
|
+
if (sortBy)
|
|
238
|
+
params.append("sortBy", sortBy);
|
|
225
239
|
const response = await fetch(`${queryBaseUrl}/api/traces/api/v1/search?${params.toString()}`, { method: "GET" });
|
|
226
240
|
if (!response.ok) {
|
|
227
241
|
return {
|
|
@@ -305,6 +319,166 @@ server.tool("ingest_metrics_prometheus", "Send metrics to CubeAPM in Prometheus
|
|
|
305
319
|
],
|
|
306
320
|
};
|
|
307
321
|
});
|
|
322
|
+
// ============================================
|
|
323
|
+
// PROMPTS - Pre-defined templates for common tasks
|
|
324
|
+
// ============================================
|
|
325
|
+
server.prompt("investigate-service", "Investigate issues with a specific service - checks errors, latency, and recent traces", {
|
|
326
|
+
service: z.string().describe("The service name to investigate (case-sensitive)"),
|
|
327
|
+
timeRange: z.string().optional().default("1h").describe("Time range to look back (e.g., 1h, 6h, 24h)"),
|
|
328
|
+
}, async ({ service, timeRange }) => {
|
|
329
|
+
return {
|
|
330
|
+
messages: [
|
|
331
|
+
{
|
|
332
|
+
role: "user",
|
|
333
|
+
content: {
|
|
334
|
+
type: "text",
|
|
335
|
+
text: `Please investigate the ${service} service for the last ${timeRange}:
|
|
336
|
+
|
|
337
|
+
1. First, check the error rate using metrics:
|
|
338
|
+
- Query: sum(increase(cube_apm_calls_total{service="${service}", status_code="ERROR"}[${timeRange}])) / sum(increase(cube_apm_calls_total{service="${service}"}[${timeRange}])) * 100
|
|
339
|
+
|
|
340
|
+
2. Check P95 latency:
|
|
341
|
+
- Query: histogram_quantiles("phi", 0.95, sum by (vmrange) (increase(cube_apm_latency_bucket{service="${service}", span_kind="server"}[5m])))
|
|
342
|
+
|
|
343
|
+
3. Search for error traces:
|
|
344
|
+
- Use search_traces with service="${service}", query="status_code=ERROR"
|
|
345
|
+
|
|
346
|
+
4. Check recent logs if available:
|
|
347
|
+
- Query logs with {service_name="${service}"} or discover labels first with *
|
|
348
|
+
|
|
349
|
+
Summarize any issues found and provide recommendations.`,
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
server.prompt("check-latency", "Check P50, P95, and P99 latency percentiles for a service", {
|
|
356
|
+
service: z.string().describe("The service name (case-sensitive)"),
|
|
357
|
+
}, async ({ service }) => {
|
|
358
|
+
return {
|
|
359
|
+
messages: [
|
|
360
|
+
{
|
|
361
|
+
role: "user",
|
|
362
|
+
content: {
|
|
363
|
+
type: "text",
|
|
364
|
+
text: `Check the latency percentiles for ${service} service:
|
|
365
|
+
|
|
366
|
+
Use query_metrics_instant with these queries:
|
|
367
|
+
|
|
368
|
+
1. P50 (median):
|
|
369
|
+
histogram_quantiles("phi", 0.5, sum by (vmrange, service) (increase(cube_apm_latency_bucket{service="${service}", span_kind="server"}[5m])))
|
|
370
|
+
|
|
371
|
+
2. P95:
|
|
372
|
+
histogram_quantiles("phi", 0.95, sum by (vmrange, service) (increase(cube_apm_latency_bucket{service="${service}", span_kind="server"}[5m])))
|
|
373
|
+
|
|
374
|
+
3. P99:
|
|
375
|
+
histogram_quantiles("phi", 0.99, sum by (vmrange, service) (increase(cube_apm_latency_bucket{service="${service}", span_kind="server"}[5m])))
|
|
376
|
+
|
|
377
|
+
Note: Latency values are in SECONDS (multiply by 1000 for milliseconds).
|
|
378
|
+
|
|
379
|
+
Present the results in a clear table format.`,
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
};
|
|
384
|
+
});
|
|
385
|
+
server.prompt("find-slow-traces", "Find the slowest traces for a service to identify performance bottlenecks", {
|
|
386
|
+
service: z.string().describe("The service name (case-sensitive)"),
|
|
387
|
+
minDuration: z.string().optional().default("1s").describe("Minimum duration to filter (e.g., 500ms, 1s, 2s)"),
|
|
388
|
+
}, async ({ service, minDuration }) => {
|
|
389
|
+
return {
|
|
390
|
+
messages: [
|
|
391
|
+
{
|
|
392
|
+
role: "user",
|
|
393
|
+
content: {
|
|
394
|
+
type: "text",
|
|
395
|
+
text: `Find slow traces for ${service} service (minimum duration: ${minDuration}):
|
|
396
|
+
|
|
397
|
+
1. Search for traces using search_traces with:
|
|
398
|
+
- service: "${service}"
|
|
399
|
+
- sortBy: "duration"
|
|
400
|
+
- spanKind: "server"
|
|
401
|
+
- limit: 10
|
|
402
|
+
|
|
403
|
+
2. For the slowest trace found, use get_trace to fetch the full trace details
|
|
404
|
+
|
|
405
|
+
3. Analyze the trace waterfall to identify:
|
|
406
|
+
- Which span took the longest
|
|
407
|
+
- Any external dependencies causing delays
|
|
408
|
+
- Database queries or API calls that are slow
|
|
409
|
+
|
|
410
|
+
Provide a summary of performance bottlenecks and recommendations.`,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
// ============================================
|
|
417
|
+
// RESOURCES - Expose CubeAPM data as readable resources
|
|
418
|
+
// ============================================
|
|
419
|
+
server.resource("config", "cubeapm://config", {
|
|
420
|
+
description: "Current CubeAPM connection configuration",
|
|
421
|
+
mimeType: "application/json",
|
|
422
|
+
}, async () => {
|
|
423
|
+
return {
|
|
424
|
+
contents: [
|
|
425
|
+
{
|
|
426
|
+
uri: "cubeapm://config",
|
|
427
|
+
mimeType: "application/json",
|
|
428
|
+
text: JSON.stringify({
|
|
429
|
+
queryUrl: queryBaseUrl,
|
|
430
|
+
ingestUrl: ingestBaseUrl,
|
|
431
|
+
usingFullUrl: !!CUBEAPM_URL,
|
|
432
|
+
host: CUBEAPM_HOST,
|
|
433
|
+
queryPort: CUBEAPM_QUERY_PORT,
|
|
434
|
+
ingestPort: CUBEAPM_INGEST_PORT,
|
|
435
|
+
}, null, 2),
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
};
|
|
439
|
+
});
|
|
440
|
+
server.resource("query-patterns", "cubeapm://query-patterns", {
|
|
441
|
+
description: "CubeAPM-specific query patterns and naming conventions",
|
|
442
|
+
mimeType: "text/markdown",
|
|
443
|
+
}, async () => {
|
|
444
|
+
return {
|
|
445
|
+
contents: [
|
|
446
|
+
{
|
|
447
|
+
uri: "cubeapm://query-patterns",
|
|
448
|
+
mimeType: "text/markdown",
|
|
449
|
+
text: `# CubeAPM Query Patterns
|
|
450
|
+
|
|
451
|
+
## Metrics Naming Conventions
|
|
452
|
+
- Prefix: \`cube_apm_*\` (e.g., cube_apm_calls_total, cube_apm_latency_bucket)
|
|
453
|
+
- Service label: \`service\` (NOT "server" or "service_name")
|
|
454
|
+
- Common labels: env, service, span_kind, status_code, http_code
|
|
455
|
+
|
|
456
|
+
## Histogram Queries (Percentiles)
|
|
457
|
+
CubeAPM uses VictoriaMetrics-style histograms with \`vmrange\` label:
|
|
458
|
+
|
|
459
|
+
\`\`\`promql
|
|
460
|
+
# P95 Latency
|
|
461
|
+
histogram_quantiles("phi", 0.95, sum by (vmrange, service) (
|
|
462
|
+
increase(cube_apm_latency_bucket{service="MyService", span_kind="server"}[5m])
|
|
463
|
+
))
|
|
464
|
+
\`\`\`
|
|
465
|
+
|
|
466
|
+
Note: Latency values are in SECONDS (0.05 = 50ms)
|
|
467
|
+
|
|
468
|
+
## Logs (LogsQL)
|
|
469
|
+
- Use \`*\` to discover available labels
|
|
470
|
+
- Lambda logs: \`{faas.name="my-function"}\`
|
|
471
|
+
- Service logs: \`{service_name="my-service"}\`
|
|
472
|
+
|
|
473
|
+
## Traces
|
|
474
|
+
- query, env, service are REQUIRED parameters
|
|
475
|
+
- Use \`sortBy=duration\` to find slow traces
|
|
476
|
+
- Filter by spanKind: server, client, consumer, producer
|
|
477
|
+
`,
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
};
|
|
481
|
+
});
|
|
308
482
|
// Start the server
|
|
309
483
|
async function main() {
|
|
310
484
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED