#
tokens: 49144/50000 22/786 files (page 12/45)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 12 of 45. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .ci
│   ├── continuous.release.cloudbuild.yaml
│   ├── generate_release_table.sh
│   ├── integration.cloudbuild.yaml
│   ├── quickstart_test
│   │   ├── go.integration.cloudbuild.yaml
│   │   ├── js.integration.cloudbuild.yaml
│   │   ├── py.integration.cloudbuild.yaml
│   │   ├── run_go_tests.sh
│   │   ├── run_js_tests.sh
│   │   ├── run_py_tests.sh
│   │   └── setup_hotels_sample.sql
│   ├── test_with_coverage.sh
│   └── versioned.release.cloudbuild.yaml
├── .github
│   ├── auto-label.yaml
│   ├── blunderbuss.yml
│   ├── CODEOWNERS
│   ├── header-checker-lint.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── feature_request.yml
│   │   └── question.yml
│   ├── label-sync.yml
│   ├── labels.yaml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── release-please.yml
│   ├── renovate.json5
│   ├── sync-repo-settings.yaml
│   └── workflows
│       ├── cloud_build_failure_reporter.yml
│       ├── deploy_dev_docs.yaml
│       ├── deploy_previous_version_docs.yaml
│       ├── deploy_versioned_docs.yaml
│       ├── docs_deploy.yaml
│       ├── docs_preview_clean.yaml
│       ├── docs_preview_deploy.yaml
│       ├── lint.yaml
│       ├── schedule_reporter.yml
│       ├── sync-labels.yaml
│       └── tests.yaml
├── .gitignore
├── .gitmodules
├── .golangci.yaml
├── .hugo
│   ├── archetypes
│   │   └── default.md
│   ├── assets
│   │   ├── icons
│   │   │   └── logo.svg
│   │   └── scss
│   │       ├── _styles_project.scss
│   │       └── _variables_project.scss
│   ├── go.mod
│   ├── go.sum
│   ├── hugo.toml
│   ├── layouts
│   │   ├── _default
│   │   │   └── home.releases.releases
│   │   ├── index.llms-full.txt
│   │   ├── index.llms.txt
│   │   ├── partials
│   │   │   ├── hooks
│   │   │   │   └── head-end.html
│   │   │   ├── navbar-version-selector.html
│   │   │   ├── page-meta-links.html
│   │   │   └── td
│   │   │       └── render-heading.html
│   │   ├── robot.txt
│   │   └── shortcodes
│   │       ├── include.html
│   │       ├── ipynb.html
│   │       └── regionInclude.html
│   ├── package-lock.json
│   ├── package.json
│   └── static
│       ├── favicons
│       │   ├── android-chrome-192x192.png
│       │   ├── android-chrome-512x512.png
│       │   ├── apple-touch-icon.png
│       │   ├── favicon-16x16.png
│       │   ├── favicon-32x32.png
│       │   └── favicon.ico
│       └── js
│           └── w3.js
├── CHANGELOG.md
├── cmd
│   ├── options_test.go
│   ├── options.go
│   ├── root_test.go
│   ├── root.go
│   └── version.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── DEVELOPER.md
├── Dockerfile
├── docs
│   └── en
│       ├── _index.md
│       ├── about
│       │   ├── _index.md
│       │   └── faq.md
│       ├── concepts
│       │   ├── _index.md
│       │   └── telemetry
│       │       ├── index.md
│       │       ├── telemetry_flow.png
│       │       └── telemetry_traces.png
│       ├── getting-started
│       │   ├── _index.md
│       │   ├── colab_quickstart.ipynb
│       │   ├── configure.md
│       │   ├── introduction
│       │   │   ├── _index.md
│       │   │   └── architecture.png
│       │   ├── local_quickstart_go.md
│       │   ├── local_quickstart_js.md
│       │   ├── local_quickstart.md
│       │   ├── mcp_quickstart
│       │   │   ├── _index.md
│       │   │   ├── inspector_tools.png
│       │   │   └── inspector.png
│       │   └── quickstart
│       │       ├── go
│       │       │   ├── genAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── genkit
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── langchain
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   ├── openAI
│       │       │   │   ├── go.mod
│       │       │   │   ├── go.sum
│       │       │   │   └── quickstart.go
│       │       │   └── quickstart_test.go
│       │       ├── golden.txt
│       │       ├── js
│       │       │   ├── genAI
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── genkit
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── langchain
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   ├── llamaindex
│       │       │   │   ├── package-lock.json
│       │       │   │   ├── package.json
│       │       │   │   └── quickstart.js
│       │       │   └── quickstart.test.js
│       │       ├── python
│       │       │   ├── __init__.py
│       │       │   ├── adk
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── core
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── langchain
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   ├── llamaindex
│       │       │   │   ├── quickstart.py
│       │       │   │   └── requirements.txt
│       │       │   └── quickstart_test.py
│       │       └── shared
│       │           ├── cloud_setup.md
│       │           ├── configure_toolbox.md
│       │           └── database_setup.md
│       ├── how-to
│       │   ├── _index.md
│       │   ├── connect_via_geminicli.md
│       │   ├── connect_via_mcp.md
│       │   ├── connect-ide
│       │   │   ├── _index.md
│       │   │   ├── alloydb_pg_admin_mcp.md
│       │   │   ├── alloydb_pg_mcp.md
│       │   │   ├── bigquery_mcp.md
│       │   │   ├── cloud_sql_mssql_admin_mcp.md
│       │   │   ├── cloud_sql_mssql_mcp.md
│       │   │   ├── cloud_sql_mysql_admin_mcp.md
│       │   │   ├── cloud_sql_mysql_mcp.md
│       │   │   ├── cloud_sql_pg_admin_mcp.md
│       │   │   ├── cloud_sql_pg_mcp.md
│       │   │   ├── firestore_mcp.md
│       │   │   ├── looker_mcp.md
│       │   │   ├── mssql_mcp.md
│       │   │   ├── mysql_mcp.md
│       │   │   ├── neo4j_mcp.md
│       │   │   ├── postgres_mcp.md
│       │   │   ├── spanner_mcp.md
│       │   │   └── sqlite_mcp.md
│       │   ├── deploy_docker.md
│       │   ├── deploy_gke.md
│       │   ├── deploy_toolbox.md
│       │   ├── export_telemetry.md
│       │   └── toolbox-ui
│       │       ├── edit-headers.gif
│       │       ├── edit-headers.png
│       │       ├── index.md
│       │       ├── optional-param-checked.png
│       │       ├── optional-param-unchecked.png
│       │       ├── run-tool.gif
│       │       ├── tools.png
│       │       └── toolsets.png
│       ├── reference
│       │   ├── _index.md
│       │   ├── cli.md
│       │   └── prebuilt-tools.md
│       ├── resources
│       │   ├── _index.md
│       │   ├── authServices
│       │   │   ├── _index.md
│       │   │   └── google.md
│       │   ├── sources
│       │   │   ├── _index.md
│       │   │   ├── alloydb-admin.md
│       │   │   ├── alloydb-pg.md
│       │   │   ├── bigquery.md
│       │   │   ├── bigtable.md
│       │   │   ├── cassandra.md
│       │   │   ├── clickhouse.md
│       │   │   ├── cloud-monitoring.md
│       │   │   ├── cloud-sql-admin.md
│       │   │   ├── cloud-sql-mssql.md
│       │   │   ├── cloud-sql-mysql.md
│       │   │   ├── cloud-sql-pg.md
│       │   │   ├── couchbase.md
│       │   │   ├── dataplex.md
│       │   │   ├── dgraph.md
│       │   │   ├── firebird.md
│       │   │   ├── firestore.md
│       │   │   ├── http.md
│       │   │   ├── looker.md
│       │   │   ├── mongodb.md
│       │   │   ├── mssql.md
│       │   │   ├── mysql.md
│       │   │   ├── neo4j.md
│       │   │   ├── oceanbase.md
│       │   │   ├── oracle.md
│       │   │   ├── postgres.md
│       │   │   ├── redis.md
│       │   │   ├── spanner.md
│       │   │   ├── sqlite.md
│       │   │   ├── tidb.md
│       │   │   ├── trino.md
│       │   │   ├── valkey.md
│       │   │   └── yugabytedb.md
│       │   └── tools
│       │       ├── _index.md
│       │       ├── alloydb
│       │       │   ├── _index.md
│       │       │   ├── alloydb-create-cluster.md
│       │       │   ├── alloydb-create-instance.md
│       │       │   ├── alloydb-create-user.md
│       │       │   ├── alloydb-get-cluster.md
│       │       │   ├── alloydb-get-instance.md
│       │       │   ├── alloydb-get-user.md
│       │       │   ├── alloydb-list-clusters.md
│       │       │   ├── alloydb-list-instances.md
│       │       │   ├── alloydb-list-users.md
│       │       │   └── alloydb-wait-for-operation.md
│       │       ├── alloydbainl
│       │       │   ├── _index.md
│       │       │   └── alloydb-ai-nl.md
│       │       ├── bigquery
│       │       │   ├── _index.md
│       │       │   ├── bigquery-analyze-contribution.md
│       │       │   ├── bigquery-conversational-analytics.md
│       │       │   ├── bigquery-execute-sql.md
│       │       │   ├── bigquery-forecast.md
│       │       │   ├── bigquery-get-dataset-info.md
│       │       │   ├── bigquery-get-table-info.md
│       │       │   ├── bigquery-list-dataset-ids.md
│       │       │   ├── bigquery-list-table-ids.md
│       │       │   ├── bigquery-search-catalog.md
│       │       │   └── bigquery-sql.md
│       │       ├── bigtable
│       │       │   ├── _index.md
│       │       │   └── bigtable-sql.md
│       │       ├── cassandra
│       │       │   ├── _index.md
│       │       │   └── cassandra-cql.md
│       │       ├── clickhouse
│       │       │   ├── _index.md
│       │       │   ├── clickhouse-execute-sql.md
│       │       │   ├── clickhouse-list-databases.md
│       │       │   ├── clickhouse-list-tables.md
│       │       │   └── clickhouse-sql.md
│       │       ├── cloudmonitoring
│       │       │   ├── _index.md
│       │       │   └── cloud-monitoring-query-prometheus.md
│       │       ├── cloudsql
│       │       │   ├── _index.md
│       │       │   ├── cloudsqlcreatedatabase.md
│       │       │   ├── cloudsqlcreateusers.md
│       │       │   ├── cloudsqlgetinstances.md
│       │       │   ├── cloudsqllistdatabases.md
│       │       │   ├── cloudsqllistinstances.md
│       │       │   ├── cloudsqlmssqlcreateinstance.md
│       │       │   ├── cloudsqlmysqlcreateinstance.md
│       │       │   ├── cloudsqlpgcreateinstances.md
│       │       │   └── cloudsqlwaitforoperation.md
│       │       ├── couchbase
│       │       │   ├── _index.md
│       │       │   └── couchbase-sql.md
│       │       ├── dataform
│       │       │   ├── _index.md
│       │       │   └── dataform-compile-local.md
│       │       ├── dataplex
│       │       │   ├── _index.md
│       │       │   ├── dataplex-lookup-entry.md
│       │       │   ├── dataplex-search-aspect-types.md
│       │       │   └── dataplex-search-entries.md
│       │       ├── dgraph
│       │       │   ├── _index.md
│       │       │   └── dgraph-dql.md
│       │       ├── firebird
│       │       │   ├── _index.md
│       │       │   ├── firebird-execute-sql.md
│       │       │   └── firebird-sql.md
│       │       ├── firestore
│       │       │   ├── _index.md
│       │       │   ├── firestore-add-documents.md
│       │       │   ├── firestore-delete-documents.md
│       │       │   ├── firestore-get-documents.md
│       │       │   ├── firestore-get-rules.md
│       │       │   ├── firestore-list-collections.md
│       │       │   ├── firestore-query-collection.md
│       │       │   ├── firestore-query.md
│       │       │   ├── firestore-update-document.md
│       │       │   └── firestore-validate-rules.md
│       │       ├── http
│       │       │   ├── _index.md
│       │       │   └── http.md
│       │       ├── looker
│       │       │   ├── _index.md
│       │       │   ├── looker-add-dashboard-element.md
│       │       │   ├── looker-conversational-analytics.md
│       │       │   ├── looker-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-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
│       │       ├── mongodb
│       │       │   ├── _index.md
│       │       │   ├── mongodb-aggregate.md
│       │       │   ├── mongodb-delete-many.md
│       │       │   ├── mongodb-delete-one.md
│       │       │   ├── mongodb-find-one.md
│       │       │   ├── mongodb-find.md
│       │       │   ├── mongodb-insert-many.md
│       │       │   ├── mongodb-insert-one.md
│       │       │   ├── mongodb-update-many.md
│       │       │   └── mongodb-update-one.md
│       │       ├── mssql
│       │       │   ├── _index.md
│       │       │   ├── mssql-execute-sql.md
│       │       │   ├── mssql-list-tables.md
│       │       │   └── mssql-sql.md
│       │       ├── mysql
│       │       │   ├── _index.md
│       │       │   ├── mysql-execute-sql.md
│       │       │   ├── mysql-list-active-queries.md
│       │       │   ├── mysql-list-table-fragmentation.md
│       │       │   ├── mysql-list-tables-missing-unique-indexes.md
│       │       │   ├── mysql-list-tables.md
│       │       │   └── mysql-sql.md
│       │       ├── neo4j
│       │       │   ├── _index.md
│       │       │   ├── neo4j-cypher.md
│       │       │   ├── neo4j-execute-cypher.md
│       │       │   └── neo4j-schema.md
│       │       ├── oceanbase
│       │       │   ├── _index.md
│       │       │   ├── oceanbase-execute-sql.md
│       │       │   └── oceanbase-sql.md
│       │       ├── oracle
│       │       │   ├── _index.md
│       │       │   ├── oracle-execute-sql.md
│       │       │   └── oracle-sql.md
│       │       ├── postgres
│       │       │   ├── _index.md
│       │       │   ├── postgres-execute-sql.md
│       │       │   ├── postgres-list-active-queries.md
│       │       │   ├── postgres-list-available-extensions.md
│       │       │   ├── postgres-list-installed-extensions.md
│       │       │   ├── postgres-list-tables.md
│       │       │   └── postgres-sql.md
│       │       ├── redis
│       │       │   ├── _index.md
│       │       │   └── redis.md
│       │       ├── spanner
│       │       │   ├── _index.md
│       │       │   ├── spanner-execute-sql.md
│       │       │   ├── spanner-list-tables.md
│       │       │   └── spanner-sql.md
│       │       ├── sqlite
│       │       │   ├── _index.md
│       │       │   ├── sqlite-execute-sql.md
│       │       │   └── sqlite-sql.md
│       │       ├── tidb
│       │       │   ├── _index.md
│       │       │   ├── tidb-execute-sql.md
│       │       │   └── tidb-sql.md
│       │       ├── trino
│       │       │   ├── _index.md
│       │       │   ├── trino-execute-sql.md
│       │       │   └── trino-sql.md
│       │       ├── utility
│       │       │   ├── _index.md
│       │       │   └── wait.md
│       │       ├── valkey
│       │       │   ├── _index.md
│       │       │   └── valkey.md
│       │       └── yuagbytedb
│       │           ├── _index.md
│       │           └── yugabytedb-sql.md
│       ├── samples
│       │   ├── _index.md
│       │   ├── alloydb
│       │   │   ├── _index.md
│       │   │   ├── ai-nl
│       │   │   │   ├── alloydb_ai_nl.ipynb
│       │   │   │   └── index.md
│       │   │   └── mcp_quickstart.md
│       │   ├── bigquery
│       │   │   ├── _index.md
│       │   │   ├── colab_quickstart_bigquery.ipynb
│       │   │   ├── local_quickstart.md
│       │   │   └── mcp_quickstart
│       │   │       ├── _index.md
│       │   │       ├── inspector_tools.png
│       │   │       └── inspector.png
│       │   └── looker
│       │       ├── _index.md
│       │       ├── looker_gemini_oauth
│       │       │   ├── _index.md
│       │       │   ├── authenticated.png
│       │       │   ├── authorize.png
│       │       │   └── registration.png
│       │       ├── looker_gemini.md
│       │       └── looker_mcp_inspector
│       │           ├── _index.md
│       │           ├── inspector_tools.png
│       │           └── inspector.png
│       └── sdks
│           ├── _index.md
│           ├── go-sdk.md
│           ├── js-sdk.md
│           └── python-sdk.md
├── gemini-extension.json
├── go.mod
├── go.sum
├── internal
│   ├── auth
│   │   ├── auth.go
│   │   └── google
│   │       └── google.go
│   ├── log
│   │   ├── handler.go
│   │   ├── log_test.go
│   │   ├── log.go
│   │   └── logger.go
│   ├── prebuiltconfigs
│   │   ├── prebuiltconfigs_test.go
│   │   ├── prebuiltconfigs.go
│   │   └── tools
│   │       ├── alloydb-postgres-admin.yaml
│   │       ├── alloydb-postgres-observability.yaml
│   │       ├── alloydb-postgres.yaml
│   │       ├── bigquery.yaml
│   │       ├── clickhouse.yaml
│   │       ├── cloud-sql-mssql-admin.yaml
│   │       ├── cloud-sql-mssql-observability.yaml
│   │       ├── cloud-sql-mssql.yaml
│   │       ├── cloud-sql-mysql-admin.yaml
│   │       ├── cloud-sql-mysql-observability.yaml
│   │       ├── cloud-sql-mysql.yaml
│   │       ├── cloud-sql-postgres-admin.yaml
│   │       ├── cloud-sql-postgres-observability.yaml
│   │       ├── cloud-sql-postgres.yaml
│   │       ├── dataplex.yaml
│   │       ├── firestore.yaml
│   │       ├── looker-conversational-analytics.yaml
│   │       ├── looker.yaml
│   │       ├── mssql.yaml
│   │       ├── mysql.yaml
│   │       ├── neo4j.yaml
│   │       ├── oceanbase.yaml
│   │       ├── postgres.yaml
│   │       ├── spanner-postgres.yaml
│   │       ├── spanner.yaml
│   │       └── sqlite.yaml
│   ├── server
│   │   ├── api_test.go
│   │   ├── api.go
│   │   ├── common_test.go
│   │   ├── config.go
│   │   ├── mcp
│   │   │   ├── jsonrpc
│   │   │   │   ├── jsonrpc_test.go
│   │   │   │   └── jsonrpc.go
│   │   │   ├── mcp.go
│   │   │   ├── util
│   │   │   │   └── lifecycle.go
│   │   │   ├── v20241105
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   ├── v20250326
│   │   │   │   ├── method.go
│   │   │   │   └── types.go
│   │   │   └── v20250618
│   │   │       ├── method.go
│   │   │       └── types.go
│   │   ├── mcp_test.go
│   │   ├── mcp.go
│   │   ├── server_test.go
│   │   ├── server.go
│   │   ├── static
│   │   │   ├── assets
│   │   │   │   └── mcptoolboxlogo.png
│   │   │   ├── css
│   │   │   │   └── style.css
│   │   │   ├── index.html
│   │   │   ├── js
│   │   │   │   ├── auth.js
│   │   │   │   ├── loadTools.js
│   │   │   │   ├── mainContent.js
│   │   │   │   ├── navbar.js
│   │   │   │   ├── runTool.js
│   │   │   │   ├── toolDisplay.js
│   │   │   │   ├── tools.js
│   │   │   │   └── toolsets.js
│   │   │   ├── tools.html
│   │   │   └── toolsets.html
│   │   ├── web_test.go
│   │   └── web.go
│   ├── sources
│   │   ├── alloydbadmin
│   │   │   ├── alloydbadmin_test.go
│   │   │   └── alloydbadmin.go
│   │   ├── alloydbpg
│   │   │   ├── alloydb_pg_test.go
│   │   │   └── alloydb_pg.go
│   │   ├── bigquery
│   │   │   ├── bigquery_test.go
│   │   │   └── bigquery.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   ├── cassandra_test.go
│   │   │   └── cassandra.go
│   │   ├── clickhouse
│   │   │   ├── clickhouse_test.go
│   │   │   └── clickhouse.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloud_monitoring_test.go
│   │   │   └── cloud_monitoring.go
│   │   ├── cloudsqladmin
│   │   │   ├── cloud_sql_admin_test.go
│   │   │   └── cloud_sql_admin.go
│   │   ├── cloudsqlmssql
│   │   │   ├── cloud_sql_mssql_test.go
│   │   │   └── cloud_sql_mssql.go
│   │   ├── cloudsqlmysql
│   │   │   ├── cloud_sql_mysql_test.go
│   │   │   └── cloud_sql_mysql.go
│   │   ├── cloudsqlpg
│   │   │   ├── cloud_sql_pg_test.go
│   │   │   └── cloud_sql_pg.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataplex
│   │   │   ├── dataplex_test.go
│   │   │   └── dataplex.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── dialect.go
│   │   ├── firebird
│   │   │   ├── firebird_test.go
│   │   │   └── firebird.go
│   │   ├── firestore
│   │   │   ├── firestore_test.go
│   │   │   └── firestore.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── ip_type.go
│   │   ├── looker
│   │   │   ├── looker_test.go
│   │   │   └── looker.go
│   │   ├── mongodb
│   │   │   ├── mongodb_test.go
│   │   │   └── mongodb.go
│   │   ├── mssql
│   │   │   ├── mssql_test.go
│   │   │   └── mssql.go
│   │   ├── mysql
│   │   │   ├── mysql_test.go
│   │   │   └── mysql.go
│   │   ├── neo4j
│   │   │   ├── neo4j_test.go
│   │   │   └── neo4j.go
│   │   ├── oceanbase
│   │   │   ├── oceanbase_test.go
│   │   │   └── oceanbase.go
│   │   ├── oracle
│   │   │   └── oracle.go
│   │   ├── postgres
│   │   │   ├── postgres_test.go
│   │   │   └── postgres.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── sources.go
│   │   ├── spanner
│   │   │   ├── spanner_test.go
│   │   │   └── spanner.go
│   │   ├── sqlite
│   │   │   ├── sqlite_test.go
│   │   │   └── sqlite.go
│   │   ├── tidb
│   │   │   ├── tidb_test.go
│   │   │   └── tidb.go
│   │   ├── trino
│   │   │   ├── trino_test.go
│   │   │   └── trino.go
│   │   ├── util.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedb
│   │       ├── yugabytedb_test.go
│   │       └── yugabytedb.go
│   ├── telemetry
│   │   ├── instrumentation.go
│   │   └── telemetry.go
│   ├── testutils
│   │   └── testutils.go
│   ├── tools
│   │   ├── alloydb
│   │   │   ├── alloydbcreatecluster
│   │   │   │   ├── alloydbcreatecluster_test.go
│   │   │   │   └── alloydbcreatecluster.go
│   │   │   ├── alloydbcreateinstance
│   │   │   │   ├── alloydbcreateinstance_test.go
│   │   │   │   └── alloydbcreateinstance.go
│   │   │   ├── alloydbcreateuser
│   │   │   │   ├── alloydbcreateuser_test.go
│   │   │   │   └── alloydbcreateuser.go
│   │   │   ├── alloydbgetcluster
│   │   │   │   ├── alloydbgetcluster_test.go
│   │   │   │   └── alloydbgetcluster.go
│   │   │   ├── alloydbgetinstance
│   │   │   │   ├── alloydbgetinstance_test.go
│   │   │   │   └── alloydbgetinstance.go
│   │   │   ├── alloydbgetuser
│   │   │   │   ├── alloydbgetuser_test.go
│   │   │   │   └── alloydbgetuser.go
│   │   │   ├── alloydblistclusters
│   │   │   │   ├── alloydblistclusters_test.go
│   │   │   │   └── alloydblistclusters.go
│   │   │   ├── alloydblistinstances
│   │   │   │   ├── alloydblistinstances_test.go
│   │   │   │   └── alloydblistinstances.go
│   │   │   ├── alloydblistusers
│   │   │   │   ├── alloydblistusers_test.go
│   │   │   │   └── alloydblistusers.go
│   │   │   └── alloydbwaitforoperation
│   │   │       ├── alloydbwaitforoperation_test.go
│   │   │       └── alloydbwaitforoperation.go
│   │   ├── alloydbainl
│   │   │   ├── alloydbainl_test.go
│   │   │   └── alloydbainl.go
│   │   ├── bigquery
│   │   │   ├── bigqueryanalyzecontribution
│   │   │   │   ├── bigqueryanalyzecontribution_test.go
│   │   │   │   └── bigqueryanalyzecontribution.go
│   │   │   ├── bigquerycommon
│   │   │   │   ├── table_name_parser_test.go
│   │   │   │   ├── table_name_parser.go
│   │   │   │   └── util.go
│   │   │   ├── bigqueryconversationalanalytics
│   │   │   │   ├── bigqueryconversationalanalytics_test.go
│   │   │   │   └── bigqueryconversationalanalytics.go
│   │   │   ├── bigqueryexecutesql
│   │   │   │   ├── bigqueryexecutesql_test.go
│   │   │   │   └── bigqueryexecutesql.go
│   │   │   ├── bigqueryforecast
│   │   │   │   ├── bigqueryforecast_test.go
│   │   │   │   └── bigqueryforecast.go
│   │   │   ├── bigquerygetdatasetinfo
│   │   │   │   ├── bigquerygetdatasetinfo_test.go
│   │   │   │   └── bigquerygetdatasetinfo.go
│   │   │   ├── bigquerygettableinfo
│   │   │   │   ├── bigquerygettableinfo_test.go
│   │   │   │   └── bigquerygettableinfo.go
│   │   │   ├── bigquerylistdatasetids
│   │   │   │   ├── bigquerylistdatasetids_test.go
│   │   │   │   └── bigquerylistdatasetids.go
│   │   │   ├── bigquerylisttableids
│   │   │   │   ├── bigquerylisttableids_test.go
│   │   │   │   └── bigquerylisttableids.go
│   │   │   ├── bigquerysearchcatalog
│   │   │   │   ├── bigquerysearchcatalog_test.go
│   │   │   │   └── bigquerysearchcatalog.go
│   │   │   └── bigquerysql
│   │   │       ├── bigquerysql_test.go
│   │   │       └── bigquerysql.go
│   │   ├── bigtable
│   │   │   ├── bigtable_test.go
│   │   │   └── bigtable.go
│   │   ├── cassandra
│   │   │   └── cassandracql
│   │   │       ├── cassandracql_test.go
│   │   │       └── cassandracql.go
│   │   ├── clickhouse
│   │   │   ├── clickhouseexecutesql
│   │   │   │   ├── clickhouseexecutesql_test.go
│   │   │   │   └── clickhouseexecutesql.go
│   │   │   ├── clickhouselistdatabases
│   │   │   │   ├── clickhouselistdatabases_test.go
│   │   │   │   └── clickhouselistdatabases.go
│   │   │   ├── clickhouselisttables
│   │   │   │   ├── clickhouselisttables_test.go
│   │   │   │   └── clickhouselisttables.go
│   │   │   └── clickhousesql
│   │   │       ├── clickhousesql_test.go
│   │   │       └── clickhousesql.go
│   │   ├── cloudmonitoring
│   │   │   ├── cloudmonitoring_test.go
│   │   │   └── cloudmonitoring.go
│   │   ├── cloudsql
│   │   │   ├── cloudsqlcreatedatabase
│   │   │   │   ├── cloudsqlcreatedatabase_test.go
│   │   │   │   └── cloudsqlcreatedatabase.go
│   │   │   ├── cloudsqlcreateusers
│   │   │   │   ├── cloudsqlcreateusers_test.go
│   │   │   │   └── cloudsqlcreateusers.go
│   │   │   ├── cloudsqlgetinstances
│   │   │   │   ├── cloudsqlgetinstances_test.go
│   │   │   │   └── cloudsqlgetinstances.go
│   │   │   ├── cloudsqllistdatabases
│   │   │   │   ├── cloudsqllistdatabases_test.go
│   │   │   │   └── cloudsqllistdatabases.go
│   │   │   ├── cloudsqllistinstances
│   │   │   │   ├── cloudsqllistinstances_test.go
│   │   │   │   └── cloudsqllistinstances.go
│   │   │   └── cloudsqlwaitforoperation
│   │   │       ├── cloudsqlwaitforoperation_test.go
│   │   │       └── cloudsqlwaitforoperation.go
│   │   ├── cloudsqlmssql
│   │   │   └── cloudsqlmssqlcreateinstance
│   │   │       ├── cloudsqlmssqlcreateinstance_test.go
│   │   │       └── cloudsqlmssqlcreateinstance.go
│   │   ├── cloudsqlmysql
│   │   │   └── cloudsqlmysqlcreateinstance
│   │   │       ├── cloudsqlmysqlcreateinstance_test.go
│   │   │       └── cloudsqlmysqlcreateinstance.go
│   │   ├── cloudsqlpg
│   │   │   └── cloudsqlpgcreateinstances
│   │   │       ├── cloudsqlpgcreateinstances_test.go
│   │   │       └── cloudsqlpgcreateinstances.go
│   │   ├── common_test.go
│   │   ├── common.go
│   │   ├── couchbase
│   │   │   ├── couchbase_test.go
│   │   │   └── couchbase.go
│   │   ├── dataform
│   │   │   └── dataformcompilelocal
│   │   │       ├── dataformcompilelocal_test.go
│   │   │       └── dataformcompilelocal.go
│   │   ├── dataplex
│   │   │   ├── dataplexlookupentry
│   │   │   │   ├── dataplexlookupentry_test.go
│   │   │   │   └── dataplexlookupentry.go
│   │   │   ├── dataplexsearchaspecttypes
│   │   │   │   ├── dataplexsearchaspecttypes_test.go
│   │   │   │   └── dataplexsearchaspecttypes.go
│   │   │   └── dataplexsearchentries
│   │   │       ├── dataplexsearchentries_test.go
│   │   │       └── dataplexsearchentries.go
│   │   ├── dgraph
│   │   │   ├── dgraph_test.go
│   │   │   └── dgraph.go
│   │   ├── firebird
│   │   │   ├── firebirdexecutesql
│   │   │   │   ├── firebirdexecutesql_test.go
│   │   │   │   └── firebirdexecutesql.go
│   │   │   └── firebirdsql
│   │   │       ├── firebirdsql_test.go
│   │   │       └── firebirdsql.go
│   │   ├── firestore
│   │   │   ├── firestoreadddocuments
│   │   │   │   ├── firestoreadddocuments_test.go
│   │   │   │   └── firestoreadddocuments.go
│   │   │   ├── firestoredeletedocuments
│   │   │   │   ├── firestoredeletedocuments_test.go
│   │   │   │   └── firestoredeletedocuments.go
│   │   │   ├── firestoregetdocuments
│   │   │   │   ├── firestoregetdocuments_test.go
│   │   │   │   └── firestoregetdocuments.go
│   │   │   ├── firestoregetrules
│   │   │   │   ├── firestoregetrules_test.go
│   │   │   │   └── firestoregetrules.go
│   │   │   ├── firestorelistcollections
│   │   │   │   ├── firestorelistcollections_test.go
│   │   │   │   └── firestorelistcollections.go
│   │   │   ├── firestorequery
│   │   │   │   ├── firestorequery_test.go
│   │   │   │   └── firestorequery.go
│   │   │   ├── firestorequerycollection
│   │   │   │   ├── firestorequerycollection_test.go
│   │   │   │   └── firestorequerycollection.go
│   │   │   ├── firestoreupdatedocument
│   │   │   │   ├── firestoreupdatedocument_test.go
│   │   │   │   └── firestoreupdatedocument.go
│   │   │   ├── firestorevalidaterules
│   │   │   │   ├── firestorevalidaterules_test.go
│   │   │   │   └── firestorevalidaterules.go
│   │   │   └── util
│   │   │       ├── converter_test.go
│   │   │       ├── converter.go
│   │   │       ├── validator_test.go
│   │   │       └── validator.go
│   │   ├── http
│   │   │   ├── http_test.go
│   │   │   └── http.go
│   │   ├── http_method.go
│   │   ├── looker
│   │   │   ├── lookeradddashboardelement
│   │   │   │   ├── lookeradddashboardelement_test.go
│   │   │   │   └── lookeradddashboardelement.go
│   │   │   ├── lookercommon
│   │   │   │   ├── lookercommon_test.go
│   │   │   │   └── lookercommon.go
│   │   │   ├── lookerconversationalanalytics
│   │   │   │   ├── lookerconversationalanalytics_test.go
│   │   │   │   └── lookerconversationalanalytics.go
│   │   │   ├── 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
│   │   │   ├── 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
│   │   ├── mongodb
│   │   │   ├── mongodbaggregate
│   │   │   │   ├── mongodbaggregate_test.go
│   │   │   │   └── mongodbaggregate.go
│   │   │   ├── mongodbdeletemany
│   │   │   │   ├── mongodbdeletemany_test.go
│   │   │   │   └── mongodbdeletemany.go
│   │   │   ├── mongodbdeleteone
│   │   │   │   ├── mongodbdeleteone_test.go
│   │   │   │   └── mongodbdeleteone.go
│   │   │   ├── mongodbfind
│   │   │   │   ├── mongodbfind_test.go
│   │   │   │   └── mongodbfind.go
│   │   │   ├── mongodbfindone
│   │   │   │   ├── mongodbfindone_test.go
│   │   │   │   └── mongodbfindone.go
│   │   │   ├── mongodbinsertmany
│   │   │   │   ├── mongodbinsertmany_test.go
│   │   │   │   └── mongodbinsertmany.go
│   │   │   ├── mongodbinsertone
│   │   │   │   ├── mongodbinsertone_test.go
│   │   │   │   └── mongodbinsertone.go
│   │   │   ├── mongodbupdatemany
│   │   │   │   ├── mongodbupdatemany_test.go
│   │   │   │   └── mongodbupdatemany.go
│   │   │   └── mongodbupdateone
│   │   │       ├── mongodbupdateone_test.go
│   │   │       └── mongodbupdateone.go
│   │   ├── mssql
│   │   │   ├── mssqlexecutesql
│   │   │   │   ├── mssqlexecutesql_test.go
│   │   │   │   └── mssqlexecutesql.go
│   │   │   ├── mssqllisttables
│   │   │   │   ├── mssqllisttables_test.go
│   │   │   │   └── mssqllisttables.go
│   │   │   └── mssqlsql
│   │   │       ├── mssqlsql_test.go
│   │   │       └── mssqlsql.go
│   │   ├── mysql
│   │   │   ├── mysqlcommon
│   │   │   │   └── mysqlcommon.go
│   │   │   ├── mysqlexecutesql
│   │   │   │   ├── mysqlexecutesql_test.go
│   │   │   │   └── mysqlexecutesql.go
│   │   │   ├── mysqllistactivequeries
│   │   │   │   ├── mysqllistactivequeries_test.go
│   │   │   │   └── mysqllistactivequeries.go
│   │   │   ├── mysqllisttablefragmentation
│   │   │   │   ├── mysqllisttablefragmentation_test.go
│   │   │   │   └── mysqllisttablefragmentation.go
│   │   │   ├── mysqllisttables
│   │   │   │   ├── mysqllisttables_test.go
│   │   │   │   └── mysqllisttables.go
│   │   │   ├── mysqllisttablesmissinguniqueindexes
│   │   │   │   ├── mysqllisttablesmissinguniqueindexes_test.go
│   │   │   │   └── mysqllisttablesmissinguniqueindexes.go
│   │   │   └── mysqlsql
│   │   │       ├── mysqlsql_test.go
│   │   │       └── mysqlsql.go
│   │   ├── neo4j
│   │   │   ├── neo4jcypher
│   │   │   │   ├── neo4jcypher_test.go
│   │   │   │   └── neo4jcypher.go
│   │   │   ├── neo4jexecutecypher
│   │   │   │   ├── classifier
│   │   │   │   │   ├── classifier_test.go
│   │   │   │   │   └── classifier.go
│   │   │   │   ├── neo4jexecutecypher_test.go
│   │   │   │   └── neo4jexecutecypher.go
│   │   │   └── neo4jschema
│   │   │       ├── cache
│   │   │       │   ├── cache_test.go
│   │   │       │   └── cache.go
│   │   │       ├── helpers
│   │   │       │   ├── helpers_test.go
│   │   │       │   └── helpers.go
│   │   │       ├── neo4jschema_test.go
│   │   │       ├── neo4jschema.go
│   │   │       └── types
│   │   │           └── types.go
│   │   ├── oceanbase
│   │   │   ├── oceanbaseexecutesql
│   │   │   │   ├── oceanbaseexecutesql_test.go
│   │   │   │   └── oceanbaseexecutesql.go
│   │   │   └── oceanbasesql
│   │   │       ├── oceanbasesql_test.go
│   │   │       └── oceanbasesql.go
│   │   ├── oracle
│   │   │   ├── oracleexecutesql
│   │   │   │   └── oracleexecutesql.go
│   │   │   └── oraclesql
│   │   │       └── oraclesql.go
│   │   ├── parameters_test.go
│   │   ├── parameters.go
│   │   ├── postgres
│   │   │   ├── postgresexecutesql
│   │   │   │   ├── postgresexecutesql_test.go
│   │   │   │   └── postgresexecutesql.go
│   │   │   ├── postgreslistactivequeries
│   │   │   │   ├── postgreslistactivequeries_test.go
│   │   │   │   └── postgreslistactivequeries.go
│   │   │   ├── postgreslistavailableextensions
│   │   │   │   ├── postgreslistavailableextensions_test.go
│   │   │   │   └── postgreslistavailableextensions.go
│   │   │   ├── postgreslistinstalledextensions
│   │   │   │   ├── postgreslistinstalledextensions_test.go
│   │   │   │   └── postgreslistinstalledextensions.go
│   │   │   ├── postgreslisttables
│   │   │   │   ├── postgreslisttables_test.go
│   │   │   │   └── postgreslisttables.go
│   │   │   └── postgressql
│   │   │       ├── postgressql_test.go
│   │   │       └── postgressql.go
│   │   ├── redis
│   │   │   ├── redis_test.go
│   │   │   └── redis.go
│   │   ├── spanner
│   │   │   ├── spannerexecutesql
│   │   │   │   ├── spannerexecutesql_test.go
│   │   │   │   └── spannerexecutesql.go
│   │   │   ├── spannerlisttables
│   │   │   │   ├── spannerlisttables_test.go
│   │   │   │   └── spannerlisttables.go
│   │   │   └── spannersql
│   │   │       ├── spanner_test.go
│   │   │       └── spannersql.go
│   │   ├── sqlite
│   │   │   ├── sqliteexecutesql
│   │   │   │   ├── sqliteexecutesql_test.go
│   │   │   │   └── sqliteexecutesql.go
│   │   │   └── sqlitesql
│   │   │       ├── sqlitesql_test.go
│   │   │       └── sqlitesql.go
│   │   ├── tidb
│   │   │   ├── tidbexecutesql
│   │   │   │   ├── tidbexecutesql_test.go
│   │   │   │   └── tidbexecutesql.go
│   │   │   └── tidbsql
│   │   │       ├── tidbsql_test.go
│   │   │       └── tidbsql.go
│   │   ├── tools_test.go
│   │   ├── tools.go
│   │   ├── toolsets.go
│   │   ├── trino
│   │   │   ├── trinoexecutesql
│   │   │   │   ├── trinoexecutesql_test.go
│   │   │   │   └── trinoexecutesql.go
│   │   │   └── trinosql
│   │   │       ├── trinosql_test.go
│   │   │       └── trinosql.go
│   │   ├── utility
│   │   │   └── wait
│   │   │       ├── wait_test.go
│   │   │       └── wait.go
│   │   ├── valkey
│   │   │   ├── valkey_test.go
│   │   │   └── valkey.go
│   │   └── yugabytedbsql
│   │       ├── yugabytedbsql_test.go
│   │       └── yugabytedbsql.go
│   └── util
│       └── util.go
├── LICENSE
├── logo.png
├── main.go
├── MCP-TOOLBOX-EXTENSION.md
├── README.md
└── tests
    ├── alloydb
    │   ├── alloydb_integration_test.go
    │   └── alloydb_wait_for_operation_test.go
    ├── alloydbainl
    │   └── alloydb_ai_nl_integration_test.go
    ├── alloydbpg
    │   └── alloydb_pg_integration_test.go
    ├── auth.go
    ├── bigquery
    │   └── bigquery_integration_test.go
    ├── bigtable
    │   └── bigtable_integration_test.go
    ├── cassandra
    │   └── cassandra_integration_test.go
    ├── clickhouse
    │   └── clickhouse_integration_test.go
    ├── cloudmonitoring
    │   └── cloud_monitoring_integration_test.go
    ├── cloudsql
    │   ├── cloud_sql_create_database_test.go
    │   ├── cloud_sql_create_users_test.go
    │   ├── cloud_sql_get_instances_test.go
    │   ├── cloud_sql_list_databases_test.go
    │   ├── cloudsql_list_instances_test.go
    │   └── cloudsql_wait_for_operation_test.go
    ├── cloudsqlmssql
    │   ├── cloud_sql_mssql_create_instance_integration_test.go
    │   └── cloud_sql_mssql_integration_test.go
    ├── cloudsqlmysql
    │   ├── cloud_sql_mysql_create_instance_integration_test.go
    │   └── cloud_sql_mysql_integration_test.go
    ├── cloudsqlpg
    │   ├── cloud_sql_pg_create_instances_test.go
    │   └── cloud_sql_pg_integration_test.go
    ├── common.go
    ├── couchbase
    │   └── couchbase_integration_test.go
    ├── dataform
    │   └── dataform_integration_test.go
    ├── dataplex
    │   └── dataplex_integration_test.go
    ├── dgraph
    │   └── dgraph_integration_test.go
    ├── firebird
    │   └── firebird_integration_test.go
    ├── firestore
    │   └── firestore_integration_test.go
    ├── http
    │   └── http_integration_test.go
    ├── looker
    │   └── looker_integration_test.go
    ├── mongodb
    │   └── mongodb_integration_test.go
    ├── mssql
    │   └── mssql_integration_test.go
    ├── mysql
    │   └── mysql_integration_test.go
    ├── neo4j
    │   └── neo4j_integration_test.go
    ├── oceanbase
    │   └── oceanbase_integration_test.go
    ├── option.go
    ├── oracle
    │   └── oracle_integration_test.go
    ├── postgres
    │   └── postgres_integration_test.go
    ├── redis
    │   └── redis_test.go
    ├── server.go
    ├── source.go
    ├── spanner
    │   └── spanner_integration_test.go
    ├── sqlite
    │   └── sqlite_integration_test.go
    ├── tidb
    │   └── tidb_integration_test.go
    ├── tool.go
    ├── trino
    │   └── trino_integration_test.go
    ├── utility
    │   └── wait_integration_test.go
    ├── valkey
    │   └── valkey_test.go
    └── yugabytedb
        └── yugabytedb_integration_test.go
