#
tokens: 48593/50000 18/807 files (page 16/47)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 16 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/looker/lookerqueryurl/lookerqueryurl.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package lookerqueryurl
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 22 | 	lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
 25 | 	"github.com/googleapis/genai-toolbox/internal/util"
 26 | 
 27 | 	"github.com/looker-open-source/sdk-codegen/go/rtl"
 28 | 	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
 29 | )
 30 | 
 31 | const kind string = "looker-query-url"
 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 Config struct {
 48 | 	Name         string   `yaml:"name" validate:"required"`
 49 | 	Kind         string   `yaml:"kind" validate:"required"`
 50 | 	Source       string   `yaml:"source" validate:"required"`
 51 | 	Description  string   `yaml:"description" validate:"required"`
 52 | 	AuthRequired []string `yaml:"authRequired"`
 53 | }
 54 | 
 55 | // validate interface
 56 | var _ tools.ToolConfig = Config{}
 57 | 
 58 | func (cfg Config) ToolConfigKind() string {
 59 | 	return kind
 60 | }
 61 | 
 62 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 63 | 	// verify source exists
 64 | 	rawS, ok := srcs[cfg.Source]
 65 | 	if !ok {
 66 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 67 | 	}
 68 | 
 69 | 	// verify the source is compatible
 70 | 	s, ok := rawS.(*lookersrc.Source)
 71 | 	if !ok {
 72 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `looker`", kind)
 73 | 	}
 74 | 
 75 | 	parameters := lookercommon.GetQueryParameters()
 76 | 
 77 | 	vizParameter := tools.NewMapParameterWithDefault("vis_config",
 78 | 		map[string]any{},
 79 | 		"The visualization config for the query",
 80 | 		"",
 81 | 	)
 82 | 	parameters = append(parameters, vizParameter)
 83 | 
 84 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 85 | 
 86 | 	// finish tool setup
 87 | 	return Tool{
 88 | 		Name:           cfg.Name,
 89 | 		Kind:           kind,
 90 | 		Parameters:     parameters,
 91 | 		AuthRequired:   cfg.AuthRequired,
 92 | 		UseClientOAuth: s.UseClientOAuth,
 93 | 		Client:         s.Client,
 94 | 		ApiSettings:    s.ApiSettings,
 95 | 		manifest: tools.Manifest{
 96 | 			Description:  cfg.Description,
 97 | 			Parameters:   parameters.Manifest(),
 98 | 			AuthRequired: cfg.AuthRequired,
 99 | 		},
100 | 		mcpManifest: mcpManifest,
101 | 	}, nil
102 | }
103 | 
104 | // validate interface
105 | var _ tools.Tool = Tool{}
106 | 
107 | type Tool struct {
108 | 	Name           string `yaml:"name"`
109 | 	Kind           string `yaml:"kind"`
110 | 	UseClientOAuth bool
111 | 	Client         *v4.LookerSDK
112 | 	ApiSettings    *rtl.ApiSettings
113 | 	AuthRequired   []string         `yaml:"authRequired"`
114 | 	Parameters     tools.Parameters `yaml:"parameters"`
115 | 	manifest       tools.Manifest
116 | 	mcpManifest    tools.McpManifest
117 | }
118 | 
119 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
120 | 	logger, err := util.LoggerFromContext(ctx)
121 | 	if err != nil {
122 | 		return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
123 | 	}
124 | 	logger.DebugContext(ctx, "params = ", params)
125 | 	wq, err := lookercommon.ProcessQueryArgs(ctx, params)
126 | 	if err != nil {
127 | 		return nil, fmt.Errorf("error building query request: %w", err)
128 | 	}
129 | 
130 | 	paramsMap := params.AsMap()
131 | 	visConfig := paramsMap["vis_config"].(map[string]any)
132 | 	wq.VisConfig = &visConfig
133 | 
134 | 	sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
135 | 	if err != nil {
136 | 		return nil, fmt.Errorf("error getting sdk: %w", err)
137 | 	}
138 | 	respFields := "id,slug,share_url,expanded_share_url"
139 | 	resp, err := sdk.CreateQuery(*wq, respFields, t.ApiSettings)
140 | 	if err != nil {
141 | 		return nil, fmt.Errorf("error making query request: %s", err)
142 | 	}
143 | 	logger.DebugContext(ctx, "resp = ", resp)
144 | 
145 | 	data := make(map[string]any)
146 | 	if resp.Id != nil {
147 | 		data["id"] = *resp.Id
148 | 	}
149 | 	if resp.Slug != nil {
150 | 		data["slug"] = *resp.Slug
151 | 	}
152 | 	if resp.ShareUrl != nil {
153 | 		data["url"] = *resp.ShareUrl
154 | 	}
155 | 	if resp.ExpandedShareUrl != nil {
156 | 		data["long_url"] = *resp.ExpandedShareUrl
157 | 	}
158 | 	logger.DebugContext(ctx, "data = %v", data)
159 | 
160 | 	return data, 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 t.UseClientOAuth
181 | }
182 | 
```

--------------------------------------------------------------------------------
/internal/tools/looker/lookergetprojectfiles/lookergetprojectfiles.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package lookergetprojectfiles
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 22 | 	lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
 25 | 	"github.com/googleapis/genai-toolbox/internal/util"
 26 | 
 27 | 	"github.com/looker-open-source/sdk-codegen/go/rtl"
 28 | 	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
 29 | )
 30 | 
 31 | const kind string = "looker-get-project-files"
 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 Config struct {
 48 | 	Name         string   `yaml:"name" validate:"required"`
 49 | 	Kind         string   `yaml:"kind" validate:"required"`
 50 | 	Source       string   `yaml:"source" validate:"required"`
 51 | 	Description  string   `yaml:"description" validate:"required"`
 52 | 	AuthRequired []string `yaml:"authRequired"`
 53 | }
 54 | 
 55 | // validate interface
 56 | var _ tools.ToolConfig = Config{}
 57 | 
 58 | func (cfg Config) ToolConfigKind() string {
 59 | 	return kind
 60 | }
 61 | 
 62 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 63 | 	// verify source exists
 64 | 	rawS, ok := srcs[cfg.Source]
 65 | 	if !ok {
 66 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 67 | 	}
 68 | 
 69 | 	// verify the source is compatible
 70 | 	s, ok := rawS.(*lookersrc.Source)
 71 | 	if !ok {
 72 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `looker`", kind)
 73 | 	}
 74 | 
 75 | 	projectIdParameter := tools.NewStringParameter("project_id", "The id of the project containing the files")
 76 | 	parameters := tools.Parameters{projectIdParameter}
 77 | 
 78 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 79 | 
 80 | 	// finish tool setup
 81 | 	return Tool{
 82 | 		Name:           cfg.Name,
 83 | 		Kind:           kind,
 84 | 		Parameters:     parameters,
 85 | 		AuthRequired:   cfg.AuthRequired,
 86 | 		UseClientOAuth: s.UseClientOAuth,
 87 | 		Client:         s.Client,
 88 | 		ApiSettings:    s.ApiSettings,
 89 | 		manifest: tools.Manifest{
 90 | 			Description:  cfg.Description,
 91 | 			Parameters:   parameters.Manifest(),
 92 | 			AuthRequired: cfg.AuthRequired,
 93 | 		},
 94 | 		mcpManifest: mcpManifest,
 95 | 	}, nil
 96 | }
 97 | 
 98 | // validate interface
 99 | var _ tools.Tool = Tool{}
100 | 
101 | type Tool struct {
102 | 	Name           string `yaml:"name"`
103 | 	Kind           string `yaml:"kind"`
104 | 	UseClientOAuth bool
105 | 	Client         *v4.LookerSDK
106 | 	ApiSettings    *rtl.ApiSettings
107 | 	AuthRequired   []string         `yaml:"authRequired"`
108 | 	Parameters     tools.Parameters `yaml:"parameters"`
109 | 	manifest       tools.Manifest
110 | 	mcpManifest    tools.McpManifest
111 | }
112 | 
113 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
114 | 	logger, err := util.LoggerFromContext(ctx)
115 | 	if err != nil {
116 | 		return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
117 | 	}
118 | 
119 | 	sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
120 | 	if err != nil {
121 | 		return nil, fmt.Errorf("error getting sdk: %w", err)
122 | 	}
123 | 
124 | 	mapParams := params.AsMap()
125 | 	projectId, ok := mapParams["project_id"].(string)
126 | 	if !ok {
127 | 		return nil, fmt.Errorf("'project_id' must be a string, got %T", mapParams["project_id"])
128 | 	}
129 | 
130 | 	resp, err := sdk.AllProjectFiles(projectId, "", t.ApiSettings)
131 | 	if err != nil {
132 | 		return nil, fmt.Errorf("error making get_project_files request: %s", err)
133 | 	}
134 | 
135 | 	var data []any
136 | 	for _, v := range resp {
137 | 		logger.DebugContext(ctx, "Got response element of %v\n", v)
138 | 		vMap := make(map[string]any)
139 | 		if v.Id != nil {
140 | 			vMap["id"] = *v.Id
141 | 		}
142 | 		if v.Path != nil {
143 | 			vMap["path"] = *v.Path
144 | 		}
145 | 		if v.Title != nil {
146 | 			vMap["title"] = *v.Title
147 | 		}
148 | 		if v.Type != nil {
149 | 			vMap["type"] = *v.Type
150 | 		}
151 | 		if v.Extension != nil {
152 | 			vMap["extension"] = *v.Extension
153 | 		}
154 | 		if v.Editable != nil {
155 | 			vMap["editable"] = *v.Editable
156 | 		}
157 | 		logger.DebugContext(ctx, "Converted to %v\n", vMap)
158 | 		data = append(data, vMap)
159 | 	}
160 | 	logger.DebugContext(ctx, "data = ", data)
161 | 
162 | 	return data, nil
163 | }
164 | 
165 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
166 | 	return tools.ParseParams(t.Parameters, data, claims)
167 | }
168 | 
169 | func (t Tool) Manifest() tools.Manifest {
170 | 	return t.manifest
171 | }
172 | 
173 | func (t Tool) McpManifest() tools.McpManifest {
174 | 	return t.mcpManifest
175 | }
176 | 
177 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
178 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
179 | }
180 | 
181 | func (t Tool) RequiresClientAuthorization() bool {
182 | 	return t.UseClientOAuth
183 | }
184 | 
```

--------------------------------------------------------------------------------
/internal/tools/looker/lookercreateprojectfile/lookercreateprojectfile.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package lookercreateprojectfile
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 22 | 	lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
 25 | 
 26 | 	"github.com/looker-open-source/sdk-codegen/go/rtl"
 27 | 	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
 28 | )
 29 | 
 30 | const kind string = "looker-create-project-file"
 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 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 (cfg Config) ToolConfigKind() string {
 58 | 	return kind
 59 | }
 60 | 
 61 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 62 | 	// verify source exists
 63 | 	rawS, ok := srcs[cfg.Source]
 64 | 	if !ok {
 65 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 66 | 	}
 67 | 
 68 | 	// verify the source is compatible
 69 | 	s, ok := rawS.(*lookersrc.Source)
 70 | 	if !ok {
 71 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `looker`", kind)
 72 | 	}
 73 | 
 74 | 	projectIdParameter := tools.NewStringParameter("project_id", "The id of the project containing the files")
 75 | 	filePathParameter := tools.NewStringParameter("file_path", "The path of the file within the project")
 76 | 	fileContentParameter := tools.NewStringParameter("file_content", "The content of the file")
 77 | 	parameters := tools.Parameters{projectIdParameter, filePathParameter, fileContentParameter}
 78 | 
 79 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 80 | 
 81 | 	// finish tool setup
 82 | 	return Tool{
 83 | 		Name:           cfg.Name,
 84 | 		Kind:           kind,
 85 | 		Parameters:     parameters,
 86 | 		AuthRequired:   cfg.AuthRequired,
 87 | 		UseClientOAuth: s.UseClientOAuth,
 88 | 		Client:         s.Client,
 89 | 		ApiSettings:    s.ApiSettings,
 90 | 		manifest: tools.Manifest{
 91 | 			Description:  cfg.Description,
 92 | 			Parameters:   parameters.Manifest(),
 93 | 			AuthRequired: cfg.AuthRequired,
 94 | 		},
 95 | 		mcpManifest: mcpManifest,
 96 | 	}, nil
 97 | }
 98 | 
 99 | // validate interface
