#
tokens: 49734/50000 23/786 files (page 10/45)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 10 of 45. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .ci
│   ├── continuous.release.cloudbuild.yaml
│   ├── generate_release_table.sh
│   ├── integration.cloudbuild.yaml
│   ├── quickstart_test
│   │   ├── go.integration.cloudbuild.yaml
│   │   ├── js.integration.cloudbuild.yaml
│   │   ├── py.integration.cloudbuild.yaml
│   │   ├── run_go_tests.sh
│   │   ├── run_js_tests.sh
│   │   ├── run_py_tests.sh
│   │   └── setup_hotels_sample.sql
│   ├── test_with_coverage.sh
│   └── versioned.release.cloudbuild.yaml
├── .github
│   ├── auto-label.yaml
│   ├── blunderbuss.yml
│   ├── CODEOWNERS
│   ├── header-checker-lint.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── label-sync.yml
│   ├── labels.yaml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── release-please.yml
│   ├── renovate.json5
│   ├── sync-repo-settings.yaml
│   └── workflows
│       ├── cloud_build_failure_reporter.yml
│       ├── deploy_dev_docs.yaml
│       ├── deploy_previous_version_docs.yaml
│       ├── deploy_versioned_docs.yaml
│       ├── docs_deploy.yaml
│       ├── docs_preview_clean.yaml
│       ├── docs_preview_deploy.yaml
│       ├── lint.yaml
│       ├── schedule_reporter.yml
│       ├── sync-labels.yaml
│       └── tests.yaml
├── .gitignore
├── .gitmodules
├── .golangci.yaml
├── .hugo
│   ├── archetypes
│   │   └── default.md
│   ├── assets
│   │   ├── icons
│   │   │   └── logo.svg
│   │   └── scss
│   │       ├── _styles_project.scss
│   │       └── _variables_project.scss
│   ├── go.mod
│   ├── go.sum
│   ├── hugo.toml
│   ├── layouts
│   │   ├── _default
│   │   │   └── home.releases.releases
│   │   ├── index.llms-full.txt
│   │   ├── index.llms.txt
│   │   ├── partials
│   │   │   ├── hooks
│   │   │   │   └── head-end.html
│   │   │   ├── navbar-version-selector.html
│   │   │   ├── page-meta-links.html
│   │   │   └── td
│   │   │       └── render-heading.html
│   │   ├── robot.txt
│   │   └── shortcodes
│   │       ├── include.html
│   │       ├── ipynb.html
│   │       └── regionInclude.html
│   ├── package-lock.json
│   ├── package.json
│   └── static
│       ├── favicons
│       │   ├── android-chrome-192x192.png
│       │   ├── android-chrome-512x512.png
│       │   ├── apple-touch-icon.png
│       │   ├── favicon-16x16.png
│       │   ├── favicon-32x32.png
│       │   └── favicon.ico
│       └── js
│           └── w3.js
├── CHANGELOG.md
├── cmd
│   ├── options_test.go
│   ├── options.go
│   ├── root_test.go
│   ├── root.go
│   └── version.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPER.md
├── Dockerfile
├── docs
│   └── en
│       ├── _index.md
│       ├── about
│       │   ├── _index.md
│       │   └── faq.md
│       ├── concepts
│       │   ├── _index.md
│       │   └── telemetry
│       │       ├── index.md
│       │       ├── telemetry_flow.png
│       │       └── telemetry_traces.png
│       ├── getting-started
│       │   ├── _index.md
│       │   ├── colab_quickstart.ipynb
│       │   ├── configure.md
│       │   ├── introduction
│       │   │   ├── _index.md
│       │   │   └── architecture.png
│       │   ├── local_quickstart_go.md
│       │   ├── local_quickstart_js.md
│       │   ├── local_quickstart.md
│       │   ├── mcp_quickstart
│       │   │   ├── _index.md
│       │   │   ├── inspector_tools.png
│       │   │   └── inspector.png
│       │   └── quickstart
│       │       ├── go
│       │       │   ├── genAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── genkit
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── langchain
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── openAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   └── quickstart_test.go
│       │       ├── golden.txt
│       │       ├── js
│       │       │   ├── genAI
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── genkit
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── langchain
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── llamaindex
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   └── quickstart.test.js
│       │       ├── python
│       │       │   ├── __init__.py
│       │       │   ├── adk
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── core
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── langchain
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── llamaindex
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   └── quickstart_test.py
│       │       └── shared
│       │           ├── cloud_setup.md
│       │           ├── configure_toolbox.md
│       │           └── database_setup.md
│       ├── how-to
│       │   ├── _index.md
│       │   ├── connect_via_geminicli.md
│       │   ├── connect_via_mcp.md
│       │   ├── connect-ide
│       │   │   ├── _index.md
│       │   │   ├── alloydb_pg_admin_mcp.md
│       │   │   ├── alloydb_pg_mcp.md
│       │   │   ├── bigquery_mcp.md
│       │   │   ├── cloud_sql_mssql_admin_mcp.md
│       │   │   ├── cloud_sql_mssql_mcp.md
│       │   │   ├── cloud_sql_mysql_admin_mcp.md
│       │   │   ├── cloud_sql_mysql_mcp.md
│       │   │   ├── cloud_sql_pg_admin_mcp.md
│       │   │   ├── cloud_sql_pg_mcp.md
│       │   │   ├── firestore_mcp.md
│       │   │   ├── looker_mcp.md
│       │   │   ├── mssql_mcp.md
│       │   │   ├── mysql_mcp.md
│       │   │   ├── neo4j_mcp.md
│       │   │   ├── postgres_mcp.md
│       │   │   ├── spanner_mcp.md
│       │   │   └── sqlite_mcp.md
│       │   ├── deploy_docker.md
│       │   ├── deploy_gke.md
│       │   ├── deploy_toolbox.md
│       │   ├── export_telemetry.md
│       │   └── toolbox-ui
│       │       ├── edit-headers.gif
│       │       ├── edit-headers.png
│       │       ├── index.md
│       │       ├── optional-param-checked.png
│       │       ├── optional-param-unchecked.png
│       │       ├── run-tool.gif
│       │       ├── tools.png
│       │       └── toolsets.png
│       ├── reference
│       │   ├── _index.md
│       │   ├── cli.md
│       │   └── prebuilt-tools.md
│       ├── resources
│       │   ├── _index.md
│       │   ├── authServices
│       │   │   ├── _index.md
│       │   │   └── google.md
│       │   ├── sources
│       │   │   ├── _index.md
│       │   │   ├── alloydb-admin.md
│       │   │   ├── alloydb-pg.md
│       │   │   ├── bigquery.md
│       │   │   ├── bigtable.md
│       │   │   ├── cassandra.md
│       │   │   ├── clickhouse.md
│       │   │   ├── cloud-monitoring.md
│       │   │   ├── cloud-sql-admin.md
│       │   │   ├── cloud-sql-mssql.md
│       │   │   ├── cloud-sql-mysql.md
│       │   │   ├── cloud-sql-pg.md
│       │   │   ├── couchbase.md
│       │   │   ├── dataplex.md
│       │   │   ├── dgraph.md
│       │   │   ├── firebird.md
│       │   │   ├── firestore.md
│       │   │   ├── http.md
│       │   │   ├── looker.md
│       │   │   ├── mongodb.md
│       │   │   ├── mssql.md
│       │   │   ├── mysql.md
│       │   │   ├── neo4j.md
│       │   │   ├── oceanbase.md
│       │   │   ├── oracle.md
│       │   │   ├── postgres.md
│       │   │   ├── redis.md
│       │   │   ├── spanner.md
│       │   │   ├── sqlite.md
│       │   │   ├── tidb.md
│       │   │   ├── trino.md
│       │   │   ├── valkey.md
│       │   │   └── yugabytedb.md
│       │   └── tools
│       │       ├── _index.md
│       │       ├── alloydb
│       │       │   ├── _index.md
│       │       │   ├── alloydb-create-cluster.md
│       │       │   ├── alloydb-create-instance.md
│       │       │   ├── alloydb-create-user.md
│       │       │   ├── alloydb-get-cluster.md
│       │       │   ├── alloydb-get-instance.md
│       │       │   ├── alloydb-get-user.md
│       │       │   ├── alloydb-list-clusters.md
│       │       │   ├── alloydb-list-instances.md
│       │       │   ├── alloydb-list-users.md
│       │       │   └── alloydb-wait-for-operation.md
│       │       ├── alloydbainl
│       │       │   ├── _index.md
│       │       │   └── alloydb-ai-nl.md
│       │       ├── bigquery
│       │       │   ├── _index.md
│       │       │   ├── bigquery-analyze-contribution.md
│       │       │   ├── bigquery-conversational-analytics.md
│       │       │   ├── bigquery-execute-sql.md
│       │       │   ├── bigquery-forecast.md
│       │       │   ├── bigquery-get-dataset-info.md
│       │       │   ├── bigquery-get-table-info.md
│       │       │   ├── bigquery-list-dataset-ids.md
│       │       │   ├── bigquery-list-table-ids.md
│       │       │   ├── bigquery-search-catalog.md
│       │       │   └── bigquery-sql.md
│       │       ├── bigtable
│       │       │   ├── _index.md
│       │       │   └── bigtable-sql.md
│       │       ├── cassandra
│       │       │   ├── _index.md
│       │       │   └── cassandra-cql.md
│       │       ├── clickhouse
│       │       │   ├── _index.md
│       │       │   ├── clickhouse-execute-sql.md
│       │       │   ├── clickhouse-list-databases.md
│       │       │   ├── clickhouse-list-tables.md
│       │       │   └── clickhouse-sql.md
│       │       ├── cloudmonitoring
│       │       │   ├── _index.md
│       │       │   └── cloud-monitoring-query-prometheus.md
│       │       ├── cloudsql
│       │       │   ├── _index.md
│       │       │   ├── cloudsqlcreatedatabase.md
│       │       │   ├── cloudsqlcreateusers.md
│       │       │   ├── cloudsqlgetinstances.md
│       │       │   ├── cloudsqllistdatabases.md
│       │       │   ├── cloudsqllistinstances.md
│       │       │   ├── cloudsqlmssqlcreateinstance.md
│       │       │   ├── cloudsqlmysqlcreateinstance.md
│       │       │   ├── cloudsqlpgcreateinstances.md
│       │       │   └── cloudsqlwaitforoperation.md
│       │       ├── couchbase
│       │       │   ├── _index.md
│       │       │   └── couchbase-sql.md
│       │       ├── dataform
│       │       │   ├── _index.md
│       │       │   └── dataform-compile-local.md
│       │       ├── dataplex
│       │       │   ├── _index.md
│       │       │   ├── dataplex-lookup-entry.md
│       │       │   ├── dataplex-search-aspect-types.md
│       │       │   └── dataplex-search-entries.md
│       │       ├── dgraph
│       │       │   ├── _index.md
│       │       │   └── dgraph-dql.md
│       │       ├── firebird
│       │       │   ├── _index.md
│       │       │   ├── firebird-execute-sql.md
│       │       │   └── firebird-sql.md
│       │       ├── firestore
│       │       │   ├── _index.md
│       │       │   ├── firestore-add-documents.md
│       │       │   ├── firestore-delete-documents.md
│       │       │   ├── firestore-get-documents.md
│       │       │   ├── firestore-get-rules.md
│       │       │   ├── firestore-list-collections.md
│       │       │   ├── firestore-query-collection.md
│       │       │   ├── firestore-query.md
│       │       │   ├── firestore-update-document.md
│       │       │   └── firestore-validate-rules.md
│       │       ├── http
│       │       │   ├── _index.md
│       │       │   └── http.md
│       │       ├── looker
│       │       │   ├── _index.md
│       │       │   ├── looker-add-dashboard-element.md
│       │       │   ├── looker-conversational-analytics.md
│       │       │   ├── looker-get-dashboards.md
│       │       │   ├── looker-get-dimensions.md
│       │       │   ├── looker-get-explores.md
│       │       │   ├── looker-get-filters.md
│       │       │   ├── looker-get-looks.md
│       │       │   ├── looker-get-measures.md
│       │       │   ├── looker-get-models.md
│       │       │   ├── looker-get-parameters.md
│       │       │   ├── looker-health-analyze.md
│       │       │   ├── looker-health-pulse.md
│       │       │   ├── looker-health-vacuum.md
│       │       │   ├── looker-make-dashboard.md
│       │       │   ├── looker-make-look.md
│       │       │   ├── looker-query-sql.md
│       │       │   ├── looker-query-url.md
│       │       │   ├── looker-query.md
│       │       │   └── looker-run-look.md
│       │       ├── mongodb
│       │       │   ├── _index.md
│       │       │   ├── mongodb-aggregate.md
│       │       │   ├── mongodb-delete-many.md
│       │       │   ├── mongodb-delete-one.md
│       │       │   ├── mongodb-find-one.md
│       │       │   ├── mongodb-find.md
│       │       │   ├── mongodb-insert-many.md
│       │       │   ├── mongodb-insert-one.md
│       │       │   ├── mongodb-update-many.md
│       │       │   └── mongodb-update-one.md
│       │       ├── mssql
│       │       │   ├── _index.md
│       │       │   ├── mssql-execute-sql.md
│       │       │   ├── mssql-list-tables.md
│       │       │   └── mssql-sql.md
│       │       ├── mysql
│       │       │   ├── _index.md
│       │       │   ├── mysql-execute-sql.md
│       │       │   ├── mysql-list-active-queries.md
│       │       │   ├── mysql-list-table-fragmentation.md
│       │       │   ├── mysql-list-tables-missing-unique-indexes.md
│       │       │   ├── mysql-list-tables.md
│       │       │   └── mysql-sql.md
│       │       ├── neo4j
│       │       │   ├── _index.md
│       │       │   ├── neo4j-cypher.md
│       │       │   ├── neo4j-execute-cypher.md
│       │       │   └── neo4j-schema.md
│       │       ├── oceanbase
│       │       │   ├── _index.md
│       │       │   ├── oceanbase-execute-sql.md
│       │       │   └── oceanbase-sql.md
│       │       ├── oracle
│       │       │   ├── _index.md
│       │       │   ├── oracle-execute-sql.md
│       │       │   └── oracle-sql.md
│       │       ├── postgres
│       │       │   ├── _index.md
│       │       │   ├── postgres-execute-sql.md
│       │       │   ├── postgres-list-active-queries.md
│       │       │   ├── postgres-list-available-extensions.md
│       │       │   ├── postgres-list-installed-extensions.md
│       │       │   ├── postgres-list-tables.md
│       │       │   └── postgres-sql.md
│       │       ├── redis
│       │       │   ├── _index.md
│       │       │   └── redis.md
│       │       ├── spanner
│       │       │   ├── _index.md
│       │       │   ├── spanner-execute-sql.md
│       │       │   ├── spanner-list-tables.md
│       │       │   └── spanner-sql.md
│       │       ├── sqlite
│       │       │   ├── _index.md
│       │       │   ├── sqlite-execute-sql.md
│       │       │   └── sqlite-sql.md
│       │       ├── tidb
│       │       │   ├── _index.md
│       │       │   ├── tidb-execute-sql.md
│       │       │   └── tidb-sql.md
│       │       ├── trino
│       │       │   ├── _index.md
│       │       │   ├── trino-execute-sql.md
│       │       │   └── trino-sql.md
│       │       ├── utility
│       │       │   ├── _index.md
│       │       │   └── wait.md
│       │       ├── valkey
│       │       │   ├── _index.md
│       │       │   └── valkey.md
│       │       └── yuagbytedb
│       │           ├── _index.md
│       │           └── yugabytedb-sql.md
│       ├── samples
│       │   ├── _index.md
│       │   ├── alloydb
│       │   │   ├── _index.md
│       │   │   ├── ai-nl
│       │   │   │   ├── alloydb_ai_nl.ipynb
│       │   │   │   └── index.md
│       │   │   └── mcp_quickstart.md
│       │   ├── bigquery
│       │   │   ├── _index.md
│       │   │   ├── colab_quickstart_bigquery.ipynb
│       │   │   ├── local_quickstart.md
│       │   │   └── mcp_quickstart
│       │   │       ├── _index.md
│       │   │       ├── inspector_tools.png
│       │   │       └── inspector.png
│       │   └── looker
│       │       ├── _index.md
│       │       ├── looker_gemini_oauth
│       │       │   ├── _index.md
│       │       │   ├── authenticated.png
│       │       │   ├── authorize.png
│       │       │   └── registration.png
│       │       ├── looker_gemini.md
│       │       └── looker_mcp_inspector
│       │           ├── _index.md
│       │           ├── inspector_tools.png
│       │           └── inspector.png
│       └── sdks
│           ├── _index.md
│           ├── go-sdk.md
│           ├── js-sdk.md
│           └── python-sdk.md
├── gemini-extension.json
├── go.mod
├── go.sum
├── internal
│   ├── auth
│   │   ├── auth.go
│   │   └── google
│   │       └── google.go
│   ├── log
│   │   ├── handler.go
│   │   ├── log_test.go
│   │   ├── log.go
│   │   └── logger.go
│   ├── prebuiltconfigs
│   │   ├── prebuiltconfigs_test.go
│   │   ├── prebuiltconfigs.go
│   │   └── tools
│   │       ├── alloydb-postgres-admin.yaml
│   │       ├── alloydb-postgres-observability.yaml
│   │       ├── alloydb-postgres.yaml
│   │       ├── bigquery.yaml
│   │       ├── clickhouse.yaml
│   │       ├── cloud-sql-mssql-admin.yaml
│   │       ├── cloud-sql-mssql-observability.yaml
│   │       ├── cloud-sql-mssql.yaml
│   │       ├── cloud-sql-mysql-admin.yaml
│   │       ├── cloud-sql-mysql-observability.yaml
│   │       ├── cloud-sql-mysql.yaml
│   │       ├── cloud-sql-postgres-admin.yaml
│   │       ├── cloud-sql-postgres-observability.yaml
│   │       ├── cloud-sql-postgres.yaml
│   │       ├── dataplex.yaml
│   │       ├── firestore.yaml
│   │       ├── looker-conversational-analytics.yaml
│   │       ├── looker.yaml
│   │       ├── mssql.yaml
│   │       ├── mysql.yaml
│   │       ├── neo4j.yaml
│   │       ├── oceanbase.yaml
│   │       ├── postgres.yaml
│   │       ├── spanner-postgres.yaml
│   │       ├── spanner.yaml
│   │       └── sqlite.yaml
│   ├── server
│   │   ├── api_test.go
│   │   ├── api.go
│   │   ├── common_test.go
│   │   ├── config.go
│   │   ├── mcp
│   │   │   ├── jsonrpc
│   │   │   │   ├── jsonrpc_test.go
│   │   │   │   └── jsonrpc.go
│   │   │   ├── mcp.go
│   │   │   ├── util
│   │   │   │   └── lifecycle.go
│   │   │   ├── v20241105
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   ├── v20250326
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   └── v20250618
│   │   │       ├── method.go
│   │   │       └── types.go
│   │   ├── mcp_test.go
│   │   ├── mcp.go
│   │   ├── server_test.go
│   │   ├── server.go
│   │   ├── static
│   │   │   ├── assets
│   │   │   │   └── mcptoolboxlogo.png
│   │   │   ├── css
│   │   │   │   └── style.css
│   │   │   ├── index.html
│   │   │   ├── js
│   │   │   │   ├── auth.js
│   │   │   │   ├── loadTools.js
│   │   │   │   ├── mainContent.js
│   │   │   │   ├── navbar.js
│   │   │   │   ├── runTool.js
│   │   │   │   ├── toolDisplay.js
│   │   │   │   ├── tools.js
│   │   │   │   └── toolsets.js
│   │   │   ├── tools.html
│   │   │   └── toolsets.html
│   │   ├── web_test.go
│   │   └── web.go
│   ├── sources
│   │   ├── alloydbadmin
│   │   │   ├── alloydbadmin_test.go
│   │   │   └── alloydbadmin.go
│   │   ├── alloydbpg
│   │   │   ├── alloydb_pg_test.go
│   │   │   └── alloydb_pg.go
│   │   ├── bigquery
│   │   │   ├── bigquery_test.go
│   │   │   └── bigquery.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   ├── cassandra_test.go
│   │   │   └── cassandra.go
│   │   ├── clickhouse
│   │   │   ├── clickhouse_test.go
│   │   │   └── clickhouse.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloud_monitoring_test.go
│   │   │   └── cloud_monitoring.go
│   │   ├── cloudsqladmin
│   │   │   ├── cloud_sql_admin_test.go
│   │   │   └── cloud_sql_admin.go
│   │   ├── cloudsqlmssql
│   │   │   ├── cloud_sql_mssql_test.go
│   │   │   └── cloud_sql_mssql.go
│   │   ├── cloudsqlmysql
│   │   │   ├── cloud_sql_mysql_test.go
│   │   │   └── cloud_sql_mysql.go
│   │   ├── cloudsqlpg
│   │   │   ├── cloud_sql_pg_test.go
│   │   │   └── cloud_sql_pg.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataplex
│   │   │   ├── dataplex_test.go
│   │   │   └── dataplex.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── dialect.go
│   │   ├── firebird
│   │   │   ├── firebird_test.go
│   │   │   └── firebird.go
│   │   ├── firestore
│   │   │   ├── firestore_test.go
│   │   │   └── firestore.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── ip_type.go
│   │   ├── looker
│   │   │   ├── looker_test.go
│   │   │   └── looker.go
│   │   ├── mongodb
│   │   │   ├── mongodb_test.go
│   │   │   └── mongodb.go
│   │   ├── mssql
│   │   │   ├── mssql_test.go
│   │   │   └── mssql.go
│   │   ├── mysql
│   │   │   ├── mysql_test.go
│   │   │   └── mysql.go
│   │   ├── neo4j
│   │   │   ├── neo4j_test.go
│   │   │   └── neo4j.go
│   │   ├── oceanbase
│   │   │   ├── oceanbase_test.go
│   │   │   └── oceanbase.go
│   │   ├── oracle
│   │   │   └── oracle.go
│   │   ├── postgres
│   │   │   ├── postgres_test.go
│   │   │   └── postgres.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── sources.go
│   │   ├── spanner
│   │   │   ├── spanner_test.go
│   │   │   └── spanner.go
│   │   ├── sqlite
│   │   │   ├── sqlite_test.go
│   │   │   └── sqlite.go
│   │   ├── tidb
│   │   │   ├── tidb_test.go
│   │   │   └── tidb.go
│   │   ├── trino
│   │   │   ├── trino_test.go
│   │   │   └── trino.go
│   │   ├── util.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedb
│   │       ├── yugabytedb_test.go
│   │       └── yugabytedb.go
│   ├── telemetry
│   │   ├── instrumentation.go
│   │   └── telemetry.go
│   ├── testutils
│   │   └── testutils.go
│   ├── tools
│   │   ├── alloydb
│   │   │   ├── alloydbcreatecluster
│   │   │   │   ├── alloydbcreatecluster_test.go
│   │   │   │   └── alloydbcreatecluster.go
│   │   │   ├── alloydbcreateinstance
│   │   │   │   ├── alloydbcreateinstance_test.go
│   │   │   │   └── alloydbcreateinstance.go
│   │   │   ├── alloydbcreateuser
│   │   │   │   ├── alloydbcreateuser_test.go
│   │   │   │   └── alloydbcreateuser.go
│   │   │   ├── alloydbgetcluster
│   │   │   │   ├── alloydbgetcluster_test.go
│   │   │   │   └── alloydbgetcluster.go
│   │   │   ├── alloydbgetinstance
│   │   │   │   ├── alloydbgetinstance_test.go
│   │   │   │   └── alloydbgetinstance.go
│   │   │   ├── alloydbgetuser
│   │   │   │   ├── alloydbgetuser_test.go
│   │   │   │   └── alloydbgetuser.go
│   │   │   ├── alloydblistclusters
│   │   │   │   ├── alloydblistclusters_test.go
│   │   │   │   └── alloydblistclusters.go
│   │   │   ├── alloydblistinstances
│   │   │   │   ├── alloydblistinstances_test.go
│   │   │   │   └── alloydblistinstances.go
│   │   │   ├── alloydblistusers
│   │   │   │   ├── alloydblistusers_test.go
│   │   │   │   └── alloydblistusers.go
│   │   │   └── alloydbwaitforoperation
│   │   │       ├── alloydbwaitforoperation_test.go
│   │   │       └── alloydbwaitforoperation.go
│   │   ├── alloydbainl
│   │   │   ├── alloydbainl_test.go
│   │   │   └── alloydbainl.go
│   │   ├── bigquery
│   │   │   ├── bigqueryanalyzecontribution
│   │   │   │   ├── bigqueryanalyzecontribution_test.go
│   │   │   │   └── bigqueryanalyzecontribution.go
│   │   │   ├── bigquerycommon
│   │   │   │   ├── table_name_parser_test.go
│   │   │   │   ├── table_name_parser.go
│   │   │   │   └── util.go
│   │   │   ├── bigqueryconversationalanalytics
│   │   │   │   ├── bigqueryconversationalanalytics_test.go
│   │   │   │   └── bigqueryconversationalanalytics.go
│   │   │   ├── bigqueryexecutesql
│   │   │   │   ├── bigqueryexecutesql_test.go
│   │   │   │   └── bigqueryexecutesql.go
│   │   │   ├── bigqueryforecast
│   │   │   │   ├── bigqueryforecast_test.go
│   │   │   │   └── bigqueryforecast.go
│   │   │   ├── bigquerygetdatasetinfo
│   │   │   │   ├── bigquerygetdatasetinfo_test.go
│   │   │   │   └── bigquerygetdatasetinfo.go
│   │   │   ├── bigquerygettableinfo
│   │   │   │   ├── bigquerygettableinfo_test.go
│   │   │   │   └── bigquerygettableinfo.go
│   │   │   ├── bigquerylistdatasetids
│   │   │   │   ├── bigquerylistdatasetids_test.go
│   │   │   │   └── bigquerylistdatasetids.go
│   │   │   ├── bigquerylisttableids
│   │   │   │   ├── bigquerylisttableids_test.go
│   │   │   │   └── bigquerylisttableids.go
│   │   │   ├── bigquerysearchcatalog
│   │   │   │   ├── bigquerysearchcatalog_test.go
│   │   │   │   └── bigquerysearchcatalog.go
│   │   │   └── bigquerysql
│   │   │       ├── bigquerysql_test.go
│   │   │       └── bigquerysql.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   └── cassandracql
│   │   │       ├── cassandracql_test.go
│   │   │       └── cassandracql.go
│   │   ├── clickhouse
│   │   │   ├── clickhouseexecutesql
│   │   │   │   ├── clickhouseexecutesql_test.go
│   │   │   │   └── clickhouseexecutesql.go
│   │   │   ├── clickhouselistdatabases
│   │   │   │   ├── clickhouselistdatabases_test.go
│   │   │   │   └── clickhouselistdatabases.go
│   │   │   ├── clickhouselisttables
│   │   │   │   ├── clickhouselisttables_test.go
│   │   │   │   └── clickhouselisttables.go
│   │   │   └── clickhousesql
│   │   │       ├── clickhousesql_test.go
│   │   │       └── clickhousesql.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloudmonitoring_test.go
│   │   │   └── cloudmonitoring.go
│   │   ├── cloudsql
│   │   │   ├── cloudsqlcreatedatabase
│   │   │   │   ├── cloudsqlcreatedatabase_test.go
│   │   │   │   └── cloudsqlcreatedatabase.go
│   │   │   ├── cloudsqlcreateusers
│   │   │   │   ├── cloudsqlcreateusers_test.go
│   │   │   │   └── cloudsqlcreateusers.go
│   │   │   ├── cloudsqlgetinstances
│   │   │   │   ├── cloudsqlgetinstances_test.go
│   │   │   │   └── cloudsqlgetinstances.go
│   │   │   ├── cloudsqllistdatabases
│   │   │   │   ├── cloudsqllistdatabases_test.go
│   │   │   │   └── cloudsqllistdatabases.go
│   │   │   ├── cloudsqllistinstances
│   │   │   │   ├── cloudsqllistinstances_test.go
│   │   │   │   └── cloudsqllistinstances.go
│   │   │   └── cloudsqlwaitforoperation
│   │   │       ├── cloudsqlwaitforoperation_test.go
│   │   │       └── cloudsqlwaitforoperation.go
│   │   ├── cloudsqlmssql
│   │   │   └── cloudsqlmssqlcreateinstance
│   │   │       ├── cloudsqlmssqlcreateinstance_test.go
│   │   │       └── cloudsqlmssqlcreateinstance.go
│   │   ├── cloudsqlmysql
│   │   │   └── cloudsqlmysqlcreateinstance
│   │   │       ├── cloudsqlmysqlcreateinstance_test.go
│   │   │       └── cloudsqlmysqlcreateinstance.go
│   │   ├── cloudsqlpg
│   │   │   └── cloudsqlpgcreateinstances
│   │   │       ├── cloudsqlpgcreateinstances_test.go
│   │   │       └── cloudsqlpgcreateinstances.go
│   │   ├── common_test.go
│   │   ├── common.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataform
│   │   │   └── dataformcompilelocal
│   │   │       ├── dataformcompilelocal_test.go
│   │   │       └── dataformcompilelocal.go
│   │   ├── dataplex
│   │   │   ├── dataplexlookupentry
│   │   │   │   ├── dataplexlookupentry_test.go
│   │   │   │   └── dataplexlookupentry.go
│   │   │   ├── dataplexsearchaspecttypes
│   │   │   │   ├── dataplexsearchaspecttypes_test.go
│   │   │   │   └── dataplexsearchaspecttypes.go
│   │   │   └── dataplexsearchentries
│   │   │       ├── dataplexsearchentries_test.go
│   │   │       └── dataplexsearchentries.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── firebird
│   │   │   ├── firebirdexecutesql
│   │   │   │   ├── firebirdexecutesql_test.go
│   │   │   │   └── firebirdexecutesql.go
│   │   │   └── firebirdsql
│   │   │       ├── firebirdsql_test.go
│   │   │       └── firebirdsql.go
│   │   ├── firestore
│   │   │   ├── firestoreadddocuments
│   │   │   │   ├── firestoreadddocuments_test.go
│   │   │   │   └── firestoreadddocuments.go
│   │   │   ├── firestoredeletedocuments
│   │   │   │   ├── firestoredeletedocuments_test.go
│   │   │   │   └── firestoredeletedocuments.go
│   │   │   ├── firestoregetdocuments
│   │   │   │   ├── firestoregetdocuments_test.go
│   │   │   │   └── firestoregetdocuments.go
│   │   │   ├── firestoregetrules
│   │   │   │   ├── firestoregetrules_test.go
│   │   │   │   └── firestoregetrules.go
│   │   │   ├── firestorelistcollections
│   │   │   │   ├── firestorelistcollections_test.go
│   │   │   │   └── firestorelistcollections.go
│   │   │   ├── firestorequery
│   │   │   │   ├── firestorequery_test.go
│   │   │   │   └── firestorequery.go
│   │   │   ├── firestorequerycollection
│   │   │   │   ├── firestorequerycollection_test.go
│   │   │   │   └── firestorequerycollection.go
│   │   │   ├── firestoreupdatedocument
│   │   │   │   ├── firestoreupdatedocument_test.go
│   │   │   │   └── firestoreupdatedocument.go
│   │   │   ├── firestorevalidaterules
│   │   │   │   ├── firestorevalidaterules_test.go
│   │   │   │   └── firestorevalidaterules.go
│   │   │   └── util
│   │   │       ├── converter_test.go
│   │   │       ├── converter.go
│   │   │       ├── validator_test.go
│   │   │       └── validator.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── http_method.go
│   │   ├── looker
│   │   │   ├── lookeradddashboardelement
│   │   │   │   ├── lookeradddashboardelement_test.go
│   │   │   │   └── lookeradddashboardelement.go
│   │   │   ├── lookercommon
│   │   │   │   ├── lookercommon_test.go
│   │   │   │   └── lookercommon.go
│   │   │   ├── lookerconversationalanalytics
│   │   │   │   ├── lookerconversationalanalytics_test.go
│   │   │   │   └── lookerconversationalanalytics.go
│   │   │   ├── lookergetdashboards
│   │   │   │   ├── lookergetdashboards_test.go
│   │   │   │   └── lookergetdashboards.go
│   │   │   ├── lookergetdimensions
│   │   │   │   ├── lookergetdimensions_test.go
│   │   │   │   └── lookergetdimensions.go
│   │   │   ├── lookergetexplores
│   │   │   │   ├── lookergetexplores_test.go
│   │   │   │   └── lookergetexplores.go
│   │   │   ├── lookergetfilters
│   │   │   │   ├── lookergetfilters_test.go
│   │   │   │   └── lookergetfilters.go
│   │   │   ├── lookergetlooks
│   │   │   │   ├── lookergetlooks_test.go
│   │   │   │   └── lookergetlooks.go
│   │   │   ├── lookergetmeasures
│   │   │   │   ├── lookergetmeasures_test.go
│   │   │   │   └── lookergetmeasures.go
│   │   │   ├── lookergetmodels
│   │   │   │   ├── lookergetmodels_test.go
│   │   │   │   └── lookergetmodels.go
│   │   │   ├── lookergetparameters
│   │   │   │   ├── lookergetparameters_test.go
│   │   │   │   └── lookergetparameters.go
│   │   │   ├── lookerhealthanalyze
│   │   │   │   ├── lookerhealthanalyze_test.go
│   │   │   │   └── lookerhealthanalyze.go
│   │   │   ├── lookerhealthpulse
│   │   │   │   ├── lookerhealthpulse_test.go
│   │   │   │   └── lookerhealthpulse.go
│   │   │   ├── lookerhealthvacuum
│   │   │   │   ├── lookerhealthvacuum_test.go
│   │   │   │   └── lookerhealthvacuum.go
│   │   │   ├── lookermakedashboard
│   │   │   │   ├── lookermakedashboard_test.go
│   │   │   │   └── lookermakedashboard.go
│   │   │   ├── lookermakelook
│   │   │   │   ├── lookermakelook_test.go
│   │   │   │   └── lookermakelook.go
│   │   │   ├── lookerquery
│   │   │   │   ├── lookerquery_test.go
│   │   │   │   └── lookerquery.go
│   │   │   ├── lookerquerysql
│   │   │   │   ├── lookerquerysql_test.go
│   │   │   │   └── lookerquerysql.go
│   │   │   ├── lookerqueryurl
│   │   │   │   ├── lookerqueryurl_test.go
│   │   │   │   └── lookerqueryurl.go
│   │   │   └── lookerrunlook
│   │   │       ├── lookerrunlook_test.go
│   │   │       └── lookerrunlook.go
│   │   ├── mongodb
│   │   │   ├── mongodbaggregate
│   │   │   │   ├── mongodbaggregate_test.go
│   │   │   │   └── mongodbaggregate.go
│   │   │   ├── mongodbdeletemany
│   │   │   │   ├── mongodbdeletemany_test.go
│   │   │   │   └── mongodbdeletemany.go
│   │   │   ├── mongodbdeleteone
│   │   │   │   ├── mongodbdeleteone_test.go
│   │   │   │   └── mongodbdeleteone.go
│   │   │   ├── mongodbfind
│   │   │   │   ├── mongodbfind_test.go
│   │   │   │   └── mongodbfind.go
│   │   │   ├── mongodbfindone
│   │   │   │   ├── mongodbfindone_test.go
│   │   │   │   └── mongodbfindone.go
│   │   │   ├── mongodbinsertmany
│   │   │   │   ├── mongodbinsertmany_test.go
│   │   │   │   └── mongodbinsertmany.go
│   │   │   ├── mongodbinsertone
│   │   │   │   ├── mongodbinsertone_test.go
│   │   │   │   └── mongodbinsertone.go
│   │   │   ├── mongodbupdatemany
│   │   │   │   ├── mongodbupdatemany_test.go
│   │   │   │   └── mongodbupdatemany.go
│   │   │   └── mongodbupdateone
│   │   │       ├── mongodbupdateone_test.go
│   │   │       └── mongodbupdateone.go
│   │   ├── mssql
│   │   │   ├── mssqlexecutesql
│   │   │   │   ├── mssqlexecutesql_test.go
│   │   │   │   └── mssqlexecutesql.go
│   │   │   ├── mssqllisttables
│   │   │   │   ├── mssqllisttables_test.go
│   │   │   │   └── mssqllisttables.go
│   │   │   └── mssqlsql
│   │   │       ├── mssqlsql_test.go
│   │   │       └── mssqlsql.go
│   │   ├── mysql
│   │   │   ├── mysqlcommon
│   │   │   │   └── mysqlcommon.go
│   │   │   ├── mysqlexecutesql
│   │   │   │   ├── mysqlexecutesql_test.go
│   │   │   │   └── mysqlexecutesql.go
│   │   │   ├── mysqllistactivequeries
│   │   │   │   ├── mysqllistactivequeries_test.go
│   │   │   │   └── mysqllistactivequeries.go
│   │   │   ├── mysqllisttablefragmentation
│   │   │   │   ├── mysqllisttablefragmentation_test.go
│   │   │   │   └── mysqllisttablefragmentation.go
│   │   │   ├── mysqllisttables
│   │   │   │   ├── mysqllisttables_test.go
│   │   │   │   └── mysqllisttables.go
│   │   │   ├── mysqllisttablesmissinguniqueindexes
│   │   │   │   ├── mysqllisttablesmissinguniqueindexes_test.go
│   │   │   │   └── mysqllisttablesmissinguniqueindexes.go
│   │   │   └── mysqlsql
│   │   │       ├── mysqlsql_test.go
│   │   │       └── mysqlsql.go
│   │   ├── neo4j
│   │   │   ├── neo4jcypher
│   │   │   │   ├── neo4jcypher_test.go
│   │   │   │   └── neo4jcypher.go
│   │   │   ├── neo4jexecutecypher
│   │   │   │   ├── classifier
│   │   │   │   │   ├── classifier_test.go
│   │   │   │   │   └── classifier.go
│   │   │   │   ├── neo4jexecutecypher_test.go
│   │   │   │   └── neo4jexecutecypher.go
│   │   │   └── neo4jschema
│   │   │       ├── cache
│   │   │       │   ├── cache_test.go
│   │   │       │   └── cache.go
│   │   │       ├── helpers
│   │   │       │   ├── helpers_test.go
│   │   │       │   └── helpers.go
│   │   │       ├── neo4jschema_test.go
│   │   │       ├── neo4jschema.go
│   │   │       └── types
│   │   │           └── types.go
│   │   ├── oceanbase
│   │   │   ├── oceanbaseexecutesql
│   │   │   │   ├── oceanbaseexecutesql_test.go
│   │   │   │   └── oceanbaseexecutesql.go
│   │   │   └── oceanbasesql
│   │   │       ├── oceanbasesql_test.go
│   │   │       └── oceanbasesql.go
│   │   ├── oracle
│   │   │   ├── oracleexecutesql
│   │   │   │   └── oracleexecutesql.go
│   │   │   └── oraclesql
│   │   │       └── oraclesql.go
│   │   ├── parameters_test.go
│   │   ├── parameters.go
│   │   ├── postgres
│   │   │   ├── postgresexecutesql
│   │   │   │   ├── postgresexecutesql_test.go
│   │   │   │   └── postgresexecutesql.go
│   │   │   ├── postgreslistactivequeries
│   │   │   │   ├── postgreslistactivequeries_test.go
│   │   │   │   └── postgreslistactivequeries.go
│   │   │   ├── postgreslistavailableextensions
│   │   │   │   ├── postgreslistavailableextensions_test.go
│   │   │   │   └── postgreslistavailableextensions.go
│   │   │   ├── postgreslistinstalledextensions
│   │   │   │   ├── postgreslistinstalledextensions_test.go
│   │   │   │   └── postgreslistinstalledextensions.go
│   │   │   ├── postgreslisttables
│   │   │   │   ├── postgreslisttables_test.go
│   │   │   │   └── postgreslisttables.go
│   │   │   └── postgressql
│   │   │       ├── postgressql_test.go
│   │   │       └── postgressql.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── spanner
│   │   │   ├── spannerexecutesql
│   │   │   │   ├── spannerexecutesql_test.go
│   │   │   │   └── spannerexecutesql.go
│   │   │   ├── spannerlisttables
│   │   │   │   ├── spannerlisttables_test.go
│   │   │   │   └── spannerlisttables.go
│   │   │   └── spannersql
│   │   │       ├── spanner_test.go
│   │   │       └── spannersql.go
│   │   ├── sqlite
│   │   │   ├── sqliteexecutesql
│   │   │   │   ├── sqliteexecutesql_test.go
│   │   │   │   └── sqliteexecutesql.go
│   │   │   └── sqlitesql
│   │   │       ├── sqlitesql_test.go
│   │   │       └── sqlitesql.go
│   │   ├── tidb
│   │   │   ├── tidbexecutesql
│   │   │   │   ├── tidbexecutesql_test.go
│   │   │   │   └── tidbexecutesql.go
│   │   │   └── tidbsql
│   │   │       ├── tidbsql_test.go
│   │   │       └── tidbsql.go
│   │   ├── tools_test.go
│   │   ├── tools.go
│   │   ├── toolsets.go
│   │   ├── trino
│   │   │   ├── trinoexecutesql
│   │   │   │   ├── trinoexecutesql_test.go
│   │   │   │   └── trinoexecutesql.go
│   │   │   └── trinosql
│   │   │       ├── trinosql_test.go
│   │   │       └── trinosql.go
│   │   ├── utility
│   │   │   └── wait
│   │   │       ├── wait_test.go
│   │   │       └── wait.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedbsql
│   │       ├── yugabytedbsql_test.go
│   │       └── yugabytedbsql.go
│   └── util
│       └── util.go
├── LICENSE
├── logo.png
├── main.go
├── MCP-TOOLBOX-EXTENSION.md
├── README.md
└── tests
    ├── alloydb
    │   ├── alloydb_integration_test.go
    │   └── alloydb_wait_for_operation_test.go
    ├── alloydbainl
    │   └── alloydb_ai_nl_integration_test.go
    ├── alloydbpg
    │   └── alloydb_pg_integration_test.go
    ├── auth.go
    ├── bigquery
    │   └── bigquery_integration_test.go
    ├── bigtable
    │   └── bigtable_integration_test.go
    ├── cassandra
    │   └── cassandra_integration_test.go
    ├── clickhouse
    │   └── clickhouse_integration_test.go
    ├── cloudmonitoring
    │   └── cloud_monitoring_integration_test.go
    ├── cloudsql
    │   ├── cloud_sql_create_database_test.go
    │   ├── cloud_sql_create_users_test.go
    │   ├── cloud_sql_get_instances_test.go
    │   ├── cloud_sql_list_databases_test.go
    │   ├── cloudsql_list_instances_test.go
    │   └── cloudsql_wait_for_operation_test.go
    ├── cloudsqlmssql
    │   ├── cloud_sql_mssql_create_instance_integration_test.go
    │   └── cloud_sql_mssql_integration_test.go
    ├── cloudsqlmysql
    │   ├── cloud_sql_mysql_create_instance_integration_test.go
    │   └── cloud_sql_mysql_integration_test.go
    ├── cloudsqlpg
    │   ├── cloud_sql_pg_create_instances_test.go
    │   └── cloud_sql_pg_integration_test.go
    ├── common.go
    ├── couchbase
    │   └── couchbase_integration_test.go
    ├── dataform
    │   └── dataform_integration_test.go
    ├── dataplex
    │   └── dataplex_integration_test.go
    ├── dgraph
    │   └── dgraph_integration_test.go
    ├── firebird
    │   └── firebird_integration_test.go
    ├── firestore
    │   └── firestore_integration_test.go
    ├── http
    │   └── http_integration_test.go
    ├── looker
    │   └── looker_integration_test.go
    ├── mongodb
    │   └── mongodb_integration_test.go
    ├── mssql
    │   └── mssql_integration_test.go
    ├── mysql
    │   └── mysql_integration_test.go
    ├── neo4j
    │   └── neo4j_integration_test.go
    ├── oceanbase
    │   └── oceanbase_integration_test.go
    ├── option.go
    ├── oracle
    │   └── oracle_integration_test.go
    ├── postgres
    │   └── postgres_integration_test.go
    ├── redis
    │   └── redis_test.go
    ├── server.go
    ├── source.go
    ├── spanner
    │   └── spanner_integration_test.go
    ├── sqlite
    │   └── sqlite_integration_test.go
    ├── tidb
    │   └── tidb_integration_test.go
    ├── tool.go
    ├── trino
    │   └── trino_integration_test.go
    ├── utility
    │   └── wait_integration_test.go
    ├── valkey
    │   └── valkey_test.go
    └── yugabytedb
        └── yugabytedb_integration_test.go