```

# Files

--------------------------------------------------------------------------------
/internal/tools/neo4j/neo4jschema/types/types.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 types contains the shared data structures for Neo4j schema representation.
 16 | package types
 17 | 
 18 | // ValueType interface representing a Neo4j value.
 19 | type ValueType interface {
 20 | 	String() string
 21 | }
 22 | 
 23 | // SchemaInfo represents the complete database schema.
 24 | type SchemaInfo struct {
 25 | 	NodeLabels    []NodeLabel    `json:"nodeLabels"`
 26 | 	Relationships []Relationship `json:"relationships"`
 27 | 	Constraints   []Constraint   `json:"constraints"`
 28 | 	Indexes       []Index        `json:"indexes"`
 29 | 	DatabaseInfo  DatabaseInfo   `json:"databaseInfo"`
 30 | 	Statistics    Statistics     `json:"statistics"`
 31 | 	Errors        []string       `json:"errors,omitempty"`
 32 | }
 33 | 
 34 | // NodeLabel represents a node label with its properties.
 35 | type NodeLabel struct {
 36 | 	Name       string         `json:"name"`
 37 | 	Properties []PropertyInfo `json:"properties"`
 38 | 	Count      int64          `json:"count"`
 39 | }
 40 | 
 41 | // RelConnectivityInfo holds information about a relationship's start and end nodes,
 42 | // primarily used during schema extraction without APOC procedures.
 43 | type RelConnectivityInfo struct {
 44 | 	StartNode string
 45 | 	EndNode   string
 46 | 	Count     int64
 47 | }
 48 | 
 49 | // Relationship represents a relationship type with its properties.
 50 | type Relationship struct {
 51 | 	Type       string         `json:"type"`
 52 | 	Properties []PropertyInfo `json:"properties"`
 53 | 	StartNode  string         `json:"startNode,omitempty"`
 54 | 	EndNode    string         `json:"endNode,omitempty"`
 55 | 	Count      int64          `json:"count"`
 56 | }
 57 | 
 58 | // PropertyInfo represents a property with its data types.
 59 | type PropertyInfo struct {
 60 | 	Name      string   `json:"name"`
 61 | 	Types     []string `json:"types"`
 62 | 	Mandatory bool     `json:"-"`
 63 | 	Unique    bool     `json:"-"`
 64 | 	Indexed   bool     `json:"-"`
 65 | }
 66 | 
 67 | // Constraint represents a database constraint.
 68 | type Constraint struct {
 69 | 	Name       string   `json:"name"`
 70 | 	Type       string   `json:"type"`
 71 | 	EntityType string   `json:"entityType"`
 72 | 	Label      string   `json:"label,omitempty"`
 73 | 	Properties []string `json:"properties"`
 74 | }
 75 | 
 76 | // Index represents a database index.
 77 | type Index struct {
 78 | 	Name       string   `json:"name"`
 79 | 	State      string   `json:"state"`
 80 | 	Type       string   `json:"type"`
 81 | 	EntityType string   `json:"entityType"`
 82 | 	Label      string   `json:"label,omitempty"`
 83 | 	Properties []string `json:"properties"`
 84 | }
 85 | 
 86 | // DatabaseInfo contains general database information.
 87 | type DatabaseInfo struct {
 88 | 	Name    string `json:"name"`
 89 | 	Version string `json:"version"`
 90 | 	Edition string `json:"edition,omitempty"`
 91 | }
 92 | 
 93 | // Statistics contains database statistics.
 94 | type Statistics struct {
 95 | 	TotalNodes          int64            `json:"totalNodes"`
 96 | 	TotalRelationships  int64            `json:"totalRelationships"`
 97 | 	TotalProperties     int64            `json:"totalProperties"`
 98 | 	NodesByLabel        map[string]int64 `json:"nodesByLabel"`
 99 | 	RelationshipsByType map[string]int64 `json:"relationshipsByType"`
100 | 	PropertiesByLabel   map[string]int64 `json:"propertiesByLabel"`
101 | 	PropertiesByRelType map[string]int64 `json:"propertiesByRelType"`
102 | }
103 | 
104 | // APOCSchemaResult represents the result from apoc.meta.schema().
105 | type APOCSchemaResult struct {
106 | 	Value map[string]APOCEntity `json:"value"`
107 | }
108 | 
109 | // APOCEntity represents a node or relationship in APOC schema.
110 | type APOCEntity struct {
111 | 	Type          string                          `json:"type"`
112 | 	Count         int64                           `json:"count"`
113 | 	Labels        []string                        `json:"labels,omitempty"`
114 | 	Properties    map[string]APOCProperty         `json:"properties"`
115 | 	Relationships map[string]APOCRelationshipInfo `json:"relationships,omitempty"`
116 | }
117 | 
118 | // APOCProperty represents property info from APOC.
119 | type APOCProperty struct {
120 | 	Type      string `json:"type"`
121 | 	Indexed   bool   `json:"indexed"`
122 | 	Unique    bool   `json:"unique"`
123 | 	Existence bool   `json:"existence"`
124 | }
125 | 
126 | // APOCRelationshipInfo represents relationship info from APOC.
127 | type APOCRelationshipInfo struct {
128 | 	Count      int64                   `json:"count"`
129 | 	Direction  string                  `json:"direction"`
130 | 	Labels     []string                `json:"labels"`
131 | 	Properties map[string]APOCProperty `json:"properties"`
132 | }
133 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/cassandra/cassandra-cql.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "cassandra-cql"
 3 | type: docs
 4 | weight: 1
 5 | description: > 
 6 |   A "cassandra-cql" tool executes a pre-defined CQL statement against a Cassandra
 7 |   database.
 8 | aliases:
 9 | - /resources/tools/cassandra-cql
10 | ---
11 | 
12 | ## About
13 | 
14 | A `cassandra-cql` tool executes a pre-defined CQL statement against a Cassandra
15 | database. It's compatible with any of the following sources:
16 | 
17 | - [cassandra](../sources/cassandra.md)
18 | 
19 | The specified CQL statement is executed as a [prepared statement][cassandra-prepare],
20 | and expects parameters in the CQL query to be in the form of placeholders `?`.
21 | 
22 | [cassandra-prepare]: https://docs.datastax.com/en/developer/go-driver/4.8/cql-prepared-statements/
23 | 
24 | ## Example
25 | 
26 | > **Note:** This tool uses parameterized queries to prevent CQL injections.
27 | > Query parameters can be used as substitutes for arbitrary expressions.
28 | > Parameters cannot be used as substitutes for keyspaces, table names, column names,
29 | > or other parts of the query.
30 | 
31 | ```yaml
32 | tools:
33 |   search_users_by_email:
34 |     kind: cassandra-cql
35 |     source: my-cassandra-cluster
36 |     statement: |
37 |       SELECT user_id, email, first_name, last_name, created_at 
38 |       FROM users 
39 |       WHERE email = ?
40 |     description: |
41 |       Use this tool to retrieve specific user information by their email address.
42 |       Takes an email address and returns user details including user ID, email, 
43 |       first name, last name, and account creation timestamp.
44 |       Do NOT use this tool with a user ID or other identifiers.
45 |       Example:
46 |       {{
47 |           "email": "[email protected]",
48 |       }}
49 |     parameters:
50 |       - name: email
51 |         type: string
52 |         description: User's email address
53 | ```
54 | 
55 | ### Example with Template Parameters
56 | 
57 | > **Note:** This tool allows direct modifications to the CQL statement,
58 | > including keyspaces, table names, and column names. **This makes it more
59 | > vulnerable to CQL injections**. Using basic parameters only (see above) is
60 | > recommended for performance and safety reasons. For more details, please check
61 | > [templateParameters](../#template-parameters).
62 | 
63 | ```yaml
64 | tools:
65 |   list_keyspace_table:
66 |     kind: cassandra-cql
67 |     source: my-cassandra-cluster
68 |     statement: |
69 |       SELECT * FROM {{.keyspace}}.{{.tableName}};
70 |     description: |
71 |       Use this tool to list all information from a specific table in a keyspace.
72 |       Example:
73 |       {{
74 |           "keyspace": "my_keyspace",
75 |           "tableName": "users",
76 |       }}
77 |     templateParameters:
78 |       - name: keyspace
79 |         type: string
80 |         description: Keyspace containing the table
81 |       - name: tableName
82 |         type: string
83 |         description: Table to select from
84 | ```
85 | 
86 | ## Reference
87 | 
88 | | **field**          |                  **type**                        | **required** | **description**                                                                                                                            |
89 | |--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
90 | | kind               |                   string                         |     true     | Must be "cassandra-cql".                                                                                                                   |
91 | | source             |                   string                         |     true     | Name of the source the CQL should execute on.                                                                                              |
92 | | description        |                   string                         |     true     | Description of the tool that is passed to the LLM.                                                                                         |
93 | | statement          |                   string                         |     true     | CQL statement to execute.                                                                                                                  |
94 | | authRequired       |                []string                         |    false     | List of authentication requirements for the source.                                                                                        |
95 | | parameters         | [parameters](../#specifying-parameters)       |    false     | List of [parameters](../#specifying-parameters) that will be inserted into the CQL statement.                                           |
96 | | templateParameters | [templateParameters](../#template-parameters) |    false     | List of [templateParameters](../#template-parameters) that will be inserted into the CQL statement before executing prepared statement. |
97 | 
```