100 | var _ tools.Tool = Tool{}
101 | 
102 | type Tool struct {
103 | 	Name           string `yaml:"name"`
104 | 	Kind           string `yaml:"kind"`
105 | 	UseClientOAuth bool
106 | 	Client         *v4.LookerSDK
107 | 	ApiSettings    *rtl.ApiSettings
108 | 	AuthRequired   []string         `yaml:"authRequired"`
109 | 	Parameters     tools.Parameters `yaml:"parameters"`
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 | 	sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
116 | 	if err != nil {
117 | 		return nil, fmt.Errorf("error getting sdk: %w", err)
118 | 	}
119 | 
120 | 	mapParams := params.AsMap()
121 | 	projectId, ok := mapParams["project_id"].(string)
122 | 	if !ok {
123 | 		return nil, fmt.Errorf("'project_id' must be a string, got %T", mapParams["project_id"])
124 | 	}
125 | 	filePath, ok := mapParams["file_path"].(string)
126 | 	if !ok {
127 | 		return nil, fmt.Errorf("'file_path' must be a string, got %T", mapParams["file_path"])
128 | 	}
129 | 	fileContent, ok := mapParams["file_content"].(string)
130 | 	if !ok {
131 | 		return nil, fmt.Errorf("'file_content' must be a string, got %T", mapParams["file_content"])
132 | 	}
133 | 
134 | 	req := lookercommon.FileContent{
135 | 		Path:    filePath,
136 | 		Content: fileContent,
137 | 	}
138 | 
139 | 	err = lookercommon.CreateProjectFile(sdk, projectId, req, t.ApiSettings)
140 | 	if err != nil {
141 | 		return nil, fmt.Errorf("error making create_project_file request: %s", err)
142 | 	}
143 | 
144 | 	data := make(map[string]any)
145 | 	data["type"] = "text"
146 | 	data["text"] = fmt.Sprintf("created file %s in project %s", filePath, projectId)
147 | 
148 | 	return data, nil
149 | }
150 | 
151 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
152 | 	return tools.ParseParams(t.Parameters, data, claims)
153 | }
154 | 
155 | func (t Tool) Manifest() tools.Manifest {
156 | 	return t.manifest
157 | }
158 | 
159 | func (t Tool) McpManifest() tools.McpManifest {
160 | 	return t.mcpManifest
161 | }
162 | 
163 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
164 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
165 | }
166 | 
167 | func (t Tool) RequiresClientAuthorization() bool {
168 | 	return t.UseClientOAuth
169 | }
```

--------------------------------------------------------------------------------
/internal/tools/looker/lookerupdateprojectfile/lookerupdateprojectfile.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package lookerupdateprojectfile
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 22 | 	lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
 25 | 
 26 | 	"github.com/looker-open-source/sdk-codegen/go/rtl"
 27 | 	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
 28 | )
 29 | 
 30 | const kind string = "looker-update-project-file"
 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 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 (cfg Config) ToolConfigKind() string {
 58 | 	return kind
 59 | }
 60 | 
 61 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 62 | 	// verify source exists
 63 | 	rawS, ok := srcs[cfg.Source]
 64 | 	if !ok {
 65 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 66 | 	}
 67 | 
 68 | 	// verify the source is compatible
 69 | 	s, ok := rawS.(*lookersrc.Source)
 70 | 	if !ok {
 71 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `looker`", kind)
 72 | 	}
 73 | 
 74 | 	projectIdParameter := tools.NewStringParameter("project_id", "The id of the project containing the files")
 75 | 	filePathParameter := tools.NewStringParameter("file_path", "The path of the file within the project")
 76 | 	fileContentParameter := tools.NewStringParameter("file_content", "The content of the file")
 77 | 	parameters := tools.Parameters{projectIdParameter, filePathParameter, fileContentParameter}
 78 | 
 79 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 80 | 
 81 | 	// finish tool setup
 82 | 	return Tool{
 83 | 		Name:           cfg.Name,
 84 | 		Kind:           kind,
 85 | 		Parameters:     parameters,
 86 | 		AuthRequired:   cfg.AuthRequired,
 87 | 		UseClientOAuth: s.UseClientOAuth,
 88 | 		Client:         s.Client,
 89 | 		ApiSettings:    s.ApiSettings,
 90 | 		manifest: tools.Manifest{
 91 | 			Description:  cfg.Description,
 92 | 			Parameters:   parameters.Manifest(),
 93 | 			AuthRequired: cfg.AuthRequired,
 94 | 		},
 95 | 		mcpManifest: mcpManifest,
 96 | 	}, nil
 97 | }
 98 | 
 99 | // validate interface
100 | var _ tools.Tool = Tool{}
101 | 
102 | type Tool struct {
103 | 	Name           string `yaml:"name"`
104 | 	Kind           string `yaml:"kind"`
105 | 	UseClientOAuth bool
106 | 	Client         *v4.LookerSDK
107 | 	ApiSettings    *rtl.ApiSettings
108 | 	AuthRequired   []string         `yaml:"authRequired"`
109 | 	Parameters     tools.Parameters `yaml:"parameters"`
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 | 	sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
116 | 	if err != nil {
117 | 		return nil, fmt.Errorf("error getting sdk: %w", err)
118 | 	}
119 | 
120 | 	mapParams := params.AsMap()
121 | 	projectId, ok := mapParams["project_id"].(string)
122 | 	if !ok {
123 | 		return nil, fmt.Errorf("'project_id' must be a string, got %T", mapParams["project_id"])
124 | 	}
125 | 	filePath, ok := mapParams["file_path"].(string)
126 | 	if !ok {
127 | 		return nil, fmt.Errorf("'file_path' must be a string, got %T", mapParams["file_path"])
128 | 	}
129 | 	fileContent, ok := mapParams["file_content"].(string)
130 | 	if !ok {
131 | 		return nil, fmt.Errorf("'file_content' must be a string, got %T", mapParams["file_content"])
132 | 	}
133 | 
134 | 	req := lookercommon.FileContent{
135 | 		Path:    filePath,
136 | 		Content: fileContent,
137 | 	}
138 | 
139 | 	err = lookercommon.UpdateProjectFile(sdk, projectId, req, t.ApiSettings)
140 | 	if err != nil {
141 | 		return nil, fmt.Errorf("error making update_project_file request: %s", err)
142 | 	}
143 | 
144 | 	data := make(map[string]any)
145 | 	data["type"] = "text"
146 | 	data["text"] = fmt.Sprintf("updated file %s in project %s", filePath, projectId)
147 | 
148 | 	return data, nil
149 | }
150 | 
151 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
152 | 	return tools.ParseParams(t.Parameters, data, claims)
153 | }
154 | 
155 | func (t Tool) Manifest() tools.Manifest {
156 | 	return t.manifest
157 | }
158 | 
159 | func (t Tool) McpManifest() tools.McpManifest {
160 | 	return t.mcpManifest
161 | }
162 | 
163 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
164 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
165 | }
166 | 
167 | func (t Tool) RequiresClientAuthorization() bool {
168 | 	return t.UseClientOAuth
169 | }
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/firestore/firestore-query-collection.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "firestore-query-collection"
  3 | type: docs
  4 | weight: 1
  5 | description: > 
  6 |   A "firestore-query-collection" tool allow to query collections in Firestore.
  7 | aliases:
  8 | - /resources/tools/firestore-query-collection
  9 | ---
 10 | 
 11 | ## About
 12 | 
 13 | The `firestore-query-collection` tool allows you to query Firestore collections
 14 | with filters, ordering, and limit capabilities.
 15 | 
 16 | ## Configuration
 17 | 
 18 | To use this tool, you need to configure it in your YAML configuration file:
 19 | 
 20 | ```yaml
 21 | sources:
 22 |   my-firestore:
 23 |     kind: firestore
 24 |     project: my-gcp-project
 25 |     database: "(default)"
 26 | 
 27 | tools:
 28 |   query_collection:
 29 |     kind: firestore-query-collection
 30 |     source: my-firestore
 31 |     description: Query Firestore collections with advanced filtering
 32 | ```
 33 | 
 34 | ## Parameters
 35 | 
 36 | | **parameters**   |   **type**   | **required** | **default** | **description**                                                       |
 37 | |------------------|:------------:|:------------:|:-----------:|-----------------------------------------------------------------------|
 38 | | `collectionPath` |    string    |     true     |      -      | The Firestore Rules source code to validate                           |
 39 | | `filters`        |    array     |     false    |      -      | Array of filter objects (as JSON strings) to apply to the query       |
 40 | | `orderBy`        |    string    |     false    |      -      | JSON string specifying field and direction to order results           |
 41 | | `limit`          |    integer   |     false    |     100     | Maximum number of documents to return                                 |
 42 | | `analyzeQuery`   |    boolean   |     false    |    false    | If true, returns query explain metrics including execution statistics |
 43 | 
 44 | ### Filter Format
 45 | 
 46 | Each filter in the `filters` array should be a JSON string with the following
 47 | structure:
 48 | 
 49 | ```json
 50 | {
 51 |   "field": "fieldName",
 52 |   "op": "operator",
 53 |   "value": "compareValue"
 54 | }
 55 | ```
 56 | 
 57 | Supported operators:
 58 | 
 59 | - `<` - Less than
 60 | - `<=` - Less than or equal to
 61 | - `>` - Greater than
 62 | - `>=` - Greater than or equal to
 63 | - `==` - Equal to
 64 | - `!=` - Not equal to
 65 | - `array-contains` - Array contains a specific value
 66 | - `array-contains-any` - Array contains any of the specified values
 67 | - `in` - Field value is in the specified array
 68 | - `not-in` - Field value is not in the specified array
 69 | 
 70 | Value types supported:
 71 | 
 72 | - String: `"value": "text"`
 73 | - Number: `"value": 123` or `"value": 45.67`
 74 | - Boolean: `"value": true` or `"value": false`
 75 | - Array: `"value": ["item1", "item2"]` (for `in`, `not-in`, `array-contains-any`
 76 |   operators)
 77 | 
 78 | ### OrderBy Format
 79 | 
 80 | The `orderBy` parameter should be a JSON string with the following structure:
 81 | 
 82 | ```json
 83 | {
 84 |   "field": "fieldName",
 85 |   "direction": "ASCENDING"
 86 | }
 87 | ```
 88 | 
 89 | Direction values:
 90 | 
 91 | - `ASCENDING`
 92 | - `DESCENDING`
 93 | 
 94 | ## Example Usage
 95 | 
 96 | ### Query with filters
 97 | 
 98 | ```json
 99 | {
100 |   "collectionPath": "users",
101 |   "filters": [
102 |     "{\"field\": \"age\", \"op\": \">\", \"value\": 18}",
103 |     "{\"field\": \"status\", \"op\": \"==\", \"value\": \"active\"}"
104 |   ],
105 |   "orderBy": "{\"field\": \"createdAt\", \"direction\": \"DESCENDING\"}",
106 |   "limit": 50
107 | }
108 | ```
109 | 
110 | ### Query with array contains filter
111 | 
112 | ```json
113 | {
114 |   "collectionPath": "products",
115 |   "filters": [
116 |     "{\"field\": \"categories\", \"op\": \"array-contains\", \"value\": \"electronics\"}",
117 |     "{\"field\": \"price\", \"op\": \"<\", \"value\": 1000}"
118 |   ],
119 |   "orderBy": "{\"field\": \"price\", \"direction\": \"ASCENDING\"}",
120 |   "limit": 20
121 | }
122 | ```
123 | 
124 | ### Query with IN operator
125 | 
126 | ```json
127 | {
128 |   "collectionPath": "orders",
129 |   "filters": [
130 |     "{\"field\": \"status\", \"op\": \"in\", \"value\": [\"pending\", \"processing\"]}"
131 |   ],
132 |   "limit": 100
133 | }
134 | ```
135 | 
136 | ### Query with explain metrics
137 | 
138 | ```json
139 | {
140 |   "collectionPath": "users",
141 |   "filters": [
142 |     "{\"field\": \"age\", \"op\": \">=\", \"value\": 21}",
143 |     "{\"field\": \"active\", \"op\": \"==\", \"value\": true}"
144 |   ],
145 |   "orderBy": "{\"field\": \"lastLogin\", \"direction\": \"DESCENDING\"}",
146 |   "limit": 25,
147 |   "analyzeQuery": true
148 | }
149 | ```
150 | 
151 | ## Response Format
152 | 
153 | ### Standard Response (analyzeQuery = false)
154 | 
155 | The tool returns an array of documents, where each document includes:
156 | 
157 | ```json
158 | {
159 |   "id": "documentId",
160 |   "path": "collection/documentId",
161 |   "data": {
162 |     // Document fields
163 |   },
164 |   "createTime": "2025-01-07T12:00:00Z",
165 |   "updateTime": "2025-01-07T12:00:00Z",
166 |   "readTime": "2025-01-07T12:00:00Z"
167 | }
168 | ```
169 | 
170 | ### Response with Query Analysis (analyzeQuery = true)
171 | 
172 | When `analyzeQuery` is set to true, the tool returns a single object containing
173 | documents and explain metrics:
174 | 
175 | ```json
176 | {
177 |   "documents": [
178 |     // Array of document objects as shown above
179 |   ],
180 |   "explainMetrics": {
181 |     "planSummary": {
182 |       "indexesUsed": [
183 |         {
184 |           "query_scope": "Collection",
185 |           "properties": "(field ASC, __name__ ASC)"
186 |         }
187 |       ]
188 |     },
189 |     "executionStats": {
190 |       "resultsReturned": 50,
191 |       "readOperations": 50,
192 |       "executionDuration": "120ms",
193 |       "debugStats": {
194 |         "indexes_entries_scanned": "1000",
195 |         "documents_scanned": "50",
196 |         "billing_details": {
197 |           "documents_billable": "50",
198 |           "index_entries_billable": "1000",
199 |           "min_query_cost": "0"
200 |         }
201 |       }
202 |     }
203 |   }
204 | }
205 | ```
206 | 
207 | ## Error Handling
208 | 
209 | The tool will return errors for:
210 | 
211 | - Invalid collection path
212 | - Malformed filter JSON
213 | - Unsupported operators
214 | - Query execution failures
215 | - Invalid orderBy format
216 | 
```