```

# Files

--------------------------------------------------------------------------------
/docs/en/getting-started/quickstart/js/genAI/quickstart.js:
--------------------------------------------------------------------------------

```javascript
  1 | import { GoogleGenAI } from "@google/genai";
  2 | import { ToolboxClient } from "@toolbox-sdk/core";
  3 | 
  4 | 
  5 | const TOOLBOX_URL = "http://127.0.0.1:5000"; // Update if needed
  6 | const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY || 'your-api-key'; // Replace it with your API key
  7 | 
  8 | const prompt = `
  9 | You're a helpful hotel assistant. You handle hotel searching, booking, and
 10 | cancellations. When the user searches for a hotel, you MUST use the available tools to find information. Mention its name, id,
 11 | location and price tier. Always mention hotel id while performing any
 12 | searches. This is very important for any operations. For any bookings or
 13 | cancellations, please provide the appropriate confirmation. Be sure to
 14 | update checkin or checkout dates if mentioned by the user.
 15 | Don't ask for confirmations from the user.
 16 | `;
 17 | 
 18 | const queries = [
 19 |   "Find hotels in Basel with Basel in its name.",
 20 |   "Can you book the Hilton Basel for me?",
 21 |   "Oh wait, this is too expensive. Please cancel it and book the Hyatt Regency instead.",
 22 |   "My check in dates would be from April 10, 2024 to April 19, 2024.",
 23 | ];
 24 | 
 25 | function mapZodTypeToOpenAPIType(zodTypeName) {
 26 | 
 27 |     console.log(zodTypeName)
 28 |     const typeMap = {
 29 |         'ZodString': 'string',
 30 |         'ZodNumber': 'number',
 31 |         'ZodBoolean': 'boolean',
 32 |         'ZodArray': 'array',
 33 |         'ZodObject': 'object',
 34 |     };
 35 |     return typeMap[zodTypeName] || 'string';
 36 | }
 37 | 
 38 | export async function main() {
 39 |    
 40 |     const toolboxClient = new ToolboxClient(TOOLBOX_URL); 
 41 |     const toolboxTools = await toolboxClient.loadToolset("my-toolset");
 42 |     
 43 |     const geminiTools = [{
 44 |         functionDeclarations: toolboxTools.map(tool => {
 45 |             
 46 |             const schema = tool.getParamSchema();
 47 |             const properties = {};
 48 |             const required = [];
 49 | 
 50 |          
 51 |             for (const [key, param] of Object.entries(schema.shape)) {
 52 |                 properties[key] = {
 53 |                         type: mapZodTypeToOpenAPIType(param.constructor.name),
 54 |                         description: param.description || '',
 55 |                     };
 56 |                 required.push(key)
 57 |                 }
 58 |             
 59 |             return {
 60 |                 name: tool.getName(),
 61 |                 description: tool.getDescription(),
 62 |                 parameters: { type: 'object', properties, required },
 63 |             };
 64 |         })
 65 |     }];
 66 | 
 67 | 
 68 |     const genAI = new GoogleGenAI({ apiKey: GOOGLE_API_KEY });
 69 |     
 70 |     const chat = genAI.chats.create({
 71 |         model: "gemini-2.5-flash",
 72 |         config: {
 73 |             systemInstruction: prompt,
 74 |             tools: geminiTools,
 75 |         }
 76 |     });
 77 | 
 78 |     for (const query of queries) {
 79 |         
 80 |         let currentResult = await chat.sendMessage({ message: query });
 81 |         
 82 |         let finalResponseGiven = false
 83 |         while (!finalResponseGiven) {
 84 |             
 85 |             const response = currentResult;
 86 |             const functionCalls = response.functionCalls || [];
 87 | 
 88 |             if (functionCalls.length === 0) {
 89 |                 console.log(response.text)
 90 |                 finalResponseGiven = true;
 91 |             } else {
 92 |                 const toolResponses = [];
 93 |                 for (const call of functionCalls) {
 94 |                     const toolName = call.name
 95 |                     const toolToExecute = toolboxTools.find(t => t.getName() === toolName);
 96 |                     
 97 |                     if (toolToExecute) {
 98 |                         try {
 99 |                             const functionResult = await toolToExecute(call.args);
100 |                             toolResponses.push({
101 |                                 functionResponse: { name: call.name, response: { result: functionResult } }
102 |                             });
103 |                         } catch (e) {
104 |                             console.error(`Error executing tool '${toolName}':`, e);
105 |                             toolResponses.push({
106 |                                 functionResponse: { name: call.name, response: { error: e.message } }
107 |                             });
108 |                         }
109 |                     }
110 |                 }
111 |                 
112 |                 currentResult = await chat.sendMessage({ message: toolResponses });
113 |             }
114 |         }
115 |         
116 |     }
117 | }
118 | 
119 | main();
```

--------------------------------------------------------------------------------
/internal/sources/alloydbadmin/alloydbadmin.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package alloydbadmin
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 	"net/http"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	"github.com/googleapis/genai-toolbox/internal/util"
 24 | 	"go.opentelemetry.io/otel/trace"
 25 | 	"golang.org/x/oauth2"
 26 | 	"golang.org/x/oauth2/google"
 27 | 	alloydbrestapi "google.golang.org/api/alloydb/v1"
 28 | 	"google.golang.org/api/option"
 29 | )
 30 | 
 31 | const SourceKind string = "alloydb-admin"
 32 | 
 33 | type userAgentRoundTripper struct {
 34 | 	userAgent string
 35 | 	next      http.RoundTripper
 36 | }
 37 | 
 38 | func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
 39 | 	newReq := *req
 40 | 	newReq.Header = make(http.Header)
 41 | 	for k, v := range req.Header {
 42 | 		newReq.Header[k] = v
 43 | 	}
 44 | 	ua := newReq.Header.Get("User-Agent")
 45 | 	if ua == "" {
 46 | 		newReq.Header.Set("User-Agent", rt.userAgent)
 47 | 	} else {
 48 | 		newReq.Header.Set("User-Agent", ua+" "+rt.userAgent)
 49 | 	}
 50 | 	return rt.next.RoundTrip(&newReq)
 51 | }
 52 | 
 53 | // validate interface
 54 | var _ sources.SourceConfig = Config{}
 55 | 
 56 | func init() {
 57 | 	if !sources.Register(SourceKind, newConfig) {
 58 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 59 | 	}
 60 | }
 61 | 
 62 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 63 | 	actual := Config{Name: name}
 64 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 65 | 		return nil, err
 66 | 	}
 67 | 	return actual, nil
 68 | }
 69 | 
 70 | type Config struct {
 71 | 	Name           string `yaml:"name" validate:"required"`
 72 | 	Kind           string `yaml:"kind" validate:"required"`
 73 | 	UseClientOAuth bool   `yaml:"useClientOAuth"`
 74 | }
 75 | 
 76 | func (r Config) SourceConfigKind() string {
 77 | 	return SourceKind
 78 | }
 79 | 
 80 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 81 | 	ua, err := util.UserAgentFromContext(ctx)
 82 | 	if err != nil {
 83 | 		fmt.Printf("Error in User Agent retrieval: %s", err)
 84 | 	}
 85 | 
 86 | 	var client *http.Client
 87 | 	if r.UseClientOAuth {
 88 | 		client = &http.Client{
 89 | 			Transport: &userAgentRoundTripper{
 90 | 				userAgent: ua,
 91 | 				next:      http.DefaultTransport,
 92 | 			},
 93 | 		}
 94 | 	} else {
 95 | 		// Use Application Default Credentials
 96 | 		creds, err := google.FindDefaultCredentials(ctx, alloydbrestapi.CloudPlatformScope)
 97 | 		if err != nil {
 98 | 			return nil, fmt.Errorf("failed to find default credentials: %w", err)
 99 | 		}
100 | 		baseClient := oauth2.NewClient(ctx, creds.TokenSource)
101 | 		baseClient.Transport = &userAgentRoundTripper{
102 | 			userAgent: ua,
103 | 			next:      baseClient.Transport,
104 | 		}
105 | 		client = baseClient
106 | 	}
107 | 
108 | 	service, err := alloydbrestapi.NewService(ctx, option.WithHTTPClient(client))
109 | 	if err != nil {
110 | 		return nil, fmt.Errorf("error creating new alloydb service: %w", err)
111 | 	}
112 | 
113 | 	s := &Source{
114 | 		Name:           r.Name,
115 | 		Kind:           SourceKind,
116 | 		BaseURL:        "https://alloydb.googleapis.com",
117 | 		Service:        service,
118 | 		UseClientOAuth: r.UseClientOAuth,
119 | 	}
120 | 
121 | 	return s, nil
122 | }
123 | 
124 | var _ sources.Source = &Source{}
125 | 
126 | type Source struct {
127 | 	Name           string `yaml:"name"`
128 | 	Kind           string `yaml:"kind"`
129 | 	BaseURL        string
130 | 	Service        *alloydbrestapi.Service
131 | 	UseClientOAuth bool
132 | }
133 | 
134 | func (s *Source) SourceKind() string {
135 | 	return SourceKind
136 | }
137 | 
138 | func (s *Source) GetService(ctx context.Context, accessToken string) (*alloydbrestapi.Service, error) {
139 | 	if s.UseClientOAuth {
140 | 		token := &oauth2.Token{AccessToken: accessToken}
141 | 		client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token))
142 | 		service, err := alloydbrestapi.NewService(ctx, option.WithHTTPClient(client))
143 | 		if err != nil {
144 | 			return nil, fmt.Errorf("error creating new alloydb service: %w", err)
145 | 		}
146 | 		return service, nil
147 | 	}
148 | 	return s.Service, nil
149 | }
150 | 
151 | func (s *Source) UseClientAuthorization() bool {
152 | 	return s.UseClientOAuth
153 | }
154 | 
```