--------------------------------------------------------------------------------
/docs/en/samples/looker/looker_gemini_oauth/_index.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "Gemini-CLI and OAuth"
  3 | type: docs
  4 | weight: 2
  5 | description: >
  6 |   How to connect to Looker from Gemini-CLI with end-user credentials
  7 | ---
  8 | 
  9 | ## Overview
 10 | 
 11 | Gemini-CLI can be configured to get an OAuth token from Looker, then send this
 12 | token to MCP Toolbox as part of the request. MCP Toolbox can then use this token
 13 | to authentincate with Looker. This means that there is no need to get a Looker
 14 | Client ID and Client Secret. This also means that MCP Toolbox can be set up as a
 15 | shared resource.
 16 | 
 17 | This configuration requires Toolbox v0.14.0 or later.
 18 | 
 19 | ## Step 1: Register the OAuth App in Looker
 20 | 
 21 | You first need to register the OAuth application. Refer to the documentation
 22 | [here](https://cloud.google.com/looker/docs/api-cors#registering_an_oauth_client_application).
 23 | You may need to ask an administrator to do this for you.
 24 | 
 25 | 1. Go to the API Explorer application, locate "Register OAuth App", and press
 26 |    the "Run It" button.
 27 | 1. Set the `client_guid` to "gemini-cli".
 28 | 1. Set the `redirect_uri` to "http://localhost:7777/oauth/callback".
 29 | 1. The `display_name` and `description` can be "Gemini-CLI" or anything
 30 |    meaningful.
 31 | 1. Set `enabled` to "true".
 32 | 1. Check the box confirming that you understand this API will change data.
 33 | 1. Click the "Run" button.
 34 | 
 35 |     ![OAuth Registration](./registration.png)
 36 | 
 37 | ## Step 2: Install and configure Toolbox
 38 | 
 39 | In this section, we will download Toolbox and run the Toolbox server.
 40 | 
 41 | 1. Download the latest version of Toolbox as a binary:
 42 | 
 43 |     {{< notice tip >}}
 44 |    Select the
 45 |    [correct binary](https://github.com/googleapis/genai-toolbox/releases)
 46 |    corresponding to your OS and CPU architecture.
 47 |     {{< /notice >}}
 48 |     <!-- {x-release-please-start-version} -->
 49 |     ```bash
 50 |     export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, or windows/amd64
 51 |     curl -O https://storage.googleapis.com/genai-toolbox/v0.17.0/$OS/toolbox
 52 |     ```
 53 |     <!-- {x-release-please-end} -->
 54 | 
 55 | 1. Make the binary executable:
 56 | 
 57 |     ```bash
 58 |     chmod +x toolbox
 59 |     ```
 60 | 
 61 | 1. Create a file `looker_env` with the settings for your
 62 |    Looker instance.
 63 | 
 64 |    ```bash
 65 |     export LOOKER_BASE_URL=https://looker.example.com
 66 |     export LOOKER_VERIFY_SSL=true
 67 |     export LOOKER_USE_CLIENT_OAUTH=true
 68 |    ```
 69 | 
 70 |    In some instances you may need to append `:19999` to
 71 |    the LOOKER_BASE_URL.
 72 | 
 73 | 1. Load the looker_env file into your environment.
 74 | 
 75 |    ```bash
 76 |    source looker_env
 77 |    ```
 78 | 
 79 | 1. Run the Toolbox server using the prebuilt Looker tools.
 80 | 
 81 |     ```bash
 82 |     ./toolbox --prebuilt looker
 83 |     ```
 84 | 
 85 |     The toolbox server will begin listening on localhost port 5000. Leave it
 86 |     running and continue in another terminal.
 87 | 
 88 |     Later, when it is time to shut everything down, you can quit the toolbox
 89 |     server with Ctrl-C in this terminal window.
 90 | 
 91 | ## Step 3: Configure Gemini-CLI
 92 | 
 93 | 1. Edit the file `~/.gemini/settings.json`. Add the following, substituting your
 94 |    Looker server host name for `looker.example.com`.
 95 | 
 96 |     ```json
 97 |     "mcpServers": {
 98 |         "looker": {
 99 |             "httpUrl": "http://localhost:5000/mcp",
100 |             "oauth": {
101 |                 "enabled": true,
102 |                 "clientId": "gemini-cli",
103 |                 "authorizationUrl": "https://looker.example.com/auth",
104 |                 "tokenUrl": "https://looker.example.com/api/token",
105 |                 "scopes": ["cors_api"]
106 |             }
107 |         }
108 |     }
109 |     ```
110 | 
111 |     The `authorizationUrl` should point to the URL you use to access Looker via the
112 |     web UI. The `tokenUrl` should point to the URL you use to access Looker via
113 |     the API. In some cases you will need to use the port number `:19999` after
114 |     the host name but before the `/api/token` part.
115 | 
116 | 1. Start Gemini-CLI.
117 | 
118 | 1. Authenticate with the command `/mcp auth looker`. Gemini-CLI will open up a
119 |    browser where you will confirm that you want to access Looker with your
120 |    account.
121 | 
122 |    ![Authorizing](./authorize.png)
123 | 
124 |    ![Authenticated](./authenticated.png)
125 | 
126 | 1. Use Gemini-CLI with your tools.
127 | 
128 | ## Using Toolbox as a Shared Service
129 | 
130 | Toolbox can be run on another server as a shared service accessed by multiple
131 | users. We strongly recommend running toolbox behind a web proxy such as `nginx`
132 | which will provide SSL encryption. Google Cloud Run is another good way to run
133 | toolbox. You will connect to a service like `https://toolbox.example.com/mcp`.
134 | The proxy server will handle the SSL encryption and certificates. Then it will
135 | foward the requests to `http://localhost:5000/mcp` running in that environment.
136 | The details of the config are beyond the scope of this document, but will be
137 | familiar to your system administrators.
138 | 
139 | To use the shared service, just change the `localhost:5000` in the `httpUrl` in
140 | `~/.gemini/settings.json` to the host name and possibly the port of the shared
141 | service.
142 | 
```

--------------------------------------------------------------------------------
/internal/tools/neo4j/neo4jcypher/neo4jcypher.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 neo4jcypher
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	neo4jsc "github.com/googleapis/genai-toolbox/internal/sources/neo4j"
 23 | 	"github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jschema/helpers"
 24 | 	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
 25 | 
 26 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 27 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 28 | )
 29 | 
 30 | const kind string = "neo4j-cypher"
 31 | 
 32 | func init() {
 33 | 	if !tools.Register(kind, newConfig) {
 34 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 35 | 	}
 36 | }
 37 | 
 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 39 | 	actual := Config{Name: name}
 40 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 41 | 		return nil, err
 42 | 	}
 43 | 	return actual, nil
 44 | }
 45 | 
 46 | type compatibleSource interface {
 47 | 	Neo4jDriver() neo4j.DriverWithContext
 48 | 	Neo4jDatabase() string
 49 | }
 50 | 
 51 | // validate compatible sources are still compatible
 52 | var _ compatibleSource = &neo4jsc.Source{}
 53 | 
 54 | var compatibleSources = [...]string{neo4jsc.SourceKind}
 55 | 
 56 | type Config struct {
 57 | 	Name         string           `yaml:"name" validate:"required"`
 58 | 	Kind         string           `yaml:"kind" validate:"required"`
 59 | 	Source       string           `yaml:"source" validate:"required"`
 60 | 	Description  string           `yaml:"description" validate:"required"`
 61 | 	Statement    string           `yaml:"statement" validate:"required"`
 62 | 	AuthRequired []string         `yaml:"authRequired"`
 63 | 	Parameters   tools.Parameters `yaml:"parameters"`
 64 | }
 65 | 
 66 | // validate interface
 67 | var _ tools.ToolConfig = Config{}
 68 | 
 69 | func (cfg Config) ToolConfigKind() string {
 70 | 	return kind
 71 | }
 72 | 
 73 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 74 | 	// verify source exists
 75 | 	rawS, ok := srcs[cfg.Source]
 76 | 	if !ok {
 77 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 78 | 	}
 79 | 
 80 | 	// verify the source is compatible
 81 | 	s, ok := rawS.(compatibleSource)
 82 | 	if !ok {
 83 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", kind, compatibleSources)
 84 | 	}
 85 | 
 86 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, cfg.Parameters)
 87 | 
 88 | 	// finish tool setup
 89 | 	t := Tool{
 90 | 		Name:         cfg.Name,
 91 | 		Kind:         kind,
 92 | 		Parameters:   cfg.Parameters,
 93 | 		Statement:    cfg.Statement,
 94 | 		AuthRequired: cfg.AuthRequired,
 95 | 		Driver:       s.Neo4jDriver(),
 96 | 		Database:     s.Neo4jDatabase(),
 97 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: cfg.Parameters.Manifest(), AuthRequired: cfg.AuthRequired},
 98 | 		mcpManifest:  mcpManifest,
 99 | 	}
100 | 	return t, nil
101 | }
102 | 
103 | // validate interface
104 | var _ tools.Tool = Tool{}
105 | 
106 | type Tool struct {
107 | 	Name         string           `yaml:"name"`
108 | 	Kind         string           `yaml:"kind"`
109 | 	Parameters   tools.Parameters `yaml:"parameters"`
110 | 	AuthRequired []string         `yaml:"authRequired"`
111 | 
112 | 	Driver      neo4j.DriverWithContext
113 | 	Database    string
114 | 	Statement   string
115 | 	manifest    tools.Manifest
116 | 	mcpManifest tools.McpManifest
117 | }
118 | 
119 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
120 | 	paramsMap := params.AsMap()
121 | 
122 | 	config := neo4j.ExecuteQueryWithDatabase(t.Database)
123 | 	results, err := neo4j.ExecuteQuery[*neo4j.EagerResult](ctx, t.Driver, t.Statement, paramsMap,
124 | 		neo4j.EagerResultTransformer, config)
125 | 	if err != nil {
126 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
127 | 	}
128 | 
129 | 	var out []any
130 | 	keys := results.Keys
131 | 	records := results.Records
132 | 	for _, record := range records {
133 | 		vMap := make(map[string]any)
134 | 		for col, value := range record.Values {
135 | 			vMap[keys[col]] = helpers.ConvertValue(value)
136 | 		}
137 | 		out = append(out, vMap)
138 | 	}
139 | 
140 | 	return out, nil
141 | }
142 | 
143 | func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (tools.ParamValues, error) {
144 | 	return tools.ParseParams(t.Parameters, data, claimsMap)
145 | }
146 | 
147 | func (t Tool) Manifest() tools.Manifest {
148 | 	return t.manifest
149 | }
150 | 
151 | func (t Tool) McpManifest() tools.McpManifest {
152 | 	return t.mcpManifest
153 | }
154 | 
155 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
156 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
157 | }
158 | 
159 | func (t Tool) RequiresClientAuthorization() bool {
160 | 	return false
161 | }
162 | 
```

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

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

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

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

```markdown
  1 | ---
  2 | title: "tidb-sql"
  3 | type: docs
  4 | weight: 1
  5 | description: > 
  6 |   A "tidb-sql" tool executes a pre-defined SQL statement against a TiDB
  7 |   database.
  8 | aliases:
  9 | - /resources/tools/tidb-sql
 10 | ---
 11 | 
 12 | ## About
 13 | 
 14 | A `tidb-sql` tool executes a pre-defined SQL statement against a TiDB
 15 | database. It's compatible with the following source:
 16 | 
 17 | - [tidb](../sources/tidb.md)
 18 | 
 19 | The specified SQL statement is executed as a [prepared statement][tidb-prepare],
 20 | and expects parameters in the SQL query to be in the form of placeholders `?`.
 21 | 
 22 | [tidb-prepare]: https://docs.pingcap.com/tidb/stable/sql-prepared-plan-cache
 23 | 
 24 | ## Example
 25 | 
 26 | > **Note:** This tool uses parameterized queries to prevent SQL injections.
 27 | > Query parameters can be used as substitutes for arbitrary expressions.
 28 | > Parameters cannot be used as substitutes for identifiers, column names, table
 29 | > names, or other parts of the query.
 30 | 
 31 | ```yaml
 32 | tools:
 33 |  search_flights_by_number:
 34 |     kind: tidb-sql
 35 |     source: my-tidb-instance
 36 |     statement: |
 37 |       SELECT * FROM flights
 38 |       WHERE airline = ?
 39 |       AND flight_number = ?
 40 |       LIMIT 10
 41 |     description: |
 42 |       Use this tool to get information for a specific flight.
 43 |       Takes an airline code and flight number and returns info on the flight.
 44 |       Do NOT use this tool with a flight id. Do NOT guess an airline code or flight number.
 45 |       A airline code is a code for an airline service consisting of two-character
 46 |       airline designator and followed by flight number, which is 1 to 4 digit number.
 47 |       For example, if given CY 0123, the airline is "CY", and flight_number is "123".
 48 |       Another example for this is DL 1234, the airline is "DL", and flight_number is "1234".
 49 |       If the tool returns more than one option choose the date closes to today.
 50 |       Example:
 51 |       {{
 52 |           "airline": "CY",
 53 |           "flight_number": "888",
 54 |       }}
 55 |       Example:
 56 |       {{
 57 |           "airline": "DL",
 58 |           "flight_number": "1234",
 59 |       }}
 60 |     parameters:
 61 |       - name: airline
 62 |         type: string
 63 |         description: Airline unique 2 letter identifier
 64 |       - name: flight_number
 65 |         type: string
 66 |         description: 1 to 4 digit number
 67 | ```
 68 | 
 69 | ### Example with Template Parameters
 70 | 
 71 | > **Note:** This tool allows direct modifications to the SQL statement,
 72 | > including identifiers, column names, and table names. **This makes it more
 73 | > vulnerable to SQL injections**. Using basic parameters only (see above) is
 74 | > recommended for performance and safety reasons. For more details, please check
 75 | > [templateParameters](_index#template-parameters).
 76 | 
 77 | ```yaml
 78 | tools:
 79 |  list_table:
 80 |     kind: tidb-sql
 81 |     source: my-tidb-instance
 82 |     statement: |
 83 |       SELECT * FROM {{.tableName}};
 84 |     description: |
 85 |       Use this tool to list all information from a specific table.
 86 |       Example:
 87 |       {{
 88 |           "tableName": "flights",
 89 |       }}
 90 |     templateParameters:
 91 |       - name: tableName
 92 |         type: string
 93 |         description: Table to select from
 94 | ```
 95 | 
 96 | ## Reference
 97 | 
 98 | | **field**          |                  **type**                        | **required** | **description**                                                                                                                            |
 99 | |--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
100 | | kind               |                   string                         |     true     | Must be "tidb-sql".                                                                                                                       |
101 | | source             |                   string                         |     true     | Name of the source the SQL should execute on.                                                                                              |
102 | | description        |                   string                         |     true     | Description of the tool that is passed to the LLM.                                                                                         |
103 | | statement          |                   string                         |     true     | SQL statement to execute on.                                                                                                               |
104 | | parameters         | [parameters](_index#specifying-parameters)       |    false     | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement.                                           |
105 | | templateParameters | [templateParameters](_index#template-parameters) |    false     | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
106 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/looker.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "Looker"
  3 | type: docs
  4 | weight: 1
  5 | description: >
  6 |   Looker is a business intelligence tool that also provides a semantic layer.
  7 | ---
  8 | 
  9 | ## About
 10 | 
 11 | [Looker][looker-docs] is a web based business intelligence and data management
 12 | tool that provides a semantic layer to facilitate querying. It can be deployed
 13 | in the cloud, on GCP, or on premises.
 14 | 
 15 | [looker-docs]: https://cloud.google.com/looker/docs
 16 | 
 17 | ## Requirements
 18 | 
 19 | ### Looker User
 20 | 
 21 | This source only uses API authentication. You will need to
 22 | [create an API user][looker-user] to login to Looker.
 23 | 
 24 | [looker-user]:
 25 |     https://cloud.google.com/looker/docs/api-auth#authentication_with_an_sdk
 26 | 
 27 | {{< notice note >}}
 28 | To use the Conversational Analytics API, you will need to have the following
 29 | Google Cloud Project API enabled and IAM permissions.
 30 | {{< /notice >}}
 31 | 
 32 | ### API Enablement in GCP
 33 | 
 34 | Enable the following APIs in your Google Cloud Project:
 35 | 
 36 | ```
 37 | gcloud services enable geminidataanalytics.googleapis.com --project=$PROJECT_ID
 38 | gcloud services enable cloudaicompanion.googleapis.com --project=$PROJECT_ID
 39 | ```
 40 | 
 41 | ### IAM Permissions in GCP
 42 | 
 43 | In addition to [setting the ADC for your server][set-adc], you need to ensure
 44 | the IAM identity has been given the following IAM roles (or corresponding
 45 | permissions):
 46 | 
 47 | - `roles/looker.instanceUser`
 48 | - `roles/cloudaicompanion.user`
 49 | - `roles/geminidataanalytics.dataAgentStatelessUser`
 50 | 
 51 | To initialize the application default credential run `gcloud auth login --update-adc`
 52 | in your environment before starting MCP Toolbox.
 53 | 
 54 | [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc
 55 | 
 56 | ## Example
 57 | 
 58 | ```yaml
 59 | sources:
 60 |     my-looker-source:
 61 |         kind: looker
 62 |         base_url: http://looker.example.com
 63 |         client_id: ${LOOKER_CLIENT_ID}
 64 |         client_secret: ${LOOKER_CLIENT_SECRET}
 65 |         project: ${LOOKER_PROJECT}
 66 |         location: ${LOOKER_LOCATION}
 67 |         verify_ssl: true
 68 |         timeout: 600s
 69 | ```
 70 | 
 71 | The Looker base url will look like "https://looker.example.com", don't include
 72 | a trailing "/". In some cases, especially if your Looker is deployed
 73 | on-premises, you may need to add the API port number like
 74 | "https://looker.example.com:19999".
 75 | 
 76 | Verify ssl should almost always be "true" (all lower case) unless you are using
 77 | a self-signed ssl certificate for the Looker server. Anything other than "true"
 78 | will be interpreted as false.
 79 | 
 80 | The client id and client secret are seemingly random character sequences
 81 | assigned by the looker server. If you are using Looker OAuth you don't need
 82 | these settings
 83 | 
 84 | The `project` and `location` fields are utilized **only** when using the conversational analytics tool.
 85 | 
 86 | {{< notice tip >}}
 87 | Use environment variable replacement with the format ${ENV_NAME}
 88 | instead of hardcoding your secrets into the configuration file.
 89 | {{< /notice >}}
 90 | 
 91 | ## Reference
 92 | 
 93 | | **field**            | **type** | **required** | **description**                                                                           |
 94 | | -------------------- | :------: | :----------: | ----------------------------------------------------------------------------------------- |
 95 | | kind                 |  string  |     true     | Must be "looker".                                                                         |
 96 | | base_url             |  string  |     true     | The URL of your Looker server with no trailing /).                                        |
 97 | | client_id            |  string  |    false     | The client id assigned by Looker.                                                         |
 98 | | client_secret        |  string  |    false     | The client secret assigned by Looker.                                                     |
 99 | | verify_ssl           |  string  |    false     | Whether to check the ssl certificate of the server.                                       |
100 | | project              |  string  |    false     | The project id to use in Google Cloud.                                                    |
101 | | location             |  string  |    false     | The location to use in Google Cloud. (default: us)                                        |
102 | | timeout              |  string  |    false     | Maximum time to wait for query execution (e.g. "30s", "2m"). By default, 120s is applied. |
103 | | use_client_oauth     |  string  |    false     | Use OAuth tokens instead of client_id and client_secret. (default: false)                 |
104 | | show_hidden_models   |  string  |    false     | Show or hide hidden models. (default: true)                                               |
105 | | show_hidden_explores |  string  |    false     | Show or hide hidden explores. (default: true)                                             |
106 | | show_hidden_fields   |  string  |    false     | Show or hide hidden fields. (default: true)                                               |
107 | 
```

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

```go
  1 | package main
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"encoding/json"
  6 | 	"fmt"
  7 | 	"log"
  8 | 	"os"
  9 | 
 10 | 	"github.com/googleapis/mcp-toolbox-sdk-go/core"
 11 | 	"github.com/tmc/langchaingo/llms"
 12 | 	"github.com/tmc/langchaingo/llms/googleai"
 13 | )
 14 | 
 15 | // ConvertToLangchainTool converts a generic core.ToolboxTool into a LangChainGo llms.Tool.
 16 | func ConvertToLangchainTool(toolboxTool *core.ToolboxTool) llms.Tool {
 17 | 
 18 | 	// Fetch the tool's input schema
 19 | 	inputschema, err := toolboxTool.InputSchema()
 20 | 	if err != nil {
 21 | 		return llms.Tool{}
 22 | 	}
 23 | 
 24 | 	var paramsSchema map[string]any
 25 | 	_ = json.Unmarshal(inputschema, &paramsSchema)
 26 | 
 27 | 	// Convert into LangChain's llms.Tool
 28 | 	return llms.Tool{
 29 | 		Type: "function",
 30 | 		Function: &llms.FunctionDefinition{
 31 | 			Name:        toolboxTool.Name(),
 32 | 			Description: toolboxTool.Description(),
 33 | 			Parameters:  paramsSchema,
 34 | 		},
 35 | 	}
 36 | }
 37 | 
 38 | const systemPrompt = `
 39 | You're a helpful hotel assistant. You handle hotel searching, booking, and
 40 | cancellations. When the user searches for a hotel, mention its name, id,
 41 | location and price tier. Always mention hotel ids while performing any
 42 | searches. This is very important for any operations. For any bookings or
 43 | cancellations, please provide the appropriate confirmation. Be sure to
 44 | update checkin or checkout dates if mentioned by the user.
 45 | Don't ask for confirmations from the user.
 46 | `
 47 | 
 48 | var queries = []string{
 49 | 	"Find hotels in Basel with Basel in its name.",
 50 | 	"Can you book the hotel Hilton Basel for me?",
 51 | 	"Oh wait, this is too expensive. Please cancel it.",
 52 | 	"Please book the Hyatt Regency instead.",
 53 | 	"My check in dates would be from April 10, 2024 to April 19, 2024.",
 54 | }
 55 | 
 56 | func main() {
 57 | 	genaiKey := os.Getenv("GOOGLE_API_KEY")
 58 | 	toolboxURL := "http://localhost:5000"
 59 | 	ctx := context.Background()
 60 | 
 61 | 	// Initialize the Google AI client (LLM).
 62 | 	llm, err := googleai.New(ctx, googleai.WithAPIKey(genaiKey), googleai.WithDefaultModel("gemini-2.0-flash"))
 63 | 	if err != nil {
 64 | 		log.Fatalf("Failed to create Google AI client: %v", err)
 65 | 	}
 66 | 
 67 | 	// Initialize the MCP Toolbox client.
 68 | 	toolboxClient, err := core.NewToolboxClient(toolboxURL)
 69 | 	if err != nil {
 70 | 		log.Fatalf("Failed to create Toolbox client: %v", err)
 71 | 	}
 72 | 
 73 | 	// Load the tool using the MCP Toolbox SDK.
 74 | 	tools, err := toolboxClient.LoadToolset("my-toolset", ctx)
 75 | 	if err != nil {
 76 | 		log.Fatalf("Failed to load tools: %v\nMake sure your Toolbox server is running and the tool is configured.", err)
 77 | 	}
 78 | 
 79 | 	toolsMap := make(map[string]*core.ToolboxTool, len(tools))
 80 | 
 81 | 	langchainTools := make([]llms.Tool, len(tools))
 82 | 	// Convert the loaded ToolboxTools into the format LangChainGo requires.
 83 | 	for i, tool := range tools {
 84 | 		langchainTools[i] = ConvertToLangchainTool(tool)
 85 | 		toolsMap[tool.Name()] = tool
 86 | 	}
 87 | 
 88 | 	// Start the conversation history.
 89 | 	messageHistory := []llms.MessageContent{
 90 | 		llms.TextParts(llms.ChatMessageTypeSystem, systemPrompt),
 91 | 	}
 92 | 
 93 | 	for _, query := range queries {
 94 | 		messageHistory = append(messageHistory, llms.TextParts(llms.ChatMessageTypeHuman, query))
 95 | 
 96 | 		// Make the first call to the LLM, making it aware of the tool.
 97 | 		resp, err := llm.GenerateContent(ctx, messageHistory, llms.WithTools(langchainTools))
 98 | 		if err != nil {
 99 | 			log.Fatalf("LLM call failed: %v", err)
100 | 		}
101 | 		respChoice := resp.Choices[0]
102 | 
103 | 		assistantResponse := llms.TextParts(llms.ChatMessageTypeAI, respChoice.Content)
104 | 		for _, tc := range respChoice.ToolCalls {
105 | 			assistantResponse.Parts = append(assistantResponse.Parts, tc)
106 | 		}
107 | 		messageHistory = append(messageHistory, assistantResponse)
108 | 
109 | 		// Process each tool call requested by the model.
110 | 		for _, tc := range respChoice.ToolCalls {
111 | 			toolName := tc.FunctionCall.Name
112 | 			tool := toolsMap[toolName]
113 | 			var args map[string]any
114 | 			if err := json.Unmarshal([]byte(tc.FunctionCall.Arguments), &args); err != nil {
115 | 				log.Fatalf("Failed to unmarshal arguments for tool '%s': %v", toolName, err)
116 | 			}
117 | 			toolResult, err := tool.Invoke(ctx, args)
118 | 			if err != nil {
119 | 				log.Fatalf("Failed to execute tool '%s': %v", toolName, err)
120 | 			}
121 | 			if toolResult == "" || toolResult == nil {
122 | 				toolResult = "Operation completed successfully with no specific return value."
123 | 			}
124 | 
125 | 			// Create the tool call response message and add it to the history.
126 | 			toolResponse := llms.MessageContent{
127 | 				Role: llms.ChatMessageTypeTool,
128 | 				Parts: []llms.ContentPart{
129 | 					llms.ToolCallResponse{
130 | 						Name:    toolName,
131 | 						Content: fmt.Sprintf("%v", toolResult),
132 | 					},
133 | 				},
134 | 			}
135 | 			messageHistory = append(messageHistory, toolResponse)
136 | 		}
137 | 		finalResp, err := llm.GenerateContent(ctx, messageHistory)
138 | 		if err != nil {
139 | 			log.Fatalf("Final LLM call failed after tool execution: %v", err)
140 | 		}
141 | 
142 | 		// Add the final textual response from the LLM to the history
143 | 		messageHistory = append(messageHistory, llms.TextParts(llms.ChatMessageTypeAI, finalResp.Choices[0].Content))
144 | 
145 | 		fmt.Println(finalResp.Choices[0].Content)
146 | 
147 | 	}
148 | 
149 | }
150 | 
```

--------------------------------------------------------------------------------
/internal/tools/postgres/postgressql/postgressql_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 postgressql_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools/postgres/postgressql"
 26 | )
 27 | 
 28 | func TestParseFromYamlPostgres(t *testing.T) {
 29 | 	ctx, err := testutils.ContextWithNewLogger()
 30 | 	if err != nil {
 31 | 		t.Fatalf("unexpected error: %s", err)
 32 | 	}
 33 | 	tcs := []struct {
 34 | 		desc string
 35 | 		in   string
 36 | 		want server.ToolConfigs
 37 | 	}{
 38 | 		{
 39 | 			desc: "basic example",
 40 | 			in: `
 41 | 			tools:
 42 | 				example_tool:
 43 | 					kind: postgres-sql
 44 | 					source: my-pg-instance
 45 | 					description: some description
 46 | 					statement: |
 47 | 						SELECT * FROM SQL_STATEMENT;
 48 | 					authRequired:
 49 | 						- my-google-auth-service
 50 | 						- other-auth-service
 51 | 					parameters:
 52 | 						- name: country
 53 | 						  type: string
 54 | 						  description: some description
 55 | 						  authServices:
 56 | 							- name: my-google-auth-service
 57 | 							  field: user_id
 58 | 							- name: other-auth-service
 59 | 							  field: user_id
 60 | 			`,
 61 | 			want: server.ToolConfigs{
 62 | 				"example_tool": postgressql.Config{
 63 | 					Name:         "example_tool",
 64 | 					Kind:         "postgres-sql",
 65 | 					Source:       "my-pg-instance",
 66 | 					Description:  "some description",
 67 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 68 | 					AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
 69 | 					Parameters: []tools.Parameter{
 70 | 						tools.NewStringParameterWithAuth("country", "some description",
 71 | 							[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
 72 | 								{Name: "other-auth-service", Field: "user_id"}}),
 73 | 					},
 74 | 				},
 75 | 			},
 76 | 		},
 77 | 	}
 78 | 	for _, tc := range tcs {
 79 | 		t.Run(tc.desc, func(t *testing.T) {
 80 | 			got := struct {
 81 | 				Tools server.ToolConfigs `yaml:"tools"`
 82 | 			}{}
 83 | 			// Parse contents
 84 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 85 | 			if err != nil {
 86 | 				t.Fatalf("unable to unmarshal: %s", err)
 87 | 			}
 88 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 89 | 				t.Fatalf("incorrect parse: diff %v", diff)
 90 | 			}
 91 | 		})
 92 | 	}
 93 | 
 94 | }
 95 | 
 96 | func TestParseFromYamlWithTemplateParamsPostgres(t *testing.T) {
 97 | 	ctx, err := testutils.ContextWithNewLogger()
 98 | 	if err != nil {
 99 | 		t.Fatalf("unexpected error: %s", err)
100 | 	}
101 | 	tcs := []struct {
102 | 		desc string
103 | 		in   string
104 | 		want server.ToolConfigs
105 | 	}{
106 | 		{
107 | 			desc: "basic example",
108 | 			in: `
109 | 			tools:
110 | 				example_tool:
111 | 					kind: postgres-sql
112 | 					source: my-pg-instance
113 | 					description: some description
114 | 					statement: |
115 | 						SELECT * FROM SQL_STATEMENT;
116 | 					parameters:
117 | 						- name: name
118 | 						  type: string
119 | 						  description: some description
120 | 					templateParameters:
121 | 						- name: tableName
122 | 						  type: string
123 | 						  description: The table to select hotels from.
124 | 						- name: fieldArray
125 | 						  type: array
126 | 						  description: The columns to return for the query.
127 | 						  items: 
128 | 								name: column
129 | 								type: string
130 | 								description: A column name that will be returned from the query.
131 | 			`,
132 | 			want: server.ToolConfigs{
133 | 				"example_tool": postgressql.Config{
134 | 					Name:         "example_tool",
135 | 					Kind:         "postgres-sql",
136 | 					Source:       "my-pg-instance",
137 | 					Description:  "some description",
138 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
139 | 					AuthRequired: []string{},
140 | 					Parameters: []tools.Parameter{
141 | 						tools.NewStringParameter("name", "some description"),
142 | 					},
143 | 					TemplateParameters: []tools.Parameter{
144 | 						tools.NewStringParameter("tableName", "The table to select hotels from."),
145 | 						tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
146 | 					},
147 | 				},
148 | 			},
149 | 		},
150 | 	}
151 | 	for _, tc := range tcs {
152 | 		t.Run(tc.desc, func(t *testing.T) {
153 | 			got := struct {
154 | 				Tools server.ToolConfigs `yaml:"tools"`
155 | 			}{}
156 | 			// Parse contents
157 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
158 | 			if err != nil {
159 | 				t.Fatalf("unable to unmarshal: %s", err)
160 | 			}
161 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
162 | 				t.Fatalf("incorrect parse: diff %v", diff)
163 | 			}
164 | 		})
165 | 	}
166 | 
167 | }
168 | 
```

--------------------------------------------------------------------------------
/internal/tools/firebird/firebirdsql/firebirdsql_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 firebirdsql_test
 16 | 
 17 | import (
 18 | 	"testing"
 19 | 
 20 | 	yaml "github.com/goccy/go-yaml"
 21 | 	"github.com/google/go-cmp/cmp"
 22 | 	"github.com/googleapis/genai-toolbox/internal/server"
 23 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | 	"github.com/googleapis/genai-toolbox/internal/tools/firebird/firebirdsql"
 26 | )
 27 | 
 28 | func TestParseFromYamlFirebird(t *testing.T) {
 29 | 	ctx, err := testutils.ContextWithNewLogger()
 30 | 	if err != nil {
 31 | 		t.Fatalf("unexpected error: %s", err)
 32 | 	}
 33 | 	tcs := []struct {
 34 | 		desc string
 35 | 		in   string
 36 | 		want server.ToolConfigs
 37 | 	}{
 38 | 		{
 39 | 			desc: "basic example",
 40 | 			in: `
 41 | 			tools:
 42 | 				example_tool:
 43 | 					kind: firebird-sql
 44 | 					source: my-fdb-instance
 45 | 					description: some description
 46 | 					statement: |
 47 | 						SELECT * FROM SQL_STATEMENT;
 48 | 					authRequired:
 49 | 						- my-google-auth-service
 50 | 						- other-auth-service
 51 | 					parameters:
 52 | 						- name: country
 53 | 						  type: string
 54 | 						  description: some description
 55 | 						  authServices:
 56 | 							- name: my-google-auth-service
 57 | 							  field: user_id
 58 | 							- name: other-auth-service
 59 | 							  field: user_id
 60 | 			`,
 61 | 			want: server.ToolConfigs{
 62 | 				"example_tool": firebirdsql.Config{
 63 | 					Name:         "example_tool",
 64 | 					Kind:         "firebird-sql",
 65 | 					Source:       "my-fdb-instance",
 66 | 					Description:  "some description",
 67 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
 68 | 					AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
 69 | 					Parameters: []tools.Parameter{
 70 | 						tools.NewStringParameterWithAuth("country", "some description",
 71 | 							[]tools.ParamAuthService{{Name: "my-google-auth-service", Field: "user_id"},
 72 | 								{Name: "other-auth-service", Field: "user_id"}}),
 73 | 					},
 74 | 				},
 75 | 			},
 76 | 		},
 77 | 	}
 78 | 	for _, tc := range tcs {
 79 | 		t.Run(tc.desc, func(t *testing.T) {
 80 | 			got := struct {
 81 | 				Tools server.ToolConfigs `yaml:"tools"`
 82 | 			}{}
 83 | 			// Parse contents
 84 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
 85 | 			if err != nil {
 86 | 				t.Fatalf("unable to unmarshal: %s", err)
 87 | 			}
 88 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
 89 | 				t.Fatalf("incorrect parse: diff %v", diff)
 90 | 			}
 91 | 		})
 92 | 	}
 93 | 
 94 | }
 95 | 
 96 | func TestParseFromYamlWithTemplateParamsFirebird(t *testing.T) {
 97 | 	ctx, err := testutils.ContextWithNewLogger()
 98 | 	if err != nil {
 99 | 		t.Fatalf("unexpected error: %s", err)
100 | 	}
101 | 	tcs := []struct {
102 | 		desc string
103 | 		in   string
104 | 		want server.ToolConfigs
105 | 	}{
106 | 		{
107 | 			desc: "basic example",
108 | 			in: `
109 | 			tools:
110 | 				example_tool:
111 | 					kind: firebird-sql
112 | 					source: my-fdb-instance
113 | 					description: some description
114 | 					statement: |
115 | 						SELECT * FROM SQL_STATEMENT;
116 | 					parameters:
117 | 						- name: name
118 | 						  type: string
119 | 						  description: some description
120 | 					templateParameters:
121 | 						- name: tableName
122 | 						  type: string
123 | 						  description: The table to select hotels from.
124 | 						- name: fieldArray
125 | 						  type: array
126 | 						  description: The columns to return for the query.
127 | 						  items: 
128 | 								name: column
129 | 								type: string
130 | 								description: A column name that will be returned from the query.
131 | 			`,
132 | 			want: server.ToolConfigs{
133 | 				"example_tool": firebirdsql.Config{
134 | 					Name:         "example_tool",
135 | 					Kind:         "firebird-sql",
136 | 					Source:       "my-fdb-instance",
137 | 					Description:  "some description",
138 | 					Statement:    "SELECT * FROM SQL_STATEMENT;\n",
139 | 					AuthRequired: []string{},
140 | 					Parameters: []tools.Parameter{
141 | 						tools.NewStringParameter("name", "some description"),
142 | 					},
143 | 					TemplateParameters: []tools.Parameter{
144 | 						tools.NewStringParameter("tableName", "The table to select hotels from."),
145 | 						tools.NewArrayParameter("fieldArray", "The columns to return for the query.", tools.NewStringParameter("column", "A column name that will be returned from the query.")),
146 | 					},
147 | 				},
148 | 			},
149 | 		},
150 | 	}
151 | 	for _, tc := range tcs {
152 | 		t.Run(tc.desc, func(t *testing.T) {
153 | 			got := struct {
154 | 				Tools server.ToolConfigs `yaml:"tools"`
155 | 			}{}
156 | 			// Parse contents
157 | 			err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
158 | 			if err != nil {
159 | 				t.Fatalf("unable to unmarshal: %s", err)
160 | 			}
161 | 			if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
162 | 				t.Fatalf("incorrect parse: diff %v", diff)
163 | 			}
164 | 		})
165 | 	}
166 | 
167 | }
168 | 
```

