Skip to main content
Glama
querySpans.ts6.96 kB
import type { Tool } from "@modelcontextprotocol/sdk/types.js"; import type { TuskDriftApiClient } from "../apiClient.js"; import { querySpansInputSchema, type QuerySpansInput } from "../types.js"; export const querySpansTool: Tool = { name: "query_spans", description: `Search and filter API traffic span recordings. Use this tool to: - Find specific API calls by endpoint name, HTTP method, or status code - Search for errors or slow requests - Get recent traffic for a specific endpoint - Debug specific API calls Examples: - Find failed requests: where.name = { contains: "/api/users" }, jsonbFilters = [{ column: "outputValue", jsonPath: "$.statusCode", gte: 400, castAs: "int" }] - Find slow requests: where.duration = { gt: 1000 } - Recent traffic for endpoint: where.name = { eq: "/api/orders" }, limit = 10, orderBy = [{ field: "timestamp", direction: "DESC" }]`, inputSchema: { type: "object", properties: { observableServiceId: { type: "string", description: "Service ID to query. Required if multiple services are available.", }, where: { type: "object", description: "Filter conditions for spans", properties: { name: { type: "object", description: "Filter by span/endpoint name", properties: { eq: { type: "string" }, contains: { type: "string" }, startsWith: { type: "string" }, in: { type: "array", items: { type: "string" } }, }, }, packageName: { type: "object", description: "Filter by instrumentation package (http, pg, fetch, grpc, etc.)", properties: { eq: { type: "string" }, in: { type: "array", items: { type: "string" } }, }, }, instrumentationName: { type: "object", description: "Filter by instrumentation name", properties: { eq: { type: "string" } }, }, duration: { type: "object", description: "Filter by duration in milliseconds", properties: { gt: { type: "number" }, gte: { type: "number" }, lt: { type: "number" }, lte: { type: "number" }, }, }, traceId: { type: "object", description: "Filter by trace ID", properties: { eq: { type: "string" } }, }, isRootSpan: { type: "object", description: "Filter by root span status", properties: { eq: { type: "boolean" } }, }, AND: { type: "array", description: "Combine conditions with AND", }, OR: { type: "array", description: "Combine conditions with OR", }, }, }, jsonbFilters: { type: "array", description: "Filters for JSONB columns (inputValue, outputValue, metadata)", items: { type: "object", properties: { column: { type: "string", enum: ["inputValue", "outputValue", "metadata", "status"], description: "JSONB column to filter", }, jsonPath: { type: "string", description: "JSONPath expression starting with $ (e.g., $.statusCode, $.body.userId)", }, eq: { description: "Equals value" }, neq: { description: "Not equals value" }, gt: { type: "number", description: "Greater than" }, gte: { type: "number", description: "Greater than or equal" }, lt: { type: "number", description: "Less than" }, lte: { type: "number", description: "Less than or equal" }, contains: { type: "string", description: "String contains" }, castAs: { type: "string", enum: ["text", "int", "float", "boolean"], description: "Cast JSONB value to type for comparison", }, decodeBase64: { type: "boolean", description: "Decode base64 string before applying filter", }, thenPath: { type: "string", description: "Additional JSONPath to apply after base64 decoding", }, }, required: ["column", "jsonPath"], }, }, orderBy: { type: "array", description: "Order results", items: { type: "object", properties: { field: { type: "string", enum: ["timestamp", "duration", "name"] }, direction: { type: "string", enum: ["ASC", "DESC"] }, }, required: ["field", "direction"], }, }, limit: { type: "number", description: "Maximum results to return (1-100, default 20)", default: 20, }, offset: { type: "number", description: "Pagination offset", default: 0, }, includeInputOutput: { type: "boolean", description: "Include full inputValue/outputValue in results (can be verbose)", default: false, }, maxPayloadLength: { type: "number", description: "Truncate payload strings to this length", default: 500, }, }, }, }; export async function handleQuerySpans( client: TuskDriftApiClient, args: Record<string, unknown> ): Promise<{ content: Array<{ type: "text"; text: string }> }> { const input = querySpansInputSchema.parse(args) as QuerySpansInput; const result = await client.querySpans(input); const summary = [ `Found ${result.total} spans (showing ${result.spans.length})`, result.hasMore ? `More results available (offset: ${(input.offset || 0) + result.spans.length})` : "", ] .filter(Boolean) .join("\n"); const spansText = result.spans .map((span, i) => { const lines = [ `[${i + 1}] ${span.name}`, ` ID: ${span.id}`, ` Trace: ${span.traceId}`, ` Package: ${span.packageName}`, ` Duration: ${span.duration.toFixed(2)}ms`, ` Status: ${span.status.code === 0 ? "OK" : span.status.code === 1 ? "UNSET" : "ERROR"}`, ` Timestamp: ${span.timestamp}`, ]; if (span.inputValue && input.includeInputOutput) { lines.push(` Input: ${JSON.stringify(span.inputValue, null, 2).split("\n").join("\n ")}`); } if (span.outputValue && input.includeInputOutput) { lines.push(` Output: ${JSON.stringify(span.outputValue, null, 2).split("\n").join("\n ")}`); } return lines.join("\n"); }) .join("\n\n"); return { content: [ { type: "text", text: `${summary}\n\n${spansText}`, }, ], }; }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Use-Tusk/drift-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server