#
tokens: 49745/50000 29/786 files (page 8/45)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 8 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

--------------------------------------------------------------------------------
/internal/tools/dataplex/dataplexlookupentry/dataplexlookupentry_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 dataplexlookupentry_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/dataplex/dataplexlookupentry"
 26 | )
 27 | 
 28 | func TestParseFromYamlDataplexLookupEntry(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: dataplex-lookup-entry
 44 | 					source: my-instance
 45 | 					description: some description
 46 | 			`,
 47 | 			want: server.ToolConfigs{
 48 | 				"example_tool": dataplexlookupentry.Config{
 49 | 					Name:         "example_tool",
 50 | 					Kind:         "dataplex-lookup-entry",
 51 | 					Source:       "my-instance",
 52 | 					Description:  "some description",
 53 | 					AuthRequired: []string{},
 54 | 				},
 55 | 			},
 56 | 		},
 57 | 		{
 58 | 			desc: "advanced example",
 59 | 			in: `
 60 | 			tools:
 61 | 				example_tool:
 62 | 					kind: dataplex-lookup-entry
 63 | 					source: my-instance
 64 | 					description: some description
 65 | 					parameters:
 66 | 						- name: name
 67 | 							type: string
 68 | 							description: some name description
 69 | 						- name: view
 70 | 							type: string
 71 | 							description: some view description
 72 | 						- name: aspectTypes
 73 | 							type: array
 74 | 							description: some aspect types description
 75 | 							default: []
 76 | 							items: 
 77 | 								name: aspectType
 78 | 								type: string
 79 | 								description: some aspect type description
 80 | 						- name: entry
 81 | 							type: string
 82 | 							description: some entry description
 83 | 			`,
 84 | 			want: server.ToolConfigs{
 85 | 				"example_tool": dataplexlookupentry.Config{
 86 | 					Name:         "example_tool",
 87 | 					Kind:         "dataplex-lookup-entry",
 88 | 					Source:       "my-instance",
 89 | 					Description:  "some description",
 90 | 					AuthRequired: []string{},
 91 | 					Parameters: []tools.Parameter{
 92 | 						tools.NewStringParameter("name", "some name description"),
 93 | 						tools.NewStringParameter("view", "some view description"),
 94 | 						tools.NewArrayParameterWithDefault("aspectTypes", []any{}, "some aspect types description", tools.NewStringParameter("aspectType", "some aspect type description")),
 95 | 						tools.NewStringParameter("entry", "some entry description"),
 96 | 					},
 97 | 				},
 98 | 			},
 99 | 		},
100 | 	}
101 | 	for _, tc := range tcs {
102 | 		t.Run(tc.desc, func(t *testing.T) {
103 | 			got := struct {
104 | 				Tools server.ToolConfigs `yaml:"tools"`
105 | 			}{}
106 | 			// Parse contents
107 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
108 | 			if err != nil {
109 | 				t.Fatalf("unable to unmarshal: %s", err)
110 | 			}
111 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
112 | 				t.Fatalf("incorrect parse: diff %v", diff)
113 | 			}
114 | 		})
115 | 	}
116 | 
117 | }
118 | 
```

--------------------------------------------------------------------------------
/internal/sources/cloudmonitoring/cloud_monitoring_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 cloudmonitoring_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"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/cloudmonitoring"
 25 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 26 | )
 27 | 
 28 | func TestParseFromYamlCloudMonitoring(t *testing.T) {
 29 | 	t.Parallel()
 30 | 	tcs := []struct {
 31 | 		desc string
 32 | 		in   string
 33 | 		want server.SourceConfigs
 34 | 	}{
 35 | 		{
 36 | 			desc: "basic example",
 37 | 			in: `
 38 | 			sources:
 39 | 				my-cloud-monitoring-instance:
 40 | 					kind: cloud-monitoring
 41 | 			`,
 42 | 			want: map[string]sources.SourceConfig{
 43 | 				"my-cloud-monitoring-instance": cloudmonitoring.Config{
 44 | 					Name:           "my-cloud-monitoring-instance",
 45 | 					Kind:           cloudmonitoring.SourceKind,
 46 | 					UseClientOAuth: false,
 47 | 				},
 48 | 			},
 49 | 		},
 50 | 		{
 51 | 			desc: "use client auth example",
 52 | 			in: `
 53 | 			sources:
 54 | 				my-cloud-monitoring-instance:
 55 | 					kind: cloud-monitoring
 56 | 					useClientOAuth: true
 57 | 			`,
 58 | 			want: map[string]sources.SourceConfig{
 59 | 				"my-cloud-monitoring-instance": cloudmonitoring.Config{
 60 | 					Name:           "my-cloud-monitoring-instance",
 61 | 					Kind:           cloudmonitoring.SourceKind,
 62 | 					UseClientOAuth: true,
 63 | 				},
 64 | 			},
 65 | 		},
 66 | 	}
 67 | 	for _, tc := range tcs {
 68 | 		tc := tc
 69 | 		t.Run(tc.desc, func(t *testing.T) {
 70 | 			t.Parallel()
 71 | 			got := struct {
 72 | 				Sources server.SourceConfigs `yaml:"sources"`
 73 | 			}{}
 74 | 			// Parse contents
 75 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
 76 | 			if err != nil {
 77 | 				t.Fatalf("unable to unmarshal: %s", err)
 78 | 			}
 79 | 			if !cmp.Equal(tc.want, got.Sources) {
 80 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
 81 | 			}
 82 | 		})
 83 | 	}
 84 | }
 85 | 
 86 | func TestFailParseFromYaml(t *testing.T) {
 87 | 	t.Parallel()
 88 | 	tcs := []struct {
 89 | 		desc string
 90 | 		in   string
 91 | 		err  string
 92 | 	}{
 93 | 		{
 94 | 			desc: "extra field",
 95 | 			in: `
 96 | 			sources:
 97 | 				my-cloud-monitoring-instance:
 98 | 					kind: cloud-monitoring
 99 | 					project: test-project
100 | 			`,
101 | 			err: `unable to parse source "my-cloud-monitoring-instance" as "cloud-monitoring": [2:1] unknown field "project"
102 |    1 | kind: cloud-monitoring
103 | >  2 | project: test-project
104 |        ^
105 | `,
106 | 		},
107 | 		{
108 | 			desc: "missing required field",
109 | 			in: `
110 | 			sources:
111 | 				my-cloud-monitoring-instance:
112 | 					useClientOAuth: true
113 | 			`,
114 | 			err: "missing 'kind' field for source \"my-cloud-monitoring-instance\"",
115 | 		},
116 | 	}
117 | 	for _, tc := range tcs {
118 | 		tc := tc
119 | 		t.Run(tc.desc, func(t *testing.T) {
120 | 			t.Parallel()
121 | 			got := struct {
122 | 				Sources server.SourceConfigs `yaml:"sources"`
123 | 			}{}
124 | 			// Parse contents
125 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
126 | 			if err == nil {
127 | 				t.Fatalf("expect parsing to fail")
128 | 			}
129 | 			errStr := err.Error()
130 | 			if errStr != tc.err {
131 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
132 | 			}
133 | 		})
134 | 	}
135 | }
136 | 
```

--------------------------------------------------------------------------------
/internal/sources/dgraph/dgraph_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 dgraph_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/dgraph"
 24 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 25 | )
 26 | 
 27 | func TestParseFromYamlDgraph(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-dgraph-instance:
 38 | 					kind: dgraph
 39 | 					dgraphUrl: https://localhost:8080
 40 | 					apiKey: abc123
 41 | 					password: pass@123
 42 | 					namespace: 0
 43 | 					user: user123
 44 | 			`,
 45 | 			want: server.SourceConfigs{
 46 | 				"my-dgraph-instance": dgraph.Config{
 47 | 					Name:      "my-dgraph-instance",
 48 | 					Kind:      dgraph.SourceKind,
 49 | 					DgraphUrl: "https://localhost:8080",
 50 | 					ApiKey:    "abc123",
 51 | 					Password:  "pass@123",
 52 | 					Namespace: 0,
 53 | 					User:      "user123",
 54 | 				},
 55 | 			},
 56 | 		},
 57 | 		{
 58 | 			desc: "basic example minimal field",
 59 | 			in: `
 60 | 			sources:
 61 | 				my-dgraph-instance:
 62 | 					kind: dgraph
 63 | 					dgraphUrl: https://localhost:8080
 64 | 			`,
 65 | 			want: server.SourceConfigs{
 66 | 				"my-dgraph-instance": dgraph.Config{
 67 | 					Name:      "my-dgraph-instance",
 68 | 					Kind:      dgraph.SourceKind,
 69 | 					DgraphUrl: "https://localhost:8080",
 70 | 				},
 71 | 			},
 72 | 		},
 73 | 	}
 74 | 
 75 | 	for _, tc := range tcs {
 76 | 		t.Run(tc.desc, func(t *testing.T) {
 77 | 			got := struct {
 78 | 				Sources server.SourceConfigs `yaml:"sources"`
 79 | 			}{}
 80 | 			// Parse contents
 81 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
 82 | 			if err != nil {
 83 | 				t.Fatalf("unable to unmarshal: %s", err)
 84 | 			}
 85 | 
 86 | 			if diff := cmp.Diff(tc.want, got.Sources); diff != "" {
 87 | 				t.Fatalf("incorrect parse: diff %v", diff)
 88 | 			}
 89 | 		})
 90 | 	}
 91 | 
 92 | }
 93 | 
 94 | func TestFailParseFromYaml(t *testing.T) {
 95 | 	tcs := []struct {
 96 | 		desc string
 97 | 		in   string
 98 | 		err  string
 99 | 	}{
100 | 		{
101 | 			desc: "extra field",
102 | 			in: `
103 | 			sources:
104 | 				my-dgraph-instance:
105 | 					kind: dgraph
106 | 					dgraphUrl: https://localhost:8080
107 | 					foo: bar
108 | 			`,
109 | 			err: "unable to parse source \"my-dgraph-instance\" as \"dgraph\": [2:1] unknown field \"foo\"\n   1 | dgraphUrl: https://localhost:8080\n>  2 | foo: bar\n       ^\n   3 | kind: dgraph",
110 | 		},
111 | 		{
112 | 			desc: "missing required field",
113 | 			in: `
114 | 			sources:
115 | 				my-dgraph-instance:
116 | 					kind: dgraph
117 | 			`,
118 | 			err: "unable to parse source \"my-dgraph-instance\" as \"dgraph\": Key: 'Config.DgraphUrl' Error:Field validation for 'DgraphUrl' failed on the 'required' tag",
119 | 		},
120 | 	}
121 | 	for _, tc := range tcs {
122 | 		t.Run(tc.desc, func(t *testing.T) {
123 | 			got := struct {
124 | 				Sources server.SourceConfigs `yaml:"sources"`
125 | 			}{}
126 | 			// Parse contents
127 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
128 | 			if err == nil {
129 | 				t.Fatalf("expect parsing to fail")
130 | 			}
131 | 			errStr := err.Error()
132 | 			if errStr != tc.err {
133 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
134 | 			}
135 | 		})
136 | 	}
137 | }
138 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/spanner.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "Spanner"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   Spanner is a fully managed database service from Google Cloud that combines 
 7 |   relational, key-value, graph, and search capabilities.
 8 | 
 9 | ---
