| 65 | } |
| 66 | |
| 67 | func (s *Server) handleQueryDatabase(ctx context.Context, req *mcp.CallToolRequest, input QueryInput) (*mcp.CallToolResult, any, error) { |
| 68 | if input.Database == "" { |
| 69 | return nil, nil, errors.New("database is required") |
| 70 | } |
| 71 | if input.Statement == "" { |
| 72 | return nil, nil, errors.New("statement is required") |
| 73 | } |
| 74 | |
| 75 | // Normalize limit. |
| 76 | limit := input.Limit |
| 77 | if limit <= 0 { |
| 78 | limit = defaultQueryLimit |
| 79 | } |
| 80 | if limit > maxQueryLimit { |
| 81 | limit = maxQueryLimit |
| 82 | } |
| 83 | |
| 84 | // Resolve database. |
| 85 | resolveCtx, resolveCancel := context.WithTimeout(ctx, resolveTimeout) |
| 86 | defer resolveCancel() |
| 87 | |
| 88 | resolved, err := s.resolveDatabase(resolveCtx, input.Database, input.Instance, input.Project) |
| 89 | if err != nil { |
| 90 | return formatToolError(err), nil, nil |
| 91 | } |
| 92 | if resolved.ambiguous { |
| 93 | picked, elicitErr := s.elicitDatabaseChoice(ctx, req, resolved) |
| 94 | if elicitErr != nil { |
| 95 | // Elicitation unsupported or user cancelled — fall back to AMBIGUOUS_TARGET. |
| 96 | return formatAmbiguousResult(input.Database, resolved.candidates), nil, nil |
| 97 | } |
| 98 | resolved = picked |
| 99 | } |
| 100 | |
| 101 | // Execute query. |
| 102 | queryCtx, queryCancel := context.WithTimeout(ctx, queryTimeout) |
| 103 | defer queryCancel() |
| 104 | |
| 105 | output, err := s.executeQuery(queryCtx, resolved, input.Statement, limit) |
| 106 | if err != nil { |
| 107 | return formatToolError(err), nil, nil |
| 108 | } |
| 109 | |
| 110 | text := formatQueryOutput(input.Statement, resolved.resourceName, output) |
| 111 | return &mcp.CallToolResult{ |
| 112 | Content: []mcp.Content{&mcp.TextContent{Text: text}}, |
| 113 | }, output, nil |
| 114 | } |
| 115 | |
| 116 | // formatQueryOutput formats a successful query result as text header + JSON body. |
| 117 | func formatQueryOutput(statement, resourceName string, output *QueryOutput) string { |