--------------------------------------------------------------------------------
/internal/sources/firestore/firestore.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package firestore
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"cloud.google.com/go/firestore"
 22 | 	"github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/util"
 25 | 	"go.opentelemetry.io/otel/trace"
 26 | 	"google.golang.org/api/firebaserules/v1"
 27 | 	"google.golang.org/api/option"
 28 | )
 29 | 
 30 | const SourceKind string = "firestore"
 31 | 
 32 | // validate interface
 33 | var _ sources.SourceConfig = Config{}
 34 | 
 35 | func init() {
 36 | 	if !sources.Register(SourceKind, newConfig) {
 37 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 38 | 	}
 39 | }
 40 | 
 41 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 42 | 	actual := Config{Name: name}
 43 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 44 | 		return nil, err
 45 | 	}
 46 | 	return actual, nil
 47 | }
 48 | 
 49 | type Config struct {
 50 | 	// Firestore configs
 51 | 	Name     string `yaml:"name" validate:"required"`
 52 | 	Kind     string `yaml:"kind" validate:"required"`
 53 | 	Project  string `yaml:"project" validate:"required"`
 54 | 	Database string `yaml:"database"` // Optional, defaults to "(default)"
 55 | }
 56 | 
 57 | func (r Config) SourceConfigKind() string {
 58 | 	// Returns Firestore source kind
 59 | 	return SourceKind
 60 | }
 61 | 
 62 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 63 | 	// Initializes a Firestore source
 64 | 	client, err := initFirestoreConnection(ctx, tracer, r.Name, r.Project, r.Database)
 65 | 	if err != nil {
 66 | 		return nil, err
 67 | 	}
 68 | 
 69 | 	// Initialize Firebase Rules client
 70 | 	rulesClient, err := initFirebaseRulesConnection(ctx, r.Project)
 71 | 	if err != nil {
 72 | 		return nil, fmt.Errorf("failed to initialize Firebase Rules client: %w", err)
 73 | 	}
 74 | 
 75 | 	s := &Source{
 76 | 		Name:        r.Name,
 77 | 		Kind:        SourceKind,
 78 | 		Client:      client,
 79 | 		RulesClient: rulesClient,
 80 | 		ProjectId:   r.Project,
 81 | 		DatabaseId:  r.Database,
 82 | 	}
 83 | 	return s, nil
 84 | }
 85 | 
 86 | var _ sources.Source = &Source{}
 87 | 
 88 | type Source struct {
 89 | 	// Firestore struct with client
 90 | 	Name        string `yaml:"name"`
 91 | 	Kind        string `yaml:"kind"`
 92 | 	Client      *firestore.Client
 93 | 	RulesClient *firebaserules.Service
 94 | 	ProjectId   string `yaml:"projectId"`
 95 | 	DatabaseId  string `yaml:"databaseId"`
 96 | }
 97 | 
 98 | func (s *Source) SourceKind() string {
 99 | 	// Returns Firestore source kind
100 | 	return SourceKind
101 | }
102 | 
103 | func (s *Source) FirestoreClient() *firestore.Client {
104 | 	return s.Client
105 | }
106 | 
107 | func (s *Source) FirebaseRulesClient() *firebaserules.Service {
108 | 	return s.RulesClient
109 | }
110 | 
111 | func (s *Source) GetProjectId() string {
112 | 	return s.ProjectId
113 | }
114 | 
115 | func (s *Source) GetDatabaseId() string {
116 | 	return s.DatabaseId
117 | }
118 | 
119 | func initFirestoreConnection(
120 | 	ctx context.Context,
121 | 	tracer trace.Tracer,
122 | 	name string,
123 | 	project string,
124 | 	database string,
125 | ) (*firestore.Client, error) {
126 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
127 | 	defer span.End()
128 | 
129 | 	userAgent, err := util.UserAgentFromContext(ctx)
130 | 	if err != nil {
131 | 		return nil, err
132 | 	}
133 | 
134 | 	// If database is not specified, use the default database
135 | 	if database == "" {
136 | 		database = "(default)"
137 | 	}
138 | 
139 | 	// Create the Firestore client
140 | 	client, err := firestore.NewClientWithDatabase(ctx, project, database, option.WithUserAgent(userAgent))
141 | 	if err != nil {
142 | 		return nil, fmt.Errorf("failed to create Firestore client for project %q and database %q: %w", project, database, err)
143 | 	}
144 | 
145 | 	return client, nil
146 | }
147 | 
148 | func initFirebaseRulesConnection(
149 | 	ctx context.Context,
150 | 	project string,
151 | ) (*firebaserules.Service, error) {
152 | 	// Create the Firebase Rules client
153 | 	rulesClient, err := firebaserules.NewService(ctx)
154 | 	if err != nil {
155 | 		return nil, fmt.Errorf("failed to create Firebase Rules client for project %q: %w", project, err)
156 | 	}
157 | 
158 | 	return rulesClient, nil
159 | }
160 | 
```

--------------------------------------------------------------------------------
/internal/tools/couchbase/couchbase_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2024 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package couchbase_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	"github.com/googleapis/genai-toolbox/internal/tools/couchbase"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/google/go-cmp/cmp"
 24 | 	"github.com/googleapis/genai-toolbox/internal/server"
 25 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 27 | )
 28 | 
 29 | func TestParseFromYamlCouchbase(t *testing.T) {
 30 | 	tcs := []struct {
 31 | 		desc string
 32 | 		in   string
 33 | 		want server.ToolConfigs
 34 | 	}{
 35 | 		{
 36 | 			desc: "basic example",
 37 | 			in: `
 38 | 			tools:
 39 | 				example_tool:
 40 | 					kind: couchbase-sql
 41 | 					source: my-couchbase-instance
 42 | 					description: some tool description
 43 | 					statement: |
 44 | 						select * from hotel WHERE name = $hotel;
 45 | 					parameters:
 46 | 						- name: hotel
 47 | 						  type: string
 48 | 						  description: hotel parameter description
 49 | 			`,
 50 | 			want: server.ToolConfigs{
 51 | 				"example_tool": couchbase.Config{
 52 | 					Name:         "example_tool",
 53 | 					Kind:         "couchbase-sql",
 54 | 					AuthRequired: []string{},
 55 | 					Source:       "my-couchbase-instance",
 56 | 					Description:  "some tool description",
 57 | 					Statement:    "select * from hotel WHERE name = $hotel;\n",
 58 | 					Parameters: []tools.Parameter{
 59 | 						tools.NewStringParameter("hotel", "hotel parameter description"),
 60 | 					},
 61 | 				},
 62 | 			},
 63 | 		},
 64 | 	}
 65 | 	for _, tc := range tcs {
 66 | 		t.Run(tc.desc, func(t *testing.T) {
 67 | 			got := struct {
 68 | 				Tools server.ToolConfigs `yaml:"tools"`
 69 | 			}{}
 70 | 
 71 | 			// Create a context with a logger
 72 | 			ctx, err := testutils.ContextWithNewLogger()
 73 | 			if err != nil {
 74 | 				t.Fatalf("unable to create context with logger: %s", err)
 75 | 			}
 76 | 
 77 | 			// Parse contents with context
 78 | 			err = yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 79 | 			if err != nil {
 80 | 				t.Fatalf("unable to unmarshal: %s", err)
 81 | 			}
 82 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 83 | 				t.Fatalf("incorrect parse: diff %v", diff)
 84 | 			}
 85 | 		})
 86 | 	}
 87 | }
 88 | 
 89 | func TestParseFromYamlWithTemplateMssql(t *testing.T) {
 90 | 	ctx, err := testutils.ContextWithNewLogger()
 91 | 	if err != nil {
 92 | 		t.Fatalf("unexpected error: %s", err)
 93 | 	}
 94 | 	tcs := []struct {
 95 | 		desc string
 96 | 		in   string
 97 | 		want server.ToolConfigs
 98 | 	}{
 99 | 		{
100 | 			desc: "basic example",
101 | 			in: `
102 | 			tools:
103 | 				example_tool:
104 | 					kind: couchbase-sql
105 | 					source: my-couchbase-instance
106 | 					description: some tool description
107 | 					statement: |
108 | 						select * from {{.tableName}} WHERE name = $hotel;
109 | 					parameters:
110 | 						- name: hotel
111 | 						  type: string
112 | 						  description: hotel parameter description
113 | 					templateParameters:
114 | 						- name: tableName
115 | 						  type: string
116 | 						  description: The table to select hotels from.
117 | 			`,
118 | 			want: server.ToolConfigs{
119 | 				"example_tool": couchbase.Config{
120 | 					Name:         "example_tool",
121 | 					Kind:         "couchbase-sql",
122 | 					AuthRequired: []string{},
123 | 					Source:       "my-couchbase-instance",
124 | 					Description:  "some tool description",
125 | 					Statement:    "select * from {{.tableName}} WHERE name = $hotel;\n",
126 | 					Parameters: []tools.Parameter{
127 | 						tools.NewStringParameter("hotel", "hotel parameter description"),
128 | 					},
129 | 					TemplateParameters: []tools.Parameter{
130 | 						tools.NewStringParameter("tableName", "The table to select hotels from."),
131 | 					},
132 | 				},
133 | 			},
134 | 		},
135 | 	}
136 | 	for _, tc := range tcs {
137 | 		t.Run(tc.desc, func(t *testing.T) {
138 | 			got := struct {
139 | 				Tools server.ToolConfigs `yaml:"tools"`
140 | 			}{}
141 | 			// Parse contents
142 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
143 | 			if err != nil {
144 | 				t.Fatalf("unable to unmarshal: %s", err)
145 | 			}
146 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
147 | 				t.Fatalf("incorrect parse: diff %v", diff)
148 | 			}
149 | 		})
150 | 	}
151 | }
152 | 
```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------