--------------------------------------------------------------------------------
/internal/tools/mongodb/mongodbdeleteone/mongodbdeleteone.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package mongodbdeleteone
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 	"slices"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	mongosrc "github.com/googleapis/genai-toolbox/internal/sources/mongodb"
 23 | 	"go.mongodb.org/mongo-driver/bson"
 24 | 	"go.mongodb.org/mongo-driver/mongo"
 25 | 	"go.mongodb.org/mongo-driver/mongo/options"
 26 | 
 27 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 28 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 29 | )
 30 | 
 31 | const kind string = "mongodb-delete-one"
 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 Config struct {
 48 | 	Name          string           `yaml:"name" validate:"required"`
 49 | 	Kind          string           `yaml:"kind" validate:"required"`
 50 | 	Source        string           `yaml:"source" validate:"required"`
 51 | 	AuthRequired  []string         `yaml:"authRequired" validate:"required"`
 52 | 	Description   string           `yaml:"description" validate:"required"`
 53 | 	Database      string           `yaml:"database" validate:"required"`
 54 | 	Collection    string           `yaml:"collection" validate:"required"`
 55 | 	FilterPayload string           `yaml:"filterPayload" validate:"required"`
 56 | 	FilterParams  tools.Parameters `yaml:"filterParams" validate:"required"`
 57 | }
 58 | 
 59 | // validate interface
 60 | var _ tools.ToolConfig = Config{}
 61 | 
 62 | func (cfg Config) ToolConfigKind() string {
 63 | 	return kind
 64 | }
 65 | 
 66 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 67 | 	// verify source exists
 68 | 	rawS, ok := srcs[cfg.Source]
 69 | 	if !ok {
 70 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 71 | 	}
 72 | 
 73 | 	// verify the source is compatible
 74 | 	s, ok := rawS.(*mongosrc.Source)
 75 | 	if !ok {
 76 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `mongodb`", kind)
 77 | 	}
 78 | 
 79 | 	// Create a slice for all parameters
 80 | 	allParameters := slices.Concat(cfg.FilterParams)
 81 | 
 82 | 	// Verify no duplicate parameter names
 83 | 	err := tools.CheckDuplicateParameters(allParameters)
 84 | 	if err != nil {
 85 | 		return nil, err
 86 | 	}
 87 | 
 88 | 	// Create Toolbox manifest
 89 | 	paramManifest := allParameters.Manifest()
 90 | 
 91 | 	if paramManifest == nil {
 92 | 		paramManifest = make([]tools.ParameterManifest, 0)
 93 | 	}
 94 | 
 95 | 	// Create MCP manifest
 96 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 97 | 
 98 | 	// finish tool setup
 99 | 	return Tool{
100 | 		Name:          cfg.Name,
101 | 		Kind:          kind,
102 | 		AuthRequired:  cfg.AuthRequired,
103 | 		Collection:    cfg.Collection,
104 | 		FilterPayload: cfg.FilterPayload,
105 | 		FilterParams:  cfg.FilterParams,
106 | 		AllParams:     allParameters,
107 | 		database:      s.Client.Database(cfg.Database),
108 | 		manifest:      tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
109 | 		mcpManifest:   mcpManifest,
110 | 	}, nil
111 | }
112 | 
113 | // validate interface
114 | var _ tools.Tool = Tool{}
115 | 
116 | type Tool struct {
117 | 	Name          string           `yaml:"name"`
118 | 	Kind          string           `yaml:"kind"`
119 | 	AuthRequired  []string         `yaml:"authRequired"`
120 | 	Description   string           `yaml:"description"`
121 | 	Collection    string           `yaml:"collection"`
122 | 	FilterPayload string           `yaml:"filterPayload"`
123 | 	FilterParams  tools.Parameters `yaml:"filterParams"`
124 | 	AllParams     tools.Parameters `yaml:"allParams"`
125 | 
126 | 	database    *mongo.Database
127 | 	manifest    tools.Manifest
128 | 	mcpManifest tools.McpManifest
129 | }
130 | 
131 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
132 | 	paramsMap := params.AsMap()
133 | 
134 | 	filterString, err := tools.PopulateTemplateWithJSON("MongoDBDeleteOneFilter", t.FilterPayload, paramsMap)
135 | 	if err != nil {
136 | 		return nil, fmt.Errorf("error populating filter: %s", err)
137 | 	}
138 | 
139 | 	opts := options.Delete()
140 | 
141 | 	var filter = bson.D{}
142 | 	err = bson.UnmarshalExtJSON([]byte(filterString), false, &filter)
143 | 	if err != nil {
144 | 		return nil, err
145 | 	}
146 | 
147 | 	res, err := t.database.Collection(t.Collection).DeleteOne(ctx, filter, opts)
148 | 	if err != nil {
149 | 		return nil, err
150 | 	}
151 | 
152 | 	// do not return an error when the count is 0, to mirror the delete many call result
153 | 	return res.DeletedCount, nil
154 | }
155 | 
156 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
157 | 	return tools.ParseParams(t.AllParams, data, claims)
158 | }
159 | 
160 | func (t Tool) Manifest() tools.Manifest {
161 | 	return t.manifest
162 | }
163 | 
164 | func (t Tool) McpManifest() tools.McpManifest {
165 | 	return t.mcpManifest
166 | }
167 | 
168 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
169 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
170 | }
171 | 
172 | func (t Tool) RequiresClientAuthorization() bool {
173 | 	return false
174 | }
175 | 
```

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

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package clickhouse
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | )
 26 | 
 27 | type compatibleSource interface {
 28 | 	ClickHousePool() *sql.DB
 29 | }
 30 | 
 31 | var compatibleSources = []string{"clickhouse"}
 32 | 
 33 | const executeSQLKind string = "clickhouse-execute-sql"
 34 | 
 35 | func init() {
 36 | 	if !tools.Register(executeSQLKind, newExecuteSQLConfig) {
 37 | 		panic(fmt.Sprintf("tool kind %q already registered", executeSQLKind))
 38 | 	}
 39 | }
 40 | 
 41 | func newExecuteSQLConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 42 | 	actual := Config{Name: name}
 43 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 44 | 		return nil, err
 45 | 	}
 46 | 	return actual, nil
 47 | }
 48 | 
 49 | type Config struct {
 50 | 	Name         string   `yaml:"name" validate:"required"`
 51 | 	Kind         string   `yaml:"kind" validate:"required"`
 52 | 	Source       string   `yaml:"source" validate:"required"`
 53 | 	Description  string   `yaml:"description" validate:"required"`
 54 | 	AuthRequired []string `yaml:"authRequired"`
 55 | }
 56 | 
 57 | var _ tools.ToolConfig = Config{}
 58 | 
 59 | func (cfg Config) ToolConfigKind() string {
 60 | 	return executeSQLKind
 61 | }
 62 | 
 63 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 64 | 	rawS, ok := srcs[cfg.Source]
 65 | 	if !ok {
 66 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 67 | 	}
 68 | 
 69 | 	s, ok := rawS.(compatibleSource)
 70 | 	if !ok {
 71 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", executeSQLKind, compatibleSources)
 72 | 	}
 73 | 
 74 | 	sqlParameter := tools.NewStringParameter("sql", "The SQL statement to execute.")
 75 | 	parameters := tools.Parameters{sqlParameter}
 76 | 
 77 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 78 | 
 79 | 	t := ExecuteSQLTool{
 80 | 		Name:         cfg.Name,
 81 | 		Kind:         executeSQLKind,
 82 | 		Parameters:   parameters,
 83 | 		AuthRequired: cfg.AuthRequired,
 84 | 		Pool:         s.ClickHousePool(),
 85 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 86 | 		mcpManifest:  mcpManifest,
 87 | 	}
 88 | 	return t, nil
 89 | }
 90 | 
 91 | var _ tools.Tool = ExecuteSQLTool{}
 92 | 
 93 | type ExecuteSQLTool struct {
 94 | 	Name         string           `yaml:"name"`
 95 | 	Kind         string           `yaml:"kind"`
 96 | 	AuthRequired []string         `yaml:"authRequired"`
 97 | 	Parameters   tools.Parameters `yaml:"parameters"`
 98 | 
 99 | 	Pool        *sql.DB
100 | 	manifest    tools.Manifest
101 | 	mcpManifest tools.McpManifest
102 | }
103 | 
104 | func (t ExecuteSQLTool) Invoke(ctx context.Context, params tools.ParamValues, token tools.AccessToken) (any, error) {
105 | 	paramsMap := params.AsMap()
106 | 	sql, ok := paramsMap["sql"].(string)
107 | 	if !ok {
108 | 		return nil, fmt.Errorf("unable to cast sql parameter %s", paramsMap["sql"])
109 | 	}
110 | 
111 | 	results, err := t.Pool.QueryContext(ctx, sql)
112 | 	if err != nil {
113 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
114 | 	}
115 | 	defer results.Close()
116 | 
117 | 	cols, err := results.Columns()
118 | 	if err != nil {
119 | 		return nil, fmt.Errorf("unable to retrieve rows column name: %w", err)
120 | 	}
121 | 
122 | 	// create an array of values for each column, which can be re-used to scan each row
123 | 	rawValues := make([]any, len(cols))
124 | 	values := make([]any, len(cols))
125 | 	for i := range rawValues {
126 | 		values[i] = &rawValues[i]
127 | 	}
128 | 
129 | 	colTypes, err := results.ColumnTypes()
130 | 	if err != nil {
131 | 		return nil, fmt.Errorf("unable to get column types: %w", err)
132 | 	}
133 | 
134 | 	var out []any
135 | 	for results.Next() {
136 | 		err := results.Scan(values...)
137 | 		if err != nil {
138 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
139 | 		}
140 | 		vMap := make(map[string]any)
141 | 		for i, name := range cols {
142 | 			// ClickHouse driver may return specific types that need handling
143 | 			switch colTypes[i].DatabaseTypeName() {
144 | 			case "String", "FixedString":
145 | 				if rawValues[i] != nil {
146 | 					// Handle potential []byte to string conversion if needed
147 | 					if b, ok := rawValues[i].([]byte); ok {
148 | 						vMap[name] = string(b)
149 | 					} else {
150 | 						vMap[name] = rawValues[i]
151 | 					}
152 | 				} else {
153 | 					vMap[name] = nil
154 | 				}
155 | 			default:
156 | 				vMap[name] = rawValues[i]
157 | 			}
158 | 		}
159 | 		out = append(out, vMap)
160 | 	}
161 | 
162 | 	if err := results.Err(); err != nil {
163 | 		return nil, fmt.Errorf("errors encountered by results.Scan: %w", err)
164 | 	}
165 | 
166 | 	return out, nil
167 | }
168 | 
169 | func (t ExecuteSQLTool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
170 | 	return tools.ParseParams(t.Parameters, data, claims)
171 | }
172 | 
173 | func (t ExecuteSQLTool) Manifest() tools.Manifest {
174 | 	return t.manifest
175 | }
176 | 
177 | func (t ExecuteSQLTool) McpManifest() tools.McpManifest {
178 | 	return t.mcpManifest
179 | }
180 | 
181 | func (t ExecuteSQLTool) Authorized(verifiedAuthServices []string) bool {
182 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
183 | }
184 | 
185 | func (t ExecuteSQLTool) RequiresClientAuthorization() bool {
186 | 	return false
187 | }
188 | 
```