10 | 
11 | # Spanner Source
12 | 
13 | [Spanner][spanner-docs] is a fully managed, mission-critical database service
14 | that brings together relational, graph, key-value, and search. It offers
15 | transactional consistency at global scale, automatic, synchronous replication
16 | for high availability, and support for two SQL dialects: GoogleSQL (ANSI 2011
17 | with extensions) and PostgreSQL.
18 | 
19 | If you are new to Spanner, you can try to [create and query a database using
20 | the Google Cloud console][spanner-quickstart].
21 | 
22 | [spanner-docs]: https://cloud.google.com/spanner/docs
23 | [spanner-quickstart]:
24 |     https://cloud.google.com/spanner/docs/create-query-database-console
25 | 
26 | ## Available Tools
27 | 
28 | - [`spanner-sql`](../tools/spanner/spanner-sql.md)  
29 |   Execute SQL on Google Cloud Spanner.
30 | 
31 | - [`spanner-execute-sql`](../tools/spanner/spanner-execute-sql.md)  
32 |   Run structured and parameterized queries on Spanner.
33 | 
34 | ### Pre-built Configurations
35 | 
36 | - [Spanner using MCP](https://googleapis.github.io/genai-toolbox/how-to/connect-ide/spanner_mcp/)  
37 | Connect your IDE to Spanner using Toolbox.
38 | 
39 | ## Requirements
40 | 
41 | ### IAM Permissions
42 | 
43 | Spanner uses [Identity and Access Management (IAM)][iam-overview] to control
44 | user and group access to Spanner resources at the project, Spanner instance, and
45 | Spanner database levels. Toolbox will use your [Application Default Credentials
46 | (ADC)][adc] to authorize and authenticate when interacting with Spanner.
47 | 
48 | In addition to [setting the ADC for your server][set-adc], you need to ensure
49 | the IAM identity has been given the correct IAM permissions for the query
50 | provided. See [Apply IAM roles][grant-permissions] for more information on
51 | applying IAM permissions and roles to an identity.
52 | 
53 | [iam-overview]: https://cloud.google.com/spanner/docs/iam
54 | [adc]: https://cloud.google.com/docs/authentication#adc
55 | [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc
56 | [grant-permissions]: https://cloud.google.com/spanner/docs/grant-permissions
57 | 
58 | ## Example
59 | 
60 | ```yaml
61 | sources:
62 |     my-spanner-source:
63 |         kind: "spanner"
64 |         project: "my-project-id"
65 |         instance: "my-instance"
66 |         database: "my_db"
67 |         # dialect: "googlesql"
68 | ```
69 | 
70 | ## Reference
71 | 
72 | | **field** | **type** | **required** | **description**                                                                                                     |
73 | |-----------|:--------:|:------------:|---------------------------------------------------------------------------------------------------------------------|
74 | | kind      |  string  |     true     | Must be "spanner".                                                                                                  |
75 | | project   |  string  |     true     | Id of the GCP project that the cluster was created in (e.g. "my-project-id").                                       |
76 | | instance  |  string  |     true     | Name of the Spanner instance.                                                                                       |
77 | | database  |  string  |     true     | Name of the database on the Spanner instance                                                                        |
78 | | dialect   |  string  |    false     | Name of the dialect type of the Spanner database, must be either `googlesql` or `postgresql`. Default: `googlesql`. |
79 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/firestore.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "Firestore"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   Firestore is a NoSQL document database built for automatic scaling, high performance, and ease of application development. It's a fully managed, serverless database that supports mobile, web, and server development.
 7 | 
 8 | ---
 9 | 
10 | # Firestore Source
11 | 
12 | [Firestore][firestore-docs] is a NoSQL document database built for automatic
13 | scaling, high performance, and ease of application development. While the
14 | Firestore interface has many of the same features as traditional databases,
15 | as a NoSQL database it differs from them in the way it describes relationships
16 | between data objects.
17 | 
18 | If you are new to Firestore, you can [create a database and learn the
19 | basics][firestore-quickstart].
20 | 
21 | [firestore-docs]: https://cloud.google.com/firestore/docs
22 | [firestore-quickstart]: https://cloud.google.com/firestore/docs/quickstart-servers
23 | 
24 | ## Requirements
25 | 
26 | ### IAM Permissions
27 | 
28 | Firestore uses [Identity and Access Management (IAM)][iam-overview] to control
29 | user and group access to Firestore resources. Toolbox will use your [Application
30 | Default Credentials (ADC)][adc] to authorize and authenticate when interacting
31 | with [Firestore][firestore-docs].
32 | 
33 | In addition to [setting the ADC for your server][set-adc], you need to ensure
34 | the IAM identity has been given the correct IAM permissions for accessing
35 | Firestore. Common roles include:
36 | 
37 | - `roles/datastore.user` - Read and write access to Firestore
38 | - `roles/datastore.viewer` - Read-only access to Firestore
39 | - `roles/firebaserules.admin` - Full management of Firebase Security Rules for
40 |   Firestore. This role is required for operations that involve creating,
41 |   updating, or managing Firestore security rules (see [Firebase Security Rules
42 |   roles][firebaserules-roles])
43 | 
44 | See [Firestore access control][firestore-iam] for more information on
45 | applying IAM permissions and roles to an identity.
46 | 
47 | [iam-overview]: https://cloud.google.com/firestore/docs/security/iam
48 | [adc]: https://cloud.google.com/docs/authentication#adc
49 | [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc
50 | [firestore-iam]: https://cloud.google.com/firestore/docs/security/iam
51 | [firebaserules-roles]:
52 |     https://cloud.google.com/iam/docs/roles-permissions/firebaserules
53 | 
54 | ### Database Selection
55 | 
56 | Firestore allows you to create multiple databases within a single project. Each
57 | database is isolated from the others and has its own set of documents and
58 | collections. If you don't specify a database in your configuration, the default
59 | database named `(default)` will be used.
60 | 
61 | ## Example
62 | 
63 | ```yaml
64 | sources:
65 |   my-firestore-source:
66 |     kind: "firestore"
67 |     project: "my-project-id"
68 |     # database: "my-database"  # Optional, defaults to "(default)"
69 | ```
70 | 
71 | ## Reference
72 | 
73 | | **field** | **type** | **required** | **description**                                                                                          |
74 | |-----------|:--------:|:------------:|----------------------------------------------------------------------------------------------------------|
75 | | kind      |  string  |     true     | Must be "firestore".                                                                                     |
76 | | project   |  string  |     true     | Id of the GCP project that contains the Firestore database (e.g. "my-project-id").                       |
77 | | database  |  string  |     false    | Name of the Firestore database to connect to. Defaults to "(default)" if not specified.                  |
78 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/bigquery/bigquery-forecast.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "bigquery-forecast"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   A "bigquery-forecast" tool forecasts time series data in BigQuery.
 7 | aliases:
 8 | - /resources/tools/bigquery-forecast
 9 | ---
10 | 
11 | ## About
12 | 
13 | A `bigquery-forecast` tool forecasts time series data in BigQuery.
14 | It's compatible with the following sources:
15 | 
16 | - [bigquery](../../sources/bigquery.md)
17 | 
18 | `bigquery-forecast` constructs and executes a `SELECT * FROM AI.FORECAST(...)`
19 | query based on the provided parameters:
20 | 
21 | - **history_data** (string, required): This specifies the source of the
22 |   historical time series data. It can be either a fully qualified BigQuery table
23 |   ID (e.g., my-project.my_dataset.my_table) or a SQL query that returns the
24 |   data.
25 | - **timestamp_col** (string, required): The name of the column in your
26 |   history_data that contains the timestamps.
27 | - **data_col** (string, required): The name of the column in your history_data
28 |   that contains the numeric values to be forecasted.
29 | - **id_cols** (array of strings, optional): If you are forecasting multiple time
30 |   series at once (e.g., sales for different products), this parameter takes an
31 |   array of column names that uniquely identify each series. It defaults to an
32 |   empty array if not provided.
33 | - **horizon** (integer, optional): The number of future time steps you want to
34 |   predict. It defaults to 10 if not specified.
35 | 
36 | The behavior of this tool is influenced by the `writeMode` setting on its `bigquery` source:
37 | 
38 | - **`allowed` (default) and `blocked`:** These modes do not impose any special restrictions on the `bigquery-forecast` tool.
39 | - **`protected`:** This mode enables session-based execution. The tool will operate within the same BigQuery session as other
40 |   tools using the same source. This allows the `history_data` parameter to be a query that references temporary resources (e.g., 
41 |   `TEMP` tables) created within that session.
42 | 
43 | The tool's behavior is also influenced by the `allowedDatasets` restriction on the `bigquery` source:
44 | 
45 | - **Without `allowedDatasets` restriction:** The tool can use any table or query for the `history_data` parameter.
46 | - **With `allowedDatasets` restriction:** The tool verifies that the `history_data` parameter only accesses tables within the allowed datasets.
47 |   - If `history_data` is a table ID, the tool checks if the table's dataset is in the allowed list.
48 |   - If `history_data` is a query, the tool performs a dry run to analyze the query and rejects it if it accesses any table outside the allowed list.
49 | 
50 | ## Example
51 | 
52 | ```yaml
53 | tools:
54 |  forecast_tool:
55 |     kind: bigquery-forecast
56 |     source: my-bigquery-source
57 |     description: Use this tool to forecast time series data in BigQuery.
58 | ```
59 | 
60 | ## Sample Prompt
61 | You can use the following sample prompts to call this tool:
62 | 
63 | - Can you forecast the history time series data in bigquery table `bqml_tutorial.google_analytic`? Use project_id `myproject`.
64 | - What are the future `total_visits` in bigquery table `bqml_tutorial.google_analytic`?
65 | 
66 | 
67 | ## Reference
68 | 
69 | | **field**   | **type** | **required** | **description**                                         |
70 | |-------------|:--------:|:------------:|---------------------------------------------------------|
71 | | kind        |  string  |     true     | Must be "bigquery-forecast".                            |
72 | | source      |  string  |     true     | Name of the source the forecast tool should execute on. |
73 | | description |  string  |     true     | Description of the tool that is passed to the LLM.      |
74 | 
```

--------------------------------------------------------------------------------
/internal/prebuiltconfigs/tools/cloud-sql-mysql.yaml:
--------------------------------------------------------------------------------

```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 | #     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 | sources:
16 |   cloud-sql-mysql-source:
17 |     kind: cloud-sql-mysql
18 |     project: ${CLOUD_SQL_MYSQL_PROJECT}
19 |     region: ${CLOUD_SQL_MYSQL_REGION}
20 |     instance: ${CLOUD_SQL_MYSQL_INSTANCE}
21 |     database: ${CLOUD_SQL_MYSQL_DATABASE}
22 |     user: ${CLOUD_SQL_MYSQL_USER}
23 |     password: ${CLOUD_SQL_MYSQL_PASSWORD}
24 |     ipType: ${CLOUD_SQL_MYSQL_IP_TYPE:PUBLIC}
25 | tools:
26 |   execute_sql:
27 |     kind: mysql-execute-sql
28 |     source: cloud-sql-mysql-source
29 |     description: Use this tool to execute SQL.
30 |   list_active_queries:
31 |     kind: mysql-list-active-queries
32 |     source: cloud-sql-mysql-source
33 |     description: Lists top N (default 10) ongoing queries from processlist and innodb_trx, ordered by execution time in descending order. Returns detailed information of those queries in json format, including process id, query, transaction duration, transaction wait duration, process time, transaction state, process state, username with host, transaction rows locked, transaction rows modified, and db schema.
34 |   get_query_plan:
35 |     kind: mysql-sql
36 |     source: cloud-sql-mysql-source
37 |     description: "Provide information about how MySQL executes a SQL statement. Common use cases include: 1) analyze query plan to improve its performance, and 2) determine effectiveness of existing indexes and evalueate new ones."
38 |     statement: |
39 |       EXPLAIN FORMAT=JSON {{.sql_statement}};
40 |     templateParameters:
41 |       - name: sql_statement
42 |         type: string
43 |         description: "the SQL statement to explain"
44 |         required: true
45 |   list_tables:
46 |     kind: mysql-list-tables
47 |     source: cloud-sql-mysql-source
48 |     description: "Lists detailed schema information (object type, columns, constraints, indexes, triggers, comment) as JSON for user-created tables (ordinary or partitioned). Filters by a comma-separated list of names. If names are omitted, lists all tables in user schemas."
49 |   list_tables_missing_unique_indexes:
50 |     kind: mysql-list-tables-missing-unique-indexes
51 |     source: cloud-sql-mysql-source
52 |     description: "Find tables that do not have primary or unique key constraint. A primary key or unique key is the only mechanism that guaranttes a row is unique. Without them, the database-level protection against data integrity issues will be missing."
53 |   list_table_fragmentation:
54 |     kind: mysql-list-table-fragmentation
55 |     source: cloud-sql-mysql-source
56 |     description: List table fragmentation in MySQL, by calculating the size of the data and index files and free space allocated to each table. The query calculates fragmentation percentage which represents the proportion of free space relative to the total data and index size. Storage can be reclaimed for tables with high fragmentation using OPTIMIZE TABLE.
57 | 
58 | toolsets:
59 |   cloud_sql_mysql_database_tools:
60 |     - execute_sql
61 |     - list_tables
62 |     - get_query_plan
63 |     - list_active_queries
64 |     - list_tables_missing_unique_indexes
65 |     - list_table_fragmentation
66 | 
```

--------------------------------------------------------------------------------
/docs/en/getting-started/quickstart/go/genkit/quickstart.go:
--------------------------------------------------------------------------------

```go
  1 | package main
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"log"
  7 | 
  8 | 	"github.com/googleapis/mcp-toolbox-sdk-go/core"
  9 | 	"github.com/googleapis/mcp-toolbox-sdk-go/tbgenkit"
 10 | 
 11 | 	"github.com/firebase/genkit/go/ai"
 12 | 	"github.com/firebase/genkit/go/genkit"
 13 | 	"github.com/firebase/genkit/go/plugins/googlegenai"
 14 | )
 15 | 
 16 | const systemPrompt = `
 17 | You're a helpful hotel assistant. You handle hotel searching, booking, and
 18 | cancellations. When the user searches for a hotel, mention its name, id,
 19 | location and price tier. Always mention hotel ids while performing any
 20 | searches. This is very important for any operations. For any bookings or
 21 | cancellations, please provide the appropriate confirmation. Be sure to
 22 | update checkin or checkout dates if mentioned by the user.
 23 | Don't ask for confirmations from the user.
 24 | `
 25 | 
 26 | var queries = []string{
 27 | 	"Find hotels in Basel with Basel in its name.",
 28 | 	"Can you book the hotel Hilton Basel for me?",
 29 | 	"Oh wait, this is too expensive. Please cancel it and book the Hyatt Regency instead.",
 30 | 	"My check in dates would be from April 10, 2024 to April 19, 2024.",
 31 | }
 32 | 
 33 | func main() {
 34 | 	ctx := context.Background()
 35 | 
 36 | 	// Create Toolbox Client
 37 | 	toolboxClient, err := core.NewToolboxClient("http://127.0.0.1:5000")
 38 | 	if err != nil {
 39 | 		log.Fatalf("Failed to create Toolbox client: %v", err)
 40 | 	}
 41 | 
 42 | 	// Load the tools using the MCP Toolbox SDK.
 43 | 	tools, err := toolboxClient.LoadToolset("my-toolset", ctx)
 44 | 	if err != nil {
 45 | 		log.Fatalf("Failed to load tools: %v\nMake sure your Toolbox server is running and the tool is configured.", err)
 46 | 	}
 47 | 
 48 | 	// Initialize Genkit
 49 | 	g := genkit.Init(ctx,
 50 | 		genkit.WithPlugins(&googlegenai.GoogleAI{}),
 51 | 		genkit.WithDefaultModel("googleai/gemini-2.0-flash"),
 52 | 	)
 53 | 	if err != nil {
 54 | 		log.Fatalf("Failed to init genkit: %v\n", err)
 55 | 	}
 56 | 
 57 | 	// Create a conversation history
 58 | 	conversationHistory := []*ai.Message{
 59 | 		ai.NewSystemTextMessage(systemPrompt),
 60 | 	}
 61 | 
 62 | 	// Convert your tool to a Genkit tool.
 63 | 	genkitTools := make([]ai.Tool, len(tools))
 64 | 	for i, tool := range tools {
 65 | 		newTool, err := tbgenkit.ToGenkitTool(tool, g)
 66 | 		if err != nil {
 67 | 			log.Fatalf("Failed to convert tool: %v\n", err)
 68 | 		}
 69 | 		genkitTools[i] = newTool
 70 | 	}
 71 | 
 72 | 	toolRefs := make([]ai.ToolRef, len(genkitTools))
 73 | 
 74 | 	for i, tool := range genkitTools {
 75 | 		toolRefs[i] = tool
 76 | 	}
 77 | 
 78 | 	for _, query := range queries {
 79 | 		conversationHistory = append(conversationHistory, ai.NewUserTextMessage(query))
 80 | 		response, err := genkit.Generate(ctx, g,
 81 | 			ai.WithMessages(conversationHistory...),
 82 | 			ai.WithTools(toolRefs...),
 83 | 			ai.WithReturnToolRequests(true),
 84 | 		)
 85 | 
 86 | 		if err != nil {
 87 | 			log.Fatalf("%v\n", err)
 88 | 		}
 89 | 		conversationHistory = append(conversationHistory, response.Message)
 90 | 
 91 | 		parts := []*ai.Part{}
 92 | 
 93 | 		for _, req := range response.ToolRequests() {
 94 | 			tool := genkit.LookupTool(g, req.Name)
 95 | 			if tool == nil {
 96 | 				log.Fatalf("tool %q not found", req.Name)
 97 | 			}
 98 | 
 99 | 			output, err := tool.RunRaw(ctx, req.Input)
100 | 			if err != nil {
101 | 				log.Fatalf("tool %q execution failed: %v", tool.Name(), err)
102 | 			}
103 | 
104 | 			parts = append(parts,
105 | 				ai.NewToolResponsePart(&ai.ToolResponse{
106 | 					Name:   req.Name,
107 | 					Ref:    req.Ref,
108 | 					Output: output,
109 | 				}))
110 | 
111 | 		}
112 | 
113 | 		if len(parts) > 0 {
114 | 			resp, err := genkit.Generate(ctx, g,
115 | 				ai.WithMessages(append(response.History(), ai.NewMessage(ai.RoleTool, nil, parts...))...),
116 | 				ai.WithTools(toolRefs...),
117 | 			)
118 | 			if err != nil {
119 | 				log.Fatal(err)
120 | 			}
121 | 			fmt.Println("\n", resp.Text())
122 | 			conversationHistory = append(conversationHistory, resp.Message)
123 | 		} else {
124 | 			fmt.Println("\n", response.Text())
125 | 		}
126 | 
127 | 	}
128 | 
129 | }
130 | 
```

--------------------------------------------------------------------------------
/internal/sources/looker/looker_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 looker_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"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/looker"
 25 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 26 | )
 27 | 
 28 | func TestParseFromYamlLooker(t *testing.T) {
 29 | 	tcs := []struct {
 30 | 		desc string
 31 | 		in   string
 32 | 		want server.SourceConfigs
 33 | 	}{
 34 | 		{
 35 | 			desc: "basic example",
 36 | 			in: `
 37 | 			sources:
 38 | 				my-looker-instance:
 39 | 					kind: looker
 40 | 					base_url: http://example.looker.com/
 41 | 					client_id: jasdl;k;tjl
 42 | 					client_secret: sdakl;jgflkasdfkfg
 43 | 			`,
 44 | 			want: map[string]sources.SourceConfig{
 45 | 				"my-looker-instance": looker.Config{
 46 | 					Name:               "my-looker-instance",
 47 | 					Kind:               looker.SourceKind,
 48 | 					BaseURL:            "http://example.looker.com/",
 49 | 					ClientId:           "jasdl;k;tjl",
 50 | 					ClientSecret:       "sdakl;jgflkasdfkfg",
 51 | 					Timeout:            "600s",
 52 | 					SslVerification:    true,
 53 | 					UseClientOAuth:     false,
 54 | 					ShowHiddenModels:   true,
 55 | 					ShowHiddenExplores: true,
 56 | 					ShowHiddenFields:   true,
 57 | 					Location:           "us",
 58 | 				},
 59 | 			},
 60 | 		},
 61 | 	}
 62 | 	for _, tc := range tcs {
 63 | 		t.Run(tc.desc, func(t *testing.T) {
 64 | 			got := struct {
 65 | 				Sources server.SourceConfigs `yaml:"sources"`
 66 | 			}{}
 67 | 			// Parse contents
 68 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
 69 | 			if err != nil {
 70 | 				t.Fatalf("unable to unmarshal: %s", err)
 71 | 			}
 72 | 			if !cmp.Equal(tc.want, got.Sources) {
 73 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
 74 | 			}
 75 | 		})
 76 | 	}
 77 | }
 78 | 
 79 | func TestFailParseFromYamlLooker(t *testing.T) {
 80 | 	tcs := []struct {
 81 | 		desc string
 82 | 		in   string
 83 | 		err  string
 84 | 	}{
 85 | 		{
 86 | 			desc: "extra field",
 87 | 			in: `
 88 | 			sources:
 89 | 				my-looker-instance:
 90 | 					kind: looker
 91 | 					base_url: http://example.looker.com/
 92 | 					client_id: jasdl;k;tjl
 93 | 					client_secret: sdakl;jgflkasdfkfg
 94 | 					schema: test-schema
 95 | 			`,
 96 | 			err: "unable to parse source \"my-looker-instance\" as \"looker\": [5:1] unknown field \"schema\"\n   2 | client_id: jasdl;k;tjl\n   3 | client_secret: sdakl;jgflkasdfkfg\n   4 | kind: looker\n>  5 | schema: test-schema\n       ^\n",
 97 | 		},
 98 | 		{
 99 | 			desc: "missing required field",
100 | 			in: `
101 | 			sources:
102 | 				my-looker-instance:
103 | 					kind: looker
104 | 					client_id: jasdl;k;tjl
105 | 			`,
106 | 			err: "unable to parse source \"my-looker-instance\" as \"looker\": Key: 'Config.BaseURL' Error:Field validation for 'BaseURL' failed on the 'required' tag",
107 | 		},
108 | 	}
109 | 	for _, tc := range tcs {
110 | 		t.Run(tc.desc, func(t *testing.T) {
111 | 			got := struct {
112 | 				Sources server.SourceConfigs `yaml:"sources"`
113 | 			}{}
114 | 			// Parse contents
115 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
116 | 			if err == nil {
117 | 				t.Fatalf("expect parsing to fail")
118 | 			}
119 | 			errStr := err.Error()
120 | 			if errStr != tc.err {
121 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
122 | 			}
123 | 		})
124 | 	}
125 | }
126 | 
```

--------------------------------------------------------------------------------
/internal/sources/util.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 sources
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"encoding/json"
 20 | 	"fmt"
 21 | 	"io"
 22 | 	"net/http"
 23 | 	"strings"
 24 | 
 25 | 	"cloud.google.com/go/cloudsqlconn"
 26 | 	"golang.org/x/oauth2/google"
 27 | )
 28 | 
 29 | // GetCloudSQLDialOpts retrieve dial options with the right ip type and user agent for cloud sql
 30 | // databases.
 31 | func GetCloudSQLOpts(ipType, userAgent string, useIAM bool) ([]cloudsqlconn.Option, error) {
 32 | 	opts := []cloudsqlconn.Option{cloudsqlconn.WithUserAgent(userAgent)}
 33 | 	switch strings.ToLower(ipType) {
 34 | 	case "private":
 35 | 		opts = append(opts, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPrivateIP()))
 36 | 	case "public":
 37 | 		opts = append(opts, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPublicIP()))
 38 | 	case "psc":
 39 | 		opts = append(opts, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPSC()))
 40 | 	default:
 41 | 		return nil, fmt.Errorf("invalid ipType %s. Must be one of `public`, `private`, or `psc`", ipType)
 42 | 	}
 43 | 
 44 | 	if useIAM {
 45 | 		opts = append(opts, cloudsqlconn.WithIAMAuthN())
 46 | 	}
 47 | 	return opts, nil
 48 | }
 49 | 
 50 | // GetIAMPrincipalEmailFromADC finds the email associated with ADC
 51 | func GetIAMPrincipalEmailFromADC(ctx context.Context) (string, error) {
 52 | 	// Finds ADC and returns an HTTP client associated with it
 53 | 	client, err := google.DefaultClient(ctx,
 54 | 		"https://www.googleapis.com/auth/userinfo.email")
 55 | 	if err != nil {
 56 | 		return "", fmt.Errorf("failed to call userinfo endpoint: %w", err)
 57 | 	}
 58 | 
 59 | 	// Retrieve the email associated with the token
 60 | 	resp, err := client.Get("https://oauth2.googleapis.com/tokeninfo")
 61 | 	if err != nil {
 62 | 		return "", fmt.Errorf("failed to call tokeninfo endpoint: %w", err)
 63 | 	}
 64 | 	defer resp.Body.Close()
 65 | 
 66 | 	bodyBytes, err := io.ReadAll(resp.Body)
 67 | 	if err != nil {
 68 | 		return "", fmt.Errorf("error reading response body %d: %s", resp.StatusCode, string(bodyBytes))
 69 | 	}
 70 | 	if resp.StatusCode != http.StatusOK {
 71 | 		return "", fmt.Errorf("tokeninfo endpoint returned non-OK status %d: %s", resp.StatusCode, string(bodyBytes))
 72 | 	}
 73 | 
 74 | 	// Unmarshal response body and get `email`
 75 | 	var responseJSON map[string]any
 76 | 	err = json.Unmarshal(bodyBytes, &responseJSON)
 77 | 	if err != nil {
 78 | 
 79 | 		return "", fmt.Errorf("error parsing JSON: %v", err)
 80 | 	}
 81 | 
 82 | 	emailValue, ok := responseJSON["email"]
 83 | 	if !ok {
 84 | 		return "", fmt.Errorf("email not found in response: %v", err)
 85 | 	}
 86 | 	// service account email used for IAM should trim the suffix
 87 | 	email := strings.TrimSuffix(emailValue.(string), ".gserviceaccount.com")
 88 | 	return email, nil
 89 | }
 90 | 
 91 | func GetIAMAccessToken(ctx context.Context) (string, error) {
 92 | 	creds, err := google.FindDefaultCredentials(ctx, "https://www.googleapis.com/auth/cloud-platform")
 93 | 	if err != nil {
 94 | 		return "", fmt.Errorf("failed to find default credentials (run 'gcloud auth application-default login'?): %w", err)
 95 | 	}
 96 | 
 97 | 	token, err := creds.TokenSource.Token() // This gets an oauth2.Token
 98 | 	if err != nil {
 99 | 		return "", fmt.Errorf("failed to get token from token source: %w", err)
100 | 	}
101 | 
102 | 	if !token.Valid() {
103 | 		return "", fmt.Errorf("retrieved token is invalid or expired")
104 | 	}
105 | 	return token.AccessToken, nil
106 | }
107 | 
```

--------------------------------------------------------------------------------
/internal/sources/tidb/tidb.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 tidb
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 	"regexp"
 22 | 
 23 | 	_ "github.com/go-sql-driver/mysql"
 24 | 	"github.com/goccy/go-yaml"
 25 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 26 | 	"go.opentelemetry.io/otel/trace"
 27 | )
 28 | 
 29 | const SourceKind string = "tidb"
 30 | const TiDBCloudHostPattern string = `gateway\d{2}\.(.+)\.(prod|dev|staging)\.(.+)\.tidbcloud\.com`
 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 | 
 47 | 	// If the host is a TiDB Cloud instance, force to use SSL
 48 | 	if IsTiDBCloudHost(actual.Host) {
 49 | 		actual.UseSSL = true
 50 | 	}
 51 | 
 52 | 	return actual, nil
 53 | }
 54 | 
 55 | type Config struct {
 56 | 	Name     string `yaml:"name" validate:"required"`
 57 | 	Kind     string `yaml:"kind" validate:"required"`
 58 | 	Host     string `yaml:"host" validate:"required"`
 59 | 	Port     string `yaml:"port" validate:"required"`
 60 | 	User     string `yaml:"user" validate:"required"`
 61 | 	Password string `yaml:"password" validate:"required"`
 62 | 	Database string `yaml:"database" validate:"required"`
 63 | 	UseSSL   bool   `yaml:"ssl"`
 64 | }
 65 | 
 66 | func (r Config) SourceConfigKind() string {
 67 | 	return SourceKind
 68 | }
 69 | 
 70 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 71 | 	pool, err := initTiDBConnectionPool(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database, r.UseSSL)
 72 | 	if err != nil {
 73 | 		return nil, fmt.Errorf("unable to create pool: %w", err)
 74 | 	}
 75 | 
 76 | 	err = pool.PingContext(ctx)
 77 | 	if err != nil {
 78 | 		return nil, fmt.Errorf("unable to connect successfully: %w", err)
 79 | 	}
 80 | 
 81 | 	s := &Source{
 82 | 		Name: r.Name,
 83 | 		Kind: SourceKind,
 84 | 		Pool: pool,
 85 | 	}
 86 | 	return s, nil
 87 | }
 88 | 
 89 | var _ sources.Source = &Source{}
 90 | 
 91 | type Source struct {
 92 | 	Name string `yaml:"name"`
 93 | 	Kind string `yaml:"kind"`
 94 | 	Pool *sql.DB
 95 | }
 96 | 
 97 | func (s *Source) SourceKind() string {
 98 | 	return SourceKind
 99 | }
100 | 
101 | func (s *Source) TiDBPool() *sql.DB {
102 | 	return s.Pool
103 | }
104 | 
105 | func IsTiDBCloudHost(host string) bool {
106 | 	pattern := `gateway\d{2}\.(.+)\.(prod|dev|staging)\.(.+)\.tidbcloud\.com`
107 | 	match, err := regexp.MatchString(pattern, host)
108 | 	if err != nil {
109 | 		return false
110 | 	}
111 | 	return match
112 | }
113 | 
114 | func initTiDBConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname string, useSSL bool) (*sql.DB, error) {
115 | 	//nolint:all // Reassigned ctx
116 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
117 | 	defer span.End()
118 | 
119 | 	// Configure the driver to connect to the database
120 | 	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true&charset=utf8mb4&tls=%t", user, pass, host, port, dbname, useSSL)
121 | 
122 | 	// Interact with the driver directly as you normally would
123 | 	pool, err := sql.Open("mysql", dsn)
124 | 	if err != nil {
125 | 		return nil, fmt.Errorf("sql.Open: %w", err)
126 | 	}
127 | 	return pool, nil
128 | }
129 | 
```

