Skip to content

Conversation

@rahulvijil
Copy link

@rahulvijil rahulvijil commented Nov 4, 2025

🧩 Summary

This PR adds support for optional matchers in the list_loki_label_values() function to allow filtered label value queries in Loki.

Previously, the MCP API did not accept matchers, causing the function to always return unfiltered results (all label values across namespaces and apps).
With this change, users can now pass Loki stream selectors (e.g., {app="...", namespace="..."}) to filter results as needed.


🔍 Why this change was needed

My agent was calling the Grafana MCP API as follows:

list_loki_label_values({
  "datasourceUid": "<datasource>",
  "labelName": "<label-name>",
  "startRfc3339": "<start-timestamp format., YYYY-MM-DDTHH:MM:SS.ssssssZ>",
  "endRfc3339": "<end-timestamp format., YYYY-MM-DDTHH:MM:SS.ssssssZ>",
  "matchers": "{app=\"<app>\", namespace=\"<namespace>\"}"
})

Since the backend did not support the matchers field, the query ignored these filters and returned all values.
This PR fixes that gap by forwarding the matchers parameter as a query argument to Loki’s /loki/api/v1/label/{name}/values endpoint.

Key Changes

  • Updated ListLokiLabelValuesParams to include an optional Matchers string field.
  • Modified listLokiLabelValues() to pass args.Matchers into fetchData().
  • Updated fetchData() to handle and append matchers (as query param) when provided.
  • Added debug prints for final URL and Loki response (for verification).

Notes

  • Tested against Loki v3.5.2 (/loki/api/v1/status/buildinfo confirmed).
  • No breaking changes introduced — matchers remains optional.

@rahulvijil rahulvijil requested a review from a team as a code owner November 4, 2025 06:46
@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

tools/loki.go Outdated
LabelName string `json:"labelName" jsonschema:"required,description=The name of the label to retrieve values for (e.g. 'app'\\, 'env'\\, 'pod')"`
StartRFC3339 string `json:"startRfc3339,omitempty" jsonschema:"description=Optionally\\, the start time of the query in RFC3339 format (defaults to 1 hour ago)"`
EndRFC3339 string `json:"endRfc3339,omitempty" jsonschema:"description=Optionally\\, the end time of the query in RFC3339 format (defaults to now)"`
Matchers string `json:"matchers"`

Choose a reason for hiding this comment

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

Hi @rahulvijil,

thanks for your contribution. Would you mind adding two things to this matchers prop:

  1. omitempty
  2. A description that would help the LLM understand what this parameter is used for.

Copy link
Author

Choose a reason for hiding this comment

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

Added the description

Comment on lines 266 to 283
return nil, fmt.Errorf("creating Loki client: %w", err)
}

// Use the client's fetchData method
urlPath := fmt.Sprintf("/loki/api/v1/label/%s/values", args.LabelName)

// Manually include matchers in query string if provided
if args.Matchers != "" {
encoded := url.QueryEscape(args.Matchers)
urlPath = fmt.Sprintf("%s?query=%s", urlPath, encoded)
}

// Call the original fetchData with start/end as before
result, err := client.fetchData(ctx, urlPath, args.StartRFC3339, args.EndRFC3339)
if err != nil {
return nil, err
return nil, fmt.Errorf("fetching label values: %w", err)
}

if len(result) == 0 {

Choose a reason for hiding this comment

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

Are these changes needed?

Copy link
Author

Choose a reason for hiding this comment

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

Not actually, I will revert fmt.Errorf("fetching label values: %w", err) to err

tools/loki.go Outdated
Comment on lines 143 to 159
func (c *Client) fetchData(ctx context.Context, urlPath string, startRFC3339, endRFC3339 string, optionals ...string) ([]string, error) {
params := url.Values{}
var optional string
if len(optionals) > 0 {
optional = optional[0]
}
if startRFC3339 != "" {
params.Add("start", startRFC3339)
}
if endRFC3339 != "" {
params.Add("end", endRFC3339)
}
if optional != "" {
params.Add("query", optional)
}
fullURL := fmt.Sprintf("%s?%s", urlPath, params.Encode())
fmt.Println("Final request URL:", fullURL)

Choose a reason for hiding this comment

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

Hm, I think instead adding this here as optional, we might be better creating a new fetchLabels method.

Copy link
Collaborator

@sd2k sd2k left a comment

Choose a reason for hiding this comment

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

Noticed a couple of unnecessary printlns, otherwise this looks good.

}
func (c *Client) handleAPICall(ctx context.Context, params url.Values, urlPath string) ([]string, error) {
fullURL := fmt.Sprintf("%s?%s", urlPath, params.Encode())
fmt.Println("Final request URL:", fullURL)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you remove this Println?

}

pretty, _ := json.MarshalIndent(labelResponse, "", " ")
fmt.Println("Loki Full Response:\n", string(pretty))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This Println also needs removing

Copy link
Collaborator

@sd2k sd2k left a comment

Choose a reason for hiding this comment

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

Actually would you mind adding some integration tests please? You can follow the existing patterns in 'loki_test.go'.

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.

4 participants