#
tokens: 48287/50000 17/807 files (page 17/47)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 17 of 47. 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-create-project-file.md
│       │       │   ├── looker-delete-project-file.md
│       │       │   ├── looker-dev-mode.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-get-project-file.md
│       │       │   ├── looker-get-project-files.md
│       │       │   ├── looker-get-projects.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
│       │       │   └── looker-update-project-file.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
│   │   │   ├── lookercreateprojectfile
│   │   │   │   ├── lookercreateprojectfile_test.go
│   │   │   │   └── lookercreateprojectfile.go
│   │   │   ├── lookerdeleteprojectfile
│   │   │   │   ├── lookerdeleteprojectfile_test.go
│   │   │   │   └── lookerdeleteprojectfile.go
│   │   │   ├── lookerdevmode
│   │   │   │   ├── lookerdevmode_test.go
│   │   │   │   └── lookerdevmode.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
│   │   │   ├── lookergetprojectfile
│   │   │   │   ├── lookergetprojectfile_test.go
│   │   │   │   └── lookergetprojectfile.go
│   │   │   ├── lookergetprojectfiles
│   │   │   │   ├── lookergetprojectfiles_test.go
│   │   │   │   └── lookergetprojectfiles.go
│   │   │   ├── lookergetprojects
│   │   │   │   ├── lookergetprojects_test.go
│   │   │   │   └── lookergetprojects.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
│   │   │   └── lookerupdateprojectfile
│   │   │       ├── lookerupdateprojectfile_test.go
│   │   │       └── lookerupdateprojectfile.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/cassandra/cassandracql/cassandracql.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 cassandracql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	yaml "github.com/goccy/go-yaml"
 22 | 	"github.com/gocql/gocql"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/cassandra"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | )
 27 | 
 28 | const kind string = "cassandra-cql"
 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 compatibleSource interface {
 45 | 	CassandraSession() *gocql.Session
 46 | }
 47 | 
 48 | var _ compatibleSource = &cassandra.Source{}
 49 | 
 50 | var compatibleSources = [...]string{cassandra.SourceKind}
 51 | 
 52 | type Config struct {
 53 | 	Name               string           `yaml:"name" validate:"required"`
 54 | 	Kind               string           `yaml:"kind" validate:"required"`
 55 | 	Source             string           `yaml:"source" validate:"required"`
 56 | 	Description        string           `yaml:"description" validate:"required"`
 57 | 	Statement          string           `yaml:"statement" validate:"required"`
 58 | 	AuthRequired       []string         `yaml:"authRequired"`
 59 | 	Parameters         tools.Parameters `yaml:"parameters"`
 60 | 	TemplateParameters tools.Parameters `yaml:"templateParameters"`
 61 | }
 62 | 
 63 | // Initialize implements tools.ToolConfig.
 64 | func (c Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 65 | 	// verify source exists
 66 | 	rawS, ok := srcs[c.Source]
 67 | 	if !ok {
 68 | 		return nil, fmt.Errorf("no source named %q configured", c.Source)
 69 | 	}
 70 | 
 71 | 	// verify the source is compatible
 72 | 	s, ok := rawS.(compatibleSource)
 73 | 	if !ok {
 74 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 75 | 	}
 76 | 
 77 | 	allParameters, paramManifest, err := tools.ProcessParameters(c.TemplateParameters, c.Parameters)
 78 | 	if err != nil {
 79 | 		return nil, err
 80 | 	}
 81 | 
 82 | 	mcpManifest := tools.GetMcpManifest(c.Name, c.Description, c.AuthRequired, allParameters)
 83 | 
 84 | 	t := Tool{
 85 | 		Name:               c.Name,
 86 | 		Kind:               kind,
 87 | 		Parameters:         c.Parameters,
 88 | 		TemplateParameters: c.TemplateParameters,
 89 | 		AllParams:          allParameters,
 90 | 		Statement:          c.Statement,
 91 | 		AuthRequired:       c.AuthRequired,
 92 | 		Session:            s.CassandraSession(),
 93 | 		manifest:           tools.Manifest{Description: c.Description, Parameters: paramManifest, AuthRequired: c.AuthRequired},
 94 | 		mcpManifest:        mcpManifest,
 95 | 	}
 96 | 	return t, nil
 97 | }
 98 | 
 99 | // ToolConfigKind implements tools.ToolConfig.