--------------------------------------------------------------------------------
/internal/tools/dataform/dataformcompilelocal/dataformcompilelocal.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 dataformcompilelocal
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 	"os/exec"
 21 | 	"strings"
 22 | 
 23 | 	"github.com/goccy/go-yaml"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | )
 27 | 
 28 | const kind string = "dataform-compile-local"
 29 | 
 30 | func init() {
 31 | 	if !tools.Register(kind, newConfig) {
 32 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 33 | 	}
 34 | }
 35 | 
 36 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 37 | 	actual := Config{Name: name}
 38 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 39 | 		return nil, err
 40 | 	}
 41 | 	return actual, nil
 42 | }
 43 | 
 44 | type Config struct {
 45 | 	Name         string   `yaml:"name" validate:"required"`
 46 | 	Kind         string   `yaml:"kind" validate:"required"`
 47 | 	Description  string   `yaml:"description" validate:"required"`
 48 | 	AuthRequired []string `yaml:"authRequired"`
 49 | }
 50 | 
 51 | var _ tools.ToolConfig = Config{}
 52 | 
 53 | func (cfg Config) ToolConfigKind() string {
 54 | 	return kind
 55 | }
 56 | 
 57 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 58 | 	allParameters := tools.Parameters{
 59 | 		tools.NewStringParameter("project_dir", "The Dataform project directory."),
 60 | 	}
 61 | 	paramManifest := allParameters.Manifest()
 62 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 63 | 
 64 | 	t := Tool{
 65 | 		Name:         cfg.Name,
 66 | 		Kind:         kind,
 67 | 		AuthRequired: cfg.AuthRequired,
 68 | 		Parameters:   allParameters,
 69 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
 70 | 		mcpManifest:  mcpManifest,
 71 | 	}
 72 | 
 73 | 	return t, nil
 74 | }
 75 | 
 76 | var _ tools.Tool = Tool{}
 77 | 
 78 | type Tool struct {
 79 | 	Name         string           `yaml:"name"`
 80 | 	Kind         string           `yaml:"kind"`
 81 | 	AuthRequired []string         `yaml:"authRequired"`
 82 | 	Parameters   tools.Parameters `yaml:"allParams"`
 83 | 	manifest     tools.Manifest
 84 | 	mcpManifest  tools.McpManifest
 85 | }
 86 | 
 87 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
 88 | 	paramsMap := params.AsMap()
 89 | 
 90 | 	projectDir, ok := paramsMap["project_dir"].(string)
 91 | 	if !ok || projectDir == "" {
 92 | 		return nil, fmt.Errorf("error casting 'project_dir' to string or invalid value")
 93 | 	}
 94 | 
 95 | 	cmd := exec.CommandContext(ctx, "dataform", "compile", projectDir, "--json")
 96 | 	output, err := cmd.CombinedOutput()
 97 | 	if err != nil {
 98 | 		return nil, fmt.Errorf("error executing dataform compile: %w\nOutput: %s", err, string(output))
 99 | 	}