```yaml
  1 | # Copyright 2025 Google LLC
  2 | #
  3 | # Licensed under the Apache License, Version 2.0 (the "License");
  4 | # you may not use this file except in compliance with the License.
  5 | # You may obtain a copy of the License at
  6 | #
  7 | #     https://www.apache.org/licenses/LICENSE-2.0
  8 | #
  9 | # Unless required by applicable law or agreed to in writing, software
 10 | # distributed under the License is distributed on an "AS IS" BASIS,
 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | # See the License for the specific language governing permissions and
 13 | # limitations under the License.
 14 | 
 15 | name: 🐞 Bug Report
 16 | description: File a report for unexpected or undesired behavior.
 17 | title: "<brief summary of what bug or error was observed>"
 18 | labels: ["type: bug"]
 19 | type: "bug"
 20 | 
 21 | body:
 22 |   - type: markdown
 23 |     attributes:
 24 |       value: |
 25 |         Thanks for helping us improve! 🙏 Please answer these questions and provide as much information as possible about your problem.
 26 |   
 27 |   - id: preamble
 28 |     type: checkboxes
 29 |     attributes:
 30 |       label: Prerequisites
 31 |       description: |
 32 |         Please run through the following list and make sure you've tried the usual "quick fixes":
 33 |         - Search the [current open issues](https://github.com/googleapis/genai-toolbox/issues)
 34 |         - Update to the [latest version of Toolbox](https://github.com/googleapis/genai-toolbox/releases)
 35 |       options: 
 36 |         - label: "I've searched the current open issues"
 37 |           required: true
 38 |         - label: "I've updated to the latest version of Toolbox"
 39 | 
 40 |   - type: input
 41 |     id: version
 42 |     attributes:
 43 |       label: Toolbox version
 44 |       description: |
 45 |         What version of Toolbox are you using (`toolbox --version`)? e.g.
 46 |         - toolbox version 0.3.0 
 47 |         - us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:0.3.0
 48 |       placeholder: ex. toolbox version 0.3.0 
 49 |     validations:
 50 |       required: true
 51 | 
 52 |   - type: textarea
 53 |     id: environment
 54 |     attributes:
 55 |       label: Environment
 56 |       description: "Let us know some details about the environment in which you are seeing the bug!"
 57 |       value: |
 58 |         1. OS type and version: (output of `uname -a`)
 59 |         2. How are you running Toolbox: 
 60 |           - As a downloaded binary (e.g. from `curl -O https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox`)
 61 |           - As a container (e.g. from `us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION`)
 62 |           - Compiled from source (include the command used to build)
 63 | 
 64 |   - type: textarea
 65 |     id: client
 66 |     attributes:
 67 |       label: Client 
 68 |       description: "How are you connecting to Toolbox?"
 69 |       value: |
 70 |         1. Client: <name and link to the client are you using>
 71 |         2. Version: <what exact version of the client are you using> 
 72 |         3. Example: If possible, please include your code of configuration:
 73 |         
 74 |         ```python
 75 |         # Code goes here! 
 76 |         ```
 77 | 
 78 |   - id: expected-behavior
 79 |     type: textarea
 80 |     attributes:
 81 |       label: Expected Behavior
 82 |       description: |
 83 |         Please enter a detailed description of the behavior you expected, and any information about what behavior you 
 84 |         noticed and why it is defective or unintentional.
 85 |     validations:
 86 |       required: true
 87 | 
 88 |   - id: current-behavior
 89 |     type: textarea
 90 |     attributes:
 91 |       label: Current Behavior
 92 |       description: "Please enter a detailed description of the behavior you encountered instead."
 93 |     validations:
 94 |       required: true
 95 | 
 96 |   - type: textarea
 97 |     id: repro
 98 |     attributes:
 99 |       label: Steps to reproduce?
100 |       description: |
101 |         How can we reproduce this bug? Please walk us through it step by step,
102 |         with as much relevant detail as possible. A 'minimal' reproduction is
103 |         preferred, which means removing as much of the examples as possible so
104 |         only the minimum required to run and reproduce the bug is left. 
105 |       value: |
106 |         1. ?
107 |         2. ?
108 |         3. ?
109 |         ...
110 |     validations:
111 |       required: true
112 | 
113 |   - type: textarea
114 |     id: additional-details
115 |     attributes:
116 |       label: Additional Details
117 |       description: |
118 |         Any other information you want us to know? Things such as tools config,
119 |         server logs, etc. can be included here.
120 | 
```

--------------------------------------------------------------------------------
/internal/server/mcp/mcp.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package mcp
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"encoding/json"
 20 | 	"fmt"
 21 | 	"net/http"
 22 | 	"slices"
 23 | 
 24 | 	"github.com/googleapis/genai-toolbox/internal/auth"
 25 | 	"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
 26 | 	mcputil "github.com/googleapis/genai-toolbox/internal/server/mcp/util"
 27 | 	v20241105 "github.com/googleapis/genai-toolbox/internal/server/mcp/v20241105"
 28 | 	v20250326 "github.com/googleapis/genai-toolbox/internal/server/mcp/v20250326"
 29 | 	v20250618 "github.com/googleapis/genai-toolbox/internal/server/mcp/v20250618"
 30 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 31 | )
 32 | 
 33 | // LATEST_PROTOCOL_VERSION is the latest version of the MCP protocol supported.
 34 | // Update the version used in InitializeResponse when this value is updated.
 35 | const LATEST_PROTOCOL_VERSION = v20250618.PROTOCOL_VERSION
 36 | 
 37 | // SUPPORTED_PROTOCOL_VERSIONS is the MCP protocol versions that are supported.
 38 | var SUPPORTED_PROTOCOL_VERSIONS = []string{
 39 | 	v20241105.PROTOCOL_VERSION,
 40 | 	v20250326.PROTOCOL_VERSION,
 41 | 	v20250618.PROTOCOL_VERSION,
 42 | }
 43 | 
 44 | // InitializeResponse runs capability negotiation and protocol version agreement.
 45 | // This is the Initialization phase of the lifecycle for MCP client-server connections.
 46 | // Always start with the latest protocol version supported.
 47 | func InitializeResponse(ctx context.Context, id jsonrpc.RequestId, body []byte, toolboxVersion string) (any, string, error) {
 48 | 	var req mcputil.InitializeRequest
 49 | 	if err := json.Unmarshal(body, &req); err != nil {
 50 | 		err = fmt.Errorf("invalid mcp initialize request: %w", err)
 51 | 		return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), "", err
 52 | 	}
 53 | 
 54 | 	var protocolVersion string
 55 | 	v := req.Params.ProtocolVersion
 56 | 	if slices.Contains(SUPPORTED_PROTOCOL_VERSIONS, v) {
 57 | 		protocolVersion = v
 58 | 	} else {
 59 | 		protocolVersion = LATEST_PROTOCOL_VERSION
 60 | 	}
 61 | 
 62 | 	toolsListChanged := false
 63 | 	result := mcputil.InitializeResult{
 64 | 		ProtocolVersion: protocolVersion,
 65 | 		Capabilities: mcputil.ServerCapabilities{
 66 | 			Tools: &mcputil.ListChanged{
 67 | 				ListChanged: &toolsListChanged,
 68 | 			},
 69 | 		},
 70 | 		ServerInfo: mcputil.Implementation{
 71 | 			BaseMetadata: mcputil.BaseMetadata{
 72 | 				Name: mcputil.SERVER_NAME,
 73 | 			},
 74 | 			Version: toolboxVersion,
 75 | 		},
 76 | 	}
 77 | 	res := jsonrpc.JSONRPCResponse{
 78 | 		Jsonrpc: jsonrpc.JSONRPC_VERSION,
 79 | 		Id:      id,
 80 | 		Result:  result,
 81 | 	}
 82 | 
 83 | 	return res, protocolVersion, nil
 84 | }
 85 | 
 86 | // NotificationHandler process notifications request. It MUST NOT send a response.
 87 | // Currently Toolbox does not process any notifications.
 88 | func NotificationHandler(ctx context.Context, body []byte) error {
 89 | 	var notification jsonrpc.JSONRPCNotification
 90 | 	if err := json.Unmarshal(body, &notification); err != nil {
 91 | 		return fmt.Errorf("invalid notification request: %w", err)
 92 | 	}
 93 | 	return nil
 94 | }
 95 | 
 96 | // ProcessMethod returns a response for the request.
 97 | // This is the Operation phase of the lifecycle for MCP client-server connections.
 98 | func ProcessMethod(ctx context.Context, mcpVersion string, id jsonrpc.RequestId, method string, toolset tools.Toolset, tools map[string]tools.Tool, authServices map[string]auth.AuthService, body []byte, header http.Header) (any, error) {
 99 | 	switch mcpVersion {
100 | 	case v20250618.PROTOCOL_VERSION:
101 | 		return v20250618.ProcessMethod(ctx, id, method, toolset, tools, authServices, body, header)
102 | 	case v20250326.PROTOCOL_VERSION:
103 | 		return v20250326.ProcessMethod(ctx, id, method, toolset, tools, authServices, body, header)
104 | 	default:
105 | 		return v20241105.ProcessMethod(ctx, id, method, toolset, tools, authServices, body, header)
106 | 	}
107 | }
108 | 
109 | // VerifyProtocolVersion verifies if the version string is valid.
110 | func VerifyProtocolVersion(version string) bool {
111 | 	return slices.Contains(SUPPORTED_PROTOCOL_VERSIONS, version)
112 | }
113 | 
```

--------------------------------------------------------------------------------
/.ci/continuous.release.cloudbuild.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | # Copyright 2024 Google LLC
  2 | # Licensed under the Apache License, Version 2.0 (the "License");
  3 | # you may not use this file except in compliance with the License.
  4 | # You may obtain a copy of the License at
  5 | #
  6 | #      http://www.apache.org/licenses/LICENSE-2.0
  7 | #
  8 | # Unless required by applicable law or agreed to in writing, software
  9 | # distributed under the License is distributed on an "AS IS" BASIS,
 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11 | # See the License for the specific language governing permissions and
 12 | # limitations under the License.
 13 | 
 14 | steps:
 15 |   - id: "build-docker"
 16 |     name: "gcr.io/cloud-builders/docker"
 17 |     waitFor: ['-']
 18 |     script: |
 19 |         #!/usr/bin/env bash
 20 |         docker buildx create --name container-builder --driver docker-container --bootstrap --use
 21 |         docker buildx build --platform linux/amd64,linux/arm64 --build-arg COMMIT_SHA=$(git rev-parse --short HEAD) -t ${_DOCKER_URI}:$REF_NAME --push .
 22 | 
 23 |   - id: "install-dependencies"
 24 |     name: golang:1
 25 |     waitFor: ['-']
 26 |     env:
 27 |       - 'GOPATH=/gopath'
 28 |     volumes:
 29 |       - name: 'go'
 30 |         path: '/gopath'
 31 |     script: |
 32 |         go get -d ./...
 33 | 
 34 |   - id: "build-linux-amd64"
 35 |     name: golang:1
 36 |     waitFor: 
 37 |       - "install-dependencies"
 38 |     env:
 39 |       - 'GOPATH=/gopath'
 40 |     volumes:
 41 |       - name: 'go'
 42 |         path: '/gopath'
 43 |     script: |
 44 |         #!/usr/bin/env bash
 45 |         CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
 46 |           go build -ldflags "-X github.com/googleapis/genai-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.linux.amd64
 47 | 
 48 |   - id: "store-linux-amd64"
 49 |     name: "gcr.io/cloud-builders/gcloud:latest"
 50 |     waitFor:
 51 |       - "build-linux-amd64"
 52 |     script: |
 53 |         #!/usr/bin/env bash
 54 |         gcloud storage cp toolbox.linux.amd64 gs://$_BUCKET_NAME/$REF_NAME/linux/amd64/toolbox
 55 | 
 56 |   - id: "build-darwin-arm64"
 57 |     name: golang:1
 58 |     waitFor: 
 59 |       - "install-dependencies"
 60 |     env:
 61 |       - 'GOPATH=/gopath'
 62 |     volumes:
 63 |       - name: 'go'
 64 |         path: '/gopath'
 65 |     script: |
 66 |         #!/usr/bin/env bash
 67 |         CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 \
 68 |           go build -ldflags "-X github.com/googleapis/genai-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.darwin.arm64
 69 | 
 70 |   - id: "store-darwin-arm64"
 71 |     name: "gcr.io/cloud-builders/gcloud:latest"
 72 |     waitFor:
 73 |       - "build-darwin-arm64"
 74 |     script: |
 75 |         #!/usr/bin/env bash
 76 |         gcloud storage cp toolbox.darwin.arm64 gs://$_BUCKET_NAME/$REF_NAME/darwin/arm64/toolbox
 77 | 
 78 |   - id: "build-darwin-amd64"
 79 |     name: golang:1
 80 |     waitFor: 
 81 |       - "install-dependencies"
 82 |     env:
 83 |       - 'GOPATH=/gopath'
 84 |     volumes:
 85 |       - name: 'go'
 86 |         path: '/gopath'
 87 |     script: |
 88 |         #!/usr/bin/env bash
 89 |         CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 \
 90 |           go build -ldflags "-X github.com/googleapis/genai-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.darwin.amd64
 91 | 
 92 |   - id: "store-darwin-amd64"
 93 |     name: "gcr.io/cloud-builders/gcloud:latest"
 94 |     waitFor:
 95 |       - "build-darwin-amd64"
 96 |     script: |
 97 |         #!/usr/bin/env bash
 98 |         gcloud storage cp toolbox.darwin.amd64 gs://$_BUCKET_NAME/$REF_NAME/darwin/amd64/toolbox
 99 | 
100 |   - id: "build-windows-amd64"
101 |     name: golang:1
102 |     waitFor: 
103 |       - "install-dependencies"
104 |     env:
105 |       - 'GOPATH=/gopath'
106 |     volumes:
107 |       - name: 'go'
108 |         path: '/gopath'
109 |     script: |
110 |         #!/usr/bin/env bash
111 |         CGO_ENABLED=0 GOOS=windows GOARCH=amd64 \
112 |           go build -ldflags "-X github.com/googleapis/genai-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.windows.amd64
113 | 
114 |   - id: "store-windows-amd64"
115 |     name: "gcr.io/cloud-builders/gcloud:latest"
116 |     waitFor:
117 |       - "build-windows-amd64"
118 |     script: |
119 |         #!/usr/bin/env bash
120 |         gcloud storage cp toolbox.windows.amd64 gs://$_BUCKET_NAME/$REF_NAME/windows/amd64/toolbox.exe
121 | 
122 | options:
123 |   automapSubstitutions: true
124 |   dynamicSubstitutions: true
125 |   logging: CLOUD_LOGGING_ONLY # Necessary for custom service account
126 |   machineType: 'E2_HIGHCPU_32'
127 | 
128 | substitutions:
129 |   _REGION: us-central1
130 |   _AR_HOSTNAME: ${_REGION}-docker.pkg.dev
131 |   _AR_REPO_NAME: toolbox-dev
132 |   _BUCKET_NAME: genai-toolbox-dev
133 |   _DOCKER_URI: ${_AR_HOSTNAME}/${PROJECT_ID}/${_AR_REPO_NAME}/toolbox
134 | 
```