--------------------------------------------------------------------------------
/internal/tools/mongodb/mongodbdeletemany/mongodbdeletemany.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package mongodbdeletemany
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"errors"
 19 | 	"fmt"
 20 | 	"slices"
 21 | 
 22 | 	"github.com/goccy/go-yaml"
 23 | 	mongosrc "github.com/googleapis/genai-toolbox/internal/sources/mongodb"
 24 | 	"go.mongodb.org/mongo-driver/bson"
 25 | 	"go.mongodb.org/mongo-driver/mongo"
 26 | 	"go.mongodb.org/mongo-driver/mongo/options"
 27 | 
 28 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 29 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 30 | )
 31 | 
 32 | const kind string = "mongodb-delete-many"
 33 | 
 34 | func init() {
 35 | 	if !tools.Register(kind, newConfig) {
 36 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 37 | 	}
 38 | }
 39 | 
 40 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 41 | 	actual := Config{Name: name}
 42 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 43 | 		return nil, err
 44 | 	}
 45 | 	return actual, nil
 46 | }
 47 | 
 48 | type Config struct {
 49 | 	Name          string           `yaml:"name" validate:"required"`
 50 | 	Kind          string           `yaml:"kind" validate:"required"`
 51 | 	Source        string           `yaml:"source" validate:"required"`
 52 | 	AuthRequired  []string         `yaml:"authRequired" validate:"required"`
 53 | 	Description   string           `yaml:"description" validate:"required"`
 54 | 	Database      string           `yaml:"database" validate:"required"`
 55 | 	Collection    string           `yaml:"collection" validate:"required"`
 56 | 	FilterPayload string           `yaml:"filterPayload" validate:"required"`
 57 | 	FilterParams  tools.Parameters `yaml:"filterParams" validate:"required"`
 58 | }
 59 | 
 60 | // validate interface
 61 | var _ tools.ToolConfig = Config{}
 62 | 
 63 | func (cfg Config) ToolConfigKind() string {
 64 | 	return kind
 65 | }
 66 | 
 67 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 68 | 	// verify source exists
 69 | 	rawS, ok := srcs[cfg.Source]
 70 | 	if !ok {
 71 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 72 | 	}
 73 | 
 74 | 	// verify the source is compatible
 75 | 	s, ok := rawS.(*mongosrc.Source)
 76 | 	if !ok {
 77 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `mongodb`", kind)
 78 | 	}
 79 | 
 80 | 	// Create a slice for all parameters
 81 | 	allParameters := slices.Concat(cfg.FilterParams)
 82 | 
 83 | 	// Verify no duplicate parameter names
 84 | 	err := tools.CheckDuplicateParameters(allParameters)
 85 | 	if err != nil {
 86 | 		return nil, err
 87 | 	}
 88 | 
 89 | 	// Create Toolbox manifest
 90 | 	paramManifest := allParameters.Manifest()
 91 | 
 92 | 	if paramManifest == nil {
 93 | 		paramManifest = make([]tools.ParameterManifest, 0)
 94 | 	}
 95 | 
 96 | 	// Create MCP manifest
 97 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 98 | 
 99 | 	// finish tool setup
100 | 	return Tool{
101 | 		Name:          cfg.Name,
102 | 		Kind:          kind,
103 | 		AuthRequired:  cfg.AuthRequired,
104 | 		Collection:    cfg.Collection,
105 | 		FilterPayload: cfg.FilterPayload,
106 | 		FilterParams:  cfg.FilterParams,
107 | 		AllParams:     allParameters,
108 | 		database:      s.Client.Database(cfg.Database),
109 | 		manifest:      tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
110 | 		mcpManifest:   mcpManifest,
111 | 	}, nil
112 | }
113 | 
114 | // validate interface
115 | var _ tools.Tool = Tool{}
116 | 
117 | type Tool struct {
118 | 	Name          string           `yaml:"name"`
119 | 	Kind          string           `yaml:"kind"`
120 | 	AuthRequired  []string         `yaml:"authRequired"`
121 | 	Description   string           `yaml:"description"`
122 | 	Collection    string           `yaml:"collection"`
123 | 	FilterPayload string           `yaml:"filterPayload"`
124 | 	FilterParams  tools.Parameters `yaml:"filterParams"`
125 | 	AllParams     tools.Parameters `yaml:"allParams"`
126 | 
127 | 	database    *mongo.Database
128 | 	manifest    tools.Manifest
129 | 	mcpManifest tools.McpManifest
130 | }
131 | 
132 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
133 | 	paramsMap := params.AsMap()
134 | 
135 | 	filterString, err := tools.PopulateTemplateWithJSON("MongoDBDeleteManyFilter", t.FilterPayload, paramsMap)
136 | 	if err != nil {
137 | 		return nil, fmt.Errorf("error populating filter: %s", err)
138 | 	}
139 | 
140 | 	opts := options.Delete()
141 | 
142 | 	var filter = bson.D{}
143 | 	err = bson.UnmarshalExtJSON([]byte(filterString), false, &filter)
144 | 	if err != nil {
145 | 		return nil, err
146 | 	}
147 | 
148 | 	res, err := t.database.Collection(t.Collection).DeleteMany(ctx, filter, opts)
149 | 	if err != nil {
150 | 		return nil, err
151 | 	}
152 | 
153 | 	if res.DeletedCount == 0 {
154 | 		return nil, errors.New("no document found")
155 | 	}
156 | 
157 | 	// not much to return actually
158 | 	return res.DeletedCount, nil
159 | }
160 | 
161 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
162 | 	return tools.ParseParams(t.AllParams, data, claims)
163 | }
164 | 
165 | func (t Tool) Manifest() tools.Manifest {
166 | 	return t.manifest
167 | }
168 | 
169 | func (t Tool) McpManifest() tools.McpManifest {
170 | 	return t.mcpManifest
171 | }
172 | 
173 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
174 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
175 | }
176 | 
177 | func (t Tool) RequiresClientAuthorization() bool {
178 | 	return false
179 | }
180 | 
```

--------------------------------------------------------------------------------
/internal/sources/cloudsqlpg/cloud_sql_pg.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
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 	"net"
 21 | 
 22 | 	"cloud.google.com/go/cloudsqlconn"
 23 | 	"github.com/goccy/go-yaml"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 25 | 	"github.com/googleapis/genai-toolbox/internal/util"
 26 | 	"github.com/jackc/pgx/v5/pgxpool"
 27 | 	"go.opentelemetry.io/otel/trace"
 28 | )
 29 | 
 30 | const SourceKind string = "cloud-sql-postgres"
 31 | 
 32 | // validate interface
 33 | var _ sources.SourceConfig = Config{}
 34 | 
 35 | func init() {
 36 | 	if !sources.Register(SourceKind, newConfig) {
 37 | 		panic(fmt.Sprintf("source kind %q already registered", SourceKind))
 38 | 	}
 39 | }
 40 | 
 41 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
 42 | 	actual := Config{Name: name, IPType: "public"} // Default IPType
 43 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 44 | 		return nil, err
 45 | 	}
 46 | 	return actual, nil
 47 | }
 48 | 
 49 | type Config struct {
 50 | 	Name     string         `yaml:"name" validate:"required"`
 51 | 	Kind     string         `yaml:"kind" validate:"required"`
 52 | 	Project  string         `yaml:"project" validate:"required"`
 53 | 	Region   string         `yaml:"region" validate:"required"`
 54 | 	Instance string         `yaml:"instance" validate:"required"`
 55 | 	IPType   sources.IPType `yaml:"ipType" validate:"required"`
 56 | 	Database string         `yaml:"database" validate:"required"`
 57 | 	User     string         `yaml:"user"`
 58 | 	Password string         `yaml:"password"`
 59 | }
 60 | 
 61 | func (r Config) SourceConfigKind() string {
 62 | 	return SourceKind
 63 | }
 64 | 
 65 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
 66 | 	pool, err := initCloudSQLPgConnectionPool(ctx, tracer, r.Name, r.Project, r.Region, r.Instance, r.IPType.String(), r.User, r.Password, r.Database)
 67 | 	if err != nil {
 68 | 		return nil, fmt.Errorf("unable to create pool: %w", err)
 69 | 	}
 70 | 
 71 | 	err = pool.Ping(ctx)
 72 | 	if err != nil {
 73 | 		return nil, fmt.Errorf("unable to connect successfully: %w", err)
 74 | 	}
 75 | 
 76 | 	s := &Source{
 77 | 		Name: r.Name,
 78 | 		Kind: SourceKind,
 79 | 		Pool: pool,
 80 | 	}
 81 | 	return s, nil
 82 | }
 83 | 
 84 | var _ sources.Source = &Source{}
 85 | 
 86 | type Source struct {
 87 | 	Name string `yaml:"name"`
 88 | 	Kind string `yaml:"kind"`
 89 | 	Pool *pgxpool.Pool
 90 | }
 91 | 
 92 | func (s *Source) SourceKind() string {
 93 | 	return SourceKind
 94 | }
 95 | 
 96 | func (s *Source) PostgresPool() *pgxpool.Pool {
 97 | 	return s.Pool
 98 | }
 99 | 
100 | func getConnectionConfig(ctx context.Context, user, pass, dbname string) (string, bool, error) {
101 | 	userAgent, err := util.UserAgentFromContext(ctx)
102 | 	if err != nil {
103 | 		userAgent = "genai-toolbox"
104 | 	}
105 | 	useIAM := true
106 | 
107 | 	// If username and password both provided, use password authentication
108 | 	if user != "" && pass != "" {
109 | 		dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable application_name=%s", user, pass, dbname, userAgent)
110 | 		useIAM = false
111 | 		return dsn, useIAM, nil
112 | 	}
113 | 
114 | 	// If username is empty, fetch email from ADC
115 | 	// otherwise, use username as IAM email
116 | 	if user == "" {
117 | 		if pass != "" {
118 | 			// If password is provided without an username, raise an error
119 | 			return "", useIAM, fmt.Errorf("password is provided without a username. Please provide both a username and password, or leave both fields empty")
120 | 		}
121 | 		email, err := sources.GetIAMPrincipalEmailFromADC(ctx)
122 | 		if err != nil {
123 | 			return "", useIAM, fmt.Errorf("error getting email from ADC: %v", err)
124 | 		}
125 | 		user = email
126 | 	}
127 | 
128 | 	// Construct IAM connection string with username
129 | 	dsn := fmt.Sprintf("user=%s dbname=%s sslmode=disable application_name=%s", user, dbname, userAgent)
130 | 	return dsn, useIAM, nil
131 | }
132 | 
133 | func initCloudSQLPgConnectionPool(ctx context.Context, tracer trace.Tracer, name, project, region, instance, ipType, user, pass, dbname string) (*pgxpool.Pool, error) {
134 | 	//nolint:all // Reassigned ctx
135 | 	ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name)
136 | 	defer span.End()
137 | 
138 | 	// Configure the driver to connect to the database
139 | 	dsn, useIAM, err := getConnectionConfig(ctx, user, pass, dbname)
140 | 	if err != nil {
141 | 		return nil, fmt.Errorf("unable to get Cloud SQL connection config: %w", err)
142 | 	}
143 | 
144 | 	config, err := pgxpool.ParseConfig(dsn)
145 | 	if err != nil {
146 | 		return nil, fmt.Errorf("unable to parse connection uri: %w", err)
147 | 	}
148 | 
149 | 	// Create a new dialer with options
150 | 	userAgent, err := util.UserAgentFromContext(ctx)
151 | 	if err != nil {
152 | 		return nil, err
153 | 	}
154 | 	opts, err := sources.GetCloudSQLOpts(ipType, userAgent, useIAM)
155 | 	if err != nil {
156 | 		return nil, err
157 | 	}
158 | 	d, err := cloudsqlconn.NewDialer(ctx, opts...)
159 | 	if err != nil {
160 | 		return nil, fmt.Errorf("unable to parse connection uri: %w", err)
161 | 	}
162 | 
163 | 	// Tell the driver to use the Cloud SQL Go Connector to create connections
164 | 	i := fmt.Sprintf("%s:%s:%s", project, region, instance)
165 | 	config.ConnConfig.DialFunc = func(ctx context.Context, _ string, instance string) (net.Conn, error) {
166 | 		return d.Dial(ctx, i)
167 | 	}
168 | 
169 | 	// Interact with the driver directly as you normally would
170 | 	pool, err := pgxpool.NewWithConfig(ctx, config)
171 | 	if err != nil {
172 | 		return nil, err
173 | 	}
174 | 	return pool, nil
175 | }
176 | 
```