100 | func (c Config) ToolConfigKind() string {
101 | 	return kind
102 | }
103 | 
104 | var _ tools.ToolConfig = Config{}
105 | 
106 | type Tool struct {
107 | 	Name               string           `yaml:"name"`
108 | 	Kind               string           `yaml:"kind"`
109 | 	AuthRequired       []string         `yaml:"authRequired"`
110 | 	Parameters         tools.Parameters `yaml:"parameters"`
111 | 	TemplateParameters tools.Parameters `yaml:"templateParameters"`
112 | 	AllParams          tools.Parameters `yaml:"allParams"`
113 | 
114 | 	Session     *gocql.Session
115 | 	Statement   string
116 | 	manifest    tools.Manifest
117 | 	mcpManifest tools.McpManifest
118 | }
119 | 
120 | // RequiresClientAuthorization implements tools.Tool.
121 | func (t Tool) RequiresClientAuthorization() bool {
122 | 	return false
123 | }
124 | 
125 | // Authorized implements tools.Tool.
126 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
127 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
128 | }
129 | 
130 | // Invoke implements tools.Tool.
131 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
132 | 	paramsMap := params.AsMap()
133 | 	newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
134 | 	if err != nil {
135 | 		return nil, fmt.Errorf("unable to extract template params %w", err)
136 | 	}
137 | 
138 | 	newParams, err := tools.GetParams(t.Parameters, paramsMap)
139 | 	if err != nil {
140 | 		return nil, fmt.Errorf("unable to extract standard params %w", err)
141 | 	}
142 | 	sliceParams := newParams.AsSlice()
143 | 	iter := t.Session.Query(newStatement, sliceParams...).WithContext(ctx).Iter()
144 | 
145 | 	// Create a slice to store the out
146 | 	var out []map[string]interface{}
147 | 
148 | 	// Scan results into a map and append to the slice
149 | 	for {
150 | 		row := make(map[string]interface{}) // Create a new map for each row
151 | 		if !iter.MapScan(row) {
152 | 			break // No more rows
153 | 		}
154 | 		out = append(out, row)
155 | 	}
156 | 
157 | 	if err := iter.Close(); err != nil {
158 | 		return nil, fmt.Errorf("unable to parse rows: %w", err)
159 | 	}
160 | 	return out, nil
161 | }
162 | 
163 | // Manifest implements tools.Tool.
164 | func (t Tool) Manifest() tools.Manifest {
165 | 	return t.manifest
166 | }
167 | 
168 | // McpManifest implements tools.Tool.
169 | func (t Tool) McpManifest() tools.McpManifest {
170 | 	return t.mcpManifest
171 | }
172 | 
173 | // ParseParams implements tools.Tool.
174 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
175 | 	return tools.ParseParams(t.AllParams, data, claims)
176 | }
177 | 
178 | var _ tools.Tool = Tool{}
179 | 
```

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

```go
  1 | // Copyright 2024 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package couchbase
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"encoding/json"
 20 | 	"fmt"
 21 | 
 22 | 	"github.com/couchbase/gocb/v2"
 23 | 	yaml "github.com/goccy/go-yaml"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 25 | 	"github.com/googleapis/genai-toolbox/internal/sources/couchbase"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 27 | )
 28 | 
 29 | const kind string = "couchbase-sql"
 30 | 
 31 | func init() {
 32 | 	if !tools.Register(kind, newConfig) {
 33 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 34 | 	}
 35 | }
 36 | 
 37 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 38 | 	actual := Config{Name: name}
 39 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 40 | 		return nil, err
 41 | 	}
 42 | 	return actual, nil
 43 | }
 44 | 
 45 | type compatibleSource interface {
 46 | 	CouchbaseScope() *gocb.Scope
 47 | 	CouchbaseQueryScanConsistency() uint
 48 | }
 49 | 
 50 | // validate compatible sources are still compatible
 51 | var _ compatibleSource = &couchbase.Source{}
 52 | 
 53 | var compatibleSources = [...]string{couchbase.SourceKind}
 54 | 
 55 | type Config struct {
 56 | 	Name               string           `yaml:"name" validate:"required"`
 57 | 	Kind               string           `yaml:"kind" validate:"required"`
 58 | 	Source             string           `yaml:"source" validate:"required"`
 59 | 	Description        string           `yaml:"description" validate:"required"`
 60 | 	Statement          string           `yaml:"statement" validate:"required"`
 61 | 	AuthRequired       []string         `yaml:"authRequired"`
 62 | 	Parameters         tools.Parameters `yaml:"parameters"`
 63 | 	TemplateParameters tools.Parameters `yaml:"templateParameters"`
 64 | }
 65 | 
 66 | // validate interface
 67 | var _ tools.ToolConfig = Config{}
 68 | 
 69 | func (cfg Config) ToolConfigKind() string {
 70 | 	return kind
 71 | }
 72 | 
 73 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 74 | 	// verify source exists
 75 | 	rawS, ok := srcs[cfg.Source]
 76 | 	if !ok {
 77 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 78 | 	}
 79 | 
 80 | 	// verify the source is compatible
 81 | 	s, ok := rawS.(compatibleSource)
 82 | 	if !ok {
 83 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 84 | 	}
 85 | 
 86 | 	allParameters, paramManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
 87 | 	if err != nil {
 88 | 		return nil, err
 89 | 	}
 90 | 
 91 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 92 | 	// finish tool setup
 93 | 	t := Tool{
 94 | 		Name:                 cfg.Name,
 95 | 		Kind:                 kind,
 96 | 		Parameters:           cfg.Parameters,
 97 | 		TemplateParameters:   cfg.TemplateParameters,
 98 | 		AllParams:            allParameters,
 99 | 		Statement:            cfg.Statement,
100 | 		Scope:                s.CouchbaseScope(),
101 | 		QueryScanConsistency: s.CouchbaseQueryScanConsistency(),
102 | 		AuthRequired:         cfg.AuthRequired,
103 | 		manifest:             tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
104 | 		mcpManifest:          mcpManifest,
105 | 	}
106 | 	return t, nil
107 | }
108 | 
109 | // validate interface
110 | var _ tools.Tool = Tool{}
111 | 
112 | type Tool struct {
113 | 	Name               string           `yaml:"name"`
114 | 	Kind               string           `yaml:"kind"`
115 | 	Parameters         tools.Parameters `yaml:"parameters"`
116 | 	TemplateParameters tools.Parameters `yaml:"templateParameters"`
117 | 	AllParams          tools.Parameters `yaml:"allParams"`
118 | 	AuthRequired       []string         `yaml:"authRequired"`
119 | 
120 | 	Scope                *gocb.Scope
121 | 	QueryScanConsistency uint
122 | 	Statement            string
123 | 	manifest             tools.Manifest
124 | 	mcpManifest          tools.McpManifest
125 | }
126 | 
127 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
128 | 	namedParamsMap := params.AsMap()
129 | 	newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, namedParamsMap)
130 | 	if err != nil {
131 | 		return nil, fmt.Errorf("unable to extract template params %w", err)
132 | 	}
133 | 
134 | 	newParams, err := tools.GetParams(t.Parameters, namedParamsMap)
135 | 	if err != nil {
136 | 		return nil, fmt.Errorf("unable to extract standard params %w", err)
137 | 	}
138 | 	results, err := t.Scope.Query(newStatement, &gocb.QueryOptions{
139 | 		ScanConsistency: gocb.QueryScanConsistency(t.QueryScanConsistency),
140 | 		NamedParameters: newParams.AsMap(),
141 | 	})
142 | 	if err != nil {
143 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
144 | 	}
145 | 
146 | 	var out []any
147 | 	for results.Next() {
148 | 		var result json.RawMessage
149 | 		err := results.Row(&result)
150 | 		if err != nil {
151 | 			return nil, fmt.Errorf("error processing row: %w", err)
152 | 		}
153 | 		out = append(out, result)
154 | 	}
155 | 	return out, nil
156 | }
157 | 
158 | func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (tools.ParamValues, error) {
159 | 	return tools.ParseParams(t.AllParams, data, claimsMap)
160 | }
161 | 
162 | func (t Tool) Manifest() tools.Manifest {
163 | 	return t.manifest
164 | }
165 | 
166 | func (t Tool) McpManifest() tools.McpManifest {
167 | 	return t.mcpManifest
168 | }
169 | 
170 | func (t Tool) Authorized(verifiedAuthSources []string) bool {
171 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthSources)
172 | }
173 | 
174 | func (t Tool) RequiresClientAuthorization() bool {
175 | 	return false
176 | }
177 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/cloud-sql-mysql.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "Cloud SQL for MySQL"
  3 | linkTitle: "Cloud SQL (MySQL)"
  4 | type: docs
  5 | weight: 1
  6 | description: >
  7 |   Cloud SQL for MySQL is a fully-managed database service for MySQL.
  8 | 
  9 | ---
 10 | 
 11 | ## About
 12 | 
 13 | [Cloud SQL for MySQL][csql-mysql-docs] is a fully-managed database service
 14 | that helps you set up, maintain, manage, and administer your MySQL
 15 | relational databases on Google Cloud Platform.
 16 | 
 17 | If you are new to Cloud SQL for MySQL, you can try [creating and connecting
 18 | to a database by following these instructions][csql-mysql-quickstart].
 19 | 
 20 | [csql-mysql-docs]: https://cloud.google.com/sql/docs/mysql
 21 | [csql-mysql-quickstart]: https://cloud.google.com/sql/docs/mysql/connect-instance-local-computer
 22 | 
 23 | ## Available Tools
 24 | 
 25 | - [`mysql-sql`](../tools/mysql/mysql-sql.md)
 26 |   Execute pre-defined prepared SQL queries in Cloud SQL for MySQL.
 27 | 
 28 | - [`mysql-execute-sql`](../tools/mysql/mysql-execute-sql.md)
 29 |   Run parameterized SQL queries in Cloud SQL for MySQL.
 30 | 
 31 | - [`mysql-list-active-queries`](../tools/mysql/mysql-list-active-queries.md)
 32 |   List active queries in Cloud SQL for MySQL.
 33 | 
 34 | - [`mysql-list-tables`](../tools/mysql/mysql-list-tables.md)
 35 |   List tables in a Cloud SQL for MySQL database.
 36 | 
 37 | - [`mysql-list-tables-missing-unique-indexes`](../tools/mysql/mysql-list-tables-missing-unique-indexes.md)
 38 |   List tables in a Cloud SQL for MySQL database that do not have primary or unique indices.
 39 | 
 40 | - [`mysql-list-table-fragmentation`](../tools/mysql/mysql-list-table-fragmentation.md)
 41 |   List table fragmentation in Cloud SQL for MySQL tables.
 42 | 
 43 | ### Pre-built Configurations
 44 | 
 45 | - [Cloud SQL for MySQL using
 46 |   MCP](https://googleapis.github.io/genai-toolbox/how-to/connect-ide/cloud_sql_mysql_mcp/)
 47 |   Connect your IDE to Cloud SQL for MySQL using Toolbox.
 48 | 
 49 | ## Requirements
 50 | 
 51 | ### IAM Permissions
 52 | 
 53 | By default, this source uses the [Cloud SQL Go Connector][csql-go-conn] to
 54 | authorize and establish mTLS connections to your Cloud SQL instance. The Go
 55 | connector uses your [Application Default Credentials (ADC)][adc] to authorize
 56 | your connection to Cloud SQL.
 57 | 
 58 | In addition to [setting the ADC for your server][set-adc], you need to ensure
 59 | the IAM identity has been given the following IAM roles (or corresponding
 60 | permissions):
 61 | 
 62 | - `roles/cloudsql.client`
 63 | 
 64 | {{< notice tip >}}
 65 | If you are connecting from Compute Engine, make sure your VM
 66 | also has the [proper
 67 | scope](https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam)
 68 | to connect using the Cloud SQL Admin API.
 69 | {{< /notice >}}
 70 | 
 71 | [csql-go-conn]: https://github.com/GoogleCloudPlatform/cloud-sql-go-connector
 72 | [adc]: https://cloud.google.com/docs/authentication#adc
 73 | [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc
 74 | 
 75 | ### Networking
 76 | 
 77 | Cloud SQL supports connecting over both from external networks via the internet
 78 | ([public IP][public-ip]), and internal networks ([private IP][private-ip]).
 79 | For more information on choosing between the two options, see the Cloud SQL page
 80 | [Connection overview][conn-overview].
 81 | 
 82 | You can configure the `ipType` parameter in your source configuration to
 83 | `public` or `private` to match your cluster's configuration. Regardless of which
 84 | you choose, all connections use IAM-based authorization and are encrypted with
 85 | mTLS.
 86 | 
 87 | [private-ip]: https://cloud.google.com/sql/docs/mysql/configure-private-ip
 88 | [public-ip]: https://cloud.google.com/sql/docs/mysql/configure-ip
 89 | [conn-overview]: https://cloud.google.com/sql/docs/mysql/connect-overview
 90 | 
 91 | ### Database User
 92 | 
 93 | Currently, this source only uses standard authentication. You will need to [create
 94 | a MySQL user][cloud-sql-users] to login to the database with.
 95 | 
 96 | [cloud-sql-users]: https://cloud.google.com/sql/docs/mysql/create-manage-users
 97 | 
 98 | ## Example
 99 | 
100 | ```yaml
101 | sources:
102 |     my-cloud-sql-mysql-source:
103 |         kind: cloud-sql-mysql
104 |         project: my-project-id
105 |         region: us-central1
106 |         instance: my-instance
107 |         database: my_db
108 |         user: ${USER_NAME}
109 |         password: ${PASSWORD}
110 |         # ipType: "private"
111 | ```
112 | 
113 | {{< notice tip >}}
114 | Use environment variable replacement with the format ${ENV_NAME}
115 | instead of hardcoding your secrets into the configuration file.
116 | {{< /notice >}}
117 | 
118 | ## Reference
119 | 
120 | | **field** | **type** | **required** | **description**                                                                                      |
121 | |-----------|:--------:|:------------:|------------------------------------------------------------------------------------------------------|
122 | | kind      |  string  |     true     | Must be "cloud-sql-mysql".                                                                           |
123 | | project   |  string  |     true     | Id of the GCP project that the cluster was created in (e.g. "my-project-id").                        |
124 | | region    |  string  |     true     | Name of the GCP region that the cluster was created in (e.g. "us-central1").                         |
125 | | instance  |  string  |     true     | Name of the Cloud SQL instance within the cluster (e.g. "my-instance").                              |
126 | | database  |  string  |     true     | Name of the MySQL database to connect to (e.g. "my_db").                                             |
127 | | user      |  string  |     true     | Name of the MySQL user to connect as (e.g. "my-pg-user").                                            |
128 | | password  |  string  |     true     | Password of the MySQL user (e.g. "my-password").                                                     |
129 | | ipType    |  string  |    false     | IP Type of the Cloud SQL instance, must be either `public`,  `private`, or `psc`. Default: `public`. |
130 | 
```

--------------------------------------------------------------------------------
/docs/en/how-to/connect_via_mcp.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "Connect via MCP Client"
  3 | type: docs
  4 | weight: 1
  5 | description: >
  6 |   How to connect to Toolbox from a MCP Client.
  7 | ---
  8 | 
  9 | ## Toolbox SDKs vs Model Context Protocol (MCP)
 10 | 
 11 | Toolbox now supports connections via both the native Toolbox SDKs and via [Model
 12 | Context Protocol (MCP)](https://modelcontextprotocol.io/). However, Toolbox has
 13 | several features which are not supported in the MCP specification (such as
 14 | Authenticated Parameters and Authorized invocation).
 15 | 
 16 | We recommend using the native SDKs over MCP clients to leverage these features.
 17 | The native SDKs can be combined with MCP clients in many cases.
 18 | 
 19 | ### Protocol Versions
 20 | 
 21 | Toolbox currently supports the following versions of MCP specification:
 22 | 
 23 | * [2025-06-18](https://modelcontextprotocol.io/specification/2025-06-18)
 24 | * [2025-03-26](https://modelcontextprotocol.io/specification/2025-03-26)
 25 | * [2024-11-05](https://modelcontextprotocol.io/specification/2024-11-05)
 26 | 
 27 | ### Toolbox AuthZ/AuthN Not Supported by MCP
 28 | 
 29 | The auth implementation in Toolbox is not supported in MCP's auth specification.
 30 | This includes:
 31 | 
 32 | * [Authenticated Parameters](../resources/tools/#authenticated-parameters)
 33 | * [Authorized Invocations](../resources/tools/#authorized-invocations)
 34 | 
 35 | ## Connecting to Toolbox with an MCP client
 36 | 
 37 | ### Before you begin
 38 | 
 39 | {{< notice note >}}
 40 | MCP is only compatible with Toolbox version 0.3.0 and above.
 41 | {{< /notice >}}
 42 | 
 43 | 1. [Install](../getting-started/introduction/#installing-the-server)
 44 |    Toolbox version 0.3.0+.
 45 | 
 46 | 1. Make sure you've set up and initialized your database.
 47 | 
 48 | 1. [Set up](../getting-started/configure.md) your `tools.yaml` file.
 49 | 
 50 | ### Connecting via Standard Input/Output (stdio)
 51 | 
 52 | Toolbox supports the
 53 | [stdio](https://modelcontextprotocol.io/docs/concepts/transports#standard-input%2Foutput-stdio)
 54 | transport protocol. Users that wish to use stdio will have to include the
 55 | `--stdio` flag when running Toolbox.
 56 | 
 57 | ```bash
 58 | ./toolbox --stdio
 59 | ```
 60 | 
 61 | When running with stdio, Toolbox will listen via stdio instead of acting as a
 62 | remote HTTP server. Logs will be set to the `warn` level by default. `debug` and
 63 | `info` logs are not supported with stdio.
 64 | 
 65 | {{< notice note >}}
 66 | Toolbox enables dynamic reloading by default. To disable, use the
 67 | `--disable-reload` flag.
 68 | {{< /notice >}}
 69 | 
 70 | ### Connecting via HTTP
 71 | 
 72 | Toolbox supports the HTTP transport protocol with and without SSE.
 73 | 
 74 | {{< tabpane text=true >}} {{% tab header="HTTP with SSE (deprecated)" lang="en" %}}
 75 | Add the following configuration to your MCP client configuration:
 76 | 
 77 | ```bash
 78 | {
 79 |   "mcpServers": {
 80 |     "toolbox": {
 81 |       "type": "sse",
 82 |       "url": "http://127.0.0.1:5000/mcp/sse",
 83 |     }
 84 |   }
 85 | }
 86 | ```
 87 | 
 88 | If you would like to connect to a specific toolset, replace `url` with
 89 | `"http://127.0.0.1:5000/mcp/{toolset_name}/sse"`.
 90 | 
 91 | HTTP with SSE is only supported in version `2024-11-05` and is currently
 92 | deprecated.
 93 | {{% /tab %}} {{% tab header="Streamable HTTP" lang="en" %}}
 94 | Add the following configuration to your MCP client configuration:
 95 | 
 96 | ```bash
 97 | {
 98 |   "mcpServers": {
 99 |     "toolbox": {
100 |       "type": "http",
101 |       "url": "http://127.0.0.1:5000/mcp",
102 |     }
103 |   }
104 | }
105 | ```
106 | 
107 | If you would like to connect to a specific toolset, replace `url` with
108 | `"http://127.0.0.1:5000/mcp/{toolset_name}"`.
109 | {{% /tab %}} {{< /tabpane >}}
110 | 
111 | ### Using the MCP Inspector with Toolbox
112 | 
113 | Use MCP [Inspector](https://github.com/modelcontextprotocol/inspector) for
114 | testing and debugging Toolbox server.
115 | 
116 | {{< tabpane text=true >}}
117 | {{% tab header="STDIO" lang="en" %}}
118 | 
119 | 1. Run Inspector with Toolbox as a subprocess:
120 | 
121 |    ```bash
122 |    npx @modelcontextprotocol/inspector ./toolbox --stdio
123 |    ```
124 | 
125 | 1. For `Transport Type` dropdown menu, select `STDIO`.
126 | 
127 | 1. In `Command`, make sure that it is set to :`./toolbox` (or the correct path
128 |    to where the Toolbox binary is installed).
129 | 
130 | 1. In `Arguments`, make sure that it's filled with `--stdio`.
131 | 
132 | 1. Click the `Connect` button. It might take awhile to spin up Toolbox. Voila!
133 |    You should be able to inspect your toolbox tools!
134 | {{% /tab %}}
135 | {{% tab header="HTTP with SSE (deprecated)" lang="en" %}}
136 | 1. [Run Toolbox](../getting-started/introduction/#running-the-server).
137 | 
138 | 1. In a separate terminal, run Inspector directly through `npx`:
139 | 
140 |     ```bash
141 |     npx @modelcontextprotocol/inspector
142 |     ```
143 | 
144 | 1. For `Transport Type` dropdown menu, select `SSE`.
145 | 
146 | 1. For `URL`, type in `http://127.0.0.1:5000/mcp/sse` to use all tool or
147 |    `http//127.0.0.1:5000/mcp/{toolset_name}/sse` to use a specific toolset.
148 | 
149 | 1. Click the `Connect` button. Voila! You should be able to inspect your toolbox
150 |    tools!
151 | {{% /tab %}}
152 | {{% tab header="Streamable HTTP" lang="en" %}}
153 | 1. [Run Toolbox](../getting-started/introduction/#running-the-server).
154 | 
155 | 1. In a separate terminal, run Inspector directly through `npx`:
156 | 
157 |     ```bash
158 |     npx @modelcontextprotocol/inspector
159 |     ```
160 | 
161 | 1. For `Transport Type` dropdown menu, select `Streamable HTTP`.
162 | 
163 | 1. For `URL`, type in `http://127.0.0.1:5000/mcp` to use all tool or
164 |    `http//127.0.0.1:5000/mcp/{toolset_name}` to use a specific toolset.
165 | 
166 | 1. Click the `Connect` button. Voila! You should be able to inspect your toolbox
167 |    tools!
168 | {{% /tab %}} {{< /tabpane >}}
169 | 
170 | ### Tested Clients
171 | 
172 | | Client | SSE Works | MCP Config Docs |
173 | |--------|--------|--------|
174 | | Claude Desktop | ✅ | <https://modelcontextprotocol.io/quickstart/user#1-download-claude-for-desktop> |
175 | | MCP Inspector | ✅ | <https://github.com/modelcontextprotocol/inspector> |
176 | | Cursor | ✅ | <https://docs.cursor.com/context/model-context-protocol> |
177 | | Windsurf | ✅ | <https://docs.windsurf.com/windsurf/mcp> |
178 | | VS Code (Insiders) | ✅ | <https://code.visualstudio.com/docs/copilot/chat/mcp-servers> |
179 | 
```

--------------------------------------------------------------------------------
/internal/tools/bigquery/bigquerygetdatasetinfo/bigquerygetdatasetinfo.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 bigquerygetdatasetinfo
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	bigqueryapi "cloud.google.com/go/bigquery"
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | )
 27 | 
 28 | const kind string = "bigquery-get-dataset-info"
 29 | const projectKey string = "project"
 30 | const datasetKey string = "dataset"
 31 | 
 32 | func init() {
 33 | 	if !tools.Register(kind, newConfig) {
 34 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type compatibleSource interface {
 47 | 	BigQueryProject() string
 48 | 	BigQueryClient() *bigqueryapi.Client
 49 | 	BigQueryClientCreator() bigqueryds.BigqueryClientCreator
 50 | 	UseClientAuthorization() bool
 51 | }
 52 | 
 53 | // validate compatible sources are still compatible
 54 | var _ compatibleSource = &bigqueryds.Source{}
 55 | 
 56 | var compatibleSources = [...]string{bigqueryds.SourceKind}
 57 | 
 58 | type Config struct {
 59 | 	Name         string   `yaml:"name" validate:"required"`
 60 | 	Kind         string   `yaml:"kind" validate:"required"`
 61 | 	Source       string   `yaml:"source" validate:"required"`
 62 | 	Description  string   `yaml:"description" validate:"required"`
 63 | 	AuthRequired []string `yaml:"authRequired"`
 64 | }
 65 | 
 66 | // validate interface
 67 | var _ tools.ToolConfig = Config{}
 68 | 
 69 | func (cfg Config) ToolConfigKind() string {
 70 | 	return kind
 71 | }
 72 | 
 73 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 74 | 	// verify source exists
 75 | 	rawS, ok := srcs[cfg.Source]
 76 | 	if !ok {
 77 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 78 | 	}
 79 | 
 80 | 	// verify the source is compatible
 81 | 	s, ok := rawS.(compatibleSource)
 82 | 	if !ok {
 83 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 84 | 	}
 85 | 
 86 | 	projectParameter := tools.NewStringParameterWithDefault(projectKey, s.BigQueryProject(), "The Google Cloud project ID containing the dataset.")
 87 | 	datasetParameter := tools.NewStringParameter(datasetKey, "The dataset to get metadata information.")
 88 | 	parameters := tools.Parameters{projectParameter, datasetParameter}
 89 | 
 90 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 91 | 
 92 | 	// finish tool setup
 93 | 	t := Tool{
 94 | 		Name:           cfg.Name,
 95 | 		Kind:           kind,
 96 | 		Parameters:     parameters,
 97 | 		AuthRequired:   cfg.AuthRequired,
 98 | 		UseClientOAuth: s.UseClientAuthorization(),
 99 | 		ClientCreator:  s.BigQueryClientCreator(),
100 | 		Client:         s.BigQueryClient(),
101 | 		manifest:       tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
102 | 		mcpManifest:    mcpManifest,
103 | 	}
104 | 	return t, nil
105 | }
106 | 
107 | // validate interface
108 | var _ tools.Tool = Tool{}
109 | 
110 | type Tool struct {
111 | 	Name           string           `yaml:"name"`
112 | 	Kind           string           `yaml:"kind"`
113 | 	AuthRequired   []string         `yaml:"authRequired"`
114 | 	UseClientOAuth bool             `yaml:"useClientOAuth"`
115 | 	Parameters     tools.Parameters `yaml:"parameters"`
116 | 
117 | 	Client        *bigqueryapi.Client
118 | 	ClientCreator bigqueryds.BigqueryClientCreator
119 | 	Statement     string
120 | 	manifest      tools.Manifest
121 | 	mcpManifest   tools.McpManifest
122 | }
123 | 
124 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
125 | 	mapParams := params.AsMap()
126 | 	projectId, ok := mapParams[projectKey].(string)
127 | 	if !ok {
128 | 		return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", projectKey)
129 | 	}
130 | 
131 | 	datasetId, ok := mapParams[datasetKey].(string)
132 | 	if !ok {
133 | 		return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", datasetKey)
134 | 	}
135 | 
136 | 	bqClient := t.Client
137 | 	var err error
138 | 
139 | 	// Initialize new client if using user OAuth token
140 | 	if t.UseClientOAuth {
141 | 		tokenStr, err := accessToken.ParseBearerToken()
142 | 		if err != nil {
143 | 			return nil, fmt.Errorf("error parsing access token: %w", err)
144 | 		}
145 | 		bqClient, _, err = t.ClientCreator(tokenStr, false)
146 | 		if err != nil {
147 | 			return nil, fmt.Errorf("error creating client from OAuth access token: %w", err)
148 | 		}
149 | 	}
150 | 	dsHandle := bqClient.DatasetInProject(projectId, datasetId)
151 | 
152 | 	metadata, err := dsHandle.Metadata(ctx)
153 | 	if err != nil {
154 | 		return nil, fmt.Errorf("failed to get metadata for dataset %s (in project %s): %w", datasetId, bqClient.Project(), err)
155 | 	}
156 | 
157 | 	return metadata, nil
158 | }
159 | 
160 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
161 | 	return tools.ParseParams(t.Parameters, data, claims)
162 | }
163 | 
164 | func (t Tool) Manifest() tools.Manifest {
165 | 	return t.manifest
166 | }
167 | 
168 | func (t Tool) McpManifest() tools.McpManifest {
169 | 	return t.mcpManifest
170 | }
171 | 
172 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
173 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
174 | }
175 | 
176 | func (t Tool) RequiresClientAuthorization() bool {
177 | 	return t.UseClientOAuth
178 | }
179 | 
```

--------------------------------------------------------------------------------
/internal/tools/postgres/postgressql/postgressql.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 postgressql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	yaml "github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlpg"
 25 | 	"github.com/googleapis/genai-toolbox/internal/sources/postgres"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 27 | 	"github.com/jackc/pgx/v5/pgxpool"
 28 | )
 29 | 
 30 | const kind string = "postgres-sql"
 31 | 
 32 | func init() {
 33 | 	if !tools.Register(kind, newConfig) {
 34 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type compatibleSource interface {
 47 | 	PostgresPool() *pgxpool.Pool
 48 | }
 49 | 
 50 | // validate compatible sources are still compatible
 51 | var _ compatibleSource = &alloydbpg.Source{}
 52 | var _ compatibleSource = &cloudsqlpg.Source{}
 53 | var _ compatibleSource = &postgres.Source{}
 54 | 
 55 | var compatibleSources = [...]string{alloydbpg.SourceKind, cloudsqlpg.SourceKind, postgres.SourceKind}
 56 | 
 57 | type Config struct {
 58 | 	Name               string           `yaml:"name" validate:"required"`
 59 | 	Kind               string           `yaml:"kind" validate:"required"`
 60 | 	Source             string           `yaml:"source" validate:"required"`
 61 | 	Description        string           `yaml:"description" validate:"required"`
 62 | 	Statement          string           `yaml:"statement" validate:"required"`
 63 | 	AuthRequired       []string         `yaml:"authRequired"`
 64 | 	Parameters         tools.Parameters `yaml:"parameters"`
 65 | 	TemplateParameters tools.Parameters `yaml:"templateParameters"`
 66 | }
 67 | 
 68 | // validate interface
 69 | var _ tools.ToolConfig = Config{}
 70 | 
 71 | func (cfg Config) ToolConfigKind() string {
 72 | 	return kind
 73 | }
 74 | 
 75 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 76 | 	// verify source exists
 77 | 	rawS, ok := srcs[cfg.Source]
 78 | 	if !ok {
 79 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 80 | 	}
 81 | 
 82 | 	// verify the source is compatible
 83 | 	s, ok := rawS.(compatibleSource)
 84 | 	if !ok {
 85 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 86 | 	}
 87 | 
 88 | 	allParameters, paramManifest, err := tools.ProcessParameters(cfg.TemplateParameters, cfg.Parameters)
 89 | 	if err != nil {
 90 | 		return nil, err
 91 | 	}
 92 | 
 93 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 94 | 
 95 | 	// finish tool setup
 96 | 	t := Tool{
 97 | 		Name:               cfg.Name,
 98 | 		Kind:               kind,
 99 | 		Parameters:         cfg.Parameters,
100 | 		TemplateParameters: cfg.TemplateParameters,
101 | 		AllParams:          allParameters,
102 | 		Statement:          cfg.Statement,
103 | 		AuthRequired:       cfg.AuthRequired,
104 | 		Pool:               s.PostgresPool(),
105 | 		manifest:           tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
106 | 		mcpManifest:        mcpManifest,
107 | 	}
108 | 	return t, nil
109 | }
110 | 
111 | // validate interface
112 | var _ tools.Tool = Tool{}
113 | 
114 | type Tool struct {
115 | 	Name               string           `yaml:"name"`
116 | 	Kind               string           `yaml:"kind"`
117 | 	AuthRequired       []string         `yaml:"authRequired"`
118 | 	Parameters         tools.Parameters `yaml:"parameters"`
119 | 	TemplateParameters tools.Parameters `yaml:"templateParameters"`
120 | 	AllParams          tools.Parameters `yaml:"allParams"`
121 | 
122 | 	Pool        *pgxpool.Pool
123 | 	Statement   string
124 | 	manifest    tools.Manifest
125 | 	mcpManifest tools.McpManifest
126 | }
127 | 
128 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
129 | 	paramsMap := params.AsMap()
130 | 	newStatement, err := tools.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
131 | 	if err != nil {
132 | 		return nil, fmt.Errorf("unable to extract template params %w", err)
133 | 	}
134 | 
135 | 	newParams, err := tools.GetParams(t.Parameters, paramsMap)
136 | 	if err != nil {
137 | 		return nil, fmt.Errorf("unable to extract standard params %w", err)
138 | 	}
139 | 	sliceParams := newParams.AsSlice()
140 | 	results, err := t.Pool.Query(ctx, newStatement, sliceParams...)
141 | 	if err != nil {
142 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
143 | 	}
144 | 
145 | 	fields := results.FieldDescriptions()
146 | 
147 | 	var out []any
148 | 	for results.Next() {
149 | 		v, err := results.Values()
150 | 		if err != nil {
151 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
152 | 		}
153 | 		vMap := make(map[string]any)
154 | 		for i, f := range fields {
155 | 			vMap[f.Name] = v[i]
156 | 		}
157 | 		out = append(out, vMap)
158 | 	}
159 | 
160 | 	return out, nil
161 | }
162 | 
163 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
164 | 	return tools.ParseParams(t.AllParams, data, claims)
165 | }
166 | 
167 | func (t Tool) Manifest() tools.Manifest {
168 | 	return t.manifest
169 | }
170 | 
171 | func (t Tool) McpManifest() tools.McpManifest {
172 | 	return t.mcpManifest
173 | }
174 | 
175 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
176 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
177 | }
178 | 
179 | func (t Tool) RequiresClientAuthorization() bool {
180 | 	return false
181 | }
182 | 
```

--------------------------------------------------------------------------------
/internal/tools/firestore/firestorelistcollections/firestorelistcollections.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 firestorelistcollections
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	firestoreapi "cloud.google.com/go/firestore"
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	firestoreds "github.com/googleapis/genai-toolbox/internal/sources/firestore"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
 27 | )
 28 | 
 29 | const kind string = "firestore-list-collections"
 30 | const parentPathKey string = "parentPath"
 31 | 
 32 | func init() {
 33 | 	if !tools.Register(kind, newConfig) {
 34 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type compatibleSource interface {
 47 | 	FirestoreClient() *firestoreapi.Client
 48 | }
 49 | 
 50 | // validate compatible sources are still compatible
 51 | var _ compatibleSource = &firestoreds.Source{}
 52 | 
 53 | var compatibleSources = [...]string{firestoreds.SourceKind}
 54 | 
 55 | type Config struct {
 56 | 	Name         string   `yaml:"name" validate:"required"`
 57 | 	Kind         string   `yaml:"kind" validate:"required"`
 58 | 	Source       string   `yaml:"source" validate:"required"`
 59 | 	Description  string   `yaml:"description" validate:"required"`
 60 | 	AuthRequired []string `yaml:"authRequired"`
 61 | }
 62 | 
 63 | // validate interface
 64 | var _ tools.ToolConfig = Config{}
 65 | 
 66 | func (cfg Config) ToolConfigKind() string {
 67 | 	return kind
 68 | }
 69 | 
 70 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 71 | 	// verify source exists
 72 | 	rawS, ok := srcs[cfg.Source]
 73 | 	if !ok {
 74 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 75 | 	}
 76 | 
 77 | 	// verify the source is compatible
 78 | 	s, ok := rawS.(compatibleSource)
 79 | 	if !ok {
 80 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 81 | 	}
 82 | 
 83 | 	emptyString := ""
 84 | 	parentPathParameter := tools.NewStringParameterWithDefault(parentPathKey, emptyString, "Relative parent document path to list subcollections from (e.g., 'users/userId'). If not provided, lists root collections. Note: This is a relative path, NOT an absolute path like 'projects/{project_id}/databases/{database_id}/documents/...'")
 85 | 	parameters := tools.Parameters{parentPathParameter}
 86 | 
 87 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 88 | 
 89 | 	// finish tool setup
 90 | 	t := Tool{
 91 | 		Name:         cfg.Name,
 92 | 		Kind:         kind,
 93 | 		Parameters:   parameters,
 94 | 		AuthRequired: cfg.AuthRequired,
 95 | 		Client:       s.FirestoreClient(),
 96 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 97 | 		mcpManifest:  mcpManifest,
 98 | 	}
 99 | 	return t, nil
100 | }
101 | 
102 | // validate interface
103 | var _ tools.Tool = Tool{}
104 | 
105 | type Tool struct {
106 | 	Name         string           `yaml:"name"`
107 | 	Kind         string           `yaml:"kind"`
108 | 	AuthRequired []string         `yaml:"authRequired"`
109 | 	Parameters   tools.Parameters `yaml:"parameters"`
110 | 
111 | 	Client      *firestoreapi.Client
112 | 	manifest    tools.Manifest
113 | 	mcpManifest tools.McpManifest
114 | }
115 | 
116 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
117 | 	mapParams := params.AsMap()
118 | 
119 | 	var collectionRefs []*firestoreapi.CollectionRef
120 | 	var err error
121 | 
122 | 	// Check if parentPath is provided
123 | 	parentPath, hasParent := mapParams[parentPathKey].(string)
124 | 
125 | 	if hasParent && parentPath != "" {
126 | 		// Validate parent document path
127 | 		if err := util.ValidateDocumentPath(parentPath); err != nil {
128 | 			return nil, fmt.Errorf("invalid parent document path: %w", err)
129 | 		}
130 | 
131 | 		// List subcollections of the specified document
132 | 		docRef := t.Client.Doc(parentPath)
133 | 		collectionRefs, err = docRef.Collections(ctx).GetAll()
134 | 		if err != nil {
135 | 			return nil, fmt.Errorf("failed to list subcollections of document %q: %w", parentPath, err)
136 | 		}
137 | 	} else {
138 | 		// List root collections
139 | 		collectionRefs, err = t.Client.Collections(ctx).GetAll()
140 | 		if err != nil {
141 | 			return nil, fmt.Errorf("failed to list root collections: %w", err)
142 | 		}
143 | 	}
144 | 
145 | 	// Convert collection references to response data
146 | 	results := make([]any, len(collectionRefs))
147 | 	for i, collRef := range collectionRefs {
148 | 		collData := make(map[string]any)
149 | 		collData["id"] = collRef.ID
150 | 		collData["path"] = collRef.Path
151 | 
152 | 		// If this is a subcollection, include parent information
153 | 		if collRef.Parent != nil {
154 | 			collData["parent"] = collRef.Parent.Path
155 | 		}
156 | 
157 | 		results[i] = collData
158 | 	}
159 | 
160 | 	return results, nil
161 | }
162 | 
163 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
164 | 	return tools.ParseParams(t.Parameters, data, claims)
165 | }
166 | 
167 | func (t Tool) Manifest() tools.Manifest {
168 | 	return t.manifest
169 | }
170 | 
171 | func (t Tool) McpManifest() tools.McpManifest {
172 | 	return t.mcpManifest
173 | }
174 | 
175 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
176 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
177 | }
178 | 
179 | func (t Tool) RequiresClientAuthorization() bool {
180 | 	return false
181 | }
182 | 
```

