This is page 26 of 48. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .ci
│ ├── continuous.release.cloudbuild.yaml
│ ├── generate_release_table.sh
│ ├── integration.cloudbuild.yaml
│ ├── quickstart_test
│ │ ├── go.integration.cloudbuild.yaml
│ │ ├── js.integration.cloudbuild.yaml
│ │ ├── py.integration.cloudbuild.yaml
│ │ ├── run_go_tests.sh
│ │ ├── run_js_tests.sh
│ │ ├── run_py_tests.sh
│ │ └── setup_hotels_sample.sql
│ ├── test_with_coverage.sh
│ └── versioned.release.cloudbuild.yaml
├── .github
│ ├── auto-label.yaml
│ ├── blunderbuss.yml
│ ├── CODEOWNERS
│ ├── header-checker-lint.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── feature_request.yml
│ │ └── question.yml
│ ├── label-sync.yml
│ ├── labels.yaml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── release-please.yml
│ ├── renovate.json5
│ ├── sync-repo-settings.yaml
│ └── workflows
│ ├── cloud_build_failure_reporter.yml
│ ├── deploy_dev_docs.yaml
│ ├── deploy_previous_version_docs.yaml
│ ├── deploy_versioned_docs.yaml
│ ├── docs_deploy.yaml
│ ├── docs_preview_clean.yaml
│ ├── docs_preview_deploy.yaml
│ ├── lint.yaml
│ ├── schedule_reporter.yml
│ ├── sync-labels.yaml
│ └── tests.yaml
├── .gitignore
├── .gitmodules
├── .golangci.yaml
├── .hugo
│ ├── archetypes
│ │ └── default.md
│ ├── assets
│ │ ├── icons
│ │ │ └── logo.svg
│ │ └── scss
│ │ ├── _styles_project.scss
│ │ └── _variables_project.scss
│ ├── go.mod
│ ├── go.sum
│ ├── hugo.toml
│ ├── layouts
│ │ ├── _default
│ │ │ └── home.releases.releases
│ │ ├── index.llms-full.txt
│ │ ├── index.llms.txt
│ │ ├── partials
│ │ │ ├── hooks
│ │ │ │ └── head-end.html
│ │ │ ├── navbar-version-selector.html
│ │ │ ├── page-meta-links.html
│ │ │ └── td
│ │ │ └── render-heading.html
│ │ ├── robot.txt
│ │ └── shortcodes
│ │ ├── include.html
│ │ ├── ipynb.html
│ │ └── regionInclude.html
│ ├── package-lock.json
│ ├── package.json
│ └── static
│ ├── favicons
│ │ ├── android-chrome-192x192.png
│ │ ├── android-chrome-512x512.png
│ │ ├── apple-touch-icon.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ └── favicon.ico
│ └── js
│ └── w3.js
├── CHANGELOG.md
├── cmd
│ ├── options_test.go
│ ├── options.go
│ ├── root_test.go
│ ├── root.go
│ └── version.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPER.md
├── Dockerfile
├── docs
│ └── en
│ ├── _index.md
│ ├── about
│ │ ├── _index.md
│ │ └── faq.md
│ ├── concepts
│ │ ├── _index.md
│ │ └── telemetry
│ │ ├── index.md
│ │ ├── telemetry_flow.png
│ │ └── telemetry_traces.png
│ ├── getting-started
│ │ ├── _index.md
│ │ ├── colab_quickstart.ipynb
│ │ ├── configure.md
│ │ ├── introduction
│ │ │ ├── _index.md
│ │ │ └── architecture.png
│ │ ├── local_quickstart_go.md
│ │ ├── local_quickstart_js.md
│ │ ├── local_quickstart.md
│ │ ├── mcp_quickstart
│ │ │ ├── _index.md
│ │ │ ├── inspector_tools.png
│ │ │ └── inspector.png
│ │ └── quickstart
│ │ ├── go
│ │ │ ├── genAI
│ │ │ │ ├── go.mod
│ │ │ │ ├── go.sum
│ │ │ │ └── quickstart.go
│ │ │ ├── genkit
│ │ │ │ ├── go.mod
│ │ │ │ ├── go.sum
│ │ │ │ └── quickstart.go
│ │ │ ├── langchain
│ │ │ │ ├── go.mod
│ │ │ │ ├── go.sum
│ │ │ │ └── quickstart.go
│ │ │ ├── openAI
│ │ │ │ ├── go.mod
│ │ │ │ ├── go.sum
│ │ │ │ └── quickstart.go
│ │ │ └── quickstart_test.go
│ │ ├── golden.txt
│ │ ├── js
│ │ │ ├── genAI
│ │ │ │ ├── package-lock.json
│ │ │ │ ├── package.json
│ │ │ │ └── quickstart.js
│ │ │ ├── genkit
│ │ │ │ ├── package-lock.json
│ │ │ │ ├── package.json
│ │ │ │ └── quickstart.js
│ │ │ ├── langchain
│ │ │ │ ├── package-lock.json
│ │ │ │ ├── package.json
│ │ │ │ └── quickstart.js
│ │ │ ├── llamaindex
│ │ │ │ ├── package-lock.json
│ │ │ │ ├── package.json
│ │ │ │ └── quickstart.js
│ │ │ └── quickstart.test.js
│ │ ├── python
│ │ │ ├── __init__.py
│ │ │ ├── adk
│ │ │ │ ├── quickstart.py
│ │ │ │ └── requirements.txt
│ │ │ ├── core
│ │ │ │ ├── quickstart.py
│ │ │ │ └── requirements.txt
│ │ │ ├── langchain
│ │ │ │ ├── quickstart.py
│ │ │ │ └── requirements.txt
│ │ │ ├── llamaindex
│ │ │ │ ├── quickstart.py
│ │ │ │ └── requirements.txt
│ │ │ └── quickstart_test.py
│ │ └── shared
│ │ ├── cloud_setup.md
│ │ ├── configure_toolbox.md
│ │ └── database_setup.md
│ ├── how-to
│ │ ├── _index.md
│ │ ├── connect_via_geminicli.md
│ │ ├── connect_via_mcp.md
│ │ ├── connect-ide
│ │ │ ├── _index.md
│ │ │ ├── alloydb_pg_admin_mcp.md
│ │ │ ├── alloydb_pg_mcp.md
│ │ │ ├── bigquery_mcp.md
│ │ │ ├── cloud_sql_mssql_admin_mcp.md
│ │ │ ├── cloud_sql_mssql_mcp.md
│ │ │ ├── cloud_sql_mysql_admin_mcp.md
│ │ │ ├── cloud_sql_mysql_mcp.md
│ │ │ ├── cloud_sql_pg_admin_mcp.md
│ │ │ ├── cloud_sql_pg_mcp.md
│ │ │ ├── firestore_mcp.md
│ │ │ ├── looker_mcp.md
│ │ │ ├── mssql_mcp.md
│ │ │ ├── mysql_mcp.md
│ │ │ ├── neo4j_mcp.md
│ │ │ ├── postgres_mcp.md
│ │ │ ├── spanner_mcp.md
│ │ │ └── sqlite_mcp.md
│ │ ├── deploy_docker.md
│ │ ├── deploy_gke.md
│ │ ├── deploy_toolbox.md
│ │ ├── export_telemetry.md
│ │ └── toolbox-ui
│ │ ├── edit-headers.gif
│ │ ├── edit-headers.png
│ │ ├── index.md
│ │ ├── optional-param-checked.png
│ │ ├── optional-param-unchecked.png
│ │ ├── run-tool.gif
│ │ ├── tools.png
│ │ └── toolsets.png
│ ├── reference
│ │ ├── _index.md
│ │ ├── cli.md
│ │ └── prebuilt-tools.md
│ ├── resources
│ │ ├── _index.md
│ │ ├── authServices
│ │ │ ├── _index.md
│ │ │ └── google.md
│ │ ├── sources
│ │ │ ├── _index.md
│ │ │ ├── alloydb-admin.md
│ │ │ ├── alloydb-pg.md
│ │ │ ├── bigquery.md
│ │ │ ├── bigtable.md
│ │ │ ├── cassandra.md
│ │ │ ├── clickhouse.md
│ │ │ ├── cloud-monitoring.md
│ │ │ ├── cloud-sql-admin.md
│ │ │ ├── cloud-sql-mssql.md
│ │ │ ├── cloud-sql-mysql.md
│ │ │ ├── cloud-sql-pg.md
│ │ │ ├── couchbase.md
│ │ │ ├── dataplex.md
│ │ │ ├── dgraph.md
│ │ │ ├── firebird.md
│ │ │ ├── firestore.md
│ │ │ ├── http.md
│ │ │ ├── looker.md
│ │ │ ├── mongodb.md
│ │ │ ├── mssql.md
│ │ │ ├── mysql.md
│ │ │ ├── neo4j.md
│ │ │ ├── oceanbase.md
│ │ │ ├── oracle.md
│ │ │ ├── postgres.md
│ │ │ ├── redis.md
│ │ │ ├── serverless-spark.md
│ │ │ ├── spanner.md
│ │ │ ├── sqlite.md
│ │ │ ├── tidb.md
│ │ │ ├── trino.md
│ │ │ ├── valkey.md
│ │ │ └── yugabytedb.md
│ │ └── tools
│ │ ├── _index.md
│ │ ├── alloydb
│ │ │ ├── _index.md
│ │ │ ├── alloydb-create-cluster.md
│ │ │ ├── alloydb-create-instance.md
│ │ │ ├── alloydb-create-user.md
│ │ │ ├── alloydb-get-cluster.md
│ │ │ ├── alloydb-get-instance.md
│ │ │ ├── alloydb-get-user.md
│ │ │ ├── alloydb-list-clusters.md
│ │ │ ├── alloydb-list-instances.md
│ │ │ ├── alloydb-list-users.md
│ │ │ └── alloydb-wait-for-operation.md
│ │ ├── alloydbainl
│ │ │ ├── _index.md
│ │ │ └── alloydb-ai-nl.md
│ │ ├── bigquery
│ │ │ ├── _index.md
│ │ │ ├── bigquery-analyze-contribution.md
│ │ │ ├── bigquery-conversational-analytics.md
│ │ │ ├── bigquery-execute-sql.md
│ │ │ ├── bigquery-forecast.md
│ │ │ ├── bigquery-get-dataset-info.md
│ │ │ ├── bigquery-get-table-info.md
│ │ │ ├── bigquery-list-dataset-ids.md
│ │ │ ├── bigquery-list-table-ids.md
│ │ │ ├── bigquery-search-catalog.md
│ │ │ └── bigquery-sql.md
│ │ ├── bigtable
│ │ │ ├── _index.md
│ │ │ └── bigtable-sql.md
│ │ ├── cassandra
│ │ │ ├── _index.md
│ │ │ └── cassandra-cql.md
│ │ ├── clickhouse
│ │ │ ├── _index.md
│ │ │ ├── clickhouse-execute-sql.md
│ │ │ ├── clickhouse-list-databases.md
│ │ │ ├── clickhouse-list-tables.md
│ │ │ └── clickhouse-sql.md
│ │ ├── cloudmonitoring
│ │ │ ├── _index.md
│ │ │ └── cloud-monitoring-query-prometheus.md
│ │ ├── cloudsql
│ │ │ ├── _index.md
│ │ │ ├── cloudsqlcreatedatabase.md
│ │ │ ├── cloudsqlcreateusers.md
│ │ │ ├── cloudsqlgetinstances.md
│ │ │ ├── cloudsqllistdatabases.md
│ │ │ ├── cloudsqllistinstances.md
│ │ │ ├── cloudsqlmssqlcreateinstance.md
│ │ │ ├── cloudsqlmysqlcreateinstance.md
│ │ │ ├── cloudsqlpgcreateinstances.md
│ │ │ └── cloudsqlwaitforoperation.md
│ │ ├── couchbase
│ │ │ ├── _index.md
│ │ │ └── couchbase-sql.md
│ │ ├── dataform
│ │ │ ├── _index.md
│ │ │ └── dataform-compile-local.md
│ │ ├── dataplex
│ │ │ ├── _index.md
│ │ │ ├── dataplex-lookup-entry.md
│ │ │ ├── dataplex-search-aspect-types.md
│ │ │ └── dataplex-search-entries.md
│ │ ├── dgraph
│ │ │ ├── _index.md
│ │ │ └── dgraph-dql.md
│ │ ├── firebird
│ │ │ ├── _index.md
│ │ │ ├── firebird-execute-sql.md
│ │ │ └── firebird-sql.md
│ │ ├── firestore
│ │ │ ├── _index.md
│ │ │ ├── firestore-add-documents.md
│ │ │ ├── firestore-delete-documents.md
│ │ │ ├── firestore-get-documents.md
│ │ │ ├── firestore-get-rules.md
│ │ │ ├── firestore-list-collections.md
│ │ │ ├── firestore-query-collection.md
│ │ │ ├── firestore-query.md
│ │ │ ├── firestore-update-document.md
│ │ │ └── firestore-validate-rules.md
│ │ ├── http
│ │ │ ├── _index.md
│ │ │ └── http.md
│ │ ├── looker
│ │ │ ├── _index.md
│ │ │ ├── looker-add-dashboard-element.md
│ │ │ ├── looker-conversational-analytics.md
│ │ │ ├── looker-create-project-file.md
│ │ │ ├── looker-delete-project-file.md
│ │ │ ├── looker-dev-mode.md
│ │ │ ├── looker-get-dashboards.md
│ │ │ ├── looker-get-dimensions.md
│ │ │ ├── looker-get-explores.md
│ │ │ ├── looker-get-filters.md
│ │ │ ├── looker-get-looks.md
│ │ │ ├── looker-get-measures.md
│ │ │ ├── looker-get-models.md
│ │ │ ├── looker-get-parameters.md
│ │ │ ├── looker-get-project-file.md
│ │ │ ├── looker-get-project-files.md
│ │ │ ├── looker-get-projects.md
│ │ │ ├── looker-health-analyze.md
│ │ │ ├── looker-health-pulse.md
│ │ │ ├── looker-health-vacuum.md
│ │ │ ├── looker-make-dashboard.md
│ │ │ ├── looker-make-look.md
│ │ │ ├── looker-query-sql.md
│ │ │ ├── looker-query-url.md
│ │ │ ├── looker-query.md
│ │ │ ├── looker-run-look.md
│ │ │ └── looker-update-project-file.md
│ │ ├── mongodb
│ │ │ ├── _index.md
│ │ │ ├── mongodb-aggregate.md
│ │ │ ├── mongodb-delete-many.md
│ │ │ ├── mongodb-delete-one.md
│ │ │ ├── mongodb-find-one.md
│ │ │ ├── mongodb-find.md
│ │ │ ├── mongodb-insert-many.md
│ │ │ ├── mongodb-insert-one.md
│ │ │ ├── mongodb-update-many.md
│ │ │ └── mongodb-update-one.md
│ │ ├── mssql
│ │ │ ├── _index.md
│ │ │ ├── mssql-execute-sql.md
│ │ │ ├── mssql-list-tables.md
│ │ │ └── mssql-sql.md
│ │ ├── mysql
│ │ │ ├── _index.md
│ │ │ ├── mysql-execute-sql.md
│ │ │ ├── mysql-list-active-queries.md
│ │ │ ├── mysql-list-table-fragmentation.md
│ │ │ ├── mysql-list-tables-missing-unique-indexes.md
│ │ │ ├── mysql-list-tables.md
│ │ │ └── mysql-sql.md
│ │ ├── neo4j
│ │ │ ├── _index.md
│ │ │ ├── neo4j-cypher.md
│ │ │ ├── neo4j-execute-cypher.md
│ │ │ └── neo4j-schema.md
│ │ ├── oceanbase
│ │ │ ├── _index.md
│ │ │ ├── oceanbase-execute-sql.md
│ │ │ └── oceanbase-sql.md
│ │ ├── oracle
│ │ │ ├── _index.md
│ │ │ ├── oracle-execute-sql.md
│ │ │ └── oracle-sql.md
│ │ ├── postgres
│ │ │ ├── _index.md
│ │ │ ├── postgres-execute-sql.md
│ │ │ ├── postgres-list-active-queries.md
│ │ │ ├── postgres-list-available-extensions.md
│ │ │ ├── postgres-list-installed-extensions.md
│ │ │ ├── postgres-list-tables.md
│ │ │ └── postgres-sql.md
│ │ ├── redis
│ │ │ ├── _index.md
│ │ │ └── redis.md
│ │ ├── serverless-spark
│ │ │ ├── _index.md
│ │ │ └── serverless-spark-list-batches.md
│ │ ├── spanner
│ │ │ ├── _index.md
│ │ │ ├── spanner-execute-sql.md
│ │ │ ├── spanner-list-tables.md
│ │ │ └── spanner-sql.md
│ │ ├── sqlite
│ │ │ ├── _index.md
│ │ │ ├── sqlite-execute-sql.md
│ │ │ └── sqlite-sql.md
│ │ ├── tidb
│ │ │ ├── _index.md
│ │ │ ├── tidb-execute-sql.md
│ │ │ └── tidb-sql.md
│ │ ├── trino
│ │ │ ├── _index.md
│ │ │ ├── trino-execute-sql.md
│ │ │ └── trino-sql.md
│ │ ├── utility
│ │ │ ├── _index.md
│ │ │ └── wait.md
│ │ ├── valkey
│ │ │ ├── _index.md
│ │ │ └── valkey.md
│ │ └── yuagbytedb
│ │ ├── _index.md
│ │ └── yugabytedb-sql.md
│ ├── samples
│ │ ├── _index.md
│ │ ├── alloydb
│ │ │ ├── _index.md
│ │ │ ├── ai-nl
│ │ │ │ ├── alloydb_ai_nl.ipynb
│ │ │ │ └── index.md
│ │ │ └── mcp_quickstart.md
│ │ ├── bigquery
│ │ │ ├── _index.md
│ │ │ ├── colab_quickstart_bigquery.ipynb
│ │ │ ├── local_quickstart.md
│ │ │ └── mcp_quickstart
│ │ │ ├── _index.md
│ │ │ ├── inspector_tools.png
│ │ │ └── inspector.png
│ │ └── looker
│ │ ├── _index.md
│ │ ├── looker_gemini_oauth
│ │ │ ├── _index.md
│ │ │ ├── authenticated.png
│ │ │ ├── authorize.png
│ │ │ └── registration.png
│ │ ├── looker_gemini.md
│ │ └── looker_mcp_inspector
│ │ ├── _index.md
│ │ ├── inspector_tools.png
│ │ └── inspector.png
│ └── sdks
│ ├── _index.md
│ ├── go-sdk.md
│ ├── js-sdk.md
│ └── python-sdk.md
├── gemini-extension.json
├── go.mod
├── go.sum
├── internal
│ ├── auth
│ │ ├── auth.go
│ │ └── google
│ │ └── google.go
│ ├── log
│ │ ├── handler.go
│ │ ├── log_test.go
│ │ ├── log.go
│ │ └── logger.go
│ ├── prebuiltconfigs
│ │ ├── prebuiltconfigs_test.go
│ │ ├── prebuiltconfigs.go
│ │ └── tools
│ │ ├── alloydb-postgres-admin.yaml
│ │ ├── alloydb-postgres-observability.yaml
│ │ ├── alloydb-postgres.yaml
│ │ ├── bigquery.yaml
│ │ ├── clickhouse.yaml
│ │ ├── cloud-sql-mssql-admin.yaml
│ │ ├── cloud-sql-mssql-observability.yaml
│ │ ├── cloud-sql-mssql.yaml
│ │ ├── cloud-sql-mysql-admin.yaml
│ │ ├── cloud-sql-mysql-observability.yaml
│ │ ├── cloud-sql-mysql.yaml
│ │ ├── cloud-sql-postgres-admin.yaml
│ │ ├── cloud-sql-postgres-observability.yaml
│ │ ├── cloud-sql-postgres.yaml
│ │ ├── dataplex.yaml
│ │ ├── firestore.yaml
│ │ ├── looker-conversational-analytics.yaml
│ │ ├── looker.yaml
│ │ ├── mssql.yaml
│ │ ├── mysql.yaml
│ │ ├── neo4j.yaml
│ │ ├── oceanbase.yaml
│ │ ├── postgres.yaml
│ │ ├── serverless-spark.yaml
│ │ ├── spanner-postgres.yaml
│ │ ├── spanner.yaml
│ │ └── sqlite.yaml
│ ├── server
│ │ ├── api_test.go
│ │ ├── api.go
│ │ ├── common_test.go
│ │ ├── config.go
│ │ ├── mcp
│ │ │ ├── jsonrpc
│ │ │ │ ├── jsonrpc_test.go
│ │ │ │ └── jsonrpc.go
│ │ │ ├── mcp.go
│ │ │ ├── util
│ │ │ │ └── lifecycle.go
│ │ │ ├── v20241105
│ │ │ │ ├── method.go
│ │ │ │ └── types.go
│ │ │ ├── v20250326
│ │ │ │ ├── method.go
│ │ │ │ └── types.go
│ │ │ └── v20250618
│ │ │ ├── method.go
│ │ │ └── types.go
│ │ ├── mcp_test.go
│ │ ├── mcp.go
│ │ ├── server_test.go
│ │ ├── server.go
│ │ ├── static
│ │ │ ├── assets
│ │ │ │ └── mcptoolboxlogo.png
│ │ │ ├── css
│ │ │ │ └── style.css
│ │ │ ├── index.html
│ │ │ ├── js
│ │ │ │ ├── auth.js
│ │ │ │ ├── loadTools.js
│ │ │ │ ├── mainContent.js
│ │ │ │ ├── navbar.js
│ │ │ │ ├── runTool.js
│ │ │ │ ├── toolDisplay.js
│ │ │ │ ├── tools.js
│ │ │ │ └── toolsets.js
│ │ │ ├── tools.html
│ │ │ └── toolsets.html
│ │ ├── web_test.go
│ │ └── web.go
│ ├── sources
│ │ ├── alloydbadmin
│ │ │ ├── alloydbadmin_test.go
│ │ │ └── alloydbadmin.go
│ │ ├── alloydbpg
│ │ │ ├── alloydb_pg_test.go
│ │ │ └── alloydb_pg.go
│ │ ├── bigquery
│ │ │ ├── bigquery_test.go
│ │ │ └── bigquery.go
│ │ ├── bigtable
│ │ │ ├── bigtable_test.go
│ │ │ └── bigtable.go
│ │ ├── cassandra
│ │ │ ├── cassandra_test.go
│ │ │ └── cassandra.go
│ │ ├── clickhouse
│ │ │ ├── clickhouse_test.go
│ │ │ └── clickhouse.go
│ │ ├── cloudmonitoring
│ │ │ ├── cloud_monitoring_test.go
│ │ │ └── cloud_monitoring.go
│ │ ├── cloudsqladmin
│ │ │ ├── cloud_sql_admin_test.go
│ │ │ └── cloud_sql_admin.go
│ │ ├── cloudsqlmssql
│ │ │ ├── cloud_sql_mssql_test.go
│ │ │ └── cloud_sql_mssql.go
│ │ ├── cloudsqlmysql
│ │ │ ├── cloud_sql_mysql_test.go
│ │ │ └── cloud_sql_mysql.go
│ │ ├── cloudsqlpg
│ │ │ ├── cloud_sql_pg_test.go
│ │ │ └── cloud_sql_pg.go
│ │ ├── couchbase
│ │ │ ├── couchbase_test.go
│ │ │ └── couchbase.go
│ │ ├── dataplex
│ │ │ ├── dataplex_test.go
│ │ │ └── dataplex.go
│ │ ├── dgraph
│ │ │ ├── dgraph_test.go
│ │ │ └── dgraph.go
│ │ ├── dialect.go
│ │ ├── firebird
│ │ │ ├── firebird_test.go
│ │ │ └── firebird.go
│ │ ├── firestore
│ │ │ ├── firestore_test.go
│ │ │ └── firestore.go
│ │ ├── http
│ │ │ ├── http_test.go
│ │ │ └── http.go
│ │ ├── ip_type.go
│ │ ├── looker
│ │ │ ├── looker_test.go
│ │ │ └── looker.go
│ │ ├── mongodb
│ │ │ ├── mongodb_test.go
│ │ │ └── mongodb.go
│ │ ├── mssql
│ │ │ ├── mssql_test.go
│ │ │ └── mssql.go
│ │ ├── mysql
│ │ │ ├── mysql_test.go
│ │ │ └── mysql.go
│ │ ├── neo4j
│ │ │ ├── neo4j_test.go
│ │ │ └── neo4j.go
│ │ ├── oceanbase
│ │ │ ├── oceanbase_test.go
│ │ │ └── oceanbase.go
│ │ ├── oracle
│ │ │ └── oracle.go
│ │ ├── postgres
│ │ │ ├── postgres_test.go
│ │ │ └── postgres.go
│ │ ├── redis
│ │ │ ├── redis_test.go
│ │ │ └── redis.go
│ │ ├── serverlessspark
│ │ │ ├── serverlessspark_test.go
│ │ │ └── serverlessspark.go
│ │ ├── sources.go
│ │ ├── spanner
│ │ │ ├── spanner_test.go
│ │ │ └── spanner.go
│ │ ├── sqlite
│ │ │ ├── sqlite_test.go
│ │ │ └── sqlite.go
│ │ ├── tidb
│ │ │ ├── tidb_test.go
│ │ │ └── tidb.go
│ │ ├── trino
│ │ │ ├── trino_test.go
│ │ │ └── trino.go
│ │ ├── util.go
│ │ ├── valkey
│ │ │ ├── valkey_test.go
│ │ │ └── valkey.go
│ │ └── yugabytedb
│ │ ├── yugabytedb_test.go
│ │ └── yugabytedb.go
│ ├── telemetry
│ │ ├── instrumentation.go
│ │ └── telemetry.go
│ ├── testutils
│ │ └── testutils.go
│ ├── tools
│ │ ├── alloydb
│ │ │ ├── alloydbcreatecluster
│ │ │ │ ├── alloydbcreatecluster_test.go
│ │ │ │ └── alloydbcreatecluster.go
│ │ │ ├── alloydbcreateinstance
│ │ │ │ ├── alloydbcreateinstance_test.go
│ │ │ │ └── alloydbcreateinstance.go
│ │ │ ├── alloydbcreateuser
│ │ │ │ ├── alloydbcreateuser_test.go
│ │ │ │ └── alloydbcreateuser.go
│ │ │ ├── alloydbgetcluster
│ │ │ │ ├── alloydbgetcluster_test.go
│ │ │ │ └── alloydbgetcluster.go
│ │ │ ├── alloydbgetinstance
│ │ │ │ ├── alloydbgetinstance_test.go
│ │ │ │ └── alloydbgetinstance.go
│ │ │ ├── alloydbgetuser
│ │ │ │ ├── alloydbgetuser_test.go
│ │ │ │ └── alloydbgetuser.go
│ │ │ ├── alloydblistclusters
│ │ │ │ ├── alloydblistclusters_test.go
│ │ │ │ └── alloydblistclusters.go
│ │ │ ├── alloydblistinstances
│ │ │ │ ├── alloydblistinstances_test.go
│ │ │ │ └── alloydblistinstances.go
│ │ │ ├── alloydblistusers
│ │ │ │ ├── alloydblistusers_test.go
│ │ │ │ └── alloydblistusers.go
│ │ │ └── alloydbwaitforoperation
│ │ │ ├── alloydbwaitforoperation_test.go
│ │ │ └── alloydbwaitforoperation.go
│ │ ├── alloydbainl
│ │ │ ├── alloydbainl_test.go
│ │ │ └── alloydbainl.go
│ │ ├── bigquery
│ │ │ ├── bigqueryanalyzecontribution
│ │ │ │ ├── bigqueryanalyzecontribution_test.go
│ │ │ │ └── bigqueryanalyzecontribution.go
│ │ │ ├── bigquerycommon
│ │ │ │ ├── table_name_parser_test.go
│ │ │ │ ├── table_name_parser.go
│ │ │ │ └── util.go
│ │ │ ├── bigqueryconversationalanalytics
│ │ │ │ ├── bigqueryconversationalanalytics_test.go
│ │ │ │ └── bigqueryconversationalanalytics.go
│ │ │ ├── bigqueryexecutesql
│ │ │ │ ├── bigqueryexecutesql_test.go
│ │ │ │ └── bigqueryexecutesql.go
│ │ │ ├── bigqueryforecast
│ │ │ │ ├── bigqueryforecast_test.go
│ │ │ │ └── bigqueryforecast.go
│ │ │ ├── bigquerygetdatasetinfo
│ │ │ │ ├── bigquerygetdatasetinfo_test.go
│ │ │ │ └── bigquerygetdatasetinfo.go
│ │ │ ├── bigquerygettableinfo
│ │ │ │ ├── bigquerygettableinfo_test.go
│ │ │ │ └── bigquerygettableinfo.go
│ │ │ ├── bigquerylistdatasetids
│ │ │ │ ├── bigquerylistdatasetids_test.go
│ │ │ │ └── bigquerylistdatasetids.go
│ │ │ ├── bigquerylisttableids
│ │ │ │ ├── bigquerylisttableids_test.go
│ │ │ │ └── bigquerylisttableids.go
│ │ │ ├── bigquerysearchcatalog
│ │ │ │ ├── bigquerysearchcatalog_test.go
│ │ │ │ └── bigquerysearchcatalog.go
│ │ │ └── bigquerysql
│ │ │ ├── bigquerysql_test.go
│ │ │ └── bigquerysql.go
│ │ ├── bigtable
│ │ │ ├── bigtable_test.go
│ │ │ └── bigtable.go
│ │ ├── cassandra
│ │ │ └── cassandracql
│ │ │ ├── cassandracql_test.go
│ │ │ └── cassandracql.go
│ │ ├── clickhouse
│ │ │ ├── clickhouseexecutesql
│ │ │ │ ├── clickhouseexecutesql_test.go
│ │ │ │ └── clickhouseexecutesql.go
│ │ │ ├── clickhouselistdatabases
│ │ │ │ ├── clickhouselistdatabases_test.go
│ │ │ │ └── clickhouselistdatabases.go
│ │ │ ├── clickhouselisttables
│ │ │ │ ├── clickhouselisttables_test.go
│ │ │ │ └── clickhouselisttables.go
│ │ │ └── clickhousesql
│ │ │ ├── clickhousesql_test.go
│ │ │ └── clickhousesql.go
│ │ ├── cloudmonitoring
│ │ │ ├── cloudmonitoring_test.go
│ │ │ └── cloudmonitoring.go
│ │ ├── cloudsql
│ │ │ ├── cloudsqlcreatedatabase
│ │ │ │ ├── cloudsqlcreatedatabase_test.go
│ │ │ │ └── cloudsqlcreatedatabase.go
│ │ │ ├── cloudsqlcreateusers
│ │ │ │ ├── cloudsqlcreateusers_test.go
│ │ │ │ └── cloudsqlcreateusers.go
│ │ │ ├── cloudsqlgetinstances
│ │ │ │ ├── cloudsqlgetinstances_test.go
│ │ │ │ └── cloudsqlgetinstances.go
│ │ │ ├── cloudsqllistdatabases
│ │ │ │ ├── cloudsqllistdatabases_test.go
│ │ │ │ └── cloudsqllistdatabases.go
│ │ │ ├── cloudsqllistinstances
│ │ │ │ ├── cloudsqllistinstances_test.go
│ │ │ │ └── cloudsqllistinstances.go
│ │ │ └── cloudsqlwaitforoperation
│ │ │ ├── cloudsqlwaitforoperation_test.go
│ │ │ └── cloudsqlwaitforoperation.go
│ │ ├── cloudsqlmssql
│ │ │ └── cloudsqlmssqlcreateinstance
│ │ │ ├── cloudsqlmssqlcreateinstance_test.go
│ │ │ └── cloudsqlmssqlcreateinstance.go
│ │ ├── cloudsqlmysql
│ │ │ └── cloudsqlmysqlcreateinstance
│ │ │ ├── cloudsqlmysqlcreateinstance_test.go
│ │ │ └── cloudsqlmysqlcreateinstance.go
│ │ ├── cloudsqlpg
│ │ │ └── cloudsqlpgcreateinstances
│ │ │ ├── cloudsqlpgcreateinstances_test.go
│ │ │ └── cloudsqlpgcreateinstances.go
│ │ ├── common_test.go
│ │ ├── common.go
│ │ ├── couchbase
│ │ │ ├── couchbase_test.go
│ │ │ └── couchbase.go
│ │ ├── dataform
│ │ │ └── dataformcompilelocal
│ │ │ ├── dataformcompilelocal_test.go
│ │ │ └── dataformcompilelocal.go
│ │ ├── dataplex
│ │ │ ├── dataplexlookupentry
│ │ │ │ ├── dataplexlookupentry_test.go
│ │ │ │ └── dataplexlookupentry.go
│ │ │ ├── dataplexsearchaspecttypes
│ │ │ │ ├── dataplexsearchaspecttypes_test.go
│ │ │ │ └── dataplexsearchaspecttypes.go
│ │ │ └── dataplexsearchentries
│ │ │ ├── dataplexsearchentries_test.go
│ │ │ └── dataplexsearchentries.go
│ │ ├── dgraph
│ │ │ ├── dgraph_test.go
│ │ │ └── dgraph.go
│ │ ├── firebird
│ │ │ ├── firebirdexecutesql
│ │ │ │ ├── firebirdexecutesql_test.go
│ │ │ │ └── firebirdexecutesql.go
│ │ │ └── firebirdsql
│ │ │ ├── firebirdsql_test.go
│ │ │ └── firebirdsql.go
│ │ ├── firestore
│ │ │ ├── firestoreadddocuments
│ │ │ │ ├── firestoreadddocuments_test.go
│ │ │ │ └── firestoreadddocuments.go
│ │ │ ├── firestoredeletedocuments
│ │ │ │ ├── firestoredeletedocuments_test.go
│ │ │ │ └── firestoredeletedocuments.go
│ │ │ ├── firestoregetdocuments
│ │ │ │ ├── firestoregetdocuments_test.go
│ │ │ │ └── firestoregetdocuments.go
│ │ │ ├── firestoregetrules
│ │ │ │ ├── firestoregetrules_test.go
│ │ │ │ └── firestoregetrules.go
│ │ │ ├── firestorelistcollections
│ │ │ │ ├── firestorelistcollections_test.go
│ │ │ │ └── firestorelistcollections.go
│ │ │ ├── firestorequery
│ │ │ │ ├── firestorequery_test.go
│ │ │ │ └── firestorequery.go
│ │ │ ├── firestorequerycollection
│ │ │ │ ├── firestorequerycollection_test.go
│ │ │ │ └── firestorequerycollection.go
│ │ │ ├── firestoreupdatedocument
│ │ │ │ ├── firestoreupdatedocument_test.go
│ │ │ │ └── firestoreupdatedocument.go
│ │ │ ├── firestorevalidaterules
│ │ │ │ ├── firestorevalidaterules_test.go
│ │ │ │ └── firestorevalidaterules.go
│ │ │ └── util
│ │ │ ├── converter_test.go
│ │ │ ├── converter.go
│ │ │ ├── validator_test.go
│ │ │ └── validator.go
│ │ ├── http
│ │ │ ├── http_test.go
│ │ │ └── http.go
│ │ ├── http_method.go
│ │ ├── looker
│ │ │ ├── lookeradddashboardelement
│ │ │ │ ├── lookeradddashboardelement_test.go
│ │ │ │ └── lookeradddashboardelement.go
│ │ │ ├── lookercommon
│ │ │ │ ├── lookercommon_test.go
│ │ │ │ └── lookercommon.go
│ │ │ ├── lookerconversationalanalytics
│ │ │ │ ├── lookerconversationalanalytics_test.go
│ │ │ │ └── lookerconversationalanalytics.go
│ │ │ ├── lookercreateprojectfile
│ │ │ │ ├── lookercreateprojectfile_test.go
│ │ │ │ └── lookercreateprojectfile.go
│ │ │ ├── lookerdeleteprojectfile
│ │ │ │ ├── lookerdeleteprojectfile_test.go
│ │ │ │ └── lookerdeleteprojectfile.go
│ │ │ ├── lookerdevmode
│ │ │ │ ├── lookerdevmode_test.go
│ │ │ │ └── lookerdevmode.go
│ │ │ ├── lookergetdashboards
│ │ │ │ ├── lookergetdashboards_test.go
│ │ │ │ └── lookergetdashboards.go
│ │ │ ├── lookergetdimensions
│ │ │ │ ├── lookergetdimensions_test.go
│ │ │ │ └── lookergetdimensions.go
│ │ │ ├── lookergetexplores
│ │ │ │ ├── lookergetexplores_test.go
│ │ │ │ └── lookergetexplores.go
│ │ │ ├── lookergetfilters
│ │ │ │ ├── lookergetfilters_test.go
│ │ │ │ └── lookergetfilters.go
│ │ │ ├── lookergetlooks
│ │ │ │ ├── lookergetlooks_test.go
│ │ │ │ └── lookergetlooks.go
│ │ │ ├── lookergetmeasures
│ │ │ │ ├── lookergetmeasures_test.go
│ │ │ │ └── lookergetmeasures.go
│ │ │ ├── lookergetmodels
│ │ │ │ ├── lookergetmodels_test.go
│ │ │ │ └── lookergetmodels.go
│ │ │ ├── lookergetparameters
│ │ │ │ ├── lookergetparameters_test.go
│ │ │ │ └── lookergetparameters.go
│ │ │ ├── lookergetprojectfile
│ │ │ │ ├── lookergetprojectfile_test.go
│ │ │ │ └── lookergetprojectfile.go
│ │ │ ├── lookergetprojectfiles
│ │ │ │ ├── lookergetprojectfiles_test.go
│ │ │ │ └── lookergetprojectfiles.go
│ │ │ ├── lookergetprojects
│ │ │ │ ├── lookergetprojects_test.go
│ │ │ │ └── lookergetprojects.go
│ │ │ ├── lookerhealthanalyze
│ │ │ │ ├── lookerhealthanalyze_test.go
│ │ │ │ └── lookerhealthanalyze.go
│ │ │ ├── lookerhealthpulse
│ │ │ │ ├── lookerhealthpulse_test.go
│ │ │ │ └── lookerhealthpulse.go
│ │ │ ├── lookerhealthvacuum
│ │ │ │ ├── lookerhealthvacuum_test.go
│ │ │ │ └── lookerhealthvacuum.go
│ │ │ ├── lookermakedashboard
│ │ │ │ ├── lookermakedashboard_test.go
│ │ │ │ └── lookermakedashboard.go
│ │ │ ├── lookermakelook
│ │ │ │ ├── lookermakelook_test.go
│ │ │ │ └── lookermakelook.go
│ │ │ ├── lookerquery
│ │ │ │ ├── lookerquery_test.go
│ │ │ │ └── lookerquery.go
│ │ │ ├── lookerquerysql
│ │ │ │ ├── lookerquerysql_test.go
│ │ │ │ └── lookerquerysql.go
│ │ │ ├── lookerqueryurl
│ │ │ │ ├── lookerqueryurl_test.go
│ │ │ │ └── lookerqueryurl.go
│ │ │ ├── lookerrunlook
│ │ │ │ ├── lookerrunlook_test.go
│ │ │ │ └── lookerrunlook.go
│ │ │ └── lookerupdateprojectfile
│ │ │ ├── lookerupdateprojectfile_test.go
│ │ │ └── lookerupdateprojectfile.go
│ │ ├── mongodb
│ │ │ ├── mongodbaggregate
│ │ │ │ ├── mongodbaggregate_test.go
│ │ │ │ └── mongodbaggregate.go
│ │ │ ├── mongodbdeletemany
│ │ │ │ ├── mongodbdeletemany_test.go
│ │ │ │ └── mongodbdeletemany.go
│ │ │ ├── mongodbdeleteone
│ │ │ │ ├── mongodbdeleteone_test.go
│ │ │ │ └── mongodbdeleteone.go
│ │ │ ├── mongodbfind
│ │ │ │ ├── mongodbfind_test.go
│ │ │ │ └── mongodbfind.go
│ │ │ ├── mongodbfindone
│ │ │ │ ├── mongodbfindone_test.go
│ │ │ │ └── mongodbfindone.go
│ │ │ ├── mongodbinsertmany
│ │ │ │ ├── mongodbinsertmany_test.go
│ │ │ │ └── mongodbinsertmany.go
│ │ │ ├── mongodbinsertone
│ │ │ │ ├── mongodbinsertone_test.go
│ │ │ │ └── mongodbinsertone.go
│ │ │ ├── mongodbupdatemany
│ │ │ │ ├── mongodbupdatemany_test.go
│ │ │ │ └── mongodbupdatemany.go
│ │ │ └── mongodbupdateone
│ │ │ ├── mongodbupdateone_test.go
│ │ │ └── mongodbupdateone.go
│ │ ├── mssql
│ │ │ ├── mssqlexecutesql
│ │ │ │ ├── mssqlexecutesql_test.go
│ │ │ │ └── mssqlexecutesql.go
│ │ │ ├── mssqllisttables
│ │ │ │ ├── mssqllisttables_test.go
│ │ │ │ └── mssqllisttables.go
│ │ │ └── mssqlsql
│ │ │ ├── mssqlsql_test.go
│ │ │ └── mssqlsql.go
│ │ ├── mysql
│ │ │ ├── mysqlcommon
│ │ │ │ └── mysqlcommon.go
│ │ │ ├── mysqlexecutesql
│ │ │ │ ├── mysqlexecutesql_test.go
│ │ │ │ └── mysqlexecutesql.go
│ │ │ ├── mysqllistactivequeries
│ │ │ │ ├── mysqllistactivequeries_test.go
│ │ │ │ └── mysqllistactivequeries.go
│ │ │ ├── mysqllisttablefragmentation
│ │ │ │ ├── mysqllisttablefragmentation_test.go
│ │ │ │ └── mysqllisttablefragmentation.go
│ │ │ ├── mysqllisttables
│ │ │ │ ├── mysqllisttables_test.go
│ │ │ │ └── mysqllisttables.go
│ │ │ ├── mysqllisttablesmissinguniqueindexes
│ │ │ │ ├── mysqllisttablesmissinguniqueindexes_test.go
│ │ │ │ └── mysqllisttablesmissinguniqueindexes.go
│ │ │ └── mysqlsql
│ │ │ ├── mysqlsql_test.go
│ │ │ └── mysqlsql.go
│ │ ├── neo4j
│ │ │ ├── neo4jcypher
│ │ │ │ ├── neo4jcypher_test.go
│ │ │ │ └── neo4jcypher.go
│ │ │ ├── neo4jexecutecypher
│ │ │ │ ├── classifier
│ │ │ │ │ ├── classifier_test.go
│ │ │ │ │ └── classifier.go
│ │ │ │ ├── neo4jexecutecypher_test.go
│ │ │ │ └── neo4jexecutecypher.go
│ │ │ └── neo4jschema
│ │ │ ├── cache
│ │ │ │ ├── cache_test.go
│ │ │ │ └── cache.go
│ │ │ ├── helpers
│ │ │ │ ├── helpers_test.go
│ │ │ │ └── helpers.go
│ │ │ ├── neo4jschema_test.go
│ │ │ ├── neo4jschema.go
│ │ │ └── types
│ │ │ └── types.go
│ │ ├── oceanbase
│ │ │ ├── oceanbaseexecutesql
│ │ │ │ ├── oceanbaseexecutesql_test.go
│ │ │ │ └── oceanbaseexecutesql.go
│ │ │ └── oceanbasesql
│ │ │ ├── oceanbasesql_test.go
│ │ │ └── oceanbasesql.go
│ │ ├── oracle
│ │ │ ├── oracleexecutesql
│ │ │ │ └── oracleexecutesql.go
│ │ │ └── oraclesql
│ │ │ └── oraclesql.go
│ │ ├── parameters_test.go
│ │ ├── parameters.go
│ │ ├── postgres
│ │ │ ├── postgresexecutesql
│ │ │ │ ├── postgresexecutesql_test.go
│ │ │ │ └── postgresexecutesql.go
│ │ │ ├── postgreslistactivequeries
│ │ │ │ ├── postgreslistactivequeries_test.go
│ │ │ │ └── postgreslistactivequeries.go
│ │ │ ├── postgreslistavailableextensions
│ │ │ │ ├── postgreslistavailableextensions_test.go
│ │ │ │ └── postgreslistavailableextensions.go
│ │ │ ├── postgreslistinstalledextensions
│ │ │ │ ├── postgreslistinstalledextensions_test.go
│ │ │ │ └── postgreslistinstalledextensions.go
│ │ │ ├── postgreslisttables
│ │ │ │ ├── postgreslisttables_test.go
│ │ │ │ └── postgreslisttables.go
│ │ │ └── postgressql
│ │ │ ├── postgressql_test.go
│ │ │ └── postgressql.go
│ │ ├── redis
│ │ │ ├── redis_test.go
│ │ │ └── redis.go
│ │ ├── serverlessspark
│ │ │ └── serverlesssparklistbatches
│ │ │ ├── serverlesssparklistbatches_test.go
│ │ │ └── serverlesssparklistbatches.go
│ │ ├── spanner
│ │ │ ├── spannerexecutesql
│ │ │ │ ├── spannerexecutesql_test.go
│ │ │ │ └── spannerexecutesql.go
│ │ │ ├── spannerlisttables
│ │ │ │ ├── spannerlisttables_test.go
│ │ │ │ └── spannerlisttables.go
│ │ │ └── spannersql
│ │ │ ├── spanner_test.go
│ │ │ └── spannersql.go
│ │ ├── sqlite
│ │ │ ├── sqliteexecutesql
│ │ │ │ ├── sqliteexecutesql_test.go
│ │ │ │ └── sqliteexecutesql.go
│ │ │ └── sqlitesql
│ │ │ ├── sqlitesql_test.go
│ │ │ └── sqlitesql.go
│ │ ├── tidb
│ │ │ ├── tidbexecutesql
│ │ │ │ ├── tidbexecutesql_test.go
│ │ │ │ └── tidbexecutesql.go
│ │ │ └── tidbsql
│ │ │ ├── tidbsql_test.go
│ │ │ └── tidbsql.go
│ │ ├── tools_test.go
│ │ ├── tools.go
│ │ ├── toolsets.go
│ │ ├── trino
│ │ │ ├── trinoexecutesql
│ │ │ │ ├── trinoexecutesql_test.go
│ │ │ │ └── trinoexecutesql.go
│ │ │ └── trinosql
│ │ │ ├── trinosql_test.go
│ │ │ └── trinosql.go
│ │ ├── utility
│ │ │ └── wait
│ │ │ ├── wait_test.go
│ │ │ └── wait.go
│ │ ├── valkey
│ │ │ ├── valkey_test.go
│ │ │ └── valkey.go
│ │ └── yugabytedbsql
│ │ ├── yugabytedbsql_test.go
│ │ └── yugabytedbsql.go
│ └── util
│ └── util.go
├── LICENSE
├── logo.png
├── main.go
├── MCP-TOOLBOX-EXTENSION.md
├── README.md
└── tests
├── alloydb
│ ├── alloydb_integration_test.go
│ └── alloydb_wait_for_operation_test.go
├── alloydbainl
│ └── alloydb_ai_nl_integration_test.go
├── alloydbpg
│ └── alloydb_pg_integration_test.go
├── auth.go
├── bigquery
│ └── bigquery_integration_test.go
├── bigtable
│ └── bigtable_integration_test.go
├── cassandra
│ └── cassandra_integration_test.go
├── clickhouse
│ └── clickhouse_integration_test.go
├── cloudmonitoring
│ └── cloud_monitoring_integration_test.go
├── cloudsql
│ ├── cloud_sql_create_database_test.go
│ ├── cloud_sql_create_users_test.go
│ ├── cloud_sql_get_instances_test.go
│ ├── cloud_sql_list_databases_test.go
│ ├── cloudsql_list_instances_test.go
│ └── cloudsql_wait_for_operation_test.go
├── cloudsqlmssql
│ ├── cloud_sql_mssql_create_instance_integration_test.go
│ └── cloud_sql_mssql_integration_test.go
├── cloudsqlmysql
│ ├── cloud_sql_mysql_create_instance_integration_test.go
│ └── cloud_sql_mysql_integration_test.go
├── cloudsqlpg
│ ├── cloud_sql_pg_create_instances_test.go
│ └── cloud_sql_pg_integration_test.go
├── common.go
├── couchbase
│ └── couchbase_integration_test.go
├── dataform
│ └── dataform_integration_test.go
├── dataplex
│ └── dataplex_integration_test.go
├── dgraph
│ └── dgraph_integration_test.go
├── firebird
│ └── firebird_integration_test.go
├── firestore
│ └── firestore_integration_test.go
├── http
│ └── http_integration_test.go
├── looker
│ └── looker_integration_test.go
├── mongodb
│ └── mongodb_integration_test.go
├── mssql
│ └── mssql_integration_test.go
├── mysql
│ └── mysql_integration_test.go
├── neo4j
│ └── neo4j_integration_test.go
├── oceanbase
│ └── oceanbase_integration_test.go
├── option.go
├── oracle
│ └── oracle_integration_test.go
├── postgres
│ └── postgres_integration_test.go
├── redis
│ └── redis_test.go
├── server.go
├── serverlessspark
│ └── serverless_spark_integration_test.go
├── source.go
├── spanner
│ └── spanner_integration_test.go
├── sqlite
│ └── sqlite_integration_test.go
├── tidb
│ └── tidb_integration_test.go
├── tool.go
├── trino
│ └── trino_integration_test.go
├── utility
│ └── wait_integration_test.go
├── valkey
│ └── valkey_test.go
└── yugabytedb
└── yugabytedb_integration_test.go
```
# Files
--------------------------------------------------------------------------------
/docs/en/how-to/connect-ide/cloud_sql_pg_admin_mcp.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "Cloud SQL for PostgreSQL Admin using MCP"
3 | type: docs
4 | weight: 3
5 | description: >
6 | Create and manage Cloud SQL for PostgreSQL (Admin) using Toolbox.
7 | ---
8 |
9 | This guide covers how to use [MCP Toolbox for Databases][toolbox] to expose your
10 | developer assistant tools to create and manage Cloud SQL for PostgreSQL
11 | instance, database and users:
12 |
13 | * [Cursor][cursor]
14 | * [Windsurf][windsurf] (Codium)
15 | * [Visual Studio Code][vscode] (Copilot)
16 | * [Cline][cline] (VS Code extension)
17 | * [Claude desktop][claudedesktop]
18 | * [Claude code][claudecode]
19 | * [Gemini CLI][geminicli]
20 | * [Gemini Code Assist][geminicodeassist]
21 |
22 | [toolbox]: https://github.com/googleapis/genai-toolbox
23 | [cursor]: #configure-your-mcp-client
24 | [windsurf]: #configure-your-mcp-client
25 | [vscode]: #configure-your-mcp-client
26 | [cline]: #configure-your-mcp-client
27 | [claudedesktop]: #configure-your-mcp-client
28 | [claudecode]: #configure-your-mcp-client
29 | [geminicli]: #configure-your-mcp-client
30 | [geminicodeassist]: #configure-your-mcp-client
31 |
32 | ## Before you begin
33 |
34 | 1. In the Google Cloud console, on the [project selector
35 | page](https://console.cloud.google.com/projectselector2/home/dashboard),
36 | select or create a Google Cloud project.
37 |
38 | 1. [Make sure that billing is enabled for your Google Cloud
39 | project](https://cloud.google.com/billing/docs/how-to/verify-billing-enabled#confirm_billing_is_enabled_on_a_project).
40 |
41 | 1. Grant the necessary IAM roles to the user that will be running the MCP
42 | server. The tools available will depend on the roles granted:
43 | * `roles/cloudsql.viewer`: Provides read-only access to resources.
44 | * `get_instance`
45 | * `list_instances`
46 | * `list_databases`
47 | * `wait_for_operation`
48 | * `roles/cloudsql.editor`: Provides permissions to manage existing resources.
49 | * All `viewer` tools
50 | * `create_database`
51 | * `roles/cloudsql.admin`: Provides full control over all resources.
52 | * All `editor` and `viewer` tools
53 | * `create_instance`
54 | * `create_user`
55 |
56 | ## Install MCP Toolbox
57 |
58 | 1. Download the latest version of Toolbox as a binary. Select the [correct
59 | binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
60 | to your OS and CPU architecture. You are required to use Toolbox version
61 | V0.15.0+:
62 |
63 | <!-- {x-release-please-start-version} -->
64 | {{< tabpane persist=header >}}
65 | {{< tab header="linux/amd64" lang="bash" >}}
66 | curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/linux/amd64/toolbox
67 | {{< /tab >}}
68 |
69 | {{< tab header="darwin/arm64" lang="bash" >}}
70 | curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/arm64/toolbox
71 | {{< /tab >}}
72 |
73 | {{< tab header="darwin/amd64" lang="bash" >}}
74 | curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/darwin/amd64/toolbox
75 | {{< /tab >}}
76 |
77 | {{< tab header="windows/amd64" lang="bash" >}}
78 | curl -O https://storage.googleapis.com/genai-toolbox/v0.15.0/windows/amd64/toolbox.exe
79 | {{< /tab >}}
80 | {{< /tabpane >}}
81 | <!-- {x-release-please-end} -->
82 |
83 | 1. Make the binary executable:
84 |
85 | ```bash
86 | chmod +x toolbox
87 | ```
88 |
89 | 1. Verify the installation:
90 |
91 | ```bash
92 | ./toolbox --version
93 | ```
94 |
95 | ## Configure your MCP Client
96 |
97 | {{< tabpane text=true >}}
98 | {{% tab header="Claude code" lang="en" %}}
99 |
100 | 1. Install [Claude
101 | Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
102 | 1. Create a `.mcp.json` file in your project root if it doesn't exist.
103 | 1. Add the following configuration and save:
104 |
105 | ```json
106 | {
107 | "mcpServers": {
108 | "cloud-sql-postgres-admin": {
109 | "command": "./PATH/TO/toolbox",
110 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
111 | "env": {
112 | }
113 | }
114 | }
115 | }
116 | ```
117 |
118 | 1. Restart Claude code to apply the new configuration.
119 | {{% /tab %}}
120 |
121 | {{% tab header="Claude desktop" lang="en" %}}
122 |
123 | 1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
124 | 1. Under the Developer tab, tap Edit Config to open the configuration file.
125 | 1. Add the following configuration and save:
126 |
127 | ```json
128 | {
129 | "mcpServers": {
130 | "cloud-sql-postgres-admin": {
131 | "command": "./PATH/TO/toolbox",
132 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
133 | "env": {
134 | }
135 | }
136 | }
137 | }
138 | ```
139 |
140 | 1. Restart Claude desktop.
141 | 1. From the new chat screen, you should see a hammer (MCP) icon appear with the
142 | new MCP server available.
143 | {{% /tab %}}
144 |
145 | {{% tab header="Cline" lang="en" %}}
146 |
147 | 1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap
148 | the **MCP Servers** icon.
149 | 1. Tap Configure MCP Servers to open the configuration file.
150 | 1. Add the following configuration and save:
151 |
152 | ```json
153 | {
154 | "mcpServers": {
155 | "cloud-sql-postgres-admin": {
156 | "command": "./PATH/TO/toolbox",
157 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
158 | "env": {
159 | }
160 | }
161 | }
162 | }
163 | ```
164 |
165 | 1. You should see a green active status after the server is successfully
166 | connected.
167 | {{% /tab %}}
168 |
169 | {{% tab header="Cursor" lang="en" %}}
170 |
171 | 1. Create a `.cursor` directory in your project root if it doesn't exist.
172 | 1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
173 | 1. Add the following configuration and save:
174 |
175 | ```json
176 | {
177 | "mcpServers": {
178 | "cloud-sql-postgres-admin": {
179 | "command": "./PATH/TO/toolbox",
180 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
181 | "env": {
182 | }
183 | }
184 | }
185 | }
186 | ```
187 |
188 | 1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor
189 | Settings > MCP**. You should see a green active status after the server is
190 | successfully connected.
191 | {{% /tab %}}
192 |
193 | {{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
194 |
195 | 1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and
196 | create a `.vscode` directory in your project root if it doesn't exist.
197 | 1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
198 | 1. Add the following configuration and save:
199 |
200 | ```json
201 | {
202 | "servers": {
203 | "cloud-sql-postgres-admin": {
204 | "command": "./PATH/TO/toolbox",
205 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
206 | "env": {
207 | }
208 | }
209 | }
210 | }
211 | ```
212 |
213 | {{% /tab %}}
214 |
215 | {{% tab header="Windsurf" lang="en" %}}
216 |
217 | 1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the
218 | Cascade assistant.
219 | 1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
220 | 1. Add the following configuration and save:
221 |
222 | ```json
223 | {
224 | "mcpServers": {
225 | "cloud-sql-postgres-admin": {
226 | "command": "./PATH/TO/toolbox",
227 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
228 | "env": {
229 | }
230 | }
231 | }
232 | }
233 | ```
234 |
235 | {{% /tab %}}
236 |
237 | {{% tab header="Gemini CLI" lang="en" %}}
238 |
239 | 1. Install the [Gemini
240 | CLI](https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart).
241 | 1. In your working directory, create a folder named `.gemini`. Within it,
242 | create a `settings.json` file.
243 | 1. Add the following configuration and save:
244 |
245 | ```json
246 | {
247 | "mcpServers": {
248 | "cloud-sql-postgres-admin": {
249 | "command": "./PATH/TO/toolbox",
250 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
251 | "env": {
252 | }
253 | }
254 | }
255 | }
256 | ```
257 | {{% /tab %}}
258 |
259 | {{% tab header="Gemini Code Assist" lang="en" %}}
260 |
261 | 1. Install the [Gemini Code
262 | Assist](https://marketplace.visualstudio.com/items?itemName=Google.geminicodeassist)
263 | extension in Visual Studio Code.
264 | 1. Enable Agent Mode in Gemini Code Assist chat.
265 | 1. In your working directory, create a folder named `.gemini`. Within it,
266 | create a `settings.json` file.
267 | 1. Add the following configuration and save:
268 |
269 | ```json
270 | {
271 | "mcpServers": {
272 | "cloud-sql-postgres-admin": {
273 | "command": "./PATH/TO/toolbox",
274 | "args": ["--prebuilt","cloud-sql-postgres-admin","--stdio"],
275 | "env": {
276 | }
277 | }
278 | }
279 | }
280 | ```
281 | {{% /tab %}}
282 | {{< /tabpane >}}
283 |
284 | ## Use Tools
285 |
286 | Your AI tool is now connected to Cloud SQL for PostgreSQL using MCP.
287 |
288 | The `cloud-sql-postgres-admin` server provides tools for managing your Cloud SQL
289 | instances and interacting with your database:
290 | * **create_instance**: Creates a new Cloud SQL for PostgreSQL instance.
291 | * **get_instance**: Gets information about a Cloud SQL instance.
292 | * **list_instances**: Lists Cloud SQL instances in a project.
293 | * **create_database**: Creates a new database in a Cloud SQL instance.
294 | * **list_databases**: Lists all databases for a Cloud SQL instance.
295 | * **create_user**: Creates a new user in a Cloud SQL instance.
296 | * **wait_for_operation**: Waits for a Cloud SQL operation to complete.
297 |
298 | {{< notice note >}}
299 | Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
300 | will adapt to the tools available, so this shouldn't affect most users.
301 | {{< /notice >}}
302 |
```
--------------------------------------------------------------------------------
/docs/en/resources/authServices/_index.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "AuthServices"
3 | type: docs
4 | weight: 1
5 | description: >
6 | AuthServices represent services that handle authentication and authorization.
7 | ---
8 |
9 | AuthServices represent services that handle authentication and authorization. It
10 | can primarily be used by [Tools](../tools/) in two different ways:
11 |
12 | - [**Authorized Invocation**][auth-invoke] is when a tool
13 | is validated by the auth service before the call can be invoked. Toolbox
14 | will reject any calls that fail to validate or have an invalid token.
15 | - [**Authenticated Parameters**][auth-params] replace the value of a parameter
16 | with a field from an [OIDC][openid-claims] claim. Toolbox will automatically
17 | resolve the ID token provided by the client and replace the parameter in the
18 | tool call.
19 |
20 | [openid-claims]: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
21 | [auth-invoke]: ../tools/#authorized-invocations
22 | [auth-params]: ../tools/#authenticated-parameters
23 |
24 | ## Example
25 |
26 | The following configurations are placed at the top level of a `tools.yaml` file.
27 |
28 | {{< notice tip >}}
29 | If you are accessing Toolbox with multiple applications, each
30 | application should register their own Client ID even if they use the same
31 | "kind" of auth provider.
32 | {{< /notice >}}
33 |
34 | ```yaml
35 | authServices:
36 | my_auth_app_1:
37 | kind: google
38 | clientId: ${YOUR_CLIENT_ID_1}
39 | my_auth_app_2:
40 | kind: google
41 | clientId: ${YOUR_CLIENT_ID_2}
42 | ```
43 |
44 | {{< notice tip >}}
45 | Use environment variable replacement with the format ${ENV_NAME}
46 | instead of hardcoding your secrets into the configuration file.
47 | {{< /notice >}}
48 |
49 | After you've configured an `authService` you'll, need to reference it in the
50 | configuration for each tool that should use it:
51 |
52 | - **Authorized Invocations** for authorizing a tool call, [use the
53 | `authRequired` field in a tool config][auth-invoke]
54 | - **Authenticated Parameters** for using the value from a OIDC claim, [use the
55 | `authServices` field in a parameter config][auth-params]
56 |
57 | ## Specifying ID Tokens from Clients
58 |
59 | After [configuring](#example) your `authServices` section, use a Toolbox SDK to
60 | add your ID tokens to the header of a Tool invocation request. When specifying a
61 | token you will provide a function (that returns an id). This function is called
62 | when the tool is invoked. This allows you to cache and refresh the ID token as
63 | needed.
64 |
65 | The primary method for providing these getters is via the `auth_token_getters`
66 | parameter when loading tools, or the `add_auth_token_getter`() /
67 | `add_auth_token_getters()` methods on a loaded tool object.
68 |
69 | ### Specifying tokens during load
70 |
71 | #### Python
72 |
73 | Use the [Python SDK](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main).
74 |
75 | {{< tabpane persist=header >}}
76 | {{< tab header="Core" lang="Python" >}}
77 | import asyncio
78 | from toolbox_core import ToolboxClient
79 |
80 | async def get_auth_token():
81 | # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow)
82 | # This example just returns a placeholder. Replace with your actual token retrieval.
83 | return "YOUR_ID_TOKEN" # Placeholder
84 |
85 | async def main():
86 | async with ToolboxClient("<http://127.0.0.1:5000>") as toolbox:
87 | auth_tool = await toolbox.load_tool(
88 | "get_sensitive_data",
89 | auth_token_getters={"my_auth_app_1": get_auth_token}
90 | )
91 | result = await auth_tool(param="value")
92 | print(result)
93 |
94 | if **name** == "**main**":
95 | asyncio.run(main())
96 | {{< /tab >}}
97 | {{< tab header="LangChain" lang="Python" >}}
98 | import asyncio
99 | from toolbox_langchain import ToolboxClient
100 |
101 | async def get_auth_token():
102 | # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow)
103 | # This example just returns a placeholder. Replace with your actual token retrieval.
104 | return "YOUR_ID_TOKEN" # Placeholder
105 |
106 | async def main():
107 | toolbox = ToolboxClient("<http://127.0.0.1:5000>")
108 |
109 | auth_tool = await toolbox.aload_tool(
110 | "get_sensitive_data",
111 | auth_token_getters={"my_auth_app_1": get_auth_token}
112 | )
113 | result = await auth_tool.ainvoke({"param": "value"})
114 | print(result)
115 |
116 | if **name** == "**main**":
117 | asyncio.run(main())
118 | {{< /tab >}}
119 | {{< tab header="Llamaindex" lang="Python" >}}
120 | import asyncio
121 | from toolbox_llamaindex import ToolboxClient
122 |
123 | async def get_auth_token():
124 | # ... Logic to retrieve ID token (e.g., from local storage, OAuth flow)
125 | # This example just returns a placeholder. Replace with your actual token retrieval.
126 | return "YOUR_ID_TOKEN" # Placeholder
127 |
128 | async def main():
129 | toolbox = ToolboxClient("<http://127.0.0.1:5000>")
130 |
131 | auth_tool = await toolbox.aload_tool(
132 | "get_sensitive_data",
133 | auth_token_getters={"my_auth_app_1": get_auth_token}
134 | )
135 | # result = await auth_tool.acall(param="value")
136 | # print(result.content)
137 |
138 | if **name** == "**main**":
139 | asyncio.run(main()){{< /tab >}}
140 | {{< /tabpane >}}
141 |
142 | #### Javascript/Typescript
143 |
144 | Use the [JS SDK](https://github.com/googleapis/mcp-toolbox-sdk-js/tree/main).
145 |
146 | ```javascript
147 | import { ToolboxClient } from '@toolbox-sdk/core';
148 |
149 | async function getAuthToken() {
150 | // ... Logic to retrieve ID token (e.g., from local storage, OAuth flow)
151 | // This example just returns a placeholder. Replace with your actual token retrieval.
152 | return "YOUR_ID_TOKEN" // Placeholder
153 | }
154 |
155 | const URL = 'http://127.0.0.1:5000';
156 | let client = new ToolboxClient(URL);
157 | const authTool = await client.loadTool("my-tool", {"my_auth_app_1": getAuthToken});
158 | const result = await authTool({param:"value"});
159 | console.log(result);
160 | print(result)
161 | ```
162 |
163 | #### Go
164 |
165 | Use the [Go SDK](https://github.com/googleapis/mcp-toolbox-sdk-go/tree/main).
166 |
167 | ```go
168 | import "github.com/googleapis/mcp-toolbox-sdk-go/core"
169 | import "fmt"
170 |
171 | func getAuthToken() string {
172 | // ... Logic to retrieve ID token (e.g., from local storage, OAuth flow)
173 | // This example just returns a placeholder. Replace with your actual token retrieval.
174 | return "YOUR_ID_TOKEN" // Placeholder
175 | }
176 |
177 | func main() {
178 | URL := 'http://127.0.0.1:5000'
179 | client, err := core.NewToolboxClient(URL)
180 | if err != nil {
181 | log.Fatalf("Failed to create Toolbox client: %v", err)
182 | }
183 | dynamicTokenSource := core.NewCustomTokenSource(getAuthToken)
184 | authTool, err := client.LoadTool(
185 | "my-tool",
186 | ctx,
187 | core.WithAuthTokenSource("my_auth_app_1", dynamicTokenSource))
188 | if err != nil {
189 | log.Fatalf("Failed to load tool: %v", err)
190 | }
191 | inputs := map[string]any{"param": "value"}
192 | result, err := authTool.Invoke(ctx, inputs)
193 | if err != nil {
194 | log.Fatalf("Failed to invoke tool: %v", err)
195 | }
196 | fmt.Println(result)
197 | }
198 | ```
199 |
200 | ### Specifying tokens for existing tools
201 |
202 | #### Python
203 |
204 | Use the [Python
205 | SDK](https://github.com/googleapis/mcp-toolbox-sdk-python/tree/main).
206 |
207 | {{< tabpane persist=header >}}
208 | {{< tab header="Core" lang="Python" >}}
209 | tools = await toolbox.load_toolset()
210 |
211 | # for a single token
212 |
213 | authorized_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token)
214 |
215 | # OR, if multiple tokens are needed
216 |
217 | authorized_tool = tools[0].add_auth_token_getters({
218 | "my_auth1": get_auth1_token,
219 | "my_auth2": get_auth2_token,
220 | })
221 | {{< /tab >}}
222 | {{< tab header="LangChain" lang="Python" >}}
223 | tools = toolbox.load_toolset()
224 |
225 | # for a single token
226 |
227 | authorized_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token)
228 |
229 | # OR, if multiple tokens are needed
230 |
231 | authorized_tool = tools[0].add_auth_token_getters({
232 | "my_auth1": get_auth1_token,
233 | "my_auth2": get_auth2_token,
234 | })
235 | {{< /tab >}}
236 | {{< tab header="Llamaindex" lang="Python" >}}
237 | tools = toolbox.load_toolset()
238 |
239 | # for a single token
240 |
241 | authorized_tool = tools[0].add_auth_token_getter("my_auth", get_auth_token)
242 |
243 | # OR, if multiple tokens are needed
244 |
245 | authorized_tool = tools[0].add_auth_token_getters({
246 | "my_auth1": get_auth1_token,
247 | "my_auth2": get_auth2_token,
248 | })
249 | {{< /tab >}}
250 | {{< /tabpane >}}
251 |
252 | #### Javascript/Typescript
253 |
254 | Use the [JS SDK](https://github.com/googleapis/mcp-toolbox-sdk-js/tree/main).
255 |
256 | ```javascript
257 | const URL = 'http://127.0.0.1:5000';
258 | let client = new ToolboxClient(URL);
259 | let tool = await client.loadTool("my-tool")
260 |
261 | // for a single token
262 | const authorizedTool = tool.addAuthTokenGetter("my_auth", get_auth_token)
263 |
264 | // OR, if multiple tokens are needed
265 | const multiAuthTool = tool.addAuthTokenGetters({
266 | "my_auth_1": getAuthToken1,
267 | "my_auth_2": getAuthToken2,
268 | })
269 |
270 | ```
271 |
272 | #### Go
273 |
274 | Use the [Go SDK](https://github.com/googleapis/mcp-toolbox-sdk-go/tree/main).
275 |
276 | ```go
277 | import "github.com/googleapis/mcp-toolbox-sdk-go/core"
278 |
279 | func main() {
280 | URL := 'http://127.0.0.1:5000'
281 | client, err := core.NewToolboxClient(URL)
282 | if err != nil {
283 | log.Fatalf("Failed to create Toolbox client: %v", err)
284 | }
285 | tool, err := client.LoadTool("my-tool", ctx))
286 | if err != nil {
287 | log.Fatalf("Failed to load tool: %v", err)
288 | }
289 | dynamicTokenSource1 := core.NewCustomTokenSource(getAuthToken1)
290 | dynamicTokenSource2 := core.NewCustomTokenSource(getAuthToken1)
291 |
292 | // For a single token
293 | authTool, err := tool.ToolFrom(
294 | core.WithAuthTokenSource("my-auth", dynamicTokenSource),
295 | )
296 |
297 | // OR, if multiple tokens are needed
298 | authTool, err := tool.ToolFrom(
299 | core.WithAuthTokenSource("my-auth_1", dynamicTokenSource1),
300 | core.WithAuthTokenSource("my-auth_2", dynamicTokenSource2),
301 | )
302 | }
303 | ```
304 |
305 | ## Kinds of Auth Services
306 |
```
--------------------------------------------------------------------------------
/docs/en/how-to/connect-ide/neo4j_mcp.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: Neo4j using MCP
3 | type: docs
4 | weight: 2
5 | description: "Connect your IDE to Neo4j using Toolbox."
6 | ---
7 |
8 | [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
9 | an open protocol for connecting Large Language Models (LLMs) to data sources
10 | like Neo4j. This guide covers how to use [MCP Toolbox for Databases][toolbox] to
11 | expose your developer assistant tools to a Neo4j instance:
12 |
13 | * [Cursor][cursor]
14 | * [Windsurf][windsurf] (Codium)
15 | * [Visual Studio Code][vscode] (Copilot)
16 | * [Cline][cline] (VS Code extension)
17 | * [Claude desktop][claudedesktop]
18 | * [Claude code][claudecode]
19 | * [Gemini CLI][geminicli]
20 | * [Gemini Code Assist][geminicodeassist]
21 |
22 | [toolbox]: https://github.com/googleapis/genai-toolbox
23 | [cursor]: #configure-your-mcp-client
24 | [windsurf]: #configure-your-mcp-client
25 | [vscode]: #configure-your-mcp-client
26 | [cline]: #configure-your-mcp-client
27 | [claudedesktop]: #configure-your-mcp-client
28 | [claudecode]: #configure-your-mcp-client
29 | [geminicli]: #configure-your-mcp-client
30 | [geminicodeassist]: #configure-your-mcp-client
31 |
32 | ## Set up the database
33 |
34 | 1. [Create or select a Neo4j
35 | instance.](https://neo4j.com/cloud/platform/aura-graph-database/)
36 |
37 | ## Install MCP Toolbox
38 |
39 | 1. Download the latest version of Toolbox as a binary. Select the [correct
40 | binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
41 | to your OS and CPU architecture. You are required to use Toolbox version
42 | v0.15.0+:
43 |
44 | <!-- {x-release-please-start-version} -->
45 | {{< tabpane persist=header >}}
46 | {{< tab header="linux/amd64" lang="bash" >}}
47 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/linux/amd64/toolbox
48 | {{< /tab >}}
49 |
50 | {{< tab header="darwin/arm64" lang="bash" >}}
51 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/arm64/toolbox
52 | {{< /tab >}}
53 |
54 | {{< tab header="darwin/amd64" lang="bash" >}}
55 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/amd64/toolbox
56 | {{< /tab >}}
57 |
58 | {{< tab header="windows/amd64" lang="bash" >}}
59 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/windows/amd64/toolbox.exe
60 | {{< /tab >}}
61 | {{< /tabpane >}}
62 | <!-- {x-release-please-end} -->
63 |
64 | 1. Make the binary executable:
65 |
66 | ```bash
67 | chmod +x toolbox
68 | ```
69 |
70 | 1. Verify the installation:
71 |
72 | ```bash
73 | ./toolbox --version
74 | ```
75 |
76 | ## Configure your MCP Client
77 |
78 | {{< tabpane text=true >}}
79 | {{% tab header="Claude code" lang="en" %}}
80 |
81 | 1. Install [Claude
82 | Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
83 | 1. Create a `.mcp.json` file in your project root if it doesn't exist.
84 | 1. Add the following configuration, replace the environment variables with your
85 | values, and save:
86 |
87 | ```json
88 | {
89 | "mcpServers": {
90 | "neo4j": {
91 | "command": "./PATH/TO/toolbox",
92 | "args": ["--prebuilt","neo4j","--stdio"],
93 | "env": {
94 | "NEO4J_URI": "",
95 | "NEO4J_DATABASE": "",
96 | "NEO4J_USERNAME": "",
97 | "NEO4J_PASSWORD": ""
98 | }
99 | }
100 | }
101 | }
102 | ```
103 |
104 | 1. Restart Claude code to apply the new configuration.
105 | {{% /tab %}}
106 | {{% tab header="Claude desktop" lang="en" %}}
107 |
108 | 1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
109 | 1. Under the Developer tab, tap Edit Config to open the configuration file.
110 | 1. Add the following configuration, replace the environment variables with your
111 | values, and save:
112 |
113 | ```json
114 | {
115 | "mcpServers": {
116 | "neo4j": {
117 | "command": "./PATH/TO/toolbox",
118 | "args": ["--prebuilt","neo4j","--stdio"],
119 | "env": {
120 | "NEO4J_URI": "",
121 | "NEO4J_DATABASE": "",
122 | "NEO4J_USERNAME": "",
123 | "NEO4J_PASSWORD": ""
124 | }
125 | }
126 | }
127 | }
128 | ```
129 |
130 | 1. Restart Claude desktop.
131 | 1. From the new chat screen, you should see a hammer (MCP) icon appear with the
132 | new MCP server available.
133 | {{% /tab %}}
134 | {{% tab header="Cline" lang="en" %}}
135 |
136 | 1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and
137 | tap the **MCP Servers** icon.
138 | 1. Tap Configure MCP Servers to open the configuration file.
139 | 1. Add the following configuration, replace the environment variables with your
140 | values, and save:
141 |
142 | ```json
143 | {
144 | "mcpServers": {
145 | "neo4j": {
146 | "command": "./PATH/TO/toolbox",
147 | "args": ["--prebuilt","neo4j","--stdio"],
148 | "env": {
149 | "NEO4J_URI": "",
150 | "NEO4J_DATABASE": "",
151 | "NEO4J_USERNAME": "",
152 | "NEO4J_PASSWORD": ""
153 | }
154 | }
155 | }
156 | }
157 | ```
158 |
159 | 1. You should see a green active status after the server is successfully connected.
160 | {{% /tab %}}
161 | {{% tab header="Cursor" lang="en" %}}
162 |
163 | 1. Create a `.cursor` directory in your project root if it doesn't exist.
164 | 1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
165 | 1. Add the following configuration, replace the environment variables with your values, and save:
166 |
167 | ```json
168 | {
169 | "mcpServers": {
170 | "neo4j": {
171 | "command": "./PATH/TO/toolbox",
172 | "args": ["--prebuilt","neo4j","--stdio"],
173 | "env": {
174 | "NEO4J_URI": "",
175 | "NEO4J_DATABASE": "",
176 | "NEO4J_USERNAME": "",
177 | "NEO4J_PASSWORD": ""
178 | }
179 | }
180 | }
181 | }
182 | ```
183 |
184 | 1. Open [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor
185 | Settings > MCP**. You should see a green active status after the server is
186 | successfully connected.
187 | {{% /tab %}}
188 | {{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
189 |
190 | 1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and
191 | create a `.vscode` directory in your project root if it doesn't exist.
192 | 1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
193 | 1. Add the following configuration, replace the environment variables with your
194 | values, and save:
195 |
196 | ```json
197 | {
198 | "mcp" : {
199 | "servers": {
200 | "neo4j": {
201 | "command": "./PATH/TO/toolbox",
202 | "args": ["--prebuilt","neo4j","--stdio"],
203 | "env": {
204 | "NEO4J_URI": "",
205 | "NEO4J_DATABASE": "",
206 | "NEO4J_USERNAME": "",
207 | "NEO4J_PASSWORD": ""
208 | }
209 | }
210 | }
211 | }
212 | }
213 | ```
214 | {{% /tab %}}
215 | {{% tab header="Windsurf" lang="en" %}}
216 |
217 | 1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the
218 | Cascade assistant.
219 | 1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
220 | 1. Add the following configuration, replace the environment variables with your
221 | values, and save:
222 |
223 | ```json
224 | {
225 | "mcpServers": {
226 | "neo4j": {
227 | "command": "./PATH/TO/toolbox",
228 | "args": ["--prebuilt","neo4j","--stdio"],
229 | "env": {
230 | "NEO4J_URI": "",
231 | "NEO4J_DATABASE": "",
232 | "NEO4J_USERNAME": "",
233 | "NEO4J_PASSWORD": ""
234 | }
235 | }
236 | }
237 | }
238 | ```
239 | {{% /tab %}}
240 | {{% tab header="Gemini CLI" lang="en" %}}
241 |
242 | 1. Install the [Gemini
243 | CLI](https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart).
244 | 1. In your working directory, create a folder named `.gemini`. Within it,
245 | create a `settings.json` file.
246 | 1. Add the following configuration, replace the environment variables with your
247 | values, and then save:
248 |
249 | ```json
250 | {
251 | "mcpServers": {
252 | "neo4j": {
253 | "command": "./PATH/TO/toolbox",
254 | "args": ["--prebuilt","neo4j","--stdio"],
255 | "env": {
256 | "NEO4J_URI": "",
257 | "NEO4J_DATABASE": "",
258 | "NEO4J_USERNAME": "",
259 | "NEO4J_PASSWORD": ""
260 | }
261 | }
262 | }
263 | }
264 | ```
265 | {{% /tab %}}
266 | {{% tab header="Gemini Code Assist" lang="en" %}}
267 |
268 | 1. Install the [Gemini Code
269 | Assist](https://marketplace.visualstudio.com/items?itemName=Google.geminicodeassist)
270 | extension in Visual Studio Code.
271 | 1. Enable Agent Mode in Gemini Code Assist chat.
272 | 1. In your working directory, create a folder named `.gemini`. Within it,
273 | create a `settings.json` file.
274 | 1. Add the following configuration, replace the environment variables with your
275 | values, and then save:
276 |
277 | ```json
278 | {
279 | "mcpServers": {
280 | "neo4j": {
281 | "command": "./PATH/TO/toolbox",
282 | "args": ["--prebuilt","neo4j","--stdio"],
283 | "env": {
284 | "NEO4J_URI": "",
285 | "NEO4J_DATABASE": "",
286 | "NEO4J_USERNAME": "",
287 | "NEO4J_PASSWORD": ""
288 | }
289 | }
290 | }
291 | }
292 | ```
293 | {{% /tab %}}
294 | {{< /tabpane >}}
295 |
296 | ## Use Tools
297 |
298 | Your AI tool is now connected to Neo4j using MCP. Try asking your AI assistant
299 | to get the graph schema or execute Cypher statements.
300 |
301 | The following tools are available to the LLM:
302 |
303 | 1. **get_schema**: extracts the complete database schema, including details
304 | about node labels, relationships, properties, constraints, and indexes.
305 | 1. **execute_cypher**: executes any arbitrary Cypher statement.
306 |
307 | {{< notice note >}}
308 | Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
309 | will adapt to the tools available, so this shouldn't affect most users.
310 | {{< /notice >}}
311 |
```
--------------------------------------------------------------------------------
/docs/en/how-to/connect-ide/mysql_mcp.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: MySQL using MCP
3 | type: docs
4 | weight: 2
5 | description: "Connect your IDE to MySQL using Toolbox."
6 | ---
7 |
8 | [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
9 | an open protocol for connecting Large Language Models (LLMs) to data sources
10 | like MySQL. This guide covers how to use [MCP Toolbox for Databases][toolbox] to
11 | expose your developer assistant tools to a MySQL instance:
12 |
13 | * [Cursor][cursor]
14 | * [Windsurf][windsurf] (Codium)
15 | * [Visual Studio Code][vscode] (Copilot)
16 | * [Cline][cline] (VS Code extension)
17 | * [Claude desktop][claudedesktop]
18 | * [Claude code][claudecode]
19 | * [Gemini CLI][geminicli]
20 | * [Gemini Code Assist][geminicodeassist]
21 |
22 | [toolbox]: https://github.com/googleapis/genai-toolbox
23 | [cursor]: #configure-your-mcp-client
24 | [windsurf]: #configure-your-mcp-client
25 | [vscode]: #configure-your-mcp-client
26 | [cline]: #configure-your-mcp-client
27 | [claudedesktop]: #configure-your-mcp-client
28 | [claudecode]: #configure-your-mcp-client
29 | [geminicli]: #configure-your-mcp-client
30 | [geminicodeassist]: #configure-your-mcp-client
31 |
32 | ## Set up the database
33 |
34 | 1. [Create or select a MySQL instance.](https://dev.mysql.com/downloads/installer/)
35 |
36 | ## Install MCP Toolbox
37 |
38 | 1. Download the latest version of Toolbox as a binary. Select the [correct
39 | binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
40 | to your OS and CPU architecture. You are required to use Toolbox version
41 | V0.10.0+:
42 |
43 | <!-- {x-release-please-start-version} -->
44 | {{< tabpane persist=header >}}
45 | {{< tab header="linux/amd64" lang="bash" >}}
46 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/linux/amd64/toolbox
47 | {{< /tab >}}
48 |
49 | {{< tab header="darwin/arm64" lang="bash" >}}
50 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/arm64/toolbox
51 | {{< /tab >}}
52 |
53 | {{< tab header="darwin/amd64" lang="bash" >}}
54 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/amd64/toolbox
55 | {{< /tab >}}
56 |
57 | {{< tab header="windows/amd64" lang="bash" >}}
58 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/windows/amd64/toolbox.exe
59 | {{< /tab >}}
60 | {{< /tabpane >}}
61 | <!-- {x-release-please-end} -->
62 |
63 | 1. Make the binary executable:
64 |
65 | ```bash
66 | chmod +x toolbox
67 | ```
68 |
69 | 1. Verify the installation:
70 |
71 | ```bash
72 | ./toolbox --version
73 | ```
74 |
75 | ## Configure your MCP Client
76 |
77 | {{< tabpane text=true >}}
78 | {{% tab header="Claude code" lang="en" %}}
79 |
80 | 1. Install [Claude
81 | Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
82 | 1. Create a `.mcp.json` file in your project root if it doesn't exist.
83 | 1. Add the following configuration, replace the environment variables with your
84 | values, and save:
85 |
86 | ```json
87 | {
88 | "mcpServers": {
89 | "mysql": {
90 | "command": "./PATH/TO/toolbox",
91 | "args": ["--prebuilt", "mysql", "--stdio"],
92 | "env": {
93 | "MYSQL_HOST": "",
94 | "MYSQL_PORT": "",
95 | "MYSQL_DATABASE": "",
96 | "MYSQL_USER": "",
97 | "MYSQL_PASSWORD": ""
98 | }
99 | }
100 | }
101 | }
102 | ```
103 |
104 | 1. Restart Claude code to apply the new configuration.
105 | {{% /tab %}}
106 | {{% tab header="Claude desktop" lang="en" %}}
107 |
108 | 1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
109 | 1. Under the Developer tab, tap Edit Config to open the configuration file.
110 | 1. Add the following configuration, replace the environment variables with your
111 | values, and save:
112 |
113 | ```json
114 | {
115 | "mcpServers": {
116 | "mysql": {
117 | "command": "./PATH/TO/toolbox",
118 | "args": ["--prebuilt", "mysql", "--stdio"],
119 | "env": {
120 | "MYSQL_HOST": "",
121 | "MYSQL_PORT": "",
122 | "MYSQL_DATABASE": "",
123 | "MYSQL_USER": "",
124 | "MYSQL_PASSWORD": ""
125 | }
126 | }
127 | }
128 | }
129 | ```
130 |
131 | 1. Restart Claude desktop.
132 | 1. From the new chat screen, you should see a hammer (MCP) icon appear with the
133 | new MCP server available.
134 | {{% /tab %}}
135 | {{% tab header="Cline" lang="en" %}}
136 |
137 | 1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and
138 | tap the **MCP Servers** icon.
139 | 1. Tap Configure MCP Servers to open the configuration file.
140 | 1. Add the following configuration, replace the environment variables with your
141 | values, and save:
142 |
143 | ```json
144 | {
145 | "mcpServers": {
146 | "mysql": {
147 | "command": "./PATH/TO/toolbox",
148 | "args": ["--prebuilt", "mysql", "--stdio"],
149 | "env": {
150 | "MYSQL_HOST": "",
151 | "MYSQL_PORT": "",
152 | "MYSQL_DATABASE": "",
153 | "MYSQL_USER": "",
154 | "MYSQL_PASSWORD": ""
155 | }
156 | }
157 | }
158 | }
159 | ```
160 |
161 | 1. You should see a green active status after the server is successfully
162 | connected.
163 | {{% /tab %}}
164 | {{% tab header="Cursor" lang="en" %}}
165 |
166 | 1. Create a `.cursor` directory in your project root if it doesn't exist.
167 | 1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
168 | 1. Add the following configuration, replace the environment variables with your
169 | values, and save:
170 |
171 | ```json
172 | {
173 | "mcpServers": {
174 | "mysql": {
175 | "command": "./PATH/TO/toolbox",
176 | "args": ["--prebuilt", "mysql", "--stdio"],
177 | "env": {
178 | "MYSQL_HOST": "",
179 | "MYSQL_PORT": "",
180 | "MYSQL_DATABASE": "",
181 | "MYSQL_USER": "",
182 | "MYSQL_PASSWORD": ""
183 | }
184 | }
185 | }
186 | }
187 | ```
188 |
189 | 1. Open [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor
190 | Settings > MCP**. You should see a green active status after the server is
191 | successfully connected.
192 | {{% /tab %}}
193 | {{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
194 |
195 | 1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and
196 | create a `.vscode` directory in your project root if it doesn't exist.
197 | 1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
198 | 1. Add the following configuration, replace the environment variables with your
199 | values, and save:
200 |
201 | ```json
202 | {
203 | "servers": {
204 | "mysql": {
205 | "command": "./PATH/TO/toolbox",
206 | "args": ["--prebuilt","mysql","--stdio"],
207 | "env": {
208 | "MYSQL_HOST": "",
209 | "MYSQL_PORT": "",
210 | "MYSQL_DATABASE": "",
211 | "MYSQL_USER": "",
212 | "MYSQL_PASSWORD": ""
213 | }
214 | }
215 | }
216 | }
217 | ```
218 | {{% /tab %}}
219 | {{% tab header="Windsurf" lang="en" %}}
220 |
221 | 1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the
222 | Cascade assistant.
223 | 1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
224 | 1. Add the following configuration, replace the environment variables with your
225 | values, and save:
226 |
227 | ```json
228 | {
229 | "mcpServers": {
230 | "mysql": {
231 | "command": "./PATH/TO/toolbox",
232 | "args": ["--prebuilt","mysql","--stdio"],
233 | "env": {
234 | "MYSQL_HOST": "",
235 | "MYSQL_PORT": "",
236 | "MYSQL_DATABASE": "",
237 | "MYSQL_USER": "",
238 | "MYSQL_PASSWORD": ""
239 | }
240 | }
241 | }
242 | }
243 | ```
244 | {{% /tab %}}
245 | {{% tab header="Gemini CLI" lang="en" %}}
246 |
247 | 1. Install the [Gemini
248 | CLI](https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart).
249 | 1. In your working directory, create a folder named `.gemini`. Within it,
250 | create a `settings.json` file.
251 | 1. Add the following configuration, replace the environment variables with your
252 | values, and then save:
253 |
254 | ```json
255 | {
256 | "mcpServers": {
257 | "mysql": {
258 | "command": "./PATH/TO/toolbox",
259 | "args": ["--prebuilt","mysql","--stdio"],
260 | "env": {
261 | "MYSQL_HOST": "",
262 | "MYSQL_PORT": "",
263 | "MYSQL_DATABASE": "",
264 | "MYSQL_USER": "",
265 | "MYSQL_PASSWORD": ""
266 | }
267 | }
268 | }
269 | }
270 | ```
271 | {{% /tab %}}
272 | {{% tab header="Gemini Code Assist" lang="en" %}}
273 |
274 | 1. Install the [Gemini Code
275 | Assist](https://marketplace.visualstudio.com/items?itemName=Google.geminicodeassist)
276 | extension in Visual Studio Code.
277 | 1. Enable Agent Mode in Gemini Code Assist chat.
278 | 1. In your working directory, create a folder named `.gemini`. Within it,
279 | create a `settings.json` file.
280 | 1. Add the following configuration, replace the environment variables with your
281 | values, and then save:
282 |
283 | ```json
284 | {
285 | "mcpServers": {
286 | "mysql": {
287 | "command": "./PATH/TO/toolbox",
288 | "args": ["--prebuilt","mysql","--stdio"],
289 | "env": {
290 | "MYSQL_HOST": "",
291 | "MYSQL_PORT": "",
292 | "MYSQL_DATABASE": "",
293 | "MYSQL_USER": "",
294 | "MYSQL_PASSWORD": ""
295 | }
296 | }
297 | }
298 | }
299 | ```
300 | {{% /tab %}}
301 | {{< /tabpane >}}
302 |
303 | ## Use Tools
304 |
305 | Your AI tool is now connected to MySQL using MCP. Try asking your AI assistant
306 | to list tables, create a table, or define and execute other SQL statements.
307 |
308 | The following tools are available to the LLM:
309 |
310 | 1. **list_tables**: lists tables and descriptions
311 | 1. **execute_sql**: execute any SQL statement
312 |
313 | {{< notice note >}}
314 | Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
315 | will adapt to the tools available, so this shouldn't affect most users.
316 | {{< /notice >}}
317 |
```
--------------------------------------------------------------------------------
/docs/en/how-to/connect-ide/mssql_mcp.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: SQL Server using MCP
3 | type: docs
4 | weight: 2
5 | description: "Connect your IDE to SQL Server using Toolbox."
6 | ---
7 |
8 | [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
9 | an open protocol for connecting Large Language Models (LLMs) to data sources
10 | like SQL Server. This guide covers how to use [MCP Toolbox for
11 | Databases][toolbox] to expose your developer assistant tools to a SQL Server
12 | instance:
13 |
14 | * [Cursor][cursor]
15 | * [Windsurf][windsurf] (Codium)
16 | * [Visual Studio Code][vscode] (Copilot)
17 | * [Cline][cline] (VS Code extension)
18 | * [Claude desktop][claudedesktop]
19 | * [Claude code][claudecode]
20 | * [Gemini CLI][geminicli]
21 | * [Gemini Code Assist][geminicodeassist]
22 |
23 | [toolbox]: https://github.com/googleapis/genai-toolbox
24 | [cursor]: #configure-your-mcp-client
25 | [windsurf]: #configure-your-mcp-client
26 | [vscode]: #configure-your-mcp-client
27 | [cline]: #configure-your-mcp-client
28 | [claudedesktop]: #configure-your-mcp-client
29 | [claudecode]: #configure-your-mcp-client
30 | [geminicli]: #configure-your-mcp-client
31 | [geminicodeassist]: #configure-your-mcp-client
32 |
33 | ## Set up the database
34 |
35 | 1. [Create or select a SQL Server
36 | instance.](https://www.microsoft.com/en-us/sql-server/sql-server-downloads)
37 |
38 | ## Install MCP Toolbox
39 |
40 | 1. Download the latest version of Toolbox as a binary. Select the [correct
41 | binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
42 | to your OS and CPU architecture. You are required to use Toolbox version
43 | V0.10.0+:
44 |
45 | <!-- {x-release-please-start-version} -->
46 | {{< tabpane persist=header >}}
47 | {{< tab header="linux/amd64" lang="bash" >}}
48 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/linux/amd64/toolbox
49 | {{< /tab >}}
50 |
51 | {{< tab header="darwin/arm64" lang="bash" >}}
52 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/arm64/toolbox
53 | {{< /tab >}}
54 |
55 | {{< tab header="darwin/amd64" lang="bash" >}}
56 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/amd64/toolbox
57 | {{< /tab >}}
58 |
59 | {{< tab header="windows/amd64" lang="bash" >}}
60 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/windows/amd64/toolbox.exe
61 | {{< /tab >}}
62 | {{< /tabpane >}}
63 | <!-- {x-release-please-end} -->
64 |
65 | 1. Make the binary executable:
66 |
67 | ```bash
68 | chmod +x toolbox
69 | ```
70 |
71 | 1. Verify the installation:
72 |
73 | ```bash
74 | ./toolbox --version
75 | ```
76 |
77 | ## Configure your MCP Client
78 |
79 | {{< tabpane text=true >}}
80 | {{% tab header="Claude code" lang="en" %}}
81 |
82 | 1. Install [Claude
83 | Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
84 | 1. Create a `.mcp.json` file in your project root if it doesn't exist.
85 | 1. Add the following configuration, replace the environment variables with your
86 | values, and save:
87 |
88 | ```json
89 | {
90 | "mcpServers": {
91 | "sqlserver": {
92 | "command": "./PATH/TO/toolbox",
93 | "args": ["--prebuilt","mssql","--stdio"],
94 | "env": {
95 | "MSSQL_HOST": "",
96 | "MSSQL_PORT": "",
97 | "MSSQL_DATABASE": "",
98 | "MSSQL_USER": "",
99 | "MSSQL_PASSWORD": ""
100 | }
101 | }
102 | }
103 | }
104 | ```
105 |
106 | 1. Restart Claude code to apply the new configuration.
107 | {{% /tab %}}
108 | {{% tab header="Claude desktop" lang="en" %}}
109 |
110 | 1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
111 | 1. Under the Developer tab, tap Edit Config to open the configuration file.
112 | 1. Add the following configuration, replace the environment variables with your
113 | values, and save:
114 |
115 | ```json
116 | {
117 | "mcpServers": {
118 | "sqlserver": {
119 | "command": "./PATH/TO/toolbox",
120 | "args": ["--prebuilt","mssql","--stdio"],
121 | "env": {
122 | "MSSQL_HOST": "",
123 | "MSSQL_PORT": "",
124 | "MSSQL_DATABASE": "",
125 | "MSSQL_USER": "",
126 | "MSSQL_PASSWORD": ""
127 | }
128 | }
129 | }
130 | }
131 | ```
132 |
133 | 1. Restart Claude desktop.
134 | 1. From the new chat screen, you should see a hammer (MCP) icon appear with the
135 | new MCP server available.
136 | {{% /tab %}}
137 | {{% tab header="Cline" lang="en" %}}
138 |
139 | 1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and
140 | tap the **MCP Servers** icon.
141 | 1. Tap Configure MCP Servers to open the configuration file.
142 | 1. Add the following configuration, replace the environment variables with your
143 | values, and save:
144 |
145 | ```json
146 | {
147 | "mcpServers": {
148 | "sqlserver": {
149 | "command": "./PATH/TO/toolbox",
150 | "args": ["--prebuilt","mssql","--stdio"],
151 | "env": {
152 | "MSSQL_HOST": "",
153 | "MSSQL_PORT": "",
154 | "MSSQL_DATABASE": "",
155 | "MSSQL_USER": "",
156 | "MSSQL_PASSWORD": ""
157 | }
158 | }
159 | }
160 | }
161 | ```
162 |
163 | 1. You should see a green active status after the server is successfully
164 | connected.
165 | {{% /tab %}}
166 | {{% tab header="Cursor" lang="en" %}}
167 |
168 | 1. Create a `.cursor` directory in your project root if it doesn't exist.
169 | 1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
170 | 1. Add the following configuration, replace the environment variables with your
171 | values, and save:
172 |
173 | ```json
174 | {
175 | "mcpServers": {
176 | "sqlserver": {
177 | "command": "./PATH/TO/toolbox",
178 | "args": ["--prebuilt","mssql","--stdio"],
179 | "env": {
180 | "MSSQL_HOST": "",
181 | "MSSQL_PORT": "",
182 | "MSSQL_DATABASE": "",
183 | "MSSQL_USER": "",
184 | "MSSQL_PASSWORD": ""
185 | }
186 | }
187 | }
188 | }
189 | ```
190 |
191 | 1. Open [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor
192 | Settings > MCP**. You should see a green active status after the server is
193 | successfully connected.
194 | {{% /tab %}}
195 | {{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
196 |
197 | 1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and
198 | create a `.vscode` directory in your project root if it doesn't exist.
199 | 1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
200 | 1. Add the following configuration, replace the environment variables with your
201 | values, and save:
202 |
203 | ```json
204 | {
205 | "servers": {
206 | "mssql": {
207 | "command": "./PATH/TO/toolbox",
208 | "args": ["--prebuilt","mssql","--stdio"],
209 | "env": {
210 | "MSSQL_HOST": "",
211 | "MSSQL_PORT": "",
212 | "MSSQL_DATABASE": "",
213 | "MSSQL_USER": "",
214 | "MSSQL_PASSWORD": ""
215 | }
216 | }
217 | }
218 | }
219 | ```
220 | {{% /tab %}}
221 | {{% tab header="Windsurf" lang="en" %}}
222 |
223 | 1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the
224 | Cascade assistant.
225 | 1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
226 | 1. Add the following configuration, replace the environment variables with your
227 | values, and save:
228 |
229 | ```json
230 | {
231 | "mcpServers": {
232 | "sqlserver": {
233 | "command": "./PATH/TO/toolbox",
234 | "args": ["--prebuilt","mssql","--stdio"],
235 | "env": {
236 | "MSSQL_HOST": "",
237 | "MSSQL_PORT": "",
238 | "MSSQL_DATABASE": "",
239 | "MSSQL_USER": "",
240 | "MSSQL_PASSWORD": ""
241 | }
242 | }
243 | }
244 | }
245 | ```
246 | {{% /tab %}}
247 | {{% tab header="Gemini CLI" lang="en" %}}
248 |
249 | 1. Install the [Gemini
250 | CLI](https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart).
251 | 1. In your working directory, create a folder named `.gemini`. Within it,
252 | create a `settings.json` file.
253 | 1. Add the following configuration, replace the environment variables with your
254 | values, and then save:
255 |
256 | ```json
257 | {
258 | "mcpServers": {
259 | "sqlserver": {
260 | "command": "./PATH/TO/toolbox",
261 | "args": ["--prebuilt","mssql","--stdio"],
262 | "env": {
263 | "MSSQL_HOST": "",
264 | "MSSQL_PORT": "",
265 | "MSSQL_DATABASE": "",
266 | "MSSQL_USER": "",
267 | "MSSQL_PASSWORD": ""
268 | }
269 | }
270 | }
271 | }
272 | ```
273 | {{% /tab %}}
274 | {{% tab header="Gemini Code Assist" lang="en" %}}
275 |
276 | 1. Install the [Gemini Code
277 | Assist](https://marketplace.visualstudio.com/items?itemName=Google.geminicodeassist)
278 | extension in Visual Studio Code.
279 | 1. Enable Agent Mode in Gemini Code Assist chat.
280 | 1. In your working directory, create a folder named `.gemini`. Within it,
281 | create a `settings.json` file.
282 | 1. Add the following configuration, replace the environment variables with your
283 | values, and then save:
284 |
285 | ```json
286 | {
287 | "mcpServers": {
288 | "sqlserver": {
289 | "command": "./PATH/TO/toolbox",
290 | "args": ["--prebuilt","mssql","--stdio"],
291 | "env": {
292 | "MSSQL_HOST": "",
293 | "MSSQL_PORT": "",
294 | "MSSQL_DATABASE": "",
295 | "MSSQL_USER": "",
296 | "MSSQL_PASSWORD": ""
297 | }
298 | }
299 | }
300 | }
301 | ```
302 | {{% /tab %}}
303 | {{< /tabpane >}}
304 |
305 | ## Use Tools
306 |
307 | Your AI tool is now connected to SQL Server using MCP. Try asking your AI
308 | assistant to list tables, create a table, or define and execute other SQL
309 | statements.
310 |
311 | The following tools are available to the LLM:
312 |
313 | 1. **list_tables**: lists tables and descriptions
314 | 1. **execute_sql**: execute any SQL statement
315 |
316 | {{< notice note >}}
317 | Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
318 | will adapt to the tools available, so this shouldn't affect most users.
319 | {{< /notice >}}
320 |
```
--------------------------------------------------------------------------------
/tests/alloydbpg/alloydb_pg_integration_test.go:
--------------------------------------------------------------------------------
```go
1 | // Copyright 2024 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package alloydbpg
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "net"
21 | "os"
22 | "regexp"
23 | "strings"
24 | "testing"
25 | "time"
26 |
27 | "cloud.google.com/go/alloydbconn"
28 | "github.com/google/uuid"
29 | "github.com/googleapis/genai-toolbox/internal/testutils"
30 | "github.com/googleapis/genai-toolbox/tests"
31 | "github.com/jackc/pgx/v5/pgxpool"
32 | )
33 |
34 | var (
35 | AlloyDBPostgresSourceKind = "alloydb-postgres"
36 | AlloyDBPostgresToolKind = "postgres-sql"
37 | AlloyDBPostgresProject = os.Getenv("ALLOYDB_POSTGRES_PROJECT")
38 | AlloyDBPostgresRegion = os.Getenv("ALLOYDB_POSTGRES_REGION")
39 | AlloyDBPostgresCluster = os.Getenv("ALLOYDB_POSTGRES_CLUSTER")
40 | AlloyDBPostgresInstance = os.Getenv("ALLOYDB_POSTGRES_INSTANCE")
41 | AlloyDBPostgresDatabase = os.Getenv("ALLOYDB_POSTGRES_DATABASE")
42 | AlloyDBPostgresUser = os.Getenv("ALLOYDB_POSTGRES_USER")
43 | AlloyDBPostgresPass = os.Getenv("ALLOYDB_POSTGRES_PASS")
44 | )
45 |
46 | func getAlloyDBPgVars(t *testing.T) map[string]any {
47 | switch "" {
48 | case AlloyDBPostgresProject:
49 | t.Fatal("'ALLOYDB_POSTGRES_PROJECT' not set")
50 | case AlloyDBPostgresRegion:
51 | t.Fatal("'ALLOYDB_POSTGRES_REGION' not set")
52 | case AlloyDBPostgresCluster:
53 | t.Fatal("'ALLOYDB_POSTGRES_CLUSTER' not set")
54 | case AlloyDBPostgresInstance:
55 | t.Fatal("'ALLOYDB_POSTGRES_INSTANCE' not set")
56 | case AlloyDBPostgresDatabase:
57 | t.Fatal("'ALLOYDB_POSTGRES_DATABASE' not set")
58 | case AlloyDBPostgresUser:
59 | t.Fatal("'ALLOYDB_POSTGRES_USER' not set")
60 | case AlloyDBPostgresPass:
61 | t.Fatal("'ALLOYDB_POSTGRES_PASS' not set")
62 | }
63 | return map[string]any{
64 | "kind": AlloyDBPostgresSourceKind,
65 | "project": AlloyDBPostgresProject,
66 | "cluster": AlloyDBPostgresCluster,
67 | "instance": AlloyDBPostgresInstance,
68 | "region": AlloyDBPostgresRegion,
69 | "database": AlloyDBPostgresDatabase,
70 | "user": AlloyDBPostgresUser,
71 | "password": AlloyDBPostgresPass,
72 | }
73 | }
74 |
75 | // Copied over from alloydb_pg.go
76 | func getAlloyDBDialOpts(ipType string) ([]alloydbconn.DialOption, error) {
77 | switch strings.ToLower(ipType) {
78 | case "private":
79 | return []alloydbconn.DialOption{alloydbconn.WithPrivateIP()}, nil
80 | case "public":
81 | return []alloydbconn.DialOption{alloydbconn.WithPublicIP()}, nil
82 | default:
83 | return nil, fmt.Errorf("invalid ipType %s", ipType)
84 | }
85 | }
86 |
87 | // Copied over from alloydb_pg.go
88 | func initAlloyDBPgConnectionPool(project, region, cluster, instance, ipType, user, pass, dbname string) (*pgxpool.Pool, error) {
89 | // Configure the driver to connect to the database
90 | dsn := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable", user, pass, dbname)
91 | config, err := pgxpool.ParseConfig(dsn)
92 | if err != nil {
93 | return nil, fmt.Errorf("unable to parse connection uri: %w", err)
94 | }
95 |
96 | // Create a new dialer with options
97 | dialOpts, err := getAlloyDBDialOpts(ipType)
98 | if err != nil {
99 | return nil, err
100 | }
101 | d, err := alloydbconn.NewDialer(context.Background(), alloydbconn.WithDefaultDialOptions(dialOpts...))
102 | if err != nil {
103 | return nil, fmt.Errorf("unable to parse connection uri: %w", err)
104 | }
105 |
106 | // Tell the driver to use the AlloyDB Go Connector to create connections
107 | i := fmt.Sprintf("projects/%s/locations/%s/clusters/%s/instances/%s", project, region, cluster, instance)
108 | config.ConnConfig.DialFunc = func(ctx context.Context, _ string, instance string) (net.Conn, error) {
109 | return d.Dial(ctx, i)
110 | }
111 |
112 | // Interact with the driver directly as you normally would
113 | pool, err := pgxpool.NewWithConfig(context.Background(), config)
114 | if err != nil {
115 | return nil, err
116 | }
117 | return pool, nil
118 | }
119 |
120 | func TestAlloyDBPgToolEndpoints(t *testing.T) {
121 | sourceConfig := getAlloyDBPgVars(t)
122 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
123 | defer cancel()
124 |
125 | var args []string
126 |
127 | pool, err := initAlloyDBPgConnectionPool(AlloyDBPostgresProject, AlloyDBPostgresRegion, AlloyDBPostgresCluster, AlloyDBPostgresInstance, "public", AlloyDBPostgresUser, AlloyDBPostgresPass, AlloyDBPostgresDatabase)
128 | if err != nil {
129 | t.Fatalf("unable to create AlloyDB connection pool: %s", err)
130 | }
131 |
132 | // create table name with UUID
133 | tableNameParam := "param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
134 | tableNameAuth := "auth_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
135 | tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
136 |
137 | // set up data for param tool
138 | createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := tests.GetPostgresSQLParamToolInfo(tableNameParam)
139 | teardownTable1 := tests.SetupPostgresSQLTable(t, ctx, pool, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
140 | defer teardownTable1(t)
141 |
142 | // set up data for auth tool
143 | createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := tests.GetPostgresSQLAuthToolInfo(tableNameAuth)
144 | teardownTable2 := tests.SetupPostgresSQLTable(t, ctx, pool, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams)
145 | defer teardownTable2(t)
146 |
147 | // Write config into a file and pass it to command
148 | toolsFile := tests.GetToolsConfig(sourceConfig, AlloyDBPostgresToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
149 | toolsFile = tests.AddExecuteSqlConfig(t, toolsFile, "postgres-execute-sql")
150 | tmplSelectCombined, tmplSelectFilterCombined := tests.GetPostgresSQLTmplToolStatement()
151 | toolsFile = tests.AddTemplateParamConfig(t, toolsFile, AlloyDBPostgresToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
152 |
153 | cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
154 | if err != nil {
155 | t.Fatalf("command initialization returned an error: %s", err)
156 | }
157 | defer cleanup()
158 |
159 | waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
160 | defer cancel()
161 | out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
162 | if err != nil {
163 | t.Logf("toolbox command logs: \n%s", out)
164 | t.Fatalf("toolbox didn't start successfully: %s", err)
165 | }
166 |
167 | // Get configs for tests
168 | select1Want, failInvocationWant, createTableStatement, mcpSelect1Want := tests.GetPostgresWants()
169 |
170 | // Run tests
171 | tests.RunToolGetTest(t)
172 | tests.RunToolInvokeTest(t, select1Want)
173 | tests.RunMCPToolCallMethod(t, failInvocationWant, mcpSelect1Want)
174 | tests.RunExecuteSqlToolInvokeTest(t, createTableStatement, select1Want)
175 | tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam)
176 | }
177 |
178 | // Test connection with different IP type
179 | func TestAlloyDBPgIpConnection(t *testing.T) {
180 | sourceConfig := getAlloyDBPgVars(t)
181 |
182 | tcs := []struct {
183 | name string
184 | ipType string
185 | }{
186 | {
187 | name: "public ip",
188 | ipType: "public",
189 | },
190 | {
191 | name: "private ip",
192 | ipType: "private",
193 | },
194 | }
195 | for _, tc := range tcs {
196 | t.Run(tc.name, func(t *testing.T) {
197 | sourceConfig["ipType"] = tc.ipType
198 | err := tests.RunSourceConnectionTest(t, sourceConfig, AlloyDBPostgresToolKind)
199 | if err != nil {
200 | t.Fatalf("Connection test failure: %s", err)
201 | }
202 | })
203 | }
204 | }
205 |
206 | // Test IAM connection
207 | func TestAlloyDBPgIAMConnection(t *testing.T) {
208 | getAlloyDBPgVars(t)
209 | // service account email used for IAM should trim the suffix
210 | serviceAccountEmail := strings.TrimSuffix(tests.ServiceAccountEmail, ".gserviceaccount.com")
211 |
212 | noPassSourceConfig := map[string]any{
213 | "kind": AlloyDBPostgresSourceKind,
214 | "project": AlloyDBPostgresProject,
215 | "cluster": AlloyDBPostgresCluster,
216 | "instance": AlloyDBPostgresInstance,
217 | "region": AlloyDBPostgresRegion,
218 | "database": AlloyDBPostgresDatabase,
219 | "user": serviceAccountEmail,
220 | }
221 |
222 | noUserSourceConfig := map[string]any{
223 | "kind": AlloyDBPostgresSourceKind,
224 | "project": AlloyDBPostgresProject,
225 | "cluster": AlloyDBPostgresCluster,
226 | "instance": AlloyDBPostgresInstance,
227 | "region": AlloyDBPostgresRegion,
228 | "database": AlloyDBPostgresDatabase,
229 | "password": "random",
230 | }
231 |
232 | noUserNoPassSourceConfig := map[string]any{
233 | "kind": AlloyDBPostgresSourceKind,
234 | "project": AlloyDBPostgresProject,
235 | "cluster": AlloyDBPostgresCluster,
236 | "instance": AlloyDBPostgresInstance,
237 | "region": AlloyDBPostgresRegion,
238 | "database": AlloyDBPostgresDatabase,
239 | }
240 | tcs := []struct {
241 | name string
242 | sourceConfig map[string]any
243 | isErr bool
244 | }{
245 | {
246 | name: "no user no pass",
247 | sourceConfig: noUserNoPassSourceConfig,
248 | isErr: false,
249 | },
250 | {
251 | name: "no password",
252 | sourceConfig: noPassSourceConfig,
253 | isErr: false,
254 | },
255 | {
256 | name: "no user",
257 | sourceConfig: noUserSourceConfig,
258 | isErr: true,
259 | },
260 | }
261 | for _, tc := range tcs {
262 | t.Run(tc.name, func(t *testing.T) {
263 | err := tests.RunSourceConnectionTest(t, tc.sourceConfig, AlloyDBPostgresToolKind)
264 | if err != nil {
265 | if tc.isErr {
266 | return
267 | }
268 | t.Fatalf("Connection test failure: %s", err)
269 | }
270 | if tc.isErr {
271 | t.Fatalf("Expected error but test passed.")
272 | }
273 | })
274 | }
275 | }
276 |
```
--------------------------------------------------------------------------------
/internal/tools/bigquery/bigquerysearchcatalog/bigquerysearchcatalog.go:
--------------------------------------------------------------------------------
```go
1 | // Copyright 2025 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package bigquerysearchcatalog
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "strings"
21 |
22 | dataplexapi "cloud.google.com/go/dataplex/apiv1"
23 | dataplexpb "cloud.google.com/go/dataplex/apiv1/dataplexpb"
24 | "github.com/goccy/go-yaml"
25 | "github.com/googleapis/genai-toolbox/internal/sources"
26 | bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
27 | "github.com/googleapis/genai-toolbox/internal/tools"
28 | "google.golang.org/api/iterator"
29 | )
30 |
31 | const kind string = "bigquery-search-catalog"
32 |
33 | func init() {
34 | if !tools.Register(kind, newConfig) {
35 | panic(fmt.Sprintf("tool kind %q already registered", kind))
36 | }
37 | }
38 |
39 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
40 | actual := Config{Name: name}
41 | if err := decoder.DecodeContext(ctx, &actual); err != nil {
42 | return nil, err
43 | }
44 | return actual, nil
45 | }
46 |
47 | type compatibleSource interface {
48 | MakeDataplexCatalogClient() func() (*dataplexapi.CatalogClient, bigqueryds.DataplexClientCreator, error)
49 | BigQueryProject() string
50 | UseClientAuthorization() bool
51 | }
52 |
53 | // validate compatible sources are still compatible
54 | var _ compatibleSource = &bigqueryds.Source{}
55 |
56 | var compatibleSources = [...]string{bigqueryds.SourceKind}
57 |
58 | type Config struct {
59 | Name string `yaml:"name" validate:"required"`
60 | Kind string `yaml:"kind" validate:"required"`
61 | Source string `yaml:"source" validate:"required"`
62 | Description string `yaml:"description"`
63 | AuthRequired []string `yaml:"authRequired"`
64 | }
65 |
66 | // validate interface
67 | var _ tools.ToolConfig = Config{}
68 |
69 | func (cfg Config) ToolConfigKind() string {
70 | return kind
71 | }
72 |
73 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
74 | // Initialize the search configuration with the provided sources
75 | rawS, ok := srcs[cfg.Source]
76 | if !ok {
77 | return nil, fmt.Errorf("no source named %q configured", cfg.Source)
78 | }
79 | // verify the source is compatible
80 | s, ok := rawS.(compatibleSource)
81 | if !ok {
82 | return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
83 | }
84 |
85 | // Get the Dataplex client using the method from the source
86 | makeCatalogClient := s.MakeDataplexCatalogClient()
87 |
88 | prompt := tools.NewStringParameter("prompt", "Prompt representing search intention. Do not rewrite the prompt.")
89 | datasetIds := tools.NewArrayParameterWithDefault("datasetIds", []any{}, "Array of dataset IDs.", tools.NewStringParameter("datasetId", "The IDs of the bigquery dataset."))
90 | projectIds := tools.NewArrayParameterWithDefault("projectIds", []any{}, "Array of project IDs.", tools.NewStringParameter("projectId", "The IDs of the bigquery project."))
91 | types := tools.NewArrayParameterWithDefault("types", []any{}, "Array of data types to filter by.", tools.NewStringParameter("type", "The type of the data. Accepted values are: CONNECTION, POLICY, DATASET, MODEL, ROUTINE, TABLE, VIEW."))
92 | pageSize := tools.NewIntParameterWithDefault("pageSize", 5, "Number of results in the search page.")
93 | parameters := tools.Parameters{prompt, datasetIds, projectIds, types, pageSize}
94 |
95 | description := "Use this tool to find tables, views, models, routines or connections."
96 | if cfg.Description != "" {
97 | description = cfg.Description
98 | }
99 | mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, parameters)
100 |
101 | t := Tool{
102 | Name: cfg.Name,
103 | Kind: kind,
104 | Parameters: parameters,
105 | AuthRequired: cfg.AuthRequired,
106 | UseClientOAuth: s.UseClientAuthorization(),
107 | MakeCatalogClient: makeCatalogClient,
108 | ProjectID: s.BigQueryProject(),
109 | manifest: tools.Manifest{
110 | Description: cfg.Description,
111 | Parameters: parameters.Manifest(),
112 | AuthRequired: cfg.AuthRequired,
113 | },
114 | mcpManifest: mcpManifest,
115 | }
116 | return t, nil
117 | }
118 |
119 | type Tool struct {
120 | Name string
121 | Kind string
122 | Parameters tools.Parameters
123 | AuthRequired []string
124 | UseClientOAuth bool
125 | MakeCatalogClient func() (*dataplexapi.CatalogClient, bigqueryds.DataplexClientCreator, error)
126 | ProjectID string
127 | manifest tools.Manifest
128 | mcpManifest tools.McpManifest
129 | }
130 |
131 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
132 | return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
133 | }
134 |
135 | func (t Tool) RequiresClientAuthorization() bool {
136 | return t.UseClientOAuth
137 | }
138 |
139 | func constructSearchQueryHelper(predicate string, operator string, items []string) string {
140 | if len(items) == 0 {
141 | return ""
142 | }
143 |
144 | if len(items) == 1 {
145 | return predicate + operator + items[0]
146 | }
147 |
148 | var builder strings.Builder
149 | builder.WriteString("(")
150 | for i, item := range items {
151 | if i > 0 {
152 | builder.WriteString(" OR ")
153 | }
154 | builder.WriteString(predicate)
155 | builder.WriteString(operator)
156 | builder.WriteString(item)
157 | }
158 | builder.WriteString(")")
159 | return builder.String()
160 | }
161 |
162 | func constructSearchQuery(projectIds []string, datasetIds []string, types []string) string {
163 | queryParts := []string{}
164 |
165 | if clause := constructSearchQueryHelper("projectid", "=", projectIds); clause != "" {
166 | queryParts = append(queryParts, clause)
167 | }
168 |
169 | if clause := constructSearchQueryHelper("parent", "=", datasetIds); clause != "" {
170 | queryParts = append(queryParts, clause)
171 | }
172 |
173 | if clause := constructSearchQueryHelper("type", "=", types); clause != "" {
174 | queryParts = append(queryParts, clause)
175 | }
176 | queryParts = append(queryParts, "system=bigquery")
177 |
178 | return strings.Join(queryParts, " AND ")
179 | }
180 |
181 | type Response struct {
182 | DisplayName string
183 | Description string
184 | Type string
185 | Resource string
186 | DataplexEntry string
187 | }
188 |
189 | var typeMap = map[string]string{
190 | "bigquery-connection": "CONNECTION",
191 | "bigquery-data-policy": "POLICY",
192 | "bigquery-dataset": "DATASET",
193 | "bigquery-model": "MODEL",
194 | "bigquery-routine": "ROUTINE",
195 | "bigquery-table": "TABLE",
196 | "bigquery-view": "VIEW",
197 | }
198 |
199 | func ExtractType(resourceString string) string {
200 | lastIndex := strings.LastIndex(resourceString, "/")
201 | if lastIndex == -1 {
202 | // No "/" found, return the original string
203 | return resourceString
204 | }
205 | return typeMap[resourceString[lastIndex+1:]]
206 | }
207 |
208 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
209 | paramsMap := params.AsMap()
210 | pageSize := int32(paramsMap["pageSize"].(int))
211 | prompt, _ := paramsMap["prompt"].(string)
212 | projectIdSlice, err := tools.ConvertAnySliceToTyped(paramsMap["projectIds"].([]any), "string")
213 | if err != nil {
214 | return nil, fmt.Errorf("can't convert projectIds to array of strings: %s", err)
215 | }
216 | projectIds := projectIdSlice.([]string)
217 | datasetIdSlice, err := tools.ConvertAnySliceToTyped(paramsMap["datasetIds"].([]any), "string")
218 | if err != nil {
219 | return nil, fmt.Errorf("can't convert datasetIds to array of strings: %s", err)
220 | }
221 | datasetIds := datasetIdSlice.([]string)
222 | typesSlice, err := tools.ConvertAnySliceToTyped(paramsMap["types"].([]any), "string")
223 | if err != nil {
224 | return nil, fmt.Errorf("can't convert types to array of strings: %s", err)
225 | }
226 | types := typesSlice.([]string)
227 |
228 | req := &dataplexpb.SearchEntriesRequest{
229 | Query: fmt.Sprintf("%s %s", prompt, constructSearchQuery(projectIds, datasetIds, types)),
230 | Name: fmt.Sprintf("projects/%s/locations/global", t.ProjectID),
231 | PageSize: pageSize,
232 | SemanticSearch: true,
233 | }
234 |
235 | catalogClient, dataplexClientCreator, _ := t.MakeCatalogClient()
236 |
237 | if t.UseClientOAuth {
238 | tokenStr, err := accessToken.ParseBearerToken()
239 | if err != nil {
240 | return nil, fmt.Errorf("error parsing access token: %w", err)
241 | }
242 | catalogClient, err = dataplexClientCreator(tokenStr)
243 | if err != nil {
244 | return nil, fmt.Errorf("error creating client from OAuth access token: %w", err)
245 | }
246 | }
247 |
248 | it := catalogClient.SearchEntries(ctx, req)
249 | if it == nil {
250 | return nil, fmt.Errorf("failed to create search entries iterator for project %q", t.ProjectID)
251 | }
252 |
253 | var results []Response
254 | for {
255 | entry, err := it.Next()
256 | if err == iterator.Done {
257 | break
258 | }
259 | if err != nil {
260 | break
261 | }
262 | entrySource := entry.DataplexEntry.GetEntrySource()
263 | resp := Response{
264 | DisplayName: entrySource.GetDisplayName(),
265 | Description: entrySource.GetDescription(),
266 | Type: ExtractType(entry.DataplexEntry.GetEntryType()),
267 | Resource: entrySource.GetResource(),
268 | DataplexEntry: entry.DataplexEntry.GetName(),
269 | }
270 | results = append(results, resp)
271 | }
272 | return results, nil
273 | }
274 |
275 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
276 | // Parse parameters from the provided data
277 | return tools.ParseParams(t.Parameters, data, claims)
278 | }
279 |
280 | func (t Tool) Manifest() tools.Manifest {
281 | // Returns the tool manifest
282 | return t.manifest
283 | }
284 |
285 | func (t Tool) McpManifest() tools.McpManifest {
286 | // Returns the tool MCP manifest
287 | return t.mcpManifest
288 | }
289 |
```
--------------------------------------------------------------------------------
/docs/en/resources/tools/http/http.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "http"
3 | type: docs
4 | weight: 1
5 | description: >
6 | A "http" tool sends out an HTTP request to an HTTP endpoint.
7 | aliases:
8 | - /resources/tools/http
9 | ---
10 |
11 |
12 | ## About
13 |
14 | The `http` tool allows you to make HTTP requests to APIs to retrieve data.
15 | An HTTP request is the method by which a client communicates with a server to
16 | retrieve or manipulate resources.
17 | Toolbox allows you to configure the request URL, method, headers, query
18 | parameters, and the request body for an HTTP Tool.
19 |
20 | ### URL
21 |
22 | An HTTP request URL identifies the target the client wants to access.
23 | Toolbox composes the request URL from 3 places:
24 |
25 | 1. The HTTP Source's `baseUrl`.
26 | 2. The HTTP Tool's `path` field.
27 | 3. The HTTP Tool's `pathParams` for dynamic path composed during Tool
28 | invocation.
29 |
30 | For example, the following config allows you to reach different paths of the
31 | same server using multiple Tools:
32 |
33 | ```yaml
34 | sources:
35 | my-http-source:
36 | kind: http
37 | baseUrl: https://api.example.com
38 |
39 | tools:
40 | my-post-tool:
41 | kind: http
42 | source: my-http-source
43 | method: POST
44 | path: /update
45 | description: Tool to update information to the example API
46 |
47 | my-get-tool:
48 | kind: http
49 | source: my-http-source
50 | method: GET
51 | path: /search
52 | description: Tool to search information from the example API
53 |
54 | my-dynamic-path-tool:
55 | kind: http
56 | source: my-http-source
57 | method: GET
58 | path: /{{.myPathParam}}/search
59 | description: Tool to reach endpoint based on the input to `myPathParam`
60 | pathParams:
61 | - name: myPathParam
62 | type: string
63 | description: The dynamic path parameter
64 |
65 | ```
66 |
67 | ### Headers
68 |
69 | An HTTP request header is a key-value pair sent by a client to a server,
70 | providing additional information about the request, such as the client's
71 | preferences, the request body content type, and other metadata.
72 | Headers specified by the HTTP Tool are combined with its HTTP Source headers for
73 | the resulting HTTP request, and override the Source headers in case of conflict.
74 | The HTTP Tool allows you to specify headers in two different ways:
75 |
76 | - Static headers can be specified using the `headers` field, and will be the
77 | same for every invocation:
78 |
79 | ```yaml
80 | my-http-tool:
81 | kind: http
82 | source: my-http-source
83 | method: GET
84 | path: /search
85 | description: Tool to search data from API
86 | headers:
87 | Authorization: API_KEY
88 | Content-Type: application/json
89 | ```
90 |
91 | - Dynamic headers can be specified as parameters in the `headerParams` field.
92 | The `name` of the `headerParams` will be used as the header key, and the value
93 | is determined by the LLM input upon Tool invocation:
94 |
95 | ```yaml
96 | my-http-tool:
97 | kind: http
98 | source: my-http-source
99 | method: GET
100 | path: /search
101 | description: some description
102 | headerParams:
103 | - name: Content-Type # Example LLM input: "application/json"
104 | description: request content type
105 | type: string
106 | ```
107 |
108 | ### Query parameters
109 |
110 | Query parameters are key-value pairs appended to a URL after a question mark (?)
111 | to provide additional information to the server for processing the request, like
112 | filtering or sorting data.
113 |
114 | - Static request query parameters should be specified in the `path` as part of
115 | the URL itself:
116 |
117 | ```yaml
118 | my-http-tool:
119 | kind: http
120 | source: my-http-source
121 | method: GET
122 | path: /search?language=en&id=1
123 | description: Tool to search for item with ID 1 in English
124 | ```
125 |
126 | - Dynamic request query parameters should be specified as parameters in the
127 | `queryParams` section:
128 |
129 | ```yaml
130 | my-http-tool:
131 | kind: http
132 | source: my-http-source
133 | method: GET
134 | path: /search
135 | description: Tool to search for item with ID
136 | queryParams:
137 | - name: id
138 | description: item ID
139 | type: integer
140 | ```
141 |
142 | ### Request body
143 |
144 | The request body payload is a string that supports parameter replacement
145 | following [Go template][go-template-doc]'s annotations.
146 | The parameter names in the `requestBody` should be preceded by "." and enclosed
147 | by double curly brackets "{{}}". The values will be populated into the request
148 | body payload upon Tool invocation.
149 |
150 | Example:
151 |
152 | ```yaml
153 | my-http-tool:
154 | kind: http
155 | source: my-http-source
156 | method: GET
157 | path: /search
158 | description: Tool to search for person with name and age
159 | requestBody: |
160 | {
161 | "age": {{.age}},
162 | "name": "{{.name}}"
163 | }
164 | bodyParams:
165 | - name: age
166 | description: age number
167 | type: integer
168 | - name: name
169 | description: name string
170 | type: string
171 | ```
172 |
173 | #### Formatting Parameters
174 |
175 | Some complex parameters (such as arrays) may require additional formatting to
176 | match the expected output. For convenience, you can specify one of the following
177 | pre-defined functions before the parameter name to format it:
178 |
179 | ##### JSON
180 |
181 | The `json` keyword converts a parameter into a JSON format.
182 |
183 | {{< notice note >}}
184 | Using JSON may add quotes to the variable name for certain types (such as
185 | strings).
186 | {{< /notice >}}
187 |
188 | Example:
189 |
190 | ```yaml
191 | requestBody: |
192 | {
193 | "age": {{json .age}},
194 | "name": {{json .name}},
195 | "nickname": "{{json .nickname}}",
196 | "nameArray": {{json .nameArray}}
197 | }
198 | ```
199 |
200 | will send the following output:
201 |
202 | ```yaml
203 | {
204 | "age": 18,
205 | "name": "Katherine",
206 | "nickname": ""Kat"", # Duplicate quotes
207 | "nameArray": ["A", "B", "C"]
208 | }
209 | ```
210 |
211 | ## Example
212 |
213 | ```yaml
214 | my-http-tool:
215 | kind: http
216 | source: my-http-source
217 | method: GET
218 | path: /search
219 | description: some description
220 | authRequired:
221 | - my-google-auth-service
222 | - other-auth-service
223 | queryParams:
224 | - name: country
225 | description: some description
226 | type: string
227 | requestBody: |
228 | {
229 | "age": {{.age}},
230 | "city": "{{.city}}"
231 | }
232 | bodyParams:
233 | - name: age
234 | description: age number
235 | type: integer
236 | - name: city
237 | description: city string
238 | type: string
239 | headers:
240 | Authorization: API_KEY
241 | Content-Type: application/json
242 | headerParams:
243 | - name: Language
244 | description: language string
245 | type: string
246 | ```
247 |
248 | ## Reference
249 |
250 | | **field** | **type** | **required** | **description** |
251 | |--------------|:------------------------------------------:|:------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
252 | | kind | string | true | Must be "http". |
253 | | source | string | true | Name of the source the HTTP request should be sent to. |
254 | | description | string | true | Description of the tool that is passed to the LLM. |
255 | | path | string | true | The path of the HTTP request. You can include static query parameters in the path string. |
256 | | method | string | true | The HTTP method to use (e.g., GET, POST, PUT, DELETE). |
257 | | headers | map[string]string | false | A map of headers to include in the HTTP request (overrides source headers). |
258 | | requestBody | string | false | The request body payload. Use [go template][go-template-doc] with the parameter name as the placeholder (e.g., `{{.id}}` will be replaced with the value of the parameter that has name `id` in the `bodyParams` section). |
259 | | queryParams | [parameters](../#specifying-parameters) | false | List of [parameters](../#specifying-parameters) that will be inserted into the query string. |
260 | | bodyParams | [parameters](../#specifying-parameters) | false | List of [parameters](../#specifying-parameters) that will be inserted into the request body payload. |
261 | | headerParams | [parameters](../#specifying-parameters) | false | List of [parameters](../#specifying-parameters) that will be inserted as the request headers. |
262 |
263 | [go-template-doc]: <https://pkg.go.dev/text/template#pkg-overview>
264 |
```
--------------------------------------------------------------------------------
/docs/en/how-to/connect-ide/postgres_mcp.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | title: "PostgreSQL using MCP"
3 | type: docs
4 | weight: 2
5 | description: >
6 | Connect your IDE to PostgreSQL using Toolbox.
7 | ---
8 |
9 | [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
10 | an open protocol for connecting Large Language Models (LLMs) to data sources
11 | like Postgres. This guide covers how to use [MCP Toolbox for Databases][toolbox]
12 | to expose your developer assistant tools to a Postgres instance:
13 |
14 | * [Cursor][cursor]
15 | * [Windsurf][windsurf] (Codium)
16 | * [Visual Studio Code][vscode] (Copilot)
17 | * [Cline][cline] (VS Code extension)
18 | * [Claude desktop][claudedesktop]
19 | * [Claude code][claudecode]
20 | * [Gemini CLI][geminicli]
21 | * [Gemini Code Assist][geminicodeassist]
22 |
23 | [toolbox]: https://github.com/googleapis/genai-toolbox
24 | [cursor]: #configure-your-mcp-client
25 | [windsurf]: #configure-your-mcp-client
26 | [vscode]: #configure-your-mcp-client
27 | [cline]: #configure-your-mcp-client
28 | [claudedesktop]: #configure-your-mcp-client
29 | [claudecode]: #configure-your-mcp-client
30 | [geminicli]: #configure-your-mcp-client
31 | [geminicodeassist]: #configure-your-mcp-client
32 |
33 | {{< notice tip >}}
34 | This guide can be used with [AlloyDB
35 | Omni](https://cloud.google.com/alloydb/omni/current/docs/overview).
36 | {{< /notice >}}
37 |
38 | ## Set up the database
39 |
40 | 1. Create or select a PostgreSQL instance.
41 |
42 | * [Install PostgreSQL locally](https://www.postgresql.org/download/)
43 | * [Install AlloyDB Omni](https://cloud.google.com/alloydb/omni/current/docs/quickstart)
44 |
45 | 1. Create or reuse [a database
46 | user](https://cloud.google.com/alloydb/omni/current/docs/database-users/manage-users)
47 | and have the username and password ready.
48 |
49 | ## Install MCP Toolbox
50 |
51 | 1. Download the latest version of Toolbox as a binary. Select the [correct
52 | binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
53 | to your OS and CPU architecture. You are required to use Toolbox version
54 | V0.6.0+:
55 |
56 | <!-- {x-release-please-start-version} -->
57 | {{< tabpane persist=header >}}
58 | {{< tab header="linux/amd64" lang="bash" >}}
59 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/linux/amd64/toolbox
60 | {{< /tab >}}
61 |
62 | {{< tab header="darwin/arm64" lang="bash" >}}
63 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/arm64/toolbox
64 | {{< /tab >}}
65 |
66 | {{< tab header="darwin/amd64" lang="bash" >}}
67 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/darwin/amd64/toolbox
68 | {{< /tab >}}
69 |
70 | {{< tab header="windows/amd64" lang="bash" >}}
71 | curl -O https://storage.googleapis.com/genai-toolbox/v0.18.0/windows/amd64/toolbox.exe
72 | {{< /tab >}}
73 | {{< /tabpane >}}
74 | <!-- {x-release-please-end} -->
75 |
76 | 1. Make the binary executable:
77 |
78 | ```bash
79 | chmod +x toolbox
80 | ```
81 |
82 | 1. Verify the installation:
83 |
84 | ```bash
85 | ./toolbox --version
86 | ```
87 |
88 | ## Configure your MCP Client
89 |
90 | {{< tabpane text=true >}}
91 | {{% tab header="Claude code" lang="en" %}}
92 |
93 | 1. Install [Claude
94 | Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
95 | 1. Create a `.mcp.json` file in your project root if it doesn't exist.
96 | 1. Add the following configuration, replace the environment variables with your
97 | values, and save:
98 |
99 | ```json
100 | {
101 | "mcpServers": {
102 | "postgres": {
103 | "command": "./PATH/TO/toolbox",
104 | "args": ["--prebuilt","postgres","--stdio"],
105 | "env": {
106 | "POSTGRES_HOST": "",
107 | "POSTGRES_PORT": "",
108 | "POSTGRES_DATABASE": "",
109 | "POSTGRES_USER": "",
110 | "POSTGRES_PASSWORD": ""
111 | }
112 | }
113 | }
114 | }
115 | ```
116 |
117 | 1. Restart Claude code to apply the new configuration.
118 | {{% /tab %}}
119 |
120 | {{% tab header="Claude desktop" lang="en" %}}
121 |
122 | 1. Open [Claude desktop](https://claude.ai/download) and navigate to Settings.
123 | 1. Under the Developer tab, tap Edit Config to open the configuration file.
124 | 1. Add the following configuration, replace the environment variables with your
125 | values, and save:
126 |
127 | ```json
128 | {
129 | "mcpServers": {
130 | "postgres": {
131 | "command": "./PATH/TO/toolbox",
132 | "args": ["--prebuilt","postgres","--stdio"],
133 | "env": {
134 | "POSTGRES_HOST": "",
135 | "POSTGRES_PORT": "",
136 | "POSTGRES_DATABASE": "",
137 | "POSTGRES_USER": "",
138 | "POSTGRES_PASSWORD": ""
139 | }
140 | }
141 | }
142 | }
143 | ```
144 |
145 | 1. Restart Claude desktop.
146 | 1. From the new chat screen, you should see a hammer (MCP) icon appear with the
147 | new MCP server available.
148 | {{% /tab %}}
149 |
150 | {{% tab header="Cline" lang="en" %}}
151 |
152 | 1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap
153 | the **MCP Servers** icon.
154 | 1. Tap Configure MCP Servers to open the configuration file.
155 | 1. Add the following configuration, replace the environment variables with your
156 | values, and save:
157 |
158 | ```json
159 | {
160 | "mcpServers": {
161 | "postgres": {
162 | "command": "./PATH/TO/toolbox",
163 | "args": ["--prebuilt","postgres","--stdio"],
164 | "env": {
165 | "POSTGRES_HOST": "",
166 | "POSTGRES_PORT": "",
167 | "POSTGRES_DATABASE": "",
168 | "POSTGRES_USER": "",
169 | "POSTGRES_PASSWORD": ""
170 | }
171 | }
172 | }
173 | }
174 | ```
175 |
176 | 1. You should see a green active status after the server is successfully
177 | connected.
178 | {{% /tab %}}
179 |
180 | {{% tab header="Cursor" lang="en" %}}
181 |
182 | 1. Create a `.cursor` directory in your project root if it doesn't exist.
183 | 1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
184 | 1. Add the following configuration, replace the environment variables with your
185 | values, and save:
186 |
187 | ```json
188 | {
189 | "mcpServers": {
190 | "postgres": {
191 | "command": "./PATH/TO/toolbox",
192 | "args": ["--prebuilt","postgres","--stdio"],
193 | "env": {
194 | "POSTGRES_HOST": "",
195 | "POSTGRES_PORT": "",
196 | "POSTGRES_DATABASE": "",
197 | "POSTGRES_USER": "",
198 | "POSTGRES_PASSWORD": ""
199 | }
200 | }
201 | }
202 | }
203 | ```
204 |
205 | 1. [Cursor](https://www.cursor.com/) and navigate to **Settings > Cursor
206 | Settings > MCP**. You should see a green active status after the server is
207 | successfully connected.
208 | {{% /tab %}}
209 |
210 | {{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
211 |
212 | 1. Open [VS Code](https://code.visualstudio.com/docs/copilot/overview) and
213 | create a `.vscode` directory in your project root if it doesn't exist.
214 | 1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
215 | 1. Add the following configuration, replace the environment variables with your
216 | values, and save:
217 |
218 | ```json
219 | {
220 | "servers": {
221 | "postgres": {
222 | "command": "./PATH/TO/toolbox",
223 | "args": ["--prebuilt","postgres","--stdio"],
224 | "env": {
225 | "POSTGRES_HOST": "",
226 | "POSTGRES_PORT": "",
227 | "POSTGRES_DATABASE": "",
228 | "POSTGRES_USER": "",
229 | "POSTGRES_PASSWORD": ""
230 | }
231 | }
232 | }
233 | }
234 | ```
235 |
236 | {{% /tab %}}
237 |
238 | {{% tab header="Windsurf" lang="en" %}}
239 |
240 | 1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the
241 | Cascade assistant.
242 | 1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
243 | 1. Add the following configuration, replace the environment variables with your
244 | values, and save:
245 |
246 | ```json
247 | {
248 | "mcpServers": {
249 | "postgres": {
250 | "command": "./PATH/TO/toolbox",
251 | "args": ["--prebuilt","postgres","--stdio"],
252 | "env": {
253 | "POSTGRES_HOST": "",
254 | "POSTGRES_PORT": "",
255 | "POSTGRES_DATABASE": "",
256 | "POSTGRES_USER": "",
257 | "POSTGRES_PASSWORD": ""
258 | }
259 | }
260 | }
261 | }
262 |
263 | ```
264 |
265 | {{% /tab %}}
266 |
267 | {{% tab header="Gemini CLI" lang="en" %}}
268 |
269 | 1. Install the [Gemini CLI](https://github.com/google-gemini/gemini-cli?tab=readme-ov-file#quickstart).
270 | 1. In your working directory, create a folder named `.gemini`. Within it, create a `settings.json` file.
271 | 1. Add the following configuration, replace the environment variables with your values, and then save:
272 |
273 | ```json
274 | {
275 | "mcpServers": {
276 | "postgres": {
277 | "command": "./PATH/TO/toolbox",
278 | "args": ["--prebuilt","postgres","--stdio"],
279 | "env": {
280 | "POSTGRES_HOST": "",
281 | "POSTGRES_PORT": "",
282 | "POSTGRES_DATABASE": "",
283 | "POSTGRES_USER": "",
284 | "POSTGRES_PASSWORD": ""
285 | }
286 | }
287 | }
288 | }
289 | ```
290 | {{% /tab %}}
291 |
292 | {{% tab header="Gemini Code Assist" lang="en" %}}
293 |
294 | 1. Install the [Gemini Code Assist](https://marketplace.visualstudio.com/items?itemName=Google.geminicodeassist) extension in Visual Studio Code.
295 | 1. Enable Agent Mode in Gemini Code Assist chat.
296 | 1. In your working directory, create a folder named `.gemini`. Within it, create a `settings.json` file.
297 | 1. Add the following configuration, replace the environment variables with your values, and then save:
298 |
299 | ```json
300 | {
301 | "mcpServers": {
302 | "postgres": {
303 | "command": "./PATH/TO/toolbox",
304 | "args": ["--prebuilt","postgres","--stdio"],
305 | "env": {
306 | "POSTGRES_HOST": "",
307 | "POSTGRES_PORT": "",
308 | "POSTGRES_DATABASE": "",
309 | "POSTGRES_USER": "",
310 | "POSTGRES_PASSWORD": ""
311 | }
312 | }
313 | }
314 | }
315 | ```
316 | {{% /tab %}}
317 | {{< /tabpane >}}
318 |
319 | ## Use Tools
320 |
321 | Your AI tool is now connected to Postgres using MCP. Try asking your AI
322 | assistant to list tables, create a table, or define and execute other SQL
323 | statements.
324 |
325 | The following tools are available to the LLM:
326 |
327 | 1. **list_tables**: lists tables and descriptions
328 | 1. **execute_sql**: execute any SQL statement
329 |
330 | {{< notice note >}}
331 | Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
332 | will adapt to the tools available, so this shouldn't affect most users.
333 | {{< /notice >}}
334 |
```
--------------------------------------------------------------------------------
/tests/sqlite/sqlite_integration_test.go:
--------------------------------------------------------------------------------
```go
1 | // Copyright 2025 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package sqlite
16 |
17 | import (
18 | "context"
19 | "database/sql"
20 | "fmt"
21 | "io"
22 | "net/http"
23 | "os"
24 | "regexp"
25 | "strings"
26 | "testing"
27 | "time"
28 |
29 | "github.com/google/uuid"
30 | "github.com/googleapis/genai-toolbox/internal/testutils"
31 | "github.com/googleapis/genai-toolbox/tests"
32 | )
33 |
34 | var (
35 | SQLiteSourceKind = "sqlite"
36 | SQLiteToolKind = "sqlite-sql"
37 | SQLiteDatabase = os.Getenv("SQLITE_DATABASE")
38 | )
39 |
40 | func getSQLiteVars(t *testing.T) map[string]any {
41 | return map[string]any{
42 | "kind": SQLiteSourceKind,
43 | "database": SQLiteDatabase,
44 | }
45 | }
46 |
47 | func initSQLiteDb(t *testing.T, sqliteDb string) (*sql.DB, func(t *testing.T), string, error) {
48 | if sqliteDb == "" {
49 | // Create a temporary database file
50 | tmpFile, err := os.CreateTemp("", "test-*.db")
51 | if err != nil {
52 | return nil, nil, "", fmt.Errorf("failed to create temp file: %v", err)
53 | }
54 | sqliteDb = tmpFile.Name()
55 | }
56 |
57 | // Open database connection
58 | db, err := sql.Open("sqlite", sqliteDb)
59 | if err != nil {
60 | return nil, nil, "", fmt.Errorf("failed to open database: %v", err)
61 | }
62 |
63 | cleanup := func(t *testing.T) {
64 | if err := os.Remove(sqliteDb); err != nil {
65 | t.Errorf("Failed to remove test database: %s", err)
66 | }
67 | }
68 |
69 | return db, cleanup, sqliteDb, nil
70 | }
71 |
72 | // setupSQLiteTestDB creates a temporary SQLite database for testing
73 | func setupSQLiteTestDB(t *testing.T, ctx context.Context, db *sql.DB, createStatement string, insertStatement string, tableName string, params []any) {
74 | // Create test table
75 | _, err := db.ExecContext(ctx, createStatement)
76 | if err != nil {
77 | t.Fatalf("unable to connect to create test table %s: %s", tableName, err)
78 | }
79 |
80 | _, err = db.ExecContext(ctx, insertStatement, params...)
81 | if err != nil {
82 | t.Fatalf("unable to insert test data: %s", err)
83 | }
84 | }
85 |
86 | func getSQLiteParamToolInfo(tableName string) (string, string, string, string, string, string, []any) {
87 | createStatement := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT);", tableName)
88 | insertStatement := fmt.Sprintf("INSERT INTO %s (name) VALUES (?), (?), (?), (?);", tableName)
89 | toolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ? OR name = ?;", tableName)
90 | idToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ?;", tableName)
91 | nameToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE name = ?;", tableName)
92 | arrayToolStatement := fmt.Sprintf("SELECT * FROM %s WHERE id = ANY({{.idArray}}) AND name = ANY({{.nameArray}});", tableName)
93 | params := []any{"Alice", "Jane", "Sid", nil}
94 | return createStatement, insertStatement, toolStatement, idToolStatement, nameToolStatement, arrayToolStatement, params
95 | }
96 |
97 | func getSQLiteAuthToolInfo(tableName string) (string, string, string, []any) {
98 | createStatement := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT)", tableName)
99 | insertStatement := fmt.Sprintf("INSERT INTO %s (name, email) VALUES (?, ?), (?,?) RETURNING id, name, email;", tableName)
100 | toolStatement := fmt.Sprintf("SELECT name FROM %s WHERE email = ?;", tableName)
101 | params := []any{"Alice", tests.ServiceAccountEmail, "Jane", "[email protected]"}
102 | return createStatement, insertStatement, toolStatement, params
103 | }
104 |
105 | func getSQLiteTmplToolStatement() (string, string) {
106 | tmplSelectCombined := "SELECT * FROM {{.tableName}} WHERE id = ?"
107 | tmplSelectFilterCombined := "SELECT * FROM {{.tableName}} WHERE {{.columnFilter}} = ?"
108 | return tmplSelectCombined, tmplSelectFilterCombined
109 | }
110 |
111 | func TestSQLiteToolEndpoint(t *testing.T) {
112 | db, teardownDb, sqliteDb, err := initSQLiteDb(t, SQLiteDatabase)
113 | if err != nil {
114 | t.Fatal(err)
115 | }
116 | defer teardownDb(t)
117 | defer db.Close()
118 |
119 | sourceConfig := getSQLiteVars(t)
120 | sourceConfig["database"] = sqliteDb
121 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
122 | defer cancel()
123 |
124 | var args []string
125 |
126 | // create table name with UUID
127 | tableNameParam := "param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
128 | tableNameAuth := "auth_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
129 | tableNameTemplateParam := "template_param_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
130 |
131 | // set up data for param tool
132 | createParamTableStmt, insertParamTableStmt, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, paramTestParams := getSQLiteParamToolInfo(tableNameParam)
133 | setupSQLiteTestDB(t, ctx, db, createParamTableStmt, insertParamTableStmt, tableNameParam, paramTestParams)
134 |
135 | // set up data for auth tool
136 | createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := getSQLiteAuthToolInfo(tableNameAuth)
137 | setupSQLiteTestDB(t, ctx, db, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams)
138 |
139 | // Write config into a file and pass it to command
140 | toolsFile := tests.GetToolsConfig(sourceConfig, SQLiteToolKind, paramToolStmt, idParamToolStmt, nameParamToolStmt, arrayToolStmt, authToolStmt)
141 | tmplSelectCombined, tmplSelectFilterCombined := getSQLiteTmplToolStatement()
142 | toolsFile = tests.AddTemplateParamConfig(t, toolsFile, SQLiteToolKind, tmplSelectCombined, tmplSelectFilterCombined, "")
143 |
144 | cmd, cleanup, err := tests.StartCmd(ctx, toolsFile, args...)
145 | if err != nil {
146 | t.Fatalf("command initialization returned an error: %s", err)
147 | }
148 | defer cleanup()
149 |
150 | waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
151 | defer cancel()
152 | out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
153 | if err != nil {
154 | t.Logf("toolbox command logs: \n%s", out)
155 | t.Fatalf("toolbox didn't start successfully: %s", err)
156 | }
157 |
158 | // Get configs for tests
159 | select1Want := "[{\"1\":1}]"
160 | mcpMyFailToolWant := `{"jsonrpc":"2.0","id":"invoke-fail-tool","result":{"content":[{"type":"text","text":"unable to execute query: SQL logic error: near \"SELEC\": syntax error (1)"}],"isError":true}}`
161 | mcpSelect1Want := `{"jsonrpc":"2.0","id":"invoke my-auth-required-tool","result":{"content":[{"type":"text","text":"{\"1\":1}"}]}}`
162 |
163 | // Run tests
164 | tests.RunToolGetTest(t)
165 | tests.RunToolInvokeTest(t, select1Want, tests.DisableArrayTest())
166 | tests.RunMCPToolCallMethod(t, mcpMyFailToolWant, mcpSelect1Want)
167 | tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam)
168 | }
169 |
170 | func TestSQLiteExecuteSqlTool(t *testing.T) {
171 | db, teardownDb, sqliteDb, err := initSQLiteDb(t, SQLiteDatabase)
172 | if err != nil {
173 | t.Fatal(err)
174 | }
175 | defer teardownDb(t)
176 | defer db.Close()
177 |
178 | sourceConfig := getSQLiteVars(t)
179 | sourceConfig["database"] = sqliteDb
180 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
181 | defer cancel()
182 |
183 | // Create a table and insert data
184 | tableName := "exec_table_" + strings.ReplaceAll(uuid.New().String(), "-", "")
185 | createStmt := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id INTEGER PRIMARY KEY, name TEXT);", tableName)
186 | insertStmt := fmt.Sprintf("INSERT INTO %s (name) VALUES (?);", tableName)
187 | params := []any{"Bob"}
188 | setupSQLiteTestDB(t, ctx, db, createStmt, insertStmt, tableName, params)
189 |
190 | // Add sqlite-execute-sql tool config
191 | toolConfig := map[string]any{
192 | "tools": map[string]any{
193 | "my-exec-sql-tool": map[string]any{
194 | "kind": "sqlite-execute-sql",
195 | "source": "my-instance",
196 | "description": "Tool to execute SQL statements",
197 | },
198 | },
199 | "sources": map[string]any{
200 | "my-instance": sourceConfig,
201 | },
202 | }
203 |
204 | cmd, cleanup, err := tests.StartCmd(ctx, toolConfig)
205 | if err != nil {
206 | t.Fatalf("command initialization returned an error: %s", err)
207 | }
208 | defer cleanup()
209 |
210 | waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
211 | defer cancel()
212 | out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
213 | if err != nil {
214 | t.Logf("toolbox command logs: \n%s", out)
215 | t.Fatalf("toolbox didn't start successfully: %s", err)
216 | }
217 |
218 | // Table-driven test cases
219 | testCases := []struct {
220 | name string
221 | sql string
222 | wantStatus int
223 | wantBody string
224 | }{
225 | {
226 | name: "select existing row",
227 | sql: fmt.Sprintf("SELECT name FROM %s WHERE id = 1", tableName),
228 | wantStatus: 200,
229 | wantBody: "Bob",
230 | },
231 | {
232 | name: "select no rows",
233 | sql: fmt.Sprintf("SELECT name FROM %s WHERE id = 999", tableName),
234 | wantStatus: 200,
235 | wantBody: "null",
236 | },
237 | {
238 | name: "invalid SQL",
239 | sql: "SELEC name FROM not_a_table",
240 | wantStatus: 400,
241 | wantBody: "SQL logic error",
242 | },
243 | }
244 |
245 | for _, tc := range testCases {
246 | t.Run(tc.name, func(t *testing.T) {
247 | api := "http://127.0.0.1:5000/api/tool/my-exec-sql-tool/invoke"
248 | reqBody := strings.NewReader(fmt.Sprintf(`{"sql":"%s"}`, tc.sql))
249 | req, err := http.NewRequest("POST", api, reqBody)
250 | if err != nil {
251 | t.Fatalf("unable to create request: %s", err)
252 | }
253 | req.Header.Set("Content-Type", "application/json")
254 | resp, err := http.DefaultClient.Do(req)
255 | if err != nil {
256 | t.Fatalf("unable to send request: %s", err)
257 | }
258 | defer resp.Body.Close()
259 | bodyBytes, err := io.ReadAll(resp.Body)
260 | if err != nil {
261 | t.Fatalf("unable to read response: %s", err)
262 | }
263 | if resp.StatusCode != tc.wantStatus {
264 | t.Fatalf("unexpected status: %d, body: %s", resp.StatusCode, string(bodyBytes))
265 | }
266 | if tc.wantBody != "" && !strings.Contains(string(bodyBytes), tc.wantBody) {
267 | t.Fatalf("expected body to contain %q, got: %s", tc.wantBody, string(bodyBytes))
268 | }
269 | })
270 | }
271 | }
272 |
```