Compare commits

..

11 Commits

Author SHA1 Message Date
duwenxin
b482dbf8e0 docs: add temporary warning on ADC connection to Cloud Run issue 2026-01-09 17:41:46 -05:00
Shobhit Singh
4abf0c39e7 feat(bigquery): make maximum rows returned from queries configurable (#2262)
This change allows the agent developer to control the maxium number of
rows returned from tools running BigQuery SQL query. Using this feature
the agent developer could limit how large output is presented to LLM in
an agentic user journey.

## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## 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)
- [ ] Make sure to open an issue
https://github.com/googleapis/genai-toolbox/issues/2261
  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)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #2261 2261
2026-01-09 20:43:46 +00:00
Yuan Teoh
dd7b9de623 docs: add issue and pr triaging and SLO (#2257)
## Description

update docs to reflect triaging workflow and SLO

## 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/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 #<issue_number_goes_here>
2026-01-09 19:21:41 +00:00
release-please[bot]
41b518b955 chore(main): release 0.25.0 (#2218)
🤖 I have created a release *beep* *boop*
---


##
[0.25.0](https://github.com/googleapis/genai-toolbox/compare/v0.24.0...v0.25.0)
(2026-01-08)


### Features

* Add `embeddingModel` support
([#2121](https://github.com/googleapis/genai-toolbox/issues/2121))
([9c62f31](9c62f313ff))
* Add `allowed-hosts` flag
([#2254](https://github.com/googleapis/genai-toolbox/issues/2254))
([17b41f6](17b41f6453))
* Add parameter default value to manifest
([#2264](https://github.com/googleapis/genai-toolbox/issues/2264))
([9d1feca](9d1feca108))
* **snowflake:** Add Snowflake Source and Tools
([#858](https://github.com/googleapis/genai-toolbox/issues/858))
([b706b5b](b706b5bc68))
* **prebuilt/cloud-sql-mysql:** Update CSQL MySQL prebuilt tools to use
IAM ([#2202](https://github.com/googleapis/genai-toolbox/issues/2202))
([731a32e](731a32e536))
* **sources/bigquery:** Make credentials scope configurable
([#2210](https://github.com/googleapis/genai-toolbox/issues/2210))
([a450600](a4506009b9))
* **sources/trino:** Add ssl verification options and fix docs example
([#2155](https://github.com/googleapis/genai-toolbox/issues/2155))
([4a4cf1e](4a4cf1e712))
* **tools/looker:** Add ability to set destination folder with
`make_look` and `make_dashboard`.
([#2245](https://github.com/googleapis/genai-toolbox/issues/2245))
([eb79339](eb793398cd))
* **tools/postgressql:** Add tool to list store procedure
([#2156](https://github.com/googleapis/genai-toolbox/issues/2156))
([cf0fc51](cf0fc515b5))
* **tools/postgressql:** Add Parameter `embeddedBy` config support
([#2151](https://github.com/googleapis/genai-toolbox/issues/2151))
([17b70cc](17b70ccaa7))


### Bug Fixes

* **server:** Add `embeddingModel` config initialization
([#2281](https://github.com/googleapis/genai-toolbox/issues/2281))
([a779975](a7799757c9))
* **sources/cloudgda:** Add import for cloudgda source
([#2217](https://github.com/googleapis/genai-toolbox/issues/2217))
([7daa411](7daa4111f4))
* **tools/alloydb-wait-for-operation:** Fix connection message
generation
([#2228](https://github.com/googleapis/genai-toolbox/issues/2228))
([7053fbb](7053fbb195))
* **tools/alloydbainl:** Only add psv when NL Config Param is defined
([#2265](https://github.com/googleapis/genai-toolbox/issues/2265))
([ef8f3b0](ef8f3b02f2))
* **tools/looker:** Looker client OAuth nil pointer error
([#2231](https://github.com/googleapis/genai-toolbox/issues/2231))
([268700b](268700bdbf))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-01-08 14:56:45 -08:00
Yuan Teoh
6e8a9eb8ec chore: update hugo for release (#2282)
Update hugo version for release v0.25.0
2026-01-08 22:18:59 +00:00
Yuan Teoh
ef8f3b02f2 fix(tools/alloydbainl): only add psv when NL Config Param is defined (#2265)
## Description

PSV should only be required when when it is needed. Currently, we
require psv even whenever user uses AlloyDB AI NL tool. This is due to
the statement that we use to execute nl query.

This PR modified the statement query to only utilize `param_names` and
`param_values` when needed.

Manually tested with a db that does not have psv installed.

🛠️ Fixes #1970
2026-01-08 21:52:05 +00:00
Yuan Teoh
351b007fe3 chore: update mcp registry schema version (#2266) 2026-01-08 13:30:35 -08:00
Yuan Teoh
9d1feca108 feat: add default value to manifest (#2264)
## Description

Add default value to manifest (for both native endpoint and mcp
endpoint).

## 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/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 #1602
2026-01-08 20:53:45 +00:00
Yuan Teoh
17b41f6453 feat: add allowed-hosts flag (#2254)
## Description

Previously added `allowed-origins` (for CORs) is not sufficient for
preventing DNS rebinding attacks. We'll have to check host headers.

To test, run Toolbox with the following:
```
go run . --allowed-hosts=127.0.0.1:5000
```

Test with the following:
```
// curl successfully
curl -H "Host: 127.0.0.1:5000" http://127.0.0.1:5000

// will show Invalid Host Header error
curl -H "Host: attacker:5000" http://127.0.0.1:5000
```

## 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:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] 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
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>
2026-01-08 19:42:54 +00:00
Wenxin Du
a7799757c9 fix(server): Add embeddingModel config initialization (#2281)
Embedding Models were only loaded in hot reload because it was not
initialized properly.
2026-01-08 13:58:39 -05:00
Twisha Bansal
d961e373e1 docs: link medium blogs to toolbox docsite (#2269)
## Description

Adds a section in the navbar that links to the toolbox medium blog: 
<img width="492" height="822" alt="87F2yTQdcbpMHs3"
src="https://github.com/user-attachments/assets/74d8b552-1e8f-449c-8b09-4f86218d2817"
/>


## 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:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] 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
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-08 10:49:55 +00:00
81 changed files with 508 additions and 219 deletions

View File

@@ -51,6 +51,10 @@ ignoreFiles = ["quickstart/shared", "quickstart/python", "quickstart/js", "quick
# Add a new version block here before every release
# The order of versions in this file is mirrored into the dropdown
[[params.versions]]
version = "v0.25.0"
url = "https://googleapis.github.io/genai-toolbox/v0.25.0/"
[[params.versions]]
version = "v0.24.0"
url = "https://googleapis.github.io/genai-toolbox/v0.24.0/"

View File

@@ -1,5 +1,30 @@
# Changelog
## [0.25.0](https://github.com/googleapis/genai-toolbox/compare/v0.24.0...v0.25.0) (2026-01-08)
### Features
* Add `embeddingModel` support ([#2121](https://github.com/googleapis/genai-toolbox/issues/2121)) ([9c62f31](https://github.com/googleapis/genai-toolbox/commit/9c62f313ff5edf0a3b5b8a3e996eba078fba4095))
* Add `allowed-hosts` flag ([#2254](https://github.com/googleapis/genai-toolbox/issues/2254)) ([17b41f6](https://github.com/googleapis/genai-toolbox/commit/17b41f64531b8fe417c28ada45d1992ba430dc1b))
* Add parameter default value to manifest ([#2264](https://github.com/googleapis/genai-toolbox/issues/2264)) ([9d1feca](https://github.com/googleapis/genai-toolbox/commit/9d1feca10810fa42cb4c94a409252f1bd373ee36))
* **snowflake:** Add Snowflake Source and Tools ([#858](https://github.com/googleapis/genai-toolbox/issues/858)) ([b706b5b](https://github.com/googleapis/genai-toolbox/commit/b706b5bc685aeda277f277868bae77d38d5fd7b6))
* **prebuilt/cloud-sql-mysql:** Update CSQL MySQL prebuilt tools to use IAM ([#2202](https://github.com/googleapis/genai-toolbox/issues/2202)) ([731a32e](https://github.com/googleapis/genai-toolbox/commit/731a32e5360b4d6862d81fcb27d7127c655679a8))
* **sources/bigquery:** Make credentials scope configurable ([#2210](https://github.com/googleapis/genai-toolbox/issues/2210)) ([a450600](https://github.com/googleapis/genai-toolbox/commit/a4506009b93771b77fb05ae97044f914967e67ed))
* **sources/trino:** Add ssl verification options and fix docs example ([#2155](https://github.com/googleapis/genai-toolbox/issues/2155)) ([4a4cf1e](https://github.com/googleapis/genai-toolbox/commit/4a4cf1e712b671853678dba99c4dc49dd4fc16a2))
* **tools/looker:** Add ability to set destination folder with `make_look` and `make_dashboard`. ([#2245](https://github.com/googleapis/genai-toolbox/issues/2245)) ([eb79339](https://github.com/googleapis/genai-toolbox/commit/eb793398cd1cc4006d9808ccda5dc7aea5e92bd5))
* **tools/postgressql:** Add tool to list store procedure ([#2156](https://github.com/googleapis/genai-toolbox/issues/2156)) ([cf0fc51](https://github.com/googleapis/genai-toolbox/commit/cf0fc515b57d9b84770076f3c0c5597c4597ef62))
* **tools/postgressql:** Add Parameter `embeddedBy` config support ([#2151](https://github.com/googleapis/genai-toolbox/issues/2151)) ([17b70cc](https://github.com/googleapis/genai-toolbox/commit/17b70ccaa754d15bcc33a1a3ecb7e652520fa600))
### Bug Fixes
* **server:** Add `embeddingModel` config initialization ([#2281](https://github.com/googleapis/genai-toolbox/issues/2281)) ([a779975](https://github.com/googleapis/genai-toolbox/commit/a7799757c9345f99b6d2717841fbf792d364e1a2))
* **sources/cloudgda:** Add import for cloudgda source ([#2217](https://github.com/googleapis/genai-toolbox/issues/2217)) ([7daa411](https://github.com/googleapis/genai-toolbox/commit/7daa4111f4ebfb0a35319fd67a8f7b9f0f99efcf))
* **tools/alloydb-wait-for-operation:** Fix connection message generation ([#2228](https://github.com/googleapis/genai-toolbox/issues/2228)) ([7053fbb](https://github.com/googleapis/genai-toolbox/commit/7053fbb1953653143d39a8510916ea97a91022a6))
* **tools/alloydbainl:** Only add psv when NL Config Param is defined ([#2265](https://github.com/googleapis/genai-toolbox/issues/2265)) ([ef8f3b0](https://github.com/googleapis/genai-toolbox/commit/ef8f3b02f2f38ce94a6ba9acf35d08b9469bef4e))
* **tools/looker:** Looker client OAuth nil pointer error ([#2231](https://github.com/googleapis/genai-toolbox/issues/2231)) ([268700b](https://github.com/googleapis/genai-toolbox/commit/268700bdbf8281de0318d60ca613ed3672990b20))
## [0.24.0](https://github.com/googleapis/genai-toolbox/compare/v0.23.0...v0.24.0) (2025-12-19)

View File

@@ -379,6 +379,23 @@ to approve PRs for main. TeamSync is used to create this team from the MDB
Group `toolbox-contributors`. Googlers who are developing for MCP-Toolbox
but aren't part of the core team should join this group.
### Issue/PR Triage and SLO
After an issue is created, maintainers will assign the following labels:
* `Priority` (defaulted to P0)
* `Type` (if applicable)
* `Product` (if applicable)
All incoming issues and PRs will follow the following SLO:
| Type | Priority | Objective |
|-----------------|----------|------------------------------------------------------------------------|
| Feature Request | P0 | Must respond within **5 days** |
| Process | P0 | Must respond within **5 days** |
| Bugs | P0 | Must respond within **5 days**, and resolve/closure within **14 days** |
| Bugs | P1 | Must respond within **7 days**, and resolve/closure within **90 days** |
| Bugs | P2 | Must respond within **30 days**
_Types that are not listed in the table do not adhere to any SLO._
### Releasing
Toolbox has two types of releases: versioned and continuous. It uses Google

View File

@@ -140,7 +140,7 @@ To install Toolbox as a binary:
>
> ```sh
> # see releases page for other versions
> export VERSION=0.24.0
> export VERSION=0.25.0
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
> chmod +x toolbox
> ```
@@ -153,7 +153,7 @@ To install Toolbox as a binary:
>
> ```sh
> # see releases page for other versions
> export VERSION=0.24.0
> export VERSION=0.25.0
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/arm64/toolbox
> chmod +x toolbox
> ```
@@ -166,7 +166,7 @@ To install Toolbox as a binary:
>
> ```sh
> # see releases page for other versions
> export VERSION=0.24.0
> export VERSION=0.25.0
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/amd64/toolbox
> chmod +x toolbox
> ```
@@ -179,7 +179,7 @@ To install Toolbox as a binary:
>
> ```cmd
> :: see releases page for other versions
> set VERSION=0.24.0
> set VERSION=0.25.0
> curl -o toolbox.exe "https://storage.googleapis.com/genai-toolbox/v%VERSION%/windows/amd64/toolbox.exe"
> ```
>
@@ -191,7 +191,7 @@ To install Toolbox as a binary:
>
> ```powershell
> # see releases page for other versions
> $VERSION = "0.24.0"
> $VERSION = "0.25.0"
> curl.exe -o toolbox.exe "https://storage.googleapis.com/genai-toolbox/v$VERSION/windows/amd64/toolbox.exe"
> ```
>
@@ -204,7 +204,7 @@ You can also install Toolbox as a container:
```sh
# see releases page for other versions
export VERSION=0.24.0
export VERSION=0.25.0
docker pull us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION
```
@@ -228,7 +228,7 @@ To install from source, ensure you have the latest version of
[Go installed](https://go.dev/doc/install), and then run the following command:
```sh
go install github.com/googleapis/genai-toolbox@v0.24.0
go install github.com/googleapis/genai-toolbox@v0.25.0
```
<!-- {x-release-please-end} -->

View File

@@ -381,7 +381,9 @@ func NewCommand(opts ...Option) *Command {
flags.BoolVar(&cmd.cfg.Stdio, "stdio", false, "Listens via MCP STDIO instead of acting as a remote HTTP server.")
flags.BoolVar(&cmd.cfg.DisableReload, "disable-reload", false, "Disables dynamic reloading of tools file.")
flags.BoolVar(&cmd.cfg.UI, "ui", false, "Launches the Toolbox UI web server.")
// TODO: Insecure by default. Might consider updating this for v1.0.0
flags.StringSliceVar(&cmd.cfg.AllowedOrigins, "allowed-origins", []string{"*"}, "Specifies a list of origins permitted to access this server. Defaults to '*'.")
flags.StringSliceVar(&cmd.cfg.AllowedHosts, "allowed-hosts", []string{"*"}, "Specifies a list of hosts permitted to access this server. Defaults to '*'.")
// wrap RunE command so that we have access to original Command object
cmd.RunE = func(*cobra.Command, []string) error { return run(cmd) }
@@ -947,6 +949,7 @@ func run(cmd *Command) error {
cmd.cfg.SourceConfigs = finalToolsFile.Sources
cmd.cfg.AuthServiceConfigs = finalToolsFile.AuthServices
cmd.cfg.EmbeddingModelConfigs = finalToolsFile.EmbeddingModels
cmd.cfg.ToolConfigs = finalToolsFile.Tools
cmd.cfg.ToolsetConfigs = finalToolsFile.Toolsets
cmd.cfg.PromptConfigs = finalToolsFile.Prompts

View File

@@ -67,6 +67,9 @@ func withDefaults(c server.ServerConfig) server.ServerConfig {
if c.AllowedOrigins == nil {
c.AllowedOrigins = []string{"*"}
}
if c.AllowedHosts == nil {
c.AllowedHosts = []string{"*"}
}
return c
}
@@ -220,6 +223,13 @@ func TestServerConfigFlags(t *testing.T) {
AllowedOrigins: []string{"http://foo.com", "http://bar.com"},
}),
},
{
desc: "allowed hosts",
args: []string{"--allowed-hosts", "http://foo.com,http://bar.com"},
want: withDefaults(server.ServerConfig{
AllowedHosts: []string{"http://foo.com", "http://bar.com"},
}),
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {

View File

@@ -1 +1 @@
0.24.0
0.25.0

18
docs/en/blogs/_index.md Normal file
View File

@@ -0,0 +1,18 @@
---
title: "Featured Articles"
weight: 3
description: Toolbox Medium Blogs
manualLink: "https://medium.com/@mcp_toolbox"
manualLinkTarget: _blank
---
<html>
<head>
<title>Redirecting to Featured Articles</title>
<link rel="canonical" href="https://medium.com/@mcp_toolbox"/>
<meta http-equiv="refresh" content="0;url=https://medium.com/@mcp_toolbox"/>
</head>
<body>
<p>If you are not automatically redirected, please <a href="https://medium.com/@mcp_toolbox">follow this link to our articles</a>.</p>
</body>
</html>

View File

@@ -234,7 +234,7 @@
},
"outputs": [],
"source": [
"version = \"0.24.0\" # x-release-please-version\n",
"version = \"0.25.0\" # x-release-please-version\n",
"! curl -O https://storage.googleapis.com/genai-toolbox/v{version}/linux/amd64/toolbox\n",
"\n",
"# Make the binary executable\n",

View File

@@ -103,7 +103,7 @@ To install Toolbox as a binary on Linux (AMD64):
```sh
# see releases page for other versions
export VERSION=0.24.0
export VERSION=0.25.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox
```
@@ -114,7 +114,7 @@ To install Toolbox as a binary on macOS (Apple Silicon):
```sh
# see releases page for other versions
export VERSION=0.24.0
export VERSION=0.25.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/arm64/toolbox
chmod +x toolbox
```
@@ -125,7 +125,7 @@ To install Toolbox as a binary on macOS (Intel):
```sh
# see releases page for other versions
export VERSION=0.24.0
export VERSION=0.25.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/amd64/toolbox
chmod +x toolbox
```
@@ -136,7 +136,7 @@ To install Toolbox as a binary on Windows (Command Prompt):
```cmd
:: see releases page for other versions
set VERSION=0.24.0
set VERSION=0.25.0
curl -o toolbox.exe "https://storage.googleapis.com/genai-toolbox/v%VERSION%/windows/amd64/toolbox.exe"
```
@@ -146,7 +146,7 @@ To install Toolbox as a binary on Windows (PowerShell):
```powershell
# see releases page for other versions
$VERSION = "0.24.0"
$VERSION = "0.25.0"
curl.exe -o toolbox.exe "https://storage.googleapis.com/genai-toolbox/v$VERSION/windows/amd64/toolbox.exe"
```
@@ -158,7 +158,7 @@ You can also install Toolbox as a container:
```sh
# see releases page for other versions
export VERSION=0.24.0
export VERSION=0.25.0
docker pull us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION
```
@@ -177,7 +177,7 @@ To install from source, ensure you have the latest version of
[Go installed](https://go.dev/doc/install), and then run the following command:
```sh
go install github.com/googleapis/genai-toolbox@v0.24.0
go install github.com/googleapis/genai-toolbox@v0.25.0
```
{{% /tab %}}

View File

@@ -105,7 +105,7 @@ In this section, we will download Toolbox, configure our tools in a
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -13,7 +13,7 @@ In this section, we will download Toolbox, configure our tools in a
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -100,19 +100,19 @@ After you install Looker in the MCP Store, resources and tools from the server a
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -45,19 +45,19 @@ instance:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -43,19 +43,19 @@ expose your developer assistant tools to a MySQL instance:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -44,19 +44,19 @@ expose your developer assistant tools to a Neo4j instance:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -56,19 +56,19 @@ Omni](https://cloud.google.com/alloydb/omni/current/docs/overview).
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -43,19 +43,19 @@ to expose your developer assistant tools to a SQLite instance:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.24.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -101,6 +101,9 @@ authentication token.
TOOLBOX_URL = "https://your-toolbox-service-xyz.a.run.app"
# Initialize the client with the Cloud Run URL and Auth headers
# WARNING: There is a known issue that may cause it to fail with a 401 error
# on local machines.
# See https://github.com/googleapis/mcp-toolbox-sdk-python/issues/496
client = ToolboxSyncClient(
TOOLBOX_URL,
client_headers={"Authorization": auth_methods.get_google_id_token(TOOLBOX_URL)}

View File

@@ -68,7 +68,12 @@ networks:
```
{{< notice tip >}}
To prevent DNS rebinding attack, use the `--allowed-origins` flag to specify a
To prevent DNS rebinding attack, use the `--allowed-hosts` flag to specify a
list of hosts for validation. E.g. `command: [ "toolbox",
"--tools-file", "/config/tools.yaml", "--address", "0.0.0.0",
"--allowed-hosts", "localhost:5000"]`
To implement CORs, use the `--allowed-origins` flag to specify a
list of origins permitted to access the server. E.g. `command: [ "toolbox",
"--tools-file", "/config/tools.yaml", "--address", "0.0.0.0",
"--allowed-origins", "https://foo.bar"]`

View File

@@ -188,9 +188,13 @@ description: >
path: tools.yaml
```
{{< notice tip >}}
{{< notice tip >}}
To prevent DNS rebinding attack, use the `--allowed-origins` flag to specify a
list of origins permitted to access the server. E.g. `args: ["--address",
"0.0.0.0", "--allowed-hosts", "foo.bar:5000"]`
To implement CORs, use the `--allowed-origins` flag to specify a
list of origins permitted to access the server. E.g. `args: ["--address",
"0.0.0.0", "--allowed-origins", "https://foo.bar"]`
{{< /notice >}}

View File

@@ -142,14 +142,18 @@ deployment will time out.
### Update deployed server to be secure
To prevent DNS rebinding attack, use the `--allowed-origins` flag to specify a
list of origins permitted to access the server. In order to do that, you will
To prevent DNS rebinding attack, use the `--allowed-hosts` flag to specify a
list of hosts. In order to do that, you will
have to re-deploy the cloud run service with the new flag.
To implement CORs checks, use the `--allowed-origins` flag to specify a list of
origins permitted to access the server.
1. Set an environment variable to the cloud run url:
```bash
export URL=<cloud run url>
export HOST=<cloud run host>
```
2. Redeploy Toolbox:
@@ -160,7 +164,7 @@ have to re-deploy the cloud run service with the new flag.
--service-account toolbox-identity \
--region us-central1 \
--set-secrets "/app/tools.yaml=tools:latest" \
--args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080","--allowed-origins=$URL"
--args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080","--allowed-origins=$URL","--allowed-hosts=$HOST"
# --allow-unauthenticated # https://cloud.google.com/run/docs/authenticating/public#gcloud
```
@@ -172,7 +176,7 @@ have to re-deploy the cloud run service with the new flag.
--service-account toolbox-identity \
--region us-central1 \
--set-secrets "/app/tools.yaml=tools:latest" \
--args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080","--allowed-origins=$URL" \
--args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080","--allowed-origins=$URL","--allowed-hosts=$HOST" \
# TODO(dev): update the following to match your VPC if necessary
--network default \
--subnet default
@@ -207,6 +211,9 @@ from toolbox_core import ToolboxClient, auth_methods
# Replace with the Cloud Run service URL generated in the previous step
URL = "https://cloud-run-url.app"
# WARNING: There is a known issue that may cause it to fail with a 401 error
# on local machines.
# See https://github.com/googleapis/mcp-toolbox-sdk-python/issues/496
auth_token_provider = auth_methods.aget_google_id_token(URL) # can also use sync method
async def main():

View File

@@ -8,25 +8,26 @@ description: >
## Reference
| Flag (Short) | Flag (Long) | Description | Default |
|--------------|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
| `-a` | `--address` | Address of the interface the server will listen on. | `127.0.0.1` |
| | `--disable-reload` | Disables dynamic reloading of tools file. | |
| `-h` | `--help` | help for toolbox | |
| | `--log-level` | Specify the minimum level logged. Allowed: 'DEBUG', 'INFO', 'WARN', 'ERROR'. | `info` |
| | `--logging-format` | Specify logging format to use. Allowed: 'standard' or 'JSON'. | `standard` |
| `-p` | `--port` | Port the server will listen on. | `5000` |
| | `--prebuilt` | Use a prebuilt tool configuration by source type. See [Prebuilt Tools Reference](prebuilt-tools.md) for allowed values. | |
| | `--stdio` | Listens via MCP STDIO instead of acting as a remote HTTP server. | |
| | `--telemetry-gcp` | Enable exporting directly to Google Cloud Monitoring. | |
| | `--telemetry-otlp` | Enable exporting using OpenTelemetry Protocol (OTLP) to the specified endpoint (e.g. 'http://127.0.0.1:4318') | |
| | `--telemetry-service-name` | Sets the value of the service.name resource attribute for telemetry data. | `toolbox` |
| Flag (Short) | Flag (Long) | Description | Default |
|--------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
| `-a` | `--address` | Address of the interface the server will listen on. | `127.0.0.1` |
| | `--disable-reload` | Disables dynamic reloading of tools file. | |
| `-h` | `--help` | help for toolbox | |
| | `--log-level` | Specify the minimum level logged. Allowed: 'DEBUG', 'INFO', 'WARN', 'ERROR'. | `info` |
| | `--logging-format` | Specify logging format to use. Allowed: 'standard' or 'JSON'. | `standard` |
| `-p` | `--port` | Port the server will listen on. | `5000` |
| | `--prebuilt` | Use a prebuilt tool configuration by source type. See [Prebuilt Tools Reference](prebuilt-tools.md) for allowed values. | |
| | `--stdio` | Listens via MCP STDIO instead of acting as a remote HTTP server. | |
| | `--telemetry-gcp` | Enable exporting directly to Google Cloud Monitoring. | |
| | `--telemetry-otlp` | Enable exporting using OpenTelemetry Protocol (OTLP) to the specified endpoint (e.g. 'http://127.0.0.1:4318') | |
| | `--telemetry-service-name` | Sets the value of the service.name resource attribute for telemetry data. | `toolbox` |
| | `--tools-file` | File path specifying the tool configuration. Cannot be used with --tools-files or --tools-folder. | |
| | `--tools-files` | Multiple file paths specifying tool configurations. Files will be merged. Cannot be used with --tools-file or --tools-folder. | |
| | `--tools-folder` | Directory path containing YAML tool configuration files. All .yaml and .yml files in the directory will be loaded and merged. Cannot be used with --tools-file or --tools-files. | |
| | `--ui` | Launches the Toolbox UI web server. | |
| | `--allowed-origins` | Specifies a list of origins permitted to access this server. | `*` |
| `-v` | `--version` | version for toolbox | |
| | `--ui` | Launches the Toolbox UI web server. | |
| | `--allowed-origins` | Specifies a list of origins permitted to access this server for CORs access. | `*` |
| | `--allowed-hosts` | Specifies a list of hosts permitted to access this server to prevent DNS rebinding attacks. | `*` |
| `-v` | `--version` | version for toolbox | |
## Examples

View File

@@ -134,6 +134,7 @@ sources:
# scopes: # Optional: List of OAuth scopes to request.
# - "https://www.googleapis.com/auth/bigquery"
# - "https://www.googleapis.com/auth/drive.readonly"
# maxQueryResultRows: 50 # Optional: Limits the number of rows returned by queries. Defaults to 50.
```
Initialize a BigQuery source that uses the client's access token:
@@ -153,6 +154,7 @@ sources:
# scopes: # Optional: List of OAuth scopes to request.
# - "https://www.googleapis.com/auth/bigquery"
# - "https://www.googleapis.com/auth/drive.readonly"
# maxQueryResultRows: 50 # Optional: Limits the number of rows returned by queries. Defaults to 50.
```
## Reference
@@ -167,3 +169,4 @@ sources:
| useClientOAuth | bool | false | If true, forwards the client's OAuth access token from the "Authorization" header to downstream queries. **Note:** This cannot be used with `writeMode: protected`. |
| scopes | []string | false | A list of OAuth 2.0 scopes to use for the credentials. If not provided, default scopes are used. |
| impersonateServiceAccount | string | false | Service account email to impersonate when making BigQuery and Dataplex API calls. The authenticated principal must have the `roles/iam.serviceAccountTokenCreator` role on the target service account. [Learn More](https://cloud.google.com/iam/docs/service-account-impersonation) |
| maxQueryResultRows | int | false | The maximum number of rows to return from a query. Defaults to 50. |

View File

@@ -771,7 +771,7 @@
},
"outputs": [],
"source": [
"version = \"0.24.0\" # x-release-please-version\n",
"version = \"0.25.0\" # x-release-please-version\n",
"! curl -L -o /content/toolbox https://storage.googleapis.com/genai-toolbox/v{version}/linux/amd64/toolbox\n",
"\n",
"# Make the binary executable\n",

View File

@@ -123,7 +123,7 @@ In this section, we will download and install the Toolbox binary.
<!-- {x-release-please-start-version} -->
```bash
export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, or windows/amd64
export VERSION="0.24.0"
export VERSION="0.25.0"
curl -O https://storage.googleapis.com/genai-toolbox/v$VERSION/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -220,7 +220,7 @@
},
"outputs": [],
"source": [
"version = \"0.24.0\" # x-release-please-version\n",
"version = \"0.25.0\" # x-release-please-version\n",
"! curl -O https://storage.googleapis.com/genai-toolbox/v{version}/linux/amd64/toolbox\n",
"\n",
"# Make the binary executable\n",

View File

@@ -179,7 +179,7 @@ to use BigQuery, and then run the Toolbox server.
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -98,7 +98,7 @@ In this section, we will download Toolbox, configure our tools in a
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -34,7 +34,7 @@ In this section, we will download Toolbox and run the Toolbox server.
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -48,7 +48,7 @@ In this section, we will download Toolbox and run the Toolbox server.
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -34,7 +34,7 @@ In this section, we will download Toolbox and run the Toolbox server.
<!-- {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.24.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.25.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -1,6 +1,6 @@
{
"name": "mcp-toolbox-for-databases",
"version": "0.24.0",
"version": "0.25.0",
"description": "MCP Toolbox for Databases is an open-source MCP server for more than 30 different datasources.",
"contextFileName": "MCP-TOOLBOX-EXTENSION.md"
}

View File

@@ -19,6 +19,7 @@ sources:
location: ${BIGQUERY_LOCATION:}
useClientOAuth: ${BIGQUERY_USE_CLIENT_OAUTH:false}
scopes: ${BIGQUERY_SCOPES:}
maxQueryResultRows: ${BIGQUERY_MAX_QUERY_RESULT_ROWS:50}
tools:
analyze_contribution:

View File

@@ -68,6 +68,8 @@ type ServerConfig struct {
UI bool
// Specifies a list of origins permitted to access this server.
AllowedOrigins []string
// Specifies a list of hosts permitted to access this server
AllowedHosts []string
}
type logFormat string

View File

@@ -300,6 +300,21 @@ func InitializeConfigs(ctx context.Context, cfg ServerConfig) (
return sourcesMap, authServicesMap, embeddingModelsMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap, nil
}
func hostCheck(allowedHosts map[string]struct{}) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, hasWildcard := allowedHosts["*"]
_, hostIsAllowed := allowedHosts[r.Host]
if !hasWildcard && !hostIsAllowed {
// Return 400 Bad Request or 403 Forbidden to block the attack
http.Error(w, "Invalid Host header", http.StatusBadRequest)
return
}
next.ServeHTTP(w, r)
})
}
}
// NewServer returns a Server object based on provided Config.
func NewServer(ctx context.Context, cfg ServerConfig) (*Server, error) {
instrumentation, err := util.InstrumentationFromContext(ctx)
@@ -374,7 +389,7 @@ func NewServer(ctx context.Context, cfg ServerConfig) (*Server, error) {
// cors
if slices.Contains(cfg.AllowedOrigins, "*") {
s.logger.WarnContext(ctx, "wildcard (`*`) allows all origin to access the resource and is not secure. Use it with cautious for public, non-sensitive data, or during local development. Recommended to use `--allowed-origins` flag to prevent DNS rebinding attacks")
s.logger.WarnContext(ctx, "wildcard (`*`) allows all origin to access the resource and is not secure. Use it with cautious for public, non-sensitive data, or during local development. Recommended to use `--allowed-origins` flag")
}
corsOpts := cors.Options{
AllowedOrigins: cfg.AllowedOrigins,
@@ -385,6 +400,15 @@ func NewServer(ctx context.Context, cfg ServerConfig) (*Server, error) {
MaxAge: 300, // cache preflight results for 5 minutes
}
r.Use(cors.Handler(corsOpts))
// validate hosts for DNS rebinding attacks
if slices.Contains(cfg.AllowedHosts, "*") {
s.logger.WarnContext(ctx, "wildcard (`*`) allows all hosts to access the resource and is not secure. Use it with cautious for public, non-sensitive data, or during local development. Recommended to use `--allowed-hosts` flag to prevent DNS rebinding attacks")
}
allowedHostsMap := make(map[string]struct{}, len(cfg.AllowedHosts))
for _, h := range cfg.AllowedHosts {
allowedHostsMap[h] = struct{}{}
}
r.Use(hostCheck(allowedHostsMap))
// control plane
apiR, err := apiRouter(s)

View File

@@ -43,9 +43,10 @@ func TestServe(t *testing.T) {
addr, port := "127.0.0.1", 5000
cfg := server.ServerConfig{
Version: "0.0.0",
Address: addr,
Port: port,
Version: "0.0.0",
Address: addr,
Port: port,
AllowedHosts: []string{"*"},
}
otelShutdown, err := telemetry.SetupOTel(ctx, "0.0.0", "", false, "toolbox")

View File

@@ -89,6 +89,7 @@ type Config struct {
UseClientOAuth bool `yaml:"useClientOAuth"`
ImpersonateServiceAccount string `yaml:"impersonateServiceAccount"`
Scopes StringOrStringSlice `yaml:"scopes"`
MaxQueryResultRows int `yaml:"maxQueryResultRows"`
}
// StringOrStringSlice is a custom type that can unmarshal both a single string
@@ -127,6 +128,10 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
r.WriteMode = WriteModeAllowed
}
if r.MaxQueryResultRows == 0 {
r.MaxQueryResultRows = 50
}
if r.WriteMode == WriteModeProtected && r.UseClientOAuth {
// The protected mode only allows write operations to the session's temporary datasets.
// when using client OAuth, a new session is created every
@@ -150,7 +155,7 @@ func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.So
Client: client,
RestService: restService,
TokenSource: tokenSource,
MaxQueryResultRows: 50,
MaxQueryResultRows: r.MaxQueryResultRows,
ClientCreator: clientCreator,
}
@@ -567,7 +572,7 @@ func (s *Source) RunSQL(ctx context.Context, bqClient *bigqueryapi.Client, state
}
var out []any
for {
for s.MaxQueryResultRows <= 0 || len(out) < s.MaxQueryResultRows {
var val []bigqueryapi.Value
err = it.Next(&val)
if err == iterator.Done {

View File

@@ -21,9 +21,12 @@ import (
yaml "github.com/goccy/go-yaml"
"github.com/google/go-cmp/cmp"
"go.opentelemetry.io/otel/trace/noop"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/testutils"
"github.com/googleapis/genai-toolbox/internal/util"
)
func TestParseFromYamlBigQuery(t *testing.T) {
@@ -154,6 +157,26 @@ func TestParseFromYamlBigQuery(t *testing.T) {
},
},
},
{
desc: "with max query result rows example",
in: `
sources:
my-instance:
kind: bigquery
project: my-project
location: us
maxQueryResultRows: 10
`,
want: server.SourceConfigs{
"my-instance": bigquery.Config{
Name: "my-instance",
Kind: bigquery.SourceKind,
Project: "my-project",
Location: "us",
MaxQueryResultRows: 10,
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
@@ -220,6 +243,59 @@ func TestFailParseFromYaml(t *testing.T) {
}
}
func TestInitialize_MaxQueryResultRows(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
ctx = util.WithUserAgent(ctx, "test-agent")
tracer := noop.NewTracerProvider().Tracer("")
tcs := []struct {
desc string
cfg bigquery.Config
want int
}{
{
desc: "default value",
cfg: bigquery.Config{
Name: "test-default",
Kind: bigquery.SourceKind,
Project: "test-project",
UseClientOAuth: true,
},
want: 50,
},
{
desc: "configured value",
cfg: bigquery.Config{
Name: "test-configured",
Kind: bigquery.SourceKind,
Project: "test-project",
UseClientOAuth: true,
MaxQueryResultRows: 100,
},
want: 100,
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
src, err := tc.cfg.Initialize(ctx, tracer)
if err != nil {
t.Fatalf("Initialize failed: %v", err)
}
bqSrc, ok := src.(*bigquery.Source)
if !ok {
t.Fatalf("Expected *bigquery.Source, got %T", src)
}
if bqSrc.MaxQueryResultRows != tc.want {
t.Errorf("MaxQueryResultRows = %d, want %d", bqSrc.MaxQueryResultRows, tc.want)
}
})
}
}
func TestNormalizeValue(t *testing.T) {
tests := []struct {
name string

View File

@@ -15,9 +15,7 @@ package looker
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"strings"
"time"
@@ -210,49 +208,6 @@ func (s *Source) LookerSessionLength() int64 {
return s.SessionLength
}
// Make types for RoundTripper
type transportWithAuthHeader struct {
Base http.RoundTripper
AuthToken string
}
func (t *transportWithAuthHeader) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("x-looker-appid", "go-sdk")
req.Header.Set("Authorization", t.AuthToken)
return t.Base.RoundTrip(req)
}
func (s *Source) GetLookerSDK(accessToken string) (*v4.LookerSDK, error) {
if s.UseClientAuthorization() {
if accessToken == "" {
return nil, fmt.Errorf("no access token supplied with request")
}
session := rtl.NewAuthSession(*s.LookerApiSettings())
// Configure base transport with TLS
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: !s.LookerApiSettings().VerifySsl,
},
}
// Build transport for end user token
session.Client = http.Client{
Transport: &transportWithAuthHeader{
Base: transport,
AuthToken: accessToken,
},
}
// return SDK with new Transport
return v4.NewLookerSDK(session), nil
}
if s.LookerClient() == nil {
return nil, fmt.Errorf("client id or client secret not valid")
}
return s.LookerClient(), nil
}
func initGoogleCloudConnection(ctx context.Context) (oauth2.TokenSource, error) {
cred, err := google.FindDefaultCredentials(ctx, geminidataanalytics.DefaultAuthScopes()...)
if err != nil {

View File

@@ -77,26 +77,21 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
placeholderParts = append(placeholderParts, fmt.Sprintf("$%d", i+3)) // $1, $2 reserved
}
var paramNamesSQL string
var paramValuesSQL string
var stmt string
if numParams > 0 {
paramNamesSQL = fmt.Sprintf("ARRAY[%s]", strings.Join(quotedNameParts, ", "))
paramValuesSQL = fmt.Sprintf("ARRAY[%s]", strings.Join(placeholderParts, ", "))
paramNamesSQL := fmt.Sprintf("ARRAY[%s]", strings.Join(quotedNameParts, ", "))
paramValuesSQL := fmt.Sprintf("ARRAY[%s]", strings.Join(placeholderParts, ", "))
// execute_nl_query is the AlloyDB AI function that executes the natural language query
// The first parameter is the natural language query, which is passed as $1
// The second parameter is the NLConfig, which is passed as a $2
// The following params are the list of PSV values passed to the NLConfig
// Example SQL statement being executed:
// SELECT alloydb_ai_nl.execute_nl_query(nl_question => 'How many tickets do I have?', nl_config_id => 'cymbal_air_nl_config', param_names => ARRAY ['user_email'], param_values => ARRAY ['hailongli@google.com']);
stmtFormat := "SELECT alloydb_ai_nl.execute_nl_query(nl_question => $1, nl_config_id => $2, param_names => %s, param_values => %s);"
stmt = fmt.Sprintf(stmtFormat, paramNamesSQL, paramValuesSQL)
} else {
paramNamesSQL = "ARRAY[]::TEXT[]"
paramValuesSQL = "ARRAY[]::TEXT[]"
stmt = "SELECT alloydb_ai_nl.execute_nl_query(nl_question => $1, nl_config_id => $2);"
}
// execute_nl_query is the AlloyDB AI function that executes the natural language query
// The first parameter is the natural language query, which is passed as $1
// The second parameter is the NLConfig, which is passed as a $2
// The following params are the list of PSV values passed to the NLConfig
// Example SQL statement being executed:
// SELECT alloydb_ai_nl.execute_nl_query(nl_question => 'How many tickets do I have?', nl_config_id => 'cymbal_air_nl_config', param_names => ARRAY ['user_email'], param_values => ARRAY ['hailongli@google.com']);
stmtFormat := "SELECT alloydb_ai_nl.execute_nl_query(nl_question => $1, nl_config_id => $2, param_names => %s, param_values => %s);"
stmt := fmt.Sprintf(stmtFormat, paramNamesSQL, paramValuesSQL)
newQuestionParam := parameters.NewStringParameter(
"question", // name
"The natural language question to ask.", // description

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -159,7 +159,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
visConfig := paramsMap["vis_config"].(map[string]any)
wq.VisConfig = &visConfig
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -191,7 +192,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
req.Dimension = &dimension
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -15,17 +15,65 @@ package lookercommon
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
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")
}
session := rtl.NewAuthSession(*config)
// Configure base transport with TLS
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: !config.VerifySsl,
},
}
// Build transport for end user token
session.Client = http.Client{
Transport: &transportWithAuthHeader{
Base: transport,
AuthToken: accessToken,
},
}
// return SDK with new Transport
return v4.NewLookerSDK(session), 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,suggestable,suggestions,suggest_dimension,suggest_explore))"
FiltersFields = "fields(filters(name,type,label,label_short,description,synonyms,tags,hidden,suggestable,suggestions,suggest_dimension,suggest_explore))"

View File

@@ -47,8 +47,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -116,7 +116,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, err
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -47,8 +47,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -117,7 +117,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, err
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -124,7 +125,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("'devMode' must be a boolean, got %T", mapParams["devMode"])
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -48,8 +49,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerSessionLength() int64
}
@@ -136,7 +137,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
contentId_ptr = nil
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -46,8 +47,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -119,7 +120,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("'conn' must be a string, got %T", mapParams["conn"])
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -118,7 +119,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -46,8 +47,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -121,7 +122,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
db, _ := mapParams["db"].(string)
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -136,7 +137,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("'tables' must be a string, got %T", mapParams["tables"])
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -131,7 +132,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("'schema' must be a string, got %T", mapParams["schema"])
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -140,7 +141,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
limit := int64(paramsMap["limit"].(int))
offset := int64(paramsMap["offset"].(int))
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerShowHiddenFields() bool
}
@@ -124,7 +124,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("error processing model or explore: %w", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerShowHiddenExplores() bool
}
@@ -125,7 +126,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("'model' must be a string, got %T", mapParams["model"])
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerShowHiddenFields() bool
}
@@ -125,7 +125,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
fields := lookercommon.FiltersFields
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -140,7 +141,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
limit := int64(paramsMap["limit"].(int))
offset := int64(paramsMap["offset"].(int))
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerShowHiddenFields() bool
}
@@ -125,7 +125,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
fields := lookercommon.MeasuresFields
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerShowHiddenModels() bool
}
@@ -123,7 +124,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
excludeHidden := !source.LookerShowHiddenModels()
includeInternal := true
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
LookerShowHiddenFields() bool
}
@@ -125,7 +125,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
fields := lookercommon.ParametersFields
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -121,7 +121,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -119,7 +120,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -47,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -118,7 +119,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -53,8 +53,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -136,7 +136,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -53,8 +53,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -127,7 +127,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -53,8 +53,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -131,7 +131,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, err
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"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/googleapis/genai-toolbox/internal/util/parameters"
@@ -49,8 +50,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -128,7 +129,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
logger.DebugContext(ctx, "params = ", params)
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -50,8 +50,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -139,7 +139,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, fmt.Errorf("error building query request: %w", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -49,8 +49,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -123,7 +123,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if err != nil {
return nil, fmt.Errorf("error building WriteQuery request: %w", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -122,7 +122,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if err != nil {
return nil, fmt.Errorf("error building query request: %w", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -48,8 +48,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -135,7 +135,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
visConfig := paramsMap["vis_config"].(map[string]any)
wq.VisConfig = &visConfig
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -50,8 +50,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -129,7 +129,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
dashboard_id := paramsMap["dashboard_id"].(string)
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -49,8 +49,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
@@ -132,7 +132,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
limit := int64(paramsMap["limit"].(int))
limitStr := fmt.Sprintf("%d", limit)
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -47,8 +47,8 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
type compatibleSource interface {
UseClientAuthorization() bool
GetAuthTokenHeaderName() string
LookerClient() *v4.LookerSDK
LookerApiSettings() *rtl.ApiSettings
GetLookerSDK(string) (*v4.LookerSDK, error)
}
type Config struct {
Name string `yaml:"name" validate:"required"`
@@ -117,7 +117,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return nil, err
}
sdk, err := source.GetLookerSDK(string(accessToken))
sdk, err := lookercommon.GetLookerSDK(source.UseClientAuthorization(), source.LookerApiSettings(), source.LookerClient(), accessToken)
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
}

View File

@@ -478,9 +478,13 @@ func (ps Parameters) McpManifest() (McpToolsSchema, map[string][]string) {
for _, p := range ps {
name := p.GetName()
paramManifest, authParamList := p.McpManifest()
defaultV := p.GetDefault()
if defaultV != nil {
paramManifest.Default = defaultV
}
properties[name] = paramManifest
// parameters that doesn't have a default value are added to the required field
if CheckParamRequired(p.GetRequired(), p.GetDefault()) {
if CheckParamRequired(p.GetRequired(), defaultV) {
required = append(required, name)
}
if len(authParamList) > 0 {
@@ -502,6 +506,7 @@ type ParameterManifest struct {
Description string `json:"description"`
AuthServices []string `json:"authSources"`
Items *ParameterManifest `json:"items,omitempty"`
Default any `json:"default,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty"`
EmbeddedBy string `json:"embeddedBy,omitempty"`
}
@@ -511,6 +516,7 @@ type ParameterMcpManifest struct {
Type string `json:"type"`
Description string `json:"description"`
Items *ParameterMcpManifest `json:"items,omitempty"`
Default any `json:"default,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty"`
}
@@ -788,6 +794,7 @@ func (p *StringParameter) Manifest() ParameterManifest {
Required: r,
Description: p.Desc,
AuthServices: authServiceNames,
Default: p.GetDefault(),
}
}
@@ -946,6 +953,7 @@ func (p *IntParameter) Manifest() ParameterManifest {
Required: r,
Description: p.Desc,
AuthServices: authServiceNames,
Default: p.GetDefault(),
}
}
@@ -1102,6 +1110,7 @@ func (p *FloatParameter) Manifest() ParameterManifest {
Required: r,
Description: p.Desc,
AuthServices: authServiceNames,
Default: p.GetDefault(),
}
}
@@ -1235,6 +1244,7 @@ func (p *BooleanParameter) Manifest() ParameterManifest {
Required: r,
Description: p.Desc,
AuthServices: authServiceNames,
Default: p.GetDefault(),
}
}
@@ -1430,6 +1440,7 @@ func (p *ArrayParameter) Manifest() ParameterManifest {
Description: p.Desc,
AuthServices: authServiceNames,
Items: &items,
Default: p.GetDefault(),
}
}
@@ -1675,7 +1686,10 @@ func (p *MapParameter) Manifest() ParameterManifest {
// If no valueType is given, allow any properties.
additionalProperties = true
}
var defaultV any
if p.Default != nil {
defaultV = *p.Default
}
return ParameterManifest{
Name: p.Name,
Type: "object",
@@ -1683,6 +1697,7 @@ func (p *MapParameter) Manifest() ParameterManifest {
Description: p.Desc,
AuthServices: authServiceNames,
AdditionalProperties: additionalProperties,
Default: defaultV,
}
}

View File

@@ -1625,22 +1625,22 @@ func TestParamManifest(t *testing.T) {
{
name: "string default",
in: parameters.NewStringParameterWithDefault("foo-string", "foo", "bar"),
want: parameters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
want: parameters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", Default: "foo", AuthServices: []string{}},
},
{
name: "int default",
in: parameters.NewIntParameterWithDefault("foo-int", 1, "bar"),
want: parameters.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", AuthServices: []string{}},
want: parameters.ParameterManifest{Name: "foo-int", Type: "integer", Required: false, Description: "bar", Default: 1, AuthServices: []string{}},
},
{
name: "float default",
in: parameters.NewFloatParameterWithDefault("foo-float", 1.1, "bar"),
want: parameters.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", AuthServices: []string{}},
want: parameters.ParameterManifest{Name: "foo-float", Type: "float", Required: false, Description: "bar", Default: 1.1, AuthServices: []string{}},
},
{
name: "boolean default",
in: parameters.NewBooleanParameterWithDefault("foo-bool", true, "bar"),
want: parameters.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", AuthServices: []string{}},
want: parameters.ParameterManifest{Name: "foo-bool", Type: "boolean", Required: false, Description: "bar", Default: true, AuthServices: []string{}},
},
{
name: "array default",
@@ -1650,6 +1650,7 @@ func TestParamManifest(t *testing.T) {
Type: "array",
Required: false,
Description: "bar",
Default: []any{"foo", "bar"},
AuthServices: []string{},
Items: &parameters.ParameterManifest{Name: "foo-string", Type: "string", Required: false, Description: "bar", AuthServices: []string{}},
},
@@ -1841,7 +1842,7 @@ func TestMcpManifest(t *testing.T) {
wantSchema: parameters.McpToolsSchema{
Type: "object",
Properties: map[string]parameters.ParameterMcpManifest{
"foo-string": {Type: "string", Description: "bar"},
"foo-string": {Type: "string", Description: "bar", Default: "foo"},
"foo-string2": {Type: "string", Description: "bar"},
"foo-string3-auth": {Type: "string", Description: "bar"},
"foo-int2": {Type: "integer", Description: "bar"},

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "io.github.googleapis/genai-toolbox",
"description": "MCP Toolbox for Databases enables your agent to connect to your database.",
"title": "MCP Toolbox",
@@ -14,11 +14,11 @@
"url": "https://github.com/googleapis/genai-toolbox",
"source": "github"
},
"version": "0.24.0",
"version": "0.25.0",
"packages": [
{
"registryType": "oci",
"identifier": "us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:0.24.0",
"identifier": "us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:0.25.0",
"transport": {
"type": "streamable-http",
"url": "http://{host}:{port}/mcp"

View File

@@ -194,7 +194,7 @@ func runAlloyDBToolGetTest(t *testing.T) {
"description": "Simple tool to test end to end functionality.",
"parameters": []any{
map[string]any{"name": "project", "type": "string", "description": "The GCP project ID to list clusters for.", "required": true, "authSources": []any{}},
map[string]any{"name": "location", "type": "string", "description": "Optional: The location to list clusters in (e.g., 'us-central1'). Use '-' to list clusters across all locations.(Default: '-')", "required": false, "authSources": []any{}},
map[string]any{"name": "location", "type": "string", "description": "Optional: The location to list clusters in (e.g., 'us-central1'). Use '-' to list clusters across all locations.(Default: '-')", "required": false, "default": "-", "authSources": []any{}},
},
"authRequired": []any{},
},

View File

@@ -431,6 +431,7 @@ func TestLooker(t *testing.T) {
"description": "The filters for the query",
"name": "filters",
"required": false,
"default": map[string]any{},
"type": "object",
},
map[string]any{
@@ -446,6 +447,7 @@ func TestLooker(t *testing.T) {
"name": "pivots",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -460,6 +462,7 @@ func TestLooker(t *testing.T) {
"name": "sorts",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -467,6 +470,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(500),
},
map[string]any{
"authSources": []any{},
@@ -519,6 +523,7 @@ func TestLooker(t *testing.T) {
"description": "The filters for the query",
"name": "filters",
"required": false,
"default": map[string]any{},
"type": "object",
},
map[string]any{
@@ -534,6 +539,7 @@ func TestLooker(t *testing.T) {
"name": "pivots",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -548,6 +554,7 @@ func TestLooker(t *testing.T) {
"name": "sorts",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -555,6 +562,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(500),
},
map[string]any{
"authSources": []any{},
@@ -607,6 +615,7 @@ func TestLooker(t *testing.T) {
"description": "The filters for the query",
"name": "filters",
"required": false,
"default": map[string]any{},
"type": "object",
},
map[string]any{
@@ -622,6 +631,7 @@ func TestLooker(t *testing.T) {
"name": "pivots",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -636,6 +646,7 @@ func TestLooker(t *testing.T) {
"name": "sorts",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -643,6 +654,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(500),
},
map[string]any{
"authSources": []any{},
@@ -658,6 +670,7 @@ func TestLooker(t *testing.T) {
"name": "vis_config",
"required": false,
"type": "object",
"default": map[string]any{},
},
},
},
@@ -675,6 +688,7 @@ func TestLooker(t *testing.T) {
"name": "title",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -682,6 +696,7 @@ func TestLooker(t *testing.T) {
"name": "desc",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -689,6 +704,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(100),
},
map[string]any{
"authSources": []any{},
@@ -696,6 +712,7 @@ func TestLooker(t *testing.T) {
"name": "offset",
"required": false,
"type": "integer",
"default": float64(0),
},
},
},
@@ -741,6 +758,7 @@ func TestLooker(t *testing.T) {
"description": "The filters for the query",
"name": "filters",
"required": false,
"default": map[string]any{},
"type": "object",
},
map[string]any{
@@ -756,6 +774,7 @@ func TestLooker(t *testing.T) {
"name": "pivots",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -770,6 +789,7 @@ func TestLooker(t *testing.T) {
"name": "sorts",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -777,6 +797,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(500),
},
map[string]any{
"authSources": []any{},
@@ -797,6 +818,7 @@ func TestLooker(t *testing.T) {
"description": "The description of the Look",
"name": "description",
"required": false,
"default": "",
"type": "string",
},
map[string]any{
@@ -804,6 +826,7 @@ func TestLooker(t *testing.T) {
"description": "The folder id where the Look will be created. Leave blank to use the user's personal folder",
"name": "folder",
"required": false,
"default": "",
"type": "string",
},
map[string]any{
@@ -813,6 +836,7 @@ func TestLooker(t *testing.T) {
"name": "vis_config",
"required": false,
"type": "object",
"default": map[string]any{},
},
},
},
@@ -830,6 +854,7 @@ func TestLooker(t *testing.T) {
"name": "title",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -837,6 +862,7 @@ func TestLooker(t *testing.T) {
"name": "desc",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -844,6 +870,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(100),
},
map[string]any{
"authSources": []any{},
@@ -851,6 +878,7 @@ func TestLooker(t *testing.T) {
"name": "offset",
"required": false,
"type": "integer",
"default": float64(0),
},
},
},
@@ -875,6 +903,7 @@ func TestLooker(t *testing.T) {
"name": "description",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -882,6 +911,7 @@ func TestLooker(t *testing.T) {
"name": "folder",
"required": false,
"type": "string",
"default": "",
},
},
},
@@ -920,6 +950,7 @@ func TestLooker(t *testing.T) {
"name": "filter_type",
"required": false,
"type": "string",
"default": "field_filter",
},
map[string]any{
"authSources": []any{},
@@ -955,6 +986,7 @@ func TestLooker(t *testing.T) {
"name": "allow_multiple_values",
"required": false,
"type": "boolean",
"default": true,
},
map[string]any{
"authSources": []any{},
@@ -962,6 +994,7 @@ func TestLooker(t *testing.T) {
"name": "required",
"required": false,
"type": "boolean",
"default": false,
},
},
},
@@ -1007,6 +1040,7 @@ func TestLooker(t *testing.T) {
"description": "The filters for the query",
"name": "filters",
"required": false,
"default": map[string]any{},
"type": "object",
},
map[string]any{
@@ -1022,6 +1056,7 @@ func TestLooker(t *testing.T) {
"name": "pivots",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -1036,6 +1071,7 @@ func TestLooker(t *testing.T) {
"name": "sorts",
"required": false,
"type": "array",
"default": []any{},
},
map[string]any{
"authSources": []any{},
@@ -1043,6 +1079,7 @@ func TestLooker(t *testing.T) {
"name": "limit",
"required": false,
"type": "integer",
"default": float64(500),
},
map[string]any{
"authSources": []any{},
@@ -1064,6 +1101,7 @@ func TestLooker(t *testing.T) {
"name": "title",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"additionalProperties": true,
@@ -1072,6 +1110,7 @@ func TestLooker(t *testing.T) {
"name": "vis_config",
"required": false,
"type": "object",
"default": map[string]any{},
},
map[string]any{
"authSources": []any{},
@@ -1083,6 +1122,7 @@ func TestLooker(t *testing.T) {
"name": "dashboard_filter",
"required": false,
"type": "object",
"default": map[string]any{},
},
"name": "dashboard_filters",
"required": false,
@@ -1181,6 +1221,7 @@ func TestLooker(t *testing.T) {
"name": "timeframe",
"required": false,
"type": "integer",
"default": float64(90),
},
map[string]any{
"authSources": []any{},
@@ -1188,6 +1229,7 @@ func TestLooker(t *testing.T) {
"name": "min_queries",
"required": false,
"type": "integer",
"default": float64(0),
},
},
},
@@ -1212,6 +1254,7 @@ func TestLooker(t *testing.T) {
"name": "project",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -1219,6 +1262,7 @@ func TestLooker(t *testing.T) {
"name": "model",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -1226,6 +1270,7 @@ func TestLooker(t *testing.T) {
"name": "explore",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -1233,6 +1278,7 @@ func TestLooker(t *testing.T) {
"name": "timeframe",
"required": false,
"type": "integer",
"default": float64(90),
},
map[string]any{
"authSources": []any{},
@@ -1240,6 +1286,7 @@ func TestLooker(t *testing.T) {
"name": "min_queries",
"required": false,
"type": "integer",
"default": float64(1),
},
},
},
@@ -1257,6 +1304,7 @@ func TestLooker(t *testing.T) {
"name": "devMode",
"required": false,
"type": "boolean",
"default": true,
},
},
},
@@ -1410,6 +1458,7 @@ func TestLooker(t *testing.T) {
"name": "type",
"required": false,
"type": "string",
"default": "",
},
map[string]any{
"authSources": []any{},
@@ -1417,6 +1466,7 @@ func TestLooker(t *testing.T) {
"name": "id",
"required": false,
"type": "string",
"default": "",
},
},
},

View File

@@ -165,6 +165,7 @@ func TestNeo4jToolEndpoints(t *testing.T) {
"type": "boolean",
"required": false,
"description": "If set to true, the query will be validated and information about the execution will be returned without running the query. Defaults to false.",
"default": false,
"authSources": []any{},
},
},