--------------------------------------------------------------------------------
/internal/tools/tidb/tidbexecutesql/tidbexecutesql.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 tidbexecutesql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/tidb"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | 	"github.com/googleapis/genai-toolbox/internal/util"
 27 | )
 28 | 
 29 | const kind string = "tidb-execute-sql"
 30 | 
 31 | func init() {
 32 | 	if !tools.Register(kind, newConfig) {
 33 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 34 | 	}
 35 | }
 36 | 
 37 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 38 | 	actual := Config{Name: name}
 39 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 40 | 		return nil, err
 41 | 	}
 42 | 	return actual, nil
 43 | }
 44 | 
 45 | type compatibleSource interface {
 46 | 	TiDBPool() *sql.DB
 47 | }
 48 | 
 49 | // validate compatible sources are still compatible
 50 | var _ compatibleSource = &tidb.Source{}
 51 | 
 52 | var compatibleSources = [...]string{tidb.SourceKind}
 53 | 
 54 | type Config struct {
 55 | 	Name         string   `yaml:"name" validate:"required"`
 56 | 	Kind         string   `yaml:"kind" validate:"required"`
 57 | 	Source       string   `yaml:"source" validate:"required"`
 58 | 	Description  string   `yaml:"description" validate:"required"`
 59 | 	AuthRequired []string `yaml:"authRequired"`
 60 | }
 61 | 
 62 | // validate interface
 63 | var _ tools.ToolConfig = Config{}
 64 | 
 65 | func (cfg Config) ToolConfigKind() string {
 66 | 	return kind
 67 | }
 68 | 
 69 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 70 | 	// verify source exists
 71 | 	rawS, ok := srcs[cfg.Source]
 72 | 	if !ok {
 73 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 74 | 	}
 75 | 
 76 | 	// verify the source is compatible
 77 | 	s, ok := rawS.(compatibleSource)
 78 | 	if !ok {
 79 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 80 | 	}
 81 | 
 82 | 	sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
 83 | 	parameters := tools.Parameters{sqlParameter}
 84 | 
 85 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 86 | 
 87 | 	// finish tool setup
 88 | 	t := Tool{
 89 | 		Name:         cfg.Name,
 90 | 		Kind:         kind,
 91 | 		Parameters:   parameters,
 92 | 		AuthRequired: cfg.AuthRequired,
 93 | 		Pool:         s.TiDBPool(),
 94 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 95 | 		mcpManifest:  mcpManifest,
 96 | 	}
 97 | 	return t, nil
 98 | }
 99 | 