100 | 
101 | 	return strings.TrimSpace(string(output)), nil
102 | }
103 | 
104 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
105 | 	return tools.ParseParams(t.Parameters, data, claims)
106 | }
107 | 
108 | func (t Tool) Manifest() tools.Manifest {
109 | 	return t.manifest
110 | }
111 | 
112 | func (t Tool) McpManifest() tools.McpManifest {
113 | 	return t.mcpManifest
114 | }
115 | 
116 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
117 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
118 | }
119 | 
120 | func (t Tool) RequiresClientAuthorization() bool {
121 | 	return false
122 | }
123 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/yugabytedb.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "YugabyteDB"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   YugabyteDB is a high-performance, distributed SQL database. 
 7 | ---
 8 | 
 9 | ## About
10 | 
11 | [YugabyteDB][yugabytedb] is a high-performance, distributed SQL database
12 | designed for global, internet-scale applications, with full PostgreSQL
13 | compatibility.
14 | 
15 | [yugabytedb]: https://www.yugabyte.com/
16 | 
17 | ## Example
18 | 
19 | ```yaml
20 | sources:
21 |     my-yb-source:
22 |         kind: yugabytedb
23 |         host: 127.0.0.1
24 |         port: 5433
25 |         database: yugabyte
26 |         user: ${USER_NAME}
27 |         password: ${PASSWORD}
28 |         loadBalance: true
29 |         topologyKeys: cloud.region.zone1:1,cloud.region.zone2:2
30 | ```
31 | 
32 | ## Reference
33 | 
34 | | **field**                    | **type** | **required** | **description**                                                                                                                                                       |
35 | |------------------------------|:--------:|:------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
36 | | kind                         |  string  |     true     | Must be "yugabytedb".                                                                                                                                                 |
37 | | host                         |  string  |     true     | IP address to connect to.                                                                                                                                             |
38 | | port                         | integer  |     true     | Port to connect to. The default port is 5433.                                                                                                                         |
39 | | database                     |  string  |     true     | Name of the YugabyteDB database to connect to. The default database name is yugabyte.                                                                                 |
40 | | user                         |  string  |     true     | Name of the YugabyteDB user to connect as. The default user is yugabyte.                                                                                              |
41 | | password                     |  string  |     true     | Password of the YugabyteDB user. The default password is yugabyte.                                                                                                    |
42 | | loadBalance                  | boolean  |    false     | If true, enable uniform load balancing. The default loadBalance value is false.                                                                                       |
43 | | topologyKeys                 |  string  |    false     | Comma-separated geo-locations in the form cloud.region.zone:priority to enable topology-aware load balancing. Ignored if loadBalance is false. It is null by default. |
44 | | ybServersRefreshInterval     | integer  |    false     | The interval (in seconds) to refresh the servers list; ignored if loadBalance is false. The default value of ybServersRefreshInterval is 300.                         |
45 | | fallbackToTopologyKeysOnly   | boolean  |    false     | If set to true and topologyKeys are specified, only connect to nodes specified in topologyKeys. By defualt, this is set to false.                                     |
46 | | failedHostReconnectDelaySecs | integer  |    false     | Time (in seconds) to wait before trying to connect to failed nodes. The default value of is 5.                                                                        |
47 | 
```

--------------------------------------------------------------------------------
/internal/sources/spanner/spanner.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 spanner
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"cloud.google.com/go/spanner"
 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 | )
 27 | 
 28 | const SourceKind string = "spanner"
 29 | 
 30 | // validate interface
 31 | var _ sources.SourceConfig = Config{}
 32 | 
 33 | func init() {
 34 | 	if !sources.Register(SourceKind, newConfig) {
 35 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 36 | 	}
 37 | }
 38 | 
 39 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 40 | 	actual := Config{Name: name, Dialect: "googlesql"} // Default dialect
 41 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 42 | 		return nil, err
 43 | 	}
 44 | 	return actual, nil
 45 | }
 46 | 
 47 | type Config struct {
 48 | 	Name     string          `yaml:"name" validate:"required"`
 49 | 	Kind     string          `yaml:"kind" validate:"required"`
 50 | 	Project  string          `yaml:"project" validate:"required"`
 51 | 	Instance string          `yaml:"instance" validate:"required"`
 52 | 	Dialect  sources.Dialect `yaml:"dialect" validate:"required"`
 53 | 	Database string          `yaml:"database" validate:"required"`
 54 | }
 55 | 
 56 | func (r Config) SourceConfigKind() string {
 57 | 	return SourceKind
 58 | }
 59 | 
 60 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 61 | 	client, err := initSpannerClient(ctx, tracer, r.Name, r.Project, r.Instance, r.Database)
 62 | 	if err != nil {
 63 | 		return nil, fmt.Errorf("unable to create client: %w", err)
 64 | 	}
 65 | 
 66 | 	s := &Source{
 67 | 		Name:    r.Name,
 68 | 		Kind:    SourceKind,
 69 | 		Client:  client,
 70 | 		Dialect: r.Dialect.String(),
 71 | 	}
 72 | 	return s, nil
 73 | }
 74 | 
 75 | var _ sources.Source = &Source{}
 76 | 
 77 | type Source struct {
 78 | 	Name    string `yaml:"name"`
 79 | 	Kind    string `yaml:"kind"`
 80 | 	Client  *spanner.Client
 81 | 	Dialect string
 82 | }
 83 | 
 84 | func (s *Source) SourceKind() string {
 85 | 	return SourceKind
 86 | }
 87 | 
 88 | func (s *Source) SpannerClient() *spanner.Client {
 89 | 	return s.Client
 90 | }
 91 | 
 92 | func (s *Source) DatabaseDialect() string {
 93 | 	return s.Dialect
 94 | }
 95 | 
 96 | func initSpannerClient(ctx context.Context, tracer trace.Tracer, name, project, instance, dbname string) (*spanner.Client, error) {
 97 | 	//nolint:all // Reassigned ctx
 98 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
 99 | 	defer span.End()
100 | 
101 | 	// Configure the connection to the database
102 | 	db := fmt.Sprintf("projects/%s/instances/%s/databases/%s", project, instance, dbname)
103 | 
104 | 	// Configure session pool to automatically clean inactive transactions
105 | 	sessionPoolConfig := spanner.SessionPoolConfig{
106 | 		TrackSessionHandles: true,
107 | 		InactiveTransactionRemovalOptions: spanner.InactiveTransactionRemovalOptions{
108 | 			ActionOnInactiveTransaction: spanner.WarnAndClose,
109 | 		},
110 | 	}
111 | 
112 | 	// Create spanner client
113 | 	userAgent, err := util.UserAgentFromContext(ctx)
114 | 	if err != nil {
115 | 		return nil, err
116 | 	}
117 | 	client, err := spanner.NewClientWithConfig(ctx, db, spanner.ClientConfig{SessionPoolConfig: sessionPoolConfig, UserAgent: userAgent})
118 | 	if err != nil {
119 | 		return nil, fmt.Errorf("unable to create new client: %w", err)
120 | 	}
121 | 
122 | 	return client, nil
123 | }
124 | 
```

--------------------------------------------------------------------------------
/internal/prebuiltconfigs/tools/mysql.yaml:
--------------------------------------------------------------------------------