--------------------------------------------------------------------------------
/internal/tools/looker/lookergetexplores/lookergetexplores.go:
--------------------------------------------------------------------------------

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //	http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | package lookergetexplores
 15 | 
 16 | import (
 17 | 	"context"
 18 | 	"fmt"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 22 | 	lookersrc "github.com/googleapis/genai-toolbox/internal/sources/looker"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
 25 | 	"github.com/googleapis/genai-toolbox/internal/util"
 26 | 
 27 | 	"github.com/looker-open-source/sdk-codegen/go/rtl"
 28 | 	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
 29 | )
 30 | 
 31 | const kind string = "looker-get-explores"
 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 Config struct {
 48 | 	Name         string   `yaml:"name" validate:"required"`
 49 | 	Kind         string   `yaml:"kind" validate:"required"`
 50 | 	Source       string   `yaml:"source" validate:"required"`
 51 | 	Description  string   `yaml:"description" validate:"required"`
 52 | 	AuthRequired []string `yaml:"authRequired"`
 53 | }
 54 | 
 55 | // validate interface
 56 | var _ tools.ToolConfig = Config{}
 57 | 
 58 | func (cfg Config) ToolConfigKind() string {
 59 | 	return kind
 60 | }
 61 | 
 62 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 63 | 	// verify source exists
 64 | 	rawS, ok := srcs[cfg.Source]
 65 | 	if !ok {
 66 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 67 | 	}
 68 | 
 69 | 	// verify the source is compatible
 70 | 	s, ok := rawS.(*lookersrc.Source)
 71 | 	if !ok {
 72 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `looker`", kind)
 73 | 	}
 74 | 
 75 | 	modelParameter := tools.NewStringParameter("model", "The model containing the explores.")
 76 | 	parameters := tools.Parameters{modelParameter}
 77 | 
 78 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 79 | 
 80 | 	// finish tool setup
 81 | 	return Tool{
 82 | 		Name:           cfg.Name,
 83 | 		Kind:           kind,
 84 | 		Parameters:     parameters,
 85 | 		AuthRequired:   cfg.AuthRequired,
 86 | 		UseClientOAuth: s.UseClientOAuth,
 87 | 		Client:         s.Client,
 88 | 		ApiSettings:    s.ApiSettings,
 89 | 		manifest: tools.Manifest{
 90 | 			Description:  cfg.Description,
 91 | 			Parameters:   parameters.Manifest(),
 92 | 			AuthRequired: cfg.AuthRequired,
 93 | 		},
 94 | 		mcpManifest:        mcpManifest,
 95 | 		ShowHiddenExplores: s.ShowHiddenExplores,
 96 | 	}, nil
 97 | }
 98 | 
 99 | // validate interface
100 | var _ tools.Tool = Tool{}
101 | 
102 | type Tool struct {
103 | 	Name               string `yaml:"name"`
104 | 	Kind               string `yaml:"kind"`
105 | 	UseClientOAuth     bool
106 | 	Client             *v4.LookerSDK
107 | 	ApiSettings        *rtl.ApiSettings
108 | 	AuthRequired       []string         `yaml:"authRequired"`
109 | 	Parameters         tools.Parameters `yaml:"parameters"`
110 | 	manifest           tools.Manifest
111 | 	mcpManifest        tools.McpManifest
112 | 	ShowHiddenExplores bool
113 | }
114 | 
115 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
116 | 	logger, err := util.LoggerFromContext(ctx)
117 | 	if err != nil {
118 | 		return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
119 | 	}
120 | 	mapParams := params.AsMap()
121 | 	model, ok := mapParams["model"].(string)
122 | 	if !ok {
123 | 		return nil, fmt.Errorf("'model' must be a string, got %T", mapParams["model"])
124 | 	}
125 | 
126 | 	sdk, err := lookercommon.GetLookerSDK(t.UseClientOAuth, t.ApiSettings, t.Client, accessToken)
127 | 	if err != nil {
128 | 		return nil, fmt.Errorf("error getting sdk: %w", err)
129 | 	}
130 | 	resp, err := sdk.LookmlModel(model, "explores(name,description,label,group_label,hidden)", t.ApiSettings)
131 | 	if err != nil {
132 | 		return nil, fmt.Errorf("error making get_explores request: %s", err)
133 | 	}
134 | 
135 | 	var data []any
136 | 	for _, v := range *resp.Explores {
137 | 		logger.DebugContext(ctx, "Got response element of %v\n", v)
138 | 		if !t.ShowHiddenExplores && v.Hidden != nil && *v.Hidden {
139 | 			continue
140 | 		}
141 | 		vMap := make(map[string]any)
142 | 		if v.Name != nil {
143 | 			vMap["name"] = *v.Name
144 | 		}
145 | 		if v.Description != nil {
146 | 			vMap["description"] = *v.Description
147 | 		}
148 | 		if v.Label != nil {
149 | 			vMap["label"] = *v.Label
150 | 		}
151 | 		if v.GroupLabel != nil {
152 | 			vMap["group_label"] = *v.GroupLabel
153 | 		}
154 | 		logger.DebugContext(ctx, "Converted to %v\n", vMap)
155 | 		data = append(data, vMap)
156 | 	}
157 | 	logger.DebugContext(ctx, "data = ", data)
158 | 
159 | 	return data, nil
160 | }
161 | 
162 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
163 | 	return tools.ParseParams(t.Parameters, data, claims)
164 | }
165 | 
166 | func (t Tool) Manifest() tools.Manifest {
167 | 	return t.manifest
168 | }
169 | 
170 | func (t Tool) McpManifest() tools.McpManifest {
171 | 	return t.mcpManifest
172 | }
173 | 
174 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
175 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
176 | }
177 | 
178 | func (t Tool) RequiresClientAuthorization() bool {
179 | 	return t.UseClientOAuth
180 | }
181 | 
```

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

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

```go
  1 | package main
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"encoding/json"
  6 | 	"fmt"
  7 | 	"log"
  8 | 	"os"
  9 | 
 10 | 	"github.com/googleapis/mcp-toolbox-sdk-go/core"
 11 | 	"google.golang.org/genai"
 12 | )
 13 | 
 14 | // ConvertToGenaiTool translates a ToolboxTool into the genai.FunctionDeclaration format.
 15 | func ConvertToGenaiTool(toolboxTool *core.ToolboxTool) *genai.Tool {
 16 | 
 17 | 	inputschema, err := toolboxTool.InputSchema()
 18 | 	if err != nil {
 19 | 		return &genai.Tool{}
 20 | 	}
 21 | 
 22 | 	var paramsSchema *genai.Schema
 23 | 	_ = json.Unmarshal(inputschema, &paramsSchema)
 24 | 	// First, create the function declaration.
 25 | 	funcDeclaration := &genai.FunctionDeclaration{
 26 | 		Name:        toolboxTool.Name(),
 27 | 		Description: toolboxTool.Description(),
 28 | 		Parameters:  paramsSchema,
 29 | 	}
 30 | 
 31 | 	// Then, wrap the function declaration in a genai.Tool struct.
 32 | 	return &genai.Tool{
 33 | 		FunctionDeclarations: []*genai.FunctionDeclaration{funcDeclaration},
 34 | 	}
 35 | }
 36 | 
 37 | func printResponse(resp *genai.GenerateContentResponse) {
 38 | 	for _, cand := range resp.Candidates {
 39 | 		if cand.Content != nil {
 40 | 			for _, part := range cand.Content.Parts {
 41 | 				fmt.Println(part.Text)
 42 | 			}
 43 | 		}
 44 | 	}
 45 | }
 46 | 
 47 | const systemPrompt = `
 48 | You're a helpful hotel assistant. You handle hotel searching, booking, and
 49 | cancellations. When the user searches for a hotel, mention its name, id,
 50 | location and price tier. Always mention hotel ids while performing any
 51 | searches. This is very important for any operations. For any bookings or
 52 | cancellations, please provide the appropriate confirmation. Be sure to
 53 | update checkin or checkout dates if mentioned by the user.
 54 | Don't ask for confirmations from the user.
 55 | `
 56 | 
 57 | var queries = []string{
 58 | 	"Find hotels in Basel with Basel in its name.",
 59 | 	"Can you book the hotel Hilton Basel for me?",
 60 | 	"Oh wait, this is too expensive. Please cancel it.",
 61 | 	"Please book the Hyatt Regency instead.",
 62 | 	"My check in dates would be from April 10, 2024 to April 19, 2024.",
 63 | }
 64 | 
 65 | func main() {
 66 | 	// Setup
 67 | 	ctx := context.Background()
 68 | 	apiKey := os.Getenv("GOOGLE_API_KEY")
 69 | 	toolboxURL := "http://localhost:5000"
 70 | 
 71 | 	// Initialize the Google GenAI client using the explicit ClientConfig.
 72 | 	client, err := genai.NewClient(ctx, &genai.ClientConfig{
 73 | 		APIKey: apiKey,
 74 | 	})
 75 | 	if err != nil {
 76 | 		log.Fatalf("Failed to create Google GenAI client: %v", err)
 77 | 	}
 78 | 
 79 | 	// Initialize the MCP Toolbox client.
 80 | 	toolboxClient, err := core.NewToolboxClient(toolboxURL)
 81 | 	if err != nil {
 82 | 		log.Fatalf("Failed to create Toolbox client: %v", err)
 83 | 	}
 84 | 
 85 | 	// Load the tool using the MCP Toolbox SDK.
 86 | 	tools, err := toolboxClient.LoadToolset("my-toolset", ctx)
 87 | 	if err != nil {
 88 | 		log.Fatalf("Failed to load tools: %v\nMake sure your Toolbox server is running and the tool is configured.", err)
 89 | 	}
 90 | 
 91 | 	genAITools := make([]*genai.Tool, len(tools))
 92 | 	toolsMap := make(map[string]*core.ToolboxTool, len(tools))
 93 | 
 94 | 	for i, tool := range tools {
 95 | 		genAITools[i] = ConvertToGenaiTool(tool)
 96 | 		toolsMap[tool.Name()] = tool
 97 | 	}
 98 | 
 99 | 	// Set up the generative model with the available tool.
100 | 	modelName := "gemini-2.0-flash"
101 | 
102 | 	// Create the initial content prompt for the model.
103 | 	messageHistory := []*genai.Content{
104 | 		genai.NewContentFromText(systemPrompt, genai.RoleUser),
105 | 	}
106 | 	config := &genai.GenerateContentConfig{
107 | 		Tools: genAITools,
108 | 		ToolConfig: &genai.ToolConfig{
109 | 			FunctionCallingConfig: &genai.FunctionCallingConfig{
110 | 				Mode: genai.FunctionCallingConfigModeAny,
111 | 			},
112 | 		},
113 | 	}
114 | 
115 | 	for _, query := range queries {
116 | 
117 | 		messageHistory = append(messageHistory, genai.NewContentFromText(query, genai.RoleUser))
118 | 
119 | 		genContentResp, err := client.Models.GenerateContent(ctx, modelName, messageHistory, config)
120 | 		if err != nil {
121 | 			log.Fatalf("LLM call failed for query '%s': %v", query, err)
122 | 		}
123 | 
124 | 		if len(genContentResp.Candidates) > 0 && genContentResp.Candidates[0].Content != nil {
125 | 			messageHistory = append(messageHistory, genContentResp.Candidates[0].Content)
126 | 		}
127 | 
128 | 		functionCalls := genContentResp.FunctionCalls()
129 | 
130 | 		toolResponseParts := []*genai.Part{}
131 | 
132 | 		for _, fc := range functionCalls {
133 | 
134 | 			toolToInvoke, found := toolsMap[fc.Name]
135 | 			if !found {
136 | 				log.Fatalf("Tool '%s' not found in loaded tools map. Check toolset configuration.", fc.Name)
137 | 			}
138 | 
139 | 			toolResult, invokeErr := toolToInvoke.Invoke(ctx, fc.Args)
140 | 			if invokeErr != nil {
141 | 				log.Fatalf("Failed to execute tool '%s': %v", fc.Name, invokeErr)
142 | 			}
143 | 
144 | 			// Enhanced Tool Result Handling (retained to prevent nil issues)
145 | 			toolResultString := ""
146 | 			if toolResult != nil {
147 | 				jsonBytes, marshalErr := json.Marshal(toolResult)
148 | 				if marshalErr == nil {
149 | 					toolResultString = string(jsonBytes)
150 | 				} else {
151 | 					toolResultString = fmt.Sprintf("%v", toolResult)
152 | 				}
153 | 			}
154 | 
155 | 			responseMap := map[string]any{"result": toolResultString}
156 | 
157 | 			toolResponseParts = append(toolResponseParts, genai.NewPartFromFunctionResponse(fc.Name, responseMap))
158 | 		}
159 | 		// Add all accumulated tool responses for this turn to the message history.
160 | 		toolResponseContent := genai.NewContentFromParts(toolResponseParts, "function")
161 | 		messageHistory = append(messageHistory, toolResponseContent)
162 | 
163 | 		finalResponse, err := client.Models.GenerateContent(ctx, modelName, messageHistory, &genai.GenerateContentConfig{})
164 | 		if err != nil {
165 | 			log.Fatalf("Error calling GenerateContent (with function result): %v", err)
166 | 		}
167 | 
168 | 		printResponse(finalResponse)
169 | 		// Add the final textual response from the LLM to the history
170 | 		if len(finalResponse.Candidates) > 0 && finalResponse.Candidates[0].Content != nil {
171 | 			messageHistory = append(messageHistory, finalResponse.Candidates[0].Content)
172 | 		}
173 | 	}
174 | }
175 | 
```

--------------------------------------------------------------------------------
/internal/sources/alloydbpg/alloydb_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 alloydbpg_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
 25 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 26 | )
 27 | 
 28 | func TestParseFromYamlAlloyDBPg(t *testing.T) {
 29 | 	tcs := []struct {
 30 | 		desc string
 31 | 		in   string
 32 | 		want server.SourceConfigs
 33 | 	}{
 34 | 		{
 35 | 			desc: "basic example",
 36 | 			in: `
 37 | 			sources:
 38 | 				my-pg-instance:
 39 | 					kind: alloydb-postgres
 40 | 					project: my-project
 41 | 					region: my-region
 42 | 					cluster: my-cluster
 43 | 					instance: my-instance
 44 | 					database: my_db
 45 | 					user: my_user
 46 | 					password: my_pass
 47 | 			`,
 48 | 			want: map[string]sources.SourceConfig{
 49 | 				"my-pg-instance": alloydbpg.Config{
 50 | 					Name:     "my-pg-instance",
 51 | 					Kind:     alloydbpg.SourceKind,
 52 | 					Project:  "my-project",
 53 | 					Region:   "my-region",
 54 | 					Cluster:  "my-cluster",
 55 | 					Instance: "my-instance",
 56 | 					IPType:   "public",
 57 | 					Database: "my_db",
 58 | 					User:     "my_user",
 59 | 					Password: "my_pass",
 60 | 				},
 61 | 			},
 62 | 		},
 63 | 		{
 64 | 			desc: "public ipType",
 65 | 			in: `
 66 | 			sources:
 67 | 				my-pg-instance:
 68 | 					kind: alloydb-postgres
 69 | 					project: my-project
 70 | 					region: my-region
 71 | 					cluster: my-cluster
 72 | 					instance: my-instance
 73 | 					ipType: Public
 74 | 					database: my_db
 75 | 					user: my_user
 76 | 					password: my_pass
 77 | 			`,
 78 | 			want: map[string]sources.SourceConfig{
 79 | 				"my-pg-instance": alloydbpg.Config{
 80 | 					Name:     "my-pg-instance",
 81 | 					Kind:     alloydbpg.SourceKind,
 82 | 					Project:  "my-project",
 83 | 					Region:   "my-region",
 84 | 					Cluster:  "my-cluster",
 85 | 					Instance: "my-instance",
 86 | 					IPType:   "public",
 87 | 					Database: "my_db",
 88 | 					User:     "my_user",
 89 | 					Password: "my_pass",
 90 | 				},
 91 | 			},
 92 | 		},
 93 | 		{
 94 | 			desc: "private ipType",
 95 | 			in: `
 96 | 			sources:
 97 | 				my-pg-instance:
 98 | 					kind: alloydb-postgres
 99 | 					project: my-project
