search_spans
Search Datadog trace spans by query, time range, and pagination to analyze application performance and troubleshoot issues.
Instructions
Tool for searching Datadog trace spans
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| filterQuery | No | Query string to search for (optional, default is '*') | * |
| filterFrom | No | Search start time (UNIX timestamp in seconds, optional, default is 15 minutes ago) | |
| filterTo | No | Search end time (UNIX timestamp in seconds, optional, default is current time) | |
| pageLimit | No | Maximum number of spans to retrieve (optional, default is 25) | |
| pageCursor | No | Cursor to retrieve the next page (optional) |
Implementation Reference
- src/tools/spans/search.ts:113-141 (handler)Handler function that validates parameters using Zod schema, calls the searchSpans helper with converted timestamps, generates a formatted summary and Datadog URL, and returns success or error response.
export const searchSpansHandler = async ( parameters: z.infer<typeof searchSpansZodSchema> ): Promise<ToolResponse> => { const validation = searchSpansZodSchema.safeParse(parameters); if (!validation.success) { return createErrorResponse( `Parameter validation error: ${validation.error.message}` ); } try { // Convert to Date objects after validation const validatedParams = { ...validation.data, filterFrom: new Date(validation.data.filterFrom * 1000), filterTo: new Date(validation.data.filterTo * 1000), }; const result = await searchSpans(validatedParams); const formattedResult = generateSummaryText(validation.data, result); const urlText = `[View in Datadog](https://app.datadoghq.com/apm/traces?query=${encodeURIComponent( validation.data.filterQuery )}&start=${validation.data.filterFrom}&end=${validation.data.filterTo})`; return createSuccessResponse([formattedResult, urlText]); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return createErrorResponse(`Span search error: ${errorMessage}`); } }; - src/tools/spans/search.ts:6-37 (schema)Zod schema defining the input parameters for the search_spans tool: filterQuery, filterFrom, filterTo, pageLimit, pageCursor with defaults and descriptions.
export const searchSpansZodSchema = z.object({ filterQuery: z .string() .optional() .default("*") .describe("Query string to search for (optional, default is '*')"), filterFrom: z .number() .optional() .default(Date.now() / 1000 - 15 * 60) .describe( "Search start time (UNIX timestamp in seconds, optional, default is 15 minutes ago)" ), filterTo: z .number() .optional() .default(Date.now() / 1000) .describe( "Search end time (UNIX timestamp in seconds, optional, default is current time)" ), pageLimit: z .number() .min(1) .max(1000) .optional() .default(25) .describe("Maximum number of spans to retrieve (optional, default is 25)"), pageCursor: z .string() .optional() .describe("Cursor to retrieve the next page (optional)"), }); - src/index.ts:25-30 (registration)Registration of the 'search_spans' tool in the MCP server using server.tool() with name, description, input schema, and handler.
server.tool( "search_spans", "Tool for searching Datadog trace spans", searchSpansZodSchema.shape, searchSpansHandler ); - src/datadog/spans/search.ts:5-51 (helper)Core helper function that performs the actual Datadog API call to search spans using the SpansApi, maps the response to Span objects, handles pagination cursor, and propagates errors.
export const searchSpans = async ( params: SpanSearchParams ): Promise<SpanSearchResult> => { try { const configuration = createConfiguration(); const spansApi = new v2.SpansApi(configuration); const response = await spansApi.listSpansGet({ filterQuery: params.filterQuery, filterFrom: params.filterFrom.toISOString(), filterTo: params.filterTo.toISOString(), pageLimit: params.pageLimit || 25, pageCursor: params.pageCursor, }); if (!response.data || response.data.length === 0) { return { spans: [] }; } const spans = response.data.map((spanData) => ({ id: spanData.id || "", traceId: spanData.attributes?.traceId, spanId: spanData.attributes?.spanId, parentId: spanData.attributes?.parentId, service: spanData.attributes?.service, resource: spanData.attributes?.resourceName, host: spanData.attributes?.host, env: spanData.attributes?.env, startTimestamp: spanData.attributes?.startTimestamp?.toISOString(), endTimestamp: spanData.attributes?.endTimestamp?.toISOString(), duration: spanData.attributes?.attributes?.duration, type: spanData.attributes?.type, tags: spanData.attributes?.tags || [], attributes: spanData.attributes?.attributes || {}, })); const nextCursor = response.meta?.page?.after; return { spans, nextCursor, }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error searching spans: ${errorMessage}`); throw new Error(`Datadog API error: ${errorMessage}`); } }; - src/tools/spans/search.ts:39-111 (helper)Helper function to generate a markdown-formatted summary text of the search results, including criteria, span summaries, and key attributes for display in responses.
const generateSummaryText = ( data: z.infer<typeof searchSpansZodSchema>, result: SpanSearchResult ): string => { let responseText = ""; responseText += `# Span Search Results\n`; responseText += `## Search Criteria\n`; responseText += `* Query: ${data.filterQuery || "*"}\n`; responseText += `* Time Range: ${new Date( data.filterFrom * 1000 ).toLocaleString()} to ${new Date(data.filterTo * 1000).toLocaleString()}\n`; responseText += `* Retrieved: ${result.spans.length} spans`; if (result.spans.length === 0) { return responseText; } if (result.nextCursor) { responseText += `* Next Page Cursor: ${result.nextCursor}\n`; } responseText += "## Span Summary\n"; for (const [index, span] of result.spans.entries()) { responseText += `### [${index + 1}]\n`; if (span.service) { responseText += `* Service: ${span.service}\n`; } if (span.startTimestamp) { responseText += `* Time: ${new Date( span.startTimestamp ).toLocaleString()}\n`; } if (span.resource) { responseText += `* Resource: ${span.resource}\n`; } if (span.duration) { responseText += `* Duration: ${(span.duration / 1000).toFixed( 3 )} seconds\n`; } if (span.host) { responseText += `* Host: ${span.host}\n`; } if (span.env) { responseText += `* Environment: ${span.env}\n`; } if (span.type) { responseText += `* Type: ${span.type}\n`; } responseText += `#### Key Attributes\n`; for (const key of [ "http.method", "http.url", "http.status_code", "error", ]) { if (span.attributes && key in span.attributes) { responseText += `* ${key}: \`${JSON.stringify( span.attributes[key] )}\`\n`; } } } return responseText; };