--------------------------------------------------------------------------------
/internal/tools/cloudsql/cloudsqllistinstances/cloudsqllistinstances.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 cloudsqllistinstances
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	"github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	cloudsqladminsrc "github.com/googleapis/genai-toolbox/internal/sources/cloudsqladmin"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | )
 26 | 
 27 | const kind string = "cloud-sql-list-instances"
 28 | 
 29 | func init() {
 30 | 	if !tools.Register(kind, newConfig) {
 31 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 32 | 	}
 33 | }
 34 | 
 35 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 36 | 	actual := Config{Name: name}
 37 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 38 | 		return nil, err
 39 | 	}
 40 | 	return actual, nil
 41 | }
 42 | 
 43 | // Config defines the configuration for the list-instance tool.
 44 | type Config struct {
 45 | 	Name         string   `yaml:"name" validate:"required"`
 46 | 	Kind         string   `yaml:"kind" validate:"required"`
 47 | 	Source       string   `yaml:"source" validate:"required"`
 48 | 	Description  string   `yaml:"description"`
 49 | 	AuthRequired []string `yaml:"authRequired"`
 50 | }
 51 | 
 52 | // validate interface
 53 | var _ tools.ToolConfig = Config{}
 54 | 
 55 | // ToolConfigKind returns the kind of the tool.
 56 | func (cfg Config) ToolConfigKind() string {
 57 | 	return kind
 58 | }
 59 | 
 60 | // Initialize initializes the tool from the configuration.
 61 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 62 | 	rawS, ok := srcs[cfg.Source]
 63 | 	if !ok {
 64 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 65 | 	}
 66 | 	s, ok := rawS.(*cloudsqladminsrc.Source)
 67 | 	if !ok {
 68 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `cloud-sql-admin`", kind)
 69 | 	}
 70 | 
 71 | 	allParameters := tools.Parameters{
 72 | 		tools.NewStringParameter("project", "The project ID"),
 73 | 	}
 74 | 	paramManifest := allParameters.Manifest()
 75 | 
 76 | 	description := cfg.Description
 77 | 	if description == "" {
 78 | 		description = "Lists all type of Cloud SQL instances for a project."
 79 | 	}
 80 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
 81 | 
 82 | 	return Tool{
 83 | 		Name:         cfg.Name,
 84 | 		Kind:         kind,
 85 | 		AuthRequired: cfg.AuthRequired,
 86 | 		source:       s,
 87 | 		AllParams:    allParameters,
 88 | 		manifest:     tools.Manifest{Description: description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
 89 | 		mcpManifest:  mcpManifest,
 90 | 	}, nil
 91 | }
 92 | 
 93 | // Tool represents the list-instance tool.
 94 | type Tool struct {
 95 | 	Name         string   `yaml:"name"`
 96 | 	Kind         string   `yaml:"kind"`
 97 | 	Description  string   `yaml:"description"`
 98 | 	AuthRequired []string `yaml:"authRequired"`
 99 | 
100 | 	AllParams   tools.Parameters `yaml:"allParams"`
101 | 	source      *cloudsqladminsrc.Source
102 | 	manifest    tools.Manifest
103 | 	mcpManifest tools.McpManifest
104 | }
105 | 
106 | // Invoke executes the tool's logic.
107 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
108 | 	paramsMap := params.AsMap()
109 | 
110 | 	project, ok := paramsMap["project"].(string)
111 | 	if !ok {
112 | 		return nil, fmt.Errorf("missing 'project' parameter")
113 | 	}
114 | 
115 | 	service, err := t.source.GetService(ctx, string(accessToken))
116 | 	if err != nil {
117 | 		return nil, err
118 | 	}
119 | 
120 | 	resp, err := service.Instances.List(project).Do()
121 | 	if err != nil {
122 | 		return nil, fmt.Errorf("error listing instances: %w", err)
123 | 	}
124 | 
125 | 	if resp.Items == nil {
126 | 		return []any{}, nil
127 | 	}
128 | 
129 | 	type instanceInfo struct {
130 | 		Name         string `json:"name"`
131 | 		InstanceType string `json:"instanceType"`
132 | 	}
133 | 
134 | 	var instances []instanceInfo
135 | 	for _, item := range resp.Items {
136 | 		instances = append(instances, instanceInfo{
137 | 			Name:         item.Name,
138 | 			InstanceType: item.InstanceType,
139 | 		})
140 | 	}
141 | 
142 | 	return instances, nil
143 | }
144 | 
145 | // ParseParams parses the parameters for the tool.
146 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
147 | 	return tools.ParseParams(t.AllParams, data, claims)
148 | }
149 | 
150 | // Manifest returns the tool's manifest.
151 | func (t Tool) Manifest() tools.Manifest {
152 | 	return t.manifest
153 | }
154 | 
155 | // McpManifest returns the tool's MCP manifest.
156 | func (t Tool) McpManifest() tools.McpManifest {
157 | 	return t.mcpManifest
158 | }
159 | 
160 | // Authorized checks if the tool is authorized.
161 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
162 | 	return true
163 | }
164 | 
165 | func (t Tool) RequiresClientAuthorization() bool {
166 | 	return t.source.UseClientAuthorization()
167 | }
168 | 
```

--------------------------------------------------------------------------------
/docs/en/getting-started/quickstart/python/core/quickstart.py:
--------------------------------------------------------------------------------

```python
  1 | import asyncio
  2 | import os
  3 | 
  4 | from google import genai
  5 | from google.genai.types import (
  6 |     Content,
  7 |     FunctionDeclaration,
  8 |     GenerateContentConfig,
  9 |     Part,
 10 |     Tool,
 11 | )
 12 | 
 13 | from toolbox_core import ToolboxClient
 14 | 
 15 | project = os.environ.get("GCP_PROJECT") or "project-id"
 16 | 
 17 | prompt = """
 18 |   You're a helpful hotel assistant. You handle hotel searching, booking and
 19 |   cancellations. When the user searches for a hotel, mention it's name, id,
 20 |   location and price tier. Always mention hotel id while performing any
 21 |   searches. This is very important for any operations. For any bookings or
 22 |   cancellations, please provide the appropriate confirmation. Be sure to
 23 |   update checkin or checkout dates if mentioned by the user.
 24 |   Don't ask for confirmations from the user.
 25 | """
 26 | 
 27 | queries = [
 28 |     "Find hotels in Basel with Basel in its name.",
 29 |     "Please book the hotel Hilton Basel for me.",
 30 |     "This is too expensive. Please cancel it.",
 31 |     "Please book Hyatt Regency for me",
 32 |     "My check in dates for my booking would be from April 10, 2024 to April 19, 2024.",
 33 | ]
 34 | 
 35 | async def main():
 36 |     async with ToolboxClient("http://127.0.0.1:5000") as toolbox_client:
 37 | 
 38 |         # The toolbox_tools list contains Python callables (functions/methods) designed for LLM tool-use
 39 |         # integration. While this example uses Google's genai client, these callables can be adapted for
 40 |         # various function-calling or agent frameworks. For easier integration with supported frameworks
 41 |         # (https://github.com/googleapis/mcp-toolbox-python-sdk/tree/main/packages), use the
 42 |         # provided wrapper packages, which handle framework-specific boilerplate.
 43 |         toolbox_tools = await toolbox_client.load_toolset("my-toolset")
 44 |         genai_client = genai.Client(
 45 |             vertexai=True, project=project, location="us-central1"
 46 |         )
 47 | 
 48 |         genai_tools = [
 49 |             Tool(
 50 |                 function_declarations=[
 51 |                     FunctionDeclaration.from_callable_with_api_option(callable=tool)
 52 |                 ]
 53 |             )
 54 |             for tool in toolbox_tools
 55 |         ]
 56 |         history = []
 57 |         for query in queries:
 58 |             user_prompt_content = Content(
 59 |                 role="user",
 60 |                 parts=[Part.from_text(text=query)],
 61 |             )
 62 |             history.append(user_prompt_content)
 63 | 
 64 |             response = genai_client.models.generate_content(
 65 |                 model="gemini-2.0-flash-001",
 66 |                 contents=history,
 67 |                 config=GenerateContentConfig(
 68 |                     system_instruction=prompt,
 69 |                     tools=genai_tools,
 70 |                 ),
 71 |             )
 72 |             history.append(response.candidates[0].content)
 73 |             function_response_parts = []
 74 | 
 75 |             if response.function_calls:
 76 |                 for function_call in response.function_calls:
 77 |                     fn_name = function_call.name
 78 |                     # The tools are sorted alphabetically
 79 |                     if fn_name == "search-hotels-by-name":
 80 |                         function_result = await toolbox_tools[3](**function_call.args)
 81 |                     elif fn_name == "search-hotels-by-location":
 82 |                         function_result = await toolbox_tools[2](**function_call.args)
 83 |                     elif fn_name == "book-hotel":
 84 |                         function_result = await toolbox_tools[0](**function_call.args)
 85 |                     elif fn_name == "update-hotel":
 86 |                         function_result = await toolbox_tools[4](**function_call.args)
 87 |                     elif fn_name == "cancel-hotel":
 88 |                         function_result = await toolbox_tools[1](**function_call.args)
 89 |                     else:
 90 |                         raise ValueError(f"Function name {fn_name} not present.")
 91 | 
 92 |                     function_response = {"result": function_result}
 93 |                     function_response_part = Part.from_function_response(
 94 |                         name=function_call.name,
 95 |                         response=function_response,
 96 |                     )
 97 |                     function_response_parts.append(function_response_part)
 98 | 
 99 |             if function_response_parts:
