feat(tool/looker): add get_field_value_suggestions tool (#3696)

## Description

This PR implements a new Looker MCP tool `get_field_value_suggestions`
to expose the Looker suggestions API (`GET
/models/{model}/views/{explore}/fields/{field}/suggestions`).

### Tool calls and frequency for a sample prompt

**Prompt**: *"Get the query count from system activity history for
completed runs where the source is 'scheduled'"*

**Before (without suggestions tool)**:
* `mcp_mylocalLookerWithOauth_get_models`: 1 time
* `mcp_mylocalLookerWithOauth_get_explores`: 1 time
* `mcp_mylocalLookerWithOauth_get_dimensions`: 1 time
* `mcp_mylocalLookerWithOauth_get_measures`: 1 time
* `mcp_mylocalLookerWithOauth_query`: 5 times (explored source values,
checked status strings, corrected bad filters, executed query, and
verified output)

**After (with suggestions tool)**:
* `mcp_looker-local_get_models`: 1 time
* `mcp_looker-local_get_explores`: 1 time
* `mcp_looker-local_get_dimensions`: 1 time
* `mcp_looker-local_get_measures`: 1 time
* `mcp_looker-local_get_field_value_suggestions`: 2 times (retrieved
"scheduled_task" and "complete" values)
* `mcp_looker-local_query`: 1 time (executed final query with correct
filters directly)

### Summary of the Solution
- Created the `get_field_value_suggestions` tool wrapping the Looker
SDK.
- Exposed the `suggestable` boolean property in the `get_dimensions`
tool metadata output, allowing LLM planning engines to identify which
dimensions support suggestions lookup.
- Wrapped the returned suggestions list in a JSON object
(`{"suggestions": [...]}`) instead of a raw JSON array to satisfy strict
client schema validation requirements and prevent `structuredContent`
parsing errors.
- Tuned parameter descriptions and prompt suggestions inside the `query`
tool and documentation to guide the LLM planning engine to use this tool
when unsure of valid values.
- Added comprehensive integration tests, unit tests, and validation test
cases.

---

🛠️ Fixes  #3695  🦕

---------

Co-authored-by: Mike DeAngelo <drstrangelove@google.com>
This commit is contained in:
Muskan Jain
2026-07-25 01:13:33 +05:30
committed by GitHub
parent d0a8f14cbe
commit 3debe8113f
10 changed files with 477 additions and 9 deletions
+1 -1
View File
@@ -2024,7 +2024,7 @@ func TestPrebuiltTools(t *testing.T) {
wantGroups: server.GroupConfigs{
"looker_tools": group.GroupConfig{
Name: "looker_tools",
ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "add_dashboard_filter", "generate_embed_url"},
ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "get_field_value_suggestions", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "add_dashboard_filter", "generate_embed_url"},
},
},
},
+1
View File
@@ -227,6 +227,7 @@ import (
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetdashboards"
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetdimensions"
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetexplores"
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetfieldvaluesuggestions"
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetfilters"
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetgitbranch"
_ "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetlookmltests"
@@ -26,6 +26,7 @@ description: "Details of the Looker prebuilt configuration."
* `get_measures`: Retrieves the list of measures in an explore.
* `get_filters`: Retrieves the list of filters in an explore.
* `get_parameters`: Retrieves the list of parameters in an explore.
* `get_field_value_suggestions`: Retrieves distinct value suggestions for a field.
* `query`: Runs a query against the LookML model.
* `query_sql`: Generates the SQL for a query.
* `query_url`: Generates a URL for a query in Looker.
@@ -0,0 +1,69 @@
---
title: "looker-get-field-value-suggestions"
type: docs
weight: 1
description: >
A "looker-get-field-value-suggestions" tool retrieves distinct value suggestions
for a given field in an explore.
---
## About
The `looker-get-field-value-suggestions` tool retrieves distinct value suggestions for a given field in an explore.
## Compatible Sources
{{< compatible-sources >}}
## Parameters
| **field** | **type** | **required** | **description** |
| --------- | :------: | :----------: | --------------------------------------------------- |
| model | string | true | The name of the LookML model. |
| explore | string | true | The name of the explore containing the field. |
| field | string | true | The name of the field to get suggestions for. |
| term | string | false | Optional search term pattern to filter suggestions. |
| filters | object | false | Optional filters to enable conditional suggestions. |
## Example
```yaml
kind: tool
name: get_field_value_suggestions
type: looker-get-field-value-suggestions
source: looker-source
description: |
This tool retrieves distinct value suggestions for a field, facilitating accurate filtering in downstream queries.
Required Parameters:
- model: The name of the LookML model, obtained from `get_models`.
- explore: The name of the explore containing the field, obtained from `get_explores`.
- field: The name of the field to get suggestions for, obtained from `get_dimensions`.
Optional Parameters:
- term: Optional search term pattern to filter suggestions. Evaluated as `%term%`.
- filters: Optional filters to enable conditional suggestions (restricting suggestions based on other field values), represented as a map of field names to filter expressions, e.g., `{"users.state": "CA", "users.age": ">=60"}`.
Output:
- A JSON object with a "suggestions" key containing an array of string values representing suggestions.
```
## Output Format
The output is a JSON object with a "suggestions" key containing an array of string values.
Example:
```json
{
"suggestions": ["CA", "NY", "TX", "WA"]
}
```
## Reference
| **field** | **type** | **required** | **description** |
| ----------- | :------: | :----------: | -------------------------------------------------- |
| type | string | true | Must be "looker-get-field-value-suggestions". |
| source | string | true | Name of the source Looker instance. |
| description | string | true | Description of the tool that is passed to the LLM. |
@@ -69,6 +69,7 @@ description: |
that can be used directly as filters for that dimension.
- If a `suggest_explore` and `suggest_dimension` are provided, you can query
that specified explore and dimension to retrieve a list of valid filter values.
- If a dimension includes `"suggestable": true`, you can retrieve its valid value suggestions by calling the 'get_field_value_suggestions' tool, passing the dimension's 'name' as the 'field' parameter, alongside the explore and model names.
---
kind: tool
name: get_measures
@@ -123,6 +124,25 @@ description: |
- explore_name (required): The name of the explore within the model, obtained from `get_explores`.
---
kind: tool
name: get_field_value_suggestions
type: looker-get-field-value-suggestions
source: looker-source
description: |
This tool retrieves distinct value suggestions for a field, facilitating accurate filtering in downstream queries.
Required Parameters:
- model: The name of the LookML model, obtained from `get_models`.
- explore: The name of the explore containing the field, obtained from `get_explores`.
- field: The name of the field to get suggestions for (obtained from a dimension where 'suggestable' is true in the 'get_dimensions' output).
Optional Parameters:
- term: Optional search term pattern to filter suggestions. Evaluated as `%term%`.
- filters: Optional filters to enable conditional suggestions, represented as a map of field names to filter expressions, e.g., `{"users.state": "CA"}`.
Output:
- A JSON object with a "suggestions" key containing an array of string values representing suggestions.
---
kind: tool
name: query
type: looker-query
source: looker-source
@@ -140,6 +160,7 @@ description: |
- Do not quote field names.
- Use `not null` instead of `-NULL`.
- If a value contains a comma, enclose it in single quotes (e.g., "'New York, NY'").
- To retrieve valid filter values for a suggestible field, use the 'get_field_value_suggestions' tool.
- filter_expression: A Looker expression filter string (custom filter). This allows complex logic and comparing fields.
- Reference fields using `${view.field_name}` syntax.
- Supports logical operators (`AND`, `OR`, `NOT`) and comparison operators.
@@ -840,6 +861,7 @@ tools:
- get_measures
- get_filters
- get_parameters
- get_field_value_suggestions
- query
- query_sql
- query_url
@@ -80,6 +80,9 @@ func ExtractLookerFieldProperties(ctx context.Context, fields *[]v4.LookmlModelE
if v.Synonyms != nil && len(*v.Synonyms) > 0 {
vMap["synonyms"] = *v.Synonyms
}
if v.Suggestable != nil {
vMap["suggestable"] = *v.Suggestable
}
if v.Suggestable != nil && *v.Suggestable {
if v.Suggestions != nil && len(*v.Suggestions) > 0 {
vMap["suggestions"] = *v.Suggestions
@@ -123,7 +126,9 @@ func GetQueryParameters() parameters.Parameters {
"(e.g. \"view.field\") and values are filter expressions or "+
"parameter values. Pass values bare — do not wrap them in extra "+
"quote characters. For LookML `parameter` fields, use the raw "+
"allowed_value (e.g. `first_touch`), not `\"first_touch\"`.",
"allowed_value (e.g. `first_touch`), not `\"first_touch\"`."+
" To retrieve valid filter values for a suggestible field, "+
"use the 'get_field_value_suggestions' tool.",
"",
parameters.WithMapDefault(map[string]any{}),
)
@@ -63,6 +63,7 @@ func TestExtractLookerFieldProperties(t *testing.T) {
"label": "Dimension Label",
"label_short": "Dim Label",
"description": "This is a dimension description",
"suggestable": true,
"suggest_explore": "explore",
"suggest_dimension": "dimension",
"suggestions": []string{"foo", "bar", "baz"},
@@ -0,0 +1,190 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lookergetfieldvaluesuggestions
import (
"context"
"fmt"
"net/http"
"strings"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/mcp-toolbox/internal/tools"
"github.com/googleapis/mcp-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/mcp-toolbox/internal/util"
"github.com/googleapis/mcp-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
)
const resourceType string = "looker-get-field-value-suggestions"
func init() {
if !tools.Register(resourceType, newConfig) {
panic(fmt.Sprintf("tool type %q already registered", resourceType))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{ConfigBase: tools.ConfigBase{Name: name}}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(context.Context, string) (*v4.LookerSDK, error)
}
type Config struct {
tools.ConfigBase `yaml:",inline"`
Type string `yaml:"type" validate:"required"`
Source string `yaml:"source" validate:"required"`
Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
return resourceType
}
func (cfg Config) Initialize(context.Context) (tools.Tool, error) {
if cfg.Description == "" {
return nil, fmt.Errorf("description is required for tool %q", cfg.Name)
}
params := lookercommon.GetFieldParameters()
// Add field value suggestion specific parameters
fieldParam := parameters.NewStringParameter("field", "The name of the field to get suggestions for.")
termParam := parameters.NewStringParameter("term", "Optional search term pattern.", parameters.WithStringRequired(false))
filtersParam := parameters.NewMapParameter("filters", "Optional filters to enable conditional suggestions (restricting suggestions based on other field values).", "", parameters.WithMapDefault(map[string]any{}))
params = append(params, fieldParam, termParam, filtersParam)
return Tool{
BaseTool: tools.NewBaseTool(
cfg,
tools.GetAnnotationsOrDefault(cfg.Annotations, tools.NewReadOnlyAnnotations),
tools.Manifest{Description: cfg.Description, Parameters: params.Manifest(), AuthRequired: cfg.AuthRequired},
params,
),
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
tools.BaseTool[Config]
}
func (t Tool) ToConfig() tools.ToolConfig {
return t.Cfg
}
func (t Tool) Invoke(ctx context.Context, primitiveMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](primitiveMgr, t.Cfg.Source, t.Cfg.Name, t.Cfg.Type)
if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
model, ok := mapParams["model"].(string)
if !ok {
return nil, util.NewAgentError("model is required and must be a string", nil)
}
explore, ok := mapParams["explore"].(string)
if !ok {
return nil, util.NewAgentError("explore is required and must be a string", nil)
}
field, ok := mapParams["field"].(string)
if !ok {
return nil, util.NewAgentError("field is required and must be a string", nil)
}
var termPtr *string
if val, ok := mapParams["term"].(string); ok && val != "" {
termPtr = &val
}
filters, _ := mapParams["filters"].(map[string]any)
sdk, err := source.GetLookerSDK(ctx, string(accessToken))
if err != nil {
return nil, util.NewClientServerError("error getting Looker SDK", http.StatusInternalServerError, err)
}
req := v4.RequestModelFieldnameSuggestions{
ModelName: model,
ViewName: explore, // Map 'explore' back to 'ViewName'
FieldName: field,
Term: termPtr,
}
if len(filters) > 0 {
var f interface{} = filters
req.Filters = &f
}
resp, err := sdk.ModelFieldnameSuggestions(req, source.LookerApiSettings())
if err != nil {
if strings.Contains(err.Error(), "status=401") {
return nil, util.NewClientServerError("unauthorized error", http.StatusUnauthorized, err)
}
return nil, util.ProcessGeneralError(err)
}
if resp.Error != nil && *resp.Error != "" {
return nil, util.NewAgentError(fmt.Sprintf("Looker API error: %s", *resp.Error), nil)
}
logger.DebugContext(ctx, "suggestions = ", resp.Suggestions)
if resp.Suggestions == nil {
return map[string]any{"suggestions": []string{}}, nil
}
return map[string]any{"suggestions": *resp.Suggestions}, nil
}
func (t Tool) RequiresClientAuthorization(primitiveMgr tools.SourceProvider) (bool, error) {
source, err := tools.GetCompatibleSource[compatibleSource](primitiveMgr, t.Cfg.Source, t.Cfg.Name, t.Cfg.Type)
if err != nil {
return false, err
}
return source.UseClientAuthorization(), nil
}
func (t Tool) GetAuthTokenHeaderName(primitiveMgr tools.SourceProvider) (string, error) {
source, err := tools.GetCompatibleSource[compatibleSource](primitiveMgr, t.Cfg.Source, t.Cfg.Name, t.Cfg.Type)
if err != nil {
return "", err
}
return source.GetAuthTokenHeaderName(), nil
}
@@ -0,0 +1,111 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lookergetfieldvaluesuggestions_test
import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/mcp-toolbox/internal/server"
"github.com/googleapis/mcp-toolbox/internal/testutils"
"github.com/googleapis/mcp-toolbox/internal/tools"
lkr "github.com/googleapis/mcp-toolbox/internal/tools/looker/lookergetfieldvaluesuggestions"
)
func TestParseFromYamlLookerGetFieldValueSuggestions(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
tcs := []struct {
desc string
in string
want server.ToolConfigs
}{
{
desc: "basic example",
in: `
kind: tool
name: example_tool
type: looker-get-field-value-suggestions
source: my-instance
description: some description
`,
want: server.ToolConfigs{
"example_tool": lkr.Config{
ConfigBase: tools.ConfigBase{
Name: "example_tool",
Description: "some description",
AuthRequired: []string{},
},
Type: "looker-get-field-value-suggestions",
Source: "my-instance",
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
// Parse contents
_, _, _, got, _, _, err := server.UnmarshalPrimitiveConfig(ctx, testutils.FormatYaml(tc.in))
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Fatalf("incorrect parse: diff %v", diff)
}
})
}
}
func TestFailParseFromYamlLookerGetFieldValueSuggestions(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
tcs := []struct {
desc string
in string
err string
}{
{
desc: "Invalid field",
in: `
kind: tool
name: example_tool
type: looker-get-field-value-suggestions
source: my-instance
method: GOT
description: some description
`,
err: "error unmarshaling tool: unable to parse tool \"example_tool\" as type \"looker-get-field-value-suggestions\": [3:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n> 3 | method: GOT\n ^\n 4 | name: example_tool\n 5 | source: my-instance\n 6 | type: looker-get-field-value-suggestions",
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
// Parse contents
_, _, _, _, _, _, err := server.UnmarshalPrimitiveConfig(ctx, testutils.FormatYaml(tc.in))
if err == nil {
t.Fatalf("expect parsing to fail")
}
errStr := err.Error()
if !strings.Contains(errStr, tc.err) {
t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err)
}
})
}
}
+75 -7
View File
@@ -108,6 +108,11 @@ func TestLooker(t *testing.T) {
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_field_value_suggestions": map[string]any{
"type": "looker-get-field-value-suggestions",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_measures": map[string]any{
"type": "looker-get-measures",
"source": "my-instance",
@@ -410,6 +415,53 @@ func TestLooker(t *testing.T) {
},
},
)
tests.RunToolGetTestByName(t, "get_field_value_suggestions",
map[string]any{
"get_field_value_suggestions": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authServices": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authServices": []any{},
"description": "The explore containing the fields.",
"name": "explore",
"required": true,
"type": "string",
},
map[string]any{
"authServices": []any{},
"description": "The name of the field to get suggestions for.",
"name": "field",
"required": true,
"type": "string",
},
map[string]any{
"authServices": []any{},
"description": "Optional search term pattern.",
"name": "term",
"required": false,
"type": "string",
},
map[string]any{
"additionalProperties": true,
"authServices": []any{},
"default": map[string]any{},
"description": "Optional filters to enable conditional suggestions (restricting suggestions based on other field values).",
"name": "filters",
"required": false,
"type": "object",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_measures",
map[string]any{
"get_measures": map[string]any{
@@ -519,7 +571,7 @@ func TestLooker(t *testing.T) {
map[string]any{
"additionalProperties": true,
"authServices": []any{},
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`.",
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`. To retrieve valid filter values for a suggestible field, use the 'get_field_value_suggestions' tool.",
"name": "filters",
"required": false,
"default": map[string]any{},
@@ -634,7 +686,7 @@ func TestLooker(t *testing.T) {
map[string]any{
"additionalProperties": true,
"authServices": []any{},
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`.",
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`. To retrieve valid filter values for a suggestible field, use the 'get_field_value_suggestions' tool.",
"name": "filters",
"required": false,
"default": map[string]any{},
@@ -749,7 +801,7 @@ func TestLooker(t *testing.T) {
map[string]any{
"additionalProperties": true,
"authServices": []any{},
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`.",
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`. To retrieve valid filter values for a suggestible field, use the 'get_field_value_suggestions' tool.",
"name": "filters",
"required": false,
"default": map[string]any{},
@@ -1024,7 +1076,7 @@ func TestLooker(t *testing.T) {
map[string]any{
"additionalProperties": true,
"authServices": []any{},
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`.",
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`. To retrieve valid filter values for a suggestible field, use the 'get_field_value_suggestions' tool.",
"name": "filters",
"required": false,
"default": map[string]any{},
@@ -1346,7 +1398,7 @@ func TestLooker(t *testing.T) {
map[string]any{
"additionalProperties": true,
"authServices": []any{},
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`.",
"description": "The filters for the query. Keys are fully-qualified field names (e.g. \"view.field\") and values are filter expressions or parameter values. Pass values bare — do not wrap them in extra quote characters. For LookML `parameter` fields, use the raw allowed_value (e.g. `first_touch`), not `\"first_touch\"`. To retrieve valid filter values for a suggestible field, use the 'get_field_value_suggestions' tool.",
"name": "filters",
"required": false,
"default": map[string]any{},
@@ -2031,10 +2083,26 @@ func TestLooker(t *testing.T) {
wantResult = "{\"description\":\"Data about Look and dashboard usage, including frequency of views, favoriting, scheduling, embedding, and access via the API. Also includes details about individual Looks and dashboards.\",\"group_label\":\"System Activity\",\"label\":\"Content Usage\",\"name\":\"content_usage\"}"
tests.RunToolInvokeParametersTest(t, "get_explores", []byte(`{"model": "system__activity"}`), wantResult)
wantResult = "{\"description\":\"Number of times this content has been viewed via the Looker API\",\"label\":\"Content Usage API Count\",\"label_short\":\"API Count\",\"name\":\"content_usage.api_count\",\"type\":\"number\"}"
wantResult = "{\"description\":\"\",\"label\":\" Dashboard Linked Looks ID\",\"label_short\":\" ID\",\"name\":\"_dashboard_linked_looks._id\",\"suggest_dimension\":\"_dashboard_linked_looks._id\",\"suggest_explore\":\"content_usage\",\"suggestable\":true,\"type\":\"string\"}"
tests.RunToolInvokeParametersTest(t, "get_dimensions", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)
wantResult = "{\"description\":\"The total number of views via the Looker API\",\"label\":\"Content Usage API Total\",\"label_short\":\"API Total\",\"name\":\"content_usage.api_total\",\"type\":\"sum\"}"
// Verify the suggestions output structure is wrapped in a JSON Object
wantResult = `{"suggestions":`
tests.RunToolInvokeParametersTest(t, "get_field_value_suggestions", []byte(`{"model": "system__activity", "explore": "history", "field": "history.source"}`), wantResult)
// Verify that the suggestions list contains the expected value
wantResult = "{\"suggestions\":[\"api4\",\"dashboard\",\"explore\",\"merge_query\",\"regenerator\",\"sqlrunner\",\"suggest\"]}"
tests.RunToolInvokeParametersTest(t, "get_field_value_suggestions", []byte(`{"model": "system__activity", "explore": "history", "field": "history.source"}`), wantResult)
// Verify that search term filtering works
wantResult = "{\"suggestions\":[\"api4\"]}"
tests.RunToolInvokeParametersTest(t, "get_field_value_suggestions", []byte(`{"model": "system__activity", "explore": "history", "field": "history.source", "term": "ap"}`), wantResult)
// Verify that conditional filtering based on other fields works
wantResult = "{\"suggestions\":[\"api4\",\"dashboard\",\"explore\",\"merge_query\",\"regenerator\",\"sqlrunner\",\"suggest\"]}"
tests.RunToolInvokeParametersTest(t, "get_field_value_suggestions", []byte(`{"model": "system__activity", "explore": "history", "field": "history.source", "filters": {"history.status": "complete"}}`), wantResult)
wantResult = "{\"description\":\"\",\"label\":\"API Usage\",\"label_short\":\"API Usage\",\"name\":\"turtle::api_usage\",\"suggestable\":false,\"type\":\"turtle_look\"}"
tests.RunToolInvokeParametersTest(t, "get_measures", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)
wantResult = "[]"