```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 | #     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 | sources:
16 |   mysql-source:
17 |     kind: mysql
18 |     host: ${MYSQL_HOST}
19 |     port: ${MYSQL_PORT}
20 |     database: ${MYSQL_DATABASE}
21 |     user: ${MYSQL_USER}
22 |     password: ${MYSQL_PASSWORD}
23 |     # Optional: supply additional DSN parameters (e.g. TLS, charset) via env var.
24 |     # Provide a YAML-encoded map in MYSQL_QUERY_PARAMS, for example:
25 |     #   export MYSQL_QUERY_PARAMS="{tls: preferred, charset: utf8mb4}"
26 |     # When the variable is empty/undefined, queryParams will be treated as nil.
27 |     queryParams: ${MYSQL_QUERY_PARAMS:}
28 |     queryTimeout: 30s # Optional
29 | tools:
30 |   execute_sql:
31 |     kind: mysql-execute-sql
32 |     source: mysql-source
33 |     description: Use this tool to execute SQL.
34 |   list_active_queries:
35 |     kind: mysql-list-active-queries
36 |     source: mysql-source
37 |     description: Lists top N (default 10) ongoing queries from processlist and innodb_trx, ordered by execution time in descending order. Returns detailed information of those queries in json format, including process id, query, transaction duration, transaction wait duration, process time, transaction state, process state, username with host, transaction rows locked, transaction rows modified, and db schema.
38 |   get_query_plan:
39 |     kind: mysql-sql
40 |     source: mysql-source
41 |     description: "Provide information about how MySQL executes a SQL statement. Common use cases include: 1) analyze query plan to improve its performance, and 2) determine effectiveness of existing indexes and evalueate new ones."
42 |     statement: |
43 |       EXPLAIN FORMAT=JSON {{.sql_statement}};
44 |     templateParameters:
45 |       - name: sql_statement
46 |         type: string
47 |         description: "the SQL statement to explain"
48 |         required: true
49 |   list_tables:
50 |     kind: mysql-list-tables
51 |     source: mysql-source
52 |     description: "Lists detailed schema information (object type, columns, constraints, indexes, triggers, comment) as JSON for user-created tables (ordinary or partitioned). Filters by a comma-separated list of names. If names are omitted, lists all tables in user schemas."
53 |   list_tables_missing_unique_indexes:
54 |     kind: mysql-list-tables-missing-unique-indexes
55 |     source: mysql-source
56 |     description: "Find tables that do not have primary or unique key constraint. A primary key or unique key is the only mechanism that guaranttes a row is unique. Without them, the database-level protection against data integrity issues will be missing."
57 |   list_table_fragmentation:
58 |     kind: mysql-list-table-fragmentation
59 |     source: mysql-source
60 |     description: List table fragmentation in MySQL, by calculating the size of the data and index files and free space allocated to each table. The query calculates fragmentation percentage which represents the proportion of free space relative to the total data and index size. Storage can be reclaimed for tables with high fragmentation using OPTIMIZE TABLE.
61 | 
62 | toolsets:
63 |   mysql_database_tools:
64 |     - execute_sql
65 |     - list_tables
66 |     - get_query_plan
67 |     - list_active_queries
68 |     - list_tables_missing_unique_indexes
69 |     - list_table_fragmentation
70 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/mssql.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "SQL Server"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   SQL Server is a relational database management system (RDBMS).
 7 | 
 8 | ---
 9 | 
10 | ## About
11 | 
12 | [SQL Server][mssql-docs] is a relational database management system (RDBMS)
13 | developed by Microsoft that allows users to store, retrieve, and manage large
14 | amount of data through a structured format.
15 | 
16 | [mssql-docs]: https://www.microsoft.com/en-us/sql-server
17 | 
18 | ## Available Tools
19 | 
20 | - [`mssql-sql`](../tools/mssql/mssql-sql.md)  
21 |   Execute pre-defined SQL Server queries with placeholder parameters.
22 | 
23 | - [`mssql-execute-sql`](../tools/mssql/mssql-execute-sql.md)  
24 |   Run parameterized SQL Server queries in SQL Server.
25 | 
26 | - [`mssql-list-tables`](../tools/mssql/mssql-list-tables.md)  
27 |   List tables in a SQL Server database.
28 | 
29 | ## Requirements
30 | 
31 | ### Database User
32 | 
33 | This source only uses standard authentication. You will need to [create a
34 | SQL Server user][mssql-users] to login to the database with.
35 | 
36 | [mssql-users]: https://learn.microsoft.com/en-us/sql/relational-databases/security/authentication-access/create-a-database-user?view=sql-server-ver16
37 | 
38 | ## Example
39 | 
40 | ```yaml
41 | sources:
42 |     my-mssql-source:
43 |         kind: mssql
44 |         host: 127.0.0.1
45 |         port: 1433
46 |         database: my_db
47 |         user: ${USER_NAME}
48 |         password: ${PASSWORD}
49 |         # encrypt: strict
50 | ```
51 | 
52 | {{< notice tip >}}
53 | Use environment variable replacement with the format ${ENV_NAME}
54 | instead of hardcoding your secrets into the configuration file.
55 | {{< /notice >}}
56 | 
57 | ## Reference
58 | 
59 | | **field** | **type** | **required** | **description**                                                                                                                                                                            |
60 | |-----------|:--------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
61 | | kind      |  string  |     true     | Must be "mssql".                                                                                                                                                                           |
62 | | host      |  string  |     true     | IP address to connect to (e.g. "127.0.0.1").                                                                                                                                               |
63 | | port      |  string  |     true     | Port to connect to (e.g. "1433").                                                                                                                                                          |
64 | | database  |  string  |     true     | Name of the SQL Server database to connect to (e.g. "my_db").                                                                                                                              |
65 | | user      |  string  |     true     | Name of the SQL Server user to connect as (e.g. "my-user").                                                                                                                                |
66 | | password  |  string  |     true     | Password of the SQL Server user (e.g. "my-password").                                                                                                                                      |
67 | | encrypt   |  string  |    false     | Encryption level for data transmitted between the client and server (e.g., "strict"). If not specified, defaults to the [github.com/microsoft/go-mssqldb](https://github.com/microsoft/go-mssqldb?tab=readme-ov-file#common-parameters) package's default encrypt value. |
68 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/bigquery/bigquery-analyze-contribution.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "bigquery-analyze-contribution"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   A "bigquery-analyze-contribution" tool performs contribution analysis in BigQuery.
 7 | aliases:
 8 | - /resources/tools/bigquery-analyze-contribution
 9 | ---
10 | 
11 | ## About
12 | 
13 | A `bigquery-analyze-contribution` tool performs contribution analysis in
14 | BigQuery by creating a temporary `CONTRIBUTION_ANALYSIS` model and then querying
15 | it with `ML.GET_INSIGHTS` to find top contributors for a given metric.
16 | 
17 | It's compatible with the following sources:
18 | 
19 | - [bigquery](../../sources/bigquery.md)
20 | 
21 | `bigquery-analyze-contribution` takes the following parameters:
22 | 
23 | - **input_data** (string, required): The data that contain the test and control
24 |   data to analyze. This can be a fully qualified BigQuery table ID (e.g.,
25 |   `my-project.my_dataset.my_table`) or a SQL query that returns the data.
26 | - **contribution_metric** (string, required): The name of the column that
27 |   contains the metric to analyze. This can be SUM(metric_column_name),
28 |   SUM(numerator_metric_column_name)/SUM(denominator_metric_column_name) or
29 |   SUM(metric_sum_column_name)/COUNT(DISTINCT categorical_column_name) depending
30 |   the type of metric to analyze.
31 | - **is_test_col** (string, required): The name of the column that identifies
32 |   whether a row is in the test or control group. The column must contain boolean
33 |   values.
34 | - **dimension_id_cols** (array of strings, optional): An array of column names
35 |   that uniquely identify each dimension.
36 | - **top_k_insights_by_apriori_support** (integer, optional): The number of top
37 |   insights to return, ranked by apriori support. Default to '30'.
38 | - **pruning_method** (string, optional): The method to use for pruning redundant
39 |   insights. Can be `'NO_PRUNING'` or `'PRUNE_REDUNDANT_INSIGHTS'`. Defaults to
40 |   `'PRUNE_REDUNDANT_INSIGHTS'`.
41 | 
42 | The behavior of this tool is influenced by the `writeMode` setting on its `bigquery` source:
43 | 
44 | - **`allowed` (default) and `blocked`:** These modes do not impose any special restrictions on the `bigquery-analyze-contribution` tool.
45 | - **`protected`:** This mode enables session-based execution. The tool will operate within the same BigQuery session as other
46 |   tools using the same source. This allows the `input_data` parameter to be a query that references temporary resources (e.g., 
47 |   `TEMP` tables) created within that session.
48 | 
49 | 
50 | ## Example
51 | 
52 | ```yaml
53 | tools:
54 |   contribution_analyzer:
55 |     kind: bigquery-analyze-contribution
56 |     source: my-bigquery-source
57 |     description: Use this tool to run contribution analysis on a dataset in BigQuery.
58 | ```
59 | 
60 | ## Sample Prompt
61 | You can prepare a sample table following
62 | https://cloud.google.com/bigquery/docs/get-contribution-analysis-insights.
63 | And use the following sample prompts to call this tool:
64 | 
65 | - What drives the changes in sales in the table
66 |   `bqml_tutorial.iowa_liquor_sales_sum_data`? Use the project id myproject.
67 | - Analyze the contribution for the `total_sales` metric in the table
68 |   `bqml_tutorial.iowa_liquor_sales_sum_data`. The test group is identified by
69 |   the `is_test` column. The dimensions are `store_name`, `city`, `vendor_name`,
70 |   `category_name` and `item_description`.
71 | 
72 | ## Reference
73 | 
74 | | **field**   | **type** | **required** | **description**                                            |
75 | |-------------|:--------:|:------------:|------------------------------------------------------------|
76 | | kind        |  string  |     true     | Must be "bigquery-analyze-contribution".                   |
77 | | source      |  string  |     true     | Name of the source the tool should execute on.             |
78 | | description |  string  |     true     | Description of the tool that is passed to the LLM.         |
79 | 
```

--------------------------------------------------------------------------------
/internal/tools/mongodb/mongodbdeleteone/mongodbdeleteone_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 mongodbdeleteone_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/mongodbdeleteone"
 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-delete-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 | 			`,
 57 | 			want: server.ToolConfigs{
 58 | 				"example_tool": mongodbdeleteone.Config{
 59 | 					Name:          "example_tool",
 60 | 					Kind:          "mongodb-delete-one",
 61 | 					Source:        "my-instance",
 62 | 					AuthRequired:  []string{},
 63 | 					Database:      "test_db",
 64 | 					Collection:    "test_coll",
 65 | 					Description:   "some description",
 66 | 					FilterPayload: "{ name: {{json .name}} }\n",
 67 | 					FilterParams: tools.Parameters{
 68 | 						&tools.StringParameter{
 69 | 							CommonParameter: tools.CommonParameter{
 70 | 								Name: "name",
 71 | 								Type: "string",
 72 | 								Desc: "small description",
 73 | 							},
 74 | 						},
 75 | 					},
 76 | 				},
 77 | 			},
 78 | 		},
 79 | 	}
 80 | 	for _, tc := range tcs {
 81 | 		t.Run(tc.desc, func(t *testing.T) {
 82 | 			got := struct {
 83 | 				Tools server.ToolConfigs `yaml:"tools"`
 84 | 			}{}
 85 | 			// Parse contents
 86 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 87 | 			if err != nil {
 88 | 				t.Fatalf("unable to unmarshal: %s", err)
 89 | 			}
 90 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 91 | 				t.Fatalf("incorrect parse: diff %v", diff)
 92 | 			}
 93 | 		})
 94 | 	}
 95 | 
 96 | }
 97 | 
 98 | func TestFailParseFromYamlMongoQuery(t *testing.T) {
 99 | 	ctx, err := testutils.ContextWithNewLogger()
100 | 	if err != nil {
101 | 		t.Fatalf("unexpected error: %s", err)
102 | 	}
103 | 	tcs := []struct {
104 | 		desc string
105 | 		in   string
106 | 		err  string
107 | 	}{
108 | 		{
109 | 			desc: "Invalid method",
110 | 			in: `
111 | 			tools:
112 | 				example_tool:
113 | 					kind: mongodb-delete-one
114 | 					source: my-instance
115 | 					description: some description
116 | 					collection: test_coll
117 | 					filterPayload: |
118 | 					  { name : {{json .name}} }
119 | 			`,
120 | 			err: `unable to parse tool "example_tool" as kind "mongodb-delete-one"`,
121 | 		},
122 | 	}
123 | 	for _, tc := range tcs {
124 | 		t.Run(tc.desc, func(t *testing.T) {
125 | 			got := struct {
126 | 				Tools server.ToolConfigs `yaml:"tools"`
127 | 			}{}
128 | 			// Parse contents
129 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
130 | 			if err == nil {
131 | 				t.Fatalf("expect parsing to fail")
132 | 			}
133 | 			errStr := err.Error()
134 | 			if !strings.Contains(errStr, tc.err) {
135 | 				t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err)
136 | 			}
137 | 		})
138 | 	}
139 | 
140 | }
141 | 
```

