This is page 29 of 35. Use http://codebase.md/googleapis/genai-toolbox?lines=false&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
│ │ │ ├── serverless-spark.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
│ │ ├── serverless-spark
│ │ │ ├── _index.md
│ │ │ └── serverless-spark-list-batches.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
│ │ ├── serverless-spark.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
│ │ ├── serverlessspark
│ │ │ ├── serverlessspark_test.go
│ │ │ └── serverlessspark.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
│ │ ├── serverlessspark
│ │ │ └── serverlesssparklistbatches
│ │ │ ├── serverlesssparklistbatches_test.go
│ │ │ └── serverlesssparklistbatches.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
├── serverlessspark
│ └── serverless_spark_integration_test.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
--------------------------------------------------------------------------------
/.hugo/assets/icons/logo.svg:
--------------------------------------------------------------------------------
```
<svg version="1.1" viewBox="0.0 0.0 960.0 662.3989501312336" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l960.0 0l0 662.3989l-960.0 0l0 -662.3989z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l960.0 0l0 662.3989l-960.0 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m0 1.9488845l960.0 0l0 658.4999l-960.0 0z" fill-rule="evenodd"/><g transform="matrix(1.5 0.0 0.0 1.4999997375328085 0.0 1.9488845144356954)"><clipPath id="p.1"><path d="m0 2.220446E-16l640.0 0l0 439.0l-640.0 0z" clip-rule="evenodd"/></clipPath><image clip-path="url(#p.1)" fill="#000" width="640.0" height="439.0" x="0.0" y="0.0" preserveAspectRatio="none" xlink:href="data:image/png;base64,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"/></g></g></svg>
```
--------------------------------------------------------------------------------
/tests/looker/looker_integration_test.go:
--------------------------------------------------------------------------------
```go
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package looker
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"regexp"
"strings"
"testing"
"time"
"github.com/googleapis/genai-toolbox/internal/log"
"github.com/googleapis/genai-toolbox/internal/testutils"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/tests"
)
var (
LookerSourceKind = "looker"
LookerBaseUrl = os.Getenv("LOOKER_BASE_URL")
LookerVerifySsl = os.Getenv("LOOKER_VERIFY_SSL")
LookerClientId = os.Getenv("LOOKER_CLIENT_ID")
LookerClientSecret = os.Getenv("LOOKER_CLIENT_SECRET")
LookerProject = os.Getenv("LOOKER_PROJECT")
LookerLocation = os.Getenv("LOOKER_LOCATION")
)
func getLookerVars(t *testing.T) map[string]any {
switch "" {
case LookerBaseUrl:
t.Fatal("'LOOKER_BASE_URL' not set")
case LookerVerifySsl:
t.Fatal("'LOOKER_VERIFY_SSL' not set")
case LookerClientId:
t.Fatal("'LOOKER_CLIENT_ID' not set")
case LookerClientSecret:
t.Fatal("'LOOKER_CLIENT_SECRET' not set")
case LookerProject:
t.Fatal("'LOOKER_PROJECT' not set")
case LookerLocation:
t.Fatal("'LOOKER_LOCATION' not set")
}
return map[string]any{
"kind": LookerSourceKind,
"base_url": LookerBaseUrl,
"verify_ssl": (LookerVerifySsl == "true"),
"client_id": LookerClientId,
"client_secret": LookerClientSecret,
"project": LookerProject,
"location": LookerLocation,
}
}
func TestLooker(t *testing.T) {
sourceConfig := getLookerVars(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
testLogger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
ctx = util.WithLogger(ctx, testLogger)
var args []string
// Write config into a file and pass it to command
toolsFile := map[string]any{
"sources": map[string]any{
"my-instance": sourceConfig,
},
"tools": map[string]any{
"get_models": map[string]any{
"kind": "looker-get-models",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_explores": map[string]any{
"kind": "looker-get-explores",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_dimensions": map[string]any{
"kind": "looker-get-dimensions",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_measures": map[string]any{
"kind": "looker-get-measures",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_filters": map[string]any{
"kind": "looker-get-filters",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_parameters": map[string]any{
"kind": "looker-get-parameters",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"query": map[string]any{
"kind": "looker-query",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"query_sql": map[string]any{
"kind": "looker-query-sql",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"query_url": map[string]any{
"kind": "looker-query-url",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_looks": map[string]any{
"kind": "looker-get-looks",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_dashboards": map[string]any{
"kind": "looker-get-dashboards",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"conversational_analytics": map[string]any{
"kind": "looker-conversational-analytics",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"health_pulse": map[string]any{
"kind": "looker-health-pulse",
"source": "my-instance",
"description": "Checks the health of a Looker instance by running a series of checks on the system.",
},
"health_analyze": map[string]any{
"kind": "looker-health-analyze",
"source": "my-instance",
"description": "Provides analysis of a Looker instance's projects, models, or explores.",
},
"health_vacuum": map[string]any{
"kind": "looker-health-vacuum",
"source": "my-instance",
"description": "Vacuums unused content from a Looker instance.",
},
"dev_mode": map[string]any{
"kind": "looker-dev-mode",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_projects": map[string]any{
"kind": "looker-get-projects",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_project_files": map[string]any{
"kind": "looker-get-project-files",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"get_project_file": map[string]any{
"kind": "looker-get-project-file",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"create_project_file": map[string]any{
"kind": "looker-create-project-file",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"update_project_file": map[string]any{
"kind": "looker-update-project-file",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
"delete_project_file": map[string]any{
"kind": "looker-delete-project-file",
"source": "my-instance",
"description": "Simple tool to test end to end functionality.",
},
},
}
cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
if err != nil {
t.Fatalf("command initialization returned an error: %s", err)
}
defer cleanup()
waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
if err != nil {
t.Logf("toolbox command logs: \n%s", out)
t.Fatalf("toolbox didn't start successfully: %s", err)
}
tests.RunToolGetTestByName(t, "get_models",
map[string]any{
"get_models": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{},
},
},
)
tests.RunToolGetTestByName(t, "get_explores",
map[string]any{
"get_explores": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explores.",
"name": "model",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_dimensions",
map[string]any{
"get_dimensions": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore containing the fields.",
"name": "explore",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_measures",
map[string]any{
"get_measures": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore containing the fields.",
"name": "explore",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_parameters",
map[string]any{
"get_parameters": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore containing the fields.",
"name": "explore",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_filters",
map[string]any{
"get_filters": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore containing the fields.",
"name": "explore",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "query",
map[string]any{
"query": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore to be queried.",
"name": "explore",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The fields to be retrieved.",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be returned in the query",
"name": "field",
"required": true,
"type": "string",
},
"name": "fields",
"required": true,
"type": "array",
},
map[string]any{
"additionalProperties": true,
"authSources": []any{},
"description": "The filters for the query",
"name": "filters",
"required": false,
"type": "object",
},
map[string]any{
"authSources": []any{},
"description": "The query pivots (must be included in fields as well).",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be used as a pivot in the query",
"name": "pivot_field",
"required": false,
"type": "string",
},
"name": "pivots",
"required": false,
"type": "array",
},
map[string]any{
"authSources": []any{},
"description": "The sorts like \"field.id desc 0\".",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be used as a sort in the query",
"name": "sort_field",
"required": false,
"type": "string",
},
"name": "sorts",
"required": false,
"type": "array",
},
map[string]any{
"authSources": []any{},
"description": "The row limit.",
"name": "limit",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The query timezone.",
"name": "tz",
"required": false,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "query_sql",
map[string]any{
"query_sql": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore to be queried.",
"name": "explore",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The fields to be retrieved.",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be returned in the query",
"name": "field",
"required": true,
"type": "string",
},
"name": "fields",
"required": true,
"type": "array",
},
map[string]any{
"additionalProperties": true,
"authSources": []any{},
"description": "The filters for the query",
"name": "filters",
"required": false,
"type": "object",
},
map[string]any{
"authSources": []any{},
"description": "The query pivots (must be included in fields as well).",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be used as a pivot in the query",
"name": "pivot_field",
"required": false,
"type": "string",
},
"name": "pivots",
"required": false,
"type": "array",
},
map[string]any{
"authSources": []any{},
"description": "The sorts like \"field.id desc 0\".",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be used as a sort in the query",
"name": "sort_field",
"required": false,
"type": "string",
},
"name": "sorts",
"required": false,
"type": "array",
},
map[string]any{
"authSources": []any{},
"description": "The row limit.",
"name": "limit",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The query timezone.",
"name": "tz",
"required": false,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "query_url",
map[string]any{
"query_url": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The model containing the explore.",
"name": "model",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The explore to be queried.",
"name": "explore",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The fields to be retrieved.",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be returned in the query",
"name": "field",
"required": true,
"type": "string",
},
"name": "fields",
"required": true,
"type": "array",
},
map[string]any{
"additionalProperties": true,
"authSources": []any{},
"description": "The filters for the query",
"name": "filters",
"required": false,
"type": "object",
},
map[string]any{
"authSources": []any{},
"description": "The query pivots (must be included in fields as well).",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be used as a pivot in the query",
"name": "pivot_field",
"required": false,
"type": "string",
},
"name": "pivots",
"required": false,
"type": "array",
},
map[string]any{
"authSources": []any{},
"description": "The sorts like \"field.id desc 0\".",
"items": map[string]any{
"authSources": []any{},
"description": "A field to be used as a sort in the query",
"name": "sort_field",
"required": false,
"type": "string",
},
"name": "sorts",
"required": false,
"type": "array",
},
map[string]any{
"authSources": []any{},
"description": "The row limit.",
"name": "limit",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The query timezone.",
"name": "tz",
"required": false,
"type": "string",
},
map[string]any{
"additionalProperties": true,
"authSources": []any{},
"description": "The visualization config for the query",
"name": "vis_config",
"required": false,
"type": "object",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_looks",
map[string]any{
"get_looks": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The title of the look.",
"name": "title",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The description of the look.",
"name": "desc",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The number of looks to fetch. Default 100",
"name": "limit",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The number of looks to skip before fetching. Default 0",
"name": "offset",
"required": false,
"type": "integer",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_dashboards",
map[string]any{
"get_dashboards": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The title of the dashboard.",
"name": "title",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The description of the dashboard.",
"name": "desc",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The number of dashboards to fetch. Default 100",
"name": "limit",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The number of dashboards to skip before fetching. Default 0",
"name": "offset",
"required": false,
"type": "integer",
},
},
},
},
)
tests.RunToolGetTestByName(t, "conversational_analytics",
map[string]any{
"conversational_analytics": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The user's question, potentially including conversation history and system instructions for context.",
"name": "user_query_with_context",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "An Array of at least one and up to 5 explore references like [{'model': 'MODEL_NAME', 'explore': 'EXPLORE_NAME'}]",
"items": map[string]any{
"additionalProperties": true,
"authSources": []any{},
"name": "explore_reference",
"description": "An explore reference like {'model': 'MODEL_NAME', 'explore': 'EXPLORE_NAME'}",
"required": true,
"type": "object",
},
"name": "explore_references",
"required": true,
"type": "array",
},
},
},
},
)
tests.RunToolGetTestByName(t, "health_pulse",
map[string]any{
"health_pulse": map[string]any{
"description": "Checks the health of a Looker instance by running a series of checks on the system.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The health check to run. Can be either: `check_db_connections`, `check_dashboard_performance`,`check_dashboard_errors`,`check_explore_performance`,`check_schedule_failures`, or `check_legacy_features`",
"name": "action",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "health_analyze",
map[string]any{
"health_analyze": map[string]any{
"description": "Provides analysis of a Looker instance's projects, models, or explores.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The analysis to run. Can be 'projects', 'models', or 'explores'.",
"name": "action",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The Looker project to analyze (optional).",
"name": "project",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The Looker model to analyze (optional).",
"name": "model",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The Looker explore to analyze (optional).",
"name": "explore",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The timeframe in days to analyze.",
"name": "timeframe",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The minimum number of queries for a model or explore to be considered used.",
"name": "min_queries",
"required": false,
"type": "integer",
},
},
},
},
)
tests.RunToolGetTestByName(t, "health_vacuum",
map[string]any{
"health_vacuum": map[string]any{
"description": "Vacuums unused content from a Looker instance.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The vacuum action to run. Can be 'models', or 'explores'.",
"name": "action",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The Looker project to vacuum (optional).",
"name": "project",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The Looker model to vacuum (optional).",
"name": "model",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The Looker explore to vacuum (optional).",
"name": "explore",
"required": false,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The timeframe in days to analyze.",
"name": "timeframe",
"required": false,
"type": "integer",
},
map[string]any{
"authSources": []any{},
"description": "The minimum number of queries for a model or explore to be considered used.",
"name": "min_queries",
"required": false,
"type": "integer",
},
},
},
},
)
tests.RunToolGetTestByName(t, "dev_mode",
map[string]any{
"dev_mode": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "Whether to set Dev Mode.",
"name": "devMode",
"required": false,
"type": "boolean",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_projects",
map[string]any{
"get_projects": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{},
},
},
)
tests.RunToolGetTestByName(t, "get_project_files",
map[string]any{
"get_project_files": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The id of the project containing the files",
"name": "project_id",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "get_project_file",
map[string]any{
"get_project_file": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The id of the project containing the files",
"name": "project_id",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The path of the file within the project",
"name": "file_path",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "create_project_file",
map[string]any{
"create_project_file": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The id of the project containing the files",
"name": "project_id",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The path of the file within the project",
"name": "file_path",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The content of the file",
"name": "file_content",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "update_project_file",
map[string]any{
"update_project_file": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The id of the project containing the files",
"name": "project_id",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The path of the file within the project",
"name": "file_path",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The content of the file",
"name": "file_content",
"required": true,
"type": "string",
},
},
},
},
)
tests.RunToolGetTestByName(t, "delete_project_file",
map[string]any{
"delete_project_file": map[string]any{
"description": "Simple tool to test end to end functionality.",
"authRequired": []any{},
"parameters": []any{
map[string]any{
"authSources": []any{},
"description": "The id of the project containing the files",
"name": "project_id",
"required": true,
"type": "string",
},
map[string]any{
"authSources": []any{},
"description": "The path of the file within the project",
"name": "file_path",
"required": true,
"type": "string",
},
},
},
},
)
wantResult := "{\"label\":\"System Activity\",\"name\":\"system__activity\",\"project_name\":\"system__activity\"}"
tests.RunToolInvokeSimpleTest(t, "get_models", wantResult)
wantResult = "{\"description\":\"Data about Look and dashboard usage, including frequency of views, favoriting, scheduling, embedding, and access via the API. Also includes details about individual Looks and dashboards.\",\"group_label\":\"System Activity\",\"label\":\"Content Usage\",\"name\":\"content_usage\"}"
tests.RunToolInvokeParametersTest(t, "get_explores", []byte(`{"model": "system__activity"}`), wantResult)
wantResult = "{\"description\":\"Number of times this content has been viewed via the Looker API\",\"label\":\"Content Usage API Count\",\"label_short\":\"API Count\",\"name\":\"content_usage.api_count\",\"type\":\"number\"}"
tests.RunToolInvokeParametersTest(t, "get_dimensions", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)
wantResult = "{\"description\":\"The total number of views via the Looker API\",\"label\":\"Content Usage API Total\",\"label_short\":\"API Total\",\"name\":\"content_usage.api_total\",\"type\":\"sum\"}"
tests.RunToolInvokeParametersTest(t, "get_measures", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)
wantResult = "[]"
tests.RunToolInvokeParametersTest(t, "get_filters", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)
wantResult = "[]"
tests.RunToolInvokeParametersTest(t, "get_parameters", []byte(`{"model": "system__activity", "explore": "content_usage"}`), wantResult)
wantResult = "{\"look.count\":"
tests.RunToolInvokeParametersTest(t, "query", []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"]}`), wantResult)
wantResult = "SELECT"
tests.RunToolInvokeParametersTest(t, "query_sql", []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"]}`), wantResult)
wantResult = "system__activity"
tests.RunToolInvokeParametersTest(t, "query_url", []byte(`{"model": "system__activity", "explore": "look", "fields": ["look.count"]}`), wantResult)
// A system that is just being used for testing has no looks or dashboards
wantResult = "null"
tests.RunToolInvokeParametersTest(t, "get_looks", []byte(`{"title": "FOO", "desc": "BAR"}`), wantResult)
wantResult = "null"
tests.RunToolInvokeParametersTest(t, "get_dashboards", []byte(`{"title": "FOO", "desc": "BAR"}`), wantResult)
runConversationalAnalytics(t, "system__activity", "content_usage")
wantResult = "\"Connection\":\"thelook\""
tests.RunToolInvokeParametersTest(t, "health_pulse", []byte(`{"action": "check_db_connections"}`), wantResult)
wantResult = "[]"
tests.RunToolInvokeParametersTest(t, "health_pulse", []byte(`{"action": "check_schedule_failures"}`), wantResult)
wantResult = "[{\"Feature\":\"Unsupported in Looker (Google Cloud core)\"}]"
tests.RunToolInvokeParametersTest(t, "health_pulse", []byte(`{"action": "check_legacy_features"}`), wantResult)
wantResult = "\"Project\":\"the_look\""
tests.RunToolInvokeParametersTest(t, "health_analyze", []byte(`{"action": "projects"}`), wantResult)
wantResult = "\"Model\":\"the_look\""
tests.RunToolInvokeParametersTest(t, "health_analyze", []byte(`{"action": "explores", "project": "the_look", "model": "the_look", "explore": "inventory_items"}`), wantResult)
wantResult = "\"Model\":\"the_look\""
tests.RunToolInvokeParametersTest(t, "health_vacuum", []byte(`{"action": "models"}`), wantResult)
wantResult = "the_look"
tests.RunToolInvokeSimpleTest(t, "get_projects", wantResult)
wantResult = "order_items.view"
tests.RunToolInvokeParametersTest(t, "get_project_files", []byte(`{"project_id": "the_look"}`), wantResult)
wantResult = "view"
tests.RunToolInvokeParametersTest(t, "get_project_file", []byte(`{"project_id": "the_look", "file_path": "order_items.view.lkml"}`), wantResult)
wantResult = "dev"
tests.RunToolInvokeParametersTest(t, "dev_mode", []byte(`{"devMode": true}`), wantResult)
wantResult = "created"
tests.RunToolInvokeParametersTest(t, "create_project_file", []byte(`{"project_id": "the_look", "file_path": "foo.view.lkml", "file_content": "view"}`), wantResult)
wantResult = "updated"
tests.RunToolInvokeParametersTest(t, "update_project_file", []byte(`{"project_id": "the_look", "file_path": "foo.view.lkml", "file_content": "model"}`), wantResult)
wantResult = "deleted"
tests.RunToolInvokeParametersTest(t, "delete_project_file", []byte(`{"project_id": "the_look", "file_path": "foo.view.lkml"}`), wantResult)
wantResult = "production"
tests.RunToolInvokeParametersTest(t, "dev_mode", []byte(`{"devMode": false}`), wantResult)
}
func runConversationalAnalytics(t *testing.T, modelName, exploreName string) {
exploreRefsJSON := fmt.Sprintf(`[{"model":"%s","explore":"%s"}]`, modelName, exploreName)
var refs []map[string]any
if err := json.Unmarshal([]byte(exploreRefsJSON), &refs); err != nil {
t.Fatalf("failed to unmarshal explore refs: %v", err)
}
testCases := []struct {
name string
exploreRefs []map[string]any
wantStatusCode int
wantInResult string
wantInError string
}{
{
name: "invoke conversational analytics with explore",
exploreRefs: refs,
wantStatusCode: http.StatusOK,
wantInResult: `Answer`,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
requestBodyMap := map[string]any{
"user_query_with_context": "What is in the explore?",
"explore_references": tc.exploreRefs,
}
bodyBytes, err := json.Marshal(requestBodyMap)
if err != nil {
t.Fatalf("failed to marshal request body: %v", err)
}
url := "http://127.0.0.1:5000/api/tool/conversational_analytics/invoke"
resp, bodyBytes := tests.RunRequest(t, http.MethodPost, url, bytes.NewBuffer(bodyBytes), nil)
if resp.StatusCode != tc.wantStatusCode {
t.Fatalf("unexpected status code: got %d, want %d. Body: %s", resp.StatusCode, tc.wantStatusCode, string(bodyBytes))
}
if tc.wantInResult != "" {
var respBody map[string]interface{}
if err := json.Unmarshal(bodyBytes, &respBody); err != nil {
t.Fatalf("error parsing response body: %v", err)
}
got, ok := respBody["result"].(string)
if !ok {
t.Fatalf("unable to find result in response body")
}
if !strings.Contains(got, tc.wantInResult) {
t.Errorf("unexpected result: got %q, want to contain %q", got, tc.wantInResult)
}
}
if tc.wantInError != "" {
if !strings.Contains(string(bodyBytes), tc.wantInError) {
t.Errorf("unexpected error message: got %q, want to contain %q", string(bodyBytes), tc.wantInError)
}
}
})
}
}
```