mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-01-12 17:09:48 -05:00
feat(tools/looker): Query tracking for MCP Toolbox in Looker System Activity views (#1410)
## Description --- Customers have requested that queries to Looker from MCP Toolbox show up in the Looker System Activity under a separate category. To do this we need to create the category `MCP Toolbox` on the Looker side. That should happen by Looker 25.18 release. With 25.18 the request goes to a new undocumented API endpoint. If that endpoint results in an error, the Toolbox will fall back to using the documented API endpoint. --- - [x] Make sure you reviewed [CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md) - [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [x] Ensure the tests and linter pass - [x] Code coverage does not decrease (if any source code was changed) - [x] Appropriate docs were updated (if necessary) - [x] Make sure to add `!` if this involve a breaking change 🛠️ Fixes #1409
This commit is contained in:
@@ -29,6 +29,10 @@ It's compatible with the following sources:
|
||||
7. an optional `limit`
|
||||
8. an optional `tz`
|
||||
|
||||
Starting in Looker v25.18, these queries can be identified in Looker's
|
||||
System Activity. In the History explore, use the field API Client Name
|
||||
to find MCP Toolbox queries.
|
||||
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -29,6 +29,10 @@ It's compatible with the following sources:
|
||||
7. an optional `limit`
|
||||
8. an optional `tz`
|
||||
|
||||
Starting in Looker v25.18, these queries can be identified in Looker's
|
||||
System Activity. In the History explore, use the field API Client Name
|
||||
to find MCP Toolbox queries.
|
||||
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
|
||||
2
go.mod
2
go.mod
@@ -30,7 +30,7 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jackc/pgx/v5 v5.7.6
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/looker-open-source/sdk-codegen/go v0.25.10
|
||||
github.com/looker-open-source/sdk-codegen/go v0.25.11
|
||||
github.com/microsoft/go-mssqldb v1.9.3
|
||||
github.com/nakagami/firebirdsql v0.9.15
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.28.3
|
||||
|
||||
5
go.sum
5
go.sum
@@ -1121,8 +1121,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/looker-open-source/sdk-codegen/go v0.25.10 h1:ltBbwkwZrQEHEIKrE5QbF+EtBlweKN0RZpQR0w2GIqo=
|
||||
github.com/looker-open-source/sdk-codegen/go v0.25.10/go.mod h1:YM/IYSsTPk7I54j4l6PduNJYgXyOShuaMi7mD6xic8E=
|
||||
github.com/looker-open-source/sdk-codegen/go v0.25.11 h1:IPxG3eTqz8ICd1ImqffEKQVd8G9/IAbjH7dg4IhiQtU=
|
||||
github.com/looker-open-source/sdk-codegen/go v0.25.11/go.mod h1:Br1ntSiruDJ/4nYNjpYyWyCbqJ7+GQceWbIgn0hYims=
|
||||
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
|
||||
@@ -2039,7 +2039,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -262,3 +262,25 @@ func ProcessQueryArgs(ctx context.Context, params tools.ParamValues) (*v4.WriteQ
|
||||
}
|
||||
return &wq, nil
|
||||
}
|
||||
|
||||
type QueryApiClientContext struct {
|
||||
Name string `json:"name"`
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
ExtraAttributes map[string]string `json:"extra_attributes,omitempty"`
|
||||
}
|
||||
|
||||
type RenderOptions struct {
|
||||
Format string `json:"format"`
|
||||
}
|
||||
|
||||
type RequestRunInlineQuery2 struct {
|
||||
Query v4.WriteQuery `json:"query"`
|
||||
RenderOpts RenderOptions `json:"render_options"`
|
||||
QueryApiClientCtx QueryApiClientContext `json:"query_api_client_context"`
|
||||
}
|
||||
|
||||
func RunInlineQuery2(l *v4.LookerSDK, request RequestRunInlineQuery2, options *rtl.ApiSettings) (string, error) {
|
||||
var result string
|
||||
err := l.AuthSession.Do(&result, "POST", "/4.0", "/queries/run_inline", nil, request, options)
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package lookercommon_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -169,3 +170,32 @@ func TestExtractLookerFieldPropertiesWithNilFields(t *testing.T) {
|
||||
t.Fatalf("incorrect result: diff %v", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestRunInlineQuery2(t *testing.T) {
|
||||
fields := make([]string, 1)
|
||||
fields[0] = "foo.bar"
|
||||
wq := v4.WriteQuery{
|
||||
Model: "model",
|
||||
View: "explore",
|
||||
Fields: &fields,
|
||||
}
|
||||
req2 := lookercommon.RequestRunInlineQuery2{
|
||||
Query: wq,
|
||||
RenderOpts: lookercommon.RenderOptions{
|
||||
Format: "json",
|
||||
},
|
||||
QueryApiClientCtx: lookercommon.QueryApiClientContext{
|
||||
Name: "MCP Toolbox",
|
||||
},
|
||||
}
|
||||
json, err := json.Marshal(req2)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not marshall req2 as json")
|
||||
}
|
||||
got := string(json)
|
||||
want := `{"query":{"model":"model","view":"explore","fields":["foo.bar"]},"render_options":{"format":"json"},"query_api_client_context":{"name":"MCP Toolbox"}}`
|
||||
if diff := cmp.Diff(want, got); diff != "" {
|
||||
t.Fatalf("incorrect result: diff %v", diff)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -131,9 +131,22 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
|
||||
Body: *wq,
|
||||
ResultFormat: "json",
|
||||
}
|
||||
resp, err := sdk.RunInlineQuery(req, t.ApiSettings)
|
||||
req2 := lookercommon.RequestRunInlineQuery2{
|
||||
Query: *wq,
|
||||
RenderOpts: lookercommon.RenderOptions{
|
||||
Format: "json",
|
||||
},
|
||||
QueryApiClientCtx: lookercommon.QueryApiClientContext{
|
||||
Name: "MCP Toolbox",
|
||||
},
|
||||
}
|
||||
resp, err := lookercommon.RunInlineQuery2(sdk, req2, t.ApiSettings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making query request: %s", err)
|
||||
logger.DebugContext(ctx, "error querying with new endpoint, trying again with original", err)
|
||||
resp, err = sdk.RunInlineQuery(req, t.ApiSettings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making query request: %s", err)
|
||||
}
|
||||
}
|
||||
logger.DebugContext(ctx, "resp = ", resp)
|
||||
|
||||
|
||||
@@ -130,9 +130,22 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
|
||||
Body: *wq,
|
||||
ResultFormat: "sql",
|
||||
}
|
||||
resp, err := sdk.RunInlineQuery(req, t.ApiSettings)
|
||||
req2 := lookercommon.RequestRunInlineQuery2{
|
||||
Query: *wq,
|
||||
RenderOpts: lookercommon.RenderOptions{
|
||||
Format: "sql",
|
||||
},
|
||||
QueryApiClientCtx: lookercommon.QueryApiClientContext{
|
||||
Name: "MCP Toolbox",
|
||||
},
|
||||
}
|
||||
resp, err := lookercommon.RunInlineQuery2(sdk, req2, t.ApiSettings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making query_sql request: %s", err)
|
||||
logger.DebugContext(ctx, "error querying with new endpoint, trying again with original", err)
|
||||
resp, err = sdk.RunInlineQuery(req, t.ApiSettings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error making query_sql request: %s", err)
|
||||
}
|
||||
}
|
||||
logger.DebugContext(ctx, "resp = ", resp)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user