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.
Files changed (3) hide show
  1. package/README.md +34 -5
  2. package/dist/index.js +190 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/cubeapm-mcp.svg)](https://www.npmjs.com/package/cubeapm-mcp)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![MCP Server](https://lobehub.com/badge/mcp/technicalrhino-cubeapm-mcp)](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` - Optional search query
164
- - `env` - Environment filter (e.g., `production`)
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
- Tips for effective trace searches:
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
- - 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
+ 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
- query: z.string().optional().describe("The traces search query"),
212
- env: z.string().optional().describe("Environment name to filter by"),
213
- service: z.string().optional().describe("Service name to filter by"),
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
- }, async ({ query, env, service, start, end, limit }) => {
218
- const params = new URLSearchParams({ start, end, limit: String(limit) });
219
- if (query)
220
- params.append("query", query);
221
- if (env)
222
- params.append("env", env);
223
- if (service)
224
- params.append("service", service);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cubeapm-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.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",