Compare commits

..

190 Commits

Author SHA1 Message Date
Harsh Jha
baa070d631 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-23 16:19:29 +05:30
Harsh Jha
2a49d93c31 Merge branch 'main' into sts-python-ci 2025-09-23 16:14:40 +05:30
Harsh Jha
e2147d0623 chore: removed deps.json variable from scirpt 2025-09-23 16:14:18 +05:30
Harsh Jha
cdf648c83f chore: removed deps.json 2025-09-23 16:13:40 +05:30
Harsh Jha
db3abc67b5 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-23 15:09:48 +05:30
Harsh Jha
fb524b91e0 Merge branch 'main' into sts-python-ci 2025-09-23 15:09:01 +05:30
Harsh Jha
48ed91c8d1 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-19 14:13:53 +05:30
Harsh Jha
73e196d3e6 nit: renamed files 2025-09-19 14:12:19 +05:30
Harsh Jha
2c9a27f24f Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-19 11:59:38 +05:30
Harsh Jha
36b9d1ac3c chore: revamped folder structure 2025-09-19 11:58:49 +05:30
Harsh Jha
4dc9cf8e69 Merge branch 'main' into sts-python-ci 2025-09-19 11:50:44 +05:30
Harsh Jha
d11b83a7fe Merge branch 'main' into sts-python-ci 2025-09-19 10:49:33 +05:30
Harsh Jha
beef5d0e22 Merge branch 'main' into sts-python-ci 2025-09-18 14:17:12 +05:30
Harsh Jha
dcaac13994 Merge branch 'main' into sts-python-ci 2025-09-18 12:36:28 +05:30
Harsh Jha
e67839b23c Merge branch 'main' into sts-python-ci 2025-09-18 12:36:06 +05:30
Twisha Bansal
93a591036d Update run_py_quickstart_tests.sh 2025-09-18 10:33:49 +05:30
Harsh Jha
02755b8799 Merge branch 'main' into sts-python-ci 2025-09-17 19:57:34 +05:30
Harsh Jha
f3808a57e1 chore: removed cloudsql public ip 2025-09-17 14:24:15 +05:30
Harsh Jha
8b26eea73c Merge branch 'main' into sts-python-ci 2025-09-17 14:14:06 +05:30
Harsh Jha
46fb0e9c07 chore: reverted unnecessary changes 2025-09-17 12:45:48 +05:30
Harsh Jha
f12f1aa3bf Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-17 12:26:49 +05:30
Harsh Jha
6c35298be4 chore: rename script file 2025-09-17 12:24:42 +05:30
Harsh Jha
41f34dcf65 Merge branch 'main' into sts-python-ci 2025-09-17 12:12:10 +05:30
Harsh Jha
0fb0cc42f1 Merge branch 'main' into sts-python-ci 2025-09-17 12:01:46 +05:30
Harsh Jha
3b34766283 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-17 11:43:26 +05:30
Harsh Jha
01667447f7 Merge branch 'main' into sts-python-ci 2025-09-17 11:35:57 +05:30
Harsh Jha
8d18e6c835 Update .ci/quickstart_py.integration.cloudbuild.yaml
Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-16 23:15:12 +05:30
Harsh Jha
6b2cf28243 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-16 17:23:41 +05:30
Harsh Jha
c581a2c48e chore: addressed pr comments 2025-09-16 17:20:40 +05:30
Harsh Jha
d186003456 Merge branch 'sts-python-test' of https://github.com/googleapis/genai-toolbox into sts-python-ci 2025-09-16 17:12:51 +05:30
Harsh Jha
cad7305209 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-ci 2025-09-16 17:12:24 +05:30
Harsh Jha
4fd4387faf Update .ci/quickstart_py.sh
Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-16 17:11:35 +05:30
Harsh Jha
0e2541f1c4 chore: added header for test file 2025-09-16 17:05:47 +05:30
Harsh Jha
aa8a886a76 Merge branch 'main' into sts-python-test 2025-09-16 17:02:46 +05:30
Harsh Jha
42e8cd4c07 cloudsqlproxy polling mehcanism 2025-09-16 16:49:03 +05:30
Harsh Jha
2a4bfb1045 nc 2025-09-16 16:42:30 +05:30
Harsh Jha
9cc85125b2 nc 2025-09-16 16:37:46 +05:30
Harsh Jha
81365831b5 netcat 2025-09-16 16:33:03 +05:30
Harsh Jha
3a8911fe96 netcat 2025-09-16 16:29:45 +05:30
Harsh Jha
05834f1cbf testing nc 2025-09-16 16:25:23 +05:30
Harsh Jha
4b94c0bf3e nc version 2025-09-16 16:20:56 +05:30
Harsh Jha
1a51a4c6ed Update netcat version to 1.10-46 2025-09-16 15:02:02 +05:30
Harsh Jha
17a845e10d Update netcat version to 1.10-46 2025-09-16 14:59:50 +05:30
Harsh Jha
f9cf22a824 chore: removed public ip connection method for cloudsql 2025-09-16 14:56:05 +05:30
Harsh Jha
b00f2b254c chore: added public ip of cloudsql 2025-09-16 14:41:23 +05:30
Harsh Jha
0450b83438 chore: removed cloudsql proxy 2025-09-16 13:04:19 +05:30
Harsh Jha
3d072c88ac chore: removed cloudsql proxy 2025-09-16 13:01:38 +05:30
Harsh Jha
a694b71331 chore: removed create table and substitution 2025-09-16 12:24:05 +05:30
Harsh Jha
8a316ecf4b chore: testing moving substitution at the end 2025-09-16 12:16:04 +05:30
Harsh Jha
e3842651c1 Merge branch 'sts-python-test' of https://github.com/googleapis/genai-toolbox into sts-python-ci 2025-09-16 12:02:17 +05:30
Harsh Jha
9315428714 Merge branch 'main' into sts-python-test 2025-09-16 11:58:59 +05:30
Harsh Jha
88d18ef21e testing: ci setup 2025-09-16 11:35:47 +05:30
Harsh Jha
9875d94fde applying changes for gettextbase 2025-09-15 21:37:40 +05:30
Harsh Jha
20e50ee003 update: gettext-base version 2025-09-15 21:32:49 +05:30
Harsh Jha
d8ad68b16c chore: jq not found 2025-09-15 21:26:57 +05:30
Harsh Jha
674372b774 chore: rename variable 2025-09-15 21:25:24 +05:30
Harsh Jha
af6f42ab0d chore: testing json format for requirement 2025-09-15 21:23:44 +05:30
Harsh Jha
4e082ea6ac chore: fix of cloudsql version variable 2025-09-15 20:59:06 +05:30
Harsh Jha
611789b440 chore: testing for script requirement file 2025-09-15 20:55:33 +05:30
Harsh Jha
ab9213c78a Update docs/en/getting-started/quickstart/python/__init__.py
Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-15 20:52:57 +05:30
Harsh Jha
3b81df3b33 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-15 15:39:08 +05:30
Harsh Jha
c9d6a29ab1 Merge branch 'sts-python-test' of https://github.com/googleapis/genai-toolbox into sts-python-ci 2025-09-15 15:37:48 +05:30
Harsh Jha
531df33c85 Merge branch 'main' into sts-python-test 2025-09-15 15:36:59 +05:30
Harsh Jha
a6125f0116 test: add dynamic module import and initialization for orchestration 2025-09-15 15:36:28 +05:30
Harsh Jha
f6a0a17c91 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-15 13:17:57 +05:30
Harsh Jha
2cc43faa7d ci: python sample testing 2025-09-15 13:17:23 +05:30
Harsh Jha
a62e59e8de chore: testing simplying script file 2025-09-15 13:09:00 +05:30
Harsh Jha
d4b98918d6 chore: fix version of tools secret 2025-09-15 12:58:22 +05:30
Harsh Jha
d8d7c3e323 chore: testing approaches for sql file 2025-09-15 12:45:24 +05:30
Harsh Jha
67b829d8da Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-15 12:37:12 +05:30
Harsh Jha
4b271cd196 chore: added gettest package 2025-09-15 12:36:33 +05:30
Harsh Jha
d8c5d1eb63 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-15 12:29:57 +05:30
Harsh Jha
c143427e85 chore: fix if statement is script file 2025-09-15 12:29:27 +05:30
Harsh Jha
85d4b26dd3 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-15 12:25:41 +05:30
Harsh Jha
9f11962714 chore: fix table creation 2025-09-15 12:25:05 +05:30
Harsh Jha
c0cc26b709 fix: comments 2025-09-15 12:14:59 +05:30
Harsh Jha
78cdf94700 Merge branch 'sts-python-test' of https://github.com/googleapis/genai-toolbox into sts-python-ci 2025-09-15 12:05:49 +05:30
Harsh Jha
29fbeef928 chore: removed unnecessary changes 2025-09-15 12:04:22 +05:30
Harsh Jha
0a79f2a116 chore: removed extra file 2025-09-15 11:56:24 +05:30
Harsh Jha
7ae06781e7 chore(python/quickstart): address multiple review comments in sample code 2025-09-15 11:56:24 +05:30
Harsh Jha
1ef9d1ff08 feat: Implement language-agnostic snippet shortcode 2025-09-15 11:56:23 +05:30
Harsh Jha
cf677a23d0 refactor(test): update quickstart tests to only check for error-free execution 2025-09-15 11:56:23 +05:30
Harsh Jha
0c0204cda4 refactor(docs): Replace duplicated content with a shared shortcode 2025-09-15 11:56:23 +05:30
Harsh Jha
3fb36e3fa5 feat: Add shortcode for including file regions 2025-09-15 11:56:23 +05:30
Harsh Jha
e07e5c8d0f refactor: ci setup for python sample 2025-09-15 11:35:08 +05:30
Ajaykumar Yadav
82ae0fbc76 feat(prebuilt/sqlite): prebuilt tools for the sqlite. (#1227)
## Description
Prebuilt tools for the sqlite
- [x] `list_table` with simple and detailed(trigger,index,column) for
each table
- [x]  `execute-sql` for executing any SQL statement for sqlite.
- [x]  added tests and done required changes in config.
- [x] **Documentation update**:Done

## PR Checklist

- [x] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involves a breaking change

🛠️ Fixes: https://github.com/googleapis/genai-toolbox/issues/1226

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:08 +05:30
Huan Chen
f692cb7aa8 chore: add usage tracker for bigquery-conversational-analytics (#1442)
## Description

---
Add client_id_enum for usage tracking.

## 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: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-09-15 11:35:08 +05:30
Huan Chen
36e0f32ee5 chore: add retry to flaky integration tests in conversational analytics api (#1430)
## Description

Added retries to bigquery-conversational-analytics api tests for timeout
error(25s in test) and service unavailable error(code 503). Other errors
will not retry.

## 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>
2025-09-15 11:35:08 +05:30
Wenxin Du
19364a6108 feat(tools/bigquery): Add useClientOAuth to BigQuery prebuilt source config (#1431)
Allow users to set `useClientOAuth` in the BQ prebuilt config
2025-09-15 11:35:08 +05:30
prernakakkar-google
079259ccb7 feat(prebuilt/cloudsql): Add create user tool for cloud sql (#1406)
## Description

---
This pull request introduces a new tool, cloud-sql-create-users, which
allows for the creation of both built-in and IAM users in a Cloud SQL
instance.
<img width="518" height="846" alt="image"
src="https://github.com/user-attachments/assets/2f96f0be-658b-46d1-9de6-f47db2804274"
/>
<img width="518" height="956" alt="image"
src="https://github.com/user-attachments/assets/2a7d80d4-eab2-4e91-b08b-5fb78c150319"
/>


## 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
- [ ] Ensure the tests and linter pass
- [ ] 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 #<issue_number_goes_here>
2025-09-15 11:35:08 +05:30
prernakakkar-google
68ffa75817 feat(prebuilt/cloudsql): Add cloud-sql-get-instances tool (#1383)
## Description

---
This pull request introduces the `cloud-sql-get-instances` tool, which
enables users to retrieve detailed information about a specified Cloud
SQL instance. This tool enhances the toolbox by providing a direct and
authenticated way to interact with the Google Cloud SQL Admin API.
Authentication is handled automatically by generating a bearer token
from the environment's Application Default Credentials with the
`https://www.googleapis.com/auth/sqlservice.admin` scope.


<img width="282" height="1064" alt="image"
src="https://github.com/user-attachments/assets/253d3939-7de2-4324-bc2b-8a2eb20eb133"
/>



####


## 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 - Tracked internally
- [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 #<issue_number_goes_here>
2025-09-15 11:35:08 +05:30
prernakakkar-google
8560dfb597 feat(prebuilt/cloudsql): Add list instances tool for cloudsql (#1310)
## Description
---
This change introduces a new tool, `cloudsqllistinstance`, to the
`cloudsql` toolset. This tool allows users to list all Cloud SQL
instances within a specified GCP project.

The implementation includes:

- The `cloudsqllistinstance` tool definition and logic, which makes an
authenticated GET request to the `sqladmin.googleapis.com` API.
- The tool takes a single required parameter: `project`.
<img width="654" height="1406" alt="image"
src="https://github.com/user-attachments/assets/7c129a54-acb7-4695-9a0b-215914a6a273"
/>



## PR Checklist
---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:
- [x] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea - tracked internally
- [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 #<issue_number_goes_here>
2025-09-15 11:35:08 +05:30
Sri Varshitha
ff5442e273 feat(tools/alloydb-get-cluster): Add get-cluster tool for alloydb (#1420)
## Description

This pull request introduces a new custom tool kind
`alloydb-get-cluster` that retrieves detailed information for a specific
AlloyDB cluster.

### Example Configuration

```yaml
tools:
  get_cluster:
    kind: alloydb-get-cluster
    source: alloydb-admin-source
    description: Use this tool to retrieve detailed information for a specific AlloyDB cluster.
```

### Example Request
``` 
curl -X POST http://127.0.0.1:5000/api/tool/get_cluster/invoke \
-H "Content-Type: application/json" \
-d '{
    "projectId": "example-project",
    "locationId": "us-central1",
    "clusterId": "my-alloydb-cluster",
}'
```
## 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 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>
2025-09-15 11:35:07 +05:30
Sri Varshitha
bed5eaff32 feat(tools/alloydb-list-instances): Add custom tool kind for AlloyDB list_instances (#1357)
## Description

---
This pull request introduces a new custom tool kind
`alloydb-list-instances` that allows users to list the AlloyDB instances
in a given project, cluster and location.

### Example Configuration

```yaml
tools:
  list_instances:
    kind: alloydb-list-instances
    source: alloydb-admin-source
    description: Use this tool to list all AlloyDB instances for a given project, cluster and location.
```

### Example Request
``` 
curl -X POST http://127.0.0.1:5000/api/tool/list_instances/invoke \
-H "Content-Type: application/json" \
-d '{
    "projectId": "example-project",
    "locationId": "us-central1",
    "clusterId": "example-cluster",
}'
```

## 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 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>
2025-09-15 11:35:07 +05:30
Sri Varshitha
3baee129d7 feat(tools/alloydb-list-users): Add new custom tool kind for AlloyDB list_users (#1377)
## Description

---
This pull request introduces a new custom tool kind `alloydb-list-users`
that allows users to list all database users within an AlloyDB cluster.

### Example Configuration

```yaml
tools:
  list_users:
    kind: alloydb-list-users
    source: alloydb-admin-source
    description: Use this tool to list all database users within an AlloyDB cluster
```

### Example Request
``` 
curl -X POST http://127.0.0.1:5000/api/tool/list_users/invoke \
-H "Content-Type: application/json" \
-d '{
    "projectId": "example-project",
    "locationId": "us-central1",
    "clusterId": "example-cluster",
}'
```
## 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 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>

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:07 +05:30
Manu Paliwal
85f3154671 feat(observability): add cloud sql observability tools (#1425)
## Description
  ---
> This PR introduces a new observability prebuilt configs that allows
fetching system and query level metrics from the Cloud Monitoring API
for CloudSQL PG, MySQL and SQLServer.
  >
  > The key changes include:
> - 3 new configs using the cloud-monitoring-query-prometheus tool and
source cloud-monitoring
> - Manual testing is also done by locally running the server/tools and
integrating with Gemini CLI

 ## Followup Changes
  ---
   > - Documentation around the tools.

 ## PR Checklist
  ---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
  > few things you can do to make sure it goes smoothly:
   - [x] Make sure you reviewed
CONTRIBUTING.md
(https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
   - [x] Make sure to open an issue as a
bug/issue
(https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
    designs, and agree on the general idea
   - [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
   - [] Appropriate docs were updated (if necessary)
   - [ ] Make sure to add ! if this involve a breaking change

---------

Co-authored-by: prernakakkar-google <158031829+prernakakkar-google@users.noreply.github.com>
2025-09-15 11:35:07 +05:30
prernakakkar-google
cce2d5e0bc feat(tool/cloudsql): Add cloud sql wait for operation tool with exponential backoff (#1306)
## Description
---
This pull request introduces a new tool, `cloudsql-wait-for-operation`,
to improve the handling of long-running operations in Google Cloud SQL.

__Key Features:__

- __Asynchronous Operation Polling:__ The `cloudsql-wait-for-operation`
tool polls the Cloud SQL operations API at a specified interval until
the operation completes or fails. This is essential for managing
asynchronous tasks like instance and database creation, which can take
several minutes.
- __Configurable Retries:__ The tool includes configurable retry logic
with exponential backoff (`delay`, `maxDelay`, `multiplier`,
`maxRetries`) to handle transient network issues and make the polling
mechanism more resilient.
- __Improved User Experience:__ By waiting for operations to complete,
this tool provides a more synchronous-like experience for the user, who
can be confident that a resource is ready before the next step in a
workflow is executed.

Tested:
<img width="592" height="1118" alt="image"
src="https://github.com/user-attachments/assets/fd64d367-0fba-4d6a-a6f1-8fc642132208"
/>



## PR Checklist
---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:
- [X] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [X] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea - (Internal bug)
- [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 #<issue_number_goes_here>
2025-09-15 11:35:07 +05:30
Mend Renovate
bf57e26736 chore(deps): update module google.golang.org/genai to v1.24.0 (#1417)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[google.golang.org/genai](https://redirect.github.com/googleapis/go-genai)
| `v1.23.0` -> `v1.24.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fgenai/v1.24.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fgenai/v1.23.0/v1.24.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>googleapis/go-genai (google.golang.org/genai)</summary>

###
[`v1.24.0`](https://redirect.github.com/googleapis/go-genai/releases/tag/v1.24.0)

[Compare
Source](https://redirect.github.com/googleapis/go-genai/compare/v1.23.0...v1.24.0)

##### Features

- \[Python] Implement async embedding batches for MLDev.
([f32fb26](f32fb26a12))
- Add labels to create tuning job config
([c13a2a5](c13a2a5f68))
- generate the function\_call class's converters
([995a3ac](995a3acc0a))
- Support Veo 2 Editing on Vertex
([7fd6940](7fd694074b))

##### Bug Fixes

- Enable `id` field in `FunctionCall` for Vertex AI.
([a3f3c2b](a3f3c2b37e))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-15 11:35:07 +05:30
Mend Renovate
e9410556bf chore(deps): update module cloud.google.com/go/dataplex to v1.27.0 (#1429)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[cloud.google.com/go/dataplex](https://redirect.github.com/googleapis/google-cloud-go)
| `v1.26.0` -> `v1.27.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/cloud.google.com%2fgo%2fdataplex/v1.27.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/cloud.google.com%2fgo%2fdataplex/v1.26.0/v1.27.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
2025-09-15 11:35:07 +05:30
Mend Renovate
627734ff01 chore(deps): update module github.com/redis/go-redis/v9 to v9.14.0 (#1405)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[github.com/redis/go-redis/v9](https://redirect.github.com/redis/go-redis)
| `v9.13.0` -> `v9.14.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fredis%2fgo-redis%2fv9/v9.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fredis%2fgo-redis%2fv9/v9.13.0/v9.14.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>redis/go-redis (github.com/redis/go-redis/v9)</summary>

###
[`v9.14.0`](https://redirect.github.com/redis/go-redis/releases/tag/v9.14.0):
9.14.0

[Compare
Source](https://redirect.github.com/redis/go-redis/compare/v9.13.0...v9.14.0)

#### Highlights

- Added batch process method to the pipeline
([#&#8203;3510](https://redirect.github.com/redis/go-redis/pull/3510))

### Changes

#### 🚀 New Features

- Added batch process method to the pipeline
([#&#8203;3510](https://redirect.github.com/redis/go-redis/pull/3510))

#### 🐛 Bug Fixes

- fix: SetErr on Cmd if the command cannot be queued correctly in
multi/exec
([#&#8203;3509](https://redirect.github.com/redis/go-redis/pull/3509))

#### 🧰 Maintenance

- Updates release drafter config to exclude dependabot
([#&#8203;3511](https://redirect.github.com/redis/go-redis/pull/3511))
- chore(deps): bump actions/setup-go from 5 to 6
([#&#8203;3504](https://redirect.github.com/redis/go-redis/pull/3504))

#### Contributors

We'd like to thank all the contributors who worked on this release!

[@&#8203;elena-kolevska](https://redirect.github.com/elena-kolevksa),
[@&#8203;htemelski-redis](https://redirect.github.com/htemelski-redis)
and [@&#8203;ndyakov](https://redirect.github.com/ndyakov)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-09-15 11:35:07 +05:30
Twisha Bansal
c11bb06cb6 fix: update mcp implementation comments (#1285)
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-09-15 11:35:07 +05:30
Yuan Teoh
f8d03bd974 docs: fix broken links in python quickstart (#1427) 2025-09-15 11:35:07 +05:30
Sri Varshitha
21bdaf4045 feat(tools/alloydb-list-cluster): Add custom tool kind for AlloyDB list_clusters (#1319)
## Description
---
This pull request introduces a new custom tool kind
`alloydb-list-clusters` that lists all AlloyDB clusters in a given
project and location.

### Example Configuration

```yaml
tools:
  list_clusters:
    kind: alloydb-list-clusters
    source: alloydb-admin-source
    description: Use this tool to list all AlloyDB clusters in a given project and location.
```

### Example Request
``` 
curl -X POST http://127.0.0.1:5000/api/tool/list_clusters/invoke \
-H "Content-Type: application/json" \
-d '{
    "projectId": "example-project",
    "locationId": "us-central1"
}'
```

## 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 as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:07 +05:30
Wenxin Du
f3f92eeac3 docs(bigquery): Update end-user OAuth doc (#1373)
Now all BQ tools support end-user credentials passthrough.
2025-09-15 11:35:07 +05:30
Pete Hampton
47302f313e feat(clickhouse): add list-databases tool (#1274)
## Description
---

Adds a tool to the list databases in a clickhouse cluster

<img width="1440" height="813" alt="Screenshot 2025-08-28 at 09 58 15"
src="https://github.com/user-attachments/assets/73643f5d-0c37-4e58-a81c-47bc3a2a5f8e"
/>


## PR Checklist
---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:
- [x] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>

---------

Co-authored-by: Pete Hampton <pjhampton@users.noreply.github.com>
Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:07 +05:30
Manu Paliwal
a30531a4e9 feat: Add cloud monitoring tool (#1311)
## Description
  ---
> This PR introduces a new observability tool that allows fetching
system and query level metrics from the Cloud Monitoring API for AlloyDB
instances.
  >
  > The key changes include:
> - A new observability tool that can make authenticated requests to the
monitoring API.
> - Pre-built configurations in observability.yaml for fetching AlloyDB
system and query metrics.
  > - Unit and integration tests for the new observability tool.
> - List of metrics is fetched from the public documentation available
https://cloud.google.com/alloydb/docs/reference/system-insights-metrics
> - Manual testing is also done by locally running the server/tools and
integrating with Gemini CLI

 ## Followup Changes
  ---
   > - Similar observability tools for Postgres, MySQL and SQLServer.
   > - Documentation around the tools.

 ## PR Checklist
  ---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
  > few things you can do to make sure it goes smoothly:
   - [x] Make sure you reviewed
CONTRIBUTING.md
(https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
   - [x] Make sure to open an issue as a
bug/issue
(https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
    designs, and agree on the general idea
   - [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
   - [] Appropriate docs were updated (if necessary)
   - [ ] Make sure to add ! if this involve a breaking change

Tested Locally:
<img width="4886" height="3068" alt="image"
src="https://github.com/user-attachments/assets/774d0776-6c91-48aa-8d1a-64d84cfdbc75"
/>

<img width="4752" height="2778" alt="image"
src="https://github.com/user-attachments/assets/839f099a-dd1a-410c-bbe4-9913603e5ff3"
/>
2025-09-15 11:35:06 +05:30
Sri Varshitha
3f21464d78 feat(tools/mysql-list-tables): Add new tool for mysql (#1287)
## Description
---
This pull request introduces a new custom tool kind `mysql-list-tables`
that allows users to list tables within a MySQL database.

### Example Configuration

```yaml
tools:
  mysql_list_tables:
    kind: mysql-list-tables
    source: mysql-source
    description: Use this tool to retrieve schema information for all or specified tables. Output format can be simple (only table names) or detailed.
```

### Example Request
``` 
curl -X POST http://127.0.0.1:5000/api/tool/mysql_list_tables/invoke \
-H "Content-Type: application/json" \
-d '{
    "table_names": "users",
    "output_format": "simple"
}'
```

## 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 as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involve a breaking change

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:06 +05:30
Sri Varshitha
45821cf73e feat(tools/postgres-list-tables): Add new tool postgres source (#1284)
## Description
---
This pull request introduces a new custom tool kind
`postgres-list-tables` that allows users to list tables within a
Postgres database.

### Example Configuration

```yaml
tools:
  postgres_list_tables:
    kind: postgres-list-tables
    source: postgres-source
    description: Use this tool to retrieve schema information for all or specified tables. Output format can be simple (only table names) or detailed.
```

### Example Request
``` 
curl -X POST http://127.0.0.1:5000/api/tool/postgres_list_tables/invoke \
-H "Content-Type: application/json" \
-d '{
    "table_names": "users",
    "output_format": "simple"
}'
```

## 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 as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involve a breaking change

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:06 +05:30
Sri Varshitha
a261188f4f feat: add alloydb admin source (#1369)
## Description

---
This pull request introduces a new source alloydb-admin which provides a
client for the Google Cloud AlloyDB API.

### Example Configuration

```yaml
sources
  alloydb_admin_source:
    kind: alloydb-admin
```

## 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 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>
2025-09-15 11:35:06 +05:30
prernakakkar-google
cd50e6b23b feat(source/cloudsqladmin): Add cloud sql admin source (#1408)
## Description

---
This PR introduces the `cloud-sql-admin` source, which provides a client
for the Google Cloud SQL Admin API.

```

sources:
  my-cloud-sql-admin:
    kind: cloud-sql-admin
```



## 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
- [ ] Ensure the tests and linter pass
- [ ] 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 #<issue_number_goes_here>
2025-09-15 11:35:06 +05:30
Yuan Teoh
5bc42045bb docs: add tools naming convention (#1414)
Add guidelines for MCP Toolbox tools naming properties: **tool name**
and **tool kind**.
2025-09-15 11:35:06 +05:30
Mend Renovate
01f795b071 chore(deps): update module google.golang.org/api to v0.249.0 (#1374)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[google.golang.org/api](https://redirect.github.com/googleapis/google-api-go-client)
| `v0.248.0` -> `v0.249.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fapi/v0.249.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fapi/v0.248.0/v0.249.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>googleapis/google-api-go-client
(google.golang.org/api)</summary>

###
[`v0.249.0`](https://redirect.github.com/googleapis/google-api-go-client/releases/tag/v0.249.0)

[Compare
Source](https://redirect.github.com/googleapis/google-api-go-client/compare/v0.248.0...v0.249.0)

##### Features

- **all:** Auto-regenerate discovery clients
([#&#8203;3281](https://redirect.github.com/googleapis/google-api-go-client/issues/3281))
([c03d56b](c03d56bd80))
- **all:** Auto-regenerate discovery clients
([#&#8203;3283](https://redirect.github.com/googleapis/google-api-go-client/issues/3283))
([4d1d336](4d1d336897))
- **all:** Auto-regenerate discovery clients
([#&#8203;3284](https://redirect.github.com/googleapis/google-api-go-client/issues/3284))
([d2b41b6](d2b41b6ebb))
- **all:** Auto-regenerate discovery clients
([#&#8203;3285](https://redirect.github.com/googleapis/google-api-go-client/issues/3285))
([bcde943](bcde9430e3))
- **all:** Auto-regenerate discovery clients
([#&#8203;3286](https://redirect.github.com/googleapis/google-api-go-client/issues/3286))
([27530dd](27530ddc51))
- **all:** Auto-regenerate discovery clients
([#&#8203;3288](https://redirect.github.com/googleapis/google-api-go-client/issues/3288))
([240e54a](240e54a474))
- **all:** Auto-regenerate discovery clients
([#&#8203;3289](https://redirect.github.com/googleapis/google-api-go-client/issues/3289))
([2bd4a72](2bd4a72fae))
- **all:** Auto-regenerate discovery clients
([#&#8203;3291](https://redirect.github.com/googleapis/google-api-go-client/issues/3291))
([d09c7d3](d09c7d319a))
- **all:** Auto-regenerate discovery clients
([#&#8203;3292](https://redirect.github.com/googleapis/google-api-go-client/issues/3292))
([7b2235b](7b2235b382))
- **all:** Auto-regenerate discovery clients
([#&#8203;3293](https://redirect.github.com/googleapis/google-api-go-client/issues/3293))
([2e60cd5](2e60cd5bcc))
- **all:** Auto-regenerate discovery clients
([#&#8203;3294](https://redirect.github.com/googleapis/google-api-go-client/issues/3294))
([162aba4](162aba409d))
- **all:** Auto-regenerate discovery clients
([#&#8203;3295](https://redirect.github.com/googleapis/google-api-go-client/issues/3295))
([e297a42](e297a42860))
- **all:** Auto-regenerate discovery clients
([#&#8203;3296](https://redirect.github.com/googleapis/google-api-go-client/issues/3296))
([f98f835](f98f8357e2))
- **all:** Auto-regenerate discovery clients
([#&#8203;3297](https://redirect.github.com/googleapis/google-api-go-client/issues/3297))
([ababe60](ababe603c1))
- **all:** Auto-regenerate discovery clients
([#&#8203;3299](https://redirect.github.com/googleapis/google-api-go-client/issues/3299))
([ad70b6e](ad70b6eee9))
- **all:** Auto-regenerate discovery clients
([#&#8203;3300](https://redirect.github.com/googleapis/google-api-go-client/issues/3300))
([4fcd5bb](4fcd5bbd71))
- **all:** Auto-regenerate discovery clients
([#&#8203;3301](https://redirect.github.com/googleapis/google-api-go-client/issues/3301))
([6db0561](6db056121d))
- **all:** Auto-regenerate discovery clients
([#&#8203;3302](https://redirect.github.com/googleapis/google-api-go-client/issues/3302))
([79b251a](79b251ae0f))
- **all:** Auto-regenerate discovery clients
([#&#8203;3303](https://redirect.github.com/googleapis/google-api-go-client/issues/3303))
([e93c8a8](e93c8a870b))
- **all:** Auto-regenerate discovery clients
([#&#8203;3305](https://redirect.github.com/googleapis/google-api-go-client/issues/3305))
([1ca0330](1ca0330e52))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45Ny4xMCIsInVwZGF0ZWRJblZlciI6IjQxLjk3LjEwIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-09-15 11:35:06 +05:30
Averi Kitsch
e9faa2ba57 chore: Add label for release candidate (#1413)
## 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:

- [ ] 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>
2025-09-15 11:35:06 +05:30
Haoming Chen
77ba31846f feat(tools/bigquery-analyze-contribution): Add analyze contribution tool (#1223)
This tool creates a contribution analysis model and use ml.get_insights
to get the results.

---------

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:06 +05:30
Niraj Nandre
bd28550fa3 feat(prebuilt/cloud-sql-mysql): Add env var support for IP Type (#1232) (#1347)
This PR fixes #1232 by allowing the `CLOUD_SQL_MYSQL_IP_TYPE`
environment variable to control the connection type for the
`cloud-sql-mysql` prebuilt tool, defaulting to "PUBLIC".

**Changes:**

* **`internal/prebuiltconfigs/tools/cloud-sql-mysql.yaml`:** Updated
`ipType` to use `${CLOUD_SQL_MYSQL_IP_TYPE:PUBLIC}`.
* **`internal/sources/cloudsqlmysql/cloud_sql_mysql.go`:** Removed
`validate:"required"` from the `IPType` field in the `Config` struct to
allow the default to be used when the env var is unset.
* **Documentation:** Updated relevant documentation to reflect the new
`CLOUD_SQL_MYSQL_IP_TYPE` environment variable usage.

**Testing:**

Built a local Docker image and tested on a GCE VM:

*   Setting `CLOUD_SQL_MYSQL_IP_TYPE="private"` connects via Private IP.
*   Setting `CLOUD_SQL_MYSQL_IP_TYPE="PUBLIC"` connects via Public IP.
*   Leaving `CLOUD_SQL_MYSQL_IP_TYPE` unset defaults to Public IP.

All tests initialized the toolbox successfully.

Fixes #1232



## 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)
- [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)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #1232

---------

Co-authored-by: Averi Kitsch <akitsch@google.com>
2025-09-15 11:35:06 +05:30
Huan Chen
bf5ff627ca feat(source/bigquery): Add support for datasets selection (#1313)
## Description
---
- bigquery Source: The source configuration now supports a new
allowedDatasets field, which defines the list of datasets the tools are
allowed to access.
- bigquery-list-table-ids: Now verifies that the requested dataset is in
the allowed datasets list before listing its tables. An error is
returned if access is not permitted.
## 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/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] 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: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2025-09-15 11:35:06 +05:30
Mend Renovate
6f17188207 chore(deps): update module google.golang.org/genai to v1.23.0 (#1361)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[google.golang.org/genai](https://redirect.github.com/googleapis/go-genai)
| `v1.21.0` -> `v1.23.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fgenai/v1.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fgenai/v1.21.0/v1.23.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>googleapis/go-genai (google.golang.org/genai)</summary>

###
[`v1.23.0`](https://redirect.github.com/googleapis/go-genai/releases/tag/v1.23.0)

[Compare
Source](https://redirect.github.com/googleapis/go-genai/compare/v1.22.0...v1.23.0)

##### Features

- Add resolution field for Gemini Developer API Veo 3 generation
([b6a989c](b6a989cdca))

###
[`v1.22.0`](https://redirect.github.com/googleapis/go-genai/releases/tag/v1.22.0)

[Compare
Source](https://redirect.github.com/googleapis/go-genai/compare/v1.21.0...v1.22.0)

##### Features

- add `sdkHttpResponse.headers` to \*Delete responses.
([ac0e763](ac0e7632e5))
- Add add\_watermark field for recontext\_image (Virtual Try-On, Product
Recontext)
([07b6f57](07b6f573b2))
- Add output\_gcs\_uri to Imagen upscale\_image
([c08d9f3](c08d9f35c3))
- Add VALIDATED mode into FunctionCallingConfigMode
([c282e79](c282e79bed))
- Add VideoGenerationReferenceType enum for generate\_videos
([635b825](635b825bed))
- refactor Go SDK to use pointers for optional parameters
([3ff328a](3ff328ac19))
- support tunings.cancel in the genai SDK for Python, Java, JS, and Go
([8c46fd2](8c46fd26e1))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTcuMTAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: Averi Kitsch <akitsch@google.com>
Co-authored-by: Anmol Shukla <shuklaanmol@google.com>
2025-09-15 11:35:06 +05:30
Harsh Jha
e4a0bb8220 Update docs/en/getting-started/quickstart/python/quickstart_test.py
Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-15 11:35:05 +05:30
Anmol Shukla
46d4a6e86d docs: updated google api key conf (#1398)
Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-15 11:35:05 +05:30
Mend Renovate
2e239424e0 chore(deps): update module github.com/googleapis/mcp-toolbox-sdk-go to v0.3.0 (#1360)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
|
[github.com/googleapis/mcp-toolbox-sdk-go](https://redirect.github.com/googleapis/mcp-toolbox-sdk-go)
| `v0.2.0` -> `v0.3.0` |
[![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgoogleapis%2fmcp-toolbox-sdk-go/v0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgoogleapis%2fmcp-toolbox-sdk-go/v0.2.0/v0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>googleapis/mcp-toolbox-sdk-go
(github.com/googleapis/mcp-toolbox-sdk-go)</summary>

###
[`v0.3.0`](https://redirect.github.com/googleapis/mcp-toolbox-sdk-go/releases/tag/v0.3.0)

[Compare
Source](https://redirect.github.com/googleapis/mcp-toolbox-sdk-go/compare/v0.2.0...v0.3.0)

##### Features

- Add support for Map parameter type
([#&#8203;51](https://redirect.github.com/googleapis/mcp-toolbox-sdk-go/issues/51))
([c80d8d6](c80d8d6e2b))
- Add support for nested maps in generic map parameter
([#&#8203;61](https://redirect.github.com/googleapis/mcp-toolbox-sdk-go/issues/61))
([3b33c52](3b33c52150))

##### Miscellaneous Chores

- Release 0.3.0
([#&#8203;59](https://redirect.github.com/googleapis/mcp-toolbox-sdk-go/issues/59))
([a89adcf](a89adcf440))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS45MS4xIiwidXBkYXRlZEluVmVyIjoiNDEuOTcuMTAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-15 11:35:05 +05:30
Yuan Teoh
d66c010b9d chore: fix lint in recent merged prs (#1396)
Run `golangcilint` on recent merged prs.
2025-09-15 11:35:05 +05:30
Wenxin Du
67e890edf8 fix(bigquery)!: Add Bearer parsing to auth token (#1386)
Previously we propagate tokens directly to the BQ API. But MCP inspector
adds a "Bearer" prefix to all authorization header. We will need to
parse the token accordingly to make it work.
2025-09-15 11:35:05 +05:30
trehanshakuntG
70b849ec24 feat(tools/firestore): Add firestore-query tool (#1305)
## Description
---

This PR introduces a new tool kind `firestore-query` that enables
parameterized querying of Firestore collections with support for
Firestore native JSON value types, ensuring proper type handling for
complex queries.

### Feature

A new Firestore tool that allows:

- __Parameterized collection paths, filters, select, orderBy, limit and
analyzeQuery__ using Go template syntax
- __Native JSON value type support__ for proper type handling in queries
- __Complex filter structures__ with AND/OR logical operators
- __Dynamic query building__ with template parameter substitution

Example usage:
<img width="761" height="721" alt="Screenshot 2025-09-09 at 1 21 16 PM"
src="https://github.com/user-attachments/assets/bb359ea8-f750-492d-9f13-cef8f3b6bfd1"
/>



## PR Checklist
---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:
- [x] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/langchain-google-alloydb-pg-python/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involve a breaking change
2025-09-15 11:35:05 +05:30
Averi Kitsch
708c8f311f feat(prebuilt): Update default values for prebuilt tools (#1355)
## Description
Provide default values for prebuilt tools

## 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>
2025-09-15 11:35:05 +05:30
Harsh Jha
b1e616f319 Merge branch 'main' into sts-python-test 2025-09-15 11:23:17 +05:30
Harsh Jha
81f0a15a4f Update docs/en/getting-started/quickstart/python/quickstart_test.py
Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2025-09-10 14:26:14 +05:30
Harsh Jha
4adc455cab Merge branch 'main' into sts-python-test 2025-09-10 12:27:47 +05:30
Harsh Jha
88a929d0b7 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-10 01:54:41 +05:30
Harsh Jha
a06a4226ae fix: psql error 2025-09-10 01:54:14 +05:30
Harsh Jha
356403674a Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-10 01:40:14 +05:30
Harsh Jha
af48843fb4 chore: clenanup of script 2025-09-10 01:39:38 +05:30
Harsh Jha
f9856a5fc7 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-10 00:44:17 +05:30
Harsh Jha
1573e8ce9a chore: updated script for single quickstart_test.py 2025-09-10 00:42:20 +05:30
Harsh Jha
475b71051c chore: rolled back to previous script 2025-09-10 00:40:31 +05:30
Harsh Jha
41d447de9f chore: fix of installing requirements.txt 2025-09-10 00:33:31 +05:30
Harsh Jha
41c71c0098 fix: requirements.txt installation 2025-09-10 00:14:20 +05:30
Harsh Jha
cad3a8acf2 fix: requirements.txt installation 2025-09-10 00:05:19 +05:30
Harsh Jha
18adf98106 fix: requirements.txt installation 2025-09-10 00:02:27 +05:30
Harsh Jha
85417324d3 MERGE sts-python-ci into sts-python-poc 2025-09-09 23:54:32 +05:30
Harsh Jha
59ae6b5208 new: ci files for py quickstart sample testing 2025-09-09 23:49:13 +05:30
Harsh Jha
82c86cfeab Merge branch 'main' into sts-python-test 2025-09-09 23:16:27 +05:30
Harsh Jha
1a272a919f chore: removed redudant test files of py quickstart sample 2025-09-09 23:14:13 +05:30
Harsh Jha
85f7e86104 fix: await of adk quickstart 2025-09-09 20:54:46 +05:30
Harsh Jha
202a30df2b Merge branch 'sts-python-test' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-09 20:53:32 +05:30
Harsh Jha
936f0d5d49 update: adk verison in py quickstart 2025-09-09 20:52:50 +05:30
Harsh Jha
ef86e37207 Merge branch 'main' into sts-python-test 2025-09-09 20:32:07 +05:30
Harsh Jha
10517eaf2e fix: proejct id for llamaindex py quickstart sample 2025-09-09 20:31:10 +05:30
Harsh Jha
60e5bcf45d fix: core quickstart sample 2025-09-09 20:21:00 +05:30
Harsh Jha
a628bcc97a fix: os import in core py sample 2025-09-09 20:13:47 +05:30
Harsh Jha
05de1b6eb4 fix: proejct id for quickstart core 2025-09-09 20:09:21 +05:30
Harsh Jha
83f21fb563 fix: google api key 2025-09-09 20:02:55 +05:30
Harsh Jha
bbf74bf241 chore: await keyword 2025-09-09 19:57:58 +05:30
Harsh Jha
730ff27485 Merge branch 'sts-python-ci' of https://github.com/googleapis/genai-toolbox into sts-python-poc 2025-09-09 19:48:32 +05:30
Harsh Jha
c1be3a730f new: ci files for py quickstart samples 2025-09-09 19:19:35 +05:30
Harsh Jha
72d70180ae new: ci files for py quickstart samples 2025-09-09 19:11:54 +05:30
Harsh Jha
1de1d8a8c7 Merge branch 'main' into sts-python-test 2025-09-09 19:06:58 +05:30
Harsh Jha
9f411c0bd6 updated: version of python packages in sample quickstart 2025-09-08 12:52:13 +05:30
Harsh Jha
3c81ee2b87 chore: corrected the name of test class 2025-09-08 12:49:34 +05:30
Harsh Jha
8c1467d8a9 Merge branch 'main' into sts-python-test 2025-09-03 10:53:23 +05:30
Harsh Jha
623b91af84 Merge branch 'main' into sts-python-test 2025-09-02 15:28:47 +05:30
Harsh Jha
e1cc144e72 Remove unnecessary newline in run_application function 2025-09-02 15:26:56 +05:30
Harsh Jha
1a0386b360 chore(python/quickstart): address multiple review comments in sample code 2025-09-02 15:25:48 +05:30
Harsh Jha
7dfdede4be test: Remove redundant script output check from tests 2025-09-02 15:17:50 +05:30
Harsh Jha
25e116584a fix(python/quickstart): pin exact versions in requirements.txt 2025-09-02 15:17:50 +05:30
Harsh Jha
e0d6c1f6d8 fix(python/quickstart): revert code sample in all python quickstart examples 2025-09-02 15:17:50 +05:30
Harsh Jha
7ffb0e2d82 feat: Implement language-agnostic snippet shortcode 2025-09-02 15:17:50 +05:30
Harsh Jha
0e23d73ea2 refactor(docs): Fix Table of Contents and optimize region include shortcode 2025-09-02 15:17:50 +05:30
Harsh Jha
79adf9d70b refactor(docs): Replace duplicated content with a shared shortcode 2025-09-02 15:17:50 +05:30
Harsh Jha
c97b89555e feat: Add shortcode for including file regions 2025-09-02 15:17:50 +05:30
Harsh Jha
018edb5d61 Merge branch 'main' into sts-python-test 2025-09-01 10:43:51 +05:30
Harsh Jha
a5beea5756 chore(docs/python): remove compile-time test from quickstart samples 2025-09-01 10:42:40 +05:30
Harsh Jha
1130354ac5 refactor: replace duplicate snippet code with regionInclude 2025-08-29 10:47:03 +05:30
Harsh Jha
28021d760e Merge branch 'main' of https://github.com/googleapis/genai-toolbox into sts-python-test 2025-08-29 10:39:56 +05:30
Harsh Jha
ff7f34bba6 feat: remove duplicate snippet shortcode 2025-08-29 10:39:44 +05:30
Harsh Jha
a93ab0c25a Merge branch 'main' into sts-python-test 2025-08-28 17:17:01 +05:30
Harsh Jha
921db2a546 refactor(test): update quickstart tests to only check for error-free execution 2025-08-28 16:08:34 +05:30
Harsh Jha
5b7cc83472 feat: add pytest-based test infrastructure for Python quickstart frameworks 2025-08-21 14:27:04 +05:30
Harsh Jha
e5922a69ec Merge branch 'sample-testing-strategy-python' of https://github.com/googleapis/genai-toolbox into sts-python-test 2025-08-20 19:46:37 +05:30
Harsh Jha
faa79cd3c1 Update quickstart.py 2025-08-20 18:21:11 +05:30
Harsh Jha
959941d4ae feat(adk): add Python quickstart testing infrastructure with requirements.txt, golden.txt, and quickstart_test.py 2025-08-20 18:14:31 +05:30
Harsh Jha
7dc77fec19 fix(quickstart): remove angle brackets from ToolboxSyncClient URL in Python ADK 2025-08-20 18:09:48 +05:30
Harsh Jha
061f38382e feat(python): add quickstart samples for adk, core, langchain, and llamaindex 2025-08-19 17:14:44 +05:30
Harsh Jha
64e64a0d51 Update quickstart docs and shortcodes for improved region includes 2025-08-19 16:34:13 +05:30
Anmol Shukla
5e0a1b03ab Merge branch 'main' into docs/add-region-shortcode 2025-08-19 14:28:14 +05:30
Anmol Shukla
b7cf0562ed Merge branch 'main' into docs/add-region-shortcode 2025-08-18 14:16:04 +05:30
Anmol Shukla
8309816f44 Merge branch 'main' into docs/add-region-shortcode 2025-08-14 15:12:34 +05:30
Harsh Jha
114a0c91d8 feat: Implement language-agnostic snippet shortcode 2025-08-05 14:39:53 +05:30
Harsh Jha
5f1e4b940c refactor(docs): Fix Table of Contents and optimize region include shortcode 2025-08-01 19:56:13 +05:30
Harsh Jha
e0b6d2d26b refactor(docs): Replace duplicated content with a shared shortcode 2025-07-31 18:52:46 +05:30
Harsh Jha
590bfaf4d3 feat: Add shortcode for including file regions 2025-07-31 11:31:19 +05:30
188 changed files with 1336 additions and 4103 deletions

View File

@@ -174,25 +174,25 @@ steps:
bigquery \
bigquery
# - id: "dataplex"
# name: golang:1
# waitFor: ["compile-test-binary"]
# entrypoint: /bin/bash
# env:
# - "GOPATH=/gopath"
# - "DATAPLEX_PROJECT=$PROJECT_ID"
# - "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
# secretEnv: ["CLIENT_ID"]
# volumes:
# - name: "go"
# path: "/gopath"
# args:
# - -c
# - |
# .ci/test_with_coverage.sh \
# "Dataplex" \
# dataplex \
# dataplex
- id: "dataplex"
name: golang:1
waitFor: ["compile-test-binary"]
entrypoint: /bin/bash
env:
- "GOPATH=/gopath"
- "DATAPLEX_PROJECT=$PROJECT_ID"
- "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
secretEnv: ["CLIENT_ID"]
volumes:
- name: "go"
path: "/gopath"
args:
- -c
- |
.ci/test_with_coverage.sh \
"Dataplex" \
dataplex \
dataplex
- id: "postgres"
name: golang:1
@@ -662,26 +662,6 @@ steps:
- |
./yugabytedb.test -test.v
- id: "cassandra"
name: golang:1
waitFor: ["compile-test-binary"]
entrypoint: /bin/bash
env:
- "GOPATH=/gopath"
- "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
secretEnv: ["CLIENT_ID", "CASSANDRA_USER", "CASSANDRA_PASS", "CASSANDRA_HOST"]
volumes:
- name: "go"
path: "/gopath"
args:
- -c
- |
.ci/test_with_coverage.sh \
"Cassandra" \
cassandra \
cassandra
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/cloud_sql_pg_user/versions/latest
@@ -766,12 +746,6 @@ availableSecrets:
env: YUGABYTEDB_USER
- versionName: projects/$PROJECT_ID/secrets/yugabytedb_pass/versions/latest
env: YUGABYTEDB_PASS
- versionName: projects/$PROJECT_ID/secrets/cassandra_user/versions/latest
env: CASSANDRA_USER
- versionName: projects/$PROJECT_ID/secrets/cassandra_pass/versions/latest
env: CASSANDRA_PASS
- versionName: projects/$PROJECT_ID/secrets/cassandra_host/versions/latest
env: CASSANDRA_HOST
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -1,47 +0,0 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
steps:
- name: 'golang:1.25.1'
id: 'go-quickstart-test'
entrypoint: 'bash'
args:
# The '-c' flag tells bash to execute the following string as a command.
# The 'set -ex' enables debug output and exits on error for easier troubleshooting.
- -c
- |
set -ex
export VERSION=$(cat ./cmd/version.txt)
chmod +x .ci/quickstart_test/run_go_tests.sh
.ci/quickstart_test/run_go_tests.sh
env:
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
- 'GCP_PROJECT=${_GCP_PROJECT}'
- 'DATABASE_NAME=${_DATABASE_NAME}'
- 'DB_USER=${_DB_USER}'
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
availableSecrets:
secretManager:
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/7
env: 'TOOLS_YAML_CONTENT'
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
env: 'GOOGLE_API_KEY'
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
env: 'DB_PASSWORD'
timeout: 1000s
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -1,47 +0,0 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
steps:
- name: 'node:20'
id: 'js-quickstart-test'
entrypoint: 'bash'
args:
# The '-c' flag tells bash to execute the following string as a command.
# The 'set -ex' enables debug output and exits on error for easier troubleshooting.
- -c
- |
set -ex
export VERSION=$(cat ./cmd/version.txt)
chmod +x .ci/quickstart_test/run_js_tests.sh
.ci/quickstart_test/run_js_tests.sh
env:
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
- 'GCP_PROJECT=${_GCP_PROJECT}'
- 'DATABASE_NAME=${_DATABASE_NAME}'
- 'DB_USER=${_DB_USER}'
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
availableSecrets:
secretManager:
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/6
env: 'TOOLS_YAML_CONTENT'
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
env: 'GOOGLE_API_KEY'
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
env: 'DB_PASSWORD'
timeout: 1000s
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -1,125 +0,0 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#!/bin/bash
set -e
TABLE_NAME="hotels_go"
QUICKSTART_GO_DIR="docs/en/getting-started/quickstart/go"
SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql"
PROXY_PID=""
TOOLBOX_PID=""
install_system_packages() {
apt-get update && apt-get install -y \
postgresql-client \
wget \
gettext-base \
netcat-openbsd
}
start_cloud_sql_proxy() {
wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" &
PROXY_PID=$!
for i in {1..30}; do
if nc -z 127.0.0.1 5432; then
echo "Cloud SQL Proxy is up and running."
return
fi
sleep 1
done
echo "Cloud SQL Proxy failed to start within the timeout period."
exit 1
}
setup_toolbox() {
TOOLBOX_YAML="/tools.yaml"
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi
wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
chmod +x "/toolbox"
/toolbox --tools-file "$TOOLBOX_YAML" &
TOOLBOX_PID=$!
sleep 2
}
setup_orch_table() {
export TABLE_NAME
envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME"
}
run_orch_test() {
local orch_dir="$1"
local orch_name
orch_name=$(basename "$orch_dir")
if [ "$orch_name" == "openAI" ]; then
echo -e "\nSkipping framework '${orch_name}': Temporarily excluded."
return
fi
(
set -e
setup_orch_table
echo "--- Preparing module for $orch_name ---"
cd "$orch_dir"
if [ -f "go.mod" ]; then
go mod tidy
fi
cd ..
export ORCH_NAME="$orch_name"
echo "--- Running tests for $orch_name ---"
go test -v ./...
)
}
cleanup_all() {
echo "--- Final cleanup: Shutting down processes and dropping table ---"
if [ -n "$TOOLBOX_PID" ]; then
kill $TOOLBOX_PID || true
fi
if [ -n "$PROXY_PID" ]; then
kill $PROXY_PID || true
fi
}
trap cleanup_all EXIT
# Main script execution
install_system_packages
start_cloud_sql_proxy
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
setup_toolbox
for ORCH_DIR in "$QUICKSTART_GO_DIR"/*/; do
if [ ! -d "$ORCH_DIR" ]; then
continue
fi
run_orch_test "$ORCH_DIR"
done

View File

@@ -1,123 +0,0 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#!/bin/bash
set -e
TABLE_NAME="hotels_js"
QUICKSTART_JS_DIR="docs/en/getting-started/quickstart/js"
SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql"
# Initialize process IDs to empty at the top of the script
PROXY_PID=""
TOOLBOX_PID=""
install_system_packages() {
apt-get update && apt-get install -y \
postgresql-client \
wget \
gettext-base \
netcat-openbsd
}
start_cloud_sql_proxy() {
wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" &
PROXY_PID=$!
for i in {1..30}; do
if nc -z 127.0.0.1 5432; then
echo "Cloud SQL Proxy is up and running."
return
fi
sleep 1
done
echo "Cloud SQL Proxy failed to start within the timeout period."
exit 1
}
setup_toolbox() {
TOOLBOX_YAML="/tools.yaml"
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi
wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
chmod +x "/toolbox"
/toolbox --tools-file "$TOOLBOX_YAML" &
TOOLBOX_PID=$!
sleep 2
}
setup_orch_table() {
export TABLE_NAME
envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME"
}
run_orch_test() {
local orch_dir="$1"
local orch_name
orch_name=$(basename "$orch_dir")
(
set -e
echo "--- Preparing environment for $orch_name ---"
setup_orch_table
cd "$orch_dir"
if [ -f "package.json" ]; then
echo "Installing dependencies for $orch_name..."
npm install
fi
cd ..
echo "--- Running tests for $orch_name ---"
export ORCH_NAME="$orch_name"
node --test quickstart.test.js
echo "--- Cleaning environment for $orch_name ---"
rm -rf "${orch_name}/node_modules"
)
}
cleanup_all() {
echo "--- Final cleanup: Shutting down processes and dropping table ---"
if [ -n "$TOOLBOX_PID" ]; then
kill $TOOLBOX_PID || true
fi
if [ -n "$PROXY_PID" ]; then
kill $PROXY_PID || true
fi
}
trap cleanup_all EXIT
# Main script execution
install_system_packages
start_cloud_sql_proxy
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
setup_toolbox
for ORCH_DIR in "$QUICKSTART_JS_DIR"/*/; do
if [ ! -d "$ORCH_DIR" ]; then
continue
fi
run_orch_test "$ORCH_DIR"
done

View File

@@ -1,27 +1,5 @@
# Changelog
## [0.16.0](https://github.com/googleapis/genai-toolbox/compare/v0.15.0...v0.16.0) (2025-09-25)
### ⚠ BREAKING CHANGES
* **tool/bigquery-execute-sql:** add allowed datasets support ([#1443](https://github.com/googleapis/genai-toolbox/issues/1443))
* **tool/bigquery-forecast:** add allowed datasets support ([#1412](https://github.com/googleapis/genai-toolbox/issues/1412))
### Features
* **cassandra:** Add Cassandra Source and Tool ([#1012](https://github.com/googleapis/genai-toolbox/issues/1012)) ([6e42053](https://github.com/googleapis/genai-toolbox/commit/6e420534ee894da4a8d226acb6cdb63d0d5d9ce5))
* **sources/postgres:** Add application_name ([#1504](https://github.com/googleapis/genai-toolbox/issues/1504)) ([72a2366](https://github.com/googleapis/genai-toolbox/commit/72a2366b28870aa6f81c4f890f4770ec5ecffdba))
* **tool/bigquery-execute-sql:** Add allowed datasets support ([#1443](https://github.com/googleapis/genai-toolbox/issues/1443)) ([9501ebb](https://github.com/googleapis/genai-toolbox/commit/9501ebbdbcba871b98663185c690308dda1729b5))
* **tool/bigquery-forecast:** Add allowed datasets support ([#1412](https://github.com/googleapis/genai-toolbox/issues/1412)) ([88bac7e](https://github.com/googleapis/genai-toolbox/commit/88bac7e36f5ebb6ad18773bff30b85ef678431e7))
* **tools/clickhouse-list-tables:** Add list-tables tool ([#1446](https://github.com/googleapis/genai-toolbox/issues/1446)) ([69a3caf](https://github.com/googleapis/genai-toolbox/commit/69a3cafabec5a40e2776d71de3587c0d16c722a2))
### Bug Fixes
* **tool/mongodb-find:** Fix find tool `limit` field ([#1570](https://github.com/googleapis/genai-toolbox/issues/1570)) ([4166bf7](https://github.com/googleapis/genai-toolbox/commit/4166bf7ab85732f64b877d5f20235057df919049))
* **tools/mongodb:** Concat filter params only once in mongodb update tools ([#1545](https://github.com/googleapis/genai-toolbox/issues/1545)) ([295f9dc](https://github.com/googleapis/genai-toolbox/commit/295f9dc41a43f0a4bdbd99e465bf2be01249084e))
## [0.15.0](https://github.com/googleapis/genai-toolbox/compare/v0.14.0...v0.15.0) (2025-09-18)

View File

@@ -25,7 +25,7 @@ ARG COMMIT_SHA=""
RUN go get ./...
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -ldflags "-X github.com/googleapis/genai-toolbox/cmd.buildType=${BUILD_TYPE} -X github.com/googleapis/genai-toolbox/cmd.commitSha=${COMMIT_SHA}"
go build -ldflags "-X github.com/googleapis/genai-toolbox/cmd.buildType=container.${BUILD_TYPE} -X github.com/googleapis/genai-toolbox/cmd.commitSha=${COMMIT_SHA}"
# Final Stage
FROM gcr.io/distroless/static:nonroot

View File

@@ -33,6 +33,7 @@ documentation](https://googleapis.github.io/genai-toolbox/).
- [Getting Started](#getting-started)
- [Installing the server](#installing-the-server)
- [Running the server](#running-the-server)
- [Homebrew Users](#homebrew-users)
- [Integrating your application](#integrating-your-application)
- [Configuration](#configuration)
- [Sources](#sources)
@@ -114,53 +115,13 @@ following instructions for your OS and CPU architecture.
To install Toolbox as a binary:
<!-- {x-release-please-start-version} -->
> <details>
> <summary>Linux (AMD64)</summary>
>
> To install Toolbox as a binary on Linux (AMD64):
> ```sh
> # see releases page for other versions
> export VERSION=0.16.0
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
> chmod +x toolbox
> ```
>
> </details>
> <details>
> <summary>macOS (Apple Silicon)</summary>
>
> To install Toolbox as a binary on macOS (Apple Silicon):
> ```sh
> # see releases page for other versions
> export VERSION=0.16.0
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/arm64/toolbox
> chmod +x toolbox
> ```
>
> </details>
> <details>
> <summary>macOS (Intel)</summary>
>
> To install Toolbox as a binary on macOS (Intel):
> ```sh
> # see releases page for other versions
> export VERSION=0.16.0
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/amd64/toolbox
> chmod +x toolbox
> ```
>
> </details>
> <details>
> <summary>Windows (AMD64)</summary>
>
> To install Toolbox as a binary on Windows (AMD64):
> ```powershell
> # see releases page for other versions
> $VERSION = "0.16.0"
> Invoke-WebRequest -Uri "https://storage.googleapis.com/genai-toolbox/v$VERSION/windows/amd64/toolbox.exe" -OutFile "toolbox.exe"
> ```
>
> </details>
```sh
# see releases page for other versions
export VERSION=0.15.0
curl -O https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox
```
</details>
<details>
@@ -169,7 +130,7 @@ You can also install Toolbox as a container:
```sh
# see releases page for other versions
export VERSION=0.16.0
export VERSION=0.15.0
docker pull us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION
```
@@ -193,23 +154,12 @@ 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.16.0
go install github.com/googleapis/genai-toolbox@v0.15.0
```
<!-- {x-release-please-end} -->
</details>
<details>
<summary>Gemini CLI Extensions</summary>
To install Gemini CLI Extensions for MCP Toolbox, run the following command:
```sh
gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox
```
</details>
### Running the server
[Configure](#configuration) a `tools.yaml` to define your tools, and then
@@ -283,16 +233,6 @@ toolbox --tools-file "tools.yaml"
</details>
<details>
<summary>Gemini CLI</summary>
Interact with your custom tools using natural language. Check
[gemini-cli-extensions/mcp-toolbox](https://github.com/gemini-cli-extensions/mcp-toolbox)
for more information.
</details>
You can use `toolbox help` for a full list of flags! To stop the server, send a
terminate signal (`ctrl+c` on most platforms).

View File

@@ -64,7 +64,6 @@ import (
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerysearchcatalog"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerysql"
_ "github.com/googleapis/genai-toolbox/internal/tools/bigtable"
_ "github.com/googleapis/genai-toolbox/internal/tools/cassandra/cassandracql"
_ "github.com/googleapis/genai-toolbox/internal/tools/clickhouse/clickhouseexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/clickhouse/clickhouselistdatabases"
_ "github.com/googleapis/genai-toolbox/internal/tools/clickhouse/clickhouselisttables"
@@ -160,7 +159,6 @@ import (
_ "github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
_ "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
_ "github.com/googleapis/genai-toolbox/internal/sources/bigtable"
_ "github.com/googleapis/genai-toolbox/internal/sources/cassandra"
_ "github.com/googleapis/genai-toolbox/internal/sources/clickhouse"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudmonitoring"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudsqladmin"
@@ -655,6 +653,20 @@ func watchChanges(ctx context.Context, watchDirs map[string]bool, watchedFiles m
}
}
// updateLogLevel checks if Toolbox have to update the existing log level set by users.
// stdio doesn't support "debug" and "info" logs.
func updateLogLevel(stdio bool, logLevel string) bool {
if stdio {
switch strings.ToUpper(logLevel) {
case log.Debug, log.Info:
return true
default:
return false
}
}
return false
}
func resolveWatcherInputs(toolsFile string, toolsFiles []string, toolsFolder string) (map[string]bool, map[string]bool) {
var relevantFiles []string
@@ -683,6 +695,10 @@ func resolveWatcherInputs(toolsFile string, toolsFiles []string, toolsFolder str
}
func run(cmd *Command) error {
if updateLogLevel(cmd.cfg.Stdio, cmd.cfg.LogLevel.String()) {
cmd.cfg.LogLevel = server.StringLevel(log.Warn)
}
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()
@@ -706,22 +722,16 @@ func run(cmd *Command) error {
cancel()
}(ctx)
// If stdio, set logger's out stream (usually DEBUG and INFO logs) to errStream
loggerOut := cmd.outStream
if cmd.cfg.Stdio {
loggerOut = cmd.errStream
}
// Handle logger separately from config
switch strings.ToLower(cmd.cfg.LoggingFormat.String()) {
case "json":
logger, err := log.NewStructuredLogger(loggerOut, cmd.errStream, cmd.cfg.LogLevel.String())
logger, err := log.NewStructuredLogger(cmd.outStream, cmd.errStream, cmd.cfg.LogLevel.String())
if err != nil {
return fmt.Errorf("unable to initialize logger: %w", err)
}
cmd.logger = logger
case "standard":
logger, err := log.NewStdLogger(loggerOut, cmd.errStream, cmd.cfg.LogLevel.String())
logger, err := log.NewStdLogger(cmd.outStream, cmd.errStream, cmd.cfg.LogLevel.String())
if err != nil {
return fmt.Errorf("unable to initialize logger: %w", err)
}

View File

@@ -1597,3 +1597,51 @@ func TestPrebuiltTools(t *testing.T) {
})
}
}
func TestUpdateLogLevel(t *testing.T) {
tcs := []struct {
desc string
stdio bool
logLevel string
want bool
}{
{
desc: "no stdio",
stdio: false,
logLevel: "info",
want: false,
},
{
desc: "stdio with info log",
stdio: true,
logLevel: "info",
want: true,
},
{
desc: "stdio with debug log",
stdio: true,
logLevel: "debug",
want: true,
},
{
desc: "stdio with warn log",
stdio: true,
logLevel: "warn",
want: false,
},
{
desc: "stdio with error log",
stdio: true,
logLevel: "error",
want: false,
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := updateLogLevel(tc.stdio, tc.logLevel)
if got != tc.want {
t.Fatalf("incorrect indication to update log level: got %t, want %t", got, tc.want)
}
})
}
}

View File

@@ -1 +1 @@
0.16.0
0.15.0

View File

@@ -234,7 +234,7 @@
},
"outputs": [],
"source": [
"version = \"0.16.0\" # x-release-please-version\n",
"version = \"0.15.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

@@ -81,50 +81,23 @@ following instructions for your OS and CPU architecture.
<!-- {x-release-please-start-version} -->
{{< tabpane text=true >}}
{{% tab header="Binary" lang="en" %}}
{{< tabpane text=true >}}
{{% tab header="Linux (AMD64)" lang="en" %}}
To install Toolbox as a binary on Linux (AMD64):
To install Toolbox as a binary:
```sh
# see releases page for other versions
export VERSION=0.16.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
export VERSION=0.15.0
curl -O https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox
```
{{% /tab %}}
{{% tab header="macOS (Apple Silicon)" lang="en" %}}
To install Toolbox as a binary on macOS (Apple Silicon):
```sh
# see releases page for other versions
export VERSION=0.16.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/arm64/toolbox
chmod +x toolbox
```
{{% /tab %}}
{{% tab header="macOS (Intel)" lang="en" %}}
To install Toolbox as a binary on macOS (Intel):
```sh
# see releases page for other versions
export VERSION=0.16.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/amd64/toolbox
chmod +x toolbox
```
{{% /tab %}}
{{% tab header="Windows (AMD64)" lang="en" %}}
To install Toolbox as a binary on Windows (AMD64):
```powershell
# see releases page for other versions
$VERSION = "0.16.0"
Invoke-WebRequest -Uri "https://storage.googleapis.com/genai-toolbox/v$VERSION/windows/amd64/toolbox.exe" -OutFile "toolbox.exe"
```
{{% /tab %}}
{{< /tabpane >}}
{{% /tab %}}
{{% tab header="Container image" lang="en" %}}
You can also install Toolbox as a container:
```sh
# see releases page for other versions
export VERSION=0.16.0
export VERSION=0.15.0
docker pull us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION
```
@@ -143,7 +116,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.16.0
go install github.com/googleapis/genai-toolbox@v0.15.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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -345,10 +345,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
"license": "MIT",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",

View File

@@ -11,7 +11,6 @@ import os
# TODO(developer): replace this with your Google API key
api_key = os.environ.get("GOOGLE_API_KEY") or "your-api-key" # Set your API key here
os.environ["GOOGLE_API_KEY"] = api_key
async def main():
with ToolboxSyncClient("http://127.0.0.1:5000") as toolbox_client:

View File

@@ -1,3 +1,3 @@
google-adk==1.15.0
toolbox-core==0.5.2
google-adk==1.14.1
toolbox-core==0.5.0
pytest==8.4.2

View File

@@ -113,4 +113,4 @@ async def main():
else:
print(response.text)
asyncio.run(main())
asyncio.run(main())

View File

@@ -1,3 +1,3 @@
google-genai==1.38.0
toolbox-core==0.5.2
toolbox-core==0.5.0
pytest==8.4.2

View File

@@ -1,4 +1,4 @@
llama-index==0.14.3
llama-index-llms-google-genai==0.6.0
llama-index==0.14.2
llama-index-llms-google-genai==0.5.0
toolbox-llamaindex==0.5.2
pytest==8.4.2

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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

View File

@@ -48,19 +48,19 @@ to expose your developer assistant tools to a Looker instance:
<!-- {x-release-please-start-version} -->
{{< tabpane persist=header >}}
{{< tab header="linux/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/linux/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
{{< /tab >}}
{{< tab header="darwin/arm64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/arm64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
{{< /tab >}}
{{< tab header="darwin/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/darwin/amd64/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
{{< /tab >}}
{{< tab header="windows/amd64" lang="bash" >}}
curl -O https://storage.googleapis.com/genai-toolbox/v0.16.0/windows/amd64/toolbox.exe
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/windows/amd64/toolbox.exe
{{< /tab >}}
{{< /tabpane >}}
<!-- {x-release-please-end} -->

View File

@@ -1,38 +0,0 @@
---
title: Connect via Gemini CLI Extensions
type: docs
weight: 2
description: "Connect to Toolbox via Gemini CLI Extensions."
---
## Gemini CLI Extensions
[Gemini CLI][gemini-cli] is an open-source AI agent designed to assist with development workflows by assisting with coding, debugging, data exploration, and content creation. Its mission is to provide an agentic interface for interacting with database and analytics services and popular open-source databases.
### How extensions work
Gemini CLI is highly extensible, allowing for the addition of new tools and capabilities through extensions. You can load the extensions from a GitHub URL, a local directory, or a configurable registry. They provide new tools, slash commands, and prompts to assist with your workflow.
Use the Gemini CLI Extensions to load prebuilt or custom tools to interact with your databases.
[gemini-cli]: https://google-gemini.github.io/gemini-cli/
Below are a list of Gemini CLI Extensions powered by MCP Toolbox:
* [alloydb](https://github.com/gemini-cli-extensions/alloydb)
* [alloydb-observability](https://github.com/gemini-cli-extensions/alloydb-observability)
* [bigquery-conversational-analytics](https://github.com/gemini-cli-extensions/bigquery-conversational-analytics)
* [bigquery-data-analytics](https://github.com/gemini-cli-extensions/bigquery-data-analytics)
* [cloud-sql-mysql](https://github.com/gemini-cli-extensions/cloud-sql-mysql)
* [cloud-sql-mysql-observability](https://github.com/gemini-cli-extensions/cloud-sql-mysql-observability)
* [cloud-sql-postgresql](https://github.com/gemini-cli-extensions/cloud-sql-postgresql)
* [cloud-sql-postgresql-observability](https://github.com/gemini-cli-extensions/cloud-sql-postgresql-observability)
* [cloud-sql-sqlserver](https://github.com/gemini-cli-extensions/cloud-sql-sqlserver)
* [cloud-sql-sqlserver-observability](https://github.com/gemini-cli-extensions/cloud-sql-sqlserver-observability)
* [dataplex](https://github.com/gemini-cli-extensions/dataplex)
* [firestore-native](https://github.com/gemini-cli-extensions/firestore-native)
* [looker](https://github.com/gemini-cli-extensions/looker)
* [mcp-toolbox](https://github.com/gemini-cli-extensions/mcp-toolbox)
* [mysql](https://github.com/gemini-cli-extensions/mysql)
* [postgres](https://github.com/gemini-cli-extensions/postgres)
* [spanner](https://github.com/gemini-cli-extensions/spanner)
* [sql-server](https://github.com/gemini-cli-extensions/sql-server)

View File

@@ -132,10 +132,6 @@ details on how to connect your AI tools (IDEs) to databases via Toolbox and MCP.
* `list_tables`: Lists tables in the database.
* `get_query_plan`: Provides information about how MySQL executes a SQL
statement.
* `list_active_queries`: Lists ongoing queries.
* `list_tables_missing_unique_indexes`: Looks for tables that do not have
primary or unique key contraint.
* `list_table_fragmentation`: Displays table fragmentation in MySQL.
## Cloud SQL for MySQL Observability
@@ -411,10 +407,6 @@ details on how to connect your AI tools (IDEs) to databases via Toolbox and MCP.
* `list_tables`: Lists tables in the database.
* `get_query_plan`: Provides information about how MySQL executes a SQL
statement.
* `list_active_queries`: Lists ongoing queries.
* `list_tables_missing_unique_indexes`: Looks for tables that do not have
primary or unique key contraint.
* `list_table_fragmentation`: Displays table fragmentation in MySQL.
## OceanBase

View File

@@ -1,57 +0,0 @@
---
title: "Cassandra"
type: docs
weight: 1
description: >
Cassandra is a NoSQL distributed database known for its horizontal scalability, distributed architecture, and flexible schema definition.
---
## About
[Cassandra][cassandra-docs] is a NoSQL distributed database. By design, NoSQL databases are lightweight, open-source, non-relational, and largely distributed. Counted among their strengths are horizontal scalability, distributed architectures, and a flexible approach to schema definition.
[cassandra-docs]: https://cassandra.apache.org/
## Available Tools
- [`cassandra-cql`](../tools/cassandra/cassandra-cql.md)
Run parameterized CQL queries in Cassandra.
## Example
```yaml
sources:
my-cassandra-source:
kind: cassandra
hosts:
- 127.0.0.1
keyspace: my_keyspace
protoVersion: 4
username: ${USER_NAME}
password: ${PASSWORD}
caPath: /path/to/ca.crt # Optional: path to CA certificate
certPath: /path/to/client.crt # Optional: path to client certificate
keyPath: /path/to/client.key # Optional: path to client key
enableHostVerification: true # Optional: enable host verification
```
{{< notice tip >}}
Use environment variable replacement with the format ${ENV_NAME}
instead of hardcoding your secrets into the configuration file.
{{< /notice >}}
## Reference
| **field** | **type** | **required** | **description** |
|------------------------|:---------:|:------------:|-------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "cassandra". |
| hosts | string[] | true | List of IP addresses to connect to (e.g., ["192.168.1.1:9042", "192.168.1.2:9042","192.168.1.3:9042"]). The default port is 9042 if not specified. |
| keyspace | string | true | Name of the Cassandra keyspace to connect to (e.g., "my_keyspace"). |
| protoVersion | integer | false | Protocol version for the Cassandra connection (e.g., 4). |
| username | string | false | Name of the Cassandra user to connect as (e.g., "my-cassandra-user"). |
| password | string | false | Password of the Cassandra user (e.g., "my-password"). |
| caPath | string | false | Path to the CA certificate for SSL/TLS (e.g., "/path/to/ca.crt"). |
| certPath | string | false | Path to the client certificate for SSL/TLS (e.g., "/path/to/client.crt"). |
| keyPath | string | false | Path to the client key for SSL/TLS (e.g., "/path/to/client.key"). |
| enableHostVerification | boolean | false | Enable host verification for SSL/TLS (e.g., true). By default, host verification is disabled. |

View File

@@ -15,23 +15,9 @@ It's compatible with the following sources:
- [bigquery](../../sources/bigquery.md)
`bigquery-execute-sql` accepts the following parameters:
- **`sql`** (required): The GoogleSQL statement to execute.
- **`dry_run`** (optional): If set to `true`, the query is validated but not run,
returning information about the execution instead. Defaults to `false`.
The tool's behavior is influenced by the `allowedDatasets` restriction on the
`bigquery` source:
- **Without `allowedDatasets` restriction:** The tool can execute any valid GoogleSQL
query.
- **With `allowedDatasets` restriction:** Before execution, the tool performs a dry run
to analyze the query.
It will reject the query if it attempts to access any table outside the
allowed `datasets` list. To enforce this restriction, the following operations
are also disallowed:
- **Dataset-level operations** (e.g., `CREATE SCHEMA`, `ALTER SCHEMA`).
- **Unanalyzable operations** where the accessed tables cannot be determined
statically (e.g., `EXECUTE IMMEDIATE`, `CREATE PROCEDURE`, `CALL`).
`bigquery-execute-sql` takes a required `sql` input parameter and runs the SQL
statement against the configured `source`. It also supports an optional `dry_run`
parameter to validate a query without executing it.
## Example

View File

@@ -33,13 +33,6 @@ query based on the provided parameters:
- **horizon** (integer, optional): The number of future time steps you want to
predict. It defaults to 10 if not specified.
The tool's behavior regarding these parameters is influenced by the `allowedDatasets` restriction on the `bigquery` source:
- **Without `allowedDatasets` restriction:** The tool can use any table or query for the `history_data` parameter.
- **With `allowedDatasets` restriction:** The tool verifies that the `history_data` parameter only accesses tables
within the allowed datasets. If `history_data` is a table ID, the tool checks if the table's dataset is in the
allowed list. If `history_data` is a query, the tool performs a dry run to analyze the query and rejects it
if it accesses any table outside the allowed list.
## Example
```yaml

View File

@@ -1,7 +0,0 @@
---
title: "Cassandra"
type: docs
weight: 1
description: >
Tools that work with Cassandra Sources.
---

View File

@@ -1,96 +0,0 @@
---
title: "cassandra-cql"
type: docs
weight: 1
description: >
A "cassandra-cql" tool executes a pre-defined CQL statement against a Cassandra
database.
aliases:
- /resources/tools/cassandra-cql
---
## About
A `cassandra-cql` tool executes a pre-defined CQL statement against a Cassandra
database. It's compatible with any of the following sources:
- [cassandra](../sources/cassandra.md)
The specified CQL statement is executed as a [prepared statement][cassandra-prepare],
and expects parameters in the CQL query to be in the form of placeholders `?`.
[cassandra-prepare]: https://docs.datastax.com/en/developer/go-driver/4.8/cql-prepared-statements/
## Example
> **Note:** This tool uses parameterized queries to prevent CQL injections.
> Query parameters can be used as substitutes for arbitrary expressions.
> Parameters cannot be used as substitutes for keyspaces, table names, column names,
> or other parts of the query.
```yaml
tools:
search_users_by_email:
kind: cassandra-cql
source: my-cassandra-cluster
statement: |
SELECT user_id, email, first_name, last_name, created_at
FROM users
WHERE email = ?
description: |
Use this tool to retrieve specific user information by their email address.
Takes an email address and returns user details including user ID, email,
first name, last name, and account creation timestamp.
Do NOT use this tool with a user ID or other identifiers.
Example:
{{
"email": "user@example.com",
}}
parameters:
- name: email
type: string
description: User's email address
```
### Example with Template Parameters
> **Note:** This tool allows direct modifications to the CQL statement,
> including keyspaces, table names, and column names. **This makes it more
> vulnerable to CQL injections**. Using basic parameters only (see above) is
> recommended for performance and safety reasons. For more details, please check
> [templateParameters](../#template-parameters).
```yaml
tools:
list_keyspace_table:
kind: cassandra-cql
source: my-cassandra-cluster
statement: |
SELECT * FROM {{.keyspace}}.{{.tableName}};
description: |
Use this tool to list all information from a specific table in a keyspace.
Example:
{{
"keyspace": "my_keyspace",
"tableName": "users",
}}
templateParameters:
- name: keyspace
type: string
description: Keyspace containing the table
- name: tableName
type: string
description: Table to select from
```
## Reference
| **field** | **type** | **required** | **description** |
|--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
| kind | string | true | Must be "cassandra-cql". |
| source | string | true | Name of the source the CQL should execute on. |
| description | string | true | Description of the tool that is passed to the LLM. |
| statement | string | true | CQL statement to execute. |
| authRequired | []string | false | List of authentication requirements for the source. |
| parameters | [parameters](../#specifying-parameters) | false | List of [parameters](../#specifying-parameters) that will be inserted into the CQL statement. |
| templateParameters | [templateParameters](../#template-parameters) | false | List of [templateParameters](../#template-parameters) that will be inserted into the CQL statement before executing prepared statement. |

View File

@@ -3,7 +3,8 @@ title: "looker-add-dashboard-element"
type: docs
weight: 1
description: >
"looker-add-dashboard-element" creates a dashboard element in the given dashboard.
"looker-add-dashboard-element" generates a Looker look in the users personal folder in
Looker
aliases:
- /resources/tools/looker-add-dashboard-element
---

View File

@@ -3,7 +3,8 @@ title: "looker-get-dashboards"
type: docs
weight: 1
description: >
"looker-get-dashboards" tool searches for a saved Dashboard by name or description.
"looker-get-dashboards" searches for saved Looks in a Looker
source.
aliases:
- /resources/tools/looker-get-dashboards
---

View File

@@ -297,8 +297,7 @@
"setup_queries = [\n",
" # Install required extension to use the AlloyDB AI natural language support API\n",
" \"\"\"CREATE EXTENSION IF NOT EXISTS alloydb_ai_nl cascade;\"\"\",\n",
" \"\"\"CREATE EXTENSION IF NOT EXISTS google_ml_integration; \"\"\",\n",
" \n",
"\n",
" # Create schema\n",
" \"\"\"CREATE SCHEMA IF NOT EXISTS nla_demo;\"\"\",\n",
"\n",
@@ -690,18 +689,15 @@
"source": [
"async def run_queries(pool):\n",
" async with pool.connect() as db_conn:\n",
"\n",
" # Verify the generated context for the tables\n",
" for query in verify_context_queries:\n",
" response = await db_conn.execute(sqlalchemy.text(query))\n",
" print(\"Verify the context:\", response.mappings().all())\n",
"\n",
" \n",
" # Update context that needs revision\n",
" for query in update_context_queries:\n",
" await db_conn.execute(sqlalchemy.text(query))\n",
"\n",
" \n",
" # The resulting context entries in the alloydb_ai_nl.generated_schema_context_view \n",
" # view are applied to the corresponding schema objects, and the comments are overwritten.\n",
" for query in apply_generated_context_queries:\n",
@@ -771,7 +767,7 @@
},
"outputs": [],
"source": [
"version = \"0.16.0\" # x-release-please-version\n",
"version = \"0.15.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.16.0"
export VERSION="0.15.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.16.0\" # x-release-please-version\n",
"version = \"0.15.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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/$OS/toolbox
```
<!-- {x-release-please-end} -->
@@ -64,7 +64,6 @@ In this section, we will download Toolbox and run the Toolbox server.
```bash
export LOOKER_BASE_URL=https://looker.example.com
export LOOKER_VERIFY_SSL=true
export LOOKER_USE_CLIENT_OAUTH=true
```
In some instances you may need to append `:19999` to

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.16.0/$OS/toolbox
curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/$OS/toolbox
```
<!-- {x-release-please-end} -->

27
go.mod
View File

@@ -6,12 +6,12 @@ toolchain go1.25.1
require (
cloud.google.com/go/alloydbconn v1.15.5
cloud.google.com/go/bigquery v1.71.0
cloud.google.com/go/bigtable v1.40.1
cloud.google.com/go/bigquery v1.70.0
cloud.google.com/go/bigtable v1.40.0
cloud.google.com/go/cloudsqlconn v1.18.1
cloud.google.com/go/dataplex v1.27.1
cloud.google.com/go/dataplex v1.27.0
cloud.google.com/go/firestore v1.18.0
cloud.google.com/go/spanner v1.86.0
cloud.google.com/go/spanner v1.85.1
github.com/ClickHouse/clickhouse-go/v2 v2.40.3
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.29.0
@@ -26,7 +26,6 @@ require (
github.com/go-playground/validator/v10 v10.27.0
github.com/go-sql-driver/mysql v1.9.3
github.com/goccy/go-yaml v1.18.0
github.com/gocql/gocql v1.7.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/jackc/pgx/v5 v5.7.6
@@ -51,8 +50,8 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.37.0
go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/oauth2 v0.31.0
google.golang.org/api v0.250.0
google.golang.org/genproto v0.0.0-20250929231259-57b25ae835d4
google.golang.org/api v0.249.0
google.golang.org/genproto v0.0.0-20250826171959-ef028d996bc1
modernc.org/sqlite v1.39.0
)
@@ -74,7 +73,7 @@ require (
cloud.google.com/go/alloydb v1.18.0 // indirect
cloud.google.com/go/auth v0.16.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.8.4 // indirect
cloud.google.com/go/compute/metadata v0.8.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
@@ -116,7 +115,6 @@ require (
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
@@ -171,14 +169,13 @@ require (
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.13.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.36.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 // indirect
google.golang.org/grpc v1.75.1 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect

62
go.sum
View File

@@ -137,10 +137,10 @@ cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/Zur
cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac=
cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q=
cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU=
cloud.google.com/go/bigquery v1.71.0 h1:NvSZvXU1Hyb+YiRVKQPuQXGeZaw/0NP6M/WOrBqSx3g=
cloud.google.com/go/bigquery v1.71.0/go.mod h1:GUbRtmeCckOE85endLherHD9RsujY+gS7i++c1CqssQ=
cloud.google.com/go/bigtable v1.40.1 h1:k8HfpUOvn7sQwc6oNKqjvD/yjkwynf4qBuyKwh5cU08=
cloud.google.com/go/bigtable v1.40.1/go.mod h1:LtPzCcrAFaGRZ82Hs8xMueUeYW9Jw12AmNdUTMfDnh4=
cloud.google.com/go/bigquery v1.70.0 h1:V1OIhhOSionCOXWMmypXOvZu/ogkzosa7s1ArWJO/Yg=
cloud.google.com/go/bigquery v1.70.0/go.mod h1:6lEAkgTJN+H2JcaX1eKiuEHTKyqBaJq5U3SpLGbSvwI=
cloud.google.com/go/bigtable v1.40.0 h1:iNeqGqkJvFdjg07Ku3F7KKfq5QZvBySisYHVsLB1RwE=
cloud.google.com/go/bigtable v1.40.0/go.mod h1:LtPzCcrAFaGRZ82Hs8xMueUeYW9Jw12AmNdUTMfDnh4=
cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI=
@@ -194,8 +194,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/compute/metadata v0.8.4 h1:oXMa1VMQBVCyewMIOm3WQsnVd9FbKBtm8reqWRaXnHQ=
cloud.google.com/go/compute/metadata v0.8.4/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
@@ -216,8 +216,8 @@ cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOX
cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M=
cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0=
cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8=
cloud.google.com/go/datacatalog v1.26.1 h1:bCRKA8uSQN8wGW3Tw0gwko4E9a64GRmbW1nCblhgC2k=
cloud.google.com/go/datacatalog v1.26.1/go.mod h1:2Qcq8vsHNxMDgjgadRFmFG47Y+uuIVsyEGUrlrKEdrg=
cloud.google.com/go/datacatalog v1.26.0 h1:eFgygb3DTufTWWUB8ARk+dSuXz+aefNJXTlkWlQcWwE=
cloud.google.com/go/datacatalog v1.26.0/go.mod h1:bLN2HLBAwB3kLTFT5ZKLHVPj/weNz6bR0c7nYp0LE14=
cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE=
@@ -236,8 +236,8 @@ cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX
cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A=
cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ=
cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs=
cloud.google.com/go/dataplex v1.27.1 h1:renSEYTQZMQ3ag7lM0BDmSj4FWqaTGW60YQ/lvAE5iA=
cloud.google.com/go/dataplex v1.27.1/go.mod h1:VB+xlYJiJ5kreonXsa2cHPj0A3CfPh/mgiHG4JFhbUA=
cloud.google.com/go/dataplex v1.27.0 h1:k6gf5DnX7YHD/hilShxVP9ExmGrEWZFjfBZ7rHt4JlM=
cloud.google.com/go/dataplex v1.27.0/go.mod h1:VB+xlYJiJ5kreonXsa2cHPj0A3CfPh/mgiHG4JFhbUA=
cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s=
cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI=
cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4=
@@ -544,8 +544,8 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+
cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos=
cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk=
cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M=
cloud.google.com/go/spanner v1.86.0 h1:jlNWusBol1Jxa9PmYGknUBzLwvD1cebuEenzqebZ9xs=
cloud.google.com/go/spanner v1.86.0/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws=
cloud.google.com/go/spanner v1.85.1 h1:cJx1ZD//C2QIfFQl8hSTn4twL8amAXtnayyflRIjj40=
cloud.google.com/go/spanner v1.85.1/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws=
cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0=
@@ -737,10 +737,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4p
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
@@ -901,8 +897,6 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus=
github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
@@ -1050,8 +1044,6 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -1687,8 +1679,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1834,8 +1826,8 @@ google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0=
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.250.0 h1:qvkwrf/raASj82UegU2RSDGWi/89WkLckn4LuO4lVXM=
google.golang.org/api v0.250.0/go.mod h1:Y9Uup8bDLJJtMzJyQnu+rLRJLA0wn+wTtc6vTlOvfXo=
google.golang.org/api v0.249.0 h1:0VrsWAKzIZi058aeq+I86uIXbNhm9GxSHpbmZ92a38w=
google.golang.org/api v0.249.0/go.mod h1:dGk9qyI0UYPwO/cjt2q06LG/EhUpwZGdAbYF14wHHrQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1976,12 +1968,12 @@ google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20250929231259-57b25ae835d4 h1:HmI33/XNQ1jVwhb5ZUgot40oiwFHa2l5ZNkQpj8VaEg=
google.golang.org/genproto v0.0.0-20250929231259-57b25ae835d4/go.mod h1:OqVwZqqGV3h7k+YCVWXoTtwC2cs55RnDEUVMMadhxrc=
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9 h1:jm6v6kMRpTYKxBRrDkYAitNJegUeO1Mf3Kt80obv0gg=
google.golang.org/genproto/googleapis/api v0.0.0-20250922171735-9219d122eba9/go.mod h1:LmwNphe5Afor5V3R5BppOULHOnt2mCIf+NxMd4XiygE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9 h1:V1jCN2HBa8sySkR5vLcCSqJSTMv093Rw9EJefhQGP7M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250922171735-9219d122eba9/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
google.golang.org/genproto v0.0.0-20250826171959-ef028d996bc1 h1:Nm5SEGIguOIBDXs5rhfz2aKwEVWlgwC58UcmEnLDc8Y=
google.golang.org/genproto v0.0.0-20250826171959-ef028d996bc1/go.mod h1:Jz9LrroM7Mcm+a0QrLh4UpZ1B/WhjIbqwEcUf4y08nQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c h1:qXWI/sQtv5UKboZ/zUk7h+mrf/lXORyI+n9DKDAusdg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -2023,8 +2015,8 @@ google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -2043,15 +2035,13 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -75,17 +75,11 @@ func (t MockTool) RequiresClientAuthorization() bool {
func (t MockTool) McpManifest() tools.McpManifest {
properties := make(map[string]tools.ParameterMcpManifest)
required := make([]string, 0)
authParams := make(map[string][]string)
for _, p := range t.Params {
name := p.GetName()
paramManifest, authParamList := p.McpManifest()
properties[name] = paramManifest
properties[name] = p.McpManifest()
required = append(required, name)
if len(authParamList) > 0 {
authParams[name] = authParamList
}
}
toolsSchema := tools.McpToolsSchema{
@@ -94,19 +88,11 @@ func (t MockTool) McpManifest() tools.McpManifest {
Required: required,
}
mcpManifest := tools.McpManifest{
return tools.McpManifest{
Name: t.Name,
Description: t.Description,
InputSchema: toolsSchema,
}
if len(authParams) > 0 {
mcpManifest.Metadata = map[string]any{
"toolbox/authParams": authParams,
}
}
return mcpManifest
}
var tool1 = MockTool{

View File

@@ -117,15 +117,11 @@ func getOpts(ipType, userAgent string, useIAM bool) ([]alloydbconn.Option, error
}
func getConnectionConfig(ctx context.Context, user, pass, dbname string) (string, bool, error) {
userAgent, err := util.UserAgentFromContext(ctx)
if err != nil {
userAgent = "genai-toolbox"
}
useIAM := true
// If username and password both provided, use password authentication
if user != "" && pass != "" {
dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable application_name=%s", user, pass, dbname, userAgent)
dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", user, pass, dbname)
useIAM = false
return dsn, useIAM, nil
}
@@ -145,7 +141,7 @@ func getConnectionConfig(ctx context.Context, user, pass, dbname string) (string
}
// Construct IAM connection string with username
dsn := fmt.Sprintf("user=%s dbname=%s sslmode=disable application_name=%s", user, dbname, userAgent)
dsn := fmt.Sprintf("user=%s dbname=%s sslmode=disable", user, dbname)
return dsn, useIAM, nil
}

View File

@@ -1,134 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cassandra
import (
"context"
"fmt"
"github.com/goccy/go-yaml"
"github.com/gocql/gocql"
"github.com/googleapis/genai-toolbox/internal/sources"
"go.opentelemetry.io/otel/trace"
)
const SourceKind string = "cassandra"
func init() {
if !sources.Register(SourceKind, newConfig) {
panic(fmt.Sprintf("source kind %q already registered", SourceKind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type Config struct {
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
Hosts []string `yaml:"hosts" validate:"required"`
Keyspace string `yaml:"keyspace"`
ProtoVersion int `yaml:"protoVersion"`
Username string `yaml:"username"`
Password string `yaml:"password"`
CAPath string `yaml:"caPath"`
CertPath string `yaml:"certPath"`
KeyPath string `yaml:"keyPath"`
EnableHostVerification bool `yaml:"enableHostVerification"`
}
// Initialize implements sources.SourceConfig.
func (c Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
session, err := initCassandraSession(ctx, tracer, c)
if err != nil {
return nil, fmt.Errorf("unable to create session: %v", err)
}
s := &Source{
Name: c.Name,
Kind: SourceKind,
Session: session,
}
return s, nil
}
// SourceConfigKind implements sources.SourceConfig.
func (c Config) SourceConfigKind() string {
return SourceKind
}
var _ sources.SourceConfig = Config{}
type Source struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
Session *gocql.Session
}
// CassandraSession implements cassandra.compatibleSource.
func (s *Source) CassandraSession() *gocql.Session {
return s.Session
}
// SourceKind implements sources.Source.
func (s Source) SourceKind() string {
return SourceKind
}
var _ sources.Source = &Source{}
func initCassandraSession(ctx context.Context, tracer trace.Tracer, c Config) (*gocql.Session, error) {
//nolint:all // Reassigned ctx
ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, c.Name)
defer span.End()
// Validate authentication configuration
if c.Password != "" && c.Username == "" {
return nil, fmt.Errorf("invalid Cassandra configuration: password provided without a username")
}
cluster := gocql.NewCluster(c.Hosts...)
cluster.ProtoVersion = c.ProtoVersion
cluster.Keyspace = c.Keyspace
// Configure authentication if username is provided
if c.Username != "" {
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: c.Username,
Password: c.Password,
}
}
// Configure SSL options if any are specified
if c.CAPath != "" || c.CertPath != "" || c.KeyPath != "" || c.EnableHostVerification {
cluster.SslOpts = &gocql.SslOptions{
CaPath: c.CAPath,
CertPath: c.CertPath,
KeyPath: c.KeyPath,
EnableHostVerification: c.EnableHostVerification,
}
}
// Create session
session, err := cluster.CreateSession()
if err != nil {
return nil, fmt.Errorf("failed to create Cassandra session: %w", err)
}
return session, nil
}

View File

@@ -1,158 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cassandra_test
import (
"testing"
yaml "github.com/goccy/go-yaml"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/sources/cassandra"
"github.com/googleapis/genai-toolbox/internal/testutils"
)
func TestParseFromYamlCassandra(t *testing.T) {
tcs := []struct {
desc string
in string
want server.SourceConfigs
}{
{
desc: "basic example (without optional fields)",
in: `
sources:
my-cassandra-instance:
kind: cassandra
hosts:
- "my-host1"
- "my-host2"
`,
want: server.SourceConfigs{
"my-cassandra-instance": cassandra.Config{
Name: "my-cassandra-instance",
Kind: cassandra.SourceKind,
Hosts: []string{"my-host1", "my-host2"},
Username: "",
Password: "",
ProtoVersion: 0,
CAPath: "",
CertPath: "",
KeyPath: "",
Keyspace: "",
EnableHostVerification: false,
},
},
},
{
desc: "with optional fields",
in: `
sources:
my-cassandra-instance:
kind: cassandra
hosts:
- "my-host1"
- "my-host2"
username: "user"
password: "pass"
keyspace: "example_keyspace"
protoVersion: 4
caPath: "path/to/ca.crt"
certPath: "path/to/cert"
keyPath: "path/to/key"
enableHostVerification: true
`,
want: server.SourceConfigs{
"my-cassandra-instance": cassandra.Config{
Name: "my-cassandra-instance",
Kind: cassandra.SourceKind,
Hosts: []string{"my-host1", "my-host2"},
Username: "user",
Password: "pass",
Keyspace: "example_keyspace",
ProtoVersion: 4,
CAPath: "path/to/ca.crt",
CertPath: "path/to/cert",
KeyPath: "path/to/key",
EnableHostVerification: true,
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := struct {
Sources server.SourceConfigs `yaml:"sources"`
}{}
// Parse contents
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if !cmp.Equal(tc.want, got.Sources) {
t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
}
})
}
}
func TestFailParseFromYaml(t *testing.T) {
tcs := []struct {
desc string
in string
err string
}{
{
desc: "extra field",
in: `
sources:
my-cassandra-instance:
kind: cassandra
hosts:
- "my-host"
foo: bar
`,
err: "unable to parse source \"my-cassandra-instance\" as \"cassandra\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | hosts:\n 3 | - my-host\n 4 | kind: cassandra",
},
{
desc: "missing required field",
in: `
sources:
my-cassandra-instance:
kind: cassandra
`,
err: "unable to parse source \"my-cassandra-instance\" as \"cassandra\": Key: 'Config.Hosts' Error:Field validation for 'Hosts' failed on the 'required' tag",
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := struct {
Sources server.SourceConfigs `yaml:"sources"`
}{}
// Parse contents
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
if err == nil {
t.Fatalf("expect parsing to fail")
}
errStr := err.Error()
if errStr != tc.err {
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
}
})
}
}

View File

@@ -98,15 +98,11 @@ func (s *Source) PostgresPool() *pgxpool.Pool {
}
func getConnectionConfig(ctx context.Context, user, pass, dbname string) (string, bool, error) {
userAgent, err := util.UserAgentFromContext(ctx)
if err != nil {
userAgent = "genai-toolbox"
}
useIAM := true
// If username and password both provided, use password authentication
if user != "" && pass != "" {
dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable application_name=%s", user, pass, dbname, userAgent)
dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", user, pass, dbname)
useIAM = false
return dsn, useIAM, nil
}
@@ -126,7 +122,7 @@ func getConnectionConfig(ctx context.Context, user, pass, dbname string) (string
}
// Construct IAM connection string with username
dsn := fmt.Sprintf("user=%s dbname=%s sslmode=disable application_name=%s", user, dbname, userAgent)
dsn := fmt.Sprintf("user=%s dbname=%s sslmode=disable", user, dbname)
return dsn, useIAM, nil
}

View File

@@ -58,8 +58,8 @@ type Config struct {
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
BaseURL string `yaml:"base_url" validate:"required"`
ClientId string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
ClientId string `yaml:"client_id" validate:"required"`
ClientSecret string `yaml:"client_secret" validate:"required"`
SslVerification bool `yaml:"verify_ssl"`
UseClientOAuth bool `yaml:"use_client_oauth"`
Timeout string `yaml:"timeout"`

View File

@@ -100,8 +100,10 @@ func TestFailParseFromYamlLooker(t *testing.T) {
sources:
my-looker-instance:
kind: looker
base_url: http://example.looker.com/
client_id: jasdl;k;tjl
`,
err: "unable to parse source \"my-looker-instance\" as \"looker\": Key: 'Config.BaseURL' Error:Field validation for 'BaseURL' failed on the 'required' tag",
err: "unable to parse source \"my-looker-instance\" as \"looker\": Key: 'Config.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'required' tag",
},
}
for _, tc := range tcs {

View File

@@ -22,7 +22,6 @@ import (
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/jackc/pgx/v5/pgxpool"
"go.opentelemetry.io/otel/trace"
)
@@ -100,17 +99,6 @@ func initPostgresConnectionPool(ctx context.Context, tracer trace.Tracer, name,
//nolint:all // Reassigned ctx
ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
defer span.End()
userAgent, err := util.UserAgentFromContext(ctx)
if err != nil {
userAgent = "genai-toolbox"
}
if queryParams == nil {
// Initialize the map before using it
queryParams = make(map[string]string)
}
if _, ok := queryParams["application_name"]; !ok {
queryParams["application_name"] = userAgent
}
// urlExample := "postgres:dd//username:password@localhost:5432/database_name"
url := &url.URL{

View File

@@ -80,11 +80,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "cluster", "password"}
description := cfg.Description
if description == "" {
description = "Creates a new AlloyDB cluster. This is a long-running operation, but the API call returns quickly. This will return operation id to be used by get operations tool. Take all parameters from user in one go."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -81,11 +81,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "cluster", "instance"}
description := cfg.Description
if description == "" {
description = "Creates a new AlloyDB instance (PRIMARY or READ_POOL) within a cluster. This is a long-running operation. This will return operation id to be used by get operations tool. Take all parameters from user in one go."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -81,11 +81,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "cluster", "user", "userType"}
description := cfg.Description
if description == "" {
description = "Creates a new AlloyDB user within a cluster. Takes the new user's name and a secure password. Optionally, a list of database roles can be assigned. Always ask the user for the type of user to create. ALLOYDB_IAM_USER is recommended."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -77,11 +77,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "cluster"}
description := cfg.Description
if description == "" {
description = "Retrieves details about a specific AlloyDB cluster."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -78,11 +78,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "cluster", "instance"}
description := cfg.Description
if description == "" {
description = "Retrieves details about a specific AlloyDB instance."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -78,11 +78,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "cluster", "user"}
description := cfg.Description
if description == "" {
description = "Retrieves details about a specific AlloyDB user."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -76,11 +76,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project"}
description := cfg.Description
if description == "" {
description = "Lists all AlloyDB clusters in a given project and location."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -77,11 +77,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project"}
description := cfg.Description
if description == "" {
description = "Lists all AlloyDB instances in a given project, location and cluster."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -77,11 +77,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "cluster"}
description := cfg.Description
if description == "" {
description = "Lists all AlloyDB users in a given project, location and cluster."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -129,12 +129,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "location", "operation"}
description := cfg.Description
if description == "" {
description = "This will poll on operations API until the operation is done. For checking operation status we need projectId, locationID and operationId. Once instance is created give follow up steps on how to use the variables to bring data plane MCP server up in local and remote setup."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
var delay time.Duration
if cfg.Delay == "" {

View File

@@ -119,7 +119,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
cfg.NLConfigParameters = append([]tools.Parameter{newQuestionParam}, cfg.NLConfigParameters...)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, cfg.NLConfigParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: cfg.NLConfigParameters.McpManifest(),
}
t := Tool{
Name: cfg.Name,

View File

@@ -118,7 +118,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
pruningMethodParameter,
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{

View File

@@ -1,431 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bigquerycommon
import (
"fmt"
"strings"
"unicode"
)
// parserState defines the state of the SQL parser's state machine.
type parserState int
const (
stateNormal parserState = iota
// String states
stateInSingleQuoteString
stateInDoubleQuoteString
stateInTripleSingleQuoteString
stateInTripleDoubleQuoteString
stateInRawSingleQuoteString
stateInRawDoubleQuoteString
stateInRawTripleSingleQuoteString
stateInRawTripleDoubleQuoteString
// Comment states
stateInSingleLineCommentDash
stateInSingleLineCommentHash
stateInMultiLineComment
)
// SQL statement verbs
const (
verbCreate = "create"
verbAlter = "alter"
verbDrop = "drop"
verbSelect = "select"
verbInsert = "insert"
verbUpdate = "update"
verbDelete = "delete"
verbMerge = "merge"
)
var tableFollowsKeywords = map[string]bool{
"from": true,
"join": true,
"update": true,
"into": true, // INSERT INTO, MERGE INTO
"table": true, // CREATE TABLE, ALTER TABLE
"using": true, // MERGE ... USING
"insert": true, // INSERT my_table
"merge": true, // MERGE my_table
}
var tableContextExitKeywords = map[string]bool{
"where": true,
"group": true, // GROUP BY
"having": true,
"order": true, // ORDER BY
"limit": true,
"window": true,
"on": true, // JOIN ... ON
"set": true, // UPDATE ... SET
"when": true, // MERGE ... WHEN
}
// TableParser is the main entry point for parsing a SQL string to find all referenced table IDs.
// It handles multi-statement SQL, comments, and recursive parsing of EXECUTE IMMEDIATE statements.
func TableParser(sql, defaultProjectID string) ([]string, error) {
tableIDSet := make(map[string]struct{})
visitedSQLs := make(map[string]struct{})
if _, err := parseSQL(sql, defaultProjectID, tableIDSet, visitedSQLs, false); err != nil {
return nil, err
}
tableIDs := make([]string, 0, len(tableIDSet))
for id := range tableIDSet {
tableIDs = append(tableIDs, id)
}
return tableIDs, nil
}
// parseSQL is the core recursive function that processes SQL strings.
// It uses a state machine to find table names and recursively parse EXECUTE IMMEDIATE.
func parseSQL(sql, defaultProjectID string, tableIDSet map[string]struct{}, visitedSQLs map[string]struct{}, inSubquery bool) (int, error) {
// Prevent infinite recursion.
if _, ok := visitedSQLs[sql]; ok {
return len(sql), nil
}
visitedSQLs[sql] = struct{}{}
state := stateNormal
expectingTable := false
var lastTableKeyword, lastToken, statementVerb string
runes := []rune(sql)
for i := 0; i < len(runes); {
char := runes[i]
remaining := sql[i:]
switch state {
case stateNormal:
if strings.HasPrefix(remaining, "--") {
state = stateInSingleLineCommentDash
i += 2
continue
}
if strings.HasPrefix(remaining, "#") {
state = stateInSingleLineCommentHash
i++
continue
}
if strings.HasPrefix(remaining, "/*") {
state = stateInMultiLineComment
i += 2
continue
}
if char == '(' {
if expectingTable {
// The subquery starts after '('.
consumed, err := parseSQL(remaining[1:], defaultProjectID, tableIDSet, visitedSQLs, true)
if err != nil {
return 0, err
}
// Advance i by the length of the subquery + the opening parenthesis.
// The recursive call returns what it consumed, including the closing parenthesis.
i += consumed + 1
// For most keywords, we expect only one table. `from` can have multiple "tables" (subqueries).
if lastTableKeyword != "from" {
expectingTable = false
}
continue
}
}
if char == ')' {
if inSubquery {
return i + 1, nil
}
}
if char == ';' {
statementVerb = ""
lastToken = ""
i++
continue
}
// Raw strings must be checked before regular strings.
if strings.HasPrefix(remaining, "r'''") || strings.HasPrefix(remaining, "R'''") {
state = stateInRawTripleSingleQuoteString
i += 4
continue
}
if strings.HasPrefix(remaining, `r"""`) || strings.HasPrefix(remaining, `R"""`) {
state = stateInRawTripleDoubleQuoteString
i += 4
continue
}
if strings.HasPrefix(remaining, "r'") || strings.HasPrefix(remaining, "R'") {
state = stateInRawSingleQuoteString
i += 2
continue
}
if strings.HasPrefix(remaining, `r"`) || strings.HasPrefix(remaining, `R"`) {
state = stateInRawDoubleQuoteString
i += 2
continue
}
if strings.HasPrefix(remaining, "'''") {
state = stateInTripleSingleQuoteString
i += 3
continue
}
if strings.HasPrefix(remaining, `"""`) {
state = stateInTripleDoubleQuoteString
i += 3
continue
}
if char == '\'' {
state = stateInSingleQuoteString
i++
continue
}
if char == '"' {
state = stateInDoubleQuoteString
i++
continue
}
if unicode.IsLetter(char) || char == '`' {
parts, consumed, err := parseIdentifierSequence(remaining)
if err != nil {
return 0, err
}
if consumed == 0 {
i++
continue
}
if len(parts) == 1 {
keyword := strings.ToLower(parts[0])
switch keyword {
case "call":
return 0, fmt.Errorf("CALL is not allowed when dataset restrictions are in place, as the called procedure's contents cannot be safely analyzed")
case "immediate":
if lastToken == "execute" {
return 0, fmt.Errorf("EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place, as its contents cannot be safely analyzed")
}
case "procedure", "function":
if lastToken == "create" || lastToken == "create or replace" {
return 0, fmt.Errorf("unanalyzable statements like '%s %s' are not allowed", strings.ToUpper(lastToken), strings.ToUpper(keyword))
}
case verbCreate, verbAlter, verbDrop, verbSelect, verbInsert, verbUpdate, verbDelete, verbMerge:
if statementVerb == "" {
statementVerb = keyword
}
}
if statementVerb == verbCreate || statementVerb == verbAlter || statementVerb == verbDrop {
if keyword == "schema" || keyword == "dataset" {
return 0, fmt.Errorf("dataset-level operations like '%s %s' are not allowed when dataset restrictions are in place", strings.ToUpper(statementVerb), strings.ToUpper(keyword))
}
}
if _, ok := tableFollowsKeywords[keyword]; ok {
expectingTable = true
lastTableKeyword = keyword
} else if _, ok := tableContextExitKeywords[keyword]; ok {
expectingTable = false
lastTableKeyword = ""
}
if lastToken == "create" && keyword == "or" {
lastToken = "create or"
} else if lastToken == "create or" && keyword == "replace" {
lastToken = "create or replace"
} else {
lastToken = keyword
}
} else if len(parts) >= 2 {
// This is a multi-part identifier. If we were expecting a table, this is it.
if expectingTable {
tableID, err := formatTableID(parts, defaultProjectID)
if err != nil {
return 0, err
}
if tableID != "" {
tableIDSet[tableID] = struct{}{}
}
// For most keywords, we expect only one table.
if lastTableKeyword != "from" {
expectingTable = false
}
}
lastToken = ""
}
i += consumed
continue
}
i++
case stateInSingleQuoteString:
if char == '\\' {
i += 2 // Skip backslash and the escaped character.
continue
}
if char == '\'' {
state = stateNormal
}
i++
case stateInDoubleQuoteString:
if char == '\\' {
i += 2 // Skip backslash and the escaped character.
continue
}
if char == '"' {
state = stateNormal
}
i++
case stateInTripleSingleQuoteString:
if strings.HasPrefix(remaining, "'''") {
state = stateNormal
i += 3
} else {
i++
}
case stateInTripleDoubleQuoteString:
if strings.HasPrefix(remaining, `"""`) {
state = stateNormal
i += 3
} else {
i++
}
case stateInSingleLineCommentDash, stateInSingleLineCommentHash:
if char == '\n' {
state = stateNormal
}
i++
case stateInMultiLineComment:
if strings.HasPrefix(remaining, "*/") {
state = stateNormal
i += 2
} else {
i++
}
case stateInRawSingleQuoteString:
if char == '\'' {
state = stateNormal
}
i++
case stateInRawDoubleQuoteString:
if char == '"' {
state = stateNormal
}
i++
case stateInRawTripleSingleQuoteString:
if strings.HasPrefix(remaining, "'''") {
state = stateNormal
i += 3
} else {
i++
}
case stateInRawTripleDoubleQuoteString:
if strings.HasPrefix(remaining, `"""`) {
state = stateNormal
i += 3
} else {
i++
}
}
}
if inSubquery {
return 0, fmt.Errorf("unclosed subquery parenthesis")
}
return len(sql), nil
}
// parseIdentifierSequence parses a sequence of dot-separated identifiers.
// It returns the parts of the identifier, the number of characters consumed, and an error.
func parseIdentifierSequence(s string) ([]string, int, error) {
var parts []string
var totalConsumed int
for {
remaining := s[totalConsumed:]
trimmed := strings.TrimLeftFunc(remaining, unicode.IsSpace)
totalConsumed += len(remaining) - len(trimmed)
current := s[totalConsumed:]
if len(current) == 0 {
break
}
var part string
var consumed int
if current[0] == '`' {
end := strings.Index(current[1:], "`")
if end == -1 {
return nil, 0, fmt.Errorf("unclosed backtick identifier")
}
part = current[1 : end+1]
consumed = end + 2
} else if len(current) > 0 && unicode.IsLetter(rune(current[0])) {
end := strings.IndexFunc(current, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r) && r != '_' && r != '-'
})
if end == -1 {
part = current
consumed = len(current)
} else {
part = current[:end]
consumed = end
}
} else {
break
}
if current[0] == '`' && strings.Contains(part, ".") {
// This handles cases like `project.dataset.table` but not `project.dataset`.table.
// If the character after the quoted identifier is not a dot, we treat it as a full name.
if len(current) <= consumed || current[consumed] != '.' {
parts = append(parts, strings.Split(part, ".")...)
totalConsumed += consumed
break
}
}
parts = append(parts, strings.Split(part, ".")...)
totalConsumed += consumed
if len(s) <= totalConsumed || s[totalConsumed] != '.' {
break
}
totalConsumed++
}
return parts, totalConsumed, nil
}
func formatTableID(parts []string, defaultProjectID string) (string, error) {
if len(parts) < 2 || len(parts) > 3 {
// Not a table identifier (could be a CTE, column, etc.).
// Return the consumed length so the main loop can skip this identifier.
return "", nil
}
var tableID string
if len(parts) == 3 { // project.dataset.table
tableID = strings.Join(parts, ".")
} else { // dataset.table
if defaultProjectID == "" {
return "", fmt.Errorf("query contains table '%s' without project ID, and no default project ID is provided", strings.Join(parts, "."))
}
tableID = fmt.Sprintf("%s.%s", defaultProjectID, strings.Join(parts, "."))
}
return tableID, nil
}

View File

@@ -1,496 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bigquerycommon_test
import (
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
)
func TestTableParser(t *testing.T) {
testCases := []struct {
name string
sql string
defaultProjectID string
want []string
wantErr bool
wantErrMsg string
}{
{
name: "single fully qualified table",
sql: "SELECT * FROM `my-project.my_dataset.my_table`",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "multiple statements with same table",
sql: "select * from proj1.data1.tbl1 limit 1; select A.b from proj1.data1.tbl1 as A limit 1;",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1"},
wantErr: false,
},
{
name: "multiple fully qualified tables",
sql: "SELECT * FROM `proj1.data1`.`tbl1` JOIN proj2.`data2.tbl2` ON id",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1", "proj2.data2.tbl2"},
wantErr: false,
},
{
name: "duplicate tables",
sql: "SELECT * FROM `proj1.data1.tbl1` JOIN proj1.data1.tbl1 ON id",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1"},
wantErr: false,
},
{
name: "partial table with default project",
sql: "SELECT * FROM `my_dataset`.my_table",
defaultProjectID: "default-proj",
want: []string{"default-proj.my_dataset.my_table"},
wantErr: false,
},
{
name: "partial table without default project",
sql: "SELECT * FROM `my_dataset.my_table`",
defaultProjectID: "",
want: nil,
wantErr: true,
},
{
name: "mixed fully qualified and partial tables",
sql: "SELECT t1.*, t2.* FROM `proj1.data1.tbl1` AS t1 JOIN `data2.tbl2` AS t2 ON t1.id = t2.id",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1", "default-proj.data2.tbl2"},
wantErr: false,
},
{
name: "no tables",
sql: "SELECT 1+1",
defaultProjectID: "default-proj",
want: []string{},
wantErr: false,
},
{
name: "ignore single part identifiers (like CTEs)",
sql: "WITH my_cte AS (SELECT 1) SELECT * FROM `my_cte`",
defaultProjectID: "default-proj",
want: []string{},
wantErr: false,
},
{
name: "complex CTE",
sql: "WITH cte1 AS (SELECT * FROM `real.table.one`), cte2 AS (SELECT * FROM cte1) SELECT * FROM cte2 JOIN `real.table.two` ON true",
defaultProjectID: "default-proj",
want: []string{"real.table.one", "real.table.two"},
wantErr: false,
},
{
name: "nested subquery should be parsed",
sql: "SELECT * FROM (SELECT a FROM (SELECT A.b FROM `real.table.nested` AS A))",
defaultProjectID: "default-proj",
want: []string{"real.table.nested"},
wantErr: false,
},
{
name: "from clause with unnest",
sql: "SELECT event.name FROM `my-project.my_dataset.my_table` AS A, UNNEST(A.events) AS event",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "ignore more than 3 parts",
sql: "SELECT * FROM `proj.data.tbl.col`",
defaultProjectID: "default-proj",
want: []string{},
wantErr: false,
},
{
name: "complex query",
sql: "SELECT name FROM (SELECT name FROM `proj1.data1.tbl1`) UNION ALL SELECT name FROM `data2.tbl2`",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1", "default-proj.data2.tbl2"},
wantErr: false,
},
{
name: "empty sql",
sql: "",
defaultProjectID: "default-proj",
want: []string{},
wantErr: false,
},
{
name: "with comments",
sql: "SELECT * FROM `proj1.data1.tbl1`; -- comment `fake.table.one` \n SELECT * FROM `proj2.data2.tbl2`; # comment `fake.table.two`",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1", "proj2.data2.tbl2"},
wantErr: false,
},
{
name: "multi-statement with semicolon",
sql: "SELECT * FROM `proj1.data1.tbl1`; SELECT * FROM `proj2.data2.tbl2`",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1", "proj2.data2.tbl2"},
wantErr: false,
},
{
name: "simple execute immediate",
sql: "EXECUTE IMMEDIATE 'SELECT * FROM `exec.proj.tbl`'",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place",
},
{
name: "execute immediate with multiple spaces",
sql: "EXECUTE IMMEDIATE 'SELECT 1'",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place",
},
{
name: "execute immediate with newline",
sql: "EXECUTE\nIMMEDIATE 'SELECT 1'",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place",
},
{
name: "execute immediate with comment",
sql: "EXECUTE -- some comment\n IMMEDIATE 'SELECT * FROM `exec.proj.tbl`'",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place",
},
{
name: "nested execute immediate",
sql: "EXECUTE IMMEDIATE \"EXECUTE IMMEDIATE '''SELECT * FROM `nested.exec.tbl`'''\"",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place",
},
{
name: "begin execute immediate",
sql: "BEGIN EXECUTE IMMEDIATE 'SELECT * FROM `exec.proj.tbl`'; END;",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "EXECUTE IMMEDIATE is not allowed when dataset restrictions are in place",
},
{
name: "table inside string literal should be ignored",
sql: "SELECT * FROM `real.table.one` WHERE name = 'select * from `fake.table.two`'",
defaultProjectID: "default-proj",
want: []string{"real.table.one"},
wantErr: false,
},
{
name: "string with escaped single quote",
sql: "SELECT 'this is a string with an escaped quote \\' and a fake table `fake.table.one`' FROM `real.table.two`",
defaultProjectID: "default-proj",
want: []string{"real.table.two"},
wantErr: false,
},
{
name: "string with escaped double quote",
sql: `SELECT "this is a string with an escaped quote \" and a fake table ` + "`fake.table.one`" + `" FROM ` + "`real.table.two`",
defaultProjectID: "default-proj",
want: []string{"real.table.two"},
wantErr: false,
},
{
name: "multi-line comment",
sql: "/* `fake.table.1` */ SELECT * FROM `real.table.2`",
defaultProjectID: "default-proj",
want: []string{"real.table.2"},
wantErr: false,
},
{
name: "raw string with backslash should be ignored",
sql: "SELECT * FROM `real.table.one` WHERE name = r'a raw string with a \\ and a fake table `fake.table.two`'",
defaultProjectID: "default-proj",
want: []string{"real.table.one"},
wantErr: false,
},
{
name: "capital R raw string with quotes inside should be ignored",
sql: `SELECT * FROM ` + "`real.table.one`" + ` WHERE name = R"""a raw string with a ' and a " and a \ and a fake table ` + "`fake.table.two`" + `"""`,
defaultProjectID: "default-proj",
want: []string{"real.table.one"},
wantErr: false,
},
{
name: "triple quoted raw string should be ignored",
sql: "SELECT * FROM `real.table.one` WHERE name = r'''a raw string with a ' and a \" and a \\ and a fake table `fake.table.two`'''",
defaultProjectID: "default-proj",
want: []string{"real.table.one"},
wantErr: false,
},
{
name: "triple quoted capital R raw string should be ignored",
sql: `SELECT * FROM ` + "`real.table.one`" + ` WHERE name = R"""a raw string with a ' and a " and a \ and a fake table ` + "`fake.table.two`" + `"""`,
defaultProjectID: "default-proj",
want: []string{"real.table.one"},
wantErr: false,
},
{
name: "unquoted fully qualified table",
sql: "SELECT * FROM my-project.my_dataset.my_table",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "unquoted partial table with default project",
sql: "SELECT * FROM my_dataset.my_table",
defaultProjectID: "default-proj",
want: []string{"default-proj.my_dataset.my_table"},
wantErr: false,
},
{
name: "unquoted partial table without default project",
sql: "SELECT * FROM my_dataset.my_table",
defaultProjectID: "",
want: nil,
wantErr: true,
},
{
name: "mixed quoting style 1",
sql: "SELECT * FROM `my-project`.my_dataset.my_table",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "mixed quoting style 2",
sql: "SELECT * FROM `my-project`.`my_dataset`.my_table",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "mixed quoting style 3",
sql: "SELECT * FROM `my-project`.`my_dataset`.`my_table`",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "mixed quoted and unquoted tables",
sql: "SELECT * FROM `proj1.data1.tbl1` JOIN proj2.data2.tbl2 ON id",
defaultProjectID: "default-proj",
want: []string{"proj1.data1.tbl1", "proj2.data2.tbl2"},
wantErr: false,
},
{
name: "create table statement",
sql: "CREATE TABLE `my-project.my_dataset.my_table` (x INT64)",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "insert into statement",
sql: "INSERT INTO `my-project.my_dataset.my_table` (x) VALUES (1)",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "update statement",
sql: "UPDATE `my-project.my_dataset.my_table` SET x = 2 WHERE true",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "delete from statement",
sql: "DELETE FROM `my-project.my_dataset.my_table` WHERE true",
defaultProjectID: "default-proj",
want: []string{"my-project.my_dataset.my_table"},
wantErr: false,
},
{
name: "merge into statement",
sql: "MERGE `proj.data.target` T USING `proj.data.source` S ON T.id = S.id WHEN NOT MATCHED THEN INSERT ROW",
defaultProjectID: "default-proj",
want: []string{"proj.data.source", "proj.data.target"},
wantErr: false,
},
{
name: "create schema statement",
sql: "CREATE SCHEMA `my-project.my_dataset`",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "dataset-level operations like 'CREATE SCHEMA' are not allowed",
},
{
name: "create dataset statement",
sql: "CREATE DATASET `my-project.my_dataset`",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "dataset-level operations like 'CREATE DATASET' are not allowed",
},
{
name: "drop schema statement",
sql: "DROP SCHEMA `my-project.my_dataset`",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "dataset-level operations like 'DROP SCHEMA' are not allowed",
},
{
name: "drop dataset statement",
sql: "DROP DATASET `my-project.my_dataset`",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "dataset-level operations like 'DROP DATASET' are not allowed",
},
{
name: "alter schema statement",
sql: "ALTER SCHEMA my_dataset SET OPTIONS(description='new description')",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "dataset-level operations like 'ALTER SCHEMA' are not allowed",
},
{
name: "alter dataset statement",
sql: "ALTER DATASET my_dataset SET OPTIONS(description='new description')",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "dataset-level operations like 'ALTER DATASET' are not allowed",
},
{
name: "begin...end block",
sql: "BEGIN CREATE TABLE `proj.data.tbl1` (x INT64); INSERT `proj.data.tbl2` (y) VALUES (1); END;",
defaultProjectID: "default-proj",
want: []string{"proj.data.tbl1", "proj.data.tbl2"},
wantErr: false,
},
{
name: "complex begin...end block with comments and different quoting",
sql: `
BEGIN
-- Create a new table
CREATE TABLE proj.data.tbl1 (x INT64);
/* Insert some data from another table */
INSERT INTO ` + "`proj.data.tbl2`" + ` (y) SELECT y FROM proj.data.source;
END;`,
defaultProjectID: "default-proj",
want: []string{"proj.data.source", "proj.data.tbl1", "proj.data.tbl2"},
wantErr: false,
},
{
name: "call fully qualified procedure",
sql: "CALL my-project.my_dataset.my_procedure()",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "CALL is not allowed when dataset restrictions are in place",
},
{
name: "call partially qualified procedure",
sql: "CALL my_dataset.my_procedure()",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "CALL is not allowed when dataset restrictions are in place",
},
{
name: "call procedure in begin...end block",
sql: "BEGIN CALL proj.data.proc1(); SELECT * FROM proj.data.tbl1; END;",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "CALL is not allowed when dataset restrictions are in place",
},
{
name: "call procedure with newline",
sql: "CALL\nmy_dataset.my_procedure()",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "CALL is not allowed when dataset restrictions are in place",
},
{
name: "call procedure without default project should fail",
sql: "CALL my_dataset.my_procedure()",
defaultProjectID: "",
want: nil,
wantErr: true,
wantErrMsg: "CALL is not allowed when dataset restrictions are in place",
},
{
name: "create procedure statement",
sql: "CREATE PROCEDURE my_dataset.my_procedure() BEGIN SELECT 1; END;",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "unanalyzable statements like 'CREATE PROCEDURE' are not allowed",
},
{
name: "create or replace procedure statement",
sql: "CREATE\n OR \nREPLACE \nPROCEDURE my_dataset.my_procedure() BEGIN SELECT 1; END;",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "unanalyzable statements like 'CREATE OR REPLACE PROCEDURE' are not allowed",
},
{
name: "create function statement",
sql: "CREATE FUNCTION my_dataset.my_function() RETURNS INT64 AS (1);",
defaultProjectID: "default-proj",
want: nil,
wantErr: true,
wantErrMsg: "unanalyzable statements like 'CREATE FUNCTION' are not allowed",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got, err := bigquerycommon.TableParser(tc.sql, tc.defaultProjectID)
if (err != nil) != tc.wantErr {
t.Errorf("TableParser() error = %v, wantErr %v", err, tc.wantErr)
return
}
if tc.wantErr && tc.wantErrMsg != "" {
if err == nil || !strings.Contains(err.Error(), tc.wantErrMsg) {
t.Errorf("TableParser() error = %v, want err containing %q", err, tc.wantErrMsg)
}
}
// Sort slices to ensure comparison is order-independent.
sort.Strings(got)
sort.Strings(tc.want)
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("TableParser() mismatch (-want +got):\n%s", diff)
}
})
}
}

View File

@@ -1,55 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bigquerycommon
import (
"context"
"fmt"
bigqueryapi "cloud.google.com/go/bigquery"
bigqueryrestapi "google.golang.org/api/bigquery/v2"
)
// DryRunQuery performs a dry run of the SQL query to validate it and get metadata.
func DryRunQuery(ctx context.Context, restService *bigqueryrestapi.Service, projectID string, location string, sql string, params []*bigqueryrestapi.QueryParameter, connProps []*bigqueryapi.ConnectionProperty) (*bigqueryrestapi.Job, error) {
useLegacySql := false
restConnProps := make([]*bigqueryrestapi.ConnectionProperty, len(connProps))
for i, prop := range connProps {
restConnProps[i] = &bigqueryrestapi.ConnectionProperty{Key: prop.Key, Value: prop.Value}
}
jobToInsert := &bigqueryrestapi.Job{
JobReference: &bigqueryrestapi.JobReference{
ProjectId: projectID,
Location: location,
},
Configuration: &bigqueryrestapi.JobConfiguration{
DryRun: true,
Query: &bigqueryrestapi.JobConfigurationQuery{
Query: sql,
UseLegacySql: &useLegacySql,
ConnectionProperties: restConnProps,
QueryParameters: params,
},
},
}
insertResponse, err := restService.Jobs.Insert(projectID, jobToInsert).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("failed to insert dry run job: %w", err)
}
return insertResponse, nil
}

View File

@@ -149,7 +149,12 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
tableRefsParameter := tools.NewStringParameter("table_references", tableRefsDescription)
parameters := tools.Parameters{userQueryParameter, tableRefsParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// Get cloud-platform token source for Gemini Data Analytics API during initialization
var bigQueryTokenSourceWithScope oauth2.TokenSource

View File

@@ -18,14 +18,12 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
bigqueryrestapi "google.golang.org/api/bigquery/v2"
"google.golang.org/api/iterator"
@@ -52,8 +50,6 @@ type compatibleSource interface {
BigQueryRestService() *bigqueryrestapi.Service
BigQueryClientCreator() bigqueryds.BigqueryClientCreator
UseClientAuthorization() bool
IsDatasetAllowed(projectID, datasetID string) bool
BigQueryAllowedDatasets() []string
}
// validate compatible sources are still compatible
@@ -89,28 +85,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
sqlDescription := "The sql to execute."
allowedDatasets := s.BigQueryAllowedDatasets()
if len(allowedDatasets) > 0 {
datasetIDs := []string{}
for _, ds := range allowedDatasets {
datasetIDs = append(datasetIDs, fmt.Sprintf("`%s`", ds))
}
if len(datasetIDs) == 1 {
parts := strings.Split(allowedDatasets[0], ".")
if len(parts) < 2 {
return nil, fmt.Errorf("expected split to have 2 parts: %s", allowedDatasets[0])
}
datasetID := parts[1]
sqlDescription += fmt.Sprintf(" The query must only access the %s dataset. "+
"To query a table within this dataset (e.g., `my_table`), "+
"qualify it with the dataset id (e.g., `%s.my_table`).", datasetIDs[0], datasetID)
} else {
sqlDescription += fmt.Sprintf(" The query must only access datasets from the following list: %s.", strings.Join(datasetIDs, ", "))
}
}
sqlParameter := tools.NewStringParameter("sql", sqlDescription)
sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
dryRunParameter := tools.NewBooleanParameterWithDefault(
"dry_run",
false,
@@ -118,22 +93,25 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
"will be returned without running the query. Defaults to false.",
)
parameters := tools.Parameters{sqlParameter, dryRunParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientAuthorization(),
ClientCreator: s.BigQueryClientCreator(),
Client: s.BigQueryClient(),
RestService: s.BigQueryRestService(),
IsDatasetAllowed: s.IsDatasetAllowed,
AllowedDatasets: allowedDatasets,
manifest: tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientAuthorization(),
ClientCreator: s.BigQueryClientCreator(),
Client: s.BigQueryClient(),
RestService: s.BigQueryRestService(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}
@@ -148,13 +126,11 @@ type Tool struct {
UseClientOAuth bool `yaml:"useClientOAuth"`
Parameters tools.Parameters `yaml:"parameters"`
Client *bigqueryapi.Client
RestService *bigqueryrestapi.Service
ClientCreator bigqueryds.BigqueryClientCreator
IsDatasetAllowed func(projectID, datasetID string) bool
AllowedDatasets []string
manifest tools.Manifest
mcpManifest tools.McpManifest
Client *bigqueryapi.Client
RestService *bigqueryrestapi.Service
ClientCreator bigqueryds.BigqueryClientCreator
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -184,65 +160,10 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
}
}
dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, sql, nil, nil)
dryRunJob, err := dryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, sql)
if err != nil {
return nil, fmt.Errorf("query validation failed during dry run: %w", err)
}
statementType := dryRunJob.Statistics.Query.StatementType
if len(t.AllowedDatasets) > 0 {
switch statementType {
case "CREATE_SCHEMA", "DROP_SCHEMA", "ALTER_SCHEMA":
return nil, fmt.Errorf("dataset-level operations like '%s' are not allowed when dataset restrictions are in place", statementType)
case "CREATE_FUNCTION", "CREATE_TABLE_FUNCTION", "CREATE_PROCEDURE":
return nil, fmt.Errorf("creating stored routines ('%s') is not allowed when dataset restrictions are in place, as their contents cannot be safely analyzed", statementType)
case "CALL":
return nil, fmt.Errorf("calling stored procedures ('%s') is not allowed when dataset restrictions are in place, as their contents cannot be safely analyzed", statementType)
}
// Use a map to avoid duplicate table names.
tableIDSet := make(map[string]struct{})
// Get all tables from the dry run result. This is the most reliable method.
queryStats := dryRunJob.Statistics.Query
if queryStats != nil {
for _, tableRef := range queryStats.ReferencedTables {
tableIDSet[fmt.Sprintf("%s.%s.%s", tableRef.ProjectId, tableRef.DatasetId, tableRef.TableId)] = struct{}{}
}
if tableRef := queryStats.DdlTargetTable; tableRef != nil {
tableIDSet[fmt.Sprintf("%s.%s.%s", tableRef.ProjectId, tableRef.DatasetId, tableRef.TableId)] = struct{}{}
}
if tableRef := queryStats.DdlDestinationTable; tableRef != nil {
tableIDSet[fmt.Sprintf("%s.%s.%s", tableRef.ProjectId, tableRef.DatasetId, tableRef.TableId)] = struct{}{}
}
}
var tableNames []string
if len(tableIDSet) > 0 {
for tableID := range tableIDSet {
tableNames = append(tableNames, tableID)
}
} else if statementType != "SELECT" {
// If dry run yields no tables, fall back to the parser for non-SELECT statements
// to catch unsafe operations like EXECUTE IMMEDIATE.
parsedTables, parseErr := bqutil.TableParser(sql, t.Client.Project())
if parseErr != nil {
// If parsing fails (e.g., EXECUTE IMMEDIATE), we cannot guarantee safety, so we must fail.
return nil, fmt.Errorf("could not parse tables from query to validate against allowed datasets: %w", parseErr)
}
tableNames = parsedTables
}
for _, tableID := range tableNames {
parts := strings.Split(tableID, ".")
if len(parts) == 3 {
projectID, datasetID := parts[0], parts[1]
if !t.IsDatasetAllowed(projectID, datasetID) {
return nil, fmt.Errorf("query accesses dataset '%s.%s', which is not in the allowed list", projectID, datasetID)
}
}
}
}
if dryRun {
if dryRunJob != nil {
@@ -256,6 +177,8 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
return "Dry run was requested, but no job information was returned.", nil
}
statementType := dryRunJob.Statistics.Query.StatementType
// JobStatistics.QueryStatistics.StatementType
query := bqClient.Query(sql)
query.Location = bqClient.Location
@@ -325,3 +248,27 @@ func (t Tool) Authorized(verifiedAuthServices []string) bool {
func (t Tool) RequiresClientAuthorization() bool {
return t.UseClientOAuth
}
// dryRunQuery performs a dry run of the SQL query to validate it and get metadata.
func dryRunQuery(ctx context.Context, restService *bigqueryrestapi.Service, projectID string, location string, sql string) (*bigqueryrestapi.Job, error) {
useLegacySql := false
jobToInsert := &bigqueryrestapi.Job{
JobReference: &bigqueryrestapi.JobReference{
ProjectId: projectID,
Location: location,
},
Configuration: &bigqueryrestapi.JobConfiguration{
DryRun: true,
Query: &bigqueryrestapi.JobConfigurationQuery{
Query: sql,
UseLegacySql: &useLegacySql,
},
},
}
insertResponse, err := restService.Jobs.Insert(projectID, jobToInsert).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("failed to insert dry run job: %w", err)
}
return insertResponse, nil
}

View File

@@ -24,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
bigqueryrestapi "google.golang.org/api/bigquery/v2"
"google.golang.org/api/iterator"
@@ -51,8 +50,6 @@ type compatibleSource interface {
BigQueryRestService() *bigqueryrestapi.Service
BigQueryClientCreator() bigqueryds.BigqueryClientCreator
UseClientAuthorization() bool
IsDatasetAllowed(projectID, datasetID string) bool
BigQueryAllowedDatasets() []string
}
// validate compatible sources are still compatible
@@ -88,17 +85,8 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allowedDatasets := s.BigQueryAllowedDatasets()
historyDataDescription := "The table id or the query of the history time series data."
if len(allowedDatasets) > 0 {
datasetIDs := []string{}
for _, ds := range allowedDatasets {
datasetIDs = append(datasetIDs, fmt.Sprintf("`%s`", ds))
}
historyDataDescription += fmt.Sprintf(" The query or table must only access datasets from the following list: %s.", strings.Join(datasetIDs, ", "))
}
historyDataParameter := tools.NewStringParameter("history_data", historyDataDescription)
historyDataParameter := tools.NewStringParameter("history_data",
"The table id or the query of the history time series data.")
timestampColumnNameParameter := tools.NewStringParameter("timestamp_col",
"The name of the time series timestamp column.")
dataColumnNameParameter := tools.NewStringParameter("data_col",
@@ -110,22 +98,24 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
parameters := tools.Parameters{historyDataParameter,
timestampColumnNameParameter, dataColumnNameParameter, idColumnNameParameter, horizonParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientAuthorization(),
ClientCreator: s.BigQueryClientCreator(),
Client: s.BigQueryClient(),
RestService: s.BigQueryRestService(),
IsDatasetAllowed: s.IsDatasetAllowed,
AllowedDatasets: allowedDatasets,
manifest: tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
Name: cfg.Name,
Kind: kind,
Parameters: parameters,
AuthRequired: cfg.AuthRequired,
UseClientOAuth: s.UseClientAuthorization(),
ClientCreator: s.BigQueryClientCreator(),
Client: s.BigQueryClient(),
RestService: s.BigQueryRestService(),
manifest: tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}
@@ -140,13 +130,11 @@ type Tool struct {
UseClientOAuth bool `yaml:"useClientOAuth"`
Parameters tools.Parameters `yaml:"parameters"`
Client *bigqueryapi.Client
RestService *bigqueryrestapi.Service
ClientCreator bigqueryds.BigqueryClientCreator
IsDatasetAllowed func(projectID, datasetID string) bool
AllowedDatasets []string
manifest tools.Manifest
mcpManifest tools.McpManifest
Client *bigqueryapi.Client
RestService *bigqueryrestapi.Service
ClientCreator bigqueryds.BigqueryClientCreator
manifest tools.Manifest
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
@@ -187,48 +175,8 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
var historyDataSource string
trimmedUpperHistoryData := strings.TrimSpace(strings.ToUpper(historyData))
if strings.HasPrefix(trimmedUpperHistoryData, "SELECT") || strings.HasPrefix(trimmedUpperHistoryData, "WITH") {
if len(t.AllowedDatasets) > 0 {
dryRunJob, err := bqutil.DryRunQuery(ctx, t.RestService, t.Client.Project(), t.Client.Location, historyData, nil, nil)
if err != nil {
return nil, fmt.Errorf("query validation failed during dry run: %w", err)
}
statementType := dryRunJob.Statistics.Query.StatementType
if statementType != "SELECT" {
return nil, fmt.Errorf("the 'history_data' parameter only supports a table ID or a SELECT query. The provided query has statement type '%s'", statementType)
}
queryStats := dryRunJob.Statistics.Query
if queryStats != nil {
for _, tableRef := range queryStats.ReferencedTables {
if !t.IsDatasetAllowed(tableRef.ProjectId, tableRef.DatasetId) {
return nil, fmt.Errorf("query in history_data accesses dataset '%s.%s', which is not in the allowed list", tableRef.ProjectId, tableRef.DatasetId)
}
}
} else {
return nil, fmt.Errorf("could not analyze query in history_data to validate against allowed datasets")
}
}
historyDataSource = fmt.Sprintf("(%s)", historyData)
} else {
if len(t.AllowedDatasets) > 0 {
parts := strings.Split(historyData, ".")
var projectID, datasetID string
switch len(parts) {
case 3: // project.dataset.table
projectID = parts[0]
datasetID = parts[1]
case 2: // dataset.table
projectID = t.Client.Project()
datasetID = parts[0]
default:
return nil, fmt.Errorf("invalid table ID format for 'history_data': %q. Expected 'dataset.table' or 'project.dataset.table'", historyData)
}
if !t.IsDatasetAllowed(projectID, datasetID) {
return nil, fmt.Errorf("access to dataset '%s.%s' (from table '%s') is not allowed", projectID, datasetID, historyData)
}
}
historyDataSource = fmt.Sprintf("TABLE `%s`", historyData)
}

View File

@@ -87,7 +87,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
datasetParameter := tools.NewStringParameter(datasetKey, "The dataset to get metadata information.")
parameters := tools.Parameters{projectParameter, datasetParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{

View File

@@ -89,7 +89,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
tableParameter := tools.NewStringParameter(tableKey, "The table to get metadata information.")
parameters := tools.Parameters{projectParameter, datasetParameter, tableParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{

View File

@@ -87,7 +87,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
parameters := tools.Parameters{projectParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{

View File

@@ -128,7 +128,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
parameters := tools.Parameters{projectParameter, datasetParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
// finish tool setup
t := Tool{

View File

@@ -96,7 +96,12 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
if cfg.Description != "" {
description = cfg.Description
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: parameters.McpManifest(),
}
t := Tool{
Name: cfg.Name,

View File

@@ -26,7 +26,6 @@ import (
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
bigqueryrestapi "google.golang.org/api/bigquery/v2"
"google.golang.org/api/iterator"
)
@@ -90,12 +89,16 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
allParameters, paramManifest, paramMcpManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
if err != nil {
return nil, err
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
}
// finish tool setup
t := Tool{
@@ -233,7 +236,7 @@ func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken
query.Parameters = highLevelParams
query.Location = bqClient.Location
dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, newStatement, lowLevelParams, query.ConnectionProperties)
dryRunJob, err := dryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, newStatement, lowLevelParams, query.ConnectionProperties)
if err != nil {
// This is a fallback check in case the switch logic was bypassed.
return nil, fmt.Errorf("final query validation failed: %w", err)
@@ -316,3 +319,42 @@ func BQTypeStringFromToolType(toolType string) (string, error) {
return "", fmt.Errorf("unsupported tool parameter type for BigQuery: %s", toolType)
}
}
func dryRunQuery(
ctx context.Context,
restService *bigqueryrestapi.Service,
projectID string,
location string,
sql string,
params []*bigqueryrestapi.QueryParameter,
connProps []*bigqueryapi.ConnectionProperty,
) (*bigqueryrestapi.Job, error) {
useLegacySql := false
restConnProps := make([]*bigqueryrestapi.ConnectionProperty, len(connProps))
for i, prop := range connProps {
restConnProps[i] = &bigqueryrestapi.ConnectionProperty{Key: prop.Key, Value: prop.Value}
}
jobToInsert := &bigqueryrestapi.Job{
JobReference: &bigqueryrestapi.JobReference{
ProjectId: projectID,
Location: location,
},
Configuration: &bigqueryrestapi.JobConfiguration{
DryRun: true,
Query: &bigqueryrestapi.JobConfigurationQuery{
Query: sql,
UseLegacySql: &useLegacySql,
ConnectionProperties: restConnProps,
QueryParameters: params,
},
},
}
insertResponse, err := restService.Jobs.Insert(projectID, jobToInsert).Context(ctx).Do()
if err != nil {
return nil, fmt.Errorf("failed to insert dry run job: %w", err)
}
return insertResponse, nil
}

View File

@@ -81,12 +81,16 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
allParameters, paramManifest, paramMcpManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
if err != nil {
return nil, err
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
}
// finish tool setup
t := Tool{

View File

@@ -1,178 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cassandracql
import (
"context"
"fmt"
yaml "github.com/goccy/go-yaml"
"github.com/gocql/gocql"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/cassandra"
"github.com/googleapis/genai-toolbox/internal/tools"
)
const kind string = "cassandra-cql"
func init() {
if !tools.Register(kind, newConfig) {
panic(fmt.Sprintf("tool kind %q already registered", kind))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type compatibleSource interface {
CassandraSession() *gocql.Session
}
var _ compatibleSource = &cassandra.Source{}
var compatibleSources = [...]string{cassandra.SourceKind}
type Config struct {
Name string `yaml:"name" validate:"required"`
Kind string `yaml:"kind" validate:"required"`
Source string `yaml:"source" validate:"required"`
Description string `yaml:"description" validate:"required"`
Statement string `yaml:"statement" validate:"required"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
}
// Initialize implements tools.ToolConfig.
func (c Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
// verify source exists
rawS, ok := srcs[c.Source]
if !ok {
return nil, fmt.Errorf("no source named %q configured", c.Source)
}
// verify the source is compatible
s, ok := rawS.(compatibleSource)
if !ok {
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, err := tools.ProcessParameters(c.TemplateParameters, c.Parameters)
if err != nil {
return nil, err
}
mcpManifest := tools.GetMcpManifest(c.Name, c.Description, c.AuthRequired, allParameters)
t := Tool{
Name: c.Name,
Kind: kind,
Parameters: c.Parameters,
TemplateParameters: c.TemplateParameters,
AllParams: allParameters,
Statement: c.Statement,
AuthRequired: c.AuthRequired,
Session: s.CassandraSession(),
manifest: tools.Manifest{Description: c.Description, Parameters: paramManifest, AuthRequired: c.AuthRequired},
mcpManifest: mcpManifest,
}
return t, nil
}
// ToolConfigKind implements tools.ToolConfig.
func (c Config) ToolConfigKind() string {
return kind
}
var _ tools.ToolConfig = Config{}
type Tool struct {
Name string `yaml:"name"`
Kind string `yaml:"kind"`
AuthRequired []string `yaml:"authRequired"`
Parameters tools.Parameters `yaml:"parameters"`
TemplateParameters tools.Parameters `yaml:"templateParameters"`
AllParams tools.Parameters `yaml:"allParams"`
Session *gocql.Session
Statement string
manifest tools.Manifest
mcpManifest tools.McpManifest
}
// RequiresClientAuthorization implements tools.Tool.
func (t Tool) RequiresClientAuthorization() bool {
return false
}
// Authorized implements tools.Tool.
func (t Tool) Authorized(verifiedAuthServices []string) bool {
return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
}
// Invoke implements tools.Tool.
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
paramsMap := params.AsMap()
newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
}
newParams, err := tools.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
}
sliceParams := newParams.AsSlice()
iter := t.Session.Query(newStatement, sliceParams...).WithContext(ctx).Iter()
// Create a slice to store the out
var out []map[string]interface{}
// Scan results into a map and append to the slice
for {
row := make(map[string]interface{}) // Create a new map for each row
if !iter.MapScan(row) {
break // No more rows
}
out = append(out, row)
}
if err := iter.Close(); err != nil {
return nil, fmt.Errorf("unable to parse rows: %w", err)
}
return out, nil
}
// Manifest implements tools.Tool.
func (t Tool) Manifest() tools.Manifest {
return t.manifest
}
// McpManifest implements tools.Tool.
func (t Tool) McpManifest() tools.McpManifest {
return t.mcpManifest
}
// ParseParams implements tools.Tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
return tools.ParseParams(t.AllParams, data, claims)
}
var _ tools.Tool = Tool{}

View File

@@ -1,171 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cassandracql_test
import (
"testing"
yaml "github.com/goccy/go-yaml"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/testutils"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cassandra/cassandracql"
)
func TestParseFromYamlCassandra(t *testing.T) {
ctx, err := testutils.ContextWithNewLogger()
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
tcs := []struct {
desc string
in string
want server.ToolConfigs
}{
{
desc: "basic example",
in: `
tools:
example_tool:
kind: cassandra-cql
source: my-cassandra-instance
description: some description
statement: |
SELECT * FROM CQL_STATEMENT;
authRequired:
- my-google-auth-service
- other-auth-service
parameters:
- name: country
type: string
description: some description
authServices:
- name: my-google-auth-service
field: user_id
- name: other-auth-service
field: user_id
`,
want: server.ToolConfigs{
"example_tool": cassandracql.Config{
Name: "example_tool",
Kind: "cassandra-cql",
Source: "my-cassandra-instance",
Description: "some description",
Statement: "SELECT * FROM CQL_STATEMENT;\n",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
Parameters: []tools.Parameter{
tools.NewStringParameterWithAuth("country", "some description",
[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
{Name: "other-auth-service", Field: "user_id"}}),
},
},
},
},
{
desc: "with template parameters",
in: `
tools:
example_tool:
kind: cassandra-cql
source: my-cassandra-instance
description: some description
statement: |
SELECT * FROM CQL_STATEMENT;
authRequired:
- my-google-auth-service
- other-auth-service
parameters:
- name: country
type: string
description: some description
authServices:
- name: my-google-auth-service
field: user_id
- name: other-auth-service
field: user_id
templateParameters:
- name: tableName
type: string
description: some description.
- name: fieldArray
type: array
description: The columns to return for the query.
items:
name: column
type: string
description: A column name that will be returned from the query.
`,
want: server.ToolConfigs{
"example_tool": cassandracql.Config{
Name: "example_tool",
Kind: "cassandra-cql",
Source: "my-cassandra-instance",
Description: "some description",
Statement: "SELECT * FROM CQL_STATEMENT;\n",
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
Parameters: []tools.Parameter{
tools.NewStringParameterWithAuth("country", "some description",
[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
{Name: "other-auth-service", Field: "user_id"}}),
},
TemplateParameters: []tools.Parameter{
tools.NewStringParameter("tableName", "some description."),
tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
},
},
},
},
{
desc: "without optional fields",
in: `
tools:
example_tool:
kind: cassandra-cql
source: my-cassandra-instance
description: some description
statement: |
SELECT * FROM CQL_STATEMENT;
`,
want: server.ToolConfigs{
"example_tool": cassandracql.Config{
Name: "example_tool",
Kind: "cassandra-cql",
Source: "my-cassandra-instance",
Description: "some description",
Statement: "SELECT * FROM CQL_STATEMENT;\n",
AuthRequired: []string{},
Parameters: nil,
TemplateParameters: nil,
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := struct {
Tools server.ToolConfigs `yaml:"tools"`
}{}
// Parse contents
err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
t.Fatalf("incorrect parse: diff %v", diff)
}
})
}
}

View File

@@ -74,7 +74,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
sqlParameter := tools.NewStringParameter("sql", "The SQL statement to execute.")
parameters := tools.Parameters{sqlParameter}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
t := ExecuteSQLTool{
Name: cfg.Name,

View File

@@ -72,8 +72,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", listDatabasesKind, compatibleSources)
}
allParameters, paramManifest, _ := tools.ProcessParameters(nil, cfg.Parameters)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
allParameters, paramManifest, paramMcpManifest, _ := tools.ProcessParameters(nil, cfg.Parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
}
t := Tool{
Name: cfg.Name,

View File

@@ -76,8 +76,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
databaseParameter := tools.NewStringParameter(databaseKey, "The database to list tables from.")
parameters := tools.Parameters{databaseParameter}
allParameters, paramManifest, _ := tools.ProcessParameters(nil, parameters)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
allParameters, paramManifest, paramMcpManifest, _ := tools.ProcessParameters(nil, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
}
t := Tool{
Name: cfg.Name,

View File

@@ -74,8 +74,13 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", sqlKind, compatibleSources)
}
allParameters, paramManifest, _ := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
allParameters, paramManifest, paramMcpManifest, _ := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
}
t := Tool{
Name: cfg.Name,

View File

@@ -77,8 +77,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
tools.NewStringParameterWithRequired("query", "The promql query to execute.", true),
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
return Tool{
Name: cfg.Name,
Kind: kind,
@@ -88,7 +86,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
UserAgent: s.UserAgent,
Client: s.Client,
manifest: tools.Manifest{Description: cfg.Description, Parameters: allParameters.Manifest()},
mcpManifest: mcpManifest,
mcpManifest: tools.McpManifest{Name: cfg.Name, Description: cfg.Description, InputSchema: allParameters.McpManifest()},
}, nil
}

View File

@@ -76,11 +76,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "instance", "name"}
description := cfg.Description
if description == "" {
description = "Creates a new database in a Cloud SQL instance."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -78,11 +78,18 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
description := cfg.Description
if description == "" {
description = "Creates a new user in a Cloud SQL instance. Both built-in and IAM users are supported. IAM users require an email account as the user name. IAM is the more secure and recommended way to manage users. The agent should always ask the user what type of user they want to create. For more information, see https://cloud.google.com/sql/docs/postgres/add-manage-iam-users"
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -75,11 +75,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"projectId", "instanceId"}
description := cfg.Description
if description == "" {
description = "Gets a particular cloud sql instance."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -74,11 +74,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "instance"}
description := cfg.Description
if description == "" {
description = "Lists all databases for a Cloud SQL instance."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -73,11 +73,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project"}
description := cfg.Description
if description == "" {
description = "Lists all type of Cloud SQL instances for a project."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -128,11 +128,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "operation"}
description := cfg.Description
if description == "" {
description = "This will poll on operations API until the operation is done. For checking operation status we need projectId and operationId. Once instance is created give follow up steps on how to use the variables to bring data plane MCP server up in local and remote setup."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
var delay time.Duration
if cfg.Delay == "" {

View File

@@ -79,11 +79,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "name", "editionPreset", "rootPassword"}
description := cfg.Description
if description == "" {
description = "Creates a SQL Server instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 8 GiB RAM (`db-custom-2-8192`) configuration with Non-HA/zonal availability. For the `Production` template, it chooses a 4 vCPU, 26 GiB RAM (`db-custom-4-26624`) configuration with HA/regional availability. The Enterprise edition is used in both cases. The default database version is `SQLSERVER_2022_STANDARD`. The agent should ask the user if they want to use a different version."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -79,11 +79,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "name", "editionPreset", "rootPassword"}
description := cfg.Description
if description == "" {
description = "Creates a MySQL instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 16 GiB RAM, 100 GiB SSD configuration with Non-HA/zonal availability. For the `Production` template, it chooses an 8 vCPU, 64 GiB RAM, 250 GiB SSD configuration with HA/regional availability. The Enterprise Plus edition is used in both cases. The default database version is `MYSQL_8_4`. The agent should ask the user if they want to use a different version."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -79,11 +79,19 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
paramManifest := allParameters.Manifest()
inputSchema := allParameters.McpManifest()
inputSchema.Required = []string{"project", "name", "editionPreset", "rootPassword"}
description := cfg.Description
if description == "" {
description = "Creates a Postgres instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 16 GiB RAM, 100 GiB SSD configuration with Non-HA/zonal availability. For the `Production` template, it chooses an 8 vCPU, 64 GiB RAM, 250 GiB SSD configuration with HA/regional availability. The Enterprise Plus edition is used in both cases. The default database version is `POSTGRES_17`. The agent should ask the user if they want to use a different version."
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: description,
InputSchema: inputSchema,
}
return Tool{
Name: cfg.Name,

View File

@@ -83,12 +83,16 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
allParameters, paramManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
allParameters, paramManifest, paramMcpManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
if err != nil {
return nil, err
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: paramMcpManifest,
}
// finish tool setup
t := Tool{
Name: cfg.Name,

View File

@@ -100,7 +100,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
entry := tools.NewStringParameter("entry", "The resource name of the Entry in the following form: projects/{project}/locations/{location}/entryGroups/{entryGroup}/entries/{entry}.")
parameters := tools.Parameters{name, view, aspectTypes, entry}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
t := Tool{
Name: cfg.Name,

View File

@@ -85,7 +85,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
orderBy := tools.NewStringParameterWithDefault("orderBy", "relevance", "Specifies the ordering of results. Supported values are: relevance, last_modified_timestamp, last_modified_timestamp asc")
parameters := tools.Parameters{query, pageSize, orderBy}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
t := Tool{
Name: cfg.Name,

View File

@@ -84,7 +84,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
orderBy := tools.NewStringParameterWithDefault("orderBy", "relevance", "Specifies the ordering of results. Supported values are: relevance, last_modified_timestamp, last_modified_timestamp asc")
parameters := tools.Parameters{query, pageSize, orderBy}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: parameters.McpManifest(),
}
t := Tool{
Name: cfg.Name,

View File

@@ -82,7 +82,11 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, cfg.Parameters)
mcpManifest := tools.McpManifest{
Name: cfg.Name,
Description: cfg.Description,
InputSchema: cfg.Parameters.McpManifest(),
}
// finish tool setup
t := Tool{

Some files were not shown because too many files have changed in this diff Show More