#
tokens: 37554/50000 1/935 files (page 53/59)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 53 of 59. 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
│       │       │   ├── adkgo
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── 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-healthcare.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
│       │   │   ├── elasticsearch.md
│       │   │   ├── firebird.md
│       │   │   ├── firestore.md
│       │   │   ├── http.md
│       │   │   ├── looker.md
│       │   │   ├── mindsdb.md
│       │   │   ├── mongodb.md
│       │   │   ├── mssql.md
│       │   │   ├── mysql.md
│       │   │   ├── neo4j.md
│       │   │   ├── oceanbase.md
│       │   │   ├── oracle.md
│       │   │   ├── postgres.md
│       │   │   ├── redis.md
│       │   │   ├── serverless-spark.md
│       │   │   ├── singlestore.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
│       │       ├── cloudhealthcare
│       │       │   ├── _index.md
│       │       │   ├── cloud-healthcare-fhir-fetch-page.md
│       │       │   ├── cloud-healthcare-fhir-patient-everything.md
│       │       │   ├── cloud-healthcare-fhir-patient-search.md
│       │       │   ├── cloud-healthcare-get-dataset.md
│       │       │   ├── cloud-healthcare-get-dicom-store-metrics.md
│       │       │   ├── cloud-healthcare-get-dicom-store.md
│       │       │   ├── cloud-healthcare-get-fhir-resource.md
│       │       │   ├── cloud-healthcare-get-fhir-store-metrics.md
│       │       │   ├── cloud-healthcare-get-fhir-store.md
│       │       │   ├── cloud-healthcare-list-dicom-stores.md
│       │       │   ├── cloud-healthcare-list-fhir-stores.md
│       │       │   ├── cloud-healthcare-retrieve-rendered-dicom-instance.md
│       │       │   ├── cloud-healthcare-search-dicom-instances.md
│       │       │   ├── cloud-healthcare-search-dicom-series.md
│       │       │   └── cloud-healthcare-search-dicom-studies.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
│       │       ├── elasticsearch
│       │       │   ├── _index.md
│       │       │   └── elasticsearch-esql.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-connection-databases.md
│       │       │   ├── looker-get-connection-schemas.md
│       │       │   ├── looker-get-connection-table-columns.md
│       │       │   ├── looker-get-connection-tables.md
│       │       │   ├── looker-get-connections.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-dashboard.md
│       │       │   ├── looker-run-look.md
│       │       │   └── looker-update-project-file.md
│       │       ├── mindsdb
│       │       │   ├── _index.md
│       │       │   ├── mindsdb-execute-sql.md
│       │       │   └── mindsdb-sql.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-schemas.md
│       │       │   ├── postgres-list-tables.md
│       │       │   ├── postgres-list-views.md
│       │       │   └── postgres-sql.md
│       │       ├── redis
│       │       │   ├── _index.md
│       │       │   └── redis.md
│       │       ├── serverless-spark
│       │       │   ├── _index.md
│       │       │   ├── serverless-spark-cancel-batch.md
│       │       │   ├── serverless-spark-get-batch.md
│       │       │   └── serverless-spark-list-batches.md
│       │       ├── singlestore
│       │       │   ├── _index.md
│       │       │   ├── singlestore-execute-sql.md
│       │       │   └── singlestore-sql.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-healthcare.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
│   │       ├── elasticsearch.yaml
│   │       ├── firestore.yaml
│   │       ├── looker-conversational-analytics.yaml
│   │       ├── looker.yaml
│   │       ├── mindsdb.yaml
│   │       ├── mssql.yaml
│   │       ├── mysql.yaml
│   │       ├── neo4j.yaml
│   │       ├── oceanbase.yaml
│   │       ├── postgres.yaml
│   │       ├── serverless-spark.yaml
│   │       ├── singlestore.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
│   │   │   └── cache.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   ├── cassandra_test.go
│   │   │   └── cassandra.go
│   │   ├── clickhouse
│   │   │   ├── clickhouse_test.go
│   │   │   └── clickhouse.go
│   │   ├── cloudhealthcare
│   │   │   ├── cloud_healthcare_test.go
│   │   │   └── cloud_healthcare.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
│   │   ├── elasticsearch
│   │   │   ├── elasticsearch_test.go
│   │   │   └── elasticsearch.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
│   │   ├── mindsdb
│   │   │   ├── mindsdb_test.go
│   │   │   └── mindsdb.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
│   │   ├── singlestore
│   │   │   ├── singlestore_test.go
│   │   │   └── singlestore.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
│   │   ├── cloudhealthcare
│   │   │   ├── cloudhealthcarefhirfetchpage
│   │   │   │   ├── cloudhealthcarefhirfetchpage_test.go
│   │   │   │   └── cloudhealthcarefhirfetchpage.go
│   │   │   ├── cloudhealthcarefhirpatienteverything
│   │   │   │   ├── cloudhealthcarefhirpatienteverything_test.go
│   │   │   │   └── cloudhealthcarefhirpatienteverything.go
│   │   │   ├── cloudhealthcarefhirpatientsearch
│   │   │   │   ├── cloudhealthcarefhirpatientsearch_test.go
│   │   │   │   └── cloudhealthcarefhirpatientsearch.go
│   │   │   ├── cloudhealthcaregetdataset
│   │   │   │   ├── cloudhealthcaregetdataset_test.go
│   │   │   │   └── cloudhealthcaregetdataset.go
│   │   │   ├── cloudhealthcaregetdicomstore
│   │   │   │   ├── cloudhealthcaregetdicomstore_test.go
│   │   │   │   └── cloudhealthcaregetdicomstore.go
│   │   │   ├── cloudhealthcaregetdicomstoremetrics
│   │   │   │   ├── cloudhealthcaregetdicomstoremetrics_test.go
│   │   │   │   └── cloudhealthcaregetdicomstoremetrics.go
│   │   │   ├── cloudhealthcaregetfhirresource
│   │   │   │   ├── cloudhealthcaregetfhirresource_test.go
│   │   │   │   └── cloudhealthcaregetfhirresource.go
│   │   │   ├── cloudhealthcaregetfhirstore
│   │   │   │   ├── cloudhealthcaregetfhirstore_test.go
│   │   │   │   └── cloudhealthcaregetfhirstore.go
│   │   │   ├── cloudhealthcaregetfhirstoremetrics
│   │   │   │   ├── cloudhealthcaregetfhirstoremetrics_test.go
│   │   │   │   └── cloudhealthcaregetfhirstoremetrics.go
│   │   │   ├── cloudhealthcarelistdicomstores
│   │   │   │   ├── cloudhealthcarelistdicomstores_test.go
│   │   │   │   └── cloudhealthcarelistdicomstores.go
│   │   │   ├── cloudhealthcarelistfhirstores
│   │   │   │   ├── cloudhealthcarelistfhirstores_test.go
│   │   │   │   └── cloudhealthcarelistfhirstores.go
│   │   │   ├── cloudhealthcareretrieverendereddicominstance
│   │   │   │   ├── cloudhealthcareretrieverendereddicominstance_test.go
│   │   │   │   └── cloudhealthcareretrieverendereddicominstance.go
│   │   │   ├── cloudhealthcaresearchdicominstances
│   │   │   │   ├── cloudhealthcaresearchdicominstances_test.go
│   │   │   │   └── cloudhealthcaresearchdicominstances.go
│   │   │   ├── cloudhealthcaresearchdicomseries
│   │   │   │   ├── cloudhealthcaresearchdicomseries_test.go
│   │   │   │   └── cloudhealthcaresearchdicomseries.go
│   │   │   ├── cloudhealthcaresearchdicomstudies
│   │   │   │   ├── cloudhealthcaresearchdicomstudies_test.go
│   │   │   │   └── cloudhealthcaresearchdicomstudies.go
│   │   │   └── common
│   │   │       └── util.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
│   │   ├── elasticsearch
│   │   │   └── elasticsearchesql
│   │   │       ├── elasticsearchesql_test.go
│   │   │       └── elasticsearchesql.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
│   │   │   ├── lookergetconnectiondatabases
│   │   │   │   ├── lookergetconnectiondatabases_test.go
│   │   │   │   └── lookergetconnectiondatabases.go
│   │   │   ├── lookergetconnections
│   │   │   │   ├── lookergetconnections_test.go
│   │   │   │   └── lookergetconnections.go
│   │   │   ├── lookergetconnectionschemas
│   │   │   │   ├── lookergetconnectionschemas_test.go
│   │   │   │   └── lookergetconnectionschemas.go
│   │   │   ├── lookergetconnectiontablecolumns
│   │   │   │   ├── lookergetconnectiontablecolumns_test.go
│   │   │   │   └── lookergetconnectiontablecolumns.go
│   │   │   ├── lookergetconnectiontables
│   │   │   │   ├── lookergetconnectiontables_test.go
│   │   │   │   └── lookergetconnectiontables.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
│   │   │   ├── lookerrundashboard
│   │   │   │   ├── lookerrundashboard_test.go
│   │   │   │   └── lookerrundashboard.go
│   │   │   ├── lookerrunlook
│   │   │   │   ├── lookerrunlook_test.go
│   │   │   │   └── lookerrunlook.go
│   │   │   └── lookerupdateprojectfile
│   │   │       ├── lookerupdateprojectfile_test.go
│   │   │       └── lookerupdateprojectfile.go
│   │   ├── mindsdb
│   │   │   ├── mindsdbexecutesql
│   │   │   │   ├── mindsdbexecutesql_test.go
│   │   │   │   └── mindsdbexecutesql.go
│   │   │   └── mindsdbsql
│   │   │       ├── mindsdbsql_test.go
│   │   │       └── mindsdbsql.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
│   │   │   ├── postgreslistschemas
│   │   │   │   ├── postgreslistschemas_test.go
│   │   │   │   └── postgreslistschemas.go
│   │   │   ├── postgreslisttables
│   │   │   │   ├── postgreslisttables_test.go
│   │   │   │   └── postgreslisttables.go
│   │   │   ├── postgreslistviews
│   │   │   │   ├── postgreslistviews_test.go
│   │   │   │   └── postgreslistviews.go
│   │   │   └── postgressql
│   │   │       ├── postgressql_test.go
│   │   │       └── postgressql.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── serverlessspark
│   │   │   ├── serverlesssparkcancelbatch
│   │   │   │   ├── serverlesssparkcancelbatch_test.go
│   │   │   │   └── serverlesssparkcancelbatch.go
│   │   │   ├── serverlesssparkgetbatch
│   │   │   │   ├── serverlesssparkgetbatch_test.go
│   │   │   │   └── serverlesssparkgetbatch.go
│   │   │   └── serverlesssparklistbatches
│   │   │       ├── serverlesssparklistbatches_test.go
│   │   │       └── serverlesssparklistbatches.go
│   │   ├── singlestore
│   │   │   ├── singlestoreexecutesql
│   │   │   │   ├── singlestoreexecutesql_test.go
│   │   │   │   └── singlestoreexecutesql.go
│   │   │   └── singlestoresql
│   │   │       ├── singlestoresql_test.go
│   │   │       └── singlestoresql.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
│       ├── orderedmap
│       │   ├── orderedmap_test.go
│       │   └── orderedmap.go
│       └── 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
    ├── cloudhealthcare
    │   └── cloud_healthcare_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
    ├── elasticsearch
    │   └── elasticsearch_integration_test.go
    ├── firebird
    │   └── firebird_integration_test.go
    ├── firestore
    │   └── firestore_integration_test.go
    ├── http
    │   └── http_integration_test.go
    ├── looker
    │   └── looker_integration_test.go
    ├── mindsdb
    │   └── mindsdb_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
    ├── singlestore
    │   └── singlestore_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