100 | // validate interface
101 | var _ tools.Tool = Tool{}
102 | 
103 | type Tool struct {
104 | 	Name         string           `yaml:"name"`
105 | 	Kind         string           `yaml:"kind"`
106 | 	AuthRequired []string         `yaml:"authRequired"`
107 | 	Parameters   tools.Parameters `yaml:"parameters"`
108 | 
109 | 	Pool        *sql.DB
110 | 	manifest    tools.Manifest
111 | 	mcpManifest tools.McpManifest
112 | }
113 | 
114 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
115 | 	paramsMap := params.AsMap()
116 | 	sql, ok := paramsMap["sql"].(string)
117 | 	if !ok {
118 | 		return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
119 | 	}
120 | 
121 | 	// Log the query executed for debugging.
122 | 	logger, err := util.LoggerFromContext(ctx)
123 | 	if err != nil {
124 | 		return nil, fmt.Errorf("error getting logger: %s", err)
125 | 	}
126 | 	logger.DebugContext(ctx, "executing `%s` tool query: %s", kind, sql)
127 | 
128 | 	results, err := t.Pool.QueryContext(ctx, sql)
129 | 	if err != nil {
130 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
131 | 	}
132 | 	defer results.Close()
133 | 
134 | 	cols, err := results.Columns()
135 | 	if err != nil {
136 | 		return nil, fmt.Errorf("unable to retrieve rows column name: %w", err)
137 | 	}
138 | 
139 | 	// create an array of values for each column, which can be re-used to scan each row
140 | 	rawValues := make([]any, len(cols))
141 | 	values := make([]any, len(cols))
142 | 	for i := range rawValues {
143 | 		values[i] = &rawValues[i]
144 | 	}
145 | 
146 | 	colTypes, err := results.ColumnTypes()
147 | 	if err != nil {
148 | 		return nil, fmt.Errorf("unable to get column types: %w", err)
149 | 	}
150 | 
151 | 	var out []any
152 | 	for results.Next() {
153 | 		err := results.Scan(values...)
154 | 		if err != nil {
155 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
156 | 		}
157 | 		vMap := make(map[string]any)
158 | 		for i, name := range cols {
159 | 			val := rawValues[i]
160 | 			if val == nil {
161 | 				vMap[name] = nil
162 | 				continue
163 | 			}
164 | 
165 | 			// mysql driver return []uint8 type for "TEXT", "VARCHAR", and "NVARCHAR"
166 | 			// we'll need to cast it back to string
167 | 			switch colTypes[i].DatabaseTypeName() {
168 | 			case "TEXT", "VARCHAR", "NVARCHAR":
169 | 				vMap[name] = string(val.([]byte))
170 | 			default:
171 | 				vMap[name] = val
172 | 			}
173 | 		}
174 | 		out = append(out, vMap)
175 | 	}
176 | 
177 | 	if err := results.Err(); err != nil {
178 | 		return nil, fmt.Errorf("errors encountered during row iteration: %w", err)
179 | 	}
180 | 
181 | 	return out, nil
182 | }
183 | 
184 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
185 | 	return tools.ParseParams(t.Parameters, data, claims)
186 | }
187 | 
188 | func (t Tool) Manifest() tools.Manifest {
189 | 	return t.manifest
190 | }
191 | 
192 | func (t Tool) McpManifest() tools.McpManifest {
193 | 	return t.mcpManifest
194 | }
195 | 
196 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
197 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
198 | }
199 | 
200 | func (t Tool) RequiresClientAuthorization() bool {
201 | 	return false
202 | }
203 | 
```

--------------------------------------------------------------------------------
/internal/tools/trino/trinosql/trinosql_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 trinosql_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/trino/trinosql"
 26 | )
 27 | 
 28 | func TestParseFromYamlTrino(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: trino-sql
 44 | 					source: my-trino-instance
 45 | 					description: some description
 46 | 					statement: |
 47 | 						SELECT * FROM catalog.schema.table WHERE id = ?;
 48 | 					authRequired:
 49 | 						- my-google-auth-service
 50 | 						- other-auth-service
 51 | 					parameters:
 52 | 						- name: id
 53 | 						  type: string
 54 | 						  description: ID to filter by
 55 | 						  authServices:
 56 | 							- name: my-google-auth-service
 57 | 							  field: user_id
 58 | 							- name: other-auth-service
 59 | 							  field: user_id
 60 | 			`,
 61 | 			want: server.ToolConfigs{
 62 | 				"example_tool": trinosql.Config{
 63 | 					Name:         "example_tool",
 64 | 					Kind:         "trino-sql",
 65 | 					Source:       "my-trino-instance",
 66 | 					Description:  "some description",
 67 | 					Statement:    "SELECT * FROM catalog.schema.table WHERE id = ?;\n",
 68 | 					AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
 69 | 					Parameters: []tools.Parameter{
 70 | 						tools.NewStringParameterWithAuth("id", "ID to filter by",
 71 | 							[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
 72 | 								{Name: "other-auth-service", Field: "user_id"}}),
 73 | 					},
 74 | 				},
 75 | 			},
 76 | 		},
 77 | 	}
 78 | 	for _, tc := range tcs {
 79 | 		t.Run(tc.desc, func(t *testing.T) {
 80 | 			got := struct {
 81 | 				Tools server.ToolConfigs `yaml:"tools"`
 82 | 			}{}
 83 | 			// Parse contents
 84 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 85 | 			if err != nil {
 86 | 				t.Fatalf("unable to unmarshal: %s", err)
 87 | 			}
 88 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 89 | 				t.Fatalf("incorrect parse: diff %v", diff)
 90 | 			}
 91 | 		})
 92 | 	}
 93 | }
 94 | 
 95 | func TestParseFromYamlWithTemplateParamsTrino(t *testing.T) {
 96 | 	ctx, err := testutils.ContextWithNewLogger()
 97 | 	if err != nil {
 98 | 		t.Fatalf("unexpected error: %s", err)
 99 | 	}
100 | 	tcs := []struct {
101 | 		desc string
102 | 		in   string
103 | 		want server.ToolConfigs
104 | 	}{
105 | 		{
106 | 			desc: "basic example",
107 | 			in: `
108 | 			tools:
109 | 				example_tool:
110 | 					kind: trino-sql
111 | 					source: my-trino-instance
112 | 					description: some description
113 | 					statement: |
114 | 						SELECT * FROM {{ .catalog }}.{{ .schema }}.{{ .tableName }} WHERE country = ?;
115 | 					authRequired:
116 | 						- my-google-auth-service
117 | 						- other-auth-service
118 | 					parameters:
119 | 						- name: country
120 | 						  type: string
121 | 						  description: some description
122 | 						  authServices:
123 | 							- name: my-google-auth-service
124 | 							  field: user_id
125 | 							- name: other-auth-service
126 | 							  field: user_id
127 | 					templateParameters:
128 | 						- name: catalog
129 | 						  type: string
130 | 						  description: The catalog to query from.
131 | 						- name: schema
132 | 						  type: string
133 | 						  description: The schema to query from.
134 | 						- name: tableName
135 | 						  type: string
136 | 						  description: The table to select data from.
137 | 						- name: fieldArray
138 | 						  type: array
139 | 						  description: The columns to return for the query.
140 | 						  items: 
141 | 								name: column
142 | 								type: string
143 | 								description: A column name that will be returned from the query.
144 | 			`,
145 | 			want: server.ToolConfigs{
146 | 				"example_tool": trinosql.Config{
147 | 					Name:         "example_tool",
148 | 					Kind:         "trino-sql",
149 | 					Source:       "my-trino-instance",
150 | 					Description:  "some description",
151 | 					Statement:    "SELECT * FROM {{ .catalog }}.{{ .schema }}.{{ .tableName }} WHERE country = ?;\n",
152 | 					AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
153 | 					Parameters: []tools.Parameter{
154 | 						tools.NewStringParameterWithAuth("country", "some description",
155 | 							[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
156 | 								{Name: "other-auth-service", Field: "user_id"}}),
157 | 					},
158 | 					TemplateParameters: []tools.Parameter{
159 | 						tools.NewStringParameter("catalog", "The catalog to query from."),
160 | 						tools.NewStringParameter("schema", "The schema to query from."),
161 | 						tools.NewStringParameter("tableName", "The table to select data from."),
162 | 						tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
163 | 					},
164 | 				},
165 | 			},
166 | 		},
167 | 	}
168 | 	for _, tc := range tcs {
169 | 		t.Run(tc.desc, func(t *testing.T) {
170 | 			got := struct {
171 | 				Tools server.ToolConfigs `yaml:"tools"`
172 | 			}{}
173 | 			// Parse contents
174 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
175 | 			if err != nil {
176 | 				t.Fatalf("unable to unmarshal: %s", err)
177 | 			}
178 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
179 | 				t.Fatalf("incorrect parse: diff %v", diff)
180 | 			}
181 | 		})
182 | 	}
183 | }
184 | 
```

--------------------------------------------------------------------------------
/internal/tools/spanner/spannerexecutesql/spannerexecutesql.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 spannerexecutesql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"cloud.google.com/go/spanner"
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	spannerdb "github.com/googleapis/genai-toolbox/internal/sources/spanner"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | 	"github.com/googleapis/genai-toolbox/internal/util"
 27 | 	"google.golang.org/api/iterator"
 28 | )
 29 | 
 30 | const kind string = "spanner-execute-sql"
 31 | 
 32 | func init() {
 33 | 	if !tools.Register(kind, newConfig) {
 34 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type compatibleSource interface {
 47 | 	SpannerClient() *spanner.Client
 48 | 	DatabaseDialect() string
 49 | }
 50 | 
 51 | // validate compatible sources are still compatible
 52 | var _ compatibleSource = &spannerdb.Source{}
 53 | 
 54 | var compatibleSources = [...]string{spannerdb.SourceKind}
 55 | 
 56 | type Config struct {
 57 | 	Name         string   `yaml:"name" validate:"required"`
 58 | 	Kind         string   `yaml:"kind" validate:"required"`
 59 | 	Source       string   `yaml:"source" validate:"required"`
 60 | 	Description  string   `yaml:"description" validate:"required"`
 61 | 	AuthRequired []string `yaml:"authRequired"`
 62 | 	ReadOnly     bool     `yaml:"readOnly"`
 63 | }
 64 | 
 65 | // validate interface
 66 | var _ tools.ToolConfig = Config{}
 67 | 
 68 | func (cfg Config) ToolConfigKind() string {
 69 | 	return kind
 70 | }
 71 | 
 72 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 73 | 	// verify source exists
 74 | 	rawS, ok := srcs[cfg.Source]
 75 | 	if !ok {
 76 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 77 | 	}
 78 | 
 79 | 	// verify the source is compatible
 80 | 	s, ok := rawS.(compatibleSource)
 81 | 	if !ok {
 82 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 83 | 	}
 84 | 
 85 | 	sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
 86 | 	parameters := tools.Parameters{sqlParameter}
 87 | 
 88 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 89 | 
 90 | 	// finish tool setup
 91 | 	t := Tool{
 92 | 		Name:         cfg.Name,
 93 | 		Kind:         kind,
 94 | 		Parameters:   parameters,
 95 | 		AuthRequired: cfg.AuthRequired,
 96 | 		ReadOnly:     cfg.ReadOnly,
 97 | 		Client:       s.SpannerClient(),
 98 | 		dialect:      s.DatabaseDialect(),
 99 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
100 | 		mcpManifest:  mcpManifest,
101 | 	}
102 | 	return t, nil
103 | }
104 | 
105 | // validate interface
106 | var _ tools.Tool = Tool{}
107 | 
108 | type Tool struct {
109 | 	Name         string           `yaml:"name"`
110 | 	Kind         string           `yaml:"kind"`
111 | 	AuthRequired []string         `yaml:"authRequired"`
112 | 	Parameters   tools.Parameters `yaml:"parameters"`
113 | 	ReadOnly     bool             `yaml:"readOnly"`
114 | 	Client       *spanner.Client
115 | 	dialect      string
116 | 	manifest     tools.Manifest
117 | 	mcpManifest  tools.McpManifest
118 | }
119 | 
120 | // processRows iterates over the spanner.RowIterator and converts each row to a map[string]any.
121 | func processRows(iter *spanner.RowIterator) ([]any, error) {
122 | 	var out []any
123 | 	defer iter.Stop()
124 | 
125 | 	for {
126 | 		row, err := iter.Next()
127 | 		if err == iterator.Done {
128 | 			break
129 | 		}
130 | 		if err != nil {
131 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
132 | 		}
133 | 
134 | 		vMap := make(map[string]any)
135 | 		cols := row.ColumnNames()
136 | 		for i, c := range cols {
137 | 			vMap[c] = row.ColumnValue(i)
138 | 		}
139 | 		out = append(out, vMap)
140 | 	}
141 | 	return out, nil
142 | }
143 | 
144 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
145 | 	paramsMap := params.AsMap()
146 | 	sql, ok := paramsMap["sql"].(string)
147 | 	if !ok {
148 | 		return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
149 | 	}
150 | 
151 | 	// Log the query executed for debugging.
152 | 	logger, err := util.LoggerFromContext(ctx)
153 | 	if err != nil {
154 | 		return nil, fmt.Errorf("error getting logger: %s", err)
155 | 	}
156 | 	logger.DebugContext(ctx, "executing `%s` tool query: %s", kind, sql)
157 | 
158 | 	var results []any
159 | 	var opErr error
160 | 	stmt := spanner.Statement{SQL: sql}
161 | 
162 | 	if t.ReadOnly {
163 | 		iter := t.Client.Single().Query(ctx, stmt)
164 | 		results, opErr = processRows(iter)
165 | 	} else {
166 | 		_, opErr = t.Client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
167 | 			var err error
168 | 			iter := txn.Query(ctx, stmt)
169 | 			results, err = processRows(iter)
170 | 			if err != nil {
171 | 				return err
172 | 			}
173 | 			return nil
174 | 		})
175 | 	}
176 | 
177 | 	if opErr != nil {
178 | 		return nil, fmt.Errorf("unable to execute query: %w", opErr)
179 | 	}
180 | 
181 | 	return results, nil
182 | }
183 | 
184 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
185 | 	return tools.ParseParams(t.Parameters, data, claims)
186 | }
187 | 
188 | func (t Tool) Manifest() tools.Manifest {
189 | 	return t.manifest
190 | }
191 | 
192 | func (t Tool) McpManifest() tools.McpManifest {
193 | 	return t.mcpManifest
194 | }
195 | 
196 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
197 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
198 | }
199 | 
200 | func (t Tool) RequiresClientAuthorization() bool {
201 | 	return false
202 | }
203 | 
```