100 |                 tool_response_content = Content(role="tool", parts=function_response_parts)
101 |                 history.append(tool_response_content)
102 | 
103 |                 response2 = genai_client.models.generate_content(
104 |                     model="gemini-2.0-flash-001",
105 |                     contents=history,
106 |                     config=GenerateContentConfig(
107 |                         tools=genai_tools,
108 |                     ),
109 |                 )
110 |                 final_model_response_content = response2.candidates[0].content
111 |                 history.append(final_model_response_content)
112 |                 print(response2.text)
113 |             else:
114 |                 print(response.text)
115 | 
116 | asyncio.run(main())
```

--------------------------------------------------------------------------------
/docs/en/resources/tools/yuagbytedb/yugabytedb-sql.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "yugabytedb-sql"
  3 | type: docs
  4 | weight: 1
  5 | description: > 
  6 |   A "yugabytedb-sql" tool executes a pre-defined SQL statement against a YugabyteDB
  7 |   database.
  8 | ---
  9 | 
 10 | ## About
 11 | 
 12 | A `yugabytedb-sql` tool executes a pre-defined SQL statement against a YugabyteDB
 13 | database.
 14 | 
 15 | The specified SQL statement is executed as a prepared statement,
 16 | and specified parameters will inserted according to their position: e.g. `1`
 17 | will be the first parameter specified, `$@` will be the second parameter, and so
 18 | on. If template parameters are included, they will be resolved before execution
 19 | of the prepared statement.
 20 | 
 21 | ## Example
 22 | 
 23 | > **Note:** This tool uses parameterized queries to prevent SQL injections.
 24 | > Query parameters can be used as substitutes for arbitrary expressions.
 25 | > Parameters cannot be used as substitutes for identifiers, column names, table
 26 | > names, or other parts of the query.
 27 | 
 28 | ```yaml
 29 | tools:
 30 |  search_flights_by_number:
 31 |     kind: yugabytedb-sql
 32 |     source: my-yb-instance
 33 |     statement: |
 34 |       SELECT * FROM flights
 35 |       WHERE airline = $1
 36 |       AND flight_number = $2
 37 |       LIMIT 10
 38 |     description: |
 39 |       Use this tool to get information for a specific flight.
 40 |       Takes an airline code and flight number and returns info on the flight.
 41 |       Do NOT use this tool with a flight id. Do NOT guess an airline code or flight number.
 42 |       A airline code is a code for an airline service consisting of two-character
 43 |       airline designator and followed by flight number, which is 1 to 4 digit number.
 44 |       For example, if given CY 0123, the airline is "CY", and flight_number is "123".
 45 |       Another example for this is DL 1234, the airline is "DL", and flight_number is "1234".
 46 |       If the tool returns more than one option choose the date closes to today.
 47 |       Example:
 48 |       {{
 49 |           "airline": "CY",
 50 |           "flight_number": "888",
 51 |       }}
 52 |       Example:
 53 |       {{
 54 |           "airline": "DL",
 55 |           "flight_number": "1234",
 56 |       }}
 57 |     parameters:
 58 |       - name: airline
 59 |         type: string
 60 |         description: Airline unique 2 letter identifier
 61 |       - name: flight_number
 62 |         type: string
 63 |         description: 1 to 4 digit number
 64 | ```
 65 | 
 66 | ### Example with Template Parameters
 67 | 
 68 | > **Note:** This tool allows direct modifications to the SQL statement,
 69 | > including identifiers, column names, and table names. **This makes it more
 70 | > vulnerable to SQL injections**. Using basic parameters  only (see above) is
 71 | > recommended for performance and safety reasons. For more details, please check
 72 | > [templateParameters](_index#template-parameters).
 73 | 
 74 | ```yaml
 75 | tools:
 76 |  list_table:
 77 |     kind: yugabytedb-sql
 78 |     source: my-yb-instance
 79 |     statement: |
 80 |       SELECT * FROM {{.tableName}}
 81 |     description: |
 82 |       Use this tool to list all information from a specific table.
 83 |       Example:
 84 |       {{
 85 |           "tableName": "flights",
 86 |       }}
 87 |     templateParameters:
 88 |       - name: tableName
 89 |         type: string
 90 |         description: Table to select from
 91 | ```
 92 | 
 93 | ## Reference
 94 | 
 95 | | **field**          |                     **type**                     | **required** | **description**                                                                                                                            |
 96 | |--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
 97 | | kind               |                      string                      |     true     | Must be "yugabytedb-sql".                                                                                                                  |
 98 | | source             |                      string                      |     true     | Name of the source the SQL should execute on.                                                                                              |
 99 | | description        |                      string                      |     true     | Description of the tool that is passed to the LLM.                                                                                         |
100 | | statement          |                      string                      |     true     | SQL statement to execute on.                                                                                                               |
101 | | parameters         |    [parameters](_index#specifying-parameters)    |    false     | List of [parameters](_index#specifying-parameters) that will be inserted into the SQL statement.                                           |
102 | | templateParameters | [templateParameters](_index#template-parameters) |    false     | List of [templateParameters](_index#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
103 | 
```

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