100 | 					region: my-region
101 | 					cluster: my-cluster
102 | 					instance: my-instance
103 | 					ipType: private
104 | 					database: my_db
105 | 					user: my_user
106 | 					password: my_pass
107 | 			`,
108 | 			want: map[string]sources.SourceConfig{
109 | 				"my-pg-instance": alloydbpg.Config{
110 | 					Name:     "my-pg-instance",
111 | 					Kind:     alloydbpg.SourceKind,
112 | 					Project:  "my-project",
113 | 					Region:   "my-region",
114 | 					Cluster:  "my-cluster",
115 | 					Instance: "my-instance",
116 | 					IPType:   "private",
117 | 					Database: "my_db",
118 | 					User:     "my_user",
119 | 					Password: "my_pass",
120 | 				},
121 | 			},
122 | 		},
123 | 	}
124 | 	for _, tc := range tcs {
125 | 		t.Run(tc.desc, func(t *testing.T) {
126 | 			got := struct {
127 | 				Sources server.SourceConfigs `yaml:"sources"`
128 | 			}{}
129 | 			// Parse contents
130 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
131 | 			if err != nil {
132 | 				t.Fatalf("unable to unmarshal: %s", err)
133 | 			}
134 | 			if !cmp.Equal(tc.want, got.Sources) {
135 | 				t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources)
136 | 			}
137 | 		})
138 | 	}
139 | }
140 | 
141 | func TestFailParseFromYaml(t *testing.T) {
142 | 	tcs := []struct {
143 | 		desc string
144 | 		in   string
145 | 		err  string
146 | 	}{
147 | 		{
148 | 			desc: "invalid ipType",
149 | 			in: `
150 | 			sources:
151 | 				my-pg-instance:
152 | 					kind: alloydb-postgres
153 | 					project: my-project
154 | 					region: my-region
155 | 					cluster: my-cluster
156 | 					instance: my-instance
157 | 					ipType: fail 
158 | 					database: my_db
159 | 					user: my_user
160 | 					password: my_pass
161 | 			`,
162 | 			err: "unable to parse source \"my-pg-instance\" as \"alloydb-postgres\": ipType invalid: must be one of \"public\", \"private\", or \"psc\"",
163 | 		},
164 | 		{
165 | 			desc: "extra field",
166 | 			in: `
167 | 			sources:
168 | 				my-pg-instance:
169 | 					kind: alloydb-postgres
170 | 					project: my-project
171 | 					region: my-region
172 | 					cluster: my-cluster
173 | 					instance: my-instance
174 | 					database: my_db
175 | 					user: my_user
176 | 					password: my_pass
177 | 					foo: bar
178 | 			`,
179 | 			err: "unable to parse source \"my-pg-instance\" as \"alloydb-postgres\": [3:1] unknown field \"foo\"\n   1 | cluster: my-cluster\n   2 | database: my_db\n>  3 | foo: bar\n       ^\n   4 | instance: my-instance\n   5 | kind: alloydb-postgres\n   6 | password: my_pass\n   7 | ",
180 | 		},
181 | 		{
182 | 			desc: "missing required field",
183 | 			in: `
184 | 			sources:
185 | 				my-pg-instance:
186 | 					kind: alloydb-postgres
187 | 					region: my-region
188 | 					cluster: my-cluster
189 | 					instance: my-instance
190 | 					database: my_db
191 | 					user: my_user
192 | 					password: my_pass
193 | 			`,
194 | 			err: "unable to parse source \"my-pg-instance\" as \"alloydb-postgres\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
195 | 		},
196 | 	}
197 | 	for _, tc := range tcs {
198 | 		t.Run(tc.desc, func(t *testing.T) {
199 | 			got := struct {
200 | 				Sources server.SourceConfigs `yaml:"sources"`
201 | 			}{}
202 | 			// Parse contents
203 | 			err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
204 | 			if err == nil {
205 | 				t.Fatalf("expect parsing to fail")
206 | 			}
207 | 			errStr := err.Error()
208 | 			if errStr != tc.err {
209 | 				t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
210 | 			}
211 | 		})
212 | 	}
213 | }
214 | 
```

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

```markdown
  1 | ---
  2 | title: "bigtable-sql"
  3 | type: docs
  4 | weight: 1
  5 | description: >
  6 |   A "bigtable-sql" tool executes a pre-defined SQL statement against a Google
  7 |   Cloud Bigtable instance.
  8 | aliases:
  9 | - /resources/tools/bigtable-sql
 10 | ---
 11 | 
 12 | ## About
 13 | 
 14 | A `bigtable-sql` tool executes a pre-defined SQL statement against a Bigtable
 15 | instance. It's compatible with any of the following sources:
 16 | 
 17 | - [bigtable](../../sources/bigtable.md)
 18 | 
 19 | ### GoogleSQL
 20 | 
 21 | Bigtable supports SQL queries. The integration with Toolbox supports `googlesql`
 22 | dialect, the specified SQL statement is executed as a [data manipulation
 23 | language (DML)][bigtable-googlesql] statements, and specified parameters will
 24 | inserted according to their name: e.g. `@name`.
 25 | 
 26 | {{<notice note>}}
 27 |   Bigtable's GoogleSQL support for DML statements might be limited to certain
 28 |   query types. For detailed information on supported DML statements and use
 29 |   cases, refer to the [Bigtable GoogleSQL use
 30 |   cases](https://cloud.google.com/bigtable/docs/googlesql-overview#use-cases).
 31 | {{</notice>}}
 32 | 
 33 | [bigtable-googlesql]: https://cloud.google.com/bigtable/docs/googlesql-overview
 34 | 
 35 | ## Example
 36 | 
 37 | > **Note:** This tool uses parameterized queries to prevent SQL injections.
 38 | > Query parameters can be used as substitutes for arbitrary expressions.
 39 | > Parameters cannot be used as substitutes for identifiers, column names, table
 40 | > names, or other parts of the query.
 41 | 
 42 | ```yaml
 43 | tools:
 44 |  search_user_by_id_or_name:
 45 |     kind: bigtable-sql
 46 |     source: my-bigtable-instance
 47 |     statement: |
 48 |       SELECT
 49 |         TO_INT64(cf[ 'id' ]) as id,
 50 |         CAST(cf[ 'name' ] AS string) as name,
 51 |       FROM
 52 |         mytable
 53 |       WHERE
 54 |         TO_INT64(cf[ 'id' ]) = @id
 55 |         OR CAST(cf[ 'name' ] AS string) = @name;
 56 |     description: |
 57 |       Use this tool to get information for a specific user.
 58 |       Takes an id number or a name and returns info on the user.
 59 | 
 60 |       Example:
 61 |       {{
 62 |           "id": 123,
 63 |           "name": "Alice",
 64 |       }}
 65 |     parameters:
 66 |       - name: id
 67 |         type: integer
 68 |         description: User ID
 69 |       - name: name
 70 |         type: string
 71 |         description: Name of the user
 72 | ```
 73 | 
 74 | ### Example with Template Parameters
 75 | 
 76 | > **Note:** This tool allows direct modifications to the SQL statement,
 77 | > including identifiers, column names, and table names. **This makes it more
 78 | > vulnerable to SQL injections**. Using basic parameters only (see above) is
 79 | > recommended for performance and safety reasons. For more details, please check
 80 | > [templateParameters](..#template-parameters).
 81 | 
 82 | ```yaml
 83 | tools:
 84 |  list_table:
 85 |     kind: bigtable-sql
 86 |     source: my-bigtable-instance
 87 |     statement: |
 88 |       SELECT * FROM {{.tableName}};
 89 |     description: |
 90 |       Use this tool to list all information from a specific table.
 91 |       Example:
 92 |       {{
 93 |           "tableName": "flights",
 94 |       }}
 95 |     templateParameters:
 96 |       - name: tableName
 97 |         type: string
 98 |         description: Table to select from
 99 | ```
100 | 
101 | ## Reference
102 | 
103 | | **field**          |                  **type**                        | **required** | **description**                                                                                                                            |
104 | |--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
105 | | kind               |                   string                         |     true     | Must be "bigtable-sql".                                                                                                                    |
106 | | source             |                   string                         |     true     | Name of the source the SQL should execute on.                                                                                              |
107 | | description        |                   string                         |     true     | Description of the tool that is passed to the LLM.                                                                                         |
108 | | statement          |                   string                         |     true     | SQL statement to execute on.                                                                                                               |
109 | | parameters         | [parameters](../#specifying-parameters)       |    false     | List of [parameters](../#specifying-parameters) that will be inserted into the SQL statement.                                           |
110 | | templateParameters | [templateParameters](..#template-parameters) |    false     | List of [templateParameters](..#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
111 | 
112 | ## Tips
113 | 
114 | - [Bigtable Studio][bigtable-studio] is a useful to explore and manage your
115 |   Bigtable data. If you're unfamiliar with the query syntax, [Query
116 |   Builder][bigtable-querybuilder] lets you build a query, run it against a
117 |   table, and then view the results in the console.
118 | - Some Python libraries limit the use of underscore columns such as `_key`. A
119 |   workaround would be to leverage Bigtable [Logical
120 |   Views][bigtable-logical-view] to rename the columns.
121 | 
122 | [bigtable-studio]: https://cloud.google.com/bigtable/docs/manage-data-using-console
123 | [bigtable-logical-view]: https://cloud.google.com/bigtable/docs/create-manage-logical-views
124 | [bigtable-querybuilder]: https://cloud.google.com/bigtable/docs/query-builder
125 | 
```

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

