handleSearch handles the traceql-search tool
(ctx context.Context, request mcp.CallToolRequest)
| 56 | |
| 57 | // handleSearch handles the traceql-search tool |
| 58 | func (s *MCPServer) handleSearch(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { |
| 59 | metricMCPToolCalls.WithLabelValues(toolTraceQLSearch).Inc() |
| 60 | |
| 61 | query, err := request.RequireString("query") |
| 62 | if err != nil { |
| 63 | return mcp.NewToolResultError(err.Error()), nil |
| 64 | } |
| 65 | |
| 66 | var startEpoch, endEpoch int64 |
| 67 | |
| 68 | start := request.GetString("start", "") |
| 69 | end := request.GetString("end", "") |
| 70 | |
| 71 | level.Info(s.logger).Log("msg", "searching traces", "query", query, "start", start, "end", end) |
| 72 | |
| 73 | if start == "" { |
| 74 | startEpoch = time.Now().Add(-1 * time.Hour).Unix() |
| 75 | } else { |
| 76 | startTS, err := time.Parse(time.RFC3339, start) |
| 77 | if err != nil { |
| 78 | return mcp.NewToolResultError(fmt.Sprintf("invalid start time: %v", err)), nil |
| 79 | } |
| 80 | startEpoch = startTS.Unix() |
| 81 | } |
| 82 | if end == "" { |
| 83 | endEpoch = time.Now().Unix() |
| 84 | } else { |
| 85 | endTS, err := time.Parse(time.RFC3339, end) |
| 86 | if err != nil { |
| 87 | return mcp.NewToolResultError(fmt.Sprintf("invalid end time: %v", err)), nil |
| 88 | } |
| 89 | endEpoch = endTS.Unix() |
| 90 | } |
| 91 | |
| 92 | parsed, err := traceql.ParseNoOptimizations(query) |
| 93 | if err != nil { |
| 94 | return mcp.NewToolResultError(fmt.Sprintf("query parse error. Consult TraceQL docs tools: %v", err)), nil |
| 95 | } |
| 96 | |
| 97 | if parsed.MetricsPipeline != nil || parsed.MetricsSecondStage != nil { |
| 98 | return mcp.NewToolResultError("TraceQL metrics query received on traceql-search tool. Use the traceql-metrics-instant or traceql-metrics-range tool instead"), nil |
| 99 | } |
| 100 | |
| 101 | searchReq := &tempopb.SearchRequest{ |
| 102 | Query: query, |
| 103 | Start: uint32(startEpoch), |
| 104 | End: uint32(endEpoch), |
| 105 | } |
| 106 | |
| 107 | req, err := api.BuildSearchRequest(nil, searchReq) |
| 108 | if err != nil { |
| 109 | return mcp.NewToolResultError(fmt.Sprintf("failed to build search request: %v", err)), nil |
| 110 | } |
| 111 | req.URL.Path = s.buildPath(api.PathSearch) |
| 112 | |
| 113 | body, err := handleHTTP(ctx, s.frontend.SearchHandler, req) |
| 114 | if err != nil { |
| 115 | return mcp.NewToolResultError(err.Error()), nil |