--------------------------------------------------------------------------------
/internal/tools/mongodb/mongodbdeletemany/mongodbdeletemany_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 mongodbdeletemany_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/mongodbdeletemany"
 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-delete-many
 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 | 			`,
 57 | 			want: server.ToolConfigs{
 58 | 				"example_tool": mongodbdeletemany.Config{
 59 | 					Name:          "example_tool",
 60 | 					Kind:          "mongodb-delete-many",
 61 | 					Source:        "my-instance",
 62 | 					AuthRequired:  []string{},
 63 | 					Database:      "test_db",
 64 | 					Collection:    "test_coll",
 65 | 					Description:   "some description",
 66 | 					FilterPayload: "{ name: {{json .name}} }\n",
 67 | 					FilterParams: tools.Parameters{
 68 | 						&tools.StringParameter{
 69 | 							CommonParameter: tools.CommonParameter{
 70 | 								Name: "name",
 71 | 								Type: "string",
 72 | 								Desc: "small description",
 73 | 							},
 74 | 						},
 75 | 					},
 76 | 				},
 77 | 			},
 78 | 		},
 79 | 	}
 80 | 	for _, tc := range tcs {
 81 | 		t.Run(tc.desc, func(t *testing.T) {
 82 | 			got := struct {
 83 | 				Tools server.ToolConfigs `yaml:"tools"`
 84 | 			}{}
 85 | 			// Parse contents
 86 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 87 | 			if err != nil {
 88 | 				t.Fatalf("unable to unmarshal: %s", err)
 89 | 			}
 90 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 91 | 				t.Fatalf("incorrect parse: diff %v", diff)
 92 | 			}
 93 | 		})
 94 | 	}
 95 | 
 96 | }
 97 | 
 98 | func TestFailParseFromYamlMongoQuery(t *testing.T) {
 99 | 	ctx, err := testutils.ContextWithNewLogger()
100 | 	if err != nil {
101 | 		t.Fatalf("unexpected error: %s", err)
102 | 	}
103 | 	tcs := []struct {
104 | 		desc string
105 | 		in   string
106 | 		err  string
107 | 	}{
108 | 		{
109 | 			desc: "Invalid method",
110 | 			in: `
111 | 			tools:
112 | 				example_tool:
113 | 					kind: mongodb-delete-many
114 | 					source: my-instance
115 | 					description: some description
116 | 					collection: test_coll
117 | 					filterPayload: |
118 | 					  { name : {{json .name}} }
119 | 			`,
120 | 			err: `unable to parse tool "example_tool" as kind "mongodb-delete-many"`,
121 | 		},
122 | 	}
123 | 	for _, tc := range tcs {
124 | 		t.Run(tc.desc, func(t *testing.T) {
125 | 			got := struct {
126 | 				Tools server.ToolConfigs `yaml:"tools"`
127 | 			}{}
128 | 			// Parse contents
129 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
130 | 			if err == nil {
131 | 				t.Fatalf("expect parsing to fail")
132 | 			}
133 | 			errStr := err.Error()
134 | 			if !strings.Contains(errStr, tc.err) {
135 | 				t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err)
136 | 			}
137 | 		})
138 | 	}
139 | 
140 | }
141 | 
```

--------------------------------------------------------------------------------
/internal/sources/mssql/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 mssql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 	"net/url"
 22 | 
 23 | 	"github.com/goccy/go-yaml"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 25 | 	"github.com/googleapis/genai-toolbox/internal/util"
 26 | 	_ "github.com/microsoft/go-mssqldb"
 27 | 	"go.opentelemetry.io/otel/trace"
 28 | )
 29 | 
 30 | const SourceKind string = "mssql"
 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 | 	// Cloud SQL MSSQL configs
 51 | 	Name     string `yaml:"name" validate:"required"`
 52 | 	Kind     string `yaml:"kind" validate:"required"`
 53 | 	Host     string `yaml:"host" validate:"required"`
 54 | 	Port     string `yaml:"port" validate:"required"`
 55 | 	User     string `yaml:"user" validate:"required"`
 56 | 	Password string `yaml:"password" validate:"required"`
 57 | 	Database string `yaml:"database" validate:"required"`
 58 | 	Encrypt  string `yaml:"encrypt"`
 59 | }
 60 | 
 61 | func (r Config) SourceConfigKind() string {
 62 | 	// Returns Cloud SQL MSSQL source kind
 63 | 	return SourceKind
 64 | }
 65 | 
 66 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 67 | 	// Initializes a MSSQL source
 68 | 	db, err := initMssqlConnection(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database, r.Encrypt)
 69 | 	if err != nil {
 70 | 		return nil, fmt.Errorf("unable to create db connection: %w", err)
 71 | 	}
 72 | 
 73 | 	// Verify db connection
 74 | 	err = db.PingContext(ctx)
 75 | 	if err != nil {
 76 | 		return nil, fmt.Errorf("unable to connect successfully: %w", err)
 77 | 	}
 78 | 
 79 | 	s := &Source{
 80 | 		Name: r.Name,
 81 | 		Kind: SourceKind,
 82 | 		Db:   db,
 83 | 	}
 84 | 	return s, nil
 85 | }
 86 | 
 87 | var _ sources.Source = &Source{}
 88 | 
 89 | type Source struct {
 90 | 	// Cloud SQL MSSQL struct with connection pool
 91 | 	Name string `yaml:"name"`
 92 | 	Kind string `yaml:"kind"`
 93 | 	Db   *sql.DB
 94 | }
 95 | 
 96 | func (s *Source) SourceKind() string {
 97 | 	// Returns Cloud SQL MSSQL source kind
 98 | 	return SourceKind
 99 | }
100 | 
101 | func (s *Source) MSSQLDB() *sql.DB {
102 | 	// Returns a Cloud SQL MSSQL database connection pool
103 | 	return s.Db
104 | }
105 | 
106 | func initMssqlConnection(
107 | 	ctx context.Context,
108 | 	tracer trace.Tracer,
109 | 	name, host, port, user, pass, dbname, encrypt string,
110 | ) (
111 | 	*sql.DB,
112 | 	error,
113 | ) {
114 | 	//nolint:all // Reassigned ctx
115 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
116 | 	defer span.End()
117 | 
118 | 	userAgent, err := util.UserAgentFromContext(ctx)
119 | 	if err != nil {
120 | 		userAgent = "genai-toolbox"
121 | 	}
122 | 	// Create dsn
123 | 	query := url.Values{}
124 | 	query.Add("app name", userAgent)
125 | 	query.Add("database", dbname)
126 | 	if encrypt != "" {
127 | 		query.Add("encrypt", encrypt)
128 | 	}
129 | 
130 | 	url := &url.URL{
131 | 		Scheme:   "sqlserver",
132 | 		User:     url.UserPassword(user, pass),
133 | 		Host:     fmt.Sprintf("%s:%s", host, port),
134 | 		RawQuery: query.Encode(),
135 | 	}
136 | 
137 | 	// Open database connection
138 | 	db, err := sql.Open("sqlserver", url.String())
139 | 	if err != nil {
140 | 		return nil, fmt.Errorf("sql.Open: %w", err)
141 | 	}
142 | 	return db, nil
143 | }
144 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/mongodb/mongodb-aggregate.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "mongodb-aggregate"
 3 | type: docs
 4 | weight: 1
 5 | description: > 
 6 |   A "mongodb-aggregate" tool executes a multi-stage aggregation pipeline against a MongoDB collection.
 7 | aliases:
 8 | - /resources/tools/mongodb-aggregate
 9 | ---
10 | 
11 | ## About
12 | 
13 | The `mongodb-aggregate` tool is the most powerful query tool for MongoDB,
14 | allowing you to process data through a multi-stage pipeline. Each stage
15 | transforms the documents as they pass through, enabling complex operations like
16 | grouping, filtering, reshaping documents, and performing calculations.
17 | 
18 | The core of this tool is the `pipelinePayload`, which must be a string
19 | containing a **JSON array of pipeline stage documents**. The tool returns a JSON
20 | array of documents produced by the final stage of the pipeline.
21 | 
22 | A `readOnly` flag can be set to `true` as a safety measure to ensure the
23 | pipeline does not contain any write stages (like `$out` or `$merge`).
24 | 
25 | This tool is compatible with the following source kind:
26 | 
27 | * [`mongodb`](../../sources/mongodb.md)
28 | 
29 | ## Example
30 | 
31 | Here is an example that calculates the average price and total count of products
32 | for each category, but only for products with an "active" status.
33 | 
34 | ```yaml
35 | tools:
36 |   get_category_stats:
37 |     kind: mongodb-aggregate
38 |     source: my-mongo-source
39 |     description: Calculates average price and count of products, grouped by category.
40 |     database: ecommerce
41 |     collection: products
42 |     readOnly: true
43 |     pipelinePayload: |
44 |       [
45 |         {
46 |           "$match": {
47 |             "status": {{json .status_filter}}
48 |           }
49 |         },
50 |         {
51 |           "$group": {
52 |             "_id": "$category",
53 |             "average_price": { "$avg": "$price" },
54 |             "item_count": { "$sum": 1 }
55 |           }
56 |         },
57 |         {
58 |           "$sort": {
59 |             "average_price": -1
60 |           }
61 |         }
62 |       ]
63 |     pipelineParams:
64 |       - name: status_filter
65 |         type: string
66 |         description: The product status to filter by (e.g., "active").
67 | ```
68 | 
69 | ## Reference
70 | 
71 | | **field**       | **type** | **required** | **description**                                                                                                |
72 | |:----------------|:---------|:-------------|:---------------------------------------------------------------------------------------------------------------|
73 | | kind            | string   | true         | Must be `mongodb-aggregate`.                                                                                   |
74 | | source          | string   | true         | The name of the `mongodb` source to use.                                                                       |
75 | | description     | string   | true         | A description of the tool that is passed to the LLM.                                                           |
76 | | database        | string   | true         | The name of the MongoDB database containing the collection.                                                    |
77 | | collection      | string   | true         | The name of the MongoDB collection to run the aggregation on.                                                  |
78 | | pipelinePayload | string   | true         | A JSON array of aggregation stage documents, provided as a string. Uses `{{json .param_name}}` for templating. |
79 | | pipelineParams  | list     | true         | A list of parameter objects that define the variables used in the `pipelinePayload`.                           |
80 | | canonical       | bool     | false        | Determines if the pipeline string is parsed using MongoDB's Canonical or Relaxed Extended JSON format.         |
81 | | readOnly        | bool     | false        | If `true`, the tool will fail if the pipeline contains write stages (`$out` or `$merge`). Defaults to `false`. |
82 | 
```

--------------------------------------------------------------------------------
/.github/workflows/deploy_previous_version_docs.yaml:
--------------------------------------------------------------------------------

```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 | #      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 | name: "Deploy Previous Version Docs"
 16 | 
 17 | on:
 18 |   workflow_dispatch:
 19 |     inputs:
 20 |       version_tag:
 21 |         description: 'The old version tag to build docs for (e.g., v0.15.0)'
 22 |         required: true
 23 |         type: string
 24 | 
 25 | jobs:
 26 |   build_and_deploy:
 27 |     runs-on: ubuntu-latest
 28 |     permissions:
 29 |       contents: write
 30 | 
 31 |     steps:
 32 |       - name: Checkout main branch (for latest templates and theme)
 33 |         uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
 34 |         with:
 35 |           ref: 'main'
 36 |           submodules: 'recursive'
 37 |           fetch-depth: 0
 38 | 
 39 |       - name: Checkout old content from tag into a temporary directory
 40 |         uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4
 41 |         with:
 42 |           ref: ${{ github.event.inputs.version_tag }}
 43 |           path: 'old_version_source' # Checkout into a temp subdir
 44 |           # Sparse checkout to only get the content directory
 45 |           sparse-checkout: |
 46 |             docs
 47 | 
 48 |       - name: Replace content with old version
 49 |         run: |
 50 |           # Remove the current content directory from the main branch checkout
 51 |           rm -rf docs/
 52 |           # Move the old content directory into place
 53 |           mv ./old_version_source/docs docs
 54 | 
 55 |       - name: Setup Hugo and Node
 56 |         uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3
 57 |         with:
 58 |           hugo-version: "0.145.0"
 59 |           extended: true
 60 |       - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
 61 |         with:
 62 |           node-version: "22"
 63 | 
 64 |       - name: Install Dependencies
 65 |         run: npm ci
 66 |         working-directory: .hugo
 67 | 
 68 |       - name: Build Hugo Site for Archived Version
 69 |         run: hugo --minify
 70 |         working-directory: .hugo
 71 |         env:
 72 |           HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ github.event.inputs.version_tag }}/
 73 |           HUGO_RELATIVEURLS: false
 74 | 
 75 |       - name: Deploy to gh-pages
 76 |         uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
 77 |         with:
 78 |           github_token: ${{ secrets.GITHUB_TOKEN }}
 79 |           publish_dir: .hugo/public
 80 |           publish_branch: versioned-gh-pages
 81 |           destination_dir: ./${{ github.event.inputs.version_tag }}
 82 |           keep_files: true
 83 |           allow_empty_commit: true
 84 |           commit_message: "docs(backport): deploy docs for ${{ github.event.inputs.version_tag }}"
 85 | 
 86 |       - name: Clean Build Directory
 87 |         run: rm -rf .hugo/public
 88 | 
 89 |       - name: Build Hugo Site
 90 |         run: hugo --minify
 91 |         working-directory: .hugo
 92 |         env:
 93 |           HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/
 94 |           HUGO_RELATIVEURLS: false
 95 | 
 96 |       - name: Deploy to root
 97 |         uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
 98 |         with:
 99 |           github_token: ${{ secrets.GITHUB_TOKEN }}