```go
  1 | // Copyright 2025 Google LLC
  2 | //
  3 | // Licensed under the Apache License, Version 2.0 (the "License");
  4 | // you may not use this file except in compliance with the License.
  5 | // You may obtain a copy of the License at
  6 | //
  7 | //     http://www.apache.org/licenses/LICENSE-2.0
  8 | //
  9 | // Unless required by applicable law or agreed to in writing, software
 10 | // distributed under the License is distributed on an "AS IS" BASIS,
 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12 | // See the License for the specific language governing permissions and
 13 | // limitations under the License.
 14 | 
 15 | package clickhouse
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"database/sql"
 20 | 	"fmt"
 21 | 
 22 | 	yaml "github.com/goccy/go-yaml"
 23 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | )
 26 | 
 27 | type compatibleSource interface {
 28 | 	ClickHousePool() *sql.DB
 29 | }
 30 | 
 31 | var compatibleSources = []string{"clickhouse"}
 32 | 
 33 | const listTablesKind string = "clickhouse-list-tables"
 34 | const databaseKey string = "database"
 35 | 
 36 | func init() {
 37 | 	if !tools.Register(listTablesKind, newListTablesConfig) {
 38 | 		panic(fmt.Sprintf("tool kind %q already registered", listTablesKind))
 39 | 	}
 40 | }
 41 | 
 42 | func newListTablesConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 43 | 	actual := Config{Name: name}
 44 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 45 | 		return nil, err
 46 | 	}
 47 | 	return actual, nil
 48 | }
 49 | 
 50 | type Config struct {
 51 | 	Name         string           `yaml:"name" validate:"required"`
 52 | 	Kind         string           `yaml:"kind" validate:"required"`
 53 | 	Source       string           `yaml:"source" validate:"required"`
 54 | 	Description  string           `yaml:"description" validate:"required"`
 55 | 	AuthRequired []string         `yaml:"authRequired"`
 56 | 	Parameters   tools.Parameters `yaml:"parameters"`
 57 | }
 58 | 
 59 | var _ tools.ToolConfig = Config{}
 60 | 
 61 | func (cfg Config) ToolConfigKind() string {
 62 | 	return listTablesKind
 63 | }
 64 | 
 65 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 66 | 	rawS, ok := srcs[cfg.Source]
 67 | 	if !ok {
 68 | 		return nil, fmt.Errorf("no source named %q configured", cfg.Source)
 69 | 	}
 70 | 
 71 | 	s, ok := rawS.(compatibleSource)
 72 | 	if !ok {
 73 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be one of %q", listTablesKind, compatibleSources)
 74 | 	}
 75 | 
 76 | 	databaseParameter := tools.NewStringParameter(databaseKey, "The database to list tables from.")
 77 | 	parameters := tools.Parameters{databaseParameter}
 78 | 
 79 | 	allParameters, paramManifest, _ := tools.ProcessParameters(nil, parameters)
 80 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters)
 81 | 
 82 | 	t := Tool{
 83 | 		Name:         cfg.Name,
 84 | 		Kind:         listTablesKind,
 85 | 		Parameters:   parameters,
 86 | 		AllParams:    allParameters,
 87 | 		AuthRequired: cfg.AuthRequired,
 88 | 		Pool:         s.ClickHousePool(),
 89 | 		manifest:     tools.Manifest{Description: cfg.Description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
 90 | 		mcpManifest:  mcpManifest,
 91 | 	}
 92 | 	return t, nil
 93 | }
 94 | 
 95 | var _ tools.Tool = Tool{}
 96 | 
 97 | type Tool struct {
 98 | 	Name         string           `yaml:"name"`
 99 | 	Kind         string           `yaml:"kind"`
100 | 	AuthRequired []string         `yaml:"authRequired"`
101 | 	Parameters   tools.Parameters `yaml:"parameters"`
102 | 	AllParams    tools.Parameters `yaml:"allParams"`
103 | 
104 | 	Pool        *sql.DB
105 | 	manifest    tools.Manifest
106 | 	mcpManifest tools.McpManifest
107 | }
108 | 
109 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, token tools.AccessToken) (any, error) {
110 | 	mapParams := params.AsMap()
111 | 	database, ok := mapParams[databaseKey].(string)
112 | 	if !ok {
113 | 		return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", databaseKey)
114 | 	}
115 | 
116 | 	// Query to list all tables in the specified database
117 | 	query := fmt.Sprintf("SHOW TABLES FROM %s", database)
118 | 
119 | 	results, err := t.Pool.QueryContext(ctx, query)
120 | 	if err != nil {
121 | 		return nil, fmt.Errorf("unable to execute query: %w", err)
122 | 	}
123 | 	defer results.Close()
124 | 
125 | 	var tables []map[string]any
126 | 	for results.Next() {
127 | 		var tableName string
128 | 		err := results.Scan(&tableName)
129 | 		if err != nil {
130 | 			return nil, fmt.Errorf("unable to parse row: %w", err)
131 | 		}
132 | 		tables = append(tables, map[string]any{
133 | 			"name":     tableName,
134 | 			"database": database,
135 | 		})
136 | 	}
137 | 
138 | 	if err := results.Err(); err != nil {
139 | 		return nil, fmt.Errorf("errors encountered by results.Scan: %w", err)
140 | 	}
141 | 
142 | 	return tables, nil
143 | }
144 | 
145 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
146 | 	return tools.ParseParams(t.AllParams, data, claims)
147 | }
148 | 
149 | func (t Tool) Manifest() tools.Manifest {
150 | 	return t.manifest
151 | }
152 | 
153 | func (t Tool) McpManifest() tools.McpManifest {
154 | 	return t.mcpManifest
155 | }
156 | 
157 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
158 | 	return tools.IsAuthorized(t.AuthRequired, verifiedAuthServices)
159 | }
160 | 
161 | func (t Tool) RequiresClientAuthorization() bool {
162 | 	return false
163 | }
164 | 
```