--------------------------------------------------------------------------------
/internal/sources/cloudsqladmin/cloud_sql_admin.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package cloudsqladmin
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 	"net/http"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	"github.com/googleapis/genai-toolbox/internal/util"
 24 | 	"go.opentelemetry.io/otel/trace"
 25 | 	"golang.org/x/oauth2"
 26 | 	"golang.org/x/oauth2/google"
 27 | 	"google.golang.org/api/option"
 28 | 	sqladmin "google.golang.org/api/sqladmin/v1"
 29 | )
 30 | 
 31 | const SourceKind string = "cloud-sql-admin"
 32 | 
 33 | type userAgentRoundTripper struct {
 34 | 	userAgent string
 35 | 	next      http.RoundTripper
 36 | }
 37 | 
 38 | func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
 39 | 	newReq := *req
 40 | 	newReq.Header = make(http.Header)
 41 | 	for k, v := range req.Header {
 42 | 		newReq.Header[k] = v
 43 | 	}
 44 | 	ua := newReq.Header.Get("User-Agent")
 45 | 	if ua == "" {
 46 | 		newReq.Header.Set("User-Agent", rt.userAgent)
 47 | 	} else {
 48 | 		newReq.Header.Set("User-Agent", ua+" "+rt.userAgent)
 49 | 	}
 50 | 	return rt.next.RoundTrip(&newReq)
 51 | }
 52 | 
 53 | // validate interface
 54 | var _ sources.SourceConfig = Config{}
 55 | 
 56 | func init() {
 57 | 	if !sources.Register(SourceKind, newConfig) {
 58 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 59 | 	}
 60 | }
 61 | 
 62 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 63 | 	actual := Config{Name: name}
 64 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 65 | 		return nil, err
 66 | 	}
 67 | 	return actual, nil
 68 | }
 69 | 
 70 | type Config struct {
 71 | 	Name           string `yaml:"name" validate:"required"`
 72 | 	Kind           string `yaml:"kind" validate:"required"`
 73 | 	UseClientOAuth bool   `yaml:"useClientOAuth"`
 74 | }
 75 | 
 76 | func (r Config) SourceConfigKind() string {
 77 | 	return SourceKind
 78 | }
 79 | 
 80 | // Initialize initializes a CloudSQL Admin Source instance.
 81 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 82 | 	ua, err := util.UserAgentFromContext(ctx)
 83 | 	if err != nil {
 84 | 		return nil, fmt.Errorf("error in User Agent retrieval: %s", err)
 85 | 	}
 86 | 
 87 | 	var client *http.Client
 88 | 	if r.UseClientOAuth {
 89 | 		client = &http.Client{
 90 | 			Transport: &userAgentRoundTripper{
 91 | 				userAgent: ua,
 92 | 				next:      http.DefaultTransport,
 93 | 			},
 94 | 		}
 95 | 	} else {
 96 | 		// Use Application Default Credentials
 97 | 		creds, err := google.FindDefaultCredentials(ctx, sqladmin.SqlserviceAdminScope)
 98 | 		if err != nil {
 99 | 			return nil, fmt.Errorf("failed to find default credentials: %w", err)
100 | 		}
101 | 		baseClient := oauth2.NewClient(ctx, creds.TokenSource)
102 | 		baseClient.Transport = &userAgentRoundTripper{
103 | 			userAgent: ua,
104 | 			next:      baseClient.Transport,
105 | 		}
106 | 		client = baseClient
107 | 	}
108 | 
109 | 	service, err := sqladmin.NewService(ctx, option.WithHTTPClient(client))
110 | 	if err != nil {
111 | 		return nil, fmt.Errorf("error creating new sqladmin service: %w", err)
112 | 	}
113 | 
114 | 	s := &Source{
115 | 		Name:           r.Name,
116 | 		Kind:           SourceKind,
117 | 		BaseURL:        "https://sqladmin.googleapis.com",
118 | 		Service:        service,
119 | 		UseClientOAuth: r.UseClientOAuth,
120 | 	}
121 | 	return s, nil
122 | }
123 | 
124 | var _ sources.Source = &Source{}
125 | 
126 | type Source struct {
127 | 	Name           string `yaml:"name"`
128 | 	Kind           string `yaml:"kind"`
129 | 	BaseURL        string
130 | 	Service        *sqladmin.Service
131 | 	UseClientOAuth bool
132 | }
133 | 
134 | func (s *Source) SourceKind() string {
135 | 	return SourceKind
136 | }
137 | 
138 | func (s *Source) GetService(ctx context.Context, accessToken string) (*sqladmin.Service, error) {
139 | 	if s.UseClientOAuth {
140 | 		token := &oauth2.Token{AccessToken: accessToken}
141 | 		client := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token))
142 | 		service, err := sqladmin.NewService(ctx, option.WithHTTPClient(client))
143 | 		if err != nil {
144 | 			return nil, fmt.Errorf("error creating new sqladmin service: %w", err)
145 | 		}
146 | 		return service, nil
147 | 	}
148 | 	return s.Service, nil
149 | }
150 | 
151 | func (s *Source) UseClientAuthorization() bool {
152 | 	return s.UseClientOAuth
153 | }
154 | 
```

--------------------------------------------------------------------------------
/internal/tools/mongodb/mongodbupdateone/mongodbupdateone_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package mongodbupdateone_test
 16 | 
 17 | import (
 18 | 	"strings"
 19 | 	"testing"
 20 | 
 21 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 22 | 	"github.com/googleapis/genai-toolbox/internal/tools/mongodb/mongodbupdateone"
 23 | 
 24 | 	yaml "github.com/goccy/go-yaml"
 25 | 	"github.com/google/go-cmp/cmp"
 26 | 	"github.com/googleapis/genai-toolbox/internal/server"
 27 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 28 | )
 29 | 
 30 | func TestParseFromYamlMongoQuery(t *testing.T) {
 31 | 	ctx, err := testutils.ContextWithNewLogger()
 32 | 	if err != nil {
 33 | 		t.Fatalf("unexpected error: %s", err)
 34 | 	}
 35 | 	tcs := []struct {
 36 | 		desc string
 37 | 		in   string
 38 | 		want server.ToolConfigs
 39 | 	}{
 40 | 		{
 41 | 			desc: "basic example",
 42 | 			in: `
 43 | 			tools:
 44 | 				example_tool:
 45 | 					kind: mongodb-update-one
 46 | 					source: my-instance
 47 | 					description: some description
 48 | 					database: test_db
 49 | 					collection: test_coll
 50 | 					filterPayload: |
 51 | 					    { name: {{json .name}} }
 52 | 					filterParams:
 53 |                         - name: name 
 54 |                           type: string
 55 |                           description: small description
 56 | 					updatePayload: |
 57 | 					    { $set : { item: {{json .item}} } }
 58 | 					updateParams:
 59 |                         - name: item
 60 |                           type: string
 61 |                           description: small description
 62 | 					canonical: true
 63 | 					upsert: true
 64 | 			`,
 65 | 			want: server.ToolConfigs{
 66 | 				"example_tool": mongodbupdateone.Config{
 67 | 					Name:          "example_tool",
 68 | 					Kind:          "mongodb-update-one",
 69 | 					Source:        "my-instance",
 70 | 					AuthRequired:  []string{},
 71 | 					Database:      "test_db",
 72 | 					Collection:    "test_coll",
 73 | 					Canonical:     true,
 74 | 					FilterPayload: "{ name: {{json .name}} }\n",
 75 | 					FilterParams: tools.Parameters{
 76 | 						&tools.StringParameter{
 77 | 							CommonParameter: tools.CommonParameter{
 78 | 								Name: "name",
 79 | 								Type: "string",
 80 | 								Desc: "small description",
 81 | 							},
 82 | 						},
 83 | 					},
 84 | 					UpdatePayload: "{ $set : { item: {{json .item}} } }\n",
 85 | 					UpdateParams: tools.Parameters{
 86 | 						&tools.StringParameter{
 87 | 							CommonParameter: tools.CommonParameter{
 88 | 								Name: "item",
 89 | 								Type: "string",
 90 | 								Desc: "small description",
 91 | 							},
 92 | 						},
 93 | 					},
 94 | 					Upsert:      true,
 95 | 					Description: "some description",
 96 | 				},
 97 | 			},
 98 | 		},
 99 | 	}
100 | 	for _, tc := range tcs {
101 | 		t.Run(tc.desc, func(t *testing.T) {
102 | 			got := struct {
103 | 				Tools server.ToolConfigs `yaml:"tools"`
104 | 			}{}
105 | 			// Parse contents
106 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
107 | 			if err != nil {
108 | 				t.Fatalf("unable to unmarshal: %s", err)
109 | 			}
110 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
111 | 				t.Fatalf("incorrect parse: diff %v", diff)
112 | 			}
113 | 		})
114 | 	}
115 | 
116 | }
117 | 
118 | func TestFailParseFromYamlMongoQuery(t *testing.T) {
119 | 	ctx, err := testutils.ContextWithNewLogger()
120 | 	if err != nil {
121 | 		t.Fatalf("unexpected error: %s", err)
122 | 	}
123 | 	tcs := []struct {
124 | 		desc string
125 | 		in   string
126 | 		err  string
127 | 	}{
128 | 		{
129 | 			desc: "Invalid method",
130 | 			in: `
131 | 			tools:
132 | 				example_tool:
133 | 					kind: mongodb-update-one
134 | 					source: my-instance
135 | 					description: some description
136 | 					collection: test_coll
137 | 					filterPayload: |
138 | 					  { name : {{json .name}} }`,
139 | 			err: `unable to parse tool "example_tool" as kind "mongodb-update-one"`,
140 | 		},
141 | 	}
142 | 	for _, tc := range tcs {
143 | 		t.Run(tc.desc, func(t *testing.T) {
144 | 			got := struct {
145 | 				Tools server.ToolConfigs `yaml:"tools"`
146 | 			}{}
147 | 			// Parse contents
148 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
149 | 			if err == nil {
150 | 				t.Fatalf("expect parsing to fail")
151 | 			}
152 | 			errStr := err.Error()
153 | 			if !strings.Contains(errStr, tc.err) {
154 | 				t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err)
155 | 			}
156 | 		})
157 | 	}
158 | 
159 | }
160 | 
```

--------------------------------------------------------------------------------
/internal/sources/cassandra/cassandra_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package cassandra_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources/cassandra"
 24 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 25 | )
 26 | 
 27 | func TestParseFromYamlCassandra(t *testing.T) {
 28 | 	tcs := []struct {
 29 | 		desc string
 30 | 		in   string
 31 | 		want server.SourceConfigs
 32 | 	}{
 33 | 		{
 34 | 			desc: "basic example (without optional fields)",
 35 | 			in: `
 36 | 			sources:
 37 | 				my-cassandra-instance:
 38 | 					kind: cassandra
 39 | 					hosts:
 40 | 						- "my-host1"
 41 | 						- "my-host2"
 42 | 			`,
 43 | 			want: server.SourceConfigs{
 44 | 				"my-cassandra-instance": cassandra.Config{
 45 | 					Name:                   "my-cassandra-instance",
 46 | 					Kind:                   cassandra.SourceKind,
 47 | 					Hosts:                  []string{"my-host1", "my-host2"},
 48 | 					Username:               "",
 49 | 					Password:               "",
 50 | 					ProtoVersion:           0,
 51 | 					CAPath:                 "",
 52 | 					CertPath:               "",
 53 | 					KeyPath:                "",
 54 | 					Keyspace:               "",
 55 | 					EnableHostVerification: false,
 56 | 				},
 57 | 			},
 58 | 		},
 59 | 		{
 60 | 			desc: "with optional fields",
 61 | 			in: `
 62 | 			sources:
 63 | 				my-cassandra-instance:
 64 | 					kind: cassandra
 65 | 					hosts:
 66 | 						- "my-host1"
 67 | 						- "my-host2"
 68 | 					username: "user"
 69 | 					password: "pass"
 70 | 					keyspace: "example_keyspace"
 71 | 					protoVersion: 4
 72 | 					caPath: "path/to/ca.crt"
 73 | 					certPath: "path/to/cert"
 74 | 					keyPath: "path/to/key"
 75 | 					enableHostVerification: true
 76 | 			`,
 77 | 			want: server.SourceConfigs{
 78 | 				"my-cassandra-instance": cassandra.Config{
 79 | 					Name:                   "my-cassandra-instance",
 80 | 					Kind:                   cassandra.SourceKind,
 81 | 					Hosts:                  []string{"my-host1", "my-host2"},
 82 | 					Username:               "user",
 83 | 					Password:               "pass",
 84 | 					Keyspace:               "example_keyspace",
 85 | 					ProtoVersion:           4,
 86 | 					CAPath:                 "path/to/ca.crt",
 87 | 					CertPath:               "path/to/cert",
 88 | 					KeyPath:                "path/to/key",
 89 | 					EnableHostVerification: true,
 90 | 				},
 91 | 			},
 92 | 		},
 93 | 	}
 94 | 	for _, tc := range tcs {
 95 | 		t.Run(tc.desc, func(t *testing.T) {
 96 | 			got := struct {
 97 | 				Sources server.SourceConfigs `yaml:"sources"`
 98 | 			}{}
 99 | 			// Parse contents
100 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
101 | 			if err != nil {
102 | 				t.Fatalf("unable to unmarshal: %s", err)
103 | 			}
104 | 			if !cmp.Equal(tc.want, got.Sources) {
105 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
106 | 			}
107 | 		})
108 | 	}
109 | 
110 | }
111 | 
112 | func TestFailParseFromYaml(t *testing.T) {
113 | 	tcs := []struct {
114 | 		desc string
115 | 		in   string
116 | 		err  string
117 | 	}{
118 | 		{
119 | 			desc: "extra field",
120 | 			in: `
121 | 			sources:
122 | 				my-cassandra-instance:
123 | 					kind: cassandra
124 | 					hosts:
125 | 						- "my-host"
126 | 					foo: bar
127 | 			`,
128 | 			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",
129 | 		},
130 | 		{
131 | 			desc: "missing required field",
132 | 			in: `
133 | 			sources:
134 | 				my-cassandra-instance:
135 | 					kind: cassandra
136 | 			`,
137 | 			err: "unable to parse source \"my-cassandra-instance\" as \"cassandra\": Key: 'Config.Hosts' Error:Field validation for 'Hosts' failed on the 'required' tag",
138 | 		},
139 | 	}
140 | 
141 | 	for _, tc := range tcs {
142 | 		t.Run(tc.desc, func(t *testing.T) {
143 | 			got := struct {
144 | 				Sources server.SourceConfigs `yaml:"sources"`
145 | 			}{}
146 | 			// Parse contents
147 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
148 | 			if err == nil {
149 | 				t.Fatalf("expect parsing to fail")
150 | 			}
151 | 			errStr := err.Error()
152 | 			if errStr != tc.err {
153 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
154 | 			}
155 | 		})
156 | 	}
157 | 
158 | }
159 | 
```

--------------------------------------------------------------------------------
/internal/sources/yugabytedb/yugabytedb.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package yugabytedb
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	"github.com/yugabyte/pgx/v5/pgxpool"
 24 | 	"go.opentelemetry.io/otel/trace"
 25 | )
 26 | 
 27 | const SourceKind string = "yugabytedb"
 28 | 
 29 | // validate interface
 30 | var _ sources.SourceConfig = Config{}
 31 | 
 32 | func init() {
 33 | 	if !sources.Register(SourceKind, newConfig) {
 34 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type Config struct {
 47 | 	Name                            string `yaml:"name" validate:"required"`
 48 | 	Kind                            string `yaml:"kind" validate:"required"`
 49 | 	Host                            string `yaml:"host" validate:"required"`
 50 | 	Port                            string `yaml:"port" validate:"required"`
 51 | 	User                            string `yaml:"user" validate:"required"`
 52 | 	Password                        string `yaml:"password" validate:"required"`
 53 | 	Database                        string `yaml:"database" validate:"required"`
 54 | 	LoadBalance                     string `yaml:"loadBalance"`
 55 | 	TopologyKeys                    string `yaml:"topologyKeys"`
 56 | 	YBServersRefreshInterval        string `yaml:"ybServersRefreshInterval"`
 57 | 	FallBackToTopologyKeysOnly      string `yaml:"fallbackToTopologyKeysOnly"`
 58 | 	FailedHostReconnectDelaySeconds string `yaml:"failedHostReconnectDelaySecs"`
 59 | }
 60 | 
 61 | func (r Config) SourceConfigKind() string {
 62 | 	return SourceKind
 63 | }
 64 | 
 65 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 66 | 	pool, err := initYugabyteDBConnectionPool(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database, r.LoadBalance, r.TopologyKeys, r.YBServersRefreshInterval, r.FallBackToTopologyKeysOnly, r.FailedHostReconnectDelaySeconds)
 67 | 	if err != nil {
 68 | 		return nil, fmt.Errorf("unable to create pool: %w", err)
 69 | 	}
 70 | 
 71 | 	err = pool.Ping(ctx)
 72 | 	if err != nil {
 73 | 		return nil, fmt.Errorf("unable to connect successfully: %w", err)
 74 | 	}
 75 | 
 76 | 	s := &Source{
 77 | 		Name: r.Name,
 78 | 		Kind: SourceKind,
 79 | 		Pool: pool,
 80 | 	}
 81 | 	return s, nil
 82 | }
 83 | 
 84 | var _ sources.Source = &Source{}
 85 | 
 86 | type Source struct {
 87 | 	Name string `yaml:"name"`
 88 | 	Kind string `yaml:"kind"`
 89 | 	Pool *pgxpool.Pool
 90 | }
 91 | 
 92 | func (s *Source) SourceKind() string {
 93 | 	return SourceKind
 94 | }
 95 | 
 96 | func (s *Source) YugabyteDBPool() *pgxpool.Pool {
 97 | 	return s.Pool
 98 | }
 99 | 
100 | func initYugabyteDBConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname, loadBalance, topologyKeys, refreshInterval, explicitFallback, failedHostTTL string) (*pgxpool.Pool, error) {
101 | 	//nolint:all // Reassigned ctx
102 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
103 | 	defer span.End()
104 | 	// urlExample := "postgres://username:password@localhost:5433/database_name"
105 | 	i := fmt.Sprintf("postgres://%s:%s@%s:%s/%s", user, pass, host, port, dbname)
106 | 	if loadBalance == "true" {
107 | 		i = fmt.Sprintf("%s?load_balance=%s", i, loadBalance)
108 | 		if topologyKeys != "" {
109 | 			i = fmt.Sprintf("%s&topology_keys=%s", i, topologyKeys)
110 | 			if explicitFallback == "true" {
111 | 				i = fmt.Sprintf("%s&fallback_to_topology_keys_only=%s", i, explicitFallback)
112 | 			}
113 | 		}
114 | 		if refreshInterval != "" {
115 | 			i = fmt.Sprintf("%s&yb_servers_refresh_interval=%s", i, refreshInterval)
116 | 		}
117 | 		if failedHostTTL != "" {
118 | 			i = fmt.Sprintf("%s&failed_host_reconnect_delay_secs=%s", i, failedHostTTL)
119 | 		}
120 | 	}
121 | 	pool, err := pgxpool.New(ctx, i)
122 | 	if err != nil {
123 | 		return nil, fmt.Errorf("unable to create connection pool: %w", err)
124 | 	}
125 | 
126 | 	return pool, nil
127 | }
128 | 
```

