Skip to content

Add per-query datasource selection to Prometheus tools#129

Merged
prathamesh-sonpatki merged 7 commits intomainfrom
feat/prometheus-dynamic-datasource
Apr 17, 2026
Merged

Add per-query datasource selection to Prometheus tools#129
prathamesh-sonpatki merged 7 commits intomainfrom
feat/prometheus-dynamic-datasource

Conversation

@prathamesh-sonpatki
Copy link
Copy Markdown
Member

@prathamesh-sonpatki prathamesh-sonpatki commented Apr 16, 2026

Summary

  • Adds optional datasource parameter to all 4 Prometheus tools (prometheus_range_query, prometheus_instant_query, prometheus_label_values, prometheus_labels) — when set, credentials for the named datasource are resolved from the startup-cached list instead of the server-wide default
  • Adds list_datasources tool so agents can discover available datasource names (and which is the default) before querying
  • Removes dead GetDatasourceByName utility that made a live API call on every invocation; all per-query resolution now uses the in-memory cache

How it works

All datasources are already fetched from the API at startup in PopulateAPICfg. We now cache them in cfg.Datasources []DatasourceInfo. At query time, resolveDatasourceCfg copies cfg by value and overwrites only the three Prometheus credential fields — safe across concurrent requests with no locking.

The list_datasources response is serialized once at handler registration since the list never changes after startup.

Test plan

  • list_datasources returns all datasources with correct is_default flag
  • Prometheus tools use default datasource when datasource param is omitted
  • Prometheus tools use overridden credentials when datasource param is set to a valid name
  • Prometheus tools return a clear error when datasource param is set to an unknown name

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Per-query Prometheus datasource selection for PromQL (range, instant, label values, labels).
    • New tool to list configured datasources and their default status.
  • Improvements

    • Datasource metadata cached at startup for faster, offline resolution.
  • Bug Fixes

    • Handlers now validate datasource names and fail early on invalid or incomplete selections.
  • Tests

    • Added tests for datasource resolution, query overrides, and the listing tool.
  • Documentation

    • PromQL tool help updated to document the optional datasource parameter.

Adds an optional `datasource` parameter to prometheus_range_query,
prometheus_instant_query, prometheus_label_values, and prometheus_labels.
When supplied, credentials for that datasource are resolved from the
startup-cached list instead of the server-wide default, enabling an LLM
agent to query multiple datasources (e.g. prod vs staging) in a single
conversation without restarting the server.

Also adds a `list_datasources` tool that returns all available datasource
names and which one is the current default, so agents can discover valid
values for the new parameter. The response is serialized once at
registration time since the list is immutable after startup.

Removes the now-dead GetDatasourceByName util that issued a live API call
on every invocation; per-query resolution now goes through the in-memory
cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

@coderabbitai do a full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

@prathamesh-sonpatki: Sure! I'll redo the entire review of the PR now.

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

Adds per-request datasource selection for PromQL tools via an optional datasource field, caches configured datasources in the process Config, resolves datasource-specific connection info at handler runtime, and adds a new list_datasources MCP tool to enumerate configured datasources.

Changes