```markdown
 1 | ---
 2 | title: "mongodb-update-one"
 3 | type: docs
 4 | weight: 1
 5 | description: > 
 6 |   A "mongodb-update-one" tool updates a single document in a MongoDB collection.
 7 | aliases:
 8 | - /resources/tools/mongodb-update-one
 9 | ---
10 | 
11 | ## About
12 | 
13 | A `mongodb-update-one` tool updates a single document within a specified MongoDB
14 | collection. It locates the document to be updated using a `filterPayload` and
15 | applies modifications defined in an `updatePayload`. If the filter matches
16 | multiple documents, only the first one found will be updated.
17 | 
18 | This tool is compatible with the following source kind:
19 | 
20 | * [`mongodb`](../../sources/mongodb.md)
21 | 
22 | ---
23 | 
24 | ## Example
25 | 
26 | Here's an example of a `mongodb-update-one` tool configuration. This tool
27 | updates the `stock` and `status` fields of a document in the `inventory`
28 | collection where the `item` field matches a provided value. If no matching
29 | document is found, the `upsert: true` option will create a new one.
30 | 
31 | ```yaml
32 | tools:
33 |   update_inventory_item:
34 |     kind: mongodb-update-one
35 |     source: my-mongo-source
36 |     description: Use this tool to update an item's stock and status in the inventory.
37 |     database: products
38 |     collection: inventory
39 |     filterPayload: |
40 |         { "item": {{json .item_name}} }
41 |     filterParams:
42 |       - name: item_name
43 |         type: string
44 |         description: The name of the item to update.
45 |     updatePayload: |
46 |         { "$set": { "stock": {{json .new_stock}}, "status": {{json .new_status}} } }
47 |     updateParams:
48 |       - name: new_stock
49 |         type: integer
50 |         description: The new stock quantity.
51 |       - name: new_status
52 |         type: string
53 |         description: The new status of the item (e.g., "In Stock", "Backordered").
54 |     canonical: false
55 |     upsert: true
56 | ```
57 | 
58 | ## Reference
59 | 
60 | | **field**     | **type** | **required** | **description**                                                                                                                                                                                                                                   |
61 | |:--------------|:---------|:-------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
62 | | kind          | string   | true         | Must be `mongodb-update-one`.                                                                                                                                                                                                                     |
63 | | source        | string   | true         | The name of the `mongodb` source to use.                                                                                                                                                                                                          |
64 | | description   | string   | true         | A description of the tool that is passed to the LLM.                                                                                                                                                                                              |
65 | | database      | string   | true         | The name of the MongoDB database containing the collection.                                                                                                                                                                                       |
66 | | collection    | string   | true         | The name of the MongoDB collection to update a document in.                                                                                                                                                                                       |
67 | | filterPayload | string   | true         | The MongoDB query filter document to select the document for updating. It's written as a Go template, using `{{json .param_name}}` to insert parameters.                                                                                          |
68 | | filterParams  | list     | true         | A list of parameter objects that define the variables used in the `filterPayload`.                                                                                                                                                                |
69 | | updatePayload | string   | true         | The MongoDB update document, which specifies the modifications. This often uses update operators like `$set`. It's written as a Go template, using `{{json .param_name}}` to insert parameters.                                                   |
70 | | updateParams  | list     | true         | A list of parameter objects that define the variables used in the `updatePayload`.                                                                                                                                                                |
71 | | canonical     | bool     | true         | Determines if the `updatePayload` string is parsed using MongoDB's Canonical or Relaxed Extended JSON format. **Canonical** is stricter about type representation (e.g., `{"$numberInt": "42"}`), while **Relaxed** is more lenient (e.g., `42`). |
72 | | upsert        | bool     | false        | If `true`, a new document is created if no document matches the `filterPayload`. Defaults to `false`.                                                                                                                                             |
73 | 
```

--------------------------------------------------------------------------------
/internal/tools/neo4j/neo4jexecutecypher/neo4jexecutecypher.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 neo4jexecutecypher
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	neo4jsc "github.com/googleapis/genai-toolbox/internal/sources/neo4j"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jexecutecypher/classifier"
 26 | 	"github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jschema/helpers"
 27 | 	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
 28 | )
 29 | 
 30 | const kind string = "neo4j-execute-cypher"
 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 | 	Neo4jDriver() neo4j.DriverWithContext
 48 | 	Neo4jDatabase() string
 49 | }
 50 | 
 51 | // validate compatible sources are still compatible
 52 | var _ compatibleSource = &neo4jsc.Source{}
 53 | 
 54 | var compatibleSources = [...]string{neo4jsc.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 | 	ReadOnly     bool     `yaml:"readOnly"`
 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 | 	var s compatibleSource
 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 | 	cypherParameter := tools.NewStringParameter("cypher", "The cypher to execute.")
 87 | 	parameters := tools.Parameters{cypherParameter}
 88 | 
 89 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, parameters)
 90 | 
 91 | 	// finish tool setup
 92 | 	t := Tool{
 93 | 		Name:         cfg.Name,
 94 | 		Kind:         kind,
 95 | 		Parameters:   parameters,
 96 | 		AuthRequired: cfg.AuthRequired,
 97 | 		ReadOnly:     cfg.ReadOnly,
 98 | 		Driver:       s.Neo4jDriver(),
 99 | 		Database:     s.Neo4jDatabase(),
100 | 		classifier:   classifier.NewQueryClassifier(),
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 | 	Parameters   tools.Parameters `yaml:"parameters"`
114 | 	AuthRequired []string         `yaml:"authRequired"`
115 | 	ReadOnly     bool             `yaml:"readOnly"`
116 | 	Database     string
117 | 	Driver       neo4j.DriverWithContext
118 | 	classifier   *classifier.QueryClassifier
119 | 	manifest     tools.Manifest
120 | 	mcpManifest  tools.McpManifest
121 | }
122 | 
123 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
124 | 	paramsMap := params.AsMap()
125 | 	cypherStr, ok := paramsMap["cypher"].(string)
126 | 	if !ok {
127 | 		return nil, fmt.Errorf("unable to get cast %s", paramsMap["cypher"])
128 | 	}
129 | 
130 | 	if cypherStr == "" {
131 | 		return nil, fmt.Errorf("parameter 'cypher' must be a non-empty string")
132 | 	}
133 | 
134 | 	// validate the cypher query before executing
135 | 	cf := t.classifier.Classify(cypherStr)
136 | 	if cf.Error != nil {
137 | 		return nil, cf.Error
138 | 	}
139 | 
140 | 	if cf.Type == classifier.WriteQuery && t.ReadOnly {
141 | 		return nil, fmt.Errorf("this tool is read-only and cannot execute write queries")
142 | 	}
143 | 
144 | 	config := neo4j.ExecuteQueryWithDatabase(t.Database)
145 | 	results, err := neo4j.ExecuteQuery(ctx, t.Driver, cypherStr, nil,
146 | 		neo4j.EagerResultTransformer, config)
147 | 	if err != nil {
148 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
149 | 	}
150 | 
151 | 	var out []any
152 | 	keys := results.Keys
153 | 	records := results.Records
154 | 	for _, record := range records {
155 | 		vMap := make(map[string]any)
156 | 		for col, value := range record.Values {
157 | 			vMap[keys[col]] = helpers.ConvertValue(value)
158 | 		}
159 | 		out = append(out, vMap)
160 | 	}
161 | 
162 | 	return out, nil
163 | }
164 | 
165 | func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (tools.ParamValues, error) {
166 | 	return tools.ParseParams(t.Parameters, data, claimsMap)
167 | }
168 | 
169 | func (t Tool) Manifest() tools.Manifest {
170 | 	return t.manifest
171 | }
172 | 
173 | func (t Tool) McpManifest() tools.McpManifest {
174 | 	return t.mcpManifest
175 | }
176 | 
177 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
178 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
179 | }
180 | 
181 | func (t Tool) RequiresClientAuthorization() bool {
182 | 	return false
183 | }
184 | 
```

--------------------------------------------------------------------------------
/internal/tools/looker/lookercommon/lookercommon_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 lookercommon_test
 16 | 
 17 | import (
 18 | 	"encoding/json"
 19 | 	"testing"
 20 | 
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
 24 | 	v4 "github.com/looker-open-source/sdk-codegen/go/sdk/v4"
 25 | )
 26 | 
 27 | func TestExtractLookerFieldProperties(t *testing.T) {
 28 | 	ctx, err := testutils.ContextWithNewLogger()
 29 | 	if err != nil {
 30 | 		t.Fatalf("unexpected error: %s", err)
 31 | 	}
 32 | 
 33 | 	// Helper function to create string pointers
 34 | 	stringPtr := func(s string) *string { return &s }
 35 | 	stringArrayPtr := func(s []string) *[]string { return &s }
 36 | 	boolPtr := func(b bool) *bool { return &b }
 37 | 
 38 | 	tcs := []struct {
 39 | 		desc   string
 40 | 		fields []v4.LookmlModelExploreField
 41 | 		want   []any
 42 | 	}{
 43 | 		{
 44 | 			desc: "field with all properties including description",
 45 | 			fields: []v4.LookmlModelExploreField{
 46 | 				{
 47 | 					Name:             stringPtr("dimension_name"),
 48 | 					Type:             stringPtr("string"),
 49 | 					Label:            stringPtr("Dimension Label"),
 50 | 					LabelShort:       stringPtr("Dim Label"),
 51 | 					Description:      stringPtr("This is a dimension description"),
 52 | 					Suggestable:      boolPtr(true),
 53 | 					SuggestExplore:   stringPtr("explore"),
 54 | 					SuggestDimension: stringPtr("dimension"),
 55 | 					Suggestions:      stringArrayPtr([]string{"foo", "bar", "baz"}),
 56 | 				},
 57 | 			},
 58 | 			want: []any{
 59 | 				map[string]any{
 60 | 					"name":              "dimension_name",
 61 | 					"type":              "string",
 62 | 					"label":             "Dimension Label",
 63 | 					"label_short":       "Dim Label",
 64 | 					"description":       "This is a dimension description",
 65 | 					"suggest_explore":   "explore",
 66 | 					"suggest_dimension": "dimension",
 67 | 					"suggestions":       []string{"foo", "bar", "baz"},
 68 | 				},
 69 | 			},
 70 | 		},
 71 | 		{
 72 | 			desc: "field with missing description",
 73 | 			fields: []v4.LookmlModelExploreField{
 74 | 				{
 75 | 					Name:       stringPtr("dimension_name"),
 76 | 					Type:       stringPtr("string"),
 77 | 					Label:      stringPtr("Dimension Label"),
 78 | 					LabelShort: stringPtr("Dim Label"),
 79 | 					// Description is nil
 80 | 				},
 81 | 			},
 82 | 			want: []any{
 83 | 				map[string]any{
 84 | 					"name":        "dimension_name",
 85 | 					"type":        "string",
 86 | 					"label":       "Dimension Label",
 87 | 					"label_short": "Dim Label",
 88 | 					// description should not be present in the map
 89 | 				},
 90 | 			},
 91 | 		},
 92 | 		{
 93 | 			desc: "field with only required fields",
 94 | 			fields: []v4.LookmlModelExploreField{
 95 | 				{
 96 | 					Name: stringPtr("simple_dimension"),
 97 | 					Type: stringPtr("number"),
 98 | 				},
 99 | 			},
100 | 			want: []any{
101 | 				map[string]any{
102 | 					"name": "simple_dimension",
103 | 					"type": "number",
104 | 				},
105 | 			},
106 | 		},
107 | 		{
108 | 			desc:   "empty fields list",
109 | 			fields: []v4.LookmlModelExploreField{},
110 | 			want:   []any{},
111 | 		},
112 | 		{
113 | 			desc: "multiple fields with mixed properties",
114 | 			fields: []v4.LookmlModelExploreField{
115 | 				{
116 | 					Name:        stringPtr("dim1"),
117 | 					Type:        stringPtr("string"),
118 | 					Label:       stringPtr("First Dimension"),
119 | 					Description: stringPtr("First dimension description"),
120 | 				},
121 | 				{
122 | 					Name:       stringPtr("dim2"),
123 | 					Type:       stringPtr("number"),
124 | 					LabelShort: stringPtr("Dim2"),
125 | 				},
126 | 			},
127 | 			want: []any{
128 | 				map[string]any{
129 | 					"name":        "dim1",
130 | 					"type":        "string",
131 | 					"label":       "First Dimension",
132 | 					"description": "First dimension description",
133 | 				},
134 | 				map[string]any{
135 | 					"name":        "dim2",
136 | 					"type":        "number",
137 | 					"label_short": "Dim2",
138 | 				},
139 | 			},
140 | 		},
141 | 	}
142 | 
143 | 	for _, tc := range tcs {
144 | 		t.Run(tc.desc, func(t *testing.T) {
145 | 			got, err := lookercommon.ExtractLookerFieldProperties(ctx, &tc.fields, true)
146 | 			if err != nil {
147 | 				t.Fatalf("unexpected error: %v", err)
148 | 			}
149 | 
150 | 			if diff := cmp.Diff(tc.want, got); diff != "" {
151 | 				t.Fatalf("incorrect result: diff %v", diff)
152 | 			}
153 | 		})
154 | 	}
155 | }
156 | 
157 | func TestExtractLookerFieldPropertiesWithNilFields(t *testing.T) {
158 | 	ctx, err := testutils.ContextWithNewLogger()
159 | 	if err != nil {
160 | 		t.Fatalf("unexpected error: %s", err)
161 | 	}
162 | 
163 | 	got, err := lookercommon.ExtractLookerFieldProperties(ctx, nil, true)
164 | 	if err != nil {
165 | 		t.Fatalf("unexpected error: %v", err)
166 | 	}
167 | 
168 | 	want := []any{}
169 | 	if diff := cmp.Diff(want, got); diff != "" {
170 | 		t.Fatalf("incorrect result: diff %v", diff)
171 | 	}
172 | }
173 | 
174 | func TestRequestRunInlineQuery2(t *testing.T) {
175 | 	fields := make([]string, 1)
176 | 	fields[0] = "foo.bar"
177 | 	wq := v4.WriteQuery{
178 | 		Model:  "model",
179 | 		View:   "explore",
180 | 		Fields: &fields,
181 | 	}
182 | 	req2 := lookercommon.RequestRunInlineQuery2{
183 | 		Query: wq,
184 | 		RenderOpts: lookercommon.RenderOptions{
185 | 			Format: "json",
186 | 		},
187 | 		QueryApiClientCtx: lookercommon.QueryApiClientContext{
188 | 			Name: "MCP Toolbox",
189 | 		},
190 | 	}
191 | 	json, err := json.Marshal(req2)
192 | 	if err != nil {
193 | 		t.Fatalf("Could not marshall req2 as json")
194 | 	}
195 | 	got := string(json)
196 | 	want := `{"query":{"model":"model","view":"explore","fields":["foo.bar"]},"render_options":{"format":"json"},"query_api_client_context":{"name":"MCP Toolbox"}}`
197 | 	if diff := cmp.Diff(want, got); diff != "" {
198 | 		t.Fatalf("incorrect result: diff %v", diff)
199 | 	}
200 | 
201 | }
202 | 
```