100 |           publish_dir: .hugo/public
101 |           publish_branch: versioned-gh-pages
102 |           keep_files: true
103 |           allow_empty_commit: true
104 |           commit_message: "deploy: docs to root for ${{ github.event.inputs.version_tag }}"
```

--------------------------------------------------------------------------------
/internal/sources/http/http.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 http
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"crypto/tls"
 19 | 	"fmt"
 20 | 	"net/http"
 21 | 	"net/url"
 22 | 	"time"
 23 | 
 24 | 	"github.com/goccy/go-yaml"
 25 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 26 | 	"github.com/googleapis/genai-toolbox/internal/util"
 27 | 	"go.opentelemetry.io/otel/trace"
 28 | )
 29 | 
 30 | const SourceKind string = "http"
 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, Timeout: "30s"} // Default timeout
 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 | 	BaseURL                string            `yaml:"baseUrl"`
 53 | 	Timeout                string            `yaml:"timeout"`
 54 | 	DefaultHeaders         map[string]string `yaml:"headers"`
 55 | 	QueryParams            map[string]string `yaml:"queryParams"`
 56 | 	DisableSslVerification bool              `yaml:"disableSslVerification"`
 57 | }
 58 | 
 59 | func (r Config) SourceConfigKind() string {
 60 | 	return SourceKind
 61 | }
 62 | 
 63 | // Initialize initializes an HTTP Source instance.
 64 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 65 | 	duration, err := time.ParseDuration(r.Timeout)
 66 | 	if err != nil {
 67 | 		return nil, fmt.Errorf("unable to parse Timeout string as time.Duration: %s", err)
 68 | 	}
 69 | 
 70 | 	tr := &http.Transport{}
 71 | 
 72 | 	logger, err := util.LoggerFromContext(ctx)
 73 | 	if err != nil {
 74 | 		return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
 75 | 	}
 76 | 
 77 | 	if r.DisableSslVerification {
 78 | 		tr.TLSClientConfig = &tls.Config{
 79 | 			InsecureSkipVerify: true,
 80 | 		}
 81 | 
 82 | 		logger.WarnContext(ctx, "Insecure HTTP is enabled for HTTP source %s. TLS certificate verification is skipped.\n", r.Name)
 83 | 	}
 84 | 
 85 | 	client := http.Client{
 86 | 		Timeout:   duration,
 87 | 		Transport: tr,
 88 | 	}
 89 | 
 90 | 	// Validate BaseURL
 91 | 	_, err = url.ParseRequestURI(r.BaseURL)
 92 | 	if err != nil {
 93 | 		return nil, fmt.Errorf("failed to parse BaseUrl %v", err)
 94 | 	}
 95 | 
 96 | 	ua, err := util.UserAgentFromContext(ctx)
 97 | 	if err != nil {
 98 | 		fmt.Printf("Error in User Agent retrieval: %s", err)
 99 | 	}
100 | 	if r.DefaultHeaders == nil {
101 | 		r.DefaultHeaders = make(map[string]string)
102 | 	}
103 | 	if existingUA, ok := r.DefaultHeaders["User-Agent"]; ok {
104 | 		ua = ua + " " + existingUA
105 | 	}
106 | 	r.DefaultHeaders["User-Agent"] = ua
107 | 
108 | 	s := &Source{
109 | 		Name:           r.Name,
110 | 		Kind:           SourceKind,
111 | 		BaseURL:        r.BaseURL,
112 | 		DefaultHeaders: r.DefaultHeaders,
113 | 		QueryParams:    r.QueryParams,
114 | 		Client:         &client,
115 | 	}
116 | 	return s, nil
117 | 
118 | }
119 | 
120 | var _ sources.Source = &Source{}
121 | 
122 | type Source struct {
123 | 	Name           string            `yaml:"name"`
124 | 	Kind           string            `yaml:"kind"`
125 | 	BaseURL        string            `yaml:"baseUrl"`
126 | 	DefaultHeaders map[string]string `yaml:"headers"`
127 | 	QueryParams    map[string]string `yaml:"queryParams"`
128 | 	Client         *http.Client
129 | }
130 | 
131 | func (s *Source) SourceKind() string {
132 | 	return SourceKind
133 | }
134 | 
```