Cohort / File(s) Summary
Configuration & Data Models
internal/models/config.go
Added exported DatasourceInfo type; added Datasources []DatasourceInfo to Config; added ResolveDatasource(name string) (DatasourceInfo, bool) method.
APM Query Handlers
internal/apm/apm.go
Added Datasource string to PromQL arg structs; added resolveDatasourceCfg(cfg models.Config, datasourceName string); handlers now resolve queryCfg and pass it into PromQL query builders; added ListDatasourcesArgs and NewListDatasourcesHandler.
Configuration Population
internal/utils/utils.go
Removed GetDatasourceByName; PopulateAPICfg(cfg *models.Config) now populates cfg.Datasources from the fetched datasource list during startup.
Tool Registration
tools.go
Registered new MCP tool list_datasources using apm.NewListDatasourcesHandler(cfg).
Tests
internal/apm/apm_test.go
Added tests for resolveDatasourceCfg, PromQL handler datasource override behavior with httptest server, and NewListDatasourcesHandler output and default flags.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Handler as PromQL Handler
    participant Resolver as resolveDatasourceCfg
    participant Config as Config.ResolveDatasource
    participant Prom as Prometheus API

    Client->>Handler: Send PromQL request (optional datasource)
    Handler->>Resolver: resolveDatasourceCfg(cfg, datasourceName)
    Resolver->>Config: ResolveDatasource(datasourceName)
    Config-->>Resolver: DatasourceInfo / not found
    Resolver->>Resolver: Build queryCfg (override read URL, creds, region, cluster)
    alt datasource resolved
        Resolver-->>Handler: queryCfg
        Handler->>Prom: Query Prometheus using queryCfg
        Prom-->>Handler: Results
        Handler-->>Client: Return results
    else resolution error
        Resolver-->>Handler: error
        Handler-->>Client: Return error
    end
Loading

Suggested reviewers

  • adityagodbole
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main change: adding per-query datasource selection to Prometheus tools, which is the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/prometheus-dynamic-datasource

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 16, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds per-query Prometheus datasource selection by caching all datasources at startup, letting each Prometheus tool optionally override credentials by datasource name, and exposing a new discovery tool to list available datasources.

Changes:

  • Added a list_datasources MCP tool for discovering datasource names and the default datasource.
  • Added optional datasource argument support across all Prometheus tools and resolved credentials from the startup cache.
  • Cached datasource credentials in models.Config during PopulateAPICfg and removed the old per-call live lookup utility.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
tools.go Registers the new list_datasources tool alongside existing tools.
internal/utils/utils.go Caches all datasource credential info at startup in cfg.Datasources.
internal/models/config.go Adds DatasourceInfo, Config.Datasources, and a ResolveDatasource lookup helper.
internal/apm/apm.go Adds datasource args to Prometheus tools, resolves per-query credentials, and implements list_datasources handler.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/apm/apm.go
Comment thread internal/apm/apm.go
Comment thread internal/apm/apm.go Outdated
Comment thread internal/apm/apm.go Outdated
"enouraged" -> "encouraged" in prometheus_label_values and
prometheus_labels tool descriptions (flagged by Copilot review).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the two gaps flagged by Copilot review:

- TestResolveDatasourceCfg: verifies empty name returns default cfg,
  known name overrides credentials, unknown name returns a clear error,
  and original cfg is never mutated.
- TestPromqlRangeHandler_DatasourceOverride: end-to-end check that the
  overridden read_url/username/password reach the outgoing HTTP request,
  and that an unknown datasource errors before hitting the server.
- TestNewListDatasourcesHandler: asserts correct JSON output with
  name + is_default fields, including the empty-list case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 16, 2026
When overriding credentials for a named datasource, also copy Region and
ClusterID so the resulting cfg is a self-consistent view of that datasource.
Without this, handlers using queryCfg.Region (e.g. traces) or ClusterID
(deep link builder) would silently use the startup-default datasource's
values even when a different datasource was requested.