--------------------------------------------------------------------------------
/internal/tools/mssql/mssqlexecutesql/mssqlexecutesql.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 mssqlexecutesql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmssql"
 25 | 	"github.com/googleapis/genai-toolbox/internal/sources/mssql"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 27 | 	"github.com/googleapis/genai-toolbox/internal/util"
 28 | )
 29 | 
 30 | const kind string = "mssql-execute-sql"
 31 | 
 32 | func init() {
 33 | 	if !tools.Register(kind, newConfig) {
 34 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type compatibleSource interface {
 47 | 	MSSQLDB() *sql.DB
 48 | }
 49 | 
 50 | // validate compatible sources are still compatible
 51 | var _ compatibleSource = &cloudsqlmssql.Source{}
 52 | var _ compatibleSource = &mssql.Source{}
 53 | 
 54 | var compatibleSources = [...]string{cloudsqlmssql.SourceKind, mssql.SourceKind}
 55 | 
 56 | type Config struct {
 57 | 	Name         string   `yaml:"name" validate:"required"`
 58 | 	Kind         string   `yaml:"kind" validate:"required"`
 59 | 	Source       string   `yaml:"source" validate:"required"`
 60 | 	Description  string   `yaml:"description" validate:"required"`
 61 | 	AuthRequired []string `yaml:"authRequired"`
 62 | }
 63 | 
 64 | // validate interface
 65 | var _ tools.ToolConfig = Config{}
 66 | 
 67 | func (cfg Config) ToolConfigKind() string {
 68 | 	return kind
 69 | }
 70 | 
 71 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 72 | 	// verify source exists
 73 | 	rawS, ok := srcs[cfg.Source]
 74 | 	if !ok {
 75 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 76 | 	}
 77 | 
 78 | 	// verify the source is compatible
 79 | 	s, ok := rawS.(compatibleSource)
 80 | 	if !ok {
 81 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 82 | 	}
 83 | 
 84 | 	sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
 85 | 	parameters := tools.Parameters{sqlParameter}
 86 | 
 87 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 88 | 
 89 | 	// finish tool setup
 90 | 	t := Tool{
 91 | 		Name:         cfg.Name,
 92 | 		Kind:         kind,
 93 | 		Parameters:   parameters,
 94 | 		AuthRequired: cfg.AuthRequired,
 95 | 		Pool:         s.MSSQLDB(),
 96 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 97 | 		mcpManifest:  mcpManifest,
 98 | 	}
 99 | 	return t, nil
100 | }
101 | 
102 | // validate interface
103 | var _ tools.Tool = Tool{}
104 | 
105 | type Tool struct {
106 | 	Name         string           `yaml:"name"`
107 | 	Kind         string           `yaml:"kind"`
108 | 	AuthRequired []string         `yaml:"authRequired"`
109 | 	Parameters   tools.Parameters `yaml:"parameters"`
110 | 
111 | 	Pool        *sql.DB
112 | 	manifest    tools.Manifest
113 | 	mcpManifest tools.McpManifest
114 | }
115 | 
116 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
117 | 	paramsMap := params.AsMap()
118 | 	sql, ok := paramsMap["sql"].(string)
119 | 	if !ok {
120 | 		return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
121 | 	}
122 | 
123 | 	// Log the query executed for debugging.
124 | 	logger, err := util.LoggerFromContext(ctx)
125 | 	if err != nil {
126 | 		return nil, fmt.Errorf("error getting logger: %s", err)
127 | 	}
128 | 	logger.DebugContext(ctx, "executing `%s` tool query: %s", kind, sql)
129 | 
130 | 	results, err := t.Pool.QueryContext(ctx, sql)
131 | 	if err != nil {
132 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
133 | 	}
134 | 	defer results.Close()
135 | 
136 | 	cols, err := results.Columns()
137 | 	// If Columns() errors, it might be a DDL/DML without an OUTPUT clause.
138 | 	// We proceed, and results.Err() will catch actual query execution errors.
139 | 	// 'out' will remain nil if cols is empty or err is not nil here.
140 | 
141 | 	var out []any
142 | 	if err == nil && len(cols) > 0 {
143 | 		// create an array of values for each column, which can be re-used to scan each row
144 | 		rawValues := make([]any, len(cols))
145 | 		values := make([]any, len(cols))
146 | 		for i := range rawValues {
147 | 			values[i] = &rawValues[i]
148 | 		}
149 | 
150 | 		for results.Next() {
151 | 			scanErr := results.Scan(values...)
152 | 			if scanErr != nil {
153 | 				return nil, fmt.Errorf("unable to parse row: %w", scanErr)
154 | 			}
155 | 			vMap := make(map[string]any)
156 | 			for i, name := range cols {
157 | 				vMap[name] = rawValues[i]
158 | 			}
159 | 			out = append(out, vMap)
160 | 		}
161 | 	}
162 | 
163 | 	// Check for errors from iterating over rows or from the query execution itself.
164 | 	// results.Close() is handled by defer.
165 | 	if err := results.Err(); err != nil {
166 | 		return nil, fmt.Errorf("errors encountered during query execution or row processing: %w", err)
167 | 	}
168 | 
169 | 	return out, nil
170 | }
171 | 
172 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
173 | 	return tools.ParseParams(t.Parameters, data, claims)
174 | }
175 | 
176 | func (t Tool) Manifest() tools.Manifest {
177 | 	return t.manifest
178 | }
179 | 
180 | func (t Tool) McpManifest() tools.McpManifest {
181 | 	return t.mcpManifest
182 | }
183 | 
184 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
185 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
186 | }
187 | 
188 | func (t Tool) RequiresClientAuthorization() bool {
189 | 	return false
190 | }
191 | 
```

--------------------------------------------------------------------------------
/internal/tools/oceanbase/oceanbaseexecutesql/oceanbaseexecutesql.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 oceanbaseexecutesql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/oceanbase"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools/mysql/mysqlcommon"
 27 | )
 28 | 
 29 | const kind string = "oceanbase-execute-sql"
 30 | 
 31 | func init() {
 32 | 	if !tools.Register(kind, newConfig) {
 33 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 34 | 	}
 35 | }
 36 | 
 37 | type compatibleSource interface {
 38 | 	OceanBasePool() *sql.DB
 39 | }
 40 | 
 41 | // validate compatible sources are still compatible
 42 | var _ compatibleSource = &oceanbase.Source{}
 43 | 
 44 | var compatibleSources = [...]string{oceanbase.SourceKind}
 45 | 
 46 | type Config struct {
 47 | 	Name         string   `yaml:"name" validate:"required"`
 48 | 	Kind         string   `yaml:"kind" validate:"required"`
 49 | 	Source       string   `yaml:"source" validate:"required"`
 50 | 	Description  string   `yaml:"description" validate:"required"`
 51 | 	AuthRequired []string `yaml:"authRequired"`
 52 | }
 53 | 
 54 | // validate interface
 55 | var _ tools.ToolConfig = Config{}
 56 | 
 57 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 58 | 	actual := Config{Name: name}
 59 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 60 | 		return nil, err
 61 | 	}
 62 | 	return actual, nil
 63 | }
 64 | 
 65 | func (cfg Config) ToolConfigKind() string {
 66 | 	return kind
 67 | }
 68 | 
 69 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 70 | 	// verify source exists
 71 | 	rawS, ok := srcs[cfg.Source]
 72 | 	if !ok {
 73 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 74 | 	}
 75 | 
 76 | 	// verify the source is compatible
 77 | 	s, ok := rawS.(compatibleSource)
 78 | 	if !ok {
 79 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 80 | 	}
 81 | 
 82 | 	sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
 83 | 	parameters := tools.Parameters{sqlParameter}
 84 | 
 85 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 86 | 
 87 | 	// finish tool setup
 88 | 	t := Tool{
 89 | 		Name:         cfg.Name,
 90 | 		Kind:         kind,
 91 | 		Parameters:   parameters,
 92 | 		AuthRequired: cfg.AuthRequired,
 93 | 		Pool:         s.OceanBasePool(),
 94 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 95 | 		mcpManifest:  mcpManifest,
 96 | 	}
 97 | 	return t, nil
 98 | }
 99 | 
100 | // validate interface
101 | var _ tools.Tool = Tool{}
102 | 
103 | type Tool struct {
104 | 	Name         string           `yaml:"name"`
105 | 	Kind         string           `yaml:"kind"`
106 | 	AuthRequired []string         `yaml:"authRequired"`
107 | 	Parameters   tools.Parameters `yaml:"parameters"`
108 | 
109 | 	Pool        *sql.DB
110 | 	manifest    tools.Manifest
111 | 	mcpManifest tools.McpManifest
112 | }
113 | 
114 | // Invoke executes the SQL statement provided in the parameters.
115 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
116 | 	sliceParams := params.AsSlice()
117 | 	sqlStr, ok := sliceParams[0].(string)
118 | 	if !ok {
119 | 		return nil, fmt.Errorf("unable to get cast %s", sliceParams[0])
120 | 	}
121 | 
122 | 	results, err := t.Pool.QueryContext(ctx, sqlStr)
123 | 	if err != nil {
124 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
125 | 	}
126 | 	defer results.Close()
127 | 
128 | 	cols, err := results.Columns()
129 | 	if err != nil {
130 | 		return nil, fmt.Errorf("unable to retrieve rows column name: %w", err)
131 | 	}
132 | 
133 | 	// create an array of values for each column, which can be re-used to scan each row
134 | 	rawValues := make([]any, len(cols))
135 | 	values := make([]any, len(cols))
136 | 	for i := range rawValues {
137 | 		values[i] = &rawValues[i]
138 | 	}
139 | 
140 | 	colTypes, err := results.ColumnTypes()
141 | 	if err != nil {
142 | 		return nil, fmt.Errorf("unable to get column types: %w", err)
143 | 	}
144 | 
145 | 	var out []any
146 | 	for results.Next() {
147 | 		err := results.Scan(values...)
148 | 		if err != nil {
149 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
150 | 		}
151 | 		vMap := make(map[string]any)
152 | 		for i, name := range cols {
153 | 			val := rawValues[i]
154 | 			if val == nil {
155 | 				vMap[name] = nil
156 | 				continue
157 | 			}
158 | 
159 | 			// oceanbase uses mysql driver
160 | 			vMap[name], err = mysqlcommon.ConvertToType(colTypes[i], val)
161 | 			if err != nil {
162 | 				return nil, fmt.Errorf("errors encountered when converting values: %w", err)
163 | 			}
164 | 		}
165 | 		out = append(out, vMap)
166 | 	}
167 | 
168 | 	if err := results.Err(); err != nil {
169 | 		return nil, fmt.Errorf("errors encountered during row iteration: %w", err)
170 | 	}
171 | 
172 | 	return out, nil
173 | }
174 | 
175 | // ParseParams parses the input parameters for the tool.
176 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
177 | 	return tools.ParseParams(t.Parameters, data, claims)
178 | }
179 | 
180 | // Manifest returns the tool manifest.
181 | func (t Tool) Manifest() tools.Manifest {
182 | 	return t.manifest
183 | }
184 | 
185 | // McpManifest returns the MCP manifest for the tool.
186 | func (t Tool) McpManifest() tools.McpManifest {
187 | 	return t.mcpManifest
188 | }
189 | 
190 | // Authorized checks if the tool is authorized for the given auth services.
191 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
192 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
193 | }
194 | 
195 | func (t Tool) RequiresClientAuthorization() bool {
196 | 	return false
197 | }
198 | 
```