--------------------------------------------------------------------------------
/internal/sources/bigquery/bigquery_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package bigquery_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources/bigquery"
 24 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 25 | )
 26 | 
 27 | func TestParseFromYamlBigQuery(t *testing.T) {
 28 | 	tcs := []struct {
 29 | 		desc string
 30 | 		in   string
 31 | 		want server.SourceConfigs
 32 | 	}{
 33 | 		{
 34 | 			desc: "basic example",
 35 | 			in: `
 36 | 			sources:
 37 | 				my-instance:
 38 | 					kind: bigquery
 39 | 					project: my-project
 40 | 			`,
 41 | 			want: server.SourceConfigs{
 42 | 				"my-instance": bigquery.Config{
 43 | 					Name:      "my-instance",
 44 | 					Kind:      bigquery.SourceKind,
 45 | 					Project:   "my-project",
 46 | 					Location:  "",
 47 | 					WriteMode: "",
 48 | 				},
 49 | 			},
 50 | 		},
 51 | 		{
 52 | 			desc: "all fields specified",
 53 | 			in: `
 54 | 			sources:
 55 | 				my-instance:
 56 | 					kind: bigquery
 57 | 					project: my-project
 58 | 					location: asia
 59 | 					writeMode: blocked
 60 | 			`,
 61 | 			want: server.SourceConfigs{
 62 | 				"my-instance": bigquery.Config{
 63 | 					Name:           "my-instance",
 64 | 					Kind:           bigquery.SourceKind,
 65 | 					Project:        "my-project",
 66 | 					Location:       "asia",
 67 | 					WriteMode:      "blocked",
 68 | 					UseClientOAuth: false,
 69 | 				},
 70 | 			},
 71 | 		},
 72 | 		{
 73 | 			desc: "use client auth example",
 74 | 			in: `
 75 | 			sources:
 76 | 				my-instance:
 77 | 					kind: bigquery
 78 | 					project: my-project
 79 | 					location: us
 80 | 					useClientOAuth: true
 81 | 			`,
 82 | 			want: server.SourceConfigs{
 83 | 				"my-instance": bigquery.Config{
 84 | 					Name:           "my-instance",
 85 | 					Kind:           bigquery.SourceKind,
 86 | 					Project:        "my-project",
 87 | 					Location:       "us",
 88 | 					UseClientOAuth: true,
 89 | 				},
 90 | 			},
 91 | 		},
 92 | 		{
 93 | 			desc: "with allowed datasets example",
 94 | 			in: `
 95 | 			sources:
 96 | 				my-instance:
 97 | 					kind: bigquery
 98 | 					project: my-project
 99 | 					location: us
100 | 					allowedDatasets:
101 | 						- my_dataset
102 | 			`,
103 | 			want: server.SourceConfigs{
104 | 				"my-instance": bigquery.Config{
105 | 					Name:            "my-instance",
106 | 					Kind:            bigquery.SourceKind,
107 | 					Project:         "my-project",
108 | 					Location:        "us",
109 | 					AllowedDatasets: []string{"my_dataset"},
110 | 				},
111 | 			},
112 | 		},
113 | 	}
114 | 	for _, tc := range tcs {
115 | 		t.Run(tc.desc, func(t *testing.T) {
116 | 			got := struct {
117 | 				Sources server.SourceConfigs `yaml:"sources"`
118 | 			}{}
119 | 			// Parse contents
120 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
121 | 			if err != nil {
122 | 				t.Fatalf("unable to unmarshal: %s", err)
123 | 			}
124 | 			if !cmp.Equal(tc.want, got.Sources) {
125 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
126 | 			}
127 | 		})
128 | 	}
129 | 
130 | }
131 | 
132 | func TestFailParseFromYaml(t *testing.T) {
133 | 	tcs := []struct {
134 | 		desc string
135 | 		in   string
136 | 		err  string
137 | 	}{
138 | 		{
139 | 			desc: "extra field",
140 | 			in: `
141 | 			sources:
142 | 				my-instance:
143 | 					kind: bigquery
144 | 					project: my-project
145 | 					location: us
146 | 					foo: bar
147 | 			`,
148 | 			err: "unable to parse source \"my-instance\" as \"bigquery\": [1:1] unknown field \"foo\"\n>  1 | foo: bar\n       ^\n   2 | kind: bigquery\n   3 | location: us\n   4 | project: my-project",
149 | 		},
150 | 		{
151 | 			desc: "missing required field",
152 | 			in: `
153 | 			sources:
154 | 				my-instance:
155 | 					kind: bigquery
156 | 					location: us
157 | 			`,
158 | 			err: "unable to parse source \"my-instance\" as \"bigquery\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
159 | 		},
160 | 	}
161 | 	for _, tc := range tcs {
162 | 		t.Run(tc.desc, func(t *testing.T) {
163 | 			got := struct {
164 | 				Sources server.SourceConfigs `yaml:"sources"`
165 | 			}{}
166 | 			// Parse contents
167 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
168 | 			if err == nil {
169 | 				t.Fatalf("expect parsing to fail")
170 | 			}
171 | 			errStr := err.Error()
172 | 			if errStr != tc.err {
173 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
174 | 			}
175 | 		})
176 | 	}
177 | }
178 | 
```

--------------------------------------------------------------------------------
/docs/en/getting-started/quickstart/shared/database_setup.md:
--------------------------------------------------------------------------------

```markdown
  1 | <!-- This file has been used in local_quickstart.md, local_quickstart_go.md & local_quickstart_js.md -->
  2 | <!-- [START database_setup] -->
  3 | In this section, we will create a database, insert some data that needs to be
  4 | accessed by our agent, and create a database user for Toolbox to connect with.
  5 | 
  6 | 1. Connect to postgres using the `psql` command:
  7 | 
  8 |     ```bash
  9 |     psql -h 127.0.0.1 -U postgres
 10 |     ```
 11 | 
 12 |     Here, `postgres` denotes the default postgres superuser.
 13 | 
 14 |     {{< notice info >}}
 15 | 
 16 | #### **Having trouble connecting?**
 17 | 
 18 | * **Password Prompt:** If you are prompted for a password for the `postgres`
 19 |   user and do not know it (or a blank password doesn't work), your PostgreSQL
 20 |   installation might require a password or a different authentication method.
 21 | * **`FATAL: role "postgres" does not exist`:** This error means the default
 22 |   `postgres` superuser role isn't available under that name on your system.
 23 | * **`Connection refused`:** Ensure your PostgreSQL server is actually running.
 24 |   You can typically check with `sudo systemctl status postgresql` and start it
 25 |   with `sudo systemctl start postgresql` on Linux systems.
 26 | 
 27 | <br/>
 28 | 
 29 | #### **Common Solution**
 30 | 
 31 | For password issues or if the `postgres` role seems inaccessible directly, try
 32 | switching to the `postgres` operating system user first. This user often has
 33 | permission to connect without a password for local connections (this is called
 34 | peer authentication).
 35 | 
 36 | ```bash
 37 | sudo -i -u postgres
 38 | psql -h 127.0.0.1
 39 | ```
 40 | 
 41 | Once you are in the `psql` shell using this method, you can proceed with the
 42 | database creation steps below. Afterwards, type `\q` to exit `psql`, and then
 43 | `exit` to return to your normal user shell.
 44 | 
 45 | If desired, once connected to `psql` as the `postgres` OS user, you can set a
 46 | password for the `postgres` *database* user using: `ALTER USER postgres WITH
 47 | PASSWORD 'your_chosen_password';`. This would allow direct connection with `-U
 48 | postgres` and a password next time.
 49 |     {{< /notice >}}
 50 | 
 51 | 1. Create a new database and a new user:
 52 | 
 53 |     {{< notice tip >}}
 54 |   For a real application, it's best to follow the principle of least permission
 55 |   and only grant the privileges your application needs.
 56 |     {{< /notice >}}
 57 | 
 58 |     ```sql
 59 |       CREATE USER toolbox_user WITH PASSWORD 'my-password';
 60 | 
 61 |       CREATE DATABASE toolbox_db;
 62 |       GRANT ALL PRIVILEGES ON DATABASE toolbox_db TO toolbox_user;
 63 | 
 64 |       ALTER DATABASE toolbox_db OWNER TO toolbox_user;
 65 |     ```
 66 | 
 67 | 1. End the database session:
 68 | 
 69 |     ```bash
 70 |     \q
 71 |     ```
 72 | 
 73 |     (If you used `sudo -i -u postgres` and then `psql`, remember you might also
 74 |     need to type `exit` after `\q` to leave the `postgres` user's shell
 75 |     session.)
 76 | 
 77 | 1. Connect to your database with your new user:
 78 | 
 79 |     ```bash
 80 |     psql -h 127.0.0.1 -U toolbox_user -d toolbox_db
 81 |     ```
 82 | 
 83 | 1. Create a table using the following command:
 84 | 
 85 |     ```sql
 86 |     CREATE TABLE hotels(
 87 |       id            INTEGER NOT NULL PRIMARY KEY,
 88 |       name          VARCHAR NOT NULL,
 89 |       location      VARCHAR NOT NULL,
 90 |       price_tier    VARCHAR NOT NULL,
 91 |       checkin_date  DATE    NOT NULL,
 92 |       checkout_date DATE    NOT NULL,
 93 |       booked        BIT     NOT NULL
 94 |     );
 95 |     ```
 96 | 
 97 | 1. Insert data into the table.
 98 | 
 99 |     ```sql
100 |     INSERT INTO hotels(id, name, location, price_tier, checkin_date, checkout_date, booked)
101 |     VALUES
102 |       (1, 'Hilton Basel', 'Basel', 'Luxury', '2024-04-22', '2024-04-20', B'0'),
103 |       (2, 'Marriott Zurich', 'Zurich', 'Upscale', '2024-04-14', '2024-04-21', B'0'),
104 |       (3, 'Hyatt Regency Basel', 'Basel', 'Upper Upscale', '2024-04-02', '2024-04-20', B'0'),
105 |       (4, 'Radisson Blu Lucerne', 'Lucerne', 'Midscale', '2024-04-24', '2024-04-05', B'0'),
106 |       (5, 'Best Western Bern', 'Bern', 'Upper Midscale', '2024-04-23', '2024-04-01', B'0'),
107 |       (6, 'InterContinental Geneva', 'Geneva', 'Luxury', '2024-04-23', '2024-04-28', B'0'),
108 |       (7, 'Sheraton Zurich', 'Zurich', 'Upper Upscale', '2024-04-27', '2024-04-02', B'0'),
109 |       (8, 'Holiday Inn Basel', 'Basel', 'Upper Midscale', '2024-04-24', '2024-04-09', B'0'),
110 |       (9, 'Courtyard Zurich', 'Zurich', 'Upscale', '2024-04-03', '2024-04-13', B'0'),
111 |       (10, 'Comfort Inn Bern', 'Bern', 'Midscale', '2024-04-04', '2024-04-16', B'0');
112 |     ```
113 | 
114 | 1. End the database session:
115 | 
116 |     ```bash
117 |     \q
118 |     ```
119 | <!-- [END database_setup] -->
```

--------------------------------------------------------------------------------
/tests/valkey/valkey_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package valkey
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"log"
 20 | 	"os"
 21 | 	"regexp"
 22 | 	"testing"
 23 | 	"time"
 24 | 
 25 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 26 | 	"github.com/googleapis/genai-toolbox/tests"
 27 | 	"github.com/valkey-io/valkey-go"
 28 | )
 29 | 
 30 | var (
 31 | 	ValkeySourceKind = "valkey"
 32 | 	ValkeyToolKind   = "valkey"
 33 | 	ValkeyAddress    = os.Getenv("VALKEY_ADDRESS")
 34 | )
 35 | 
 36 | func getValkeyVars(t *testing.T) map[string]any {
 37 | 	switch "" {
 38 | 	case ValkeyAddress:
 39 | 		t.Fatal("'VALKEY_ADDRESS' not set")
 40 | 	}
 41 | 	return map[string]any{
 42 | 		"kind":         ValkeySourceKind,
 43 | 		"address":      []string{ValkeyAddress},
 44 | 		"disableCache": true,
 45 | 	}
 46 | }
 47 | 
 48 | func initValkeyClient(ctx context.Context, addr []string) (valkey.Client, error) {
 49 | 	// Pass in an access token getter fn for IAM auth
 50 | 	client, err := valkey.NewClient(valkey.ClientOption{
 51 | 		InitAddress:       addr,
 52 | 		ForceSingleClient: true,
 53 | 		DisableCache:      true,
 54 | 	})
 55 | 
 56 | 	if err != nil {
 57 | 		log.Fatalf("error creating client: %v", err)
 58 | 	}
 59 | 
 60 | 	// Ping the server to check connectivity (using Do)
 61 | 	pingCmd := client.B().Ping().Build()
 62 | 	_, err = client.Do(ctx, pingCmd).ToString()
 63 | 	if err != nil {
 64 | 		log.Fatalf("Failed to execute PING command: %v", err)
 65 | 	}
 66 | 	log.Println("Successfully connected to Valkey")
 67 | 	return client, nil
 68 | }
 69 | 
 70 | func TestValkeyToolEndpoints(t *testing.T) {
 71 | 	sourceConfig := getValkeyVars(t)
 72 | 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
 73 | 	defer cancel()
 74 | 
 75 | 	var args []string
 76 | 
 77 | 	client, err := initValkeyClient(ctx, []string{ValkeyAddress})
 78 | 	if err != nil {
 79 | 		t.Fatalf("unable to create Valkey connection: %s", err)
 80 | 	}
 81 | 
 82 | 	// set up data for param tool
 83 | 	teardownDB := setupValkeyDB(t, ctx, client)
 84 | 	defer teardownDB(t)
 85 | 
 86 | 	// Write config into a file and pass it to command
 87 | 	toolsFile := tests.GetRedisValkeyToolsConfig(sourceConfig, ValkeyToolKind)
 88 | 
 89 | 	cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
 90 | 	if err != nil {
 91 | 		t.Fatalf("command initialization returned an error: %s", err)
 92 | 	}
 93 | 	defer cleanup()
 94 | 
 95 | 	waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
 96 | 	defer cancel()
 97 | 	out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
 98 | 	if err != nil {
 99 | 		t.Logf("toolbox command logs: \n%s", out)
100 | 		t.Fatalf("toolbox didn't start successfully: %s", err)
101 | 	}
102 | 
103 | 	// Get configs for tests
104 | 	select1Want, mcpMyFailToolWant, invokeParamWant, invokeIdNullWant, nullWant, mcpSelect1Want, mcpInvokeParamWant := tests.GetRedisValkeyWants()
105 | 
106 | 	// Run tests
107 | 	tests.RunToolGetTest(t)
108 | 	tests.RunToolInvokeTest(t, select1Want,
109 | 		tests.WithMyToolId3NameAliceWant(invokeParamWant),
110 | 		tests.WithMyArrayToolWant(invokeParamWant),
111 | 		tests.WithMyToolById4Want(invokeIdNullWant),
112 | 		tests.WithNullWant(nullWant),
113 | 	)
114 | 	tests.RunMCPToolCallMethod(t, mcpMyFailToolWant, mcpSelect1Want,
115 | 		tests.WithMcpMyToolId3NameAliceWant(mcpInvokeParamWant),
116 | 	)
117 | }
118 | 
119 | func setupValkeyDB(t *testing.T, ctx context.Context, client valkey.Client) func(*testing.T) {
120 | 	keys := []string{"row1", "row2", "row3", "row4", "null"}
121 | 	commands := [][]string{
122 | 		{"HSET", keys[0], "name", "Alice", "id", "1"},
123 | 		{"HSET", keys[1], "name", "Jane", "id", "2"},
124 | 		{"HSET", keys[2], "name", "Sid", "id", "3"},
125 | 		{"HSET", keys[3], "name", "", "id", "4"},
126 | 		{"SET", keys[4], "null"},
127 | 		{"HSET", tests.ServiceAccountEmail, "name", "Alice"},
128 | 	}
129 | 	builtCmds := make(valkey.Commands, len(commands))
130 | 
131 | 	for i, cmd := range commands {
132 | 		builtCmds[i] = client.B().Arbitrary(cmd...).Build()
133 | 	}
134 | 
135 | 	responses := client.DoMulti(ctx, builtCmds...)
136 | 	for _, resp := range responses {
137 | 		if err := resp.Error(); err != nil {
138 | 			t.Fatalf("unable to insert test data: %s", err)
139 | 		}
140 | 	}
141 | 
142 | 	return func(t *testing.T) {
143 | 		// tear down test
144 | 		_, err := client.Do(ctx, client.B().Del().Key(keys...).Build()).AsInt64()
145 | 		if err != nil {
146 | 			t.Errorf("Teardown failed: %s", err)
147 | 		}
148 | 	}
149 | 
150 | }
151 | 
```

--------------------------------------------------------------------------------
/internal/tools/bigtable/bigtable_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package bigtable_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools/bigtable"
 26 | )
 27 | 
 28 | func TestParseFromYamlBigtable(t *testing.T) {
 29 | 	ctx, err := testutils.ContextWithNewLogger()
 30 | 	if err != nil {
 31 | 		t.Fatalf("unexpected error: %s", err)
 32 | 	}
 33 | 	tcs := []struct {
 34 | 		desc string
 35 | 		in   string
 36 | 		want server.ToolConfigs
 37 | 	}{
 38 | 		{
 39 | 			desc: "basic example",
 40 | 			in: `
 41 | 			tools:
 42 | 				example_tool:
 43 | 					kind: bigtable-sql
 44 | 					source: my-pg-instance
 45 | 					description: some description
 46 | 					statement: |
 47 | 						SELECT * FROM SQL_STATEMENT;
 48 | 					parameters:
 49 | 						- name: country
 50 | 						  type: string
 51 | 						  description: some description
 52 | 			`,
 53 | 			want: server.ToolConfigs{
 54 | 				"example_tool": bigtable.Config{
 55 | 					Name:         "example_tool",
 56 | 					Kind:         "bigtable-sql",
 57 | 					Source:       "my-pg-instance",
 58 | 					Description:  "some description",
 59 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 60 | 					AuthRequired: []string{},
 61 | 					Parameters: []tools.Parameter{
 62 | 						tools.NewStringParameter("country", "some description"),
 63 | 					},
 64 | 				},
 65 | 			},
 66 | 		},
 67 | 	}
 68 | 	for _, tc := range tcs {
 69 | 		t.Run(tc.desc, func(t *testing.T) {
 70 | 			got := struct {
 71 | 				Tools server.ToolConfigs `yaml:"tools"`
 72 | 			}{}
 73 | 			// Parse contents
 74 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 75 | 			if err != nil {
 76 | 				t.Fatalf("unable to unmarshal: %s", err)
 77 | 			}
 78 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 79 | 				t.Fatalf("incorrect parse: diff %v", diff)
 80 | 			}
 81 | 		})
 82 | 	}
 83 | 
 84 | }
 85 | 
 86 | func TestParseFromYamlWithTemplateBigtable(t *testing.T) {
 87 | 	ctx, err := testutils.ContextWithNewLogger()
 88 | 	if err != nil {
 89 | 		t.Fatalf("unexpected error: %s", err)
 90 | 	}
 91 | 	tcs := []struct {
 92 | 		desc string
 93 | 		in   string
 94 | 		want server.ToolConfigs
 95 | 	}{
 96 | 		{
 97 | 			desc: "basic example",
 98 | 			in: `
 99 | 			tools:
100 | 				example_tool:
101 | 					kind: bigtable-sql
102 | 					source: my-pg-instance
103 | 					description: some description
104 | 					statement: |
105 | 						SELECT * FROM SQL_STATEMENT;
106 | 					parameters:
107 | 						- name: country
108 | 						  type: string
109 | 						  description: some description
110 | 					templateParameters:
111 | 						- name: tableName
112 | 						  type: string
113 | 						  description: The table to select hotels from.
114 | 						- name: fieldArray
115 | 						  type: array
116 | 						  description: The columns to return for the query.
117 | 						  items: 
118 | 								name: column
119 | 								type: string
120 | 								description: A column name that will be returned from the query.
121 | 			`,
122 | 			want: server.ToolConfigs{
123 | 				"example_tool": bigtable.Config{
124 | 					Name:         "example_tool",
125 | 					Kind:         "bigtable-sql",
126 | 					Source:       "my-pg-instance",
127 | 					Description:  "some description",
128 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
129 | 					AuthRequired: []string{},
130 | 					Parameters: []tools.Parameter{
131 | 						tools.NewStringParameter("country", "some description"),
132 | 					},
133 | 					TemplateParameters: []tools.Parameter{
134 | 						tools.NewStringParameter("tableName", "The table to select hotels from."),
135 | 						tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
136 | 					},
137 | 				},
138 | 			},
139 | 		},
140 | 	}
141 | 	for _, tc := range tcs {
142 | 		t.Run(tc.desc, func(t *testing.T) {
143 | 			got := struct {
144 | 				Tools server.ToolConfigs `yaml:"tools"`
145 | 			}{}
146 | 			// Parse contents
147 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
148 | 			if err != nil {
149 | 				t.Fatalf("unable to unmarshal: %s", err)
150 | 			}
151 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
152 | 				t.Fatalf("incorrect parse: diff %v", diff)
153 | 			}
154 | 		})
155 | 	}
156 | 
157 | }
158 | 
```

--------------------------------------------------------------------------------
/internal/server/mcp/util/lifecycle.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package util
 16 | 
 17 | import (
 18 | 	"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
 19 | )
 20 | 
 21 | const (
 22 | 	// SERVER_NAME is the server name used in Implementation.
 23 | 	SERVER_NAME = "Toolbox"
 24 | 	// methods that are supported
 25 | 	INITIALIZE = "initialize"
 26 | )
 27 | 
 28 | /* Initialization */
 29 | 
 30 | // Params to define MCP Client during initialize request.
 31 | type InitializeParams struct {
 32 | 	// The latest version of the Model Context Protocol that the client supports.
 33 | 	// The client MAY decide to support older versions as well.
 34 | 	ProtocolVersion string             `json:"protocolVersion"`
 35 | 	Capabilities    ClientCapabilities `json:"capabilities"`
 36 | 	ClientInfo      Implementation     `json:"clientInfo"`
 37 | }
 38 | 
 39 | // InitializeRequest is sent from the client to the server when it first
 40 | // connects, asking it to begin initialization.
 41 | type InitializeRequest struct {
 42 | 	jsonrpc.Request
 43 | 	Params InitializeParams `json:"params"`
 44 | }
 45 | 
 46 | // InitializeResult is sent after receiving an initialize request from the
 47 | // client.
 48 | type InitializeResult struct {
 49 | 	jsonrpc.Result
 50 | 	// The version of the Model Context Protocol that the server wants to use.
 51 | 	// This may not match the version that the client requested. If the client cannot
 52 | 	// support this version, it MUST disconnect.
 53 | 	ProtocolVersion string             `json:"protocolVersion"`
 54 | 	Capabilities    ServerCapabilities `json:"capabilities"`
 55 | 	ServerInfo      Implementation     `json:"serverInfo"`
 56 | 	// Instructions describing how to use the server and its features.
 57 | 	//
 58 | 	// This can be used by clients to improve the LLM's understanding of
 59 | 	// available tools, resources, etc. It can be thought of like a "hint" to the model.
 60 | 	// For example, this information MAY be added to the system prompt.
 61 | 	Instructions string `json:"instructions,omitempty"`
 62 | }
 63 | 
 64 | // InitializedNotification is sent from the client to the server after
 65 | // initialization has finished.
 66 | type InitializedNotification struct {
 67 | 	jsonrpc.Notification
 68 | }
 69 | 
 70 | // ListChange represents whether the server supports notification for changes to the capabilities.
 71 | type ListChanged struct {
 72 | 	ListChanged *bool `json:"listChanged,omitempty"`
 73 | }
 74 | 
 75 | // ClientCapabilities represents capabilities a client may support. Known
 76 | // capabilities are defined here, in this schema, but this is not a closed set: any
 77 | // client can define its own, additional capabilities.
 78 | type ClientCapabilities struct {
 79 | 	// Experimental, non-standard capabilities that the client supports.
 80 | 	Experimental map[string]interface{} `json:"experimental,omitempty"`
 81 | 	// Present if the client supports listing roots.
 82 | 	Roots *ListChanged `json:"roots,omitempty"`
 83 | 	// Present if the client supports sampling from an LLM.
 84 | 	Sampling struct{} `json:"sampling,omitempty"`
 85 | }
 86 | 
 87 | // ServerCapabilities represents capabilities that a server may support. Known
 88 | // capabilities are defined here, in this schema, but this is not a closed set: any
 89 | // server can define its own, additional capabilities.
 90 | type ServerCapabilities struct {
 91 | 	Tools *ListChanged `json:"tools,omitempty"`
 92 | }
 93 | 
 94 | // Base interface for metadata with name (identifier) and title (display name) properties.
 95 | type BaseMetadata struct {
 96 | 	// Intended for programmatic or logical use, but used as a display name in past specs
 97 | 	// or fallback (if title isn't present).
 98 | 	Name string `json:"name"`
 99 | 	// Intended for UI and end-user contexts — optimized to be human-readable and easily understood,
100 | 	//even by those unfamiliar with domain-specific terminology.
101 | 	//
102 | 	// If not provided, the name should be used for display (except for Tool,
103 | 	// where `annotations.title` should be given precedence over using `name`,
104 | 	// if present).
105 | 	Title string `json:"title,omitempty"`
106 | }
107 | 
108 | // Implementation describes the name and version of an MCP implementation.
109 | type Implementation struct {
110 | 	BaseMetadata
111 | 	Version string `json:"version"`
112 | }
113 | 
```