--------------------------------------------------------------------------------
/internal/server/server_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 server_test
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 	"io"
 21 | 	"net/http"
 22 | 	"os"
 23 | 	"strings"
 24 | 	"testing"
 25 | 
 26 | 	"github.com/google/go-cmp/cmp"
 27 | 	"github.com/googleapis/genai-toolbox/internal/auth"
 28 | 	"github.com/googleapis/genai-toolbox/internal/log"
 29 | 	"github.com/googleapis/genai-toolbox/internal/server"
 30 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 31 | 	"github.com/googleapis/genai-toolbox/internal/sources/alloydbpg"
 32 | 	"github.com/googleapis/genai-toolbox/internal/telemetry"
 33 | 	"github.com/googleapis/genai-toolbox/internal/testutils"
 34 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 35 | 	"github.com/googleapis/genai-toolbox/internal/util"
 36 | )
 37 | 
 38 | func TestServe(t *testing.T) {
 39 | 	ctx, cancel := context.WithCancel(context.Background())
 40 | 	defer cancel()
 41 | 
 42 | 	addr, port := "127.0.0.1", 5000
 43 | 	cfg := server.ServerConfig{
 44 | 		Version: "0.0.0",
 45 | 		Address: addr,
 46 | 		Port:    port,
 47 | 	}
 48 | 
 49 | 	otelShutdown, err := telemetry.SetupOTel(ctx, "0.0.0", "", false, "toolbox")
 50 | 	if err != nil {
 51 | 		t.Fatalf("unexpected error: %s", err)
 52 | 	}
 53 | 	defer func() {
 54 | 		err := otelShutdown(ctx)
 55 | 		if err != nil {
 56 | 			t.Fatalf("unexpected error: %s", err)
 57 | 		}
 58 | 	}()
 59 | 
 60 | 	testLogger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info")
 61 | 	if err != nil {
 62 | 		t.Fatalf("unexpected error: %s", err)
 63 | 	}
 64 | 	ctx = util.WithLogger(ctx, testLogger)
 65 | 
 66 | 	instrumentation, err := telemetry.CreateTelemetryInstrumentation(cfg.Version)
 67 | 	if err != nil {
 68 | 		t.Fatalf("unexpected error: %s", err)
 69 | 	}
 70 | 
 71 | 	ctx = util.WithInstrumentation(ctx, instrumentation)
 72 | 
 73 | 	s, err := server.NewServer(ctx, cfg)
 74 | 	if err != nil {
 75 | 		t.Fatalf("unable to initialize server: %v", err)
 76 | 	}
 77 | 
 78 | 	err = s.Listen(ctx)
 79 | 	if err != nil {
 80 | 		t.Fatalf("unable to start server: %v", err)
 81 | 	}
 82 | 
 83 | 	// start server in background
 84 | 	errCh := make(chan error)
 85 | 	go func() {
 86 | 		defer close(errCh)
 87 | 
 88 | 		err = s.Serve(ctx)
 89 | 		if err != nil {
 90 | 			errCh <- err
 91 | 		}
 92 | 	}()
 93 | 
 94 | 	url := fmt.Sprintf("http://%s:%d/", addr, port)
 95 | 	resp, err := http.Get(url)
 96 | 	if err != nil {
 97 | 		t.Fatalf("error when sending a request: %s", err)
 98 | 	}
 99 | 	defer resp.Body.Close()
100 | 	if resp.StatusCode != 200 {
101 | 		t.Fatalf("response status code is not 200")
102 | 	}
103 | 	raw, err := io.ReadAll(resp.Body)
104 | 	if err != nil {
105 | 		t.Fatalf("error reading from request body: %s", err)
106 | 	}
107 | 	if got := string(raw); strings.Contains(got, "0.0.0") {
108 | 		t.Fatalf("version missing from output: %q", got)
109 | 	}
110 | }
111 | 
112 | func TestUpdateServer(t *testing.T) {
113 | 	ctx, err := testutils.ContextWithNewLogger()
114 | 	if err != nil {
115 | 		t.Fatalf("error setting up logger: %s", err)
116 | 	}
117 | 
118 | 	addr, port := "127.0.0.1", 5000
119 | 	cfg := server.ServerConfig{
120 | 		Version: "0.0.0",
121 | 		Address: addr,
122 | 		Port:    port,
123 | 	}
124 | 
125 | 	instrumentation, err := telemetry.CreateTelemetryInstrumentation(cfg.Version)
126 | 	if err != nil {
127 | 		t.Fatalf("unexpected error: %s", err)
128 | 	}
129 | 
130 | 	ctx = util.WithInstrumentation(ctx, instrumentation)
131 | 
132 | 	s, err := server.NewServer(ctx, cfg)
133 | 	if err != nil {
134 | 		t.Fatalf("error setting up server: %s", err)
135 | 	}
136 | 
137 | 	newSources := map[string]sources.Source{
138 | 		"example-source": &alloydbpg.Source{
139 | 			Name: "example-alloydb-source",
140 | 			Kind: "alloydb-postgres",
141 | 		},
142 | 	}
143 | 	newAuth := map[string]auth.AuthService{"example-auth": nil}
144 | 	newTools := map[string]tools.Tool{"example-tool": nil}
145 | 	newToolsets := map[string]tools.Toolset{
146 | 		"example-toolset": {
147 | 			Name: "example-toolset", Tools: []*tools.Tool{},
148 | 		},
149 | 	}
150 | 	s.ResourceMgr.SetResources(newSources, newAuth, newTools, newToolsets)
151 | 	if err != nil {
152 | 		t.Errorf("error updating server: %s", err)
153 | 	}
154 | 
155 | 	gotSource, _ := s.ResourceMgr.GetSource("example-source")
156 | 	if diff := cmp.Diff(gotSource, newSources["example-source"]); diff != "" {
157 | 		t.Errorf("error updating server, sources (-want +got):\n%s", diff)
158 | 	}
159 | 
160 | 	gotAuthService, _ := s.ResourceMgr.GetAuthService("example-auth")
161 | 	if diff := cmp.Diff(gotAuthService, newAuth["example-auth"]); diff != "" {
162 | 		t.Errorf("error updating server, authServices (-want +got):\n%s", diff)
163 | 	}
164 | 
165 | 	gotTool, _ := s.ResourceMgr.GetTool("example-tool")
166 | 	if diff := cmp.Diff(gotTool, newTools["example-tool"]); diff != "" {
167 | 		t.Errorf("error updating server, tools (-want +got):\n%s", diff)
168 | 	}
169 | 
170 | 	gotToolset, _ := s.ResourceMgr.GetToolset("example-toolset")
171 | 	if diff := cmp.Diff(gotToolset, newToolsets["example-toolset"]); diff != "" {
172 | 		t.Errorf("error updating server, toolset (-want +got):\n%s", diff)
173 | 	}
174 | }
175 | 
```

--------------------------------------------------------------------------------
/docs/en/resources/sources/oracle.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | title: "Oracle"
 3 | type: docs
 4 | weight: 1
 5 | description: >
 6 |   Oracle Database is a widely-used relational database management system.
 7 | ---
 8 | 
 9 | ## About
10 | 
11 | [Oracle Database][oracle-docs] is a multi-model database management system produced and marketed by Oracle Corporation. It is commonly used for running online transaction processing (OLTP), data warehousing (DW), and mixed (OLTP & DW) database workloads.
12 | 
13 | [oracle-docs]: https://www.oracle.com/database/
14 | 
15 | ## Available Tools
16 | 
17 | - [`oracle-sql`](../tools/oracle/oracle-sql.md)
18 |   Execute pre-defined prepared SQL queries in Oracle.
19 | 
20 | - [`oracle-execute-sql`](../tools/oracle/oracle-execute-sql.md)
21 |   Run parameterized SQL queries in Oracle.
22 | 
23 | ## Requirements
24 | 
25 | ### Database User
26 | 
27 | This source uses standard authentication. You will need to [create an Oracle user][oracle-users] to log in to the database with the necessary permissions.
28 | 
29 | [oracle-users]:
30 |     https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/CREATE-USER.html
31 | 
32 | ## Connection Methods
33 | 
34 | You can configure the connection to your Oracle database using one of the following three methods. **You should only use one method** in your source configuration.
35 | 
36 | ### Basic Connection (Host/Port/Service Name)
37 | 
38 | This is the most straightforward method, where you provide the connection details as separate fields:
39 | 
40 | - `host`: The IP address or hostname of the database server.
41 | - `port`: The port number the Oracle listener is running on (typically 1521).
42 | - `serviceName`: The service name for the database instance you wish to connect to.
43 | 
44 | ### Connection String
45 | 
46 | As an alternative, you can provide all the connection details in a single `connectionString`. This is a convenient way to consolidate the connection information. The typical format is `hostname:port/servicename`.
47 | 
48 | ### TNS Alias
49 | 
50 | For environments that use a `tnsnames.ora` configuration file, you can connect using a TNS (Transparent Network Substrate) alias.
51 | 
52 | - `tnsAlias`: Specify the alias name defined in your `tnsnames.ora` file.
53 | - `tnsAdmin` (Optional): If your configuration file is not in a standard location, you can use this field to provide the path to the directory containing it. This setting will override the `TNS_ADMIN` environment variable.
54 | 
55 | ## Example
56 | 
57 | ```yaml
58 | sources:
59 |     my-oracle-source:
60 |         kind: oracle
61 |         # --- Choose one connection method ---
62 |         # 1. Host, Port, and Service Name
63 |         host: 127.0.0.1
64 |         port: 1521
65 |         serviceName: XEPDB1
66 | 
67 |         # 2. Direct Connection String
68 |         connectionString: "127.0.0.1:1521/XEPDB1"
69 | 
70 |         # 3. TNS Alias (requires tnsnames.ora)
71 |         tnsAlias: "MY_DB_ALIAS"
72 |         tnsAdmin: "/opt/oracle/network/admin" # Optional: overrides TNS_ADMIN env var
73 | 
74 |         user: ${USER_NAME}
75 |         password: ${PASSWORD}
76 | 
77 | ```
78 | 
79 | {{< notice tip >}}
80 | Use environment variable replacement with the format ${ENV_NAME}
81 | instead of hardcoding your secrets into the configuration file.
82 | {{< /notice >}}
83 | 
84 | ## Reference
85 | 
86 | | **field**        | **type** | **required** | **description**                                                                                                             |
87 | |------------------|:--------:|:------------:|-----------------------------------------------------------------------------------------------------------------------------|
88 | | kind             |  string  |     true     | Must be "oracle".                                                                                                           |
89 | | user             |  string  |     true     | Name of the Oracle user to connect as (e.g. "my-oracle-user").                                                              |
90 | | password         |  string  |     true     | Password of the Oracle user (e.g. "my-password").                                                                           |
91 | | host             |  string  |    false     | IP address or hostname to connect to (e.g. "127.0.0.1"). Required if not using `connectionString` or `tnsAlias`.            |
92 | | port             | integer  |    false     | Port to connect to (e.g. "1521"). Required if not using `connectionString` or `tnsAlias`.                                   |
93 | | serviceName      |  string  |    false     | The Oracle service name of the database to connect to. Required if not using `connectionString` or `tnsAlias`.              |
94 | | connectionString |  string  |    false     | A direct connection string (e.g. "hostname:port/servicename"). Use as an alternative to `host`, `port`, and `serviceName`.  |
95 | | tnsAlias         |  string  |    false     | A TNS alias from a `tnsnames.ora` file. Use as an alternative to `host`/`port` or `connectionString`.                       |
96 | | tnsAdmin         |  string  |    false     | Path to the directory containing the `tnsnames.ora` file. This overrides the `TNS_ADMIN` environment variable if it is set. |
97 | 
```