--------------------------------------------------------------------------------
/internal/sources/mssql/mssql_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 mssql_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/mssql"
 24 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 25 | )
 26 | 
 27 | func TestParseFromYamlMssql(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-mssql-instance:
 38 | 					kind: mssql
 39 | 					host: 0.0.0.0
 40 | 					port: my-port
 41 | 					database: my_db
 42 | 					user: my_user
 43 | 					password: my_pass
 44 | 			`,
 45 | 			want: server.SourceConfigs{
 46 | 				"my-mssql-instance": mssql.Config{
 47 | 					Name:     "my-mssql-instance",
 48 | 					Kind:     mssql.SourceKind,
 49 | 					Host:     "0.0.0.0",
 50 | 					Port:     "my-port",
 51 | 					Database: "my_db",
 52 | 					User:     "my_user",
 53 | 					Password: "my_pass",
 54 | 				},
 55 | 			},
 56 | 		},
 57 | 		{
 58 | 			desc: "with encrypt field",
 59 | 			in: `
 60 | 			sources:
 61 | 				my-mssql-instance:
 62 | 					kind: mssql
 63 | 					host: 0.0.0.0
 64 | 					port: my-port
 65 | 					database: my_db
 66 | 					user: my_user
 67 | 					password: my_pass
 68 | 					encrypt: strict
 69 | 			`,
 70 | 			want: server.SourceConfigs{
 71 | 				"my-mssql-instance": mssql.Config{
 72 | 					Name:     "my-mssql-instance",
 73 | 					Kind:     mssql.SourceKind,
 74 | 					Host:     "0.0.0.0",
 75 | 					Port:     "my-port",
 76 | 					Database: "my_db",
 77 | 					User:     "my_user",
 78 | 					Password: "my_pass",
 79 | 					Encrypt:  "strict",
 80 | 				},
 81 | 			},
 82 | 		},
 83 | 	}
 84 | 	for _, tc := range tcs {
 85 | 		t.Run(tc.desc, func(t *testing.T) {
 86 | 			got := struct {
 87 | 				Sources server.SourceConfigs `yaml:"sources"`
 88 | 			}{}
 89 | 			// Parse contents
 90 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
 91 | 			if err != nil {
 92 | 				t.Fatalf("unable to unmarshal: %s", err)
 93 | 			}
 94 | 			if !cmp.Equal(tc.want, got.Sources) {
 95 | 				t.Fatalf("incorrect psarse: want %v, got %v", tc.want, got.Sources)
 96 | 			}
 97 | 		})
 98 | 	}
 99 | }
100 | 
101 | func TestFailParseFromYaml(t *testing.T) {
102 | 	tcs := []struct {
103 | 		desc string
104 | 		in   string
105 | 		err  string
106 | 	}{
107 | 		{
108 | 			desc: "extra field",
109 | 			in: `
110 | 			sources:
111 | 				my-mssql-instance:
112 | 					kind: mssql
113 | 					host: 0.0.0.0
114 | 					port: my-port
115 | 					database: my_db
116 | 					user: my_user
117 | 					password: my_pass
118 | 					foo: bar
119 | 			`,
120 | 			err: "unable to parse source \"my-mssql-instance\" as \"mssql\": [2:1] unknown field \"foo\"\n   1 | database: my_db\n>  2 | foo: bar\n       ^\n   3 | host: 0.0.0.0\n   4 | kind: mssql\n   5 | password: my_pass\n   6 | ",
121 | 		},
122 | 		{
123 | 			desc: "missing required field",
124 | 			in: `
125 | 			sources:
126 | 				my-mssql-instance:
127 | 					kind: mssql
128 | 					host: 0.0.0.0
129 | 					port: my-port
130 | 					database: my_db
131 | 					user: my_user
132 | 			`,
133 | 			err: "unable to parse source \"my-mssql-instance\" as \"mssql\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
134 | 		},
135 | 	}
136 | 	for _, tc := range tcs {
137 | 		t.Run(tc.desc, func(t *testing.T) {
138 | 			got := struct {
139 | 				Sources server.SourceConfigs `yaml:"sources"`
140 | 			}{}
141 | 			// Parse contents
142 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
143 | 			if err == nil {
144 | 				t.Fatalf("expect parsing to fail")
145 | 			}
146 | 			errStr := err.Error()
147 | 			if errStr != tc.err {
148 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
149 | 			}
150 | 		})
151 | 	}
152 | }
153 | 
```

--------------------------------------------------------------------------------
/internal/tools/mongodb/mongodbaggregate/mongodbaggregate_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 mongodbaggregate_test
 16 | 
 17 | import (
 18 | 	"strings"
 19 | 	"testing"
 20 | 
 21 | 	"github.com/googleapis/genai-toolbox/internal/tools/mongodb/mongodbaggregate"
 22 | 
 23 | 	yaml "github.com/goccy/go-yaml"
 24 | 	"github.com/google/go-cmp/cmp"
 25 | 	"github.com/googleapis/genai-toolbox/internal/server"
 26 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 27 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 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-aggregate
 46 | 					source: my-instance
 47 | 					description: some description
 48 | 					database: test_db
 49 | 					collection: test_coll
 50 | 					readOnly: true
 51 | 					pipelinePayload: |
 52 | 					    [{ $match: { name: {{json .name}} }}]
 53 | 					pipelineParams:
 54 |                         - name: name 
 55 |                           type: string
 56 |                           description: small description
 57 | 			`,
 58 | 			want: server.ToolConfigs{
 59 | 				"example_tool": mongodbaggregate.Config{
 60 | 					Name:            "example_tool",
 61 | 					Kind:            "mongodb-aggregate",
 62 | 					Source:          "my-instance",
 63 | 					AuthRequired:    []string{},
 64 | 					Database:        "test_db",
 65 | 					Collection:      "test_coll",
 66 | 					Description:     "some description",
 67 | 					PipelinePayload: "[{ $match: { name: {{json .name}} }}]\n",
 68 | 					PipelineParams: tools.Parameters{
 69 | 						&tools.StringParameter{
 70 | 							CommonParameter: tools.CommonParameter{
 71 | 								Name: "name",
 72 | 								Type: "string",
 73 | 								Desc: "small description",
 74 | 							},
 75 | 						},
 76 | 					},
 77 | 					ReadOnly: true,
 78 | 				},
 79 | 			},
 80 | 		},
 81 | 	}
 82 | 	for _, tc := range tcs {
 83 | 		t.Run(tc.desc, func(t *testing.T) {
 84 | 			got := struct {
 85 | 				Tools server.ToolConfigs `yaml:"tools"`
 86 | 			}{}
 87 | 			// Parse contents
 88 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 89 | 			if err != nil {
 90 | 				t.Fatalf("unable to unmarshal: %s", err)
 91 | 			}
 92 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 93 | 				t.Fatalf("incorrect parse: diff %v", diff)
 94 | 			}
 95 | 		})
 96 | 	}
 97 | 
 98 | }
 99 | 
100 | func TestFailParseFromYamlMongoQuery(t *testing.T) {
101 | 	ctx, err := testutils.ContextWithNewLogger()
102 | 	if err != nil {
103 | 		t.Fatalf("unexpected error: %s", err)
104 | 	}
105 | 	tcs := []struct {
106 | 		desc string
107 | 		in   string
108 | 		err  string
109 | 	}{
110 | 		{
111 | 			desc: "Invalid method",
112 | 			in: `
113 | 			tools:
114 | 				example_tool:
115 | 					kind: mongodb-aggregate
116 | 					source: my-instance
117 | 					description: some description
118 | 					collection: test_coll
119 | 					pipelinePayload: |
120 | 					  [{ $match: { name : {{json .name}} }}]
121 | 			`,
122 | 			err: `unable to parse tool "example_tool" as kind "mongodb-aggregate"`,
123 | 		},
124 | 	}
125 | 	for _, tc := range tcs {
126 | 		t.Run(tc.desc, func(t *testing.T) {
127 | 			got := struct {
128 | 				Tools server.ToolConfigs `yaml:"tools"`
129 | 			}{}
130 | 			// Parse contents
131 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
132 | 			if err == nil {
133 | 				t.Fatalf("expect parsing to fail")
134 | 			}
135 | 			errStr := err.Error()
136 | 			if !strings.Contains(errStr, tc.err) {
137 | 				t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err)
138 | 			}
139 | 		})
140 | 	}
141 | 
142 | }
143 | 
```

--------------------------------------------------------------------------------
/internal/sources/redis/redis_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 redis_test
 16 | 
 17 | import (
 18 | 	"strings"
 19 | 	"testing"
 20 | 
 21 | 	yaml "github.com/goccy/go-yaml"
 22 | 	"github.com/google/go-cmp/cmp"
 23 | 	"github.com/googleapis/genai-toolbox/internal/server"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/redis"
 25 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 26 | )
 27 | 
 28 | func TestParseFromYamlRedis(t *testing.T) {
 29 | 	tcs := []struct {
 30 | 		desc string
 31 | 		in   string
 32 | 		want server.SourceConfigs
 33 | 	}{
 34 | 		{
 35 | 			desc: "default setting",
 36 | 			in: `
 37 | 			sources:
 38 | 				my-redis-instance:
 39 | 					kind: redis
 40 | 					address:
 41 | 					  - 127.0.0.1
 42 | 			`,
 43 | 			want: server.SourceConfigs{
 44 | 				"my-redis-instance": redis.Config{
 45 | 					Name:           "my-redis-instance",
 46 | 					Kind:           redis.SourceKind,
 47 | 					Address:        []string{"127.0.0.1"},
 48 | 					ClusterEnabled: false,
 49 | 					UseGCPIAM:      false,
 50 | 				},
 51 | 			},
 52 | 		},
 53 | 		{
 54 | 			desc: "advanced example",
 55 | 			in: `
 56 | 			sources:
 57 | 				my-redis-instance:
 58 | 					kind: redis
 59 | 					address:
 60 | 					  - 127.0.0.1
 61 | 					password: my-pass
 62 | 					database: 1
 63 | 					useGCPIAM: true
 64 | 					clusterEnabled: true
 65 | 			`,
 66 | 			want: server.SourceConfigs{
 67 | 				"my-redis-instance": redis.Config{
 68 | 					Name:           "my-redis-instance",
 69 | 					Kind:           redis.SourceKind,
 70 | 					Address:        []string{"127.0.0.1"},
 71 | 					Password:       "my-pass",
 72 | 					Database:       1,
 73 | 					ClusterEnabled: true,
 74 | 					UseGCPIAM:      true,
 75 | 				},
 76 | 			},
 77 | 		},
 78 | 	}
 79 | 	for _, tc := range tcs {
 80 | 		t.Run(tc.desc, func(t *testing.T) {
 81 | 			got := struct {
 82 | 				Sources server.SourceConfigs `yaml:"sources"`
 83 | 			}{}
 84 | 			// Parse contents
 85 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
 86 | 			if err != nil {
 87 | 				t.Fatalf("unable to unmarshal: %s", err)
 88 | 			}
 89 | 			if !cmp.Equal(tc.want, got.Sources) {
 90 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
 91 | 			}
 92 | 		})
 93 | 	}
 94 | 
 95 | }
 96 | 
 97 | func TestFailParseFromYaml(t *testing.T) {
 98 | 	tcs := []struct {
 99 | 		desc string
100 | 		in   string
101 | 		err  string
102 | 	}{
103 | 		{
104 | 			desc: "invalid database",
105 | 			in: `
106 | 			sources:
107 | 				my-redis-instance:
108 | 					kind: redis
109 | 					project: my-project
110 | 					address:
111 | 					  - 127.0.0.1
112 | 					password: my-pass
113 | 					database: data
114 | 			`,
115 | 			err: "cannot unmarshal string into Go struct field .Sources of type int",
116 | 		},
117 | 		{
118 | 			desc: "extra field",
119 | 			in: `
120 | 			sources:
121 | 				my-redis-instance:
122 | 					kind: redis
123 | 					project: my-project
124 | 					address:
125 | 					  - 127.0.0.1
126 | 					password: my-pass
127 | 					database: 1
128 | 			`,
129 | 			err: "unable to parse source \"my-redis-instance\" as \"redis\": [6:1] unknown field \"project\"",
130 | 		},
131 | 		{
132 | 			desc: "missing required field",
133 | 			in: `
134 | 			sources:
135 | 				my-redis-instance:
136 | 					kind: redis
137 | 			`,
138 | 			err: "unable to parse source \"my-redis-instance\" as \"redis\": Key: 'Config.Address' Error:Field validation for 'Address' failed on the 'required' tag",
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 !strings.Contains(errStr, tc.err) {
153 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
154 | 			}
155 | 		})
156 | 	}
157 | }
158 | 
```

--------------------------------------------------------------------------------
/.github/workflows/docs_preview_deploy.yaml:
--------------------------------------------------------------------------------

```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 | #      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 | name: "docs"
 16 | 
 17 | permissions:
 18 |   contents: write
 19 |   pull-requests: write
 20 |   
 21 | # This Workflow depends on 'github.event.number',
 22 | # not compatible with branch or manual triggers.
 23 | on:
 24 |   pull_request:
 25 |     # Sync with github_actions_preview_fallback.yml on.pull_request.paths-ignore
 26 |     paths:
 27 |       - 'docs/**'
 28 |       - 'github/workflows/docs**'
 29 |       - '.hugo/**'
 30 |   pull_request_target:
 31 |     types: [labeled]
 32 |     paths:
 33 |       - 'docs/**'
 34 |       - 'github/workflows/docs**'
 35 |       - '.hugo/**'
 36 | 
 37 | jobs:
 38 |   preview:
 39 |     # run job on proper workflow event triggers (skip job for pull_request event
 40 |     # from forks and only run pull_request_target for "docs: deploy-preview"
 41 |     # label)
 42 |     if: "${{ (github.event.action != 'labeled' && github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name) || github.event.label.name == 'docs: deploy-preview' }}"
 43 |     runs-on: ubuntu-24.04
 44 |     defaults:
 45 |       run:
 46 |         working-directory: .hugo
 47 |     concurrency:
 48 |       # Shared concurrency group wih preview cleanup.
 49 |       group: "preview-${{ github.event.number }}"
 50 |       cancel-in-progress: true
 51 |     steps:
 52 |       - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
 53 |         with:
 54 |           # Checkout the PR's HEAD commit (supports forks).
 55 |           ref: ${{ github.event.pull_request.head.sha }}
 56 |           fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
 57 | 
 58 |       - name: Setup Hugo
 59 |         uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3
 60 |         with:
 61 |           hugo-version: "0.145.0"
 62 |           extended: true
 63 | 
 64 |       - name: Setup Node
 65 |         uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
 66 |         with:
 67 |           node-version: "22"
 68 | 
 69 |       - name: Cache dependencies
 70 |         uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
 71 |         with:
 72 |           path: ~/.npm
 73 |           key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
 74 |           restore-keys: |
 75 |             ${{ runner.os }}-node-
 76 | 
 77 |       - run: npm ci
 78 |       - run: hugo --minify
 79 |         env:
 80 |           HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/previews/PR-${{ github.event.number }}/
 81 |           HUGO_ENVIRONMENT: preview
 82 |           HUGO_RELATIVEURLS: false
 83 | 
 84 |       - name: Deploy
 85 |         uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
 86 |         with:
 87 |           github_token: ${{ secrets.GITHUB_TOKEN }}
 88 |           publish_dir: .hugo/public
 89 |           publish_branch: versioned-gh-pages
 90 |           destination_dir: ./previews/PR-${{ github.event.number }}
 91 |           commit_message: "stage: PR-${{ github.event.number }}: ${{ github.event.head_commit.message }}"
 92 | 
 93 |       - name: Comment
 94 |         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
 95 |         with:
 96 |           script: |
 97 |             github.rest.issues.createComment({
 98 |               issue_number: context.payload.number,
 99 |               owner: context.repo.owner,
100 |               repo: context.repo.repo,
101 |               body: "🔎 Preview at https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/previews/PR-${{ github.event.number }}/"
102 |             })
103 | 
```

--------------------------------------------------------------------------------
/internal/sources/clickhouse/clickhouse.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 | 	"net/url"
 22 | 	"time"
 23 | 
 24 | 	_ "github.com/ClickHouse/clickhouse-go/v2"
 25 | 	"github.com/goccy/go-yaml"
 26 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 27 | 	"go.opentelemetry.io/otel/trace"
 28 | )
 29 | 
 30 | const SourceKind string = "clickhouse"
 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 | 	Name     string `yaml:"name" validate:"required"`
 51 | 	Kind     string `yaml:"kind" validate:"required"`
 52 | 	Host     string `yaml:"host" validate:"required"`
 53 | 	Port     string `yaml:"port" validate:"required"`
 54 | 	Database string `yaml:"database" validate:"required"`
 55 | 	User     string `yaml:"user" validate:"required"`
 56 | 	Password string `yaml:"password"`
 57 | 	Protocol string `yaml:"protocol"`
 58 | 	Secure   bool   `yaml:"secure"`
 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 := initClickHouseConnectionPool(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database, r.Protocol, r.Secure)
 67 | 	if err != nil {
 68 | 		return nil, fmt.Errorf("unable to create pool: %w", err)
 69 | 	}
 70 | 
 71 | 	err = pool.PingContext(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 *sql.DB
 90 | }
 91 | 
 92 | func (s *Source) SourceKind() string {
 93 | 	return SourceKind
 94 | }
 95 | 
 96 | func (s *Source) ClickHousePool() *sql.DB {
 97 | 	return s.Pool
 98 | }
 99 | 
100 | func validateConfig(protocol string) error {
101 | 	validProtocols := map[string]bool{"http": true, "https": true}
102 | 
103 | 	if protocol != "" && !validProtocols[protocol] {
104 | 		return fmt.Errorf("invalid protocol: %s, must be one of: http, https", protocol)
105 | 	}
106 | 	return nil
107 | }
108 | 
109 | func initClickHouseConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname, protocol string, secure bool) (*sql.DB, error) {
110 | 	//nolint:all // Reassigned ctx
111 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
112 | 	defer span.End()
113 | 
114 | 	if protocol == "" {
115 | 		protocol = "https"
116 | 	}
117 | 
118 | 	if err := validateConfig(protocol); err != nil {
119 | 		return nil, err
120 | 	}
121 | 
122 | 	encodedUser := url.QueryEscape(user)
123 | 	encodedPass := url.QueryEscape(pass)
124 | 
125 | 	var dsn string
126 | 	scheme := protocol
127 | 	if protocol == "http" && secure {
128 | 		scheme = "https"
129 | 	}
130 | 	dsn = fmt.Sprintf("%s://%s:%s@%s:%s/%s", scheme, encodedUser, encodedPass, host, port, dbname)
131 | 	if scheme == "https" {
132 | 		dsn += "?secure=true&skip_verify=false"
133 | 	}
134 | 
135 | 	pool, err := sql.Open("clickhouse", dsn)
136 | 	if err != nil {
137 | 		return nil, fmt.Errorf("sql.Open: %w", err)
138 | 	}
139 | 
140 | 	pool.SetMaxOpenConns(25)
141 | 	pool.SetMaxIdleConns(5)
142 | 	pool.SetConnMaxLifetime(5 * time.Minute)
143 | 
144 | 	return pool, nil
145 | }
146 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/mongodb/mongodb-find-one.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "mongodb-find-one"
 3 | type: docs
 4 | weight: 1
 5 | description: > 
 6 |   A "mongodb-find-one" tool finds and retrieves a single document from a MongoDB collection.
 7 | aliases:
 8 | - /resources/tools/mongodb-find-one
 9 | ---
10 | 
11 | ## About
12 | 
13 | A `mongodb-find-one` tool is used to retrieve the **first single document** that
14 | matches a specified filter from a MongoDB collection. If multiple documents
15 | match the filter, you can use `sort` options to control which document is
16 | returned. Otherwise, the selection is not guaranteed.
17 | 
18 | The tool returns a single JSON object representing the document, wrapped in a
19 | JSON array.
20 | 
21 | This tool is compatible with the following source kind:
22 | 
23 | * [`mongodb`](../../sources/mongodb.md)
24 | 
25 | ---
26 | 
27 | ## Example
28 | 
29 | Here's a common use case: finding a specific user by their unique email address
30 | and returning their profile information, while excluding sensitive fields like
31 | the password hash.
32 | 
33 | ```yaml
34 | tools:
35 |   get_user_profile:
36 |     kind: mongodb-find-one
37 |     source: my-mongo-source
38 |     description: Retrieves a user's profile by their email address.
39 |     database: user_data
40 |     collection: profiles
41 |     filterPayload: |
42 |         { "email": {{json .email}} }
43 |     filterParams:
44 |       - name: email
45 |         type: string
46 |         description: The email address of the user to find.
47 |     projectPayload: |
48 |         { 
49 |           "password_hash": 0,
50 |           "login_history": 0
51 |         }
52 | ```
53 | 
54 | ## Reference
55 | 
56 | | **field**      | **type** | **required** | **description**                                                                                                                              |
57 | |:---------------|:---------|:-------------|:---------------------------------------------------------------------------------------------------------------------------------------------|
58 | | kind           | string   | true         | Must be `mongodb-find-one`.                                                                                                                  |
59 | | source         | string   | true         | The name of the `mongodb` source to use.                                                                                                     |
60 | | description    | string   | true         | A description of the tool that is passed to the LLM.                                                                                         |
61 | | database       | string   | true         | The name of the MongoDB database to query.                                                                                                   |
62 | | collection     | string   | true         | The name of the MongoDB collection to query.                                                                                                 |
63 | | filterPayload  | string   | true         | The MongoDB query filter document to select the document. Uses `{{json .param_name}}` for templating.                                        |
64 | | filterParams   | list     | true         | A list of parameter objects that define the variables used in the `filterPayload`.                                                           |
65 | | projectPayload | string   | false        | An optional MongoDB projection document to specify which fields to include (1) or exclude (0) in the result.                                 |
66 | | projectParams  | list     | false        | A list of parameter objects for the `projectPayload`.                                                                                        |
67 | | sortPayload    | string   | false        | An optional MongoDB sort document. Useful for selecting which document to return if the filter matches multiple (e.g., get the most recent). |
68 | | sortParams     | list     | false        | A list of parameter objects for the `sortPayload`.                                                                                           |
69 | 
```
Page 8/45FirstPrevNextLast