--------------------------------------------------------------------------------
/tests/cloudsql/cloud_sql_list_databases_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 cloudsql
 16 | 
 17 | import (
 18 | 	"bytes"
 19 | 	"context"
 20 | 	"encoding/json"
 21 | 	"fmt"
 22 | 	"io"
 23 | 	"net/http"
 24 | 	"net/http/httptest"
 25 | 	"net/url"
 26 | 	"reflect"
 27 | 	"regexp"
 28 | 	"strings"
 29 | 	"testing"
 30 | 	"time"
 31 | 
 32 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 33 | 	"github.com/googleapis/genai-toolbox/tests"
 34 | )
 35 | 
 36 | var (
 37 | 	listDatabasesToolKind = "cloud-sql-list-databases"
 38 | )
 39 | 
 40 | type listDatabasesTransport struct {
 41 | 	transport http.RoundTripper
 42 | 	url       *url.URL
 43 | }
 44 | 
 45 | func (t *listDatabasesTransport) RoundTrip(req *http.Request) (*http.Response, error) {
 46 | 	if strings.HasPrefix(req.URL.String(), "https://sqladmin.googleapis.com") {
 47 | 		req.URL.Scheme = t.url.Scheme
 48 | 		req.URL.Host = t.url.Host
 49 | 	}
 50 | 	return t.transport.RoundTrip(req)
 51 | }
 52 | 
 53 | type masterListDatabasesHandler struct {
 54 | 	t *testing.T
 55 | }
 56 | 
 57 | func (h *masterListDatabasesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 58 | 	if !strings.Contains(r.UserAgent(), "genai-toolbox/") {
 59 | 		h.t.Errorf("User-Agent header not found")
 60 | 	}
 61 | 
 62 | 	response := map[string]any{
 63 | 		"items": []map[string]any{
 64 | 			{
 65 | 				"name":      "db1",
 66 | 				"charset":   "utf8",
 67 | 				"collation": "utf8_general_ci",
 68 | 			},
 69 | 			{
 70 | 				"name":      "db2",
 71 | 				"charset":   "utf8mb4",
 72 | 				"collation": "utf8mb4_unicode_ci",
 73 | 			},
 74 | 		},
 75 | 	}
 76 | 	statusCode := http.StatusOK
 77 | 
 78 | 	w.Header().Set("Content-Type", "application/json")
 79 | 	w.WriteHeader(statusCode)
 80 | 	if err := json.NewEncoder(w).Encode(response); err != nil {
 81 | 		http.Error(w, err.Error(), http.StatusInternalServerError)
 82 | 	}
 83 | }
 84 | 
 85 | func TestListDatabasesToolEndpoints(t *testing.T) {
 86 | 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
 87 | 	defer cancel()
 88 | 
 89 | 	handler := &masterListDatabasesHandler{t: t}
 90 | 	server := httptest.NewServer(handler)
 91 | 	defer server.Close()
 92 | 
 93 | 	serverURL, err := url.Parse(server.URL)
 94 | 	if err != nil {
 95 | 		t.Fatalf("failed to parse server URL: %v", err)
 96 | 	}
 97 | 
 98 | 	originalTransport := http.DefaultClient.Transport
 99 | 	if originalTransport == nil {
100 | 		originalTransport = http.DefaultTransport
101 | 	}
102 | 	http.DefaultClient.Transport = &listDatabasesTransport{
103 | 		transport: originalTransport,
104 | 		url:       serverURL,
105 | 	}
106 | 	t.Cleanup(func() {
107 | 		http.DefaultClient.Transport = originalTransport
108 | 	})
109 | 
110 | 	var args []string
111 | 	toolsFile := getListDatabasesToolsConfig()
112 | 	cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
113 | 	if err != nil {
114 | 		t.Fatalf("command initialization returned an error: %s", err)
115 | 	}
116 | 	defer cleanup()
117 | 
118 | 	waitCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
119 | 	defer cancel()
120 | 	out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
121 | 	if err != nil {
122 | 		t.Logf("toolbox command logs: \n%s", out)
123 | 		t.Fatalf("toolbox didn't start successfully: %s", err)
124 | 	}
125 | 
126 | 	tcs := []struct {
127 | 		name        string
128 | 		toolName    string
129 | 		body        string
130 | 		want        string
131 | 		expectError bool
132 | 		errorStatus int
133 | 	}{
134 | 		{
135 | 			name:     "successful databases listing",
136 | 			toolName: "list-databases",
137 | 			body:     `{"project": "p1", "instance": "i1"}`,
138 | 			want:     `[{"name":"db1","charset":"utf8","collation":"utf8_general_ci"},{"name":"db2","charset":"utf8mb4","collation":"utf8mb4_unicode_ci"}]`,
139 | 		},
140 | 		{
141 | 			name:        "missing instance",
142 | 			toolName:    "list-databases",
143 | 			body:        `{"project": "p1"}`,
144 | 			expectError: true,
145 | 			errorStatus: http.StatusBadRequest,
146 | 		},
147 | 	}
148 | 
149 | 	for _, tc := range tcs {
150 | 		tc := tc
151 | 		t.Run(tc.name, func(t *testing.T) {
152 | 			api := fmt.Sprintf("http://127.0.0.1:5000/api/tool/%s/invoke", tc.toolName)
153 | 			req, err := http.NewRequest(http.MethodPost, api, bytes.NewBufferString(tc.body))
154 | 			if err != nil {
155 | 				t.Fatalf("unable to create request: %s", err)
156 | 			}
157 | 			req.Header.Add("Content-type", "application/json")
158 | 			resp, err := http.DefaultClient.Do(req)
159 | 			if err != nil {
160 | 				t.Fatalf("unable to send request: %s", err)
161 | 			}
162 | 			defer resp.Body.Close()
163 | 
164 | 			if tc.expectError {
165 | 				if resp.StatusCode != tc.errorStatus {
166 | 					bodyBytes, _ := io.ReadAll(resp.Body)
167 | 					t.Fatalf("expected status %d but got %d: %s", tc.errorStatus, resp.StatusCode, string(bodyBytes))
168 | 				}
169 | 				return
170 | 			}
171 | 
172 | 			if resp.StatusCode != http.StatusOK {
173 | 				bodyBytes, _ := io.ReadAll(resp.Body)
174 | 				t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(bodyBytes))
175 | 			}
176 | 
177 | 			var result struct {
178 | 				Result string `json:"result"`
179 | 			}
180 | 			if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
181 | 				t.Fatalf("failed to decode response: %v", err)
182 | 			}
183 | 
184 | 			var got, want []map[string]any
185 | 			if err := json.Unmarshal([]byte(result.Result), &got); err != nil {
186 | 				t.Fatalf("failed to unmarshal result: %v", err)
187 | 			}
188 | 			if err := json.Unmarshal([]byte(tc.want), &want); err != nil {
189 | 				t.Fatalf("failed to unmarshal want: %v", err)
190 | 			}
191 | 
192 | 			if !reflect.DeepEqual(got, want) {
193 | 				t.Fatalf("unexpected result: got %+v, want %+v", got, want)
194 | 			}
195 | 		})
196 | 	}
197 | }
198 | 
199 | func getListDatabasesToolsConfig() map[string]any {
200 | 	return map[string]any{
201 | 		"sources": map[string]any{
202 | 			"my-cloud-sql-source": map[string]any{
203 | 				"kind": "cloud-sql-admin",
204 | 			},
205 | 		},
206 | 		"tools": map[string]any{
207 | 			"list-databases": map[string]any{
208 | 				"kind":   listDatabasesToolKind,
209 | 				"source": "my-cloud-sql-source",
210 | 			},
211 | 		},
212 | 	}
213 | }
214 | 
```

--------------------------------------------------------------------------------
/internal/sources/cloudsqlpg/cloud_sql_pg_test.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2024 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package cloudsqlpg_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/cloudsqlpg"
 24 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 25 | )
 26 | 
 27 | func TestParseFromYamlCloudSQLPg(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-pg-instance:
 38 | 					kind: cloud-sql-postgres
 39 | 					project: my-project
 40 | 					region: my-region
 41 | 					instance: my-instance
 42 | 					database: my_db
 43 | 					user: my_user
 44 | 					password: my_pass
 45 | 			`,
 46 | 			want: server.SourceConfigs{
 47 | 				"my-pg-instance": cloudsqlpg.Config{
 48 | 					Name:     "my-pg-instance",
 49 | 					Kind:     cloudsqlpg.SourceKind,
 50 | 					Project:  "my-project",
 51 | 					Region:   "my-region",
 52 | 					Instance: "my-instance",
 53 | 					IPType:   "public",
 54 | 					Database: "my_db",
 55 | 					User:     "my_user",
 56 | 					Password: "my_pass",
 57 | 				},
 58 | 			},
 59 | 		},
 60 | 		{
 61 | 			desc: "public ipType",
 62 | 			in: `
 63 | 			sources:
 64 | 				my-pg-instance:
 65 | 					kind: cloud-sql-postgres
 66 | 					project: my-project
 67 | 					region: my-region
 68 | 					instance: my-instance
 69 | 					ipType: Public
 70 | 					database: my_db
 71 | 					user: my_user
 72 | 					password: my_pass
 73 | 			`,
 74 | 			want: server.SourceConfigs{
 75 | 				"my-pg-instance": cloudsqlpg.Config{
 76 | 					Name:     "my-pg-instance",
 77 | 					Kind:     cloudsqlpg.SourceKind,
 78 | 					Project:  "my-project",
 79 | 					Region:   "my-region",
 80 | 					Instance: "my-instance",
 81 | 					IPType:   "public",
 82 | 					Database: "my_db",
 83 | 					User:     "my_user",
 84 | 					Password: "my_pass",
 85 | 				},
 86 | 			},
 87 | 		},
 88 | 		{
 89 | 			desc: "private ipType",
 90 | 			in: `
 91 | 			sources:
 92 | 				my-pg-instance:
 93 | 					kind: cloud-sql-postgres
 94 | 					project: my-project
 95 | 					region: my-region
 96 | 					instance: my-instance
 97 | 					ipType: private 
 98 | 					database: my_db
 99 | 					user: my_user
100 | 					password: my_pass
101 | 			`,
102 | 			want: server.SourceConfigs{
103 | 				"my-pg-instance": cloudsqlpg.Config{
104 | 					Name:     "my-pg-instance",
105 | 					Kind:     cloudsqlpg.SourceKind,
106 | 					Project:  "my-project",
107 | 					Region:   "my-region",
108 | 					Instance: "my-instance",
109 | 					IPType:   "private",
110 | 					Database: "my_db",
111 | 					User:     "my_user",
112 | 					Password: "my_pass",
113 | 				},
114 | 			},
115 | 		},
116 | 		{
117 | 			desc: "psc ipType",
118 | 			in: `
119 | 			sources:
120 | 				my-pg-instance:
121 | 					kind: cloud-sql-postgres
122 | 					project: my-project
123 | 					region: my-region
124 | 					instance: my-instance
125 | 					ipType: psc 
126 | 					database: my_db
127 | 					user: my_user
128 | 					password: my_pass
129 | 			`,
130 | 			want: server.SourceConfigs{
131 | 				"my-pg-instance": cloudsqlpg.Config{
132 | 					Name:     "my-pg-instance",
133 | 					Kind:     cloudsqlpg.SourceKind,
134 | 					Project:  "my-project",
135 | 					Region:   "my-region",
136 | 					Instance: "my-instance",
137 | 					IPType:   "psc",
138 | 					Database: "my_db",
139 | 					User:     "my_user",
140 | 					Password: "my_pass",
141 | 				},
142 | 			},
143 | 		},
144 | 	}
145 | 	for _, tc := range tcs {
146 | 		t.Run(tc.desc, func(t *testing.T) {
147 | 			got := struct {
148 | 				Sources server.SourceConfigs `yaml:"sources"`
149 | 			}{}
150 | 			// Parse contents
151 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
152 | 			if err != nil {
153 | 				t.Fatalf("unable to unmarshal: %s", err)
154 | 			}
155 | 			if !cmp.Equal(tc.want, got.Sources) {
156 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
157 | 			}
158 | 		})
159 | 	}
160 | 
161 | }
162 | 
163 | func TestFailParseFromYaml(t *testing.T) {
164 | 	tcs := []struct {
165 | 		desc string
166 | 		in   string
167 | 		err  string
168 | 	}{
169 | 		{
170 | 			desc: "invalid ipType",
171 | 			in: `
172 | 			sources:
173 | 				my-pg-instance:
174 | 					kind: cloud-sql-postgres
175 | 					project: my-project
176 | 					region: my-region
177 | 					instance: my-instance
178 | 					ipType: fail 
179 | 					database: my_db
180 | 					user: my_user
181 | 					password: my_pass
182 | 			`,
183 | 			err: "unable to parse source \"my-pg-instance\" as \"cloud-sql-postgres\": ipType invalid: must be one of \"public\", \"private\", or \"psc\"",
184 | 		},
185 | 		{
186 | 			desc: "extra field",
187 | 			in: `
188 | 			sources:
189 | 				my-pg-instance:
190 | 					kind: cloud-sql-postgres
191 | 					project: my-project
192 | 					region: my-region
193 | 					instance: my-instance
194 | 					database: my_db
195 | 					user: my_user
196 | 					password: my_pass
197 | 					foo: bar
198 | 			`,
199 | 			err: "unable to parse source \"my-pg-instance\" as \"cloud-sql-postgres\": [2:1] unknown field \"foo\"\n   1 | database: my_db\n>  2 | foo: bar\n       ^\n   3 | instance: my-instance\n   4 | kind: cloud-sql-postgres\n   5 | password: my_pass\n   6 | ",
200 | 		},
201 | 		{
202 | 			desc: "missing required field",
203 | 			in: `
204 | 			sources:
205 | 				my-pg-instance:
206 | 					kind: cloud-sql-postgres
207 | 					region: my-region
208 | 					instance: my-instance
209 | 					database: my_db
210 | 					user: my_user
211 | 					password: my_pass
212 | 			`,
213 | 			err: "unable to parse source \"my-pg-instance\" as \"cloud-sql-postgres\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
214 | 		},
215 | 	}
216 | 	for _, tc := range tcs {
217 | 		t.Run(tc.desc, func(t *testing.T) {
218 | 			got := struct {
219 | 				Sources server.SourceConfigs `yaml:"sources"`
220 | 			}{}
221 | 			// Parse contents
222 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
223 | 			if err == nil {
224 | 				t.Fatalf("expect parsing to fail")
225 | 			}
226 | 			errStr := err.Error()
227 | 			if errStr != tc.err {
228 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
229 | 			}
230 | 		})
231 | 	}
232 | }
233 | 
```

--------------------------------------------------------------------------------
/internal/tools/yugabytedbsql/yugabytedbsql_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 yugabytedbsql_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/yugabytedbsql"
 26 | )
 27 | 
 28 | func TestParseFromYamlYugabyteDBSQL(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 valid config",
 40 | 			in: `
 41 | 			tools:
 42 | 			  hotel_search:
 43 | 			    kind: yugabytedb-sql
 44 | 			    source: yb-source
 45 | 			    description: search hotels by city
 46 | 			    statement: |
 47 | 			      SELECT * FROM hotels WHERE city = $1;
 48 | 			    authRequired:
 49 | 			      - auth-service-a
 50 | 			      - auth-service-b
 51 | 			    parameters:
 52 | 			      - name: city
 53 | 			        type: string
 54 | 			        description: city name
 55 | 			        authServices:
 56 | 			          - name: auth-service-a
 57 | 			            field: user_id
 58 | 			          - name: auth-service-b
 59 | 			            field: user_id
 60 | 			`,
 61 | 			want: server.ToolConfigs{
 62 | 				"hotel_search": yugabytedbsql.Config{
 63 | 					Name:         "hotel_search",
 64 | 					Kind:         "yugabytedb-sql",
 65 | 					Source:       "yb-source",
 66 | 					Description:  "search hotels by city",
 67 | 					Statement:    "SELECT * FROM hotels WHERE city = $1;\n",
 68 | 					AuthRequired: []string{"auth-service-a", "auth-service-b"},
 69 | 					Parameters: []tools.Parameter{
 70 | 						tools.NewStringParameterWithAuth("city", "city name",
 71 | 							[]tools.ParamAuthService{
 72 | 								{Name: "auth-service-a", Field: "user_id"},
 73 | 								{Name: "auth-service-b", Field: "user_id"},
 74 | 							},
 75 | 						),
 76 | 					},
 77 | 				},
 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 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 88 | 			if err != nil {
 89 | 				t.Fatalf("unable to unmarshal: %s", err)
 90 | 			}
 91 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 92 | 				t.Fatalf("incorrect parse: diff %v", diff)
 93 | 			}
 94 | 		})
 95 | 	}
 96 | }
 97 | 
 98 | func TestFailParseFromYamlYugabyteDBSQL(t *testing.T) {
 99 | 	ctx, err := testutils.ContextWithNewLogger()
100 | 	if err != nil {
101 | 		t.Fatalf("unexpected error: %s", err)
102 | 	}
103 | 	cases := []struct {
104 | 		desc string
105 | 		in   string
106 | 	}{
107 | 		{
108 | 			desc: "missing required field (statement)",
109 | 			in: `
110 | 			tools:
111 | 			  tool1:
112 | 			    kind: yugabytedb-sql
113 | 			    source: yb-source
114 | 			    description: incomplete config
115 | 			`,
116 | 		},
117 | 		{
118 | 			desc: "unknown field (foo)",
119 | 			in: `
120 | 			tools:
121 | 			  tool2:
122 | 			    kind: yugabytedb-sql
123 | 			    source: yb-source
124 | 			    description: test
125 | 			    statement: SELECT 1;
126 | 			    foo: bar
127 | 			`,
128 | 		},
129 | 	}
130 | 	for _, tc := range cases {
131 | 		t.Run(tc.desc, func(t *testing.T) {
132 | 			cfg := struct {
133 | 				Tools server.ToolConfigs `yaml:"tools"`
134 | 			}{}
135 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &cfg)
136 | 			if err == nil {
137 | 				t.Fatalf("expected error but got none")
138 | 			}
139 | 		})
140 | 	}
141 | }
142 | 
143 | func TestParseFromYamlWithTemplateParamsYugabyteDB(t *testing.T) {
144 | 	ctx, err := testutils.ContextWithNewLogger()
145 | 	if err != nil {
146 | 		t.Fatalf("unexpected error: %s", err)
147 | 	}
148 | 	tcs := []struct {
149 | 		desc string
150 | 		in   string
151 | 		want server.ToolConfigs
152 | 	}{
153 | 		{
154 | 			desc: "basic example",
155 | 			in: `
156 | 			tools:
157 | 				example_tool:
158 | 					kind: yugabytedb-sql
159 | 					source: my-yb-instance
160 | 					description: some description
161 | 					statement: |
162 | 						SELECT * FROM SQL_STATEMENT;
163 | 					parameters:
164 | 						- name: name
165 | 						  type: string
166 | 						  description: some description
167 | 					templateParameters:
168 | 						- name: tableName
169 | 						  type: string
170 | 						  description: The table to select hotels from.
171 | 						- name: fieldArray
172 | 						  type: array
173 | 						  description: The columns to return for the query.
174 | 						  items: 
175 | 								name: column
176 | 								type: string
177 | 								description: A column name that will be returned from the query.
178 | 			`,
179 | 			want: server.ToolConfigs{
180 | 				"example_tool": yugabytedbsql.Config{
181 | 					Name:         "example_tool",
182 | 					Kind:         "yugabytedb-sql",
183 | 					Source:       "my-yb-instance",
184 | 					Description:  "some description",
185 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
186 | 					AuthRequired: []string{},
187 | 					Parameters: []tools.Parameter{
188 | 						tools.NewStringParameter("name", "some description"),
189 | 					},
190 | 					TemplateParameters: []tools.Parameter{
191 | 						tools.NewStringParameter("tableName", "The table to select hotels from."),
192 | 						tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
193 | 					},
194 | 				},
195 | 			},
196 | 		},
197 | 	}
198 | 	for _, tc := range tcs {
199 | 		t.Run(tc.desc, func(t *testing.T) {
200 | 			got := struct {
201 | 				Tools server.ToolConfigs `yaml:"tools"`
202 | 			}{}
203 | 			// Parse contents
204 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
205 | 			if err != nil {
206 | 				t.Fatalf("unable to unmarshal: %s", err)
207 | 			}
208 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
209 | 				t.Fatalf("incorrect parse: diff %v", diff)
210 | 			}
211 | 		})
212 | 	}
213 | 
214 | }
215 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/firebird/firebird-sql.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "firebird-sql"
  3 | type: docs
  4 | weight: 1
  5 | description: > 
  6 |   A "firebird-sql" tool executes a pre-defined SQL statement against a Firebird
  7 |   database.
  8 | aliases:
  9 | - /resources/tools/firebird-sql
 10 | ---
 11 | 
 12 | ## About
 13 | 
 14 | A `firebird-sql` tool executes a pre-defined SQL statement against a Firebird
 15 | database. It's compatible with the following source:
 16 | 
 17 | - [firebird](../sources/firebird.md)
 18 | 
 19 | The specified SQL statement is executed as a [prepared statement][fb-prepare],
 20 | and supports both positional parameters (`?`) and named parameters (`:param_name`).
 21 | Parameters will be inserted according to their position or name. If template 
 22 | parameters are included, they will be resolved before the execution of the 
 23 | prepared statement.
 24 | 
 25 | [fb-prepare]: https://firebirdsql.org/refdocs/langrefupd25-psql-execstat.html
 26 | 
 27 | ## Example
 28 | 
 29 | > **Note:** This tool uses parameterized queries to prevent SQL injections.
 30 | > Query parameters can be used as substitutes for arbitrary expressions.
 31 | > Parameters cannot be used as substitutes for identifiers, column names, table
 32 | > names, or other parts of the query.
 33 | 
 34 | ```yaml
 35 | tools:
 36 |   search_flights_by_number:
 37 |     kind: firebird-sql
 38 |     source: my_firebird_db
 39 |     statement: |
 40 |       SELECT * FROM flights
 41 |       WHERE airline = ?
 42 |       AND flight_number = ?
 43 |       LIMIT 10
 44 |     description: |
 45 |       Use this tool to get information for a specific flight.
 46 |       Takes an airline code and flight number and returns info on the flight.
 47 |       Do NOT use this tool with a flight id. Do NOT guess an airline code or flight number.
 48 |       A airline code is a code for an airline service consisting of two-character
 49 |       airline designator and followed by flight number, which is 1 to 4 digit number.
 50 |       For example, if given CY 0123, the airline is "CY", and flight_number is "123".
 51 |       Another example for this is DL 1234, the airline is "DL", and flight_number is "1234".
 52 |       If the tool returns more than one option choose the date closes to today.
 53 |       Example:
 54 |       {{
 55 |           "airline": "CY",
 56 |           "flight_number": "888",
 57 |       }}
 58 |       Example:
 59 |       {{
 60 |           "airline": "DL",
 61 |           "flight_number": "1234",
 62 |       }}
 63 |     parameters:
 64 |       - name: airline
 65 |         type: string
 66 |         description: Airline unique 2 letter identifier
 67 |       - name: flight_number
 68 |         type: string
 69 |         description: 1 to 4 digit number
 70 | ```
 71 | 
 72 | ### Example with Named Parameters
 73 | 
 74 | ```yaml
 75 | tools:
 76 |   search_flights_by_airline:
 77 |     kind: firebird-sql
 78 |     source: my_firebird_db
 79 |     statement: |
 80 |       SELECT * FROM flights
 81 |       WHERE airline = :airline
 82 |       AND departure_date >= :start_date
 83 |       AND departure_date <= :end_date
 84 |       ORDER BY departure_date
 85 |     description: |
 86 |       Search for flights by airline within a date range using named parameters.
 87 |     parameters:
 88 |       - name: airline
 89 |         type: string
 90 |         description: Airline unique 2 letter identifier
 91 |       - name: start_date
 92 |         type: string
 93 |         description: Start date in YYYY-MM-DD format
 94 |       - name: end_date
 95 |         type: string
 96 |         description: End date in YYYY-MM-DD format
 97 | ```
 98 | 
 99 | ### Example with Template Parameters
100 | 
101 | > **Note:** This tool allows direct modifications to the SQL statement,
102 | > including identifiers, column names, and table names. **This makes it more
103 | > vulnerable to SQL injections**. Using basic parameters only (see above) is
104 | > recommended for performance and safety reasons. For more details, please check
105 | > [templateParameters](_index#template-parameters).
106 | 
107 | ```yaml
108 | tools:
109 |   list_table:
110 |     kind: firebird-sql
111 |     source: my_firebird_db
112 |     statement: |
113 |       SELECT * FROM {{.tableName}}
114 |     description: |
115 |       Use this tool to list all information from a specific table.
116 |       Example:
117 |       {{
118 |           "tableName": "flights",
119 |       }}
120 |     templateParameters:
121 |       - name: tableName
122 |         type: string
123 |         description: Table to select from
124 | ```
125 | 
126 | ## Reference
127 | 
128 | | **field**          |                     **type**                     | **required** | **description**                                                                                                                            |
129 | |--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
130 | | kind               |                      string                      |     true     | Must be "firebird-sql".                                                                                                                    |
131 | | source             |                      string                      |     true     | Name of the source the SQL should execute on.                                                                                              |
132 | | description        |                      string                      |     true     | Description of the tool that is passed to the LLM.                                                                                         |
133 | | statement          |                      string                      |     true     | SQL statement to execute on.                                                                                                               |
134 | | parameters         |    [parameters](_index#specifying-parameters)    |    false     | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement.                                           |
135 | | templateParameters | [templateParameters](_index#template-parameters) |    false     | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
```

