Skip to main content
Glama
Nozomuts

Datadog MCP Server

by Nozomuts

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
NameRequiredDescriptionDefault
filterQueryNoQuery string to search for (optional, default is '*')*
filterFromNoSearch start time (UNIX timestamp in seconds, optional, default is 15 minutes ago)
filterToNoSearch end time (UNIX timestamp in seconds, optional, default is current time)
pageLimitNoMaximum number of spans to retrieve (optional, default is 25)
pageCursorNoCursor to retrieve the next page (optional)

Implementation Reference

  • 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}`);
      }
    };
  • 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
    );
  • 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}`);
      }
    };
  • 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;
    };
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It only states it's a search tool without mentioning whether it's read-only, what permissions are needed, rate limits, pagination behavior (though schema hints at it), or what the output looks like. For a search tool with 5 parameters, this leaves significant behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the core purpose without unnecessary words. It's appropriately sized for a search tool, though it could be more informative by adding context about when to use it versus siblings.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has 5 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain what the search returns, how results are structured, or behavioral aspects like pagination. For a search tool with moderate complexity, this leaves too much unspecified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all 5 parameters with their types, defaults, and descriptions. The description adds no additional parameter information beyond what's in the schema. This meets the baseline of 3 for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the tool searches Datadog trace spans, which is a clear verb+resource combination. However, it doesn't differentiate from sibling tools like 'aggregate_spans' (which likely aggregates rather than searches) or 'search_logs' (which searches logs rather than spans). The purpose is understandable but lacks sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. There's no mention of when to choose search_spans over aggregate_spans or search_logs, nor any context about prerequisites or typical use cases. The user must infer usage from the tool name alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/Nozomuts/datadog-mcp'

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