Also removes a stale test comment that incorrectly attributed the strings
import to TestNewServiceSummaryHandler_ExtraParams; it is used by the
datasource tests added in the previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/apm/apm_test.go`:
- Around line 536-542: The test using the httptest.Server with capturedReq
doesn't verify the handler was never invoked; modify the server handler to
atomically increment a request counter or set an atomic/chan flag (e.g., using a
uint32 counter via sync/atomic or a channel) when it receives any request, then
after calling the code under test assert the counter/flag is zero (or no value
received) to prove the error occurred before hitting the server; apply the same
change to the second occurrence (the block using capturedReq around lines
589-596) so both tests assert the server was not contacted.

In `@internal/apm/apm.go`:
- Around line 1810-1823: resolveDatasourceCfg currently trusts that a listed
datasource has usable connection info; validate the resolved datasource before
overwriting query config by returning an error if required fields are missing.
In resolveDatasourceCfg, after obtaining ds from cfg.ResolveDatasource, ensure
ds.ReadURL is non-empty (return fmt.Errorf("datasource %q has empty read_url",
datasourceName) if it is) and validate credentials consistently (e.g., if
ds.Username != "" require ds.Password != "" and error otherwise). Only assign
PrometheusReadURL, PrometheusUsername, PrometheusPassword, Region, and ClusterID
to cfg after these checks pass.

In `@internal/utils/utils.go`:
- Around line 470-483: The cached datasource entries in cfg.Datasources
currently use ds.IsDefault from the API, which becomes incorrect when startup
overrides the default via cfg.DatasourceName; update the loop that builds
cfg.Datasources (iterating datasourcesList and creating models.DatasourceInfo)
to compute IsDefault by comparing each datasource's Name to cfg.DatasourceName
(set true when names match, false otherwise) so the cached IsDefault reflects
the effective runtime default rather than the API-provided ds.IsDefault.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 71f33604-0d1e-41c0-addb-c7aaa3d8633f

📥 Commits

Reviewing files that changed from the base of the PR and between a9a9ae6 and f72adc8.

📒 Files selected for processing (5)
  • internal/apm/apm.go
  • internal/apm/apm_test.go
  • internal/models/config.go
  • internal/utils/utils.go
  • tools.go

Comment thread internal/apm/apm_test.go Outdated
Comment thread internal/apm/apm.go
Comment thread internal/utils/utils.go
prathamesh-sonpatki and others added 2 commits April 16, 2026 13:25
… test coverage

- resolveDatasourceCfg: return error when matched datasource lacks ReadURL,
  Username, Password, or Region (incomplete records should fail fast)
- IsDefault in Datasources cache: derive from which datasource was actually
  selected at startup (selectedDatasource.Name == ds.Name) rather than the
  raw API flag, which doesn't account for DatasourceName config override
- TestPromqlRangeHandler_DatasourceOverride: add atomic hitCount to assert the
  unknown-datasource error fires before any outbound HTTP request; expand
  staging fixture with Region to satisfy new validation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rizwan2000rm
Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/apm/apm_test.go`:
- Around line 469-528: Add a new subtest inside TestResolveDatasourceCfg that
constructs a models.DatasourceInfo entry with empty Region but valid
ReadURL/Username/Password/ClusterID (e.g., name "noregion"), call
resolveDatasourceCfg(cfg, "noregion"), assert no error and that
PrometheusReadURL, PrometheusUsername, PrometheusPassword and ClusterID are
overridden to the datasource values while Region may remain empty, and also
verify the original cfg is not mutated; this ensures resolveDatasourceCfg
accepts datasources with empty Region rather than over-validating.

In `@internal/apm/apm.go`:
- Around line 1818-1825: The current validation block in internal/apm/apm.go
incorrectly requires ds.Region (and effectively ClusterID) for Prometheus
datasource overrides; change the check that currently tests ds.ReadURL,
ds.Username, ds.Password, and ds.Region to only validate ds.ReadURL,
ds.Username, and ds.Password so Prometheus-only handlers aren't rejected when
Region is empty. Keep assigning cfg.PrometheusReadURL, cfg.PrometheusUsername,
cfg.PrometheusPassword, and continue to set cfg.Region and cfg.ClusterID if
present, but do not make their presence mandatory; also update the returned
error message (the fmt.Errorf call referencing datasourceName) to reflect only
the required Prometheus configuration fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 50982d58-c0e5-4fbc-b250-b2968002d565

📥 Commits

Reviewing files that changed from the base of the PR and between f72adc8 and 5e9342c.

📒 Files selected for processing (3)
  • internal/apm/apm.go
  • internal/apm/apm_test.go
  • internal/utils/utils.go

Comment thread internal/apm/apm_test.go
Comment thread internal/apm/apm.go
@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