--------------------------------------------------------------------------------
/internal/tools/mysql/mysqlexecutesql/mysqlexecutesql.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 mysqlexecutesql
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmysql"
 25 | 	"github.com/googleapis/genai-toolbox/internal/sources/mysql"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 27 | 	"github.com/googleapis/genai-toolbox/internal/tools/mysql/mysqlcommon"
 28 | 	"github.com/googleapis/genai-toolbox/internal/util"
 29 | )
 30 | 
 31 | const kind string = "mysql-execute-sql"
 32 | 
 33 | func init() {
 34 | 	if !tools.Register(kind, newConfig) {
 35 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 36 | 	}
 37 | }
 38 | 
 39 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 40 | 	actual := Config{Name: name}
 41 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 42 | 		return nil, err
 43 | 	}
 44 | 	return actual, nil
 45 | }
 46 | 
 47 | type compatibleSource interface {
 48 | 	MySQLPool() *sql.DB
 49 | }
 50 | 
 51 | // validate compatible sources are still compatible
 52 | var _ compatibleSource = &cloudsqlmysql.Source{}
 53 | var _ compatibleSource = &mysql.Source{}
 54 | 
 55 | var compatibleSources = [...]string{cloudsqlmysql.SourceKind, mysql.SourceKind}
 56 | 
 57 | type Config struct {
 58 | 	Name         string   `yaml:"name" validate:"required"`
 59 | 	Kind         string   `yaml:"kind" validate:"required"`
 60 | 	Source       string   `yaml:"source" validate:"required"`
 61 | 	Description  string   `yaml:"description" validate:"required"`
 62 | 	AuthRequired []string `yaml:"authRequired"`
 63 | }
 64 | 
 65 | // validate interface
 66 | var _ tools.ToolConfig = Config{}
 67 | 
 68 | func (cfg Config) ToolConfigKind() string {
 69 | 	return kind
 70 | }
 71 | 
 72 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 73 | 	// verify source exists
 74 | 	rawS, ok := srcs[cfg.Source]
 75 | 	if !ok {
 76 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 77 | 	}
 78 | 
 79 | 	// verify the source is compatible
 80 | 	s, ok := rawS.(compatibleSource)
 81 | 	if !ok {
 82 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 83 | 	}
 84 | 
 85 | 	sqlParameter := tools.NewStringParameter("sql", "The sql to execute.")
 86 | 	parameters := tools.Parameters{sqlParameter}
 87 | 
 88 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 89 | 
 90 | 	// finish tool setup
 91 | 	t := Tool{
 92 | 		Name:         cfg.Name,
 93 | 		Kind:         kind,
 94 | 		Parameters:   parameters,
 95 | 		AuthRequired: cfg.AuthRequired,
 96 | 		Pool:         s.MySQLPool(),
 97 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 98 | 		mcpManifest:  mcpManifest,
 99 | 	}