--------------------------------------------------------------------------------
/internal/tools/firestore/util/validator.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package util
 16 | 
 17 | import (
 18 | 	"fmt"
 19 | 	"regexp"
 20 | 	"strings"
 21 | )
 22 | 
 23 | // Regular expressions for validating Firestore paths
 24 | var (
 25 | 	// Pattern to detect absolute paths (those starting with "projects/")
 26 | 	absolutePathRegex = regexp.MustCompile(`^projects/[^/]+/databases/[^/]+/documents/`)
 27 | )
 28 | 
 29 | // PathType represents the type of Firestore path
 30 | type PathType int
 31 | 
 32 | const (
 33 | 	CollectionPath PathType = iota
 34 | 	DocumentPath
 35 | )
 36 | 
 37 | // ValidateCollectionPath validates that a path is a valid Firestore collection path.
 38 | // Collection paths must have an odd number of segments (collection/doc/collection)
 39 | func ValidateCollectionPath(path string) error {
 40 | 	return validatePath(path, CollectionPath)
 41 | }
 42 | 
 43 | // ValidateDocumentPath validates that a path is a valid Firestore document path.
 44 | // Document paths must have an even number of segments (collection/doc or collection/doc/collection/doc)
 45 | func ValidateDocumentPath(path string) error {
 46 | 	return validatePath(path, DocumentPath)
 47 | }
 48 | 
 49 | // validatePath is the common validation function for both collection and document paths
 50 | func validatePath(path string, pathType PathType) error {
 51 | 	pathTypeName := "document"
 52 | 	if pathType == CollectionPath {
 53 | 		pathTypeName = "collection"
 54 | 	}
 55 | 
 56 | 	// Check for empty path
 57 | 	if path == "" {
 58 | 		return fmt.Errorf("%s path cannot be empty", pathTypeName)
 59 | 	}
 60 | 
 61 | 	// Check if it's an absolute path
 62 | 	if absolutePathRegex.MatchString(path) {
 63 | 		example := "users/userId"
 64 | 		if pathType == CollectionPath {
 65 | 			example = "users"
 66 | 		}
 67 | 		return fmt.Errorf("path must be relative (e.g., '%s'), not absolute (matching pattern: ^projects/[^/]+/databases/[^/]+/documents/)", example)
 68 | 	}
 69 | 
 70 | 	// Split the path using strings.Split to preserve empty segments
 71 | 	segments := strings.Split(path, "/")
 72 | 
 73 | 	// Check for empty result
 74 | 	if len(segments) == 0 {
 75 | 		return fmt.Errorf("%s path cannot be empty or contain only slashes", pathTypeName)
 76 | 	}
 77 | 
 78 | 	// Check segment count based on path type
 79 | 	segmentCount := len(segments)
 80 | 	if pathType == CollectionPath && segmentCount%2 == 0 {
 81 | 		// Collection paths must have an odd number of segments
 82 | 		return fmt.Errorf("invalid collection path: must have an odd number of segments (e.g., 'collection' or 'collection/doc/subcollection'), got %d segments", segmentCount)
 83 | 	} else if pathType == DocumentPath && segmentCount%2 != 0 {
 84 | 		// Document paths must have an even number of segments
 85 | 		return fmt.Errorf("invalid document path: must have an even number of segments (e.g., 'collection/doc'), got %d segments", segmentCount)
 86 | 	}
 87 | 
 88 | 	// Validate each segment
 89 | 	for i, segment := range segments {
 90 | 		isCollectionSegment := (i % 2) == 0
 91 | 		if err := validateSegment(segment, isCollectionSegment); err != nil {
 92 | 			return fmt.Errorf("invalid segment at position %d (%s): %w", i+1, segment, err)
 93 | 		}
 94 | 	}
 95 | 
 96 | 	return nil
 97 | }
 98 | 
 99 | // validateSegment validates a single path segment
100 | func validateSegment(segment string, isCollection bool) error {
101 | 	segmentType := "document ID"
102 | 	if isCollection {
103 | 		segmentType = "collection ID"
104 | 	}
105 | 
106 | 	// Check for empty segment
107 | 	if segment == "" {
108 | 		return fmt.Errorf("segment cannot be empty")
109 | 	}
110 | 
111 | 	// Check for whitespace-only segment
112 | 	if strings.TrimSpace(segment) == "" {
113 | 		return fmt.Errorf("segment cannot be only whitespace")
114 | 	}
115 | 
116 | 	// Check for single or double period
117 | 	if segment == "." || segment == ".." {
118 | 		return fmt.Errorf("segment cannot be '.' or '..'")
119 | 	}
120 | 
121 | 	// Check for reserved prefix
122 | 	if strings.HasPrefix(segment, "__") {
123 | 		return fmt.Errorf("%s cannot start with '__' (reserved prefix)", segmentType)
124 | 	}
125 | 
126 | 	return nil
127 | }
128 | 
129 | // IsAbsolutePath checks if a path is an absolute Firestore path
130 | func IsAbsolutePath(path string) bool {
131 | 	return absolutePathRegex.MatchString(path)
132 | }
133 | 
134 | // IsRelativePath checks if a path is a relative Firestore path
135 | func IsRelativePath(path string) bool {
136 | 	return path != "" && !IsAbsolutePath(path)
137 | }
138 | 
```

--------------------------------------------------------------------------------
/internal/tools/bigquery/bigquerysql/bigquerysql_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package bigquerysql_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerysql"
 26 | )
 27 | 
 28 | func TestParseFromYamlBigQuery(t *testing.T) {
 29 | 	ctx, err := testutils.ContextWithNewLogger()
 30 | 	if err != nil {
 31 | 		t.Fatalf("unexpected error: %s", err)
 32 | 	}
 33 | 	tcs := []struct {
 34 | 		desc string
 35 | 		in   string
 36 | 		want server.ToolConfigs
 37 | 	}{
 38 | 		{
 39 | 			desc: "basic example",
 40 | 			in: `
 41 | 			tools:
 42 | 				example_tool:
 43 | 					kind: bigquery-sql
 44 | 					source: my-instance
 45 | 					description: some description
 46 | 					statement: |
 47 | 						SELECT * FROM SQL_STATEMENT;
 48 | 					parameters:
 49 | 						- name: country
 50 | 						  type: string
 51 | 						  description: some description
 52 | 			`,
 53 | 			want: server.ToolConfigs{
 54 | 				"example_tool": bigquerysql.Config{
 55 | 					Name:         "example_tool",
 56 | 					Kind:         "bigquery-sql",
 57 | 					Source:       "my-instance",
 58 | 					Description:  "some description",
 59 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 60 | 					AuthRequired: []string{},
 61 | 					Parameters: []tools.Parameter{
 62 | 						tools.NewStringParameter("country", "some description"),
 63 | 					},
 64 | 				},
 65 | 			},
 66 | 		},
 67 | 	}
 68 | 	for _, tc := range tcs {
 69 | 		t.Run(tc.desc, func(t *testing.T) {
 70 | 			got := struct {
 71 | 				Tools server.ToolConfigs `yaml:"tools"`
 72 | 			}{}
 73 | 			// Parse contents
 74 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 75 | 			if err != nil {
 76 | 				t.Fatalf("unable to unmarshal: %s", err)
 77 | 			}
 78 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 79 | 				t.Fatalf("incorrect parse: diff %v", diff)
 80 | 			}
 81 | 		})
 82 | 	}
 83 | 
 84 | }
 85 | 
 86 | func TestParseFromYamlWithTemplateBigQuery(t *testing.T) {
 87 | 	ctx, err := testutils.ContextWithNewLogger()
 88 | 	if err != nil {
 89 | 		t.Fatalf("unexpected error: %s", err)
 90 | 	}
 91 | 	tcs := []struct {
 92 | 		desc string
 93 | 		in   string
 94 | 		want server.ToolConfigs
 95 | 	}{
 96 | 		{
 97 | 			desc: "basic example",
 98 | 			in: `
 99 | 			tools:
100 | 				example_tool:
101 | 					kind: bigquery-sql
102 | 					source: my-instance
103 | 					description: some description
104 | 					statement: |
105 | 						SELECT * FROM SQL_STATEMENT;
106 | 					parameters:
107 | 						- name: country
108 | 						  type: string
109 | 						  description: some description
110 | 					templateParameters:
111 | 						- name: tableName
112 | 						  type: string
113 | 						  description: The table to select hotels from.
114 | 						- name: fieldArray
115 | 						  type: array
116 | 						  description: The columns to return for the query.
117 | 						  items: 
118 | 								name: column
119 | 								type: string
120 | 								description: A column name that will be returned from the query.
121 | 			`,
122 | 			want: server.ToolConfigs{
123 | 				"example_tool": bigquerysql.Config{
124 | 					Name:         "example_tool",
125 | 					Kind:         "bigquery-sql",
126 | 					Source:       "my-instance",
127 | 					Description:  "some description",
128 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
129 | 					AuthRequired: []string{},
130 | 					Parameters: []tools.Parameter{
131 | 						tools.NewStringParameter("country", "some description"),
132 | 					},
133 | 					TemplateParameters: []tools.Parameter{
134 | 						tools.NewStringParameter("tableName", "The table to select hotels from."),
135 | 						tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
136 | 					},
137 | 				},
138 | 			},
139 | 		},
140 | 	}
141 | 	for _, tc := range tcs {
142 | 		t.Run(tc.desc, func(t *testing.T) {
143 | 			got := struct {
144 | 				Tools server.ToolConfigs `yaml:"tools"`
145 | 			}{}
146 | 			// Parse contents
147 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
148 | 			if err != nil {
149 | 				t.Fatalf("unable to unmarshal: %s", err)
150 | 			}
151 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
152 | 				t.Fatalf("incorrect parse: diff %v", diff)
153 | 			}
154 | 		})
155 | 	}
156 | 
157 | }
158 | 
```