Datasource selection evals14/15 passed (7s)

Case Operation Result
ds-001 type_coercion
ds-002 type_coercion
ds-003 type_coercion ✗ must_contain
ds-004 type_coercion
ds-005 type_coercion
ds-006 type_coercion
ds-007 type_coercion
ds-008 type_coercion
ds-009 type_coercion
ds-010 type_coercion
ds-011 type_coercion
ds-012 datasource_selection
ds-013 datasource_selection
ds-014 datasource_selection
ds-015 datasource_selection

Evaluated against SHA 5e9342c01682a5979fc1b927b2636288ec026191.

@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

Datasource selection evals15/15 passed (8s)

Case Operation Result
ds-001 type_coercion
ds-002 type_coercion
ds-003 type_coercion
ds-004 type_coercion
ds-005 type_coercion
ds-006 type_coercion
ds-007 type_coercion
ds-008 type_coercion
ds-009 type_coercion
ds-010 type_coercion
ds-011 type_coercion
ds-012 datasource_selection
ds-013 datasource_selection
ds-014 datasource_selection
ds-015 datasource_selection

Evaluated against SHA 5e9342c01682a5979fc1b927b2636288ec026191.

@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

Datasource selection evals15/15 passed (10s)

Case Operation Result
ds-001 type_coercion
ds-002 type_coercion
ds-003 type_coercion
ds-004 type_coercion
ds-005 type_coercion
ds-006 type_coercion
ds-007 type_coercion
ds-008 type_coercion
ds-009 type_coercion
ds-010 type_coercion
ds-011 type_coercion
ds-012 datasource_selection
ds-013 datasource_selection
ds-014 datasource_selection
ds-015 datasource_selection

Evaluated against SHA 5e9342c01682a5979fc1b927b2636288ec026191.

1 similar comment
@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

Datasource selection evals15/15 passed (10s)

Case Operation Result
ds-001 type_coercion
ds-002 type_coercion
ds-003 type_coercion
ds-004 type_coercion
ds-005 type_coercion
ds-006 type_coercion
ds-007 type_coercion
ds-008 type_coercion
ds-009 type_coercion
ds-010 type_coercion
ds-011 type_coercion
ds-012 datasource_selection
ds-013 datasource_selection
ds-014 datasource_selection
ds-015 datasource_selection

Evaluated against SHA 5e9342c01682a5979fc1b927b2636288ec026191.

@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

Datasource selection evals14/15 passed (11s)

Case Operation Result
ds-001 type_coercion
ds-002 type_coercion
ds-003 type_coercion ✗ must_contain
ds-004 type_coercion
ds-005 type_coercion
ds-006 type_coercion
ds-007 type_coercion
ds-008 type_coercion
ds-009 type_coercion
ds-010 type_coercion
ds-011 type_coercion
ds-012 datasource_selection
ds-013 datasource_selection
ds-014 datasource_selection
ds-015 datasource_selection

Evaluated against SHA 5e9342c01682a5979fc1b927b2636288ec026191.

@prathamesh-sonpatki
Copy link
Copy Markdown
Member Author

Datasource selection evals15/15 passed (12s)

Case Operation Result
ds-001 type_coercion
ds-002 type_coercion
ds-003 type_coercion
ds-004 type_coercion
ds-005 type_coercion
ds-006 type_coercion
ds-007 type_coercion
ds-008 type_coercion
ds-009 type_coercion
ds-010 type_coercion
ds-011 type_coercion
ds-012 datasource_selection
ds-013 datasource_selection
ds-014 datasource_selection
ds-015 datasource_selection

Evaluated against SHA 5e9342c01682a5979fc1b927b2636288ec026191.

@prathamesh-sonpatki prathamesh-sonpatki merged commit d1b5de4 into main Apr 17, 2026
7 checks passed
@prathamesh-sonpatki prathamesh-sonpatki deleted the feat/prometheus-dynamic-datasource branch April 17, 2026 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants