feat(tools/looker): Authenticate via end user credentials (#1257)

Authenticate to Looker with end user credentials

## PR Checklist
---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:
- [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/langchain-google-alloydb-pg-python/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 #1258

---------

Co-authored-by: Wenxin Du <117315983+duwenxin99@users.noreply.github.com>
This commit is contained in:
Dr. Strangelove
2025-09-03 13:29:25 -04:00
committed by GitHub
parent ade9a2515b
commit 8755e3db34
25 changed files with 529 additions and 217 deletions

View File

@@ -47,7 +47,8 @@ a self-signed ssl certificate for the Looker server. Anything other than "true"
will be interpreted as false.
The client id and client secret are seemingly random character sequences
assigned by the looker server.
assigned by the looker server. If you are using Looker OAuth you don't need
these settings
{{< notice tip >}}
Use environment variable replacement with the format ${ENV_NAME}
@@ -60,10 +61,11 @@ instead of hardcoding your secrets into the configuration file.
| -------------------- | :------: | :----------: | ----------------------------------------------------------------------------------------- |
| kind | string | true | Must be "looker". |
| base_url | string | true | The URL of your Looker server with no trailing /). |
| client_id | string | true | The client id assigned by Looker. |
| client_secret | string | true | The client secret assigned by Looker. |
| verify_ssl | string | true | Whether to check the ssl certificate of the server. |
| client_id | string | false | The client id assigned by Looker. |
| client_secret | string | false | The client secret assigned by Looker. |
| verify_ssl | string | false | Whether to check the ssl certificate of the server. |
| timeout | string | false | Maximum time to wait for query execution (e.g. "30s", "2m"). By default, 120s is applied. |
| use_client_oauth | string | false | Use OAuth tokens instead of client_id and client_secret. (default: false) |
| show_hidden_models | string | false | Show or hide hidden models. (default: true) |
| show_hidden_explores | string | false | Show or hide hidden explores. (default: true) |
| show_hidden_fields | string | false | Show or hide hidden fields. (default: true) |

View File

@@ -0,0 +1,140 @@
---
title: "Gemini-CLI and OAuth"
type: docs
weight: 2
description: >
How to connect to Looker from Gemini-CLI with end-user credentials
---
## Overview
Gemini-CLI can be configured to get an OAuth token from Looker, then send this
token to MCP Toolbox as part of the request. MCP Toolbox can then use this token
to authentincate with Looker. This means that there is no need to get a Looker
Client ID and Client Secret. This also means that MCP Toolbox can be set up as a
shared resource.
This configuration requires Toolbox v0.14.0 or later.
## Step 1: Register the OAuth App in Looker
You first need to register the OAuth application. Refer to the documentation
[here](https://cloud.google.com/looker/docs/api-cors#registering_an_oauth_client_application).
You may need to ask an administrator to do this for you.
1. Go to the API Explorer application, locate "Register OAuth App", and press
the "Run It" button.
1. Set the `client_guid` to "gemini-cli".
1. Set the `redirect_uri` to "http://localhost:7777/oauth/callback".
1. The `display_name` and `description` can be "Gemini-CLI" or anything
meaningful.
1. Set `enabled` to "true".
1. Check the box confirming that you understand this API will change data.
1. Click the "Run" button.
![OAuth Registration](./registration.png)
## Step 2: Install and configure Toolbox
In this section, we will download Toolbox and run the Toolbox server.
1. Download the latest version of Toolbox as a binary:
{{< notice tip >}}
Select the
[correct binary](https://github.com/googleapis/genai-toolbox/releases)
corresponding to your OS and CPU architecture.
{{< /notice >}}
<!-- {x-release-please-start-version} -->
```bash
export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, or windows/amd64
curl -O https://storage.googleapis.com/genai-toolbox/v0.14.0/$OS/toolbox
```
<!-- {x-release-please-end} -->
1. Make the binary executable:
```bash
chmod +x toolbox
```
1. Create a file `looker_env` with the settings for your
Looker instance.
```bash
export LOOKER_BASE_URL=https://looker.example.com
export LOOKER_VERIFY_SSL=true
```
In some instances you may need to append `:19999` to
the LOOKER_BASE_URL.
1. Load the looker_env file into your environment.
```bash
source looker_env
```
1. Run the Toolbox server using the prebuilt Looker tools.
```bash
./toolbox --prebuilt looker
```
The toolbox server will begin listening on localhost port 5000. Leave it
running and continue in another terminal.
Later, when it is time to shut everything down, you can quit the toolbox
server with Ctrl-C in this terminal window.
## Step 3: Configure Gemini-CLI
1. Edit the file `~/.gemini/settings.json`. Add the following, substituting your
Looker server host name for `looker.example.com`.
```json
"mcpServers": {
"looker": {
"httpUrl": "http://localhost:5000/mcp",
"oauth": {
"enabled": true,
"clientId": "gemini-cli",
"authorizationUrl": "https://looker.example.com/auth",
"tokenUrl": "https://looker.example.com/api/token",
"scopes": ["cors_api"]
}
}
}
```
The `authorizationUrl` should point to the URL you use to access Looker via the
web UI. The `tokenUrl` should point to the URL you use to access Looker via
the API. In some cases you will need to use the port number `:19999` after
the host name but before the `/api/token` part.
1. Start Gemini-CLI.
1. Authenticate with the command `/mcp auth looker`. Gemini-CLI will open up a
browser where you will confirm that you want to access Looker with your
account.
![Authorizing](./authorize.png)
![Authenticated](./authenticated.png)
1. Use Gemini-CLI with your tools.
## Using Toolbox as a Shared Service
Toolbox can be run on another server as a shared service accessed by multiple
users. We strongly recommend running toolbox behind a web proxy such as `nginx`
which will provide SSL encryption. Google Cloud Run is another good way to run
toolbox. You will connect to a service like `https://toolbox.example.com/mcp`.
The proxy server will handle the SSL encryption and certificates. Then it will
foward the requests to `http://localhost:5000/mcp` running in that environment.
The details of the config are beyond the scope of this document, but will be
familiar to your system administrators.
To use the shared service, just change the `localhost:5000` in the `httpUrl` in
`~/.gemini/settings.json` to the host name and possibly the port of the shared
service.

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -1,11 +1,26 @@
# Copyright 2025 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.
sources:
looker-source:
kind: looker
base_url: ${LOOKER_BASE_URL}
client_id: ${LOOKER_CLIENT_ID}
client_secret: ${LOOKER_CLIENT_SECRET}
client_id: ${LOOKER_CLIENT_ID:}
client_secret: ${LOOKER_CLIENT_SECRET:}
verify_ssl: ${LOOKER_VERIFY_SSL:true}
timeout: 600s
use_client_oauth: ${LOOKER_USE_CLIENT_OAUTH:false}
show_hidden_models: ${LOOKER_SHOW_HIDDEN_MODELS:true}
show_hidden_explores: ${LOOKER_SHOW_HIDDEN_EXPLORES:true}
show_hidden_fields: ${LOOKER_SHOW_HIDDEN_FIELDS:true}

View File

@@ -43,6 +43,7 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources
Name: name,
SslVerification: true,
Timeout: "600s",
UseClientOAuth: false,
ShowHiddenModels: true,
ShowHiddenExplores: true,
ShowHiddenFields: true,
@@ -60,6 +61,7 @@ type Config struct {
ClientId string `yaml:"client_id" validate:"required"`
ClientSecret string `yaml:"client_secret" validate:"required"`
SslVerification bool `yaml:"verify_ssl"`
UseClientOAuth bool `yaml:"use_client_oauth"`
Timeout string `yaml:"timeout"`
ShowHiddenModels bool `yaml:"show_hidden_models"`
ShowHiddenExplores bool `yaml:"show_hidden_explores"`
@@ -100,23 +102,29 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
ClientSecret: r.ClientSecret,
}
sdk := v4.NewLookerSDK(rtl.NewAuthSession(cfg))
me, err := sdk.Me("", &cfg)
if err != nil {
return nil, fmt.Errorf("unable to log in: %s", err)
}
logger.DebugContext(ctx, fmt.Sprintf("logged in as user %v %v.\n", *me.FirstName, *me.LastName))
s := &Source{
Name: r.Name,
Kind: SourceKind,
Timeout: r.Timeout,
Client: sdk,
UseClientOAuth: r.UseClientOAuth,
ApiSettings: &cfg,
ShowHiddenModels: r.ShowHiddenModels,
ShowHiddenExplores: r.ShowHiddenExplores,
ShowHiddenFields: r.ShowHiddenFields,
}
if !r.UseClientOAuth {
if r.ClientId == "" || r.ClientSecret == "" {
return nil, fmt.Errorf("client_id and client_secret need to be specified")
}
s.Client = v4.NewLookerSDK(rtl.NewAuthSession(cfg))
resp, err := s.Client.Me("", s.ApiSettings)
if err != nil {
return nil, fmt.Errorf("incorrect settings: %w", err)
}
logger.DebugContext(ctx, fmt.Sprintf("logged in as %s %s", *resp.FirstName, *resp.LastName))
}
return s, nil
}
@@ -129,6 +137,7 @@ type Source struct {
Timeout string `yaml:"timeout"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
UseClientOAuth bool `yaml:"use_client_oauth"`
ShowHiddenModels bool `yaml:"show_hidden_models"`
ShowHiddenExplores bool `yaml:"show_hidden_explores"`
ShowHiddenFields bool `yaml:"show_hidden_fields"`

View File

@@ -50,6 +50,7 @@ func TestParseFromYamlLooker(t *testing.T) {
ClientSecret: "sdakl;jgflkasdfkfg",
Timeout: "600s",
SslVerification: true,
UseClientOAuth: false,
ShowHiddenModels: true,
ShowHiddenExplores: true,
ShowHiddenFields: true,

View File

@@ -93,12 +93,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -112,14 +113,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
var (
@@ -146,9 +148,15 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
wq.VisConfig = &visConfig
qrespFields := "id"
qresp, err := t.Client.CreateQuery(*wq, qrespFields, t.ApiSettings)
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error making create query request: %s", err)
return nil, fmt.Errorf("error getting sdk: %w", err)
}
qresp, err := sdk.CreateQuery(*wq, qrespFields, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making create query request: %w", err)
}
wde := v4.WriteDashboardElement{
@@ -170,9 +178,9 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
Fields: &fields,
}
resp, err := t.Client.CreateDashboardElement(req, t.ApiSettings)
resp, err := sdk.CreateDashboardElement(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making create dashboard element request: %s", err)
return nil, fmt.Errorf("error making create dashboard element request: %w", err)
}
logger.DebugContext(ctx, "resp = %v", resp)
@@ -200,5 +208,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -15,15 +15,62 @@ package lookercommon
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
rtl "github.com/looker-open-source/sdk-codegen/go/rtl"
v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
"github.com/thlib/go-timezone-local/tzlocal"
)
// Make types for RoundTripper
type transportWithAuthHeader struct {
Base http.RoundTripper
AuthToken tools.AccessToken
}
func (t *transportWithAuthHeader) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("x-looker-appid", "go-sdk")
req.Header.Set("Authorization", string(t.AuthToken))
return t.Base.RoundTrip(req)
}
func GetLookerSDK(useClientOAuth bool, config *rtl.ApiSettings, client *v4.LookerSDK, accessToken tools.AccessToken) (*v4.LookerSDK, error) {
if useClientOAuth {
if accessToken == "" {
return nil, fmt.Errorf("no access token supplied with request")
}
// Configure base transport with TLS
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: !config.VerifySsl,
},
}
// Build transport for end user token
newTransport := &transportWithAuthHeader{
Base: transport,
AuthToken: accessToken,
}
// return SDK with new Transport
return v4.NewLookerSDK(&rtl.AuthSession{
Config: *config,
Client: http.Client{Transport: newTransport},
}), nil
}
if client == nil {
return nil, fmt.Errorf("client id or client secret not valid")
}
return client, nil
}
const (
DimensionsFields = "fields(dimensions(name,type,label,label_short,description,synonyms,tags,hidden))"
FiltersFields = "fields(filters(name,type,label,label_short,description,synonyms,tags,hidden))"

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -90,12 +91,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -109,14 +111,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -138,6 +141,10 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
limit := int64(paramsMap["limit"].(int))
offset := int64(paramsMap["offset"].(int))
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestSearchDashboards{
Title: title_ptr,
Description: desc_ptr,
@@ -145,7 +152,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
Offset: &offset,
}
logger.ErrorContext(ctx, "Making request %v", req)
resp, err := t.Client.SearchDashboards(req, t.ApiSettings)
resp, err := sdk.SearchDashboards(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_dashboards request: %s", err)
}

View File

@@ -82,12 +82,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
AuthRequired: cfg.AuthRequired,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -104,6 +105,7 @@ var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
@@ -123,13 +125,17 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
return nil, fmt.Errorf("error processing model or explore: %w", err)
}
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
fields := lookercommon.DimensionsFields
req := v4.RequestLookmlModelExplore{
LookmlModelName: *model,
ExploreName: *explore,
Fields: &fields,
}
resp, err := t.Client.LookmlModelExplore(req, t.ApiSettings)
resp, err := sdk.LookmlModelExplore(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_dimensions request: %w", err)
}
@@ -164,5 +170,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -82,12 +83,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -104,6 +106,7 @@ var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
@@ -124,7 +127,11 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
return nil, fmt.Errorf("'model' must be a string, got %T", mapParams["model"])
}
resp, err := t.Client.LookmlModel(model, "explores(name,description,label,group_label,hidden)", t.ApiSettings)
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
resp, err := sdk.LookmlModel(model, "explores(name,description,label,group_label,hidden)", t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_explores request: %s", err)
}
@@ -173,5 +180,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -82,12 +82,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -104,6 +105,7 @@ var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
@@ -124,12 +126,16 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
}
fields := lookercommon.FiltersFields
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestLookmlModelExplore{
LookmlModelName: *model,
ExploreName: *explore,
Fields: &fields,
}
resp, err := t.Client.LookmlModelExplore(req, t.ApiSettings)
resp, err := sdk.LookmlModelExplore(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_filters request: %w", err)
}
@@ -164,5 +170,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -90,12 +91,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -109,14 +111,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -138,13 +141,17 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
limit := int64(paramsMap["limit"].(int))
offset := int64(paramsMap["offset"].(int))
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestSearchLooks{
Title: title_ptr,
Description: desc_ptr,
Limit: &limit,
Offset: &offset,
}
resp, err := t.Client.SearchLooks(req, t.ApiSettings)
resp, err := sdk.SearchLooks(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_looks request: %s", err)
}
@@ -188,5 +195,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -82,12 +82,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -104,6 +105,7 @@ var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
@@ -124,12 +126,16 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
}
fields := lookercommon.MeasuresFields
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestLookmlModelExplore{
LookmlModelName: *model,
ExploreName: *explore,
Fields: &fields,
}
resp, err := t.Client.LookmlModelExplore(req, t.ApiSettings)
resp, err := sdk.LookmlModelExplore(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_measures request: %w", err)
}
@@ -164,5 +170,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -81,12 +82,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -103,6 +105,7 @@ var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
@@ -122,12 +125,16 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
excludeHidden := !t.ShowHiddenModels
includeInternal := true
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestAllLookmlModels{
ExcludeEmpty: &excludeEmpty,
ExcludeHidden: &excludeHidden,
IncludeInternal: &includeInternal,
}
resp, err := t.Client.AllLookmlModels(req, t.ApiSettings)
resp, err := sdk.AllLookmlModels(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_models request: %s", err)
}
@@ -164,5 +171,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -82,12 +82,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -104,6 +105,7 @@ var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
@@ -124,12 +126,16 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
}
fields := lookercommon.ParametersFields
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestLookmlModelExplore{
LookmlModelName: *model,
ExploreName: *explore,
Fields: &fields,
}
resp, err := t.Client.LookmlModelExplore(req, t.ApiSettings)
resp, err := sdk.LookmlModelExplore(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making get_parameters request: %w", err)
}
@@ -164,5 +170,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -88,12 +89,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -107,14 +109,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -124,8 +127,12 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
}
logger.DebugContext(ctx, "params = ", params)
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
mrespFields := "id,personal_folder_id"
mresp, err := t.Client.Me(mrespFields, t.ApiSettings)
mresp, err := sdk.Me(mrespFields, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making me request: %s", err)
}
@@ -138,7 +145,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
return nil, fmt.Errorf("user does not have a personal folder. cannot continue")
}
dashs, err := t.Client.FolderDashboards(*mresp.PersonalFolderId, "title", t.ApiSettings)
dashs, err := sdk.FolderDashboards(*mresp.PersonalFolderId, "title", t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error getting existing dashboards in folder: %s", err)
}
@@ -157,13 +164,13 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
Description: &description,
FolderId: mresp.PersonalFolderId,
}
resp, err := t.Client.CreateDashboard(wd, t.ApiSettings)
resp, err := sdk.CreateDashboard(wd, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making create dashboard request: %s", err)
}
logger.DebugContext(ctx, "resp = %v", resp)
setting, err := t.Client.GetSetting("host_url", t.ApiSettings)
setting, err := sdk.GetSetting("host_url", t.ApiSettings)
if err != nil {
logger.ErrorContext(ctx, "error getting settings: %s", err)
}
@@ -201,5 +208,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -95,12 +95,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -114,14 +115,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -135,8 +137,12 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
return nil, fmt.Errorf("error building query request: %w", err)
}
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
mrespFields := "id,personal_folder_id"
mresp, err := t.Client.Me(mrespFields, t.ApiSettings)
mresp, err := sdk.Me(mrespFields, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making me request: %s", err)
}
@@ -145,7 +151,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
title := paramsMap["title"].(string)
description := paramsMap["description"].(string)
looks, err := t.Client.FolderLooks(*mresp.PersonalFolderId, "title", t.ApiSettings)
looks, err := sdk.FolderLooks(*mresp.PersonalFolderId, "title", t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error getting existing looks in folder: %s", err)
}
@@ -163,7 +169,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
wq.VisConfig = &visConfig
qrespFields := "id"
qresp, err := t.Client.CreateQuery(*wq, qrespFields, t.ApiSettings)
qresp, err := sdk.CreateQuery(*wq, qrespFields, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making create query request: %s", err)
}
@@ -175,13 +181,13 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
QueryId: qresp.Id,
FolderId: mresp.PersonalFolderId,
}
resp, err := t.Client.CreateLook(wlwq, "", t.ApiSettings)
resp, err := sdk.CreateLook(wlwq, "", t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making create look request: %s", err)
}
logger.DebugContext(ctx, "resp = %v", resp)
setting, err := t.Client.GetSetting("host_url", t.ApiSettings)
setting, err := sdk.GetSetting("host_url", t.ApiSettings)
if err != nil {
logger.ErrorContext(ctx, "error getting settings: %s", err)
}
@@ -219,5 +225,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -83,12 +83,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -102,14 +103,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -121,11 +123,15 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
if err != nil {
return nil, fmt.Errorf("error building WriteQuery request: %w", err)
}
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestRunInlineQuery{
Body: *wq,
ResultFormat: "json",
}
resp, err := t.Client.RunInlineQuery(req, t.ApiSettings)
resp, err := sdk.RunInlineQuery(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making query request: %s", err)
}
@@ -159,5 +165,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -82,12 +82,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -101,14 +102,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -120,11 +122,15 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
if err != nil {
return nil, fmt.Errorf("error building query request: %w", err)
}
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestRunInlineQuery{
Body: *wq,
ResultFormat: "sql",
}
resp, err := t.Client.RunInlineQuery(req, t.ApiSettings)
resp, err := sdk.RunInlineQuery(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making query_sql request: %s", err)
}
@@ -150,5 +156,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -89,12 +89,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -108,14 +109,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -133,8 +135,12 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
visConfig := paramsMap["vis_config"].(map[string]any)
wq.VisConfig = &visConfig
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
respFields := "id,slug,share_url,expanded_share_url"
resp, err := t.Client.CreateQuery(*wq, respFields, t.ApiSettings)
resp, err := sdk.CreateQuery(*wq, respFields, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making query request: %s", err)
}
@@ -175,5 +181,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -88,12 +89,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
// finish tool setup
return Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
Client: s.Client,
ApiSettings: s.ApiSettings,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientOAuth,
Client: s.Client,
ApiSettings: s.ApiSettings,
manifest: tools.Manifest{
Description: cfg.Description,
Parameters: parameters.Manifest(),
@@ -107,14 +109,15 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
var _ tools.Tool = Tool{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
Name string `yaml:"name"`
Kind string `yaml:"kind"`
UseClientOAuth bool
Client *v4.LookerSDK
ApiSettings *rtl.ApiSettings
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -128,12 +131,16 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
look_id := paramsMap["look_id"].(string)
limit := int64(paramsMap["limit"].(int))
sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}
req := v4.RequestRunLook{
LookId: look_id,
ResultFormat: "json",
Limit: &limit,
}
resp, err := t.Client.RunLook(req, t.ApiSettings)
resp, err := sdk.RunLook(req, t.ApiSettings)
if err != nil {
return nil, fmt.Errorf("error making run_look request: %s", err)
}
@@ -167,5 +174,5 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
}
func (t Tool) RequiresClientAuthorization() bool {
return false
return t.UseClientOAuth
}

View File

@@ -147,10 +147,10 @@ func TestClickHouse(t *testing.T) {
t.Fatalf("toolbox didn't start successfully: %s", err)
}
// Get configs for tests
// Get configs for tests
select1Want, mcpSelect1Want, mcpMyFailToolWant, createTableStatement, nilIdWant := getClickHouseWants()
// Run tests
// Run tests
tests.RunToolGetTest(t)
tests.RunToolInvokeTest(t, select1Want, tests.WithMyToolById4Want(nilIdWant))
tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)