100 | 	return t, nil
101 | }
102 | 
103 | // validate interface
104 | var _ tools.Tool = Tool{}
105 | 
106 | type Tool struct {
107 | 	Name         string           `yaml:"name"`
108 | 	Kind         string           `yaml:"kind"`
109 | 	AuthRequired []string         `yaml:"authRequired"`
110 | 	Parameters   tools.Parameters `yaml:"parameters"`
111 | 
112 | 	Pool        *sql.DB
113 | 	manifest    tools.Manifest
114 | 	mcpManifest tools.McpManifest
115 | }
116 | 
117 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
118 | 	paramsMap := params.AsMap()
119 | 	sql, ok := paramsMap["sql"].(string)
120 | 	if !ok {
121 | 		return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
122 | 	}
123 | 
124 | 	// Log the query executed for debugging.
125 | 	logger, err := util.LoggerFromContext(ctx)
126 | 	if err != nil {
127 | 		return nil, fmt.Errorf("error getting logger: %s", err)
128 | 	}
129 | 	logger.DebugContext(ctx, "executing `%s` tool query: %s", kind, sql)
130 | 
131 | 	results, err := t.Pool.QueryContext(ctx, sql)
132 | 	if err != nil {
133 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
134 | 	}
135 | 	defer results.Close()
136 | 
137 | 	cols, err := results.Columns()
138 | 	if err != nil {
139 | 		return nil, fmt.Errorf("unable to retrieve rows column name: %w", err)
140 | 	}
141 | 
142 | 	// create an array of values for each column, which can be re-used to scan each row
143 | 	rawValues := make([]any, len(cols))
144 | 	values := make([]any, len(cols))
145 | 	for i := range rawValues {
146 | 		values[i] = &rawValues[i]
147 | 	}
148 | 
149 | 	colTypes, err := results.ColumnTypes()
150 | 	if err != nil {
151 | 		return nil, fmt.Errorf("unable to get column types: %w", err)
152 | 	}
153 | 
154 | 	var out []any
155 | 	for results.Next() {
156 | 		err := results.Scan(values...)
157 | 		if err != nil {
158 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
159 | 		}
160 | 		vMap := make(map[string]any)
161 | 		for i, name := range cols {
162 | 			val := rawValues[i]
163 | 			if val == nil {
164 | 				vMap[name] = nil
165 | 				continue
166 | 			}
167 | 
168 | 			vMap[name], err = mysqlcommon.ConvertToType(colTypes[i], val)
169 | 			if err != nil {
170 | 				return nil, fmt.Errorf("errors encountered when converting values: %w", err)
171 | 			}
172 | 		}
173 | 		out = append(out, vMap)
174 | 	}
175 | 
176 | 	if err := results.Err(); err != nil {
177 | 		return nil, fmt.Errorf("errors encountered during row iteration: %w", err)
178 | 	}
179 | 
180 | 	return out, nil
181 | }
182 | 
183 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
184 | 	return tools.ParseParams(t.Parameters, data, claims)
185 | }
186 | 
187 | func (t Tool) Manifest() tools.Manifest {
188 | 	return t.manifest
189 | }
190 | 
191 | func (t Tool) McpManifest() tools.McpManifest {
192 | 	return t.mcpManifest
193 | }
194 | 
195 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
196 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
197 | }
198 | 
199 | func (t Tool) RequiresClientAuthorization() bool {
200 | 	return false
201 | }
202 | 
```
Page 17/47FirstPrevNextLast