--------------------------------------------------------------------------------
/internal/sources/redis/redis.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package redis
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 	"time"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	"github.com/redis/go-redis/v9"
 24 | 	"go.opentelemetry.io/otel/trace"
 25 | )
 26 | 
 27 | const SourceKind string = "redis"
 28 | 
 29 | // validate interface
 30 | var _ sources.SourceConfig = Config{}
 31 | 
 32 | func init() {
 33 | 	if !sources.Register(SourceKind, newConfig) {
 34 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type Config struct {
 47 | 	Name           string   `yaml:"name" validate:"required"`
 48 | 	Kind           string   `yaml:"kind" validate:"required"`
 49 | 	Address        []string `yaml:"address" validate:"required"`
 50 | 	Username       string   `yaml:"username"`
 51 | 	Password       string   `yaml:"password"`
 52 | 	Database       int      `yaml:"database"`
 53 | 	UseGCPIAM      bool     `yaml:"useGCPIAM"`
 54 | 	ClusterEnabled bool     `yaml:"clusterEnabled"`
 55 | }
 56 | 
 57 | func (r Config) SourceConfigKind() string {
 58 | 	return SourceKind
 59 | }
 60 | 
 61 | // RedisClient is an interface for `redis.Client` and `redis.ClusterClient
 62 | type RedisClient interface {
 63 | 	Do(context.Context, ...any) *redis.Cmd
 64 | }
 65 | 
 66 | var _ RedisClient = (*redis.Client)(nil)
 67 | var _ RedisClient = (*redis.ClusterClient)(nil)
 68 | 
 69 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 70 | 	client, err := initRedisClient(ctx, r)
 71 | 	if err != nil {
 72 | 		return nil, fmt.Errorf("error initializing Redis client: %s", err)
 73 | 	}
 74 | 	s := &Source{
 75 | 		Name:   r.Name,
 76 | 		Kind:   SourceKind,
 77 | 		Client: client,
 78 | 	}
 79 | 	return s, nil
 80 | }
 81 | 
 82 | func initRedisClient(ctx context.Context, r Config) (RedisClient, error) {
 83 | 	var authFn func(ctx context.Context) (username string, password string, err error)
 84 | 	if r.UseGCPIAM {
 85 | 		// Pass in an access token getter fn for IAM auth
 86 | 		authFn = func(ctx context.Context) (username string, password string, err error) {
 87 | 			token, err := sources.GetIAMAccessToken(ctx)
 88 | 			if err != nil {
 89 | 				return "", "", err
 90 | 			}
 91 | 			return "default", token, nil
 92 | 		}
 93 | 	}
 94 | 
 95 | 	var client RedisClient
 96 | 	var err error
 97 | 	if r.ClusterEnabled {
 98 | 		// Create a new Redis Cluster client
 99 | 		clusterClient := redis.NewClusterClient(&redis.ClusterOptions{
100 | 			Addrs: r.Address,
101 | 			// PoolSize applies per cluster node and not for the whole cluster.
102 | 			PoolSize:                   10,
103 | 			ConnMaxIdleTime:            60 * time.Second,
104 | 			MinIdleConns:               1,
105 | 			CredentialsProviderContext: authFn,
106 | 			Username:                   r.Username,
107 | 			Password:                   r.Password,
108 | 		})
109 | 		err = clusterClient.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
110 | 			return shard.Ping(ctx).Err()
111 | 		})
112 | 		if err != nil {
113 | 			return nil, fmt.Errorf("unable to connect to redis cluster: %s", err)
114 | 		}
115 | 		client = clusterClient
116 | 		return client, nil
117 | 	}
118 | 
119 | 	// Create a new Redis client
120 | 	standaloneClient := redis.NewClient(&redis.Options{
121 | 		Addr:                       r.Address[0],
122 | 		PoolSize:                   10,
123 | 		ConnMaxIdleTime:            60 * time.Second,
124 | 		MinIdleConns:               1,
125 | 		DB:                         r.Database,
126 | 		CredentialsProviderContext: authFn,
127 | 		Username:                   r.Username,
128 | 		Password:                   r.Password,
129 | 	})
130 | 	_, err = standaloneClient.Ping(ctx).Result()
131 | 	if err != nil {
132 | 		return nil, fmt.Errorf("unable to connect to redis: %s", err)
133 | 	}
134 | 	client = standaloneClient
135 | 	return client, nil
136 | }
137 | 
138 | var _ sources.Source = &Source{}
139 | 
140 | type Source struct {
141 | 	Name   string `yaml:"name"`
142 | 	Kind   string `yaml:"kind"`
143 | 	Client RedisClient
144 | }
145 | 
146 | func (s *Source) SourceKind() string {
147 | 	return SourceKind
148 | }
149 | 
150 | func (s *Source) RedisClient() RedisClient {
151 | 	return s.Client
152 | }
153 | 
```

--------------------------------------------------------------------------------
/internal/sources/cloudsqlmssql/cloud_sql_mssql.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package cloudsqlmssql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 	"net/url"
 22 | 	"slices"
 23 | 
 24 | 	"cloud.google.com/go/cloudsqlconn/sqlserver/mssql"
 25 | 	"github.com/goccy/go-yaml"
 26 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 27 | 	"github.com/googleapis/genai-toolbox/internal/util"
 28 | 	"go.opentelemetry.io/otel/trace"
 29 | )
 30 | 
 31 | const SourceKind string = "cloud-sql-mssql"
 32 | 
 33 | // validate interface
 34 | var _ sources.SourceConfig = Config{}
 35 | 
 36 | func init() {
 37 | 	if !sources.Register(SourceKind, newConfig) {
 38 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 39 | 	}
 40 | }
 41 | 
 42 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 43 | 	actual := Config{Name: name, IPType: "public"} // Default IPType
 44 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 45 | 		return nil, err
 46 | 	}
 47 | 	return actual, nil
 48 | }
 49 | 
 50 | type Config struct {
 51 | 	// Cloud SQL MSSQL configs
 52 | 	Name      string         `yaml:"name" validate:"required"`
 53 | 	Kind      string         `yaml:"kind" validate:"required"`
 54 | 	Project   string         `yaml:"project" validate:"required"`
 55 | 	Region    string         `yaml:"region" validate:"required"`
 56 | 	Instance  string         `yaml:"instance" validate:"required"`
 57 | 	IPAddress string         `yaml:"ipAddress" validate:"required"`
 58 | 	IPType    sources.IPType `yaml:"ipType" validate:"required"`
 59 | 	User      string         `yaml:"user" validate:"required"`
 60 | 	Password  string         `yaml:"password" validate:"required"`
 61 | 	Database  string         `yaml:"database" validate:"required"`
 62 | }
 63 | 
 64 | func (r Config) SourceConfigKind() string {
 65 | 	// Returns Cloud SQL MSSQL source kind
 66 | 	return SourceKind
 67 | }
 68 | 
 69 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 70 | 	// Initializes a Cloud SQL MSSQL source
 71 | 	db, err := initCloudSQLMssqlConnection(ctx, tracer, r.Name, r.Project, r.Region, r.Instance, r.IPAddress, r.IPType.String(), r.User, r.Password, r.Database)
 72 | 	if err != nil {
 73 | 		return nil, fmt.Errorf("unable to create db connection: %w", err)
 74 | 	}
 75 | 
 76 | 	// Verify db connection
 77 | 	err = db.PingContext(ctx)
 78 | 	if err != nil {
 79 | 		return nil, fmt.Errorf("unable to connect successfully: %w", err)
 80 | 	}
 81 | 
 82 | 	s := &Source{
 83 | 		Name: r.Name,
 84 | 		Kind: SourceKind,
 85 | 		Db:   db,
 86 | 	}
 87 | 	return s, nil
 88 | }
 89 | 
 90 | var _ sources.Source = &Source{}
 91 | 
 92 | type Source struct {
 93 | 	// Cloud SQL MSSQL struct with connection pool
 94 | 	Name string `yaml:"name"`
 95 | 	Kind string `yaml:"kind"`
 96 | 	Db   *sql.DB
 97 | }
 98 | 
 99 | func (s *Source) SourceKind() string {
100 | 	// Returns Cloud SQL MSSQL source kind
101 | 	return SourceKind
102 | }
103 | 
104 | func (s *Source) MSSQLDB() *sql.DB {
105 | 	// Returns a Cloud SQL MSSQL database connection pool
106 | 	return s.Db
107 | }
108 | 
109 | func initCloudSQLMssqlConnection(ctx context.Context, tracer trace.Tracer, name, project, region, instance, ipAddress, ipType, user, pass, dbname string) (*sql.DB, error) {
110 | 	//nolint:all // Reassigned ctx
111 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
112 | 	defer span.End()
113 | 
114 | 	userAgent, err := util.UserAgentFromContext(ctx)
115 | 	if err != nil {
116 | 		return nil, err
117 | 	}
118 | 
119 | 	// Create dsn
120 | 	query := url.Values{}
121 | 	query.Add("app name", userAgent)
122 | 	query.Add("database", dbname)
123 | 	query.Add("cloudsql", fmt.Sprintf("%s:%s:%s", project, region, instance))
124 | 
125 | 	url := &url.URL{
126 | 		Scheme:   "sqlserver",
127 | 		User:     url.UserPassword(user, pass),
128 | 		Host:     ipAddress,
129 | 		RawQuery: query.Encode(),
130 | 	}
131 | 
132 | 	// Get dial options
133 | 	opts, err := sources.GetCloudSQLOpts(ipType, userAgent, false)
134 | 	if err != nil {
135 | 		return nil, err
136 | 	}
137 | 
138 | 	// Register sql server driver
139 | 	if !slices.Contains(sql.Drivers(), "cloudsql-sqlserver-driver") {
140 | 		_, err := mssql.RegisterDriver("cloudsql-sqlserver-driver", opts...)
141 | 		if err != nil {
142 | 			return nil, err
143 | 		}
144 | 	}
145 | 
146 | 	// Open database connection
147 | 	db, err := sql.Open(
148 | 		"cloudsql-sqlserver-driver",
149 | 		url.String(),
150 | 	)
151 | 	if err != nil {
152 | 		return nil, err
153 | 	}
154 | 	return db, nil
155 | }
156 | 
```

--------------------------------------------------------------------------------
/internal/tools/clickhouse/clickhouselistdatabases/clickhouselistdatabases.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package clickhouse
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | )
 26 | 
 27 | type compatibleSource interface {
 28 | 	ClickHousePool() *sql.DB
 29 | }
 30 | 
 31 | var compatibleSources = []string{"clickhouse"}
 32 | 
 33 | const listDatabasesKind string = "clickhouse-list-databases"
 34 | 
 35 | func init() {
 36 | 	if !tools.Register(listDatabasesKind, newListDatabasesConfig) {
 37 | 		panic(fmt.Sprintf("tool kind %q already registered", listDatabasesKind))
 38 | 	}
 39 | }
 40 | 
 41 | func newListDatabasesConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 42 | 	actual := Config{Name: name}
 43 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 44 | 		return nil, err
 45 | 	}
 46 | 	return actual, nil
 47 | }
 48 | 
 49 | type Config struct {
 50 | 	Name         string           `yaml:"name" validate:"required"`
 51 | 	Kind         string           `yaml:"kind" validate:"required"`
 52 | 	Source       string           `yaml:"source" validate:"required"`
 53 | 	Description  string           `yaml:"description" validate:"required"`
 54 | 	AuthRequired []string         `yaml:"authRequired"`
 55 | 	Parameters   tools.Parameters `yaml:"parameters"`
 56 | }
 57 | 
 58 | var _ tools.ToolConfig = Config{}
 59 | 
 60 | func (cfg Config) ToolConfigKind() string {
 61 | 	return listDatabasesKind
 62 | }
 63 | 
 64 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 65 | 	rawS, ok := srcs[cfg.Source]
 66 | 	if !ok {
 67 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 68 | 	}
 69 | 
 70 | 	s, ok := rawS.(compatibleSource)
 71 | 	if !ok {
 72 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", listDatabasesKind, compatibleSources)
 73 | 	}
 74 | 
 75 | 	allParameters, paramManifest, _ := tools.ProcessParameters(nil, cfg.Parameters)
 76 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 77 | 
 78 | 	t := Tool{
 79 | 		Name:         cfg.Name,
 80 | 		Kind:         listDatabasesKind,
 81 | 		Parameters:   cfg.Parameters,
 82 | 		AllParams:    allParameters,
 83 | 		AuthRequired: cfg.AuthRequired,
 84 | 		Pool:         s.ClickHousePool(),
 85 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
 86 | 		mcpManifest:  mcpManifest,
 87 | 	}
 88 | 	return t, nil
 89 | }
 90 | 
 91 | var _ tools.Tool = Tool{}
 92 | 
 93 | type Tool struct {
 94 | 	Name         string           `yaml:"name"`
 95 | 	Kind         string           `yaml:"kind"`
 96 | 	AuthRequired []string         `yaml:"authRequired"`
 97 | 	Parameters   tools.Parameters `yaml:"parameters"`
 98 | 	AllParams    tools.Parameters `yaml:"allParams"`
 99 | 
100 | 	Pool        *sql.DB
101 | 	manifest    tools.Manifest
102 | 	mcpManifest tools.McpManifest
103 | }
104 | 
105 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, token tools.AccessToken) (any, error) {
106 | 	// Query to list all databases
107 | 	query := "SHOW DATABASES"
108 | 
109 | 	results, err := t.Pool.QueryContext(ctx, query)
110 | 	if err != nil {
111 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
112 | 	}
113 | 	defer results.Close()
114 | 
115 | 	var databases []map[string]any
116 | 	for results.Next() {
117 | 		var dbName string
118 | 		err := results.Scan(&dbName)
119 | 		if err != nil {
120 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
121 | 		}
122 | 		databases = append(databases, map[string]any{
123 | 			"name": dbName,
124 | 		})
125 | 	}
126 | 
127 | 	if err := results.Err(); err != nil {
128 | 		return nil, fmt.Errorf("errors encountered by results.Scan: %w", err)
129 | 	}
130 | 
131 | 	return databases, nil
132 | }
133 | 
134 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
135 | 	return tools.ParseParams(t.AllParams, data, claims)
136 | }
137 | 
138 | func (t Tool) Manifest() tools.Manifest {
139 | 	return t.manifest
140 | }
141 | 
142 | func (t Tool) McpManifest() tools.McpManifest {
143 | 	return t.mcpManifest
144 | }
145 | 
146 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
147 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
148 | }
149 | 
150 | func (t Tool) RequiresClientAuthorization() bool {
151 | 	return false
152 | }
153 | 
```

--------------------------------------------------------------------------------
/internal/tools/firestore/firestoreadddocuments/firestoreadddocuments_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package firestoreadddocuments_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools/firestore/firestoreadddocuments"
 25 | )
 26 | 
 27 | func TestParseFromYamlFirestoreAddDocuments(t *testing.T) {
 28 | 	ctx, err := testutils.ContextWithNewLogger()
 29 | 	if err != nil {
 30 | 		t.Fatalf("unexpected error: %s", err)
 31 | 	}
 32 | 	tcs := []struct {
 33 | 		desc string
 34 | 		in   string
 35 | 		want server.ToolConfigs
 36 | 	}{
 37 | 		{
 38 | 			desc: "basic example",
 39 | 			in: `
 40 | 			tools:
 41 | 				add_docs_tool:
 42 | 					kind: firestore-add-documents
 43 | 					source: my-firestore-instance
 44 | 					description: Add documents to Firestore collections
 45 | 			`,
 46 | 			want: server.ToolConfigs{
 47 | 				"add_docs_tool": firestoreadddocuments.Config{
 48 | 					Name:         "add_docs_tool",
 49 | 					Kind:         "firestore-add-documents",
 50 | 					Source:       "my-firestore-instance",
 51 | 					Description:  "Add documents to Firestore collections",
 52 | 					AuthRequired: []string{},
 53 | 				},
 54 | 			},
 55 | 		},
 56 | 		{
 57 | 			desc: "with auth requirements",
 58 | 			in: `
 59 | 			tools:
 60 | 				secure_add_docs:
 61 | 					kind: firestore-add-documents
 62 | 					source: prod-firestore
 63 | 					description: Add documents with authentication
 64 | 					authRequired:
 65 | 						- google-auth-service
 66 | 						- api-key-service
 67 | 			`,
 68 | 			want: server.ToolConfigs{
 69 | 				"secure_add_docs": firestoreadddocuments.Config{
 70 | 					Name:         "secure_add_docs",
 71 | 					Kind:         "firestore-add-documents",
 72 | 					Source:       "prod-firestore",
 73 | 					Description:  "Add documents with authentication",
 74 | 					AuthRequired: []string{"google-auth-service", "api-key-service"},
 75 | 				},
 76 | 			},
 77 | 		},
 78 | 	}
 79 | 	for _, tc := range tcs {
 80 | 		t.Run(tc.desc, func(t *testing.T) {
 81 | 			got := struct {
 82 | 				Tools server.ToolConfigs `yaml:"tools"`
 83 | 			}{}
 84 | 			// Parse contents
 85 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 86 | 			if err != nil {
 87 | 				t.Fatalf("unable to unmarshal: %s", err)
 88 | 			}
 89 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 90 | 				t.Fatalf("incorrect parse: diff %v", diff)
 91 | 			}
 92 | 		})
 93 | 	}
 94 | }
 95 | 
 96 | func TestParseFromYamlMultipleTools(t *testing.T) {
 97 | 	ctx, err := testutils.ContextWithNewLogger()
 98 | 	if err != nil {
 99 | 		t.Fatalf("unexpected error: %s", err)
100 | 	}
101 | 	in := `
102 | 	tools:
103 | 		add_user_docs:
104 | 			kind: firestore-add-documents
105 | 			source: users-firestore
106 | 			description: Add user documents
107 | 			authRequired:
108 | 				- user-auth
109 | 		add_product_docs:
110 | 			kind: firestore-add-documents
111 | 			source: products-firestore
112 | 			description: Add product documents
113 | 		add_order_docs:
114 | 			kind: firestore-add-documents
115 | 			source: orders-firestore
116 | 			description: Add order documents
117 | 			authRequired:
118 | 				- user-auth
119 | 				- admin-auth
120 | 	`
121 | 	want := server.ToolConfigs{
122 | 		"add_user_docs": firestoreadddocuments.Config{
123 | 			Name:         "add_user_docs",
124 | 			Kind:         "firestore-add-documents",
125 | 			Source:       "users-firestore",
126 | 			Description:  "Add user documents",
127 | 			AuthRequired: []string{"user-auth"},
128 | 		},
129 | 		"add_product_docs": firestoreadddocuments.Config{
130 | 			Name:         "add_product_docs",
131 | 			Kind:         "firestore-add-documents",
132 | 			Source:       "products-firestore",
133 | 			Description:  "Add product documents",
134 | 			AuthRequired: []string{},
135 | 		},
136 | 		"add_order_docs": firestoreadddocuments.Config{
137 | 			Name:         "add_order_docs",
138 | 			Kind:         "firestore-add-documents",
139 | 			Source:       "orders-firestore",
140 | 			Description:  "Add order documents",
141 | 			AuthRequired: []string{"user-auth", "admin-auth"},
142 | 		},
143 | 	}
144 | 
145 | 	got := struct {
146 | 		Tools server.ToolConfigs `yaml:"tools"`
147 | 	}{}
148 | 	// Parse contents
149 | 	err = yaml.UnmarshalContext(ctx, testutils.FormatYaml(in), &got)
150 | 	if err != nil {
151 | 		t.Fatalf("unable to unmarshal: %s", err)
152 | 	}
153 | 	if diff := cmp.Diff(want, got.Tools); diff != "" {
154 | 		t.Fatalf("incorrect parse: diff %v", diff)
155 | 	}
156 | }
157 | 
```

--------------------------------------------------------------------------------
/internal/server/mcp/v20241105/types.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package v20241105
 16 | 
 17 | import (
 18 | 	"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
 19 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 20 | )
 21 | 
 22 | // SERVER_NAME is the server name used in Implementation.
 23 | const SERVER_NAME = "Toolbox"
 24 | 
 25 | // PROTOCOL_VERSION is the version of the MCP protocol in this package.
 26 | const PROTOCOL_VERSION = "2024-11-05"
 27 | 
 28 | // methods that are supported.
 29 | const (
 30 | 	PING       = "ping"
 31 | 	TOOLS_LIST = "tools/list"
 32 | 	TOOLS_CALL = "tools/call"
 33 | )
 34 | 
 35 | /* Empty result */
 36 | 
 37 | // EmptyResult represents a response that indicates success but carries no data.
 38 | type EmptyResult jsonrpc.Result
 39 | 
 40 | /* Pagination */
 41 | 
 42 | // Cursor is an opaque token used to represent a cursor for pagination.
 43 | type Cursor string
 44 | 
 45 | type PaginatedRequest struct {
 46 | 	jsonrpc.Request
 47 | 	Params struct {
 48 | 		// An opaque token representing the current pagination position.
 49 | 		// If provided, the server should return results starting after this cursor.
 50 | 		Cursor Cursor `json:"cursor,omitempty"`
 51 | 	} `json:"params,omitempty"`
 52 | }
 53 | 
 54 | type PaginatedResult struct {
 55 | 	jsonrpc.Result
 56 | 	// An opaque token representing the pagination position after the last returned result.
 57 | 	// If present, there may be more results available.
 58 | 	NextCursor Cursor `json:"nextCursor,omitempty"`
 59 | }
 60 | 
 61 | /* Tools */
 62 | 
 63 | // Sent from the client to request a list of tools the server has.
 64 | type ListToolsRequest struct {
 65 | 	PaginatedRequest
 66 | }
 67 | 
 68 | // The server's response to a tools/list request from the client.
 69 | type ListToolsResult struct {
 70 | 	PaginatedResult
 71 | 	Tools []tools.McpManifest `json:"tools"`
 72 | }
 73 | 
 74 | // Used by the client to invoke a tool provided by the server.
 75 | type CallToolRequest struct {
 76 | 	jsonrpc.Request
 77 | 	Params struct {
 78 | 		Name      string         `json:"name"`
 79 | 		Arguments map[string]any `json:"arguments,omitempty"`
 80 | 	} `json:"params,omitempty"`
 81 | }
 82 | 
 83 | // The sender or recipient of messages and data in a conversation.
 84 | type Role string
 85 | 
 86 | const (
 87 | 	RoleUser      Role = "user"
 88 | 	RoleAssistant Role = "assistant"
 89 | )
 90 | 
 91 | // Base for objects that include optional annotations for the client.
 92 | // The client can use annotations to inform how objects are used or displayed
 93 | type Annotated struct {
 94 | 	Annotations *struct {
 95 | 		// Describes who the intended customer of this object or data is.
 96 | 		// It can include multiple entries to indicate content useful for multiple
 97 | 		// audiences (e.g., `["user", "assistant"]`).
 98 | 		Audience []Role `json:"audience,omitempty"`
 99 | 		// Describes how important this data is for operating the server.
100 | 		//
101 | 		// A value of 1 means "most important," and indicates that the data is
102 | 		// effectively required, while 0 means "least important," and indicates that
103 | 		// the data is entirely optional.
104 | 		//
105 | 		// @TJS-type number
106 | 		// @minimum 0
107 | 		// @maximum 1
108 | 		Priority float64 `json:"priority,omitempty"`
109 | 	} `json:"annotations,omitempty"`
110 | }
111 | 
112 | // TextContent represents text provided to or from an LLM.
113 | type TextContent struct {
114 | 	Annotated
115 | 	Type string `json:"type"`
116 | 	// The text content of the message.
117 | 	Text string `json:"text"`
118 | }
119 | 
120 | // The server's response to a tool call.
121 | //
122 | // Any errors that originate from the tool SHOULD be reported inside the result
123 | // object, with `isError` set to true, _not_ as an MCP protocol-level error
124 | // response. Otherwise, the LLM would not be able to see that an error occurred
125 | // and self-correct.
126 | //
127 | // However, any errors in _finding_ the tool, an error indicating that the
128 | // server does not support tool calls, or any other exceptional conditions,
129 | // should be reported as an MCP error response.
130 | type CallToolResult struct {
131 | 	jsonrpc.Result
132 | 	// Could be either a TextContent, ImageContent, or EmbeddedResources
133 | 	// For Toolbox, we will only be sending TextContent
134 | 	Content []TextContent `json:"content"`
135 | 	// Whether the tool call ended in an error.
136 | 	// If not set, this is assumed to be false (the call was successful).
137 | 	IsError bool `json:"isError,omitempty"`
138 | }
139 | 
```
Page 10/45FirstPrevNextLast