--------------------------------------------------------------------------------
/tests/yugabytedb/yugabytedb_integration_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 yugabytedb
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 	"os"
 21 | 	"regexp"
 22 | 	"strings"
 23 | 	"testing"
 24 | 	"time"
 25 | 
 26 | 	"github.com/google/uuid"
 27 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 28 | 	"github.com/googleapis/genai-toolbox/tests"
 29 | 	"github.com/yugabyte/pgx/v5/pgxpool"
 30 | )
 31 | 
 32 | var (
 33 | 	YBDB_SOURCE_KIND = "yugabytedb"
 34 | 	YBDB_TOOL_KIND   = "yugabytedb-sql"
 35 | 	YBDB_DATABASE    = os.Getenv("YUGABYTEDB_DATABASE")
 36 | 	YBDB_HOST        = os.Getenv("YUGABYTEDB_HOST")
 37 | 	YBDB_PORT        = os.Getenv("YUGABYTEDB_PORT")
 38 | 	YBDB_USER        = os.Getenv("YUGABYTEDB_USER")
 39 | 	YBDB_PASS        = os.Getenv("YUGABYTEDB_PASS")
 40 | 	YBDB_LB          = os.Getenv("YUGABYTEDB_LOADBALANCE")
 41 | )
 42 | 
 43 | func getYBVars(t *testing.T) map[string]any {
 44 | 	switch "" {
 45 | 	case YBDB_DATABASE:
 46 | 		t.Fatal("'YUGABYTEDB_DATABASE' not set")
 47 | 	case YBDB_HOST:
 48 | 		t.Fatal("'YUGABYTEDB_HOST' not set")
 49 | 	case YBDB_PORT:
 50 | 		t.Fatal("'YUGABYTEDB_PORT' not set")
 51 | 	case YBDB_USER:
 52 | 		t.Fatal("'YUGABYTEDB_USER' not set")
 53 | 	case YBDB_PASS:
 54 | 		t.Fatal("'YUGABYTEDB_PASS' not set")
 55 | 	case YBDB_LB:
 56 | 		fmt.Printf("YUGABYTEDB_LOADBALANCE value not set. Setting default value: false")
 57 | 		YBDB_LB = "false"
 58 | 	}
 59 | 
 60 | 	return map[string]any{
 61 | 		"kind":        YBDB_SOURCE_KIND,
 62 | 		"host":        YBDB_HOST,
 63 | 		"port":        YBDB_PORT,
 64 | 		"database":    YBDB_DATABASE,
 65 | 		"user":        YBDB_USER,
 66 | 		"password":    YBDB_PASS,
 67 | 		"loadBalance": YBDB_LB,
 68 | 	}
 69 | }
 70 | 
 71 | func initYBConnectionPool(host, port, user, pass, dbname, loadBalance string) (*pgxpool.Pool, error) {
 72 | 	dsn := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?load_balance=%s", user, pass, host, port, dbname, loadBalance)
 73 | 	pool, err := pgxpool.New(context.Background(), dsn)
 74 | 	if err != nil {
 75 | 		return nil, fmt.Errorf("unable to create YugabyteDB connection pool: %w", err)
 76 | 	}
 77 | 	return pool, nil
 78 | }
 79 | 
 80 | // SetupYugabyteDBSQLTable creates and inserts data into a table of tool
 81 | // compatible with yugabytedb-sql tool
 82 | func SetupYugabyteDBSQLTable(t *testing.T, ctx context.Context, pool *pgxpool.Pool, create_statement, insert_statement, tableName string, params []any) func(*testing.T) {
 83 | 	err := pool.Ping(ctx)
 84 | 	if err != nil {
 85 | 		t.Fatalf("unable to connect to test database: %s", err)
 86 | 	}
 87 | 
 88 | 	// Create table
 89 | 	_, err = pool.Query(ctx, create_statement)
 90 | 	if err != nil {
 91 | 		t.Fatalf("unable to create test table %s: %s", tableName, err)
 92 | 	}
 93 | 
 94 | 	// Insert test data
 95 | 	_, err = pool.Query(ctx, insert_statement, params...)
 96 | 	if err != nil {
 97 | 		t.Fatalf("unable to insert test data: %s", err)
 98 | 	}
 99 | 
100 | 	return func(t *testing.T) {
101 | 		// tear down test
102 | 		_, err = pool.Exec(ctx, fmt.Sprintf("DROP TABLE %s;", tableName))
103 | 		if err != nil {
104 | 			t.Errorf("Teardown failed: %s", err)
105 | 		}
106 | 	}
107 | }
108 | 
109 | func TestYugabyteDB(t *testing.T) {
110 | 	sourceConfig := getYBVars(t)
111 | 	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
112 | 	defer cancel()
113 | 
114 | 	var args []string
115 | 
116 | 	pool, err := initYBConnectionPool(YBDB_HOST, YBDB_PORT, YBDB_USER, YBDB_PASS, YBDB_DATABASE, YBDB_LB)
117 | 	if err != nil {
118 | 		t.Fatalf("unable to create YugabyteDB connection pool: %s", err)
119 | 	}
120 | 
121 | 	tableNameParam := "param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
122 | 	tableNameAuth := "auth_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
123 | 	tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
124 | 
125 | 	createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
126 | 	teardownTable1 := SetupYugabyteDBSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
127 | 	defer teardownTable1(t)
128 | 
129 | 	createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := tests.GetPostgresSQLAuthToolInfo(tableNameAuth)
130 | 	teardownTable2 := SetupYugabyteDBSQLTable(t, ctx, pool, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams)
131 | 	defer teardownTable2(t)
132 | 
133 | 	toolsFile := tests.GetToolsConfig(sourceConfig, YBDB_TOOL_KIND, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
134 | 	tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
135 | 	toolsFile = tests.AddTemplateParamConfig(t, toolsFile, YBDB_TOOL_KIND, tmplSelectCombined, tmplSelectFilterCombined, "")
136 | 
137 | 	cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
138 | 	if err != nil {
139 | 		t.Fatalf("command initialization returned an error: %s", err)
140 | 	}
141 | 	defer cleanup()
142 | 
143 | 	waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
144 | 	defer cancel()
145 | 	out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
146 | 	if err != nil {
147 | 		t.Logf("toolbox command logs: \n%s", out)
148 | 		t.Fatalf("toolbox didn't start successfully: %s", err)
149 | 	}
150 | 
151 | 	select1Want, mcpMyFailToolWant, _, mcpSelect1Want := tests.GetPostgresWants()
152 | 
153 | 	tests.RunToolGetTest(t)
154 | 	tests.RunToolInvokeTest(t, select1Want)
155 | 	tests.RunMCPToolCallMethod(t, mcpMyFailToolWant, mcpSelect1Want)
156 | 	tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam)
157 | }
158 | 
```
Page 16/47FirstPrevNextLast