--------------------------------------------------------------------------------
/cmd/root_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 cmd
  16 | 
  17 | import (
  18 | 	"bytes"
  19 | 	"context"
  20 | 	_ "embed"
  21 | 	"fmt"
  22 | 	"io"
  23 | 	"os"
  24 | 	"path"
  25 | 	"path/filepath"
  26 | 	"regexp"
  27 | 	"runtime"
  28 | 	"strings"
  29 | 	"testing"
  30 | 	"time"
  31 | 
  32 | 	"github.com/google/go-cmp/cmp"
  33 | 
  34 | 	"github.com/googleapis/genai-toolbox/internal/auth/google"
  35 | 	"github.com/googleapis/genai-toolbox/internal/log"
  36 | 	"github.com/googleapis/genai-toolbox/internal/prebuiltconfigs"
  37 | 	"github.com/googleapis/genai-toolbox/internal/server"
  38 | 	cloudsqlpgsrc "github.com/googleapis/genai-toolbox/internal/sources/cloudsqlpg"
  39 | 	httpsrc "github.com/googleapis/genai-toolbox/internal/sources/http"
  40 | 	"github.com/googleapis/genai-toolbox/internal/telemetry"
  41 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
  42 | 	"github.com/googleapis/genai-toolbox/internal/tools"
  43 | 	"github.com/googleapis/genai-toolbox/internal/tools/http"
  44 | 	"github.com/googleapis/genai-toolbox/internal/tools/postgres/postgressql"
  45 | 	"github.com/googleapis/genai-toolbox/internal/util"
  46 | 	"github.com/spf13/cobra"
  47 | )
  48 | 
  49 | func withDefaults(c server.ServerConfig) server.ServerConfig {
  50 | 	data, _ := os.ReadFile("version.txt")
  51 | 	version := strings.TrimSpace(string(data)) // Preserving 'data', new var for clarity
  52 | 	c.Version = version + "+" + strings.Join([]string{"dev", runtime.GOOS, runtime.GOARCH}, ".")
  53 | 
  54 | 	if c.Address == "" {
  55 | 		c.Address = "127.0.0.1"
  56 | 	}
  57 | 	if c.Port == 0 {
  58 | 		c.Port = 5000
  59 | 	}
  60 | 	if c.TelemetryServiceName == "" {
  61 | 		c.TelemetryServiceName = "toolbox"
  62 | 	}
  63 | 	return c
  64 | }
  65 | 
  66 | func invokeCommand(args []string) (*Command, string, error) {
  67 | 	c := NewCommand()
  68 | 
  69 | 	// Keep the test output quiet
  70 | 	c.SilenceUsage = true
  71 | 	c.SilenceErrors = true
  72 | 
  73 | 	// Capture output
  74 | 	buf := new(bytes.Buffer)
  75 | 	c.SetOut(buf)
  76 | 	c.SetErr(buf)
  77 | 	c.SetArgs(args)
  78 | 
  79 | 	// Disable execute behavior
  80 | 	c.RunE = func(*cobra.Command, []string) error {
  81 | 		return nil
  82 | 	}
  83 | 
  84 | 	err := c.Execute()
  85 | 
  86 | 	return c, buf.String(), err
  87 | }
  88 | 
  89 | func TestVersion(t *testing.T) {
  90 | 	data, err := os.ReadFile("version.txt")
  91 | 	if err != nil {
  92 | 		t.Fatalf("failed to read version.txt: %v", err)
  93 | 	}
  94 | 	want := strings.TrimSpace(string(data))
  95 | 
  96 | 	_, got, err := invokeCommand([]string{"--version"})
  97 | 	if err != nil {
  98 | 		t.Fatalf("error invoking command: %s", err)
  99 | 	}
 100 | 
 101 | 	if !strings.Contains(got, want) {
 102 | 		t.Errorf("cli did not return correct version: want %q, got %q", want, got)
 103 | 	}
 104 | }
 105 | 
 106 | func TestServerConfigFlags(t *testing.T) {
 107 | 	tcs := []struct {
 108 | 		desc string
 109 | 		args []string
 110 | 		want server.ServerConfig
 111 | 	}{
 112 | 		{
 113 | 			desc: "default values",
 114 | 			args: []string{},
 115 | 			want: withDefaults(server.ServerConfig{}),
 116 | 		},
 117 | 		{
 118 | 			desc: "address short",
 119 | 			args: []string{"-a", "127.0.1.1"},
 120 | 			want: withDefaults(server.ServerConfig{
 121 | 				Address: "127.0.1.1",
 122 | 			}),
 123 | 		},
 124 | 		{
 125 | 			desc: "address long",
 126 | 			args: []string{"--address", "0.0.0.0"},
 127 | 			want: withDefaults(server.ServerConfig{
 128 | 				Address: "0.0.0.0",
 129 | 			}),
 130 | 		},
 131 | 		{
 132 | 			desc: "port short",
 133 | 			args: []string{"-p", "5052"},
 134 | 			want: withDefaults(server.ServerConfig{
 135 | 				Port: 5052,
 136 | 			}),
 137 | 		},
 138 | 		{
 139 | 			desc: "port long",
 140 | 			args: []string{"--port", "5050"},
 141 | 			want: withDefaults(server.ServerConfig{
 142 | 				Port: 5050,
 143 | 			}),
 144 | 		},
 145 | 		{
 146 | 			desc: "logging format",
 147 | 			args: []string{"--logging-format", "JSON"},
 148 | 			want: withDefaults(server.ServerConfig{
 149 | 				LoggingFormat: "JSON",
 150 | 			}),
 151 | 		},
 152 | 		{
 153 | 			desc: "debug logs",
 154 | 			args: []string{"--log-level", "WARN"},
 155 | 			want: withDefaults(server.ServerConfig{
 156 | 				LogLevel: "WARN",
 157 | 			}),
 158 | 		},
 159 | 		{
 160 | 			desc: "telemetry gcp",
 161 | 			args: []string{"--telemetry-gcp"},
 162 | 			want: withDefaults(server.ServerConfig{
 163 | 				TelemetryGCP: true,
 164 | 			}),
 165 | 		},
 166 | 		{
 167 | 			desc: "telemetry otlp",
 168 | 			args: []string{"--telemetry-otlp", "http://127.0.0.1:4553"},
 169 | 			want: withDefaults(server.ServerConfig{
 170 | 				TelemetryOTLP: "http://127.0.0.1:4553",
 171 | 			}),
 172 | 		},
 173 | 		{
 174 | 			desc: "telemetry service name",
 175 | 			args: []string{"--telemetry-service-name", "toolbox-custom"},
 176 | 			want: withDefaults(server.ServerConfig{
 177 | 				TelemetryServiceName: "toolbox-custom",
 178 | 			}),
 179 | 		},
 180 | 		{
 181 | 			desc: "stdio",
 182 | 			args: []string{"--stdio"},
 183 | 			want: withDefaults(server.ServerConfig{
 184 | 				Stdio: true,
 185 | 			}),
 186 | 		},
 187 | 		{
 188 | 			desc: "disable reload",
 189 | 			args: []string{"--disable-reload"},
 190 | 			want: withDefaults(server.ServerConfig{
 191 | 				DisableReload: true,
 192 | 			}),
 193 | 		},
 194 | 	}
 195 | 	for _, tc := range tcs {
 196 | 		t.Run(tc.desc, func(t *testing.T) {
 197 | 			c, _, err := invokeCommand(tc.args)
 198 | 			if err != nil {
 199 | 				t.Fatalf("unexpected error invoking command: %s", err)
 200 | 			}
 201 | 
 202 | 			if !cmp.Equal(c.cfg, tc.want) {
 203 | 				t.Fatalf("got %v, want %v", c.cfg, tc.want)
 204 | 			}
 205 | 		})
 206 | 	}
 207 | }
 208 | 
 209 | func TestParseEnv(t *testing.T) {
 210 | 	tcs := []struct {
 211 | 		desc      string
 212 | 		env       map[string]string
 213 | 		in        string
 214 | 		want      string
 215 | 		err       bool
 216 | 		errString string
 217 | 	}{
 218 | 		{
 219 | 			desc:      "without default without env",
 220 | 			in:        "${FOO}",
 221 | 			want:      "",
 222 | 			err:       true,
 223 | 			errString: `environment variable not found: "FOO"`,
 224 | 		},
 225 | 		{
 226 | 			desc: "without default with env",
 227 | 			env: map[string]string{
 228 | 				"FOO": "bar",
 229 | 			},
 230 | 			in:   "${FOO}",
 231 | 			want: "bar",
 232 | 		},
 233 | 		{
 234 | 			desc: "with empty default",
 235 | 			in:   "${FOO:}",
 236 | 			want: "",
 237 | 		},
 238 | 		{
 239 | 			desc: "with default",
 240 | 			in:   "${FOO:bar}",
 241 | 			want: "bar",
 242 | 		},
 243 | 		{
 244 | 			desc: "with default with env",
 245 | 			env: map[string]string{
 246 | 				"FOO": "hello",
 247 | 			},
 248 | 			in:   "${FOO:bar}",
 249 | 			want: "hello",
 250 | 		},
 251 | 	}
 252 | 	for _, tc := range tcs {
 253 | 		t.Run(tc.desc, func(t *testing.T) {
 254 | 			if tc.env != nil {
 255 | 				for k, v := range tc.env {
 256 | 					t.Setenv(k, v)
 257 | 				}
 258 | 			}
 259 | 			got, err := parseEnv(tc.in)
 260 | 			if tc.err {
 261 | 				if err == nil {
 262 | 					t.Fatalf("expected error not found")
 263 | 				}
 264 | 				if tc.errString != err.Error() {
 265 | 					t.Fatalf("incorrect error string: got %s, want %s", err, tc.errString)
 266 | 				}
 267 | 			}
 268 | 			if tc.want != got {
 269 | 				t.Fatalf("unexpected want: got %s, want %s", got, tc.want)
 270 | 			}
 271 | 		})
 272 | 	}
 273 | }
 274 | 
 275 | func TestToolFileFlag(t *testing.T) {
 276 | 	tcs := []struct {
 277 | 		desc string
 278 | 		args []string
 279 | 		want string
 280 | 	}{
 281 | 		{
 282 | 			desc: "default value",
 283 | 			args: []string{},
 284 | 			want: "",
 285 | 		},
 286 | 		{
 287 | 			desc: "foo file",
 288 | 			args: []string{"--tools-file", "foo.yaml"},
 289 | 			want: "foo.yaml",
 290 | 		},
 291 | 		{
 292 | 			desc: "address long",
 293 | 			args: []string{"--tools-file", "bar.yaml"},
 294 | 			want: "bar.yaml",
 295 | 		},
 296 | 		{
 297 | 			desc: "deprecated flag",
 298 | 			args: []string{"--tools_file", "foo.yaml"},
 299 | 			want: "foo.yaml",
 300 | 		},
 301 | 	}
 302 | 	for _, tc := range tcs {
 303 | 		t.Run(tc.desc, func(t *testing.T) {
 304 | 			c, _, err := invokeCommand(tc.args)
 305 | 			if err != nil {
 306 | 				t.Fatalf("unexpected error invoking command: %s", err)
 307 | 			}
 308 | 			if c.tools_file != tc.want {
 309 | 				t.Fatalf("got %v, want %v", c.cfg, tc.want)
 310 | 			}
 311 | 		})
 312 | 	}
 313 | }
 314 | 
 315 | func TestToolsFilesFlag(t *testing.T) {
 316 | 	tcs := []struct {
 317 | 		desc string
 318 | 		args []string
 319 | 		want []string
 320 | 	}{
 321 | 		{
 322 | 			desc: "no value",
 323 | 			args: []string{},
 324 | 			want: []string{},
 325 | 		},
 326 | 		{
 327 | 			desc: "single file",
 328 | 			args: []string{"--tools-files", "foo.yaml"},
 329 | 			want: []string{"foo.yaml"},
 330 | 		},
 331 | 		{
 332 | 			desc: "multiple files",
 333 | 			args: []string{"--tools-files", "foo.yaml,bar.yaml"},
 334 | 			want: []string{"foo.yaml", "bar.yaml"},
 335 | 		},
 336 | 	}
 337 | 	for _, tc := range tcs {
 338 | 		t.Run(tc.desc, func(t *testing.T) {
 339 | 			c, _, err := invokeCommand(tc.args)
 340 | 			if err != nil {
 341 | 				t.Fatalf("unexpected error invoking command: %s", err)
 342 | 			}
 343 | 			if diff := cmp.Diff(c.tools_files, tc.want); diff != "" {
 344 | 				t.Fatalf("got %v, want %v", c.tools_files, tc.want)
 345 | 			}
 346 | 		})
 347 | 	}
 348 | }
 349 | 
 350 | func TestToolsFolderFlag(t *testing.T) {
 351 | 	tcs := []struct {
 352 | 		desc string
 353 | 		args []string
 354 | 		want string
 355 | 	}{
 356 | 		{
 357 | 			desc: "no value",
 358 | 			args: []string{},
 359 | 			want: "",
 360 | 		},
 361 | 		{
 362 | 			desc: "folder set",
 363 | 			args: []string{"--tools-folder", "test-folder"},
 364 | 			want: "test-folder",
 365 | 		},
 366 | 	}
 367 | 	for _, tc := range tcs {
 368 | 		t.Run(tc.desc, func(t *testing.T) {
 369 | 			c, _, err := invokeCommand(tc.args)
 370 | 			if err != nil {
 371 | 				t.Fatalf("unexpected error invoking command: %s", err)
 372 | 			}
 373 | 			if c.tools_folder != tc.want {
 374 | 				t.Fatalf("got %v, want %v", c.tools_folder, tc.want)
 375 | 			}
 376 | 		})
 377 | 	}
 378 | }
 379 | 
 380 | func TestPrebuiltFlag(t *testing.T) {
 381 | 	tcs := []struct {
 382 | 		desc string
 383 | 		args []string
 384 | 		want string
 385 | 	}{
 386 | 		{
 387 | 			desc: "default value",
 388 | 			args: []string{},
 389 | 			want: "",
 390 | 		},
 391 | 		{
 392 | 			desc: "custom pre built flag",
 393 | 			args: []string{"--tools-file", "alloydb"},
 394 | 			want: "alloydb",
 395 | 		},
 396 | 	}
 397 | 	for _, tc := range tcs {
 398 | 		t.Run(tc.desc, func(t *testing.T) {
 399 | 			c, _, err := invokeCommand(tc.args)
 400 | 			if err != nil {
 401 | 				t.Fatalf("unexpected error invoking command: %s", err)
 402 | 			}
 403 | 			if c.tools_file != tc.want {
 404 | 				t.Fatalf("got %v, want %v", c.cfg, tc.want)
 405 | 			}
 406 | 		})
 407 | 	}
 408 | }
 409 | 
 410 | func TestFailServerConfigFlags(t *testing.T) {
 411 | 	tcs := []struct {
 412 | 		desc string
 413 | 		args []string
 414 | 	}{
 415 | 		{
 416 | 			desc: "logging format",
 417 | 			args: []string{"--logging-format", "fail"},
 418 | 		},
 419 | 		{
 420 | 			desc: "debug logs",
 421 | 			args: []string{"--log-level", "fail"},
 422 | 		},
 423 | 	}
 424 | 	for _, tc := range tcs {
 425 | 		t.Run(tc.desc, func(t *testing.T) {
 426 | 			_, _, err := invokeCommand(tc.args)
 427 | 			if err == nil {
 428 | 				t.Fatalf("expected an error, but got nil")
 429 | 			}
 430 | 		})
 431 | 	}
 432 | }
 433 | 
 434 | func TestDefaultLoggingFormat(t *testing.T) {
 435 | 	c, _, err := invokeCommand([]string{})
 436 | 	if err != nil {
 437 | 		t.Fatalf("unexpected error invoking command: %s", err)
 438 | 	}
 439 | 	got := c.cfg.LoggingFormat.String()
 440 | 	want := "standard"
 441 | 	if got != want {
 442 | 		t.Fatalf("unexpected default logging format flag: got %v, want %v", got, want)
 443 | 	}
 444 | }
 445 | 
 446 | func TestDefaultLogLevel(t *testing.T) {
 447 | 	c, _, err := invokeCommand([]string{})
 448 | 	if err != nil {
 449 | 		t.Fatalf("unexpected error invoking command: %s", err)
 450 | 	}
 451 | 	got := c.cfg.LogLevel.String()
 452 | 	want := "info"
 453 | 	if got != want {
 454 | 		t.Fatalf("unexpected default log level flag: got %v, want %v", got, want)
 455 | 	}
 456 | }
 457 | 
 458 | func TestParseToolFile(t *testing.T) {
 459 | 	ctx, err := testutils.ContextWithNewLogger()
 460 | 	if err != nil {
 461 | 		t.Fatalf("unexpected error: %s", err)
 462 | 	}
 463 | 	tcs := []struct {
 464 | 		description   string
 465 | 		in            string
 466 | 		wantToolsFile ToolsFile
 467 | 	}{
 468 | 		{
 469 | 			description: "basic example",
 470 | 			in: `
 471 | 			sources:
 472 | 				my-pg-instance:
 473 | 					kind: cloud-sql-postgres
 474 | 					project: my-project
 475 | 					region: my-region
 476 | 					instance: my-instance
 477 | 					database: my_db
 478 | 					user: my_user
 479 | 					password: my_pass
 480 | 			tools:
 481 | 				example_tool:
 482 | 					kind: postgres-sql
 483 | 					source: my-pg-instance
 484 | 					description: some description
 485 | 					statement: |
 486 | 						SELECT * FROM SQL_STATEMENT;
 487 | 					parameters:
 488 | 						- name: country
 489 | 							type: string
 490 | 							description: some description
 491 | 			toolsets:
 492 | 				example_toolset:
 493 | 					- example_tool
 494 | 			`,
 495 | 			wantToolsFile: ToolsFile{
 496 | 				Sources: server.SourceConfigs{
 497 | 					"my-pg-instance": cloudsqlpgsrc.Config{
 498 | 						Name:     "my-pg-instance",
 499 | 						Kind:     cloudsqlpgsrc.SourceKind,
 500 | 						Project:  "my-project",
 501 | 						Region:   "my-region",
 502 | 						Instance: "my-instance",
 503 | 						IPType:   "public",
 504 | 						Database: "my_db",
 505 | 						User:     "my_user",
 506 | 						Password: "my_pass",
 507 | 					},
 508 | 				},
 509 | 				Tools: server.ToolConfigs{
 510 | 					"example_tool": postgressql.Config{
 511 | 						Name:        "example_tool",
 512 | 						Kind:        "postgres-sql",
 513 | 						Source:      "my-pg-instance",
 514 | 						Description: "some description",
 515 | 						Statement:   "SELECT * FROM SQL_STATEMENT;\n",
 516 | 						Parameters: []tools.Parameter{
 517 | 							tools.NewStringParameter("country", "some description"),
 518 | 						},
 519 | 						AuthRequired: []string{},
 520 | 					},
 521 | 				},
 522 | 				Toolsets: server.ToolsetConfigs{
 523 | 					"example_toolset": tools.ToolsetConfig{
 524 | 						Name:      "example_toolset",
 525 | 						ToolNames: []string{"example_tool"},
 526 | 					},
 527 | 				},
 528 | 			},
 529 | 		},
 530 | 	}
 531 | 	for _, tc := range tcs {
 532 | 		t.Run(tc.description, func(t *testing.T) {
 533 | 			toolsFile, err := parseToolsFile(ctx, testutils.FormatYaml(tc.in))
 534 | 			if err != nil {
 535 | 				t.Fatalf("failed to parse input: %v", err)
 536 | 			}
 537 | 			if diff := cmp.Diff(tc.wantToolsFile.Sources, toolsFile.Sources); diff != "" {
 538 | 				t.Fatalf("incorrect sources parse: diff %v", diff)
 539 | 			}
 540 | 			if diff := cmp.Diff(tc.wantToolsFile.AuthServices, toolsFile.AuthServices); diff != "" {
 541 | 				t.Fatalf("incorrect authServices parse: diff %v", diff)
 542 | 			}
 543 | 			if diff := cmp.Diff(tc.wantToolsFile.Tools, toolsFile.Tools); diff != "" {
 544 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
 545 | 			}
 546 | 			if diff := cmp.Diff(tc.wantToolsFile.Toolsets, toolsFile.Toolsets); diff != "" {
 547 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
 548 | 			}
 549 | 		})
 550 | 	}
 551 | 
 552 | }
 553 | 
 554 | func TestParseToolFileWithAuth(t *testing.T) {
 555 | 	ctx, err := testutils.ContextWithNewLogger()
 556 | 	if err != nil {
 557 | 		t.Fatalf("unexpected error: %s", err)
 558 | 	}
 559 | 	tcs := []struct {
 560 | 		description   string
 561 | 		in            string
 562 | 		wantToolsFile ToolsFile
 563 | 	}{
 564 | 		{
 565 | 			description: "basic example",
 566 | 			in: `
 567 | 			sources:
 568 | 				my-pg-instance:
 569 | 					kind: cloud-sql-postgres
 570 | 					project: my-project
 571 | 					region: my-region
 572 | 					instance: my-instance
 573 | 					database: my_db
 574 | 					user: my_user
 575 | 					password: my_pass
 576 | 			authServices:
 577 | 				my-google-service:
 578 | 					kind: google
 579 | 					clientId: my-client-id
 580 | 				other-google-service:
 581 | 					kind: google
 582 | 					clientId: other-client-id
 583 | 
 584 | 			tools:
 585 | 				example_tool:
 586 | 					kind: postgres-sql
 587 | 					source: my-pg-instance
 588 | 					description: some description
 589 | 					statement: |
 590 | 						SELECT * FROM SQL_STATEMENT;
 591 | 					parameters:
 592 | 						- name: country
 593 | 						  type: string
 594 | 						  description: some description
 595 | 						- name: id
 596 | 						  type: integer
 597 | 						  description: user id
 598 | 						  authServices:
 599 | 							- name: my-google-service
 600 | 								field: user_id
 601 | 						- name: email
 602 | 							type: string
 603 | 							description: user email
 604 | 							authServices:
 605 | 							- name: my-google-service
 606 | 							  field: email
 607 | 							- name: other-google-service
 608 | 							  field: other_email
 609 | 
 610 | 			toolsets:
 611 | 				example_toolset:
 612 | 					- example_tool
 613 | 			`,
 614 | 			wantToolsFile: ToolsFile{
 615 | 				Sources: server.SourceConfigs{
 616 | 					"my-pg-instance": cloudsqlpgsrc.Config{
 617 | 						Name:     "my-pg-instance",
 618 | 						Kind:     cloudsqlpgsrc.SourceKind,
 619 | 						Project:  "my-project",
 620 | 						Region:   "my-region",
 621 | 						Instance: "my-instance",
 622 | 						IPType:   "public",
 623 | 						Database: "my_db",
 624 | 						User:     "my_user",
 625 | 						Password: "my_pass",
 626 | 					},
 627 | 				},
 628 | 				AuthServices: server.AuthServiceConfigs{
 629 | 					"my-google-service": google.Config{
 630 | 						Name:     "my-google-service",
 631 | 						Kind:     google.AuthServiceKind,
 632 | 						ClientID: "my-client-id",
 633 | 					},
 634 | 					"other-google-service": google.Config{
 635 | 						Name:     "other-google-service",
 636 | 						Kind:     google.AuthServiceKind,
 637 | 						ClientID: "other-client-id",
 638 | 					},
 639 | 				},
 640 | 				Tools: server.ToolConfigs{
 641 | 					"example_tool": postgressql.Config{
 642 | 						Name:         "example_tool",
 643 | 						Kind:         "postgres-sql",
 644 | 						Source:       "my-pg-instance",
 645 | 						Description:  "some description",
 646 | 						Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 647 | 						AuthRequired: []string{},
 648 | 						Parameters: []tools.Parameter{
 649 | 							tools.NewStringParameter("country", "some description"),
 650 | 							tools.NewIntParameterWithAuth("id", "user id", []tools.ParamAuthService{{Name: "my-google-service", Field: "user_id"}}),
 651 | 							tools.NewStringParameterWithAuth("email", "user email", []tools.ParamAuthService{{Name: "my-google-service", Field: "email"}, {Name: "other-google-service", Field: "other_email"}}),
 652 | 						},
 653 | 					},
 654 | 				},
 655 | 				Toolsets: server.ToolsetConfigs{
 656 | 					"example_toolset": tools.ToolsetConfig{
 657 | 						Name:      "example_toolset",
 658 | 						ToolNames: []string{"example_tool"},
 659 | 					},
 660 | 				},
 661 | 			},
 662 | 		},
 663 | 		{
 664 | 			description: "basic example with authSources",
 665 | 			in: `
 666 | 			sources:
 667 | 				my-pg-instance:
 668 | 					kind: cloud-sql-postgres
 669 | 					project: my-project
 670 | 					region: my-region
 671 | 					instance: my-instance
 672 | 					database: my_db
 673 | 					user: my_user
 674 | 					password: my_pass
 675 | 			authSources:
 676 | 				my-google-service:
 677 | 					kind: google
 678 | 					clientId: my-client-id
 679 | 				other-google-service:
 680 | 					kind: google
 681 | 					clientId: other-client-id
 682 | 
 683 | 			tools:
 684 | 				example_tool:
 685 | 					kind: postgres-sql
 686 | 					source: my-pg-instance
 687 | 					description: some description
 688 | 					statement: |
 689 | 						SELECT * FROM SQL_STATEMENT;
 690 | 					parameters:
 691 | 						- name: country
 692 | 						  type: string
 693 | 						  description: some description
 694 | 						- name: id
 695 | 						  type: integer
 696 | 						  description: user id
 697 | 						  authSources:
 698 | 							- name: my-google-service
 699 | 								field: user_id
 700 | 						- name: email
 701 | 							type: string
 702 | 							description: user email
 703 | 							authSources:
 704 | 							- name: my-google-service
 705 | 							  field: email
 706 | 							- name: other-google-service
 707 | 							  field: other_email
 708 | 
 709 | 			toolsets:
 710 | 				example_toolset:
 711 | 					- example_tool
 712 | 			`,
 713 | 			wantToolsFile: ToolsFile{
 714 | 				Sources: server.SourceConfigs{
 715 | 					"my-pg-instance": cloudsqlpgsrc.Config{
 716 | 						Name:     "my-pg-instance",
 717 | 						Kind:     cloudsqlpgsrc.SourceKind,
 718 | 						Project:  "my-project",
 719 | 						Region:   "my-region",
 720 | 						Instance: "my-instance",
 721 | 						IPType:   "public",
 722 | 						Database: "my_db",
 723 | 						User:     "my_user",
 724 | 						Password: "my_pass",
 725 | 					},
 726 | 				},
 727 | 				AuthSources: server.AuthServiceConfigs{
 728 | 					"my-google-service": google.Config{
 729 | 						Name:     "my-google-service",
 730 | 						Kind:     google.AuthServiceKind,
 731 | 						ClientID: "my-client-id",
 732 | 					},
 733 | 					"other-google-service": google.Config{
 734 | 						Name:     "other-google-service",
 735 | 						Kind:     google.AuthServiceKind,
 736 | 						ClientID: "other-client-id",
 737 | 					},
 738 | 				},
 739 | 				Tools: server.ToolConfigs{
 740 | 					"example_tool": postgressql.Config{
 741 | 						Name:         "example_tool",
 742 | 						Kind:         "postgres-sql",
 743 | 						Source:       "my-pg-instance",
 744 | 						Description:  "some description",
 745 | 						Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 746 | 						AuthRequired: []string{},
 747 | 						Parameters: []tools.Parameter{
 748 | 							tools.NewStringParameter("country", "some description"),
 749 | 							tools.NewIntParameterWithAuth("id", "user id", []tools.ParamAuthService{{Name: "my-google-service", Field: "user_id"}}),
 750 | 							tools.NewStringParameterWithAuth("email", "user email", []tools.ParamAuthService{{Name: "my-google-service", Field: "email"}, {Name: "other-google-service", Field: "other_email"}}),
 751 | 						},
 752 | 					},
 753 | 				},
 754 | 				Toolsets: server.ToolsetConfigs{
 755 | 					"example_toolset": tools.ToolsetConfig{
 756 | 						Name:      "example_toolset",
 757 | 						ToolNames: []string{"example_tool"},
 758 | 					},
 759 | 				},
 760 | 			},
 761 | 		},
 762 | 		{
 763 | 			description: "basic example with authRequired",
 764 | 			in: `
 765 | 			sources:
 766 | 				my-pg-instance:
 767 | 					kind: cloud-sql-postgres
 768 | 					project: my-project
 769 | 					region: my-region
 770 | 					instance: my-instance
 771 | 					database: my_db
 772 | 					user: my_user
 773 | 					password: my_pass
 774 | 			authServices:
 775 | 				my-google-service:
 776 | 					kind: google
 777 | 					clientId: my-client-id
 778 | 				other-google-service:
 779 | 					kind: google
 780 | 					clientId: other-client-id
 781 | 
 782 | 			tools:
 783 | 				example_tool:
 784 | 					kind: postgres-sql
 785 | 					source: my-pg-instance
 786 | 					description: some description
 787 | 					statement: |
 788 | 						SELECT * FROM SQL_STATEMENT;
 789 | 					authRequired:
 790 | 						- my-google-service
 791 | 					parameters:
 792 | 						- name: country
 793 | 						  type: string
 794 | 						  description: some description
 795 | 						- name: id
 796 | 						  type: integer
 797 | 						  description: user id
 798 | 						  authServices:
 799 | 							- name: my-google-service
 800 | 								field: user_id
 801 | 						- name: email
 802 | 							type: string
 803 | 							description: user email
 804 | 							authServices:
 805 | 							- name: my-google-service
 806 | 							  field: email
 807 | 							- name: other-google-service
 808 | 							  field: other_email
 809 | 
 810 | 			toolsets:
 811 | 				example_toolset:
 812 | 					- example_tool
 813 | 			`,
 814 | 			wantToolsFile: ToolsFile{
 815 | 				Sources: server.SourceConfigs{
 816 | 					"my-pg-instance": cloudsqlpgsrc.Config{
 817 | 						Name:     "my-pg-instance",
 818 | 						Kind:     cloudsqlpgsrc.SourceKind,
 819 | 						Project:  "my-project",
 820 | 						Region:   "my-region",
 821 | 						Instance: "my-instance",
 822 | 						IPType:   "public",
 823 | 						Database: "my_db",
 824 | 						User:     "my_user",
 825 | 						Password: "my_pass",
 826 | 					},
 827 | 				},
 828 | 				AuthServices: server.AuthServiceConfigs{
 829 | 					"my-google-service": google.Config{
 830 | 						Name:     "my-google-service",
 831 | 						Kind:     google.AuthServiceKind,
 832 | 						ClientID: "my-client-id",
 833 | 					},
 834 | 					"other-google-service": google.Config{
 835 | 						Name:     "other-google-service",
 836 | 						Kind:     google.AuthServiceKind,
 837 | 						ClientID: "other-client-id",
 838 | 					},
 839 | 				},
 840 | 				Tools: server.ToolConfigs{
 841 | 					"example_tool": postgressql.Config{
 842 | 						Name:         "example_tool",
 843 | 						Kind:         "postgres-sql",
 844 | 						Source:       "my-pg-instance",
 845 | 						Description:  "some description",
 846 | 						Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 847 | 						AuthRequired: []string{"my-google-service"},
 848 | 						Parameters: []tools.Parameter{
 849 | 							tools.NewStringParameter("country", "some description"),
 850 | 							tools.NewIntParameterWithAuth("id", "user id", []tools.ParamAuthService{{Name: "my-google-service", Field: "user_id"}}),
 851 | 							tools.NewStringParameterWithAuth("email", "user email", []tools.ParamAuthService{{Name: "my-google-service", Field: "email"}, {Name: "other-google-service", Field: "other_email"}}),
 852 | 						},
 853 | 					},
 854 | 				},
 855 | 				Toolsets: server.ToolsetConfigs{
 856 | 					"example_toolset": tools.ToolsetConfig{
 857 | 						Name:      "example_toolset",
 858 | 						ToolNames: []string{"example_tool"},
 859 | 					},
 860 | 				},
 861 | 			},
 862 | 		},
 863 | 	}
 864 | 	for _, tc := range tcs {
 865 | 		t.Run(tc.description, func(t *testing.T) {
 866 | 			toolsFile, err := parseToolsFile(ctx, testutils.FormatYaml(tc.in))
 867 | 			if err != nil {
 868 | 				t.Fatalf("failed to parse input: %v", err)
 869 | 			}
 870 | 			if diff := cmp.Diff(tc.wantToolsFile.Sources, toolsFile.Sources); diff != "" {
 871 | 				t.Fatalf("incorrect sources parse: diff %v", diff)
 872 | 			}
 873 | 			if diff := cmp.Diff(tc.wantToolsFile.AuthServices, toolsFile.AuthServices); diff != "" {
 874 | 				t.Fatalf("incorrect authServices parse: diff %v", diff)
 875 | 			}
 876 | 			if diff := cmp.Diff(tc.wantToolsFile.Tools, toolsFile.Tools); diff != "" {
 877 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
 878 | 			}
 879 | 			if diff := cmp.Diff(tc.wantToolsFile.Toolsets, toolsFile.Toolsets); diff != "" {
 880 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
 881 | 			}
 882 | 		})
 883 | 	}
 884 | 
 885 | }
 886 | 
 887 | func TestEnvVarReplacement(t *testing.T) {
 888 | 	ctx, err := testutils.ContextWithNewLogger()
 889 | 	t.Setenv("TestHeader", "ACTUAL_HEADER")
 890 | 	t.Setenv("API_KEY", "ACTUAL_API_KEY")
 891 | 	t.Setenv("clientId", "ACTUAL_CLIENT_ID")
 892 | 	t.Setenv("clientId2", "ACTUAL_CLIENT_ID_2")
 893 | 	t.Setenv("toolset_name", "ACTUAL_TOOLSET_NAME")
 894 | 	t.Setenv("cat_string", "cat")
 895 | 	t.Setenv("food_string", "food")
 896 | 	t.Setenv("TestHeader", "ACTUAL_HEADER")
 897 | 
 898 | 	if err != nil {
 899 | 		t.Fatalf("unexpected error: %s", err)
 900 | 	}
 901 | 	tcs := []struct {
 902 | 		description   string
 903 | 		in            string
 904 | 		wantToolsFile ToolsFile
 905 | 	}{
 906 | 		{
 907 | 			description: "file with env var example",
 908 | 			in: `
 909 | 			sources:
 910 | 				my-http-instance:
 911 | 					kind: http
 912 | 					baseUrl: http://test_server/
 913 | 					timeout: 10s
 914 | 					headers:
 915 | 						Authorization: ${TestHeader}
 916 | 					queryParams:
 917 | 						api-key: ${API_KEY}
 918 | 			authServices:
 919 | 				my-google-service:
 920 | 					kind: google
 921 | 					clientId: ${clientId}
 922 | 				other-google-service:
 923 | 					kind: google
 924 | 					clientId: ${clientId2}
 925 | 
 926 | 			tools:
 927 | 				example_tool:
 928 | 					kind: http
 929 | 					source: my-instance
 930 | 					method: GET
 931 | 					path: "search?name=alice&pet=${cat_string}"
 932 | 					description: some description
 933 | 					authRequired:
 934 | 						- my-google-auth-service
 935 | 						- other-auth-service
 936 | 					queryParams:
 937 | 						- name: country
 938 | 						  type: string
 939 | 						  description: some description
 940 | 						  authServices:
 941 | 							- name: my-google-auth-service
 942 | 							  field: user_id
 943 | 							- name: other-auth-service
 944 | 							  field: user_id
 945 | 					requestBody: |
 946 | 							{
 947 | 								"age": {{.age}},
 948 | 								"city": "{{.city}}",
 949 | 								"food": "${food_string}",
 950 | 								"other": "$OTHER"
 951 | 							}
 952 | 					bodyParams:
 953 | 						- name: age
 954 | 						  type: integer
 955 | 						  description: age num
 956 | 						- name: city
 957 | 						  type: string
 958 | 						  description: city string
 959 | 					headers:
 960 | 						Authorization: API_KEY
 961 | 						Content-Type: application/json
 962 | 					headerParams:
 963 | 						- name: Language
 964 | 						  type: string
 965 | 						  description: language string
 966 | 
 967 | 			toolsets:
 968 | 				${toolset_name}:
 969 | 					- example_tool
 970 | 			`,
 971 | 			wantToolsFile: ToolsFile{
 972 | 				Sources: server.SourceConfigs{
 973 | 					"my-http-instance": httpsrc.Config{
 974 | 						Name:           "my-http-instance",
 975 | 						Kind:           httpsrc.SourceKind,
 976 | 						BaseURL:        "http://test_server/",
 977 | 						Timeout:        "10s",
 978 | 						DefaultHeaders: map[string]string{"Authorization": "ACTUAL_HEADER"},
 979 | 						QueryParams:    map[string]string{"api-key": "ACTUAL_API_KEY"},
 980 | 					},
 981 | 				},
 982 | 				AuthServices: server.AuthServiceConfigs{
 983 | 					"my-google-service": google.Config{
 984 | 						Name:     "my-google-service",
 985 | 						Kind:     google.AuthServiceKind,
 986 | 						ClientID: "ACTUAL_CLIENT_ID",
 987 | 					},
 988 | 					"other-google-service": google.Config{
 989 | 						Name:     "other-google-service",
 990 | 						Kind:     google.AuthServiceKind,
 991 | 						ClientID: "ACTUAL_CLIENT_ID_2",
 992 | 					},
 993 | 				},
 994 | 				Tools: server.ToolConfigs{
 995 | 					"example_tool": http.Config{
 996 | 						Name:         "example_tool",
 997 | 						Kind:         "http",
 998 | 						Source:       "my-instance",
 999 | 						Method:       "GET",
1000 | 						Path:         "search?name=alice&pet=cat",
1001 | 						Description:  "some description",
1002 | 						AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
1003 | 						QueryParams: []tools.Parameter{
1004 | 							tools.NewStringParameterWithAuth("country", "some description",
1005 | 								[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
1006 | 									{Name: "other-auth-service", Field: "user_id"}}),
1007 | 						},
1008 | 						RequestBody: `{
1009 |   "age": {{.age}},
1010 |   "city": "{{.city}}",
1011 |   "food": "food",
1012 |   "other": "$OTHER"
1013 | }
1014 | `,
1015 | 						BodyParams:   []tools.Parameter{tools.NewIntParameter("age", "age num"), tools.NewStringParameter("city", "city string")},
1016 | 						Headers:      map[string]string{"Authorization": "API_KEY", "Content-Type": "application/json"},
1017 | 						HeaderParams: []tools.Parameter{tools.NewStringParameter("Language", "language string")},
1018 | 					},
1019 | 				},
1020 | 				Toolsets: server.ToolsetConfigs{
1021 | 					"ACTUAL_TOOLSET_NAME": tools.ToolsetConfig{
1022 | 						Name:      "ACTUAL_TOOLSET_NAME",
1023 | 						ToolNames: []string{"example_tool"},
1024 | 					},
1025 | 				},
1026 | 			},
1027 | 		},
1028 | 	}
1029 | 	for _, tc := range tcs {
1030 | 		t.Run(tc.description, func(t *testing.T) {
1031 | 			toolsFile, err := parseToolsFile(ctx, testutils.FormatYaml(tc.in))
1032 | 			if err != nil {
1033 | 				t.Fatalf("failed to parse input: %v", err)
1034 | 			}
1035 | 			if diff := cmp.Diff(tc.wantToolsFile.Sources, toolsFile.Sources); diff != "" {
1036 | 				t.Fatalf("incorrect sources parse: diff %v", diff)
1037 | 			}
1038 | 			if diff := cmp.Diff(tc.wantToolsFile.AuthServices, toolsFile.AuthServices); diff != "" {
1039 | 				t.Fatalf("incorrect authServices parse: diff %v", diff)
1040 | 			}
1041 | 			if diff := cmp.Diff(tc.wantToolsFile.Tools, toolsFile.Tools); diff != "" {
1042 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
1043 | 			}
1044 | 			if diff := cmp.Diff(tc.wantToolsFile.Toolsets, toolsFile.Toolsets); diff != "" {
1045 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
1046 | 			}
1047 | 		})
1048 | 	}
1049 | 
1050 | }
1051 | 
1052 | // normalizeFilepaths is a helper function to allow same filepath formats for Mac and Windows.
1053 | // this prevents needing multiple "want" cases for TestResolveWatcherInputs
1054 | func normalizeFilepaths(m map[string]bool) map[string]bool {
1055 | 	newMap := make(map[string]bool)
1056 | 	for k, v := range m {
1057 | 		newMap[filepath.ToSlash(k)] = v
1058 | 	}
1059 | 	return newMap
1060 | }
1061 | 
1062 | func TestResolveWatcherInputs(t *testing.T) {
1063 | 	tcs := []struct {
1064 | 		description      string
1065 | 		toolsFile        string
1066 | 		toolsFiles       []string
1067 | 		toolsFolder      string
1068 | 		wantWatchDirs    map[string]bool
1069 | 		wantWatchedFiles map[string]bool
1070 | 	}{
1071 | 		{
1072 | 			description:      "single tools file",
1073 | 			toolsFile:        "tools_folder/example_tools.yaml",
1074 | 			toolsFiles:       []string{},
1075 | 			toolsFolder:      "",
1076 | 			wantWatchDirs:    map[string]bool{"tools_folder": true},
1077 | 			wantWatchedFiles: map[string]bool{"tools_folder/example_tools.yaml": true},
1078 | 		},
1079 | 		{
1080 | 			description:      "default tools file (root dir)",
1081 | 			toolsFile:        "tools.yaml",
1082 | 			toolsFiles:       []string{},
1083 | 			toolsFolder:      "",
1084 | 			wantWatchDirs:    map[string]bool{".": true},
1085 | 			wantWatchedFiles: map[string]bool{"tools.yaml": true},
1086 | 		},
1087 | 		{
1088 | 			description:   "multiple files in different folders",
1089 | 			toolsFile:     "",
1090 | 			toolsFiles:    []string{"tools_folder/example_tools.yaml", "tools_folder2/example_tools.yaml"},
1091 | 			toolsFolder:   "",
1092 | 			wantWatchDirs: map[string]bool{"tools_folder": true, "tools_folder2": true},
1093 | 			wantWatchedFiles: map[string]bool{
1094 | 				"tools_folder/example_tools.yaml":  true,
1095 | 				"tools_folder2/example_tools.yaml": true,
1096 | 			},
1097 | 		},
1098 | 		{
1099 | 			description:   "multiple files in same folder",
1100 | 			toolsFile:     "",
1101 | 			toolsFiles:    []string{"tools_folder/example_tools.yaml", "tools_folder/example_tools2.yaml"},
1102 | 			toolsFolder:   "",
1103 | 			wantWatchDirs: map[string]bool{"tools_folder": true},
1104 | 			wantWatchedFiles: map[string]bool{
1105 | 				"tools_folder/example_tools.yaml":  true,
1106 | 				"tools_folder/example_tools2.yaml": true,
1107 | 			},
1108 | 		},
1109 | 		{
1110 | 			description: "multiple files in different levels",
1111 | 			toolsFile:   "",
1112 | 			toolsFiles: []string{
1113 | 				"tools_folder/example_tools.yaml",
1114 | 				"tools_folder/special_tools/example_tools2.yaml"},
1115 | 			toolsFolder:   "",
1116 | 			wantWatchDirs: map[string]bool{"tools_folder": true, "tools_folder/special_tools": true},
1117 | 			wantWatchedFiles: map[string]bool{
1118 | 				"tools_folder/example_tools.yaml":                true,
1119 | 				"tools_folder/special_tools/example_tools2.yaml": true,
1120 | 			},
1121 | 		},
1122 | 		{
1123 | 			description:      "tools folder",
1124 | 			toolsFile:        "",
1125 | 			toolsFiles:       []string{},
1126 | 			toolsFolder:      "tools_folder",
1127 | 			wantWatchDirs:    map[string]bool{"tools_folder": true},
1128 | 			wantWatchedFiles: map[string]bool{},
1129 | 		},
1130 | 	}
1131 | 	for _, tc := range tcs {
1132 | 		t.Run(tc.description, func(t *testing.T) {
1133 | 			gotWatchDirs, gotWatchedFiles := resolveWatcherInputs(tc.toolsFile, tc.toolsFiles, tc.toolsFolder)
1134 | 
1135 | 			normalizedGotWatchDirs := normalizeFilepaths(gotWatchDirs)
1136 | 			normalizedGotWatchedFiles := normalizeFilepaths(gotWatchedFiles)
1137 | 
1138 | 			if diff := cmp.Diff(tc.wantWatchDirs, normalizedGotWatchDirs); diff != "" {
1139 | 				t.Errorf("incorrect watchDirs: diff %v", diff)
1140 | 			}
1141 | 			if diff := cmp.Diff(tc.wantWatchedFiles, normalizedGotWatchedFiles); diff != "" {
1142 | 				t.Errorf("incorrect watchedFiles: diff %v", diff)
1143 | 			}
1144 | 
1145 | 		})
1146 | 	}
1147 | }
1148 | 
1149 | // helper function for testing file detection in dynamic reloading
1150 | func tmpFileWithCleanup(content []byte) (string, func(), error) {
1151 | 	f, err := os.CreateTemp("", "*")
1152 | 	if err != nil {
1153 | 		return "", nil, err
1154 | 	}
1155 | 	cleanup := func() { os.Remove(f.Name()) }
1156 | 
1157 | 	if _, err := f.Write(content); err != nil {
1158 | 		cleanup()
1159 | 		return "", nil, err
1160 | 	}
1161 | 	if err := f.Close(); err != nil {
1162 | 		cleanup()
1163 | 		return "", nil, err
1164 | 	}
1165 | 	return f.Name(), cleanup, err
1166 | }
1167 | 
1168 | func TestSingleEdit(t *testing.T) {
1169 | 	ctx, cancelCtx := context.WithTimeout(context.Background(), time.Minute)
1170 | 	defer cancelCtx()
1171 | 
1172 | 	pr, pw := io.Pipe()
1173 | 	defer pw.Close()
1174 | 	defer pr.Close()
1175 | 
1176 | 	fileToWatch, cleanup, err := tmpFileWithCleanup([]byte("initial content"))
1177 | 	if err != nil {
1178 | 		t.Fatalf("error editing tools file %s", err)
1179 | 	}
1180 | 	defer cleanup()
1181 | 
1182 | 	logger, err := log.NewStdLogger(pw, pw, "DEBUG")
1183 | 	if err != nil {
1184 | 		t.Fatalf("failed to setup logger %s", err)
1185 | 	}
1186 | 	ctx = util.WithLogger(ctx, logger)
1187 | 
1188 | 	instrumentation, err := telemetry.CreateTelemetryInstrumentation(versionString)
1189 | 	if err != nil {
1190 | 		t.Fatalf("failed to setup instrumentation %s", err)
1191 | 	}
1192 | 	ctx = util.WithInstrumentation(ctx, instrumentation)
1193 | 
1194 | 	mockServer := &server.Server{}
1195 | 
1196 | 	cleanFileToWatch := filepath.Clean(fileToWatch)
1197 | 	watchDir := filepath.Dir(cleanFileToWatch)
1198 | 
1199 | 	watchedFiles := map[string]bool{cleanFileToWatch: true}
1200 | 	watchDirs := map[string]bool{watchDir: true}
1201 | 
1202 | 	go watchChanges(ctx, watchDirs, watchedFiles, mockServer)
1203 | 
1204 | 	// escape backslash so regex doesn't fail on windows filepaths
1205 | 	regexEscapedPathFile := strings.ReplaceAll(cleanFileToWatch, `\`, `\\\\*\\`)
1206 | 	regexEscapedPathFile = path.Clean(regexEscapedPathFile)
1207 | 
1208 | 	regexEscapedPathDir := strings.ReplaceAll(watchDir, `\`, `\\\\*\\`)
1209 | 	regexEscapedPathDir = path.Clean(regexEscapedPathDir)
1210 | 
1211 | 	begunWatchingDir := regexp.MustCompile(fmt.Sprintf(`DEBUG "Added directory %s to watcher."`, regexEscapedPathDir))
1212 | 	_, err = testutils.WaitForString(ctx, begunWatchingDir, pr)
1213 | 	if err != nil {
1214 | 		t.Fatalf("timeout or error waiting for watcher to start: %s", err)
1215 | 	}
1216 | 
1217 | 	err = os.WriteFile(fileToWatch, []byte("modification"), 0777)
1218 | 	if err != nil {
1219 | 		t.Fatalf("error writing to file: %v", err)
1220 | 	}
1221 | 
1222 | 	// only check substring of DEBUG message due to some OS/editors firing different operations
1223 | 	detectedFileChange := regexp.MustCompile(fmt.Sprintf(`event detected in %s"`, regexEscapedPathFile))
1224 | 	_, err = testutils.WaitForString(ctx, detectedFileChange, pr)
1225 | 	if err != nil {
1226 | 		t.Fatalf("timeout or error waiting for file to detect write: %s", err)
1227 | 	}
1228 | }
1229 | 
1230 | func TestPrebuiltTools(t *testing.T) {
1231 | 	// Get prebuilt configs
1232 | 	alloydb_admin_config, _ := prebuiltconfigs.Get("alloydb-postgres-admin")
1233 | 	alloydb_config, _ := prebuiltconfigs.Get("alloydb-postgres")
1234 | 	bigquery_config, _ := prebuiltconfigs.Get("bigquery")
1235 | 	clickhouse_config, _ := prebuiltconfigs.Get("clickhouse")
1236 | 	cloudsqlpg_config, _ := prebuiltconfigs.Get("cloud-sql-postgres")
1237 | 	cloudsqlpg_admin_config, _ := prebuiltconfigs.Get("cloud-sql-postgres-admin")
1238 | 	cloudsqlmysql_config, _ := prebuiltconfigs.Get("cloud-sql-mysql")
1239 | 	cloudsqlmysql_admin_config, _ := prebuiltconfigs.Get("cloud-sql-mysql-admin")
1240 | 	cloudsqlmssql_config, _ := prebuiltconfigs.Get("cloud-sql-mssql")
1241 | 	cloudsqlmssql_admin_config, _ := prebuiltconfigs.Get("cloud-sql-mssql-admin")
1242 | 	dataplex_config, _ := prebuiltconfigs.Get("dataplex")
1243 | 	firestoreconfig, _ := prebuiltconfigs.Get("firestore")
1244 | 	mysql_config, _ := prebuiltconfigs.Get("mysql")
1245 | 	mssql_config, _ := prebuiltconfigs.Get("mssql")
1246 | 	looker_config, _ := prebuiltconfigs.Get("looker")
1247 | 	lookerca_config, _ := prebuiltconfigs.Get("looker-conversational-analytics")
1248 | 	postgresconfig, _ := prebuiltconfigs.Get("postgres")
1249 | 	spanner_config, _ := prebuiltconfigs.Get("spanner")
1250 | 	spannerpg_config, _ := prebuiltconfigs.Get("spanner-postgres")
1251 | 	mindsdb_config, _ := prebuiltconfigs.Get("mindsdb")
1252 | 	sqlite_config, _ := prebuiltconfigs.Get("sqlite")
1253 | 	neo4jconfig, _ := prebuiltconfigs.Get("neo4j")
1254 | 	alloydbobsvconfig, _ := prebuiltconfigs.Get("alloydb-postgres-observability")
1255 | 	cloudsqlpgobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-postgres-observability")
1256 | 	cloudsqlmysqlobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-mysql-observability")
1257 | 	cloudsqlmssqlobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-mssql-observability")
1258 | 	serverless_spark_config, _ := prebuiltconfigs.Get("serverless-spark")
1259 | 	cloudhealthcare_config, _ := prebuiltconfigs.Get("cloud-healthcare")
1260 | 
1261 | 	// Set environment variables
1262 | 	t.Setenv("API_KEY", "your_api_key")
1263 | 
1264 | 	t.Setenv("BIGQUERY_PROJECT", "your_gcp_project_id")
1265 | 	t.Setenv("DATAPLEX_PROJECT", "your_gcp_project_id")
1266 | 	t.Setenv("FIRESTORE_PROJECT", "your_gcp_project_id")
1267 | 	t.Setenv("FIRESTORE_DATABASE", "your_firestore_db_name")
1268 | 
1269 | 	t.Setenv("SPANNER_PROJECT", "your_gcp_project_id")
1270 | 	t.Setenv("SPANNER_INSTANCE", "your_spanner_instance")
1271 | 	t.Setenv("SPANNER_DATABASE", "your_spanner_db")
1272 | 
1273 | 	t.Setenv("ALLOYDB_POSTGRES_PROJECT", "your_gcp_project_id")
1274 | 	t.Setenv("ALLOYDB_POSTGRES_REGION", "your_gcp_region")
1275 | 	t.Setenv("ALLOYDB_POSTGRES_CLUSTER", "your_alloydb_cluster")
1276 | 	t.Setenv("ALLOYDB_POSTGRES_INSTANCE", "your_alloydb_instance")
1277 | 	t.Setenv("ALLOYDB_POSTGRES_DATABASE", "your_alloydb_db")
1278 | 	t.Setenv("ALLOYDB_POSTGRES_USER", "your_alloydb_user")
1279 | 	t.Setenv("ALLOYDB_POSTGRES_PASSWORD", "your_alloydb_password")
1280 | 
1281 | 	t.Setenv("CLICKHOUSE_PROTOCOL", "your_clickhouse_protocol")
1282 | 	t.Setenv("CLICKHOUSE_DATABASE", "your_clickhouse_database")
1283 | 	t.Setenv("CLICKHOUSE_PASSWORD", "your_clickhouse_password")
1284 | 	t.Setenv("CLICKHOUSE_USER", "your_clickhouse_user")
1285 | 	t.Setenv("CLICKHOUSE_HOST", "your_clickhosue_host")
1286 | 	t.Setenv("CLICKHOUSE_PORT", "8123")
1287 | 
1288 | 	t.Setenv("CLOUD_SQL_POSTGRES_PROJECT", "your_pg_project")
1289 | 	t.Setenv("CLOUD_SQL_POSTGRES_INSTANCE", "your_pg_instance")
1290 | 	t.Setenv("CLOUD_SQL_POSTGRES_DATABASE", "your_pg_db")
1291 | 	t.Setenv("CLOUD_SQL_POSTGRES_REGION", "your_pg_region")
1292 | 	t.Setenv("CLOUD_SQL_POSTGRES_USER", "your_pg_user")
1293 | 	t.Setenv("CLOUD_SQL_POSTGRES_PASS", "your_pg_pass")
1294 | 
1295 | 	t.Setenv("CLOUD_SQL_MYSQL_PROJECT", "your_gcp_project_id")
1296 | 	t.Setenv("CLOUD_SQL_MYSQL_REGION", "your_gcp_region")
1297 | 	t.Setenv("CLOUD_SQL_MYSQL_INSTANCE", "your_instance")
1298 | 	t.Setenv("CLOUD_SQL_MYSQL_DATABASE", "your_cloudsql_mysql_db")
1299 | 	t.Setenv("CLOUD_SQL_MYSQL_USER", "your_cloudsql_mysql_user")
1300 | 	t.Setenv("CLOUD_SQL_MYSQL_PASSWORD", "your_cloudsql_mysql_password")
1301 | 
1302 | 	t.Setenv("CLOUD_SQL_MSSQL_PROJECT", "your_gcp_project_id")
1303 | 	t.Setenv("CLOUD_SQL_MSSQL_REGION", "your_gcp_region")
1304 | 	t.Setenv("CLOUD_SQL_MSSQL_INSTANCE", "your_cloudsql_mssql_instance")
1305 | 	t.Setenv("CLOUD_SQL_MSSQL_DATABASE", "your_cloudsql_mssql_db")
1306 | 	t.Setenv("CLOUD_SQL_MSSQL_IP_ADDRESS", "127.0.0.1")
1307 | 	t.Setenv("CLOUD_SQL_MSSQL_USER", "your_cloudsql_mssql_user")
1308 | 	t.Setenv("CLOUD_SQL_MSSQL_PASSWORD", "your_cloudsql_mssql_password")
1309 | 	t.Setenv("CLOUD_SQL_POSTGRES_PASSWORD", "your_cloudsql_pg_password")
1310 | 
1311 | 	t.Setenv("SERVERLESS_SPARK_PROJECT", "your_gcp_project_id")
1312 | 	t.Setenv("SERVERLESS_SPARK_LOCATION", "your_gcp_location")
1313 | 
1314 | 	t.Setenv("POSTGRES_HOST", "localhost")
1315 | 	t.Setenv("POSTGRES_PORT", "5432")
1316 | 	t.Setenv("POSTGRES_DATABASE", "your_postgres_db")
1317 | 	t.Setenv("POSTGRES_USER", "your_postgres_user")
1318 | 	t.Setenv("POSTGRES_PASSWORD", "your_postgres_password")
1319 | 
1320 | 	t.Setenv("MYSQL_HOST", "localhost")
1321 | 	t.Setenv("MYSQL_PORT", "3306")
1322 | 	t.Setenv("MYSQL_DATABASE", "your_mysql_db")
1323 | 	t.Setenv("MYSQL_USER", "your_mysql_user")
1324 | 	t.Setenv("MYSQL_PASSWORD", "your_mysql_password")
1325 | 
1326 | 	t.Setenv("MSSQL_HOST", "localhost")
1327 | 	t.Setenv("MSSQL_PORT", "1433")
1328 | 	t.Setenv("MSSQL_DATABASE", "your_mssql_db")
1329 | 	t.Setenv("MSSQL_USER", "your_mssql_user")
1330 | 	t.Setenv("MSSQL_PASSWORD", "your_mssql_password")
1331 | 
1332 | 	t.Setenv("MINDSDB_HOST", "localhost")
1333 | 	t.Setenv("MINDSDB_PORT", "47334")
1334 | 	t.Setenv("MINDSDB_DATABASE", "your_mindsdb_db")
1335 | 	t.Setenv("MINDSDB_USER", "your_mindsdb_user")
1336 | 	t.Setenv("MINDSDB_PASS", "your_mindsdb_password")
1337 | 
1338 | 	t.Setenv("LOOKER_BASE_URL", "https://your_company.looker.com")
1339 | 	t.Setenv("LOOKER_CLIENT_ID", "your_looker_client_id")
1340 | 	t.Setenv("LOOKER_CLIENT_SECRET", "your_looker_client_secret")
1341 | 	t.Setenv("LOOKER_VERIFY_SSL", "true")
1342 | 
1343 | 	t.Setenv("LOOKER_PROJECT", "your_project_id")
1344 | 	t.Setenv("LOOKER_LOCATION", "us")
1345 | 
1346 | 	t.Setenv("SQLITE_DATABASE", "test.db")
1347 | 
1348 | 	t.Setenv("NEO4J_URI", "bolt://localhost:7687")
1349 | 	t.Setenv("NEO4J_DATABASE", "neo4j")
1350 | 	t.Setenv("NEO4J_USERNAME", "your_neo4j_user")
1351 | 	t.Setenv("NEO4J_PASSWORD", "your_neo4j_password")
1352 | 
1353 | 	t.Setenv("CLOUD_HEALTHCARE_PROJECT", "your_gcp_project_id")
1354 | 	t.Setenv("CLOUD_HEALTHCARE_REGION", "your_gcp_region")
1355 | 	t.Setenv("CLOUD_HEALTHCARE_DATASET", "your_healthcare_dataset")
1356 | 
1357 | 	ctx, err := testutils.ContextWithNewLogger()
1358 | 	if err != nil {
1359 | 		t.Fatalf("unexpected error: %s", err)
1360 | 	}
1361 | 	tcs := []struct {
1362 | 		name        string
1363 | 		in          []byte
1364 | 		wantToolset server.ToolsetConfigs
1365 | 	}{
1366 | 		{
1367 | 			name: "alloydb postgres admin prebuilt tools",
1368 | 			in:   alloydb_admin_config,
1369 | 			wantToolset: server.ToolsetConfigs{
1370 | 				"alloydb_postgres_admin_tools": tools.ToolsetConfig{
1371 | 					Name:      "alloydb_postgres_admin_tools",
1372 | 					ToolNames: []string{"create_cluster", "wait_for_operation", "create_instance", "list_clusters", "list_instances", "list_users", "create_user", "get_cluster", "get_instance", "get_user"},
1373 | 				},
1374 | 			},
1375 | 		},
1376 | 		{
1377 | 			name: "cloudsql pg admin prebuilt tools",
1378 | 			in:   cloudsqlpg_admin_config,
1379 | 			wantToolset: server.ToolsetConfigs{
1380 | 				"cloud_sql_postgres_admin_tools": tools.ToolsetConfig{
1381 | 					Name:      "cloud_sql_postgres_admin_tools",
1382 | 					ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation"},
1383 | 				},
1384 | 			},
1385 | 		},
1386 | 		{
1387 | 			name: "cloudsql mysql admin prebuilt tools",
1388 | 			in:   cloudsqlmysql_admin_config,
1389 | 			wantToolset: server.ToolsetConfigs{
1390 | 				"cloud_sql_mysql_admin_tools": tools.ToolsetConfig{
1391 | 					Name:      "cloud_sql_mysql_admin_tools",
1392 | 					ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation"},
1393 | 				},
1394 | 			},
1395 | 		},
1396 | 		{
1397 | 			name: "cloudsql mssql admin prebuilt tools",
1398 | 			in:   cloudsqlmssql_admin_config,
1399 | 			wantToolset: server.ToolsetConfigs{
1400 | 				"cloud_sql_mssql_admin_tools": tools.ToolsetConfig{
1401 | 					Name:      "cloud_sql_mssql_admin_tools",
1402 | 					ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation"},
1403 | 				},
1404 | 			},
1405 | 		},
1406 | 		{
1407 | 			name: "alloydb prebuilt tools",
1408 | 			in:   alloydb_config,
1409 | 			wantToolset: server.ToolsetConfigs{
1410 | 				"alloydb_postgres_database_tools": tools.ToolsetConfig{
1411 | 					Name:      "alloydb_postgres_database_tools",
1412 | 					ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas"},
1413 | 				},
1414 | 			},
1415 | 		},
1416 | 		{
1417 | 			name: "bigquery prebuilt tools",
1418 | 			in:   bigquery_config,
1419 | 			wantToolset: server.ToolsetConfigs{
1420 | 				"bigquery_database_tools": tools.ToolsetConfig{
1421 | 					Name:      "bigquery_database_tools",
1422 | 					ToolNames: []string{"analyze_contribution", "ask_data_insights", "execute_sql", "forecast", "get_dataset_info", "get_table_info", "list_dataset_ids", "list_table_ids", "search_catalog"},
1423 | 				},
1424 | 			},
1425 | 		},
1426 | 		{
1427 | 			name: "clickhouse prebuilt tools",
1428 | 			in:   clickhouse_config,
1429 | 			wantToolset: server.ToolsetConfigs{
1430 | 				"clickhouse_database_tools": tools.ToolsetConfig{
1431 | 					Name:      "clickhouse_database_tools",
1432 | 					ToolNames: []string{"execute_sql", "list_databases", "list_tables"},
1433 | 				},
1434 | 			},
1435 | 		},
1436 | 		{
1437 | 			name: "cloudsqlpg prebuilt tools",
1438 | 			in:   cloudsqlpg_config,
1439 | 			wantToolset: server.ToolsetConfigs{
1440 | 				"cloud_sql_postgres_database_tools": tools.ToolsetConfig{
1441 | 					Name:      "cloud_sql_postgres_database_tools",
1442 | 					ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas"},
1443 | 				},
1444 | 			},
1445 | 		},
1446 | 		{
1447 | 			name: "cloudsqlmysql prebuilt tools",
1448 | 			in:   cloudsqlmysql_config,
1449 | 			wantToolset: server.ToolsetConfigs{
1450 | 				"cloud_sql_mysql_database_tools": tools.ToolsetConfig{
1451 | 					Name:      "cloud_sql_mysql_database_tools",
1452 | 					ToolNames: []string{"execute_sql", "list_tables", "get_query_plan", "list_active_queries", "list_tables_missing_unique_indexes", "list_table_fragmentation"},
1453 | 				},
1454 | 			},
1455 | 		},
1456 | 		{
1457 | 			name: "cloudsqlmssql prebuilt tools",
1458 | 			in:   cloudsqlmssql_config,
1459 | 			wantToolset: server.ToolsetConfigs{
1460 | 				"cloud_sql_mssql_database_tools": tools.ToolsetConfig{
1461 | 					Name:      "cloud_sql_mssql_database_tools",
1462 | 					ToolNames: []string{"execute_sql", "list_tables"},
1463 | 				},
1464 | 			},
1465 | 		},
1466 | 		{
1467 | 			name: "dataplex prebuilt tools",
1468 | 			in:   dataplex_config,
1469 | 			wantToolset: server.ToolsetConfigs{
1470 | 				"dataplex_tools": tools.ToolsetConfig{
1471 | 					Name:      "dataplex_tools",
1472 | 					ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types"},
1473 | 				},
1474 | 			},
1475 | 		},
1476 | 		{
1477 | 			name: "serverless spark prebuilt tools",
1478 | 			in:   serverless_spark_config,
1479 | 			wantToolset: server.ToolsetConfigs{
1480 | 				"serverless_spark_tools": tools.ToolsetConfig{
1481 | 					Name:      "serverless_spark_tools",
1482 | 					ToolNames: []string{"list_batches", "get_batch", "cancel_batch"},
1483 | 				},
1484 | 			},
1485 | 		},
1486 | 		{
1487 | 			name: "firestore prebuilt tools",
1488 | 			in:   firestoreconfig,
1489 | 			wantToolset: server.ToolsetConfigs{
1490 | 				"firestore_database_tools": tools.ToolsetConfig{
1491 | 					Name:      "firestore_database_tools",
1492 | 					ToolNames: []string{"get_documents", "add_documents", "update_document", "list_collections", "delete_documents", "query_collection", "get_rules", "validate_rules"},
1493 | 				},
1494 | 			},
1495 | 		},
1496 | 		{
1497 | 			name: "mysql prebuilt tools",
1498 | 			in:   mysql_config,
1499 | 			wantToolset: server.ToolsetConfigs{
1500 | 				"mysql_database_tools": tools.ToolsetConfig{
1501 | 					Name:      "mysql_database_tools",
1502 | 					ToolNames: []string{"execute_sql", "list_tables", "get_query_plan", "list_active_queries", "list_tables_missing_unique_indexes", "list_table_fragmentation"},
1503 | 				},
1504 | 			},
1505 | 		},
1506 | 		{
1507 | 			name: "mssql prebuilt tools",
1508 | 			in:   mssql_config,
1509 | 			wantToolset: server.ToolsetConfigs{
1510 | 				"mssql_database_tools": tools.ToolsetConfig{
1511 | 					Name:      "mssql_database_tools",
1512 | 					ToolNames: []string{"execute_sql", "list_tables"},
1513 | 				},
1514 | 			},
1515 | 		},
1516 | 		{
1517 | 			name: "looker prebuilt tools",
1518 | 			in:   looker_config,
1519 | 			wantToolset: server.ToolsetConfigs{
1520 | 				"looker_tools": tools.ToolsetConfig{
1521 | 					Name:      "looker_tools",
1522 | 					ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "health_pulse", "health_analyze", "health_vacuum", "dev_mode", "get_projects", "get_project_files", "get_project_file", "create_project_file", "update_project_file", "delete_project_file", "get_connections", "get_connection_schemas", "get_connection_databases", "get_connection_tables", "get_connection_table_columns"},
1523 | 				},
1524 | 			},
1525 | 		},
1526 | 		{
1527 | 			name: "looker-conversational-analytics prebuilt tools",
1528 | 			in:   lookerca_config,
1529 | 			wantToolset: server.ToolsetConfigs{
1530 | 				"looker_conversational_analytics_tools": tools.ToolsetConfig{
1531 | 					Name:      "looker_conversational_analytics_tools",
1532 | 					ToolNames: []string{"ask_data_insights", "get_models", "get_explores"},
1533 | 				},
1534 | 			},
1535 | 		},
1536 | 		{
1537 | 			name: "postgres prebuilt tools",
1538 | 			in:   postgresconfig,
1539 | 			wantToolset: server.ToolsetConfigs{
1540 | 				"postgres_database_tools": tools.ToolsetConfig{
1541 | 					Name:      "postgres_database_tools",
1542 | 					ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas"},
1543 | 				},
1544 | 			},
1545 | 		},
1546 | 		{
1547 | 			name: "spanner prebuilt tools",
1548 | 			in:   spanner_config,
1549 | 			wantToolset: server.ToolsetConfigs{
1550 | 				"spanner-database-tools": tools.ToolsetConfig{
1551 | 					Name:      "spanner-database-tools",
1552 | 					ToolNames: []string{"execute_sql", "execute_sql_dql", "list_tables"},
1553 | 				},
1554 | 			},
1555 | 		},
1556 | 		{
1557 | 			name: "spanner pg prebuilt tools",
1558 | 			in:   spannerpg_config,
1559 | 			wantToolset: server.ToolsetConfigs{
1560 | 				"spanner_postgres_database_tools": tools.ToolsetConfig{
1561 | 					Name:      "spanner_postgres_database_tools",
1562 | 					ToolNames: []string{"execute_sql", "execute_sql_dql", "list_tables"},
1563 | 				},
1564 | 			},
1565 | 		},
1566 | 		{
1567 | 			name: "mindsdb prebuilt tools",
1568 | 			in:   mindsdb_config,
1569 | 			wantToolset: server.ToolsetConfigs{
1570 | 				"mindsdb-tools": tools.ToolsetConfig{
1571 | 					Name:      "mindsdb-tools",
1572 | 					ToolNames: []string{"mindsdb-execute-sql", "mindsdb-sql"},
1573 | 				},
1574 | 			},
1575 | 		},
1576 | 		{
1577 | 			name: "sqlite prebuilt tools",
1578 | 			in:   sqlite_config,
1579 | 			wantToolset: server.ToolsetConfigs{
1580 | 				"sqlite_database_tools": tools.ToolsetConfig{
1581 | 					Name:      "sqlite_database_tools",
1582 | 					ToolNames: []string{"execute_sql", "list_tables"},
1583 | 				},
1584 | 			},
1585 | 		},
1586 | 		{
1587 | 			name: "neo4j prebuilt tools",
1588 | 			in:   neo4jconfig,
1589 | 			wantToolset: server.ToolsetConfigs{
1590 | 				"neo4j_database_tools": tools.ToolsetConfig{
1591 | 					Name:      "neo4j_database_tools",
1592 | 					ToolNames: []string{"execute_cypher", "get_schema"},
1593 | 				},
1594 | 			},
1595 | 		},
1596 | 		{
1597 | 			name: "alloydb postgres observability prebuilt tools",
1598 | 			in:   alloydbobsvconfig,
1599 | 			wantToolset: server.ToolsetConfigs{
1600 | 				"alloydb_postgres_cloud_monitoring_tools": tools.ToolsetConfig{
1601 | 					Name:      "alloydb_postgres_cloud_monitoring_tools",
1602 | 					ToolNames: []string{"get_system_metrics", "get_query_metrics"},
1603 | 				},
1604 | 			},
1605 | 		},
1606 | 		{
1607 | 			name: "cloudsql postgres observability prebuilt tools",
1608 | 			in:   cloudsqlpgobsvconfig,
1609 | 			wantToolset: server.ToolsetConfigs{
1610 | 				"cloud_sql_postgres_cloud_monitoring_tools": tools.ToolsetConfig{
1611 | 					Name:      "cloud_sql_postgres_cloud_monitoring_tools",
1612 | 					ToolNames: []string{"get_system_metrics", "get_query_metrics"},
1613 | 				},
1614 | 			},
1615 | 		},
1616 | 		{
1617 | 			name: "cloudsql mysql observability prebuilt tools",
1618 | 			in:   cloudsqlmysqlobsvconfig,
1619 | 			wantToolset: server.ToolsetConfigs{
1620 | 				"cloud_sql_mysql_cloud_monitoring_tools": tools.ToolsetConfig{
1621 | 					Name:      "cloud_sql_mysql_cloud_monitoring_tools",
1622 | 					ToolNames: []string{"get_system_metrics", "get_query_metrics"},
1623 | 				},
1624 | 			},
1625 | 		},
1626 | 		{
1627 | 			name: "cloudsql mssql observability prebuilt tools",
1628 | 			in:   cloudsqlmssqlobsvconfig,
1629 | 			wantToolset: server.ToolsetConfigs{
1630 | 				"cloud_sql_mssql_cloud_monitoring_tools": tools.ToolsetConfig{
1631 | 					Name:      "cloud_sql_mssql_cloud_monitoring_tools",
1632 | 					ToolNames: []string{"get_system_metrics"},
1633 | 				},
1634 | 			},
1635 | 		},
1636 | 		{
1637 | 			name: "cloud healthcare prebuilt tools",
1638 | 			in:   cloudhealthcare_config,
1639 | 			wantToolset: server.ToolsetConfigs{
1640 | 				"cloud_healthcare_dataset_tools": tools.ToolsetConfig{
1641 | 					Name:      "cloud_healthcare_dataset_tools",
1642 | 					ToolNames: []string{"get_dataset", "list_dicom_stores", "list_fhir_stores"},
1643 | 				},
1644 | 				"cloud_healthcare_fhir_tools": tools.ToolsetConfig{
1645 | 					Name:      "cloud_healthcare_fhir_tools",
1646 | 					ToolNames: []string{"get_fhir_store", "get_fhir_store_metrics", "get_fhir_resource", "fhir_patient_search", "fhir_patient_everything", "fhir_fetch_page"},
1647 | 				},
1648 | 				"cloud_healthcare_dicom_tools": tools.ToolsetConfig{
1649 | 					Name:      "cloud_healthcare_dicom_tools",
1650 | 					ToolNames: []string{"get_dicom_store", "get_dicom_store_metrics", "search_dicom_studies", "search_dicom_series", "search_dicom_instances", "retrieve_rendered_dicom_instance"},
1651 | 				},
1652 | 			},
1653 | 		},
1654 | 	}
1655 | 
1656 | 	for _, tc := range tcs {
1657 | 		t.Run(tc.name, func(t *testing.T) {
1658 | 			toolsFile, err := parseToolsFile(ctx, tc.in)
1659 | 			if err != nil {
1660 | 				t.Fatalf("failed to parse input: %v", err)
1661 | 			}
1662 | 			if diff := cmp.Diff(tc.wantToolset, toolsFile.Toolsets); diff != "" {
1663 | 				t.Fatalf("incorrect tools parse: diff %v", diff)
1664 | 			}
1665 | 		})
1666 | 	}
1667 | }
1668 | 
1669 | func TestMutuallyExclusiveFlags(t *testing.T) {
1670 | 	testCases := []struct {
1671 | 		desc      string
1672 | 		args      []string
1673 | 		errString string
1674 | 	}{
1675 | 		{
1676 | 			desc:      "--prebuilt and --tools-file",
1677 | 			args:      []string{"--prebuilt", "alloydb", "--tools-file", "my.yaml"},
1678 | 			errString: "--prebuilt and --tools-file/--tools-files/--tools-folder flags cannot be used simultaneously",
1679 | 		},
1680 | 		{
1681 | 			desc:      "--tools-file and --tools-files",
1682 | 			args:      []string{"--tools-file", "my.yaml", "--tools-files", "a.yaml,b.yaml"},
1683 | 			errString: "--tools-file, --tools-files, and --tools-folder flags cannot be used simultaneously",
1684 | 		},
1685 | 		{
1686 | 			desc:      "--tools-folder and --tools-files",
1687 | 			args:      []string{"--tools-folder", "./", "--tools-files", "a.yaml,b.yaml"},
1688 | 			errString: "--tools-file, --tools-files, and --tools-folder flags cannot be used simultaneously",
1689 | 		},
1690 | 	}
1691 | 
1692 | 	for _, tc := range testCases {
1693 | 		t.Run(tc.desc, func(t *testing.T) {
1694 | 			cmd := NewCommand()
1695 | 			cmd.SetArgs(tc.args)
1696 | 			err := cmd.Execute()
1697 | 			if err == nil {
1698 | 				t.Fatalf("expected an error but got none")
1699 | 			}
1700 | 			if !strings.Contains(err.Error(), tc.errString) {
1701 | 				t.Errorf("expected error message to contain %q, but got %q", tc.errString, err.Error())
1702 | 			}
1703 | 		})
1704 | 	}
1705 | }
1706 | 
1707 | func TestFileLoadingErrors(t *testing.T) {
1708 | 	t.Run("non-existent tools-file", func(t *testing.T) {
1709 | 		cmd := NewCommand()
1710 | 		// Use a file that is guaranteed not to exist
1711 | 		nonExistentFile := filepath.Join(t.TempDir(), "non-existent-tools.yaml")
1712 | 		cmd.SetArgs([]string{"--tools-file", nonExistentFile})
1713 | 
1714 | 		err := cmd.Execute()
1715 | 		if err == nil {
1716 | 			t.Fatal("expected an error for non-existent file but got none")
1717 | 		}
1718 | 		if !strings.Contains(err.Error(), "unable to read tool file") {
1719 | 			t.Errorf("expected error about reading file, but got: %v", err)
1720 | 		}
1721 | 	})
1722 | 
1723 | 	t.Run("non-existent tools-folder", func(t *testing.T) {
1724 | 		cmd := NewCommand()
1725 | 		nonExistentFolder := filepath.Join(t.TempDir(), "non-existent-folder")
1726 | 		cmd.SetArgs([]string{"--tools-folder", nonExistentFolder})
1727 | 
1728 | 		err := cmd.Execute()
1729 | 		if err == nil {
1730 | 			t.Fatal("expected an error for non-existent folder but got none")
1731 | 		}
1732 | 		if !strings.Contains(err.Error(), "unable to access tools folder") {
1733 | 			t.Errorf("expected error about accessing folder, but got: %v", err)
1734 | 		}
1735 | 	})
1736 | }
1737 | 
```
Page 53/59FirstPrevNextLast