--------------------------------------------------------------------------------
/docs/en/getting-started/local_quickstart.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | title: "Python Quickstart (Local)"
  3 | type: docs
  4 | weight: 2
  5 | description: >
  6 |   How to get started running Toolbox locally with [Python](https://github.com/googleapis/mcp-toolbox-sdk-python), PostgreSQL, and  [Agent Development Kit](https://google.github.io/adk-docs/),
  7 |   [LangGraph](https://www.langchain.com/langgraph), [LlamaIndex](https://www.llamaindex.ai/) or [GoogleGenAI](https://pypi.org/project/google-genai/).
  8 | ---
  9 | 
 10 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/googleapis/genai-toolbox/blob/main/docs/en/getting-started/colab_quickstart.ipynb)
 11 | 
 12 | ## Before you begin
 13 | 
 14 | This guide assumes you have already done the following:
 15 | 
 16 | 1. Installed [Python 3.9+][install-python] (including [pip][install-pip] and
 17 |    your preferred virtual environment tool for managing dependencies e.g.
 18 |    [venv][install-venv]).
 19 | 1. Installed [PostgreSQL 16+ and the `psql` client][install-postgres].
 20 | 
 21 | [install-python]: https://wiki.python.org/moin/BeginnersGuide/Download
 22 | [install-pip]: https://pip.pypa.io/en/stable/installation/
 23 | [install-venv]:
 24 |     https://packaging.python.org/en/latest/tutorials/installing-packages/#creating-virtual-environments
 25 | [install-postgres]: https://www.postgresql.org/download/
 26 | 
 27 | ### Cloud Setup (Optional)
 28 | {{< regionInclude "quickstart/shared/cloud_setup.md" "cloud_setup" >}}
 29 | 
 30 | ## Step 1: Set up your database
 31 | {{< regionInclude "quickstart/shared/database_setup.md" "database_setup" >}}
 32 | 
 33 | ## Step 2: Install and configure Toolbox
 34 | {{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}}
 35 | 
 36 | ## Step 3: Connect your agent to Toolbox
 37 | 
 38 | In this section, we will write and run an agent that will load the Tools
 39 | from Toolbox.
 40 | 
 41 | {{< notice tip>}}
 42 | If you prefer to experiment within a Google Colab environment, you can connect
 43 | to a [local
 44 | runtime](https://research.google.com/colaboratory/local-runtimes.html).
 45 | {{< /notice >}}
 46 | 
 47 | 1. In a new terminal, install the SDK package.
 48 | 
 49 |     {{< tabpane persist=header >}}
 50 | {{< tab header="ADK" lang="bash" >}}
 51 | 
 52 | pip install toolbox-core
 53 | {{< /tab >}}
 54 | {{< tab header="Langchain" lang="bash" >}}
 55 | 
 56 | pip install toolbox-langchain
 57 | {{< /tab >}}
 58 | {{< tab header="LlamaIndex" lang="bash" >}}
 59 | 
 60 | pip install toolbox-llamaindex
 61 | {{< /tab >}}
 62 | {{< tab header="Core" lang="bash" >}}
 63 | 
 64 | pip install toolbox-core
 65 | {{< /tab >}}
 66 | {{< /tabpane >}}
 67 | 
 68 | 1. Install other required dependencies:
 69 | 
 70 |     {{< tabpane persist=header >}}
 71 | {{< tab header="ADK" lang="bash" >}}
 72 | 
 73 | pip install google-adk
 74 | {{< /tab >}}
 75 | {{< tab header="Langchain" lang="bash" >}}
 76 | 
 77 | # TODO(developer): replace with correct package if needed
 78 | 
 79 | pip install langgraph langchain-google-vertexai
 80 | 
 81 | # pip install langchain-google-genai
 82 | 
 83 | # pip install langchain-anthropic
 84 | 
 85 | {{< /tab >}}
 86 | {{< tab header="LlamaIndex" lang="bash" >}}
 87 | 
 88 | # TODO(developer): replace with correct package if needed
 89 | 
 90 | pip install llama-index-llms-google-genai
 91 | 
 92 | # pip install llama-index-llms-anthropic
 93 | 
 94 | {{< /tab >}}
 95 | {{< tab header="Core" lang="bash" >}}
 96 | 
 97 | pip install google-genai
 98 | {{< /tab >}}
 99 | {{< /tabpane >}}
100 | 
101 | 1. Create a new file named `hotel_agent.py` and copy the following
102 |    code to create an agent:
103 |     {{< tabpane persist=header >}}
104 | {{< tab header="ADK" lang="python" >}}
105 | 
106 | {{< include "quickstart/python/adk/quickstart.py" >}}
107 | 
108 | {{< /tab >}}
109 | {{< tab header="LangChain" lang="python" >}}
110 | 
111 | {{< include "quickstart/python/langchain/quickstart.py" >}}
112 | 
113 | {{< /tab >}}
114 | {{< tab header="LlamaIndex" lang="python" >}}
115 | 
116 | {{< include "quickstart/python/llamaindex/quickstart.py" >}}
117 | 
118 | {{< /tab >}}
119 | {{< tab header="Core" lang="python" >}}
120 | 
121 | {{< include "quickstart/python/core/quickstart.py" >}}
122 | 
123 | {{< /tab >}}
124 | {{< /tabpane >}}
125 | 
126 |     {{< tabpane text=true persist=header >}}
127 | {{% tab header="ADK" lang="en" %}}
128 | To learn more about Agent Development Kit, check out the [ADK
129 | documentation.](https://google.github.io/adk-docs/)
130 | {{% /tab %}}
131 | {{% tab header="Langchain" lang="en" %}}
132 | To learn more about Agents in LangChain, check out the [LangGraph Agent
133 | documentation.](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.chat_agent_executor.create_react_agent)
134 | {{% /tab %}}
135 | {{% tab header="LlamaIndex" lang="en" %}}
136 | To learn more about Agents in LlamaIndex, check out the [LlamaIndex
137 | AgentWorkflow
138 | documentation.](https://docs.llamaindex.ai/en/stable/examples/agent/agent_workflow_basic/)
139 | {{% /tab %}}
140 | {{% tab header="Core" lang="en" %}}
141 | To learn more about tool calling with Google GenAI, check out the
142 | [Google GenAI
143 | Documentation](https://github.com/googleapis/python-genai?tab=readme-ov-file#manually-declare-and-invoke-a-function-for-function-calling).
144 | {{% /tab %}}
145 | {{< /tabpane >}}
146 | 
147 | 1. Run your agent, and observe the results:
148 | 
149 |     ```sh
150 |     python hotel_agent.py
151 |     ```
152 | 
153 | {{< notice info >}}
154 | For more information, visit the [Python SDK
155 | repo](https://github.com/googleapis/mcp-toolbox-sdk-python).
156 | {{</ notice >}}
157 | 
```

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

```markdown
  1 | ---
  2 | title: "mysql-sql"
  3 | type: docs
  4 | weight: 1
  5 | description: >
  6 |   A "mysql-sql" tool executes a pre-defined SQL statement against a MySQL
  7 |   database.
  8 | aliases:
  9 | - /resources/tools/mysql-sql
 10 | ---
 11 | 
 12 | ## About
 13 | 
 14 | A `mysql-sql` tool executes a pre-defined SQL statement against a MySQL
 15 | database. It's compatible with any of the following sources:
 16 | 
 17 | - [cloud-sql-mysql](../../sources/cloud-sql-mysql.md)
 18 | - [mysql](../../sources/mysql.md)
 19 | 
 20 | The specified SQL statement is executed as a [prepared statement][mysql-prepare],
 21 | and expects parameters in the SQL query to be in the form of placeholders `?`.
 22 | 
 23 | [mysql-prepare]: https://dev.mysql.com/doc/refman/8.4/en/sql-prepared-statements.html
 24 | 
 25 | ## Example
 26 | 
 27 | > **Note:** This tool uses parameterized queries to prevent SQL injections.
 28 | > Query parameters can be used as substitutes for arbitrary expressions.
 29 | > Parameters cannot be used as substitutes for identifiers, column names, table
 30 | > names, or other parts of the query.
 31 | 
 32 | ```yaml
 33 | tools:
 34 |  search_flights_by_number:
 35 |     kind: mysql-sql
 36 |     source: my-mysql-instance
 37 |     statement: |
 38 |       SELECT * FROM flights
 39 |       WHERE airline = ?
 40 |       AND flight_number = ?
 41 |       LIMIT 10
 42 |     description: |
 43 |       Use this tool to get information for a specific flight.
 44 |       Takes an airline code and flight number and returns info on the flight.
 45 |       Do NOT use this tool with a flight id. Do NOT guess an airline code or flight number.
 46 |       A airline code is a code for an airline service consisting of two-character
 47 |       airline designator and followed by flight number, which is 1 to 4 digit number.
 48 |       For example, if given CY 0123, the airline is "CY", and flight_number is "123".
 49 |       Another example for this is DL 1234, the airline is "DL", and flight_number is "1234".
 50 |       If the tool returns more than one option choose the date closes to today.
 51 |       Example:
 52 |       {{
 53 |           "airline": "CY",
 54 |           "flight_number": "888",
 55 |       }}
 56 |       Example:
 57 |       {{
 58 |           "airline": "DL",
 59 |           "flight_number": "1234",
 60 |       }}
 61 |     parameters:
 62 |       - name: airline
 63 |         type: string
 64 |         description: Airline unique 2 letter identifier
 65 |       - name: flight_number
 66 |         type: string
 67 |         description: 1 to 4 digit number
 68 | ```
 69 | 
 70 | ### Example with Template Parameters
 71 | 
 72 | > **Note:** This tool allows direct modifications to the SQL statement,
 73 | > including identifiers, column names, and table names. **This makes it more
 74 | > vulnerable to SQL injections**. Using basic parameters only (see above) is
 75 | > recommended for performance and safety reasons. For more details, please check
 76 | > [templateParameters](..#template-parameters).
 77 | 
 78 | ```yaml
 79 | tools:
 80 |  list_table:
 81 |     kind: mysql-sql
 82 |     source: my-mysql-instance
 83 |     statement: |
 84 |       SELECT * FROM {{.tableName}};
 85 |     description: |
 86 |       Use this tool to list all information from a specific table.
 87 |       Example:
 88 |       {{
 89 |           "tableName": "flights",
 90 |       }}
 91 |     templateParameters:
 92 |       - name: tableName
 93 |         type: string
 94 |         description: Table to select from
 95 | ```
 96 | 
 97 | ## Reference
 98 | 
 99 | | **field**          |                  **type**                        | **required** | **description**                                                                                                                            |
100 | |--------------------|:------------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------------------------------------------------|
101 | | kind               |                   string                         |     true     | Must be "mysql-sql".                                                                                                                       |
102 | | source             |                   string                         |     true     | Name of the source the SQL should execute on.                                                                                              |
103 | | description        |                   string                         |     true     | Description of the tool that is passed to the LLM.                                                                                         |
104 | | statement          |                   string                         |     true     | SQL statement to execute on.                                                                                                               |
105 | | parameters         | [parameters](../#specifying-parameters)       |    false     | List of [parameters](../#specifying-parameters) that will be inserted into the SQL statement.                                           |
106 | | templateParameters | [templateParameters](..#template-parameters) |    false     | List of [templateParameters](..#template-parameters) that will be inserted into the SQL statement before executing prepared statement. |
107 | 
```

--------------------------------------------------------------------------------
/internal/tools/alloydb/alloydblistclusters/alloydblistclusters.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 alloydblistclusters
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	yaml "github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	alloydbadmin "github.com/googleapis/genai-toolbox/internal/sources/alloydbadmin"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | )
 26 | 
 27 | const kind string = "alloydb-list-clusters"
 28 | 
 29 | func init() {
 30 | 	if !tools.Register(kind, newConfig) {
 31 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 32 | 	}
 33 | }
 34 | 
 35 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 36 | 	actual := Config{Name: name}
 37 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 38 | 		return nil, err
 39 | 	}
 40 | 	return actual, nil
 41 | }
 42 | 
 43 | // Configuration for the list-clusters tool.
 44 | type Config struct {
 45 | 	Name         string   `yaml:"name" validate:"required"`
 46 | 	Kind         string   `yaml:"kind" validate:"required"`
 47 | 	Source       string   `yaml:"source" validate:"required"`
 48 | 	Description  string   `yaml:"description"`
 49 | 	AuthRequired []string `yaml:"authRequired"`
 50 | 	BaseURL      string   `yaml:"baseURL"`
 51 | }
 52 | 
 53 | // validate interface
 54 | var _ tools.ToolConfig = Config{}
 55 | 
 56 | // ToolConfigKind returns the kind of the tool.
 57 | func (cfg Config) ToolConfigKind() string {
 58 | 	return kind
 59 | }
 60 | 
 61 | // Initialize initializes the tool from the configuration.
 62 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 63 | 	rawS, ok := srcs[cfg.Source]
 64 | 	if !ok {
 65 | 		return nil, fmt.Errorf("source %q not found", cfg.Source)
 66 | 	}
 67 | 
 68 | 	s, ok := rawS.(*alloydbadmin.Source)
 69 | 	if !ok {
 70 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `%s`", kind, alloydbadmin.SourceKind)
 71 | 	}
 72 | 
 73 | 	allParameters := tools.Parameters{
 74 | 		tools.NewStringParameter("project", "The GCP project ID to list clusters for."),
 75 | 		tools.NewStringParameterWithDefault("location", "-", "Optional: The location to list clusters in (e.g., 'us-central1'). Use '-' to list clusters across all locations.(Default: '-')"),
 76 | 	}
 77 | 	paramManifest := allParameters.Manifest()
 78 | 
 79 | 	description := cfg.Description
 80 | 	if description == "" {
 81 | 		description = "Lists all AlloyDB clusters in a given project and location."
 82 | 	}
 83 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
 84 | 
 85 | 	return Tool{
 86 | 		Name:        cfg.Name,
 87 | 		Kind:        kind,
 88 | 		Source:      s,
 89 | 		AllParams:   allParameters,
 90 | 		manifest:    tools.Manifest{Description: description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
 91 | 		mcpManifest: mcpManifest,
 92 | 	}, nil
 93 | }
 94 | 
 95 | // Tool represents the list-clusters tool.
 96 | type Tool struct {
 97 | 	Name        string `yaml:"name"`
 98 | 	Kind        string `yaml:"kind"`
 99 | 	Description string `yaml:"description"`
100 | 
101 | 	Source    *alloydbadmin.Source
102 | 	AllParams tools.Parameters `yaml:"allParams"`
103 | 
104 | 	manifest    tools.Manifest
105 | 	mcpManifest tools.McpManifest
106 | }
107 | 
108 | // Invoke executes the tool's logic.
109 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
110 | 	paramsMap := params.AsMap()
111 | 
112 | 	project, ok := paramsMap["project"].(string)
113 | 	if !ok {
114 | 		return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
115 | 	}
116 | 	location, ok := paramsMap["location"].(string)
117 | 	if !ok {
118 | 		return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
119 | 	}
120 | 
121 | 	service, err := t.Source.GetService(ctx, string(accessToken))
122 | 	if err != nil {
123 | 		return nil, err
124 | 	}
125 | 
126 | 	urlString := fmt.Sprintf("projects/%s/locations/%s", project, location)
127 | 
128 | 	resp, err := service.Projects.Locations.Clusters.List(urlString).Do()
129 | 	if err != nil {
130 | 		return nil, fmt.Errorf("error listing AlloyDB clusters: %w", err)
131 | 	}
132 | 
133 | 	return resp, nil
134 | }
135 | 
136 | // ParseParams parses the parameters for the tool.
137 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
138 | 	return tools.ParseParams(t.AllParams, data, claims)
139 | }
140 | 
141 | // Manifest returns the tool's manifest.
142 | func (t Tool) Manifest() tools.Manifest {
143 | 	return t.manifest
144 | }
145 | 
146 | // McpManifest returns the tool's MCP manifest.
147 | func (t Tool) McpManifest() tools.McpManifest {
148 | 	return t.mcpManifest
149 | }
150 | 
151 | // Authorized checks if the tool is authorized.
152 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
153 | 	return true
154 | }
155 | 
156 | func (t Tool) RequiresClientAuthorization() bool {
157 | 	return t.Source.UseClientAuthorization()
158 | }
159 | 
```

--------------------------------------------------------------------------------
/internal/tools/neo4j/neo4jschema/cache/cache_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 cache
 16 | 
 17 | import (
 18 | 	"sync"
 19 | 	"testing"
 20 | 	"time"
 21 | )
 22 | 
 23 | // TestCache_SetAndGet verifies the basic functionality of setting a value
 24 | // and immediately retrieving it.
 25 | func TestCache_SetAndGet(t *testing.T) {
 26 | 	cache := NewCache()
 27 | 	defer cache.Stop()
 28 | 
 29 | 	key := "testKey"
 30 | 	value := "testValue"
 31 | 
 32 | 	cache.Set(key, value, 1*time.Minute)
 33 | 
 34 | 	retrievedValue, found := cache.Get(key)
 35 | 	if !found {
 36 | 		t.Errorf("Expected to find key %q, but it was not found", key)
 37 | 	}
 38 | 
 39 | 	if retrievedValue != value {
 40 | 		t.Errorf("Expected value %q, but got %q", value, retrievedValue)
 41 | 	}
 42 | }
 43 | 
 44 | // TestCache_GetExpired tests that an item is not retrievable after it has expired.
 45 | func TestCache_GetExpired(t *testing.T) {
 46 | 	cache := NewCache()
 47 | 	defer cache.Stop()
 48 | 
 49 | 	key := "expiredKey"
 50 | 	value := "expiredValue"
 51 | 
 52 | 	// Set an item with a very short TTL.
 53 | 	cache.Set(key, value, 1*time.Millisecond)
 54 | 	time.Sleep(2 * time.Millisecond) // Wait for the item to expire.
 55 | 
 56 | 	// Attempt to get the expired item.
 57 | 	_, found := cache.Get(key)
 58 | 	if found {
 59 | 		t.Errorf("Expected key %q to be expired, but it was found", key)
 60 | 	}
 61 | }
 62 | 
 63 | // TestCache_SetNoExpiration ensures that an item with a TTL of 0 or less
 64 | // does not expire.
 65 | func TestCache_SetNoExpiration(t *testing.T) {
 66 | 	cache := NewCache()
 67 | 	defer cache.Stop()
 68 | 
 69 | 	key := "noExpireKey"
 70 | 	value := "noExpireValue"
 71 | 
 72 | 	cache.Set(key, value, 0) // Setting with 0 should mean no expiration.
 73 | 	time.Sleep(5 * time.Millisecond)
 74 | 
 75 | 	retrievedValue, found := cache.Get(key)
 76 | 	if !found {
 77 | 		t.Errorf("Expected to find key %q, but it was not found", key)
 78 | 	}
 79 | 	if retrievedValue != value {
 80 | 		t.Errorf("Expected value %q, but got %q", value, retrievedValue)
 81 | 	}
 82 | }
 83 | 
 84 | // TestCache_Janitor verifies that the janitor goroutine automatically removes
 85 | // expired items from the cache.
 86 | func TestCache_Janitor(t *testing.T) {
 87 | 	// Initialize cache with a very short janitor interval for quick testing.
 88 | 	cache := NewCache().WithJanitor(10 * time.Millisecond)
 89 | 	defer cache.Stop()
 90 | 
 91 | 	expiredKey := "expired"
 92 | 	activeKey := "active"
 93 | 
 94 | 	// Set one item that will expire and one that will not.
 95 | 	cache.Set(expiredKey, "value", 1*time.Millisecond)
 96 | 	cache.Set(activeKey, "value", 1*time.Hour)
 97 | 
 98 | 	// Wait longer than the janitor interval to ensure it has a chance to run.
 99 | 	time.Sleep(20 * time.Millisecond)
100 | 
101 | 	// Check that the expired key has been removed.
102 | 	_, found := cache.Get(expiredKey)
103 | 	if found {
104 | 		t.Errorf("Expected janitor to clean up expired key %q, but it was found", expiredKey)
105 | 	}
106 | 
107 | 	// Check that the active key is still present.
108 | 	_, found = cache.Get(activeKey)
109 | 	if !found {
110 | 		t.Errorf("Expected active key %q to be present, but it was not found", activeKey)
111 | 	}
112 | }
113 | 
114 | // TestCache_Stop ensures that calling the Stop method does not cause a panic,
115 | // regardless of whether the janitor is running or not. It also tests idempotency.
116 | func TestCache_Stop(t *testing.T) {
117 | 	t.Run("Stop without janitor", func(t *testing.T) {
118 | 		cache := NewCache()
119 | 		// Test that calling Stop multiple times on a cache without a janitor is safe.
120 | 		cache.Stop()
121 | 		cache.Stop()
122 | 	})
123 | 
124 | 	t.Run("Stop with janitor", func(t *testing.T) {
125 | 		cache := NewCache().WithJanitor(1 * time.Minute)
126 | 		// Test that calling Stop multiple times on a cache with a janitor is safe.
127 | 		cache.Stop()
128 | 		cache.Stop()
129 | 	})
130 | }
131 | 
132 | // TestCache_Concurrent performs a stress test on the cache with concurrent
133 | // reads and writes to check for race conditions.
134 | func TestCache_Concurrent(t *testing.T) {
135 | 	cache := NewCache().WithJanitor(100 * time.Millisecond)
136 | 	defer cache.Stop()
137 | 
138 | 	var wg sync.WaitGroup
139 | 	numGoroutines := 100
140 | 	numOperations := 1000
141 | 
142 | 	// Start concurrent writer goroutines.
143 | 	for i := 0; i < numGoroutines; i++ {
144 | 		wg.Add(1)
145 | 		go func(g int) {
146 | 			defer wg.Done()
147 | 			for j := 0; j < numOperations; j++ {
148 | 				key := string(rune(g*numOperations + j))
149 | 				value := g*numOperations + j
150 | 				cache.Set(key, value, 10*time.Second)
151 | 			}
152 | 		}(i)
153 | 	}
154 | 
155 | 	// Start concurrent reader goroutines.
156 | 	for i := 0; i < numGoroutines; i++ {
157 | 		wg.Add(1)
158 | 		go func(g int) {
159 | 			defer wg.Done()
160 | 			for j := 0; j < numOperations; j++ {
161 | 				key := string(rune(g*numOperations + j))
162 | 				cache.Get(key) // We don't check the result, just that access is safe.
163 | 			}
164 | 		}(i)
165 | 	}
166 | 
167 | 	// Wait for all goroutines to complete. If a race condition exists, the Go
168 | 	// race detector (`go test -race`) will likely catch it.
169 | 	wg.Wait()
170 | }
171 | 
```

--------------------------------------------------------------------------------
/internal/tools/alloydb/alloydbgetcluster/alloydbgetcluster.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 alloydbgetcluster
 16 | 
 17 | import (
 18 | 	"context"
 19 | 	"fmt"
 20 | 
 21 | 	yaml "github.com/goccy/go-yaml"
 22 | 	"github.com/googleapis/genai-toolbox/internal/sources"
 23 | 	alloydbadmin "github.com/googleapis/genai-toolbox/internal/sources/alloydbadmin"
 24 | 	"github.com/googleapis/genai-toolbox/internal/tools"
 25 | )
 26 | 
 27 | const kind string = "alloydb-get-cluster"
 28 | 
 29 | func init() {
 30 | 	if !tools.Register(kind, newConfig) {
 31 | 		panic(fmt.Sprintf("tool kind %q already registered", kind))
 32 | 	}
 33 | }
 34 | 
 35 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
 36 | 	actual := Config{Name: name}
 37 | 	if err := decoder.DecodeContext(ctx, &actual); err != nil {
 38 | 		return nil, err
 39 | 	}
 40 | 	return actual, nil
 41 | }
 42 | 
 43 | // Configuration for the get-cluster tool.
 44 | type Config struct {
 45 | 	Name         string   `yaml:"name" validate:"required"`
 46 | 	Kind         string   `yaml:"kind" validate:"required"`
 47 | 	Source       string   `yaml:"source" validate:"required"`
 48 | 	Description  string   `yaml:"description"`
 49 | 	AuthRequired []string `yaml:"authRequired"`
 50 | 	BaseURL      string   `yaml:"baseURL"`
 51 | }
 52 | 
 53 | // validate interface
 54 | var _ tools.ToolConfig = Config{}
 55 | 
 56 | // ToolConfigKind returns the kind of the tool.
 57 | func (cfg Config) ToolConfigKind() string {
 58 | 	return kind
 59 | }
 60 | 
 61 | // Initialize initializes the tool from the configuration.
 62 | func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
 63 | 	rawS, ok := srcs[cfg.Source]
 64 | 	if !ok {
 65 | 		return nil, fmt.Errorf("source %q not found", cfg.Source)
 66 | 	}
 67 | 
 68 | 	s, ok := rawS.(*alloydbadmin.Source)
 69 | 	if !ok {
 70 | 		return nil, fmt.Errorf("invalid source for %q tool: source kind must be `%s`", kind, alloydbadmin.SourceKind)
 71 | 	}
 72 | 
 73 | 	allParameters := tools.Parameters{
 74 | 		tools.NewStringParameter("project", "The GCP project ID."),
 75 | 		tools.NewStringParameter("location", "The location of the cluster (e.g., 'us-central1')."),
 76 | 		tools.NewStringParameter("cluster", "The ID of the cluster."),
 77 | 	}
 78 | 	paramManifest := allParameters.Manifest()
 79 | 
 80 | 	description := cfg.Description
 81 | 	if description == "" {
 82 | 		description = "Retrieves details about a specific AlloyDB cluster."
 83 | 	}
 84 | 	mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters)
 85 | 
 86 | 	return Tool{
 87 | 		Name:        cfg.Name,
 88 | 		Kind:        kind,
 89 | 		Source:      s,
 90 | 		AllParams:   allParameters,
 91 | 		manifest:    tools.Manifest{Description: description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
 92 | 		mcpManifest: mcpManifest,
 93 | 	}, nil
 94 | }
 95 | 
 96 | // Tool represents the get-cluster tool.
 97 | type Tool struct {
 98 | 	Name string `yaml:"name"`
 99 | 	Kind string `yaml:"kind"`
100 | 
101 | 	Source    *alloydbadmin.Source
102 | 	AllParams tools.Parameters
103 | 
104 | 	manifest    tools.Manifest
105 | 	mcpManifest tools.McpManifest
106 | }
107 | 
108 | // Invoke executes the tool's logic.
109 | func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
110 | 	paramsMap := params.AsMap()
111 | 
112 | 	project, ok := paramsMap["project"].(string)
113 | 	if !ok {
114 | 		return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
115 | 	}
116 | 	location, ok := paramsMap["location"].(string)
117 | 	if !ok {
118 | 		return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
119 | 	}
120 | 	cluster, ok := paramsMap["cluster"].(string)
121 | 	if !ok {
122 | 		return nil, fmt.Errorf("invalid 'cluster' parameter; expected a string")
123 | 	}
124 | 
125 | 	service, err := t.Source.GetService(ctx, string(accessToken))
126 | 	if err != nil {
127 | 		return nil, err
128 | 	}
129 | 
130 | 	urlString := fmt.Sprintf("projects/%s/locations/%s/clusters/%s", project, location, cluster)
131 | 
132 | 	resp, err := service.Projects.Locations.Clusters.Get(urlString).Do()
133 | 	if err != nil {
134 | 		return nil, fmt.Errorf("error getting AlloyDB cluster: %w", err)
135 | 	}
136 | 
137 | 	return resp, nil
138 | }
139 | 
140 | // ParseParams parses the parameters for the tool.
141 | func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
142 | 	return tools.ParseParams(t.AllParams, data, claims)
143 | }
144 | 
145 | // Manifest returns the tool's manifest.
146 | func (t Tool) Manifest() tools.Manifest {
147 | 	return t.manifest
148 | }
149 | 
150 | // McpManifest returns the tool's MCP manifest.
151 | func (t Tool) McpManifest() tools.McpManifest {
152 | 	return t.mcpManifest
153 | }
154 | 
155 | // Authorized checks if the tool is authorized.
156 | func (t Tool) Authorized(verifiedAuthServices []string) bool {
157 | 	return true
158 | }
159 | 
160 | func (t Tool) RequiresClientAuthorization() bool {
161 | 	return t.Source.UseClientAuthorization()
162 | }
163 | 
```
Page 12/45FirstPrevNextLast