This is page 4 of 33. Use http://codebase.md/googleapis/genai-toolbox?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 ├── 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 ├── 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/alloydb/alloydblistinstances/alloydblistinstances_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alloydblistinstances_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" alloydblistinstances "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydblistinstances" ) func TestParseFromYaml(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: list-my-instances: kind: alloydb-list-instances source: my-alloydb-admin-source description: some description `, want: server.ToolConfigs{ "list-my-instances": alloydblistinstances.Config{ Name: "list-my-instances", Kind: "alloydb-list-instances", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{}, }, }, }, { desc: "with auth required", in: ` tools: list-my-instances-auth: kind: alloydb-list-instances source: my-alloydb-admin-source description: some description authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "list-my-instances-auth": alloydblistinstances.Config{ Name: "list-my-instances-auth", Kind: "alloydb-list-instances", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/postgres/postgreslistavailableextensions/postgreslistavailableextensions_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package postgreslistavailableextensions_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" "github.com/googleapis/genai-toolbox/internal/tools/postgres/postgreslistavailableextensions" ) func TestParseFromYamlPostgres(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: postgres-list-available-extensions source: my-pg-instance description: "some description" authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "example_tool": postgreslistavailableextensions.Config{ Name: "example_tool", Kind: "postgres-list-available-extensions", Source: "my-pg-instance", Description: "some description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, }, }, }, { desc: "basic example without authRequired", in: ` tools: example_tool: kind: postgres-list-available-extensions source: my-pg-instance description: "some description" `, want: server.ToolConfigs{ "example_tool": postgreslistavailableextensions.Config{ Name: "example_tool", Kind: "postgres-list-available-extensions", Source: "my-pg-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/postgres/postgreslistinstalledextensions/postgreslistinstalledextensions_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package postgreslistinstalledextensions_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" "github.com/googleapis/genai-toolbox/internal/tools/postgres/postgreslistinstalledextensions" ) func TestParseFromYamlPostgres(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: postgres-list-installed-extensions source: my-pg-instance description: "some description" authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "example_tool": postgreslistinstalledextensions.Config{ Name: "example_tool", Kind: "postgres-list-installed-extensions", Source: "my-pg-instance", Description: "some description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, }, }, }, { desc: "basic example without authRequired", in: ` tools: example_tool: kind: postgres-list-installed-extensions source: my-pg-instance description: "some description" `, want: server.ToolConfigs{ "example_tool": postgreslistinstalledextensions.Config{ Name: "example_tool", Kind: "postgres-list-installed-extensions", Source: "my-pg-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /docs/en/resources/sources/dgraph.md: -------------------------------------------------------------------------------- ```markdown --- title: "Dgraph" type: docs weight: 1 description: > Dgraph is fully open-source, built-for-scale graph database for Gen AI workloads --- ## About [Dgraph][dgraph-docs] is an open-source graph database. It is designed for real-time workloads, horizontal scalability, and data flexibility. Implemented as a distributed system, Dgraph processes queries in parallel to deliver the fastest result. This source can connect to either a self-managed Dgraph cluster or one hosted on Dgraph Cloud. If you're new to Dgraph, the fastest way to get started is to [sign up for Dgraph Cloud][dgraph-login]. [dgraph-docs]: https://dgraph.io/docs [dgraph-login]: https://cloud.dgraph.io/login ## Available Tools - [`dgraph-dql`](../tools/dgraph/dgraph-dql.md) Run DQL (Dgraph Query Language) queries. ## Requirements ### Database User When **connecting to a hosted Dgraph database**, this source uses the API key for access. If you are using a dedicated environment, you will additionally need the namespace and user credentials for that namespace. For **connecting to a local or self-hosted Dgraph database**, use the namespace and user credentials for that namespace. ## Example ```yaml sources: my-dgraph-source: kind: dgraph dgraphUrl: https://xxxx.cloud.dgraph.io user: ${USER_NAME} password: ${PASSWORD} apiKey: ${API_KEY} namespace : 0 ``` {{< notice tip >}} Use environment variable replacement with the format ${ENV_NAME} instead of hardcoding your secrets into the configuration file. {{< /notice >}} ## Reference | **Field** | **Type** | **Required** | **Description** | |-------------|:--------:|:------------:|--------------------------------------------------------------------------------------------------| | kind | string | true | Must be "dgraph". | | dgraphUrl | string | true | Connection URI (e.g. "<https://xxx.cloud.dgraph.io>", "<https://localhost:8080>"). | | user | string | false | Name of the Dgraph user to connect as (e.g., "groot"). | | password | string | false | Password of the Dgraph user (e.g., "password"). | | apiKey | string | false | API key to connect to a Dgraph Cloud instance. | | namespace | uint64 | false | Dgraph namespace (not required for Dgraph Cloud Shared Clusters). | ``` -------------------------------------------------------------------------------- /internal/prebuiltconfigs/prebuiltconfigs.go: -------------------------------------------------------------------------------- ```go // Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package prebuiltconfigs import ( "embed" "fmt" "path" "strings" ) var ( //go:embed tools/*.yaml prebuiltConfigsFS embed.FS // Map of sources to their prebuilt tools prebuiltToolYAMLs map[string][]byte // List of sources with prebuilt tools prebuiltToolsSources []string ) func init() { var err error prebuiltToolYAMLs, prebuiltToolsSources, err = loadPrebuiltToolYAMLs() if err != nil { panic(fmt.Sprintf("Unexpected Error: %v\n", err)) } } // Getter for the prebuiltToolsSources func GetPrebuiltSources() []string { return prebuiltToolsSources } // Get prebuilt tools for a source func Get(prebuiltSourceConfig string) ([]byte, error) { content, ok := prebuiltToolYAMLs[prebuiltSourceConfig] if !ok { prebuiltHelpSuffix := "no prebuilt configurations found." if len(prebuiltToolsSources) > 0 { prebuiltHelpSuffix = fmt.Sprintf("available: %s", strings.Join(prebuiltToolsSources, ", ")) } errMsg := fmt.Errorf("prebuilt source tool for '%s' not found. %s", prebuiltSourceConfig, prebuiltHelpSuffix) return nil, errMsg } return content, nil } // Load all available pre built tools func loadPrebuiltToolYAMLs() (map[string][]byte, []string, error) { toolYAMLs := make(map[string][]byte) var sourceTypes []string entries, err := prebuiltConfigsFS.ReadDir("tools") if err != nil { errMsg := fmt.Errorf("failed to read prebuilt tools %w", err) return nil, nil, errMsg } for _, entry := range entries { lowerName := strings.ToLower(entry.Name()) if !entry.IsDir() && (strings.HasSuffix(lowerName, ".yaml")) { filePathInFS := path.Join("tools", entry.Name()) content, err := prebuiltConfigsFS.ReadFile(filePathInFS) if err != nil { errMsg := fmt.Errorf("failed to read a prebuilt tool %w", err) return nil, nil, errMsg } sourceTypeKey := entry.Name()[:len(entry.Name())-len(".yaml")] sourceTypes = append(sourceTypes, sourceTypeKey) toolYAMLs[sourceTypeKey] = content } } if len(toolYAMLs) == 0 { errMsg := fmt.Errorf("no prebuilt tool configurations were loaded.%w", err) return nil, nil, errMsg } return toolYAMLs, sourceTypes, nil } ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mysql/mysql-list-table-fragmentation.md: -------------------------------------------------------------------------------- ```markdown --- title: "mysql-list-table-fragmentation" type: docs weight: 1 description: > A "mysql-list-table-fragmentation" tool lists top N fragemented tables in MySQL. aliases: - /resources/tools/mysql-list-table-fragmentation --- ## About A `mysql-list-table-fragmentation` tool checks table fragmentation of MySQL tables by calculating the size of the data and index files in bytes and comparing with free space allocated to each table. This tool calculates `fragmentation_percentage` which represents the proportion of free space relative to the total data and index size. It's compatible with - [cloud-sql-mysql](../../sources/cloud-sql-mysql.md) - [mysql](../../sources/mysql.md) `mysql-list-table-fragmentation` outputs detailed information as JSON , ordered by the fragmentation percentage in descending order. This tool takes 4 optional input parameters: - `table_schema` (optional): The database where fragmentation check is to be executed. Check all tables visible to the current user if not specified. - `table_name` (optional): Name of the table to be checked. Check all tables visible to the current user if not specified. - `data_free_threshold_bytes` (optional): Only show tables with at least this much free space in bytes. Default 1. - `limit` (optional): Max rows to return, default 10. ## Example ```yaml tools: list_table_fragmentation: kind: mysql-list-table-fragmentation source: my-mysql-instance description: List table fragmentation in MySQL, by calculating the size of the data and index files and free space allocated to each table. The query calculates fragmentation percentage which represents the proportion of free space relative to the total data and index size. Storage can be reclaimed for tables with high fragmentation using OPTIMIZE TABLE. ``` The response is a json array with the following fields: ```json { "table_schema": "The schema/database this table belongs to", "table_name": "Name of this table", "data_size": "Size of the table data in bytes", "index_size": "Size of the table's indexes in bytes", "data_free": "Free space (bytes) available in the table's data file", "fragmentation_percentage": "How much fragementation this table has", } ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:--------:|:------------:|----------------------------------------------------| | kind | string | true | Must be "mysql-list-table-fragmentation". | | source | string | true | Name of the source the SQL should execute on. | | description | string | true | Description of the tool that is passed to the LLM. | ``` -------------------------------------------------------------------------------- /internal/tools/neo4j/neo4jschema/neo4jschema_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package neo4jschema import ( "testing" "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" ) func TestParseFromYamlNeo4j(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() exp := 30 if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example with default cache expiration", in: ` tools: example_tool: kind: neo4j-schema source: my-neo4j-instance description: some tool description authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "example_tool": Config{ Name: "example_tool", Kind: "neo4j-schema", Source: "my-neo4j-instance", Description: "some tool description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, CacheExpireMinutes: nil, }, }, }, { desc: "cache expire minutes set explicitly", in: ` tools: example_tool: kind: neo4j-schema source: my-neo4j-instance description: some tool description cacheExpireMinutes: 30 `, want: server.ToolConfigs{ "example_tool": Config{ Name: "example_tool", Kind: "neo4j-schema", Source: "my-neo4j-instance", Description: "some tool description", AuthRequired: []string{}, // Expect an empty slice, not nil. CacheExpireMinutes: &exp, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err = yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mongodb/mongodb-delete-many.md: -------------------------------------------------------------------------------- ```markdown --- title: "mongodb-delete-many" type: docs weight: 1 description: > A "mongodb-delete-many" tool deletes all documents from a MongoDB collection that match a filter. aliases: - /resources/tools/mongodb-delete-many --- ## About The `mongodb-delete-many` tool performs a **bulk destructive operation**, deleting **ALL** documents from a collection that match a specified filter. The tool returns the total count of documents that were deleted. If the filter does not match any documents (i.e., the deleted count is 0), the tool will return an error. This tool is compatible with the following source kind: * [`mongodb`](../../sources/mongodb.md) --- ## Example Here is an example that performs a cleanup task by deleting all products from the `inventory` collection that belong to a discontinued brand. ```yaml tools: retire_brand_products: kind: mongodb-delete-many source: my-mongo-source description: Deletes all products from a specified discontinued brand. database: ecommerce collection: inventory filterPayload: | { "brand_name": {{json .brand_to_delete}} } filterParams: - name: brand_to_delete type: string description: The name of the discontinued brand whose products should be deleted. ``` ## Reference | **field** | **type** | **required** | **description** | |:--------------|:---------|:-------------|:--------------------------------------------------------------------------------------------------------------------| | kind | string | true | Must be `mongodb-delete-many`. | | source | string | true | The name of the `mongodb` source to use. | | description | string | true | A description of the tool that is passed to the LLM. | | database | string | true | The name of the MongoDB database containing the collection. | | collection | string | true | The name of the MongoDB collection from which to delete documents. | | filterPayload | string | true | The MongoDB query filter document to select the documents for deletion. Uses `{{json .param_name}}` for templating. | | filterParams | list | true | A list of parameter objects that define the variables used in the `filterPayload`. | ``` -------------------------------------------------------------------------------- /docs/en/getting-started/local_quickstart_js.md: -------------------------------------------------------------------------------- ```markdown --- title: "JS Quickstart (Local)" type: docs weight: 3 description: > How to get started running Toolbox locally with [JavaScript](https://github.com/googleapis/mcp-toolbox-sdk-js), PostgreSQL, and orchestration frameworks such as [LangChain](https://js.langchain.com/docs/introduction/), [GenkitJS](https://genkit.dev/docs/get-started/), [LlamaIndex](https://ts.llamaindex.ai/) and [GoogleGenAI](https://github.com/googleapis/js-genai). --- ## Before you begin This guide assumes you have already done the following: 1. Installed [Node.js (v18 or higher)]. 1. Installed [PostgreSQL 16+ and the `psql` client][install-postgres]. [Node.js (v18 or higher)]: https://nodejs.org/ [install-postgres]: https://www.postgresql.org/download/ ### Cloud Setup (Optional) {{< regionInclude "quickstart/shared/cloud_setup.md" "cloud_setup" >}} ## Step 1: Set up your database {{< regionInclude "quickstart/shared/database_setup.md" "database_setup" >}} ## Step 2: Install and configure Toolbox {{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}} ## Step 3: Connect your agent to Toolbox In this section, we will write and run an agent that will load the Tools from Toolbox. 1. (Optional) Initialize a Node.js project: ```bash npm init -y ``` 1. In a new terminal, install the [SDK](https://www.npmjs.com/package/@toolbox-sdk/core). ```bash npm install @toolbox-sdk/core ``` 1. Install other required dependencies {{< tabpane persist=header >}} {{< tab header="LangChain" lang="bash" >}} npm install langchain @langchain/google-genai {{< /tab >}} {{< tab header="GenkitJS" lang="bash" >}} npm install genkit @genkit-ai/googleai {{< /tab >}} {{< tab header="LlamaIndex" lang="bash" >}} npm install llamaindex @llamaindex/google @llamaindex/workflow {{< /tab >}} {{< tab header="GoogleGenAI" lang="bash" >}} npm install @google/genai {{< /tab >}} {{< /tabpane >}} 1. Create a new file named `hotelAgent.js` and copy the following code to create an agent: {{< tabpane persist=header >}} {{< tab header="LangChain" lang="js" >}} {{< include "quickstart/js/langchain/quickstart.js" >}} {{< /tab >}} {{< tab header="GenkitJS" lang="js" >}} {{< include "quickstart/js/genkit/quickstart.js" >}} {{< /tab >}} {{< tab header="LlamaIndex" lang="js" >}} {{< include "quickstart/js/llamaindex/quickstart.js" >}} {{< /tab >}} {{< tab header="GoogleGenAI" lang="js" >}} {{< include "quickstart/js/genAI/quickstart.js" >}} {{< /tab >}} {{< /tabpane >}} 1. Run your agent, and observe the results: ```sh node hotelAgent.js ``` {{< notice info >}} For more information, visit the [JS SDK repo](https://github.com/googleapis/mcp-toolbox-sdk-js). {{</ notice >}} ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/neo4j/neo4j-execute-cypher.md: -------------------------------------------------------------------------------- ```markdown --- title: "neo4j-execute-cypher" type: docs weight: 1 description: > A "neo4j-execute-cypher" tool executes any arbitrary Cypher statement against a Neo4j database. aliases: - /resources/tools/neo4j-execute-cypher --- ## About A `neo4j-execute-cypher` tool executes an arbitrary Cypher query provided as a string parameter against a Neo4j database. It's designed to be a flexible tool for interacting with the database when a pre-defined query is not sufficient. This tool is compatible with any of the following sources: - [neo4j](../../sources/neo4j.md) For security, the tool can be configured to be read-only. If the `readOnly` flag is set to `true`, the tool will analyze the incoming Cypher query and reject any write operations (like `CREATE`, `MERGE`, `DELETE`, etc.) before execution. The Cypher query uses standard [Neo4j Cypher](https://neo4j.com/docs/cypher-manual/current/queries/) syntax and supports all Cypher features, including pattern matching, filtering, and aggregation. `neo4j-execute-cypher` takes one input parameter `cypher` and run the cypher query against the `source`. > **Note:** This tool is intended for developer assistant workflows with > human-in-the-loop and shouldn't be used for production agents. ## Example ```yaml tools: query_neo4j: kind: neo4j-execute-cypher source: my-neo4j-prod-db readOnly: true description: | Use this tool to execute a Cypher query against the production database. Only read-only queries are allowed. Takes a single 'cypher' parameter containing the full query string. Example: {{ "cypher": "MATCH (m:Movie {title: 'The Matrix'}) RETURN m.released" }} ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:------------------------------------------:|:------------:|-------------------------------------------------------------------------------------------------| | kind | string | true | Must be "neo4j-cypher". | | source | string | true | Name of the source the Cypher query should execute on. | | description | string | true | Description of the tool that is passed to the LLM. | | readOnly | boolean | false | If set to `true`, the tool will reject any write operations in the Cypher query. Default is `false`. | ``` -------------------------------------------------------------------------------- /docs/en/samples/looker/looker_mcp_inspector/_index.md: -------------------------------------------------------------------------------- ```markdown --- title: "Quickstart (MCP with Looker)" type: docs weight: 2 description: > How to get started running Toolbox with MCP Inspector and Looker as the source. --- ## Overview [Model Context Protocol](https://modelcontextprotocol.io) is an open protocol that standardizes how applications provide context to LLMs. Check out this page on how to [connect to Toolbox via MCP](../../how-to/connect_via_mcp.md). ## Step 1: Get a Looker Client ID and Client Secret The Looker Client ID and Client Secret can be obtained from the Users page of your Looker instance. Refer to the documentation [here](https://cloud.google.com/looker/docs/api-auth#authentication_with_an_sdk). You may need to ask an administrator to get the Client ID and Client Secret for you. ## Step 2: Install and configure Toolbox In this section, we will download Toolbox and run the Toolbox server. 1. Download the latest version of Toolbox as a binary: {{< notice tip >}} Select the [correct binary](https://github.com/googleapis/genai-toolbox/releases) corresponding to your OS and CPU architecture. {{< /notice >}} <!-- {x-release-please-start-version} --> ```bash export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, or windows/amd64 curl -O https://storage.googleapis.com/genai-toolbox/v0.17.0/$OS/toolbox ``` <!-- {x-release-please-end} --> 1. Make the binary executable: ```bash chmod +x toolbox ``` 1. Create a file `looker_env` with the settings for your Looker instance. Use the Client ID and Client Secret you obtained earlier. ```bash export LOOKER_BASE_URL=https://looker.example.com export LOOKER_VERIFY_SSL=true export LOOKER_CLIENT_ID=Q7ynZkRkvj9S9FHPm4Wj export LOOKER_CLIENT_SECRET=P5JvZstFnhpkhCYy2yNSfJ6x ``` In some instances you may need to append `:19999` to the LOOKER_BASE_URL. 1. Load the looker_env file into your environment. ```bash source looker_env ``` 1. Run the Toolbox server using the prebuilt Looker tools. ```bash ./toolbox --prebuilt looker ``` ## Step 3: Connect to MCP Inspector 1. Run the MCP Inspector: ```bash npx @modelcontextprotocol/inspector ``` 1. Type `y` when it asks to install the inspector package. 1. It should show the following when the MCP Inspector is up and running: ```bash 🔍 MCP Inspector is up and running at http://127.0.0.1:5173 🚀 ``` 1. Open the above link in your browser. 1. For `Transport Type`, select `SSE`. 1. For `URL`, type in `http://127.0.0.1:5000/mcp/sse`. 1. Click Connect.  1. Select `List Tools`, you will see a list of tools.  1. Test out your tools here! ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mysql/mysql-list-active-queries.md: -------------------------------------------------------------------------------- ```markdown --- title: "mysql-list-active-queries" type: docs weight: 1 description: > A "mysql-list-active-queries" tool lists active queries in a MySQL database. aliases: - /resources/tools/mysql-list-active-queries --- ## About A `mysql-list-active-queries` tool retrieves information about active queries in a MySQL database. It's compatible with: - [cloud-sql-mysql](../../sources/cloud-sql-mysql.md) - [mysql](../../sources/mysql.md) `mysql-list-active-queries` outputs detailed information as JSON for current active queries, ordered by execution time in descending order. This tool takes 2 optional input parameters: - `min_duration_secs` (optional): Only show queries running for at least this long in seconds, default `0`. - `limit` (optional): max number of queries to return, default `10`. ## Example ```yaml tools: list_active_queries: kind: mysql-list-active-queries source: my-mysql-instance description: Lists top N (default 10) ongoing queries from processlist and innodb_trx, ordered by execution time in descending order. Returns detailed information of those queries in json format, including process id, query, transaction duration, transaction wait duration, process time, transaction state, process state, username with host, transaction rows locked, transaction rows modified, and db schema. ``` The response is a json array with the following fields: ```json { "proccess_id": "id of the MySQL process/connection this query belongs to", "query": "query text", "trx_started": "the time when the transaction (this query belongs to) started", "trx_duration_seconds": "the total elapsed time (in seconds) of the owning transaction so far", "trx_wait_duration_seconds": "the total wait time (in seconds) of the owning transaction so far", "query_time": "the time (in seconds) that the owning connection has been in its current state", "trx_state": "the transaction execution state", "proces_state": "the current state of the owning connection", "user": "the user who issued this query", "trx_rows_locked": "the approximate number of rows locked by the owning transaction", "trx_rows_modified": "the approximate number of rows modified by the owning transaction", "db": "the default database for the owning connection" } ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:--------:|:------------:|----------------------------------------------------| | kind | string | true | Must be "mysql-list-active-queries". | | source | string | true | Name of the source the SQL should execute on. | | description | string | true | Description of the tool that is passed to the LLM. | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/looker/looker-health-pulse.md: -------------------------------------------------------------------------------- ```markdown --- title: "looker-health-pulse" type: docs weight: 1 description: > "looker-health-pulse" performs health checks on a Looker instance, with multiple actions available (e.g., checking database connections, dashboard performance, etc). aliases: - /resources/tools/looker-health-pulse --- ## About The `looker-health-pulse` tool performs health checks on a Looker instance. The `action` parameter selects the type of check to perform: - `check_db_connections`: Checks all database connections, runs supported tests, and reports query counts. - `check_dashboard_performance`: Finds dashboards with slow running queries in the last 7 days. - `check_dashboard_errors`: Lists dashboards with erroring queries in the last 7 days. - `check_explore_performance`: Lists the slowest explores in the last 7 days and reports average query runtime. - `check_schedule_failures`: Lists schedules that have failed in the last 7 days. - `check_legacy_features`: Lists enabled legacy features. (*To note, this function is not available in Looker Core. You will get an error running this command with a Core instance configured.*) ## Parameters | **field** | **type** | **required** | **description** | |---------------|:--------:|:------------:|---------------------------------------------| | kind | string | true | Must be "looker-health-pulse" | | source | string | true | Looker source name | | action | string | true | The health check to perform | ## Example ```yaml tools: pulse: kind: looker-health-pulse source: looker-source description: | Pulse Tool Performs health checks on Looker instance. Specify the `action` parameter to select the check. parameters: action: check_dashboard_performance ``` ## Reference | **action** | **description** | |---------------------------|--------------------------------------------------------------------------------| | check_db_connections | Checks all database connections and reports query counts and errors | | check_dashboard_performance | Finds dashboards with slow queries (>30s) in the last 7 days | | check_dashboard_errors | Lists dashboards with erroring queries in the last 7 days | | check_explore_performance | Lists slowest explores and average query runtime | | check_schedule_failures | Lists failed schedules in the last 7 days | | check_legacy_features | Lists enabled legacy features | ``` -------------------------------------------------------------------------------- /internal/sources/sqlite/sqlite.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sqlite import ( "context" "database/sql" "fmt" "github.com/goccy/go-yaml" "github.com/googleapis/genai-toolbox/internal/sources" "go.opentelemetry.io/otel/trace" _ "modernc.org/sqlite" // Pure Go SQLite driver ) const SourceKind string = "sqlite" // validate interface var _ sources.SourceConfig = Config{} func init() { if !sources.Register(SourceKind, newConfig) { panic(fmt.Sprintf("source kind %q already registered", SourceKind)) } } func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { actual := Config{Name: name} if err := decoder.DecodeContext(ctx, &actual); err != nil { return nil, err } return actual, nil } type Config struct { Name string `yaml:"name" validate:"required"` Kind string `yaml:"kind" validate:"required"` Database string `yaml:"database" validate:"required"` // Path to SQLite database file } func (r Config) SourceConfigKind() string { return SourceKind } func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { db, err := initSQLiteConnection(ctx, tracer, r.Name, r.Database) if err != nil { return nil, fmt.Errorf("unable to create db connection: %w", err) } err = db.PingContext(context.Background()) if err != nil { return nil, fmt.Errorf("unable to connect successfully: %w", err) } s := &Source{ Name: r.Name, Kind: SourceKind, Db: db, } return s, nil } var _ sources.Source = &Source{} type Source struct { Name string `yaml:"name"` Kind string `yaml:"kind"` Db *sql.DB } func (s *Source) SourceKind() string { return SourceKind } func (s *Source) SQLiteDB() *sql.DB { return s.Db } func initSQLiteConnection(ctx context.Context, tracer trace.Tracer, name, dbPath string) (*sql.DB, error) { //nolint:all // Reassigned ctx ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) defer span.End() // Open database connection db, err := sql.Open("sqlite", dbPath) if err != nil { return nil, fmt.Errorf("sql.Open: %w", err) } // Set some reasonable defaults for SQLite db.SetMaxOpenConns(1) // SQLite only supports one writer at a time db.SetMaxIdleConns(1) return db, nil } ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/postgres/postgres-list-active-queries.md: -------------------------------------------------------------------------------- ```markdown --- title: "postgres-list-active-queries" type: docs weight: 1 description: > The "postgres-list-active-queries" tool lists currently active queries in a Postgres database. aliases: - /resources/tools/postgres-list-active-queries --- ## About The `postgres-list-active-queries` tool retrieves information about currently active queries in a Postgres database. It's compatible with any of the following sources: - [alloydb-postgres](../../sources/alloydb-pg.md) - [cloud-sql-postgres](../../sources/cloud-sql-pg.md) - [postgres](../../sources/postgres.md) `postgres-list-active-queries` lists detailed information as JSON for currently active queries. The tool takes the following input parameters: - `min_duraton` (optional): Only show queries running at least this long (e.g., '1 minute', '1 second', '2 seconds'). Default: '1 minute'. - `exclude_application_names` (optional): A comma-separated list of application names to exclude from the query results. This is useful for filtering out queries from specific applications (e.g., 'psql', 'pgAdmin', 'DBeaver'). The match is case-sensitive. Whitespace around commas and names is automatically handled. If this parameter is omitted, no applications are excluded. - `limit` (optional): The maximum number of rows to return. Default: `50`. ## Example ```yaml tools: list_active_queries: kind: postgres-list-active-queries source: postgres-source description: List the top N (default 50) currently running queries (state='active') from pg_stat_activity, ordered by longest-running first. Returns pid, user, database, application_name, client_addr, state, wait_event_type/wait_event, backend/xact/query start times, computed query_duration, and the SQL text. ``` The response is a json array with the following elements: ```json { "pid": "process id", "user": "database user name", "datname": "database name", "application_name": "connecting application name", "client_addr": "connecting client ip address", "state": "connection state", "wait_event_type": "connection wait event type", "wait_event": "connection wait event", "backend_start": "connection start time", "xact_start": "transaction start time", "query_start": "query start time", "query_duration": "query duration", "query": "query text" } ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:--------:|:------------:|----------------------------------------------------| | kind | string | true | Must be "postgres-list-active-queries". | | source | string | true | Name of the source the SQL should execute on. | | description | string | true | Description of the tool that is passed to the LLM. | ``` -------------------------------------------------------------------------------- /internal/sources/dataplex/dataplex_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dataplex_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/sources/dataplex" "github.com/googleapis/genai-toolbox/internal/testutils" ) func TestParseFromYamlDataplex(t *testing.T) { tcs := []struct { desc string in string want server.SourceConfigs }{ { desc: "basic example", in: ` sources: my-instance: kind: dataplex project: my-project `, want: server.SourceConfigs{ "my-instance": dataplex.Config{ Name: "my-instance", Kind: dataplex.SourceKind, Project: "my-project", }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Sources server.SourceConfigs `yaml:"sources"` }{} // Parse contents err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if !cmp.Equal(tc.want, got.Sources) { t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources) } }) } } func TestFailParseFromYaml(t *testing.T) { tcs := []struct { desc string in string err string }{ { desc: "extra field", in: ` sources: my-instance: kind: dataplex project: my-project foo: bar `, err: "unable to parse source \"my-instance\" as \"dataplex\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | kind: dataplex\n 3 | project: my-project", }, { desc: "missing required field", in: ` sources: my-instance: kind: dataplex `, err: "unable to parse source \"my-instance\" as \"dataplex\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag", }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Sources server.SourceConfigs `yaml:"sources"` }{} // Parse contents err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) if err == nil { t.Fatalf("expect parsing to fail") } errStr := err.Error() if errStr != tc.err { t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err) } }) } } ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/clickhouse/clickhouse-sql.md: -------------------------------------------------------------------------------- ```markdown --- title: "clickhouse-sql" type: docs weight: 2 description: > A "clickhouse-sql" tool executes SQL queries as prepared statements in ClickHouse. aliases: - /resources/tools/clickhouse-sql --- ## About A `clickhouse-sql` tool executes SQL queries as prepared statements against a ClickHouse database. It's compatible with the [clickhouse](../../sources/clickhouse.md) source. This tool supports both template parameters (for SQL statement customization) and regular parameters (for prepared statement values), providing flexible query execution capabilities. ## Example ```yaml tools: my_analytics_query: kind: clickhouse-sql source: my-clickhouse-instance description: Get user analytics for a specific date range statement: | SELECT user_id, count(*) as event_count, max(timestamp) as last_event FROM events WHERE date >= ? AND date <= ? GROUP BY user_id ORDER BY event_count DESC LIMIT ? parameters: - name: start_date description: Start date for the query (YYYY-MM-DD format) - name: end_date description: End date for the query (YYYY-MM-DD format) - name: limit description: Maximum number of results to return ``` ## Template Parameters Example ```yaml tools: flexible_table_query: kind: clickhouse-sql source: my-clickhouse-instance description: Query any table with flexible columns statement: | SELECT {{columns}} FROM {{table_name}} WHERE created_date >= ? LIMIT ? templateParameters: - name: columns description: Comma-separated list of columns to select - name: table_name description: Name of the table to query parameters: - name: start_date description: Start date filter - name: limit description: Maximum number of results ``` ## Reference | **field** | **type** | **required** | **description** | |--------------------|:------------------:|:------------:|-------------------------------------------------------| | kind | string | true | Must be "clickhouse-sql". | | source | string | true | Name of the ClickHouse source to execute SQL against. | | description | string | true | Description of the tool that is passed to the LLM. | | statement | string | true | The SQL statement template to execute. | | parameters | array of Parameter | false | Parameters for prepared statement values. | | templateParameters | array of Parameter | false | Parameters for SQL statement template customization. | ``` -------------------------------------------------------------------------------- /internal/tools/alloydb/alloydbcreateuser/alloydbcreateuser_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alloydbcreateuser_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" alloydbcreateuser "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateuser" ) func TestParseFromYaml(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: create-my-user: kind: alloydb-create-user source: my-alloydb-admin-source description: some description `, want: server.ToolConfigs{ "create-my-user": alloydbcreateuser.Config{ Name: "create-my-user", Kind: "alloydb-create-user", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{}, }, }, }, { desc: "with auth required", in: ` tools: create-my-user-auth: kind: alloydb-create-user source: my-alloydb-admin-source description: some description authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "create-my-user-auth": alloydbcreateuser.Config{ Name: "create-my-user-auth", Kind: "alloydb-create-user", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/dgraph/dgraph_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dgraph_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" "github.com/googleapis/genai-toolbox/internal/tools/dgraph" ) func TestParseFromYamlDgraph(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic query example", in: ` tools: example_tool: kind: dgraph-dql source: my-dgraph-instance description: some tool description isQuery: true timeout: 20s statement: | query {q(func: eq(email, "[email protected]")) {email}} `, want: server.ToolConfigs{ "example_tool": dgraph.Config{ Name: "example_tool", Kind: "dgraph-dql", Source: "my-dgraph-instance", AuthRequired: []string{}, Description: "some tool description", IsQuery: true, Timeout: "20s", Statement: "query {q(func: eq(email, \"[email protected]\")) {email}}\n", }, }, }, { desc: "basic mutation example", in: ` tools: example_tool: kind: dgraph-dql source: my-dgraph-instance description: some tool description statement: | mutation {set { _:a <name> "[email protected]" . _:b <email> "[email protected]" .}} `, want: server.ToolConfigs{ "example_tool": dgraph.Config{ Name: "example_tool", Kind: "dgraph-dql", Source: "my-dgraph-instance", Description: "some tool description", AuthRequired: []string{}, Statement: "mutation {set { _:a <name> \"[email protected]\" . _:b <email> \"[email protected]\" .}}\n", }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /docs/en/resources/sources/trino.md: -------------------------------------------------------------------------------- ```markdown --- title: "Trino" type: docs weight: 1 description: > Trino is a distributed SQL query engine for big data analytics. --- ## About [Trino][trino-docs] is a distributed SQL query engine designed for fast analytic queries against data of any size. It allows you to query data where it lives, including Hive, Cassandra, relational databases or even proprietary data stores. [trino-docs]: https://trino.io/docs/ ## Available Tools - [`trino-sql`](../tools/trino/trino-sql.md) Execute parameterized SQL queries against Trino. - [`trino-execute-sql`](../tools/trino/trino-execute-sql.md) Execute arbitrary SQL queries against Trino. ## Requirements ### Trino Cluster You need access to a running Trino cluster with appropriate user permissions for the catalogs and schemas you want to query. ## Example ```yaml sources: my-trino-source: kind: trino host: trino.example.com port: "8080" user: ${TRINO_USER} # Optional for anonymous access password: ${TRINO_PASSWORD} # Optional catalog: hive schema: default ``` {{< notice tip >}} Use environment variable replacement with the format ${ENV_NAME} instead of hardcoding your secrets into the configuration file. {{< /notice >}} ## Reference | **field** | **type** | **required** | **description** | |-----------------|:--------:|:------------:|------------------------------------------------------------------------------| | kind | string | true | Must be "trino". | | host | string | true | Trino coordinator hostname (e.g. "trino.example.com") | | port | string | true | Trino coordinator port (e.g. "8080", "8443") | | user | string | false | Username for authentication (e.g. "analyst"). Optional for anonymous access. | | password | string | false | Password for basic authentication | | catalog | string | true | Default catalog to use for queries (e.g. "hive") | | schema | string | true | Default schema to use for queries (e.g. "default") | | queryTimeout | string | false | Query timeout duration (e.g. "30m", "1h") | | accessToken | string | false | JWT access token for authentication | | kerberosEnabled | boolean | false | Enable Kerberos authentication (default: false) | | sslEnabled | boolean | false | Enable SSL/TLS (default: false) | ``` -------------------------------------------------------------------------------- /internal/tools/alloydb/alloydbcreatecluster/alloydbcreatecluster_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alloydbcreatecluster_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" alloydbcreatecluster "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreatecluster" ) func TestParseFromYaml(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: create-my-cluster: kind: alloydb-create-cluster source: my-alloydb-admin-source description: some description `, want: server.ToolConfigs{ "create-my-cluster": alloydbcreatecluster.Config{ Name: "create-my-cluster", Kind: "alloydb-create-cluster", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{}, }, }, }, { desc: "with auth required", in: ` tools: create-my-cluster-auth: kind: alloydb-create-cluster source: my-alloydb-admin-source description: some description authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "create-my-cluster-auth": alloydbcreatecluster.Config{ Name: "create-my-cluster-auth", Kind: "alloydb-create-cluster", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /docs/en/how-to/deploy_docker.md: -------------------------------------------------------------------------------- ```markdown --- title: "Deploy using Docker Compose" type: docs weight: 4 description: > How to deploy Toolbox using Docker Compose. --- <!-- Contributor: Sujith R Pillai <[email protected]> --> ## Before you begin 1. [Install Docker Compose.](https://docs.docker.com/compose/install/) ## Configure `tools.yaml` file Create a `tools.yaml` file that contains your configuration for Toolbox. For details, see the [configuration](https://github.com/googleapis/genai-toolbox/blob/main/README.md#configuration) section. ## Deploy using Docker Compose 1. Create a `docker-compose.yml` file, customizing as needed: ```yaml services: toolbox: # TODO: It is recommended to pin to a specific image version instead of latest. image: us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest hostname: toolbox platform: linux/amd64 ports: - "5000:5000" volumes: - ./config:/config command: [ "toolbox", "--tools-file", "/config/tools.yaml", "--address", "0.0.0.0"] depends_on: db: condition: service_healthy networks: - tool-network db: # TODO: It is recommended to pin to a specific image version instead of latest. image: postgres hostname: db environment: POSTGRES_USER: toolbox_user POSTGRES_PASSWORD: my-password POSTGRES_DB: toolbox_db ports: - "5432:5432" volumes: - ./db:/var/lib/postgresql/data # This file can be used to bootstrap your schema if needed. # See "initialization scripts" on https://hub.docker.com/_/postgres/ for more info - ./config/init.sql:/docker-entrypoint-initdb.d/init.sql healthcheck: test: ["CMD-SHELL", "pg_isready -U toolbox_user -d toolbox_db"] interval: 10s timeout: 5s retries: 5 networks: - tool-network networks: tool-network: ``` 1. Run the following command to bring up the Toolbox and Postgres instance ```bash docker-compose up -d ``` {{< notice tip >}} You can use this setup to quickly set up Toolbox + Postgres to follow along in our [Quickstart](../getting-started/local_quickstart.md) {{< /notice >}} ## Connecting with Toolbox Client SDK Next, we will use Toolbox with the Client SDKs: 1. The url for the Toolbox server running using docker-compose will be: ``` http://localhost:5000 ``` 1. Import and initialize the client with the URL: {{< tabpane persist=header >}} {{< tab header="LangChain" lang="Python" >}} from toolbox_langchain import ToolboxClient # Replace with the cloud run service URL generated above async with ToolboxClient("http://$YOUR_URL") as toolbox: {{< /tab >}} {{< tab header="Llamaindex" lang="Python" >}} from toolbox_llamaindex import ToolboxClient # Replace with the cloud run service URL generated above async with ToolboxClient("http://$YOUR_URL") as toolbox: {{< /tab >}} {{< /tabpane >}} ``` -------------------------------------------------------------------------------- /internal/sources/mongodb/mongodb_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mongodb_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/sources/mongodb" "github.com/googleapis/genai-toolbox/internal/testutils" ) func TestParseFromYamlMongoDB(t *testing.T) { tcs := []struct { desc string in string want server.SourceConfigs }{ { desc: "basic example", in: ` sources: mongo-db: kind: "mongodb" uri: "mongodb+srv://username:password@host/dbname" `, want: server.SourceConfigs{ "mongo-db": mongodb.Config{ Name: "mongo-db", Kind: mongodb.SourceKind, Uri: "mongodb+srv://username:password@host/dbname", }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Sources server.SourceConfigs `yaml:"sources"` }{} // Parse contents err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if !cmp.Equal(tc.want, got.Sources) { t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources) } }) } } func TestFailParseFromYaml(t *testing.T) { tcs := []struct { desc string in string err string }{ { desc: "extra field", in: ` sources: mongo-db: kind: mongodb uri: "mongodb+srv://username:password@host/dbname" foo: bar `, err: "unable to parse source \"mongo-db\" as \"mongodb\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | kind: mongodb\n 3 | uri: mongodb+srv://username:password@host/dbname", }, { desc: "missing required field", in: ` sources: mongo-db: kind: mongodb `, err: "unable to parse source \"mongo-db\" as \"mongodb\": Key: 'Config.Uri' Error:Field validation for 'Uri' failed on the 'required' tag", }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Sources server.SourceConfigs `yaml:"sources"` }{} // Parse contents err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) if err == nil { t.Fatalf("expect parsing to fail") } errStr := err.Error() if errStr != tc.err { t.Fatalf("unexpected error: got \n%q, want \n%q", errStr, tc.err) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/alloydb/alloydbcreateinstance/alloydbcreateinstance_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package alloydbcreateinstance_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" alloydbcreateinstance "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateinstance" ) func TestParseFromYaml(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: create-my-instance: kind: alloydb-create-instance source: my-alloydb-admin-source description: some description `, want: server.ToolConfigs{ "create-my-instance": alloydbcreateinstance.Config{ Name: "create-my-instance", Kind: "alloydb-create-instance", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{}, }, }, }, { desc: "with auth required", in: ` tools: create-my-instance-auth: kind: alloydb-create-instance source: my-alloydb-admin-source description: some description authRequired: - my-google-auth-service - other-auth-service `, want: server.ToolConfigs{ "create-my-instance-auth": alloydbcreateinstance.Config{ Name: "create-my-instance-auth", Kind: "alloydb-create-instance", Source: "my-alloydb-admin-source", Description: "some description", AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/clickhouse/clickhouselistdatabases/clickhouselistdatabases_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package clickhouse import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/testutils" "github.com/googleapis/genai-toolbox/internal/tools" ) func TestListDatabasesConfigToolConfigKind(t *testing.T) { cfg := Config{} if cfg.ToolConfigKind() != listDatabasesKind { t.Errorf("expected %q, got %q", listDatabasesKind, cfg.ToolConfigKind()) } } func TestListDatabasesConfigInitializeMissingSource(t *testing.T) { cfg := Config{ Name: "test-list-databases", Kind: listDatabasesKind, Source: "missing-source", Description: "Test list databases tool", } srcs := map[string]sources.Source{} _, err := cfg.Initialize(srcs) if err == nil { t.Error("expected error for missing source") } } func TestParseFromYamlClickHouseListDatabases(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: clickhouse-list-databases source: my-instance description: some description `, want: server.ToolConfigs{ "example_tool": Config{ Name: "example_tool", Kind: "clickhouse-list-databases", Source: "my-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } func TestListDatabasesToolParseParams(t *testing.T) { tool := Tool{ Parameters: tools.Parameters{}, } params, err := tool.ParseParams(map[string]any{}, map[string]map[string]any{}) if err != nil { t.Errorf("unexpected error: %v", err) } if len(params) != 0 { t.Errorf("expected 0 parameters, got %d", len(params)) } } ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/cloudsql/cloudsqlmysqlcreateinstance.md: -------------------------------------------------------------------------------- ```markdown --- title: cloud-sql-mysql-create-instance type: docs weight: 2 description: "Create a Cloud SQL for MySQL instance." --- The `cloud-sql-mysql-create-instance` tool creates a new Cloud SQL for MySQL instance in a specified Google Cloud project. {{< notice info >}} This tool uses the `cloud-sql-admin` source. {{< /notice >}} ## Configuration Here is an example of how to configure the `cloud-sql-mysql-create-instance` tool in your `tools.yaml` file: ```yaml sources: my-cloud-sql-admin-source: kind: cloud-sql-admin tools: create_my_mysql_instance: kind: cloud-sql-mysql-create-instance source: my-cloud-sql-admin-source description: "Creates a MySQL instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 16 GiB RAM, 100 GiB SSD configuration with Non-HA/zonal availability. For the `Production` template, it chooses an 8 vCPU, 64 GiB RAM, 250 GiB SSD configuration with HA/regional availability. The Enterprise Plus edition is used in both cases. The default database version is `MYSQL_8_4`. The agent should ask the user if they want to use a different version." ``` ## Parameters The `cloud-sql-mysql-create-instance` tool has the following parameters: | **field** | **type** | **required** | **description** | | --------------- | :------: | :----------: | --------------------------------------------------------------------------------------------------------------- | | project | string | true | The Google Cloud project ID. | | name | string | true | The name of the instance to create. | | databaseVersion | string | false | The database version for MySQL. If not specified, defaults to the latest available version (e.g., `MYSQL_8_4`). | | rootPassword | string | true | The root password for the instance. | | editionPreset | string | false | The edition of the instance. Can be `Production` or `Development`. Defaults to `Development`. | ## Reference | **field** | **type** | **required** | **description** | | ----------- | :------: | :----------: | -------------------------------------------------------------- | | kind | string | true | Must be `cloud-sql-mysql-create-instance`. | | source | string | true | The name of the `cloud-sql-admin` source to use for this tool. | | description | string | false | A description of the tool that is passed to the agent. | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/cloudsql/cloudsqlpgcreateinstances.md: -------------------------------------------------------------------------------- ```markdown --- title: cloud-sql-postgres-create-instance type: docs weight: 10 description: Create a Cloud SQL for PostgreSQL instance. --- The `cloud-sql-postgres-create-instance` tool creates a Cloud SQL for PostgreSQL instance using the Cloud SQL Admin API. {{< notice info >}} This tool uses a `source` of kind `cloud-sql-admin`. {{< /notice >}} ## Example ```yaml tools: create-sql-instance: kind: cloud-sql-postgres-create-instance source: cloud-sql-admin-source description: "Creates a Postgres instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 16 GiB RAM, 100 GiB SSD configuration with Non-HA/zonal availability. For the `Production` template, it chooses an 8 vCPU, 64 GiB RAM, 250 GiB SSD configuration with HA/regional availability. The Enterprise Plus edition is used in both cases. The default database version is `POSTGRES_17`. The agent should ask the user if they want to use a different version." ``` ## Reference ### Tool Configuration | **field** | **type** | **required** | **description** | | ----------- | :------: | :----------: | ------------------------------------------------ | | kind | string | true | Must be "cloud-sql-postgres-create-instance". | | source | string | true | The name of the `cloud-sql-admin` source to use. | | description | string | false | A description of the tool. | ### Tool Inputs | **parameter** | **type** | **required** | **description** | |-----------------|:--------:|:------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------| | project | string | true | The project ID. | | name | string | true | The name of the instance. | | databaseVersion | string | false | The database version for Postgres. If not specified, defaults to the latest available version (e.g., POSTGRES_17). | | rootPassword | string | true | The root password for the instance. | | editionPreset | string | false | The edition of the instance. Can be `Production` or `Development`. This determines the default machine type and availability. Defaults to `Development`. | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mongodb/mongodb-delete-one.md: -------------------------------------------------------------------------------- ```markdown --- title: "mongodb-delete-one" type: docs weight: 1 description: > A "mongodb-delete-one" tool deletes a single document from a MongoDB collection. aliases: - /resources/tools/mongodb-delete-one --- ## About The `mongodb-delete-one` tool performs a destructive operation, deleting the **first single document** that matches a specified filter from a MongoDB collection. If the filter matches multiple documents, only the first one found by the database will be deleted. This tool is useful for removing specific entries, such as a user account or a single item from an inventory based on a unique ID. The tool returns the number of documents deleted, which will be either `1` if a document was found and deleted, or `0` if no matching document was found. This tool is compatible with the following source kind: * [`mongodb`](../../sources/mongodb.md) --- ## Example Here is an example that deletes a specific user account from the `users` collection by matching their unique email address. This is a permanent action. ```yaml tools: delete_user_account: kind: mongodb-delete-one source: my-mongo-source description: Permanently deletes a user account by their email address. database: user_data collection: users filterPayload: | { "email": {{json .email_address}} } filterParams: - name: email_address type: string description: The email of the user account to delete. ``` ## Reference | **field** | **type** | **required** | **description** | |:--------------|:---------|:-------------|:-------------------------------------------------------------------------------------------------------------------| | kind | string | true | Must be `mongodb-delete-one`. | | source | string | true | The name of the `mongodb` source to use. | | description | string | true | A description of the tool that is passed to the LLM. | | database | string | true | The name of the MongoDB database containing the collection. | | collection | string | true | The name of the MongoDB collection from which to delete a document. | | filterPayload | string | true | The MongoDB query filter document to select the document for deletion. Uses `{{json .param_name}}` for templating. | | filterParams | list | true | A list of parameter objects that define the variables used in the `filterPayload`. | ``` -------------------------------------------------------------------------------- /internal/sources/bigtable/bigtable.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package bigtable import ( "context" "fmt" "cloud.google.com/go/bigtable" "github.com/goccy/go-yaml" "github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/util" "go.opentelemetry.io/otel/trace" "google.golang.org/api/option" ) const SourceKind string = "bigtable" // validate interface var _ sources.SourceConfig = Config{} func init() { if !sources.Register(SourceKind, newConfig) { panic(fmt.Sprintf("source kind %q already registered", SourceKind)) } } func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { actual := Config{Name: name} if err := decoder.DecodeContext(ctx, &actual); err != nil { return nil, err } return actual, nil } type Config struct { Name string `yaml:"name" validate:"required"` Kind string `yaml:"kind" validate:"required"` Project string `yaml:"project" validate:"required"` Instance string `yaml:"instance" validate:"required"` } func (r Config) SourceConfigKind() string { return SourceKind } func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { client, err := initBigtableClient(ctx, tracer, r.Name, r.Project, r.Instance) if err != nil { return nil, fmt.Errorf("unable to create client: %w", err) } s := &Source{ Name: r.Name, Kind: SourceKind, Client: client, } return s, nil } var _ sources.Source = &Source{} type Source struct { Name string `yaml:"name"` Kind string `yaml:"kind"` Client *bigtable.Client } func (s *Source) SourceKind() string { return SourceKind } func (s *Source) BigtableClient() *bigtable.Client { return s.Client } func initBigtableClient(ctx context.Context, tracer trace.Tracer, name, project, instance string) (*bigtable.Client, error) { //nolint:all // Reassigned ctx ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) defer span.End() // Set up Bigtable data operations client. poolSize := 10 userAgent, err := util.UserAgentFromContext(ctx) if err != nil { return nil, err } client, err := bigtable.NewClient(ctx, project, instance, option.WithUserAgent(userAgent), option.WithGRPCConnectionPool(poolSize)) if err != nil { return nil, fmt.Errorf("unable to create bigtable.NewClient: %w", err) } return client, nil } ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/cloudsql/cloudsqlmssqlcreateinstance.md: -------------------------------------------------------------------------------- ```markdown --- title: cloud-sql-mssql-create-instance type: docs weight: 10 description: "Create a Cloud SQL for SQL Server instance." --- The `cloud-sql-mssql-create-instance` tool creates a Cloud SQL for SQL Server instance using the Cloud SQL Admin API. {{< notice info dd>}} This tool uses a `source` of kind `cloud-sql-admin`. {{< /notice >}} ## Example ```yaml tools: create-sql-instance: kind: cloud-sql-mssql-create-instance source: cloud-sql-admin-source description: "Creates a SQL Server instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 8 GiB RAM (`db-custom-2-8192`) configuration with Non-HA/zonal availability. For the `Production` template, it chooses a 4 vCPU, 26 GiB RAM (`db-custom-4-26624`) configuration with HA/regional availability. The Enterprise edition is used in both cases. The default database version is `SQLSERVER_2022_STANDARD`. The agent should ask the user if they want to use a different version." ``` ## Reference ### Tool Configuration | **field** | **type** | **required** | **description** | | ----------- | :------: | :----------: | ------------------------------------------------ | | kind | string | true | Must be "cloud-sql-mssql-create-instance". | | source | string | true | The name of the `cloud-sql-admin` source to use. | | description | string | false | A description of the tool. | ### Tool Inputs | **parameter** | **type** | **required** | **description** | |-----------------|:--------:|:------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------| | project | string | true | The project ID. | | name | string | true | The name of the instance. | | databaseVersion | string | false | The database version for SQL Server. If not specified, defaults to the latest available version (e.g., SQLSERVER_2022_STANDARD). | | rootPassword | string | true | The root password for the instance. | | editionPreset | string | false | The edition of the instance. Can be `Production` or `Development`. This determines the default machine type and availability. Defaults to `Development`. | ``` -------------------------------------------------------------------------------- /.ci/quickstart_test/run_py_tests.sh: -------------------------------------------------------------------------------- ```bash # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #!/bin/bash set -e TABLE_NAME="hotels_python" QUICKSTART_PYTHON_DIR="docs/en/getting-started/quickstart/python" SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql" PROXY_PID="" TOOLBOX_PID="" install_system_packages() { apt-get update && apt-get install -y \ postgresql-client \ python3-venv \ wget \ gettext-base \ netcat-openbsd } start_cloud_sql_proxy() { wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy chmod +x /usr/local/bin/cloud-sql-proxy cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" & PROXY_PID=$! for i in {1..30}; do if nc -z 127.0.0.1 5432; then echo "Cloud SQL Proxy is up and running." return fi sleep 1 done echo "Cloud SQL Proxy failed to start within the timeout period." exit 1 } setup_toolbox() { TOOLBOX_YAML="/tools.yaml" echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML" if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox" chmod +x "/toolbox" /toolbox --tools-file "$TOOLBOX_YAML" & TOOLBOX_PID=$! sleep 2 } setup_orch_table() { export TABLE_NAME envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME" } run_orch_test() { local orch_dir="$1" local orch_name orch_name=$(basename "$orch_dir") ( set -e setup_orch_table cd "$orch_dir" local VENV_DIR=".venv" python3 -m venv "$VENV_DIR" source "$VENV_DIR/bin/activate" pip install -r requirements.txt echo "--- Running tests for $orch_name ---" cd .. ORCH_NAME="$orch_name" pytest rm -rf "$VENV_DIR" ) } cleanup_all() { echo "--- Final cleanup: Shutting down processes and dropping table ---" if [ -n "$TOOLBOX_PID" ]; then kill $TOOLBOX_PID || true fi if [ -n "$PROXY_PID" ]; then kill $PROXY_PID || true fi } trap cleanup_all EXIT # Main script execution install_system_packages start_cloud_sql_proxy export PGHOST=127.0.0.1 export PGPORT=5432 export PGPASSWORD="$DB_PASSWORD" export GOOGLE_API_KEY="$GOOGLE_API_KEY" setup_toolbox for ORCH_DIR in "$QUICKSTART_PYTHON_DIR"/*/; do if [ ! -d "$ORCH_DIR" ]; then continue fi run_orch_test "$ORCH_DIR" done ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/bigquery/bigquery-conversational-analytics.md: -------------------------------------------------------------------------------- ```markdown --- title: "bigquery-conversational-analytics" type: docs weight: 1 description: > A "bigquery-conversational-analytics" tool allows conversational interaction with a BigQuery source. aliases: - /resources/tools/bigquery-conversational-analytics --- ## About A `bigquery-conversational-analytics` tool allows you to ask questions about your data in natural language. This function takes a user's question (which can include conversational history for context) and references to specific BigQuery tables, and sends them to a stateless conversational API. The API uses a GenAI agent to understand the question, generate and execute SQL queries and Python code, and formulate an answer. This function returns a detailed, sequential log of this entire process, which includes any generated SQL or Python code, the data retrieved, and the final text answer. **Note**: This tool requires additional setup in your project. Please refer to the official [Conversational Analytics API documentation](https://cloud.google.com/gemini/docs/conversational-analytics-api/overview) for instructions. It's compatible with the following sources: - [bigquery](../../sources/bigquery.md) `bigquery-conversational-analytics` accepts the following parameters: - **`user_query_with_context`:** The user's question, potentially including conversation history and system instructions for context. - **`table_references`:** A JSON string of a list of BigQuery tables to use as context. Each object in the list must contain `projectId`, `datasetId`, and `tableId`. Example: `'[{"projectId": "my-gcp-project", "datasetId": "my_dataset", "tableId": "my_table"}]'` The tool's behavior regarding these parameters is influenced by the `allowedDatasets` restriction on the `bigquery` source: - **Without `allowedDatasets` restriction:** The tool can use tables from any dataset specified in the `table_references` parameter. - **With `allowedDatasets` restriction:** Before processing the request, the tool verifies that every table in `table_references` belongs to a dataset in the allowed list. If any table is from a dataset that is not in the list, the request is denied. ## Example ```yaml tools: ask_data_insights: kind: bigquery-conversational-analytics source: my-bigquery-source description: | Use this tool to perform data analysis, get insights, or answer complex questions about the contents of specific BigQuery tables. ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:--------:|:------------:|----------------------------------------------------| | kind | string | true | Must be "bigquery-conversational-analytics". | | source | string | true | Name of the source for chat. | | description | string | true | Description of the tool that is passed to the LLM. | ``` -------------------------------------------------------------------------------- /internal/testutils/testutils.go: -------------------------------------------------------------------------------- ```go // Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package testutils import ( "bufio" "context" "fmt" "io" "os" "regexp" "strings" "github.com/googleapis/genai-toolbox/internal/log" "github.com/googleapis/genai-toolbox/internal/util" ) // formatYaml is a utility function for stripping out tabs in multiline strings func FormatYaml(in string) []byte { // removes any leading indentation(tabs) in = strings.ReplaceAll(in, "\n\t", "\n ") // converts remaining indentation in = strings.ReplaceAll(in, "\t", " ") return []byte(in) } // ContextWithNewLogger create a new context with new logger func ContextWithNewLogger() (context.Context, error) { ctx := context.Background() logger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info") if err != nil { return nil, fmt.Errorf("unable to create logger: %s", err) } return util.WithLogger(ctx, logger), nil } // WaitForString waits until the server logs a single line that matches the provided regex. // returns the output of whatever the server sent so far. func WaitForString(ctx context.Context, re *regexp.Regexp, pr io.ReadCloser) (string, error) { in := bufio.NewReader(pr) ctx, cancel := context.WithCancel(ctx) defer cancel() // read lines in background, sending result of each read over a channel // this allows us to use in.ReadString without blocking type result struct { s string err error } output := make(chan result) go func() { defer close(output) for { select { case <-ctx.Done(): // if the context is canceled, the orig thread will send back the error // so we can just exit the goroutine here return default: // otherwise read a line from the output s, err := in.ReadString('\n') if err != nil { output <- result{err: err} return } output <- result{s: s} // if that last string matched, exit the goroutine if re.MatchString(s) { return } } } }() // collect the output until the ctx is canceled, an error was hit, // or match was found (which is indicated the channel is closed) var sb strings.Builder for { select { case <-ctx.Done(): // if ctx is done, return that error return sb.String(), ctx.Err() case o, ok := <-output: if !ok { // match was found! return sb.String(), nil } if o.err != nil { // error was found! return sb.String(), o.err } sb.WriteString(o.s) } } } ``` -------------------------------------------------------------------------------- /internal/sources/mongodb/mongodb.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mongodb import ( "context" "fmt" "github.com/goccy/go-yaml" "github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/util" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.opentelemetry.io/otel/trace" ) const SourceKind string = "mongodb" // validate interface var _ sources.SourceConfig = Config{} func init() { if !sources.Register(SourceKind, newConfig) { panic(fmt.Sprintf("source kind %q already registered", SourceKind)) } } func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { actual := Config{Name: name} if err := decoder.DecodeContext(ctx, &actual); err != nil { return nil, err } return actual, nil } type Config struct { Name string `yaml:"name" validate:"required"` Kind string `yaml:"kind" validate:"required"` Uri string `yaml:"uri" validate:"required"` // MongoDB Atlas connection URI } func (r Config) SourceConfigKind() string { return SourceKind } func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { client, err := initMongoDBClient(ctx, tracer, r.Name, r.Uri) if err != nil { return nil, fmt.Errorf("unable to create MongoDB client: %w", err) } // Verify the connection err = client.Ping(ctx, nil) if err != nil { return nil, fmt.Errorf("unable to connect successfully: %w", err) } s := &Source{ Name: r.Name, Kind: SourceKind, Client: client, } return s, nil } var _ sources.Source = &Source{} type Source struct { Name string `yaml:"name"` Kind string `yaml:"kind"` Client *mongo.Client } func (s *Source) SourceKind() string { return SourceKind } func (s *Source) MongoClient() *mongo.Client { return s.Client } func initMongoDBClient(ctx context.Context, tracer trace.Tracer, name, uri string) (*mongo.Client, error) { // Start a tracing span ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) defer span.End() userAgent, err := util.UserAgentFromContext(ctx) if err != nil { return nil, err } // Create a new MongoDB client clientOpts := options.Client().ApplyURI(uri).SetAppName(userAgent) client, err := mongo.Connect(ctx, clientOpts) if err != nil { return nil, fmt.Errorf("unable to create MongoDB client: %w", err) } return client, nil } ``` -------------------------------------------------------------------------------- /internal/tools/tools_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tools_test import ( "testing" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/tools" ) func TestGetMcpManifestMetadata(t *testing.T) { authServices := []tools.ParamAuthService{ { Name: "my-google-auth-service", Field: "auth_field", }, { Name: "other-auth-service", Field: "other_auth_field", }} tcs := []struct { desc string name string description string authInvoke []string params tools.Parameters wantMetadata map[string]any }{ { desc: "basic manifest without metadata", name: "basic", description: "foo bar", authInvoke: []string{}, params: tools.Parameters{tools.NewStringParameter("string-param", "string parameter")}, wantMetadata: nil, }, { desc: "with auth invoke metadata", name: "basic", description: "foo bar", authInvoke: []string{"auth1", "auth2"}, params: tools.Parameters{tools.NewStringParameter("string-param", "string parameter")}, wantMetadata: map[string]any{"toolbox/authInvoke": []string{"auth1", "auth2"}}, }, { desc: "with auth param metadata", name: "basic", description: "foo bar", authInvoke: []string{}, params: tools.Parameters{tools.NewStringParameterWithAuth("string-param", "string parameter", authServices)}, wantMetadata: map[string]any{ "toolbox/authParam": map[string][]string{ "string-param": []string{"my-google-auth-service", "other-auth-service"}, }, }, }, { desc: "with auth invoke and auth param metadata", name: "basic", description: "foo bar", authInvoke: []string{"auth1", "auth2"}, params: tools.Parameters{tools.NewStringParameterWithAuth("string-param", "string parameter", authServices)}, wantMetadata: map[string]any{ "toolbox/authInvoke": []string{"auth1", "auth2"}, "toolbox/authParam": map[string][]string{ "string-param": []string{"my-google-auth-service", "other-auth-service"}, }, }, }, } for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { got := tools.GetMcpManifest(tc.name, tc.description, tc.authInvoke, tc.params) gotM := got.Metadata if diff := cmp.Diff(tc.wantMetadata, gotM); diff != "" { t.Fatalf("unexpected metadata (-want +got):\n%s", diff) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerhealthpulse/lookerhealthpulse_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package lookerhealthpulse_test import ( "strings" "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" lhp "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerhealthpulse" ) func TestParseFromYamlLookerHealthPulse(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: looker-health-pulse source: my-instance description: some description `, want: server.ToolConfigs{ "example_tool": lhp.Config{ Name: "example_tool", Kind: "looker-health-pulse", Source: "my-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } func TestFailParseFromYamlLookerHealthPulse(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string err string }{ { desc: "Invalid field", in: ` tools: example_tool: kind: looker-health-pulse source: my-instance invalid_field: true `, err: "unable to parse tool \"example_tool\" as kind \"looker-health-pulse\": [2:1] unknown field \"invalid_field\"", }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err == nil { t.Fatalf("expect parsing to fail") } errStr := err.Error() if !strings.Contains(errStr, tc.err) { t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerhealthvacuum/lookerhealthvacuum_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package lookerhealthvacuum_test import ( "strings" "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" lhv "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerhealthvacuum" ) func TestParseFromYamlLookerHealthVacuum(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: looker-health-vacuum source: my-instance description: some description `, want: server.ToolConfigs{ "example_tool": lhv.Config{ Name: "example_tool", Kind: "looker-health-vacuum", Source: "my-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } func TestFailParseFromYamlLookerHealthVacuum(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string err string }{ { desc: "Invalid field", in: ` tools: example_tool: kind: looker-health-vacuum source: my-instance invalid_field: true `, err: "unable to parse tool \"example_tool\" as kind \"looker-health-vacuum\": [2:1] unknown field \"invalid_field\"", }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err == nil { t.Fatalf("expect parsing to fail") } errStr := err.Error() if !strings.Contains(errStr, tc.err) { t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/spanner/spannerlisttables/spannerlisttables_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package spannerlisttables_test import ( "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" "github.com/googleapis/genai-toolbox/internal/tools/spanner/spannerlisttables" ) func TestParseFromYamlListTables(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: spanner-list-tables source: my-spanner-instance description: Lists tables in the database `, want: server.ToolConfigs{ "example_tool": spannerlisttables.Config{ Name: "example_tool", Kind: "spanner-list-tables", Source: "my-spanner-instance", Description: "Lists tables in the database", AuthRequired: []string{}, }, }, }, { desc: "with auth required", in: ` tools: example_tool: kind: spanner-list-tables source: my-spanner-instance description: Lists tables in the database authRequired: - auth1 - auth2 `, want: server.ToolConfigs{ "example_tool": spannerlisttables.Config{ Name: "example_tool", Kind: "spanner-list-tables", Source: "my-spanner-instance", Description: "Lists tables in the database", AuthRequired: []string{"auth1", "auth2"}, }, }, }, { desc: "minimal config", in: ` tools: example_tool: kind: spanner-list-tables source: my-spanner-instance `, want: server.ToolConfigs{ "example_tool": spannerlisttables.Config{ Name: "example_tool", Kind: "spanner-list-tables", Source: "my-spanner-instance", Description: "", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerhealthanalyze/lookerhealthanalyze_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package lookerhealthanalyze_test import ( "strings" "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" lha "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerhealthanalyze" ) func TestParseFromYamlLookerHealthAnalyze(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: looker-health-analyze source: my-instance description: some description `, want: server.ToolConfigs{ "example_tool": lha.Config{ Name: "example_tool", Kind: "looker-health-analyze", Source: "my-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } func TestFailParseFromYamlLookerHealthAnalyze(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string err string }{ { desc: "Invalid field", in: ` tools: example_tool: kind: looker-health-analyze source: my-instance invalid_field: true `, err: "unable to parse tool \"example_tool\" as kind \"looker-health-analyze\": [2:1] unknown field \"invalid_field\"", }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err == nil { t.Fatalf("expect parsing to fail") } errStr := err.Error() if !strings.Contains(errStr, tc.err) { t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) } }) } } ``` -------------------------------------------------------------------------------- /.github/workflows/deploy_versioned_docs.yaml: -------------------------------------------------------------------------------- ```yaml # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. name: "Deploy Versioned Docs" permissions: contents: write on: release: types: [published] jobs: deploy: runs-on: ubuntu-24.04 # This shared concurrency group ensures only one docs deployment runs at a time. concurrency: group: docs-deployment cancel-in-progress: false steps: - name: Checkout Code at Tag uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: ref: ${{ github.event.release.tag_name }} - name: Get Version from Release Tag run: echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV - name: Setup Hugo uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3 with: hugo-version: "0.145.0" extended: true - name: Setup Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" - name: Install Dependencies run: npm ci working-directory: .hugo - name: Build Hugo Site run: hugo --minify working-directory: .hugo env: HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ env.VERSION }}/ HUGO_RELATIVEURLS: false - name: Deploy uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: .hugo/public publish_branch: versioned-gh-pages destination_dir: ./${{ env.VERSION }} keep_files: true commit_message: "deploy: docs for ${{ env.VERSION }}" - name: Clean Build Directory run: rm -rf .hugo/public - name: Build Hugo Site run: hugo --minify working-directory: .hugo env: HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ HUGO_RELATIVEURLS: false - name: Deploy to root uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: .hugo/public publish_branch: versioned-gh-pages keep_files: true allow_empty_commit: true commit_message: "deploy: docs to root for ${{ env.VERSION }}" ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-wait-for-operation.md: -------------------------------------------------------------------------------- ```markdown --- title: alloydb-wait-for-operation type: docs weight: 10 description: "Wait for a long-running AlloyDB operation to complete.\n" --- The `alloydb-wait-for-operation` tool is a utility tool that waits for a long-running AlloyDB operation to complete. It does this by polling the AlloyDB Admin API operation status endpoint until the operation is finished, using exponential backoff. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source. | Parameter | Type | Description | Required | | :---------- | :----- | :--------------------------------------------------- | :------- | | `project` | string | The GCP project ID. | Yes | | `location` | string | The location of the operation (e.g., 'us-central1'). | Yes | | `operation` | string | The ID of the operation to wait for. | Yes | {{< notice info >}} This tool is intended for developer assistant workflows with human-in-the-loop and shouldn't be used for production agents. {{< /notice >}} ## Example ```yaml tools: wait_for_operation: kind: alloydb-wait-for-operation source: my-alloydb-admin-source description: "This will poll on operations API until the operation is done. For checking operation status we need projectId, locationID and operationId. Once instance is created give follow up steps on how to use the variables to bring data plane MCP server up in local and remote setup." delay: 1s maxDelay: 4m multiplier: 2 maxRetries: 10 ``` ## Reference | **field** | **type** | **required** | **description** | | ----------- | :------: | :----------: | ---------------------------------------------------------------------------------------------------------------- | | kind | string | true | Must be "alloydb-wait-for-operation". | | source | string | true | The name of a `alloydb-admin` source to use for authentication. | | description | string | false | A description of the tool. | | delay | duration | false | The initial delay between polling requests (e.g., `3s`). Defaults to 3 seconds. | | maxDelay | duration | false | The maximum delay between polling requests (e.g., `4m`). Defaults to 4 minutes. | | multiplier | float | false | The multiplier for the polling delay. The delay is multiplied by this value after each request. Defaults to 2.0. | | maxRetries | int | false | The maximum number of polling attempts before giving up. Defaults to 10. | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/bigquery/bigquery-search-catalog.md: -------------------------------------------------------------------------------- ```markdown --- title: "bigquery-search-catalog" type: docs weight: 1 description: > A "bigquery-search-catalog" tool allows to search for entries based on the provided query. --- ## About A `bigquery-search-catalog` tool returns all entries in Dataplex Catalog (e.g. tables, views, models) with system=bigquery that matches given user query. It's compatible with the following sources: - [bigquery](../../sources/bigquery.md) `bigquery-search-catalog` takes a required `query` parameter based on which entries are filtered and returned to the user. It also optionally accepts following parameters: - `datasetIds` - The IDs of the bigquery dataset. - `projectIds` - The IDs of the bigquery project. - `types` - The type of the data. Accepted values are: CONNECTION, POLICY, DATASET, MODEL, ROUTINE, TABLE, VIEW. - `pageSize` - Number of results in the search page. Defaults to `5`. ## Requirements ### IAM Permissions Bigquery uses [Identity and Access Management (IAM)][iam-overview] to control user and group access to Dataplex resources. Toolbox will use your [Application Default Credentials (ADC)][adc] to authorize and authenticate when interacting with [Dataplex][dataplex-docs]. In addition to [setting the ADC for your server][set-adc], you need to ensure the IAM identity has been given the correct IAM permissions for the tasks you intend to perform. See [Dataplex Universal Catalog IAM permissions][iam-permissions] and [Dataplex Universal Catalog IAM roles][iam-roles] for more information on applying IAM permissions and roles to an identity. [iam-overview]: https://cloud.google.com/dataplex/docs/iam-and-access-control [adc]: https://cloud.google.com/docs/authentication#adc [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc [iam-permissions]: https://cloud.google.com/dataplex/docs/iam-permissions [iam-roles]: https://cloud.google.com/dataplex/docs/iam-roles ## Example ```yaml tools: search_catalog: kind: bigquery-search-catalog source: bigquery-source description: Use this tool to find tables, views, models, routines or connections. ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------| | kind | string | true | Must be "bigquery-search-catalog". | | source | string | true | Name of the source the tool should execute on. | | description | string | true | Description of the tool that is passed to the LLM. | ``` -------------------------------------------------------------------------------- /.ci/quickstart_test/run_go_tests.sh: -------------------------------------------------------------------------------- ```bash # Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #!/bin/bash set -e TABLE_NAME="hotels_go" QUICKSTART_GO_DIR="docs/en/getting-started/quickstart/go" SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql" PROXY_PID="" TOOLBOX_PID="" install_system_packages() { apt-get update && apt-get install -y \ postgresql-client \ wget \ gettext-base \ netcat-openbsd } start_cloud_sql_proxy() { wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy chmod +x /usr/local/bin/cloud-sql-proxy cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" & PROXY_PID=$! for i in {1..30}; do if nc -z 127.0.0.1 5432; then echo "Cloud SQL Proxy is up and running." return fi sleep 1 done echo "Cloud SQL Proxy failed to start within the timeout period." exit 1 } setup_toolbox() { TOOLBOX_YAML="/tools.yaml" echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML" if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox" chmod +x "/toolbox" /toolbox --tools-file "$TOOLBOX_YAML" & TOOLBOX_PID=$! sleep 2 } setup_orch_table() { export TABLE_NAME envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME" } run_orch_test() { local orch_dir="$1" local orch_name orch_name=$(basename "$orch_dir") if [ "$orch_name" == "openAI" ]; then echo -e "\nSkipping framework '${orch_name}': Temporarily excluded." return fi ( set -e setup_orch_table echo "--- Preparing module for $orch_name ---" cd "$orch_dir" if [ -f "go.mod" ]; then go mod tidy fi cd .. export ORCH_NAME="$orch_name" echo "--- Running tests for $orch_name ---" go test -v ./... ) } cleanup_all() { echo "--- Final cleanup: Shutting down processes and dropping table ---" if [ -n "$TOOLBOX_PID" ]; then kill $TOOLBOX_PID || true fi if [ -n "$PROXY_PID" ]; then kill $PROXY_PID || true fi } trap cleanup_all EXIT # Main script execution install_system_packages start_cloud_sql_proxy export PGHOST=127.0.0.1 export PGPORT=5432 export PGPASSWORD="$DB_PASSWORD" export GOOGLE_API_KEY="$GOOGLE_API_KEY" setup_toolbox for ORCH_DIR in "$QUICKSTART_GO_DIR"/*/; do if [ ! -d "$ORCH_DIR" ]; then continue fi run_orch_test "$ORCH_DIR" done ``` -------------------------------------------------------------------------------- /docs/en/how-to/export_telemetry.md: -------------------------------------------------------------------------------- ```markdown --- title: "Export Telemetry" type: docs weight: 5 description: > How to set up and configure Toolbox to use the Otel Collector. --- ## About The [OpenTelemetry Collector][about-collector] offers a vendor-agnostic implementation of how to receive, process and export telemetry data. It removes the need to run, operate, and maintain multiple agents/collectors. [about-collector]: https://opentelemetry.io/docs/collector/ ## Configure the Collector To configure the collector, you will have to provide a configuration file. The configuration file consists of four classes of pipeline component that access telemetry data. - `Receivers` - `Processors` - `Exporters` - `Connectors` Example of setting up the classes of pipeline components (in this example, we don't use connectors): ```yaml receivers: otlp: protocols: http: endpoint: "127.0.0.1:4553" exporters: googlecloud: project: <YOUR_GOOGLE_CLOUD_PROJECT> processors: batch: send_batch_size: 200 ``` After each pipeline component is configured, you will enable it within the `service` section of the configuration file. ```yaml service: pipelines: traces: receivers: ["otlp"] processors: ["batch"] exporters: ["googlecloud"] ``` ## Running the Collector There are a couple of steps to run and use a Collector. 1. [Install the Collector](https://opentelemetry.io/docs/collector/installation/) binary. Pull a binary or Docker image for the OpenTelemetry contrib collector. 1. Set up credentials for telemetry backend. 1. Set up the Collector config. Below are some examples for setting up the Collector config: - [Google Cloud Exporter][google-cloud-exporter] - [Google Managed Service for Prometheus Exporter][google-prometheus-exporter] 1. Run the Collector with the configuration file. ```bash ./otelcol-contrib --config=collector-config.yaml ``` 1. Run toolbox with the `--telemetry-otlp` flag. Configure it to send them to `http://127.0.0.1:4553` (for HTTP) or the Collector's URL. ```bash ./toolbox --telemetry-otlp=http://127.0.0.1:4553 ``` 1. Once telemetry datas are collected, you can view them in your telemetry backend. If you are using GCP exporters, telemetry will be visible in GCP dashboard at [Metrics Explorer][metrics-explorer] and [Trace Explorer][trace-explorer]. {{< notice note >}} If you are exporting to Google Cloud monitoring, we recommend that you use the Google Cloud Exporter for traces and the Google Managed Service for Prometheus Exporter for metrics. {{< /notice >}} [google-cloud-exporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/googlecloudexporter [google-prometheus-exporter]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/googlemanagedprometheusexporter#example-configuration [metrics-explorer]: https://console.cloud.google.com/monitoring/metrics-explorer [trace-explorer]: https://console.cloud.google.com/traces ``` -------------------------------------------------------------------------------- /docs/en/getting-started/quickstart/js/genkit/quickstart.js: -------------------------------------------------------------------------------- ```javascript import { ToolboxClient } from "@toolbox-sdk/core"; import { genkit } from "genkit"; import { googleAI } from '@genkit-ai/googleai'; const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY || 'your-api-key'; // Replace it with your API key const systemPrompt = ` You're a helpful hotel assistant. You handle hotel searching, booking, and cancellations. When the user searches for a hotel, mention its name, id, location and price tier. Always mention hotel ids while performing any searches. This is very important for any operations. For any bookings or cancellations, please provide the appropriate confirmation. Be sure to update checkin or checkout dates if mentioned by the user. Don't ask for confirmations from the user. `; const queries = [ "Find hotels in Basel with Basel in its name.", "Can you book the Hilton Basel for me?", "Oh wait, this is too expensive. Please cancel it and book the Hyatt Regency instead.", "My check in dates would be from April 10, 2024 to April 19, 2024.", ]; export async function main() { const toolboxClient = new ToolboxClient("http://127.0.0.1:5000"); const ai = genkit({ plugins: [ googleAI({ apiKey: process.env.GEMINI_API_KEY || GOOGLE_API_KEY }) ], model: googleAI.model('gemini-2.0-flash'), }); const toolboxTools = await toolboxClient.loadToolset("my-toolset"); const toolMap = Object.fromEntries( toolboxTools.map((tool) => { const definedTool = ai.defineTool( { name: tool.getName(), description: tool.getDescription(), inputSchema: tool.getParamSchema(), }, tool ); return [tool.getName(), definedTool]; }) ); const tools = Object.values(toolMap); let conversationHistory = [{ role: "system", content: [{ text: systemPrompt }] }]; for (const query of queries) { conversationHistory.push({ role: "user", content: [{ text: query }] }); const response = await ai.generate({ messages: conversationHistory, tools: tools, }); conversationHistory.push(response.message); const toolRequests = response.toolRequests; if (toolRequests?.length > 0) { // Execute tools concurrently and collect their responses. const toolResponses = await Promise.all( toolRequests.map(async (call) => { try { const toolOutput = await toolMap[call.name].invoke(call.input); return { role: "tool", content: [{ toolResponse: { name: call.name, output: toolOutput } }] }; } catch (e) { console.error(`Error executing tool ${call.name}:`, e); return { role: "tool", content: [{ toolResponse: { name: call.name, output: { error: e.message } } }] }; } }) ); conversationHistory.push(...toolResponses); // Call the AI again with the tool results. response = await ai.generate({ messages: conversationHistory, tools }); conversationHistory.push(response.message); } console.log(response.text); } } main(); ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-create-cluster.md: -------------------------------------------------------------------------------- ```markdown --- title: alloydb-create-cluster type: docs weight: 1 description: "The \"alloydb-create-cluster\" tool creates a new AlloyDB for PostgreSQL cluster in a specified project and location.\n" aliases: [/resources/tools/alloydb-create-cluster] --- ## About The `alloydb-create-cluster` tool creates a new AlloyDB for PostgreSQL cluster in a specified project and location. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source. This tool provisions a cluster with a **private IP address** within the specified VPC network. **Permissions & APIs Required:** Before using, ensure the following on your GCP project: 1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled. 2. The user or service account executing the tool has one of the following IAM roles: - `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role) - `roles/owner` (the Owner basic IAM role) - `roles/editor` (the Editor basic IAM role) The tool takes the following input parameters: | Parameter | Type | Description | Required | | :--------- | :----- | :------------------------------------------------------------------------------------------------------------------------ | :------- | | `project` | string | The GCP project ID where the cluster will be created. | Yes | | `cluster` | string | A unique identifier for the new AlloyDB cluster. | Yes | | `password` | string | A secure password for the initial user. | Yes | | `location` | string | The GCP location where the cluster will be created. Default: `us-central1`. If quota is exhausted then use other regions. | No | | `network` | string | The name of the VPC network to connect the cluster to. Default: `default`. | No | | `user` | string | The name for the initial superuser. Default: `postgres`. | No | ## Example ```yaml tools: create_cluster: kind: alloydb-create-cluster source: alloydb-admin-source description: Use this tool to create a new AlloyDB cluster in a given project and location. ``` ## Reference | **field** | **type** | **required** | **description** | | | ----------- | :------: | :----------: | ---------------------------------------------------- | - | | kind | string | true | Must be alloydb-create-cluster. | | | source | string | true | The name of an `alloydb-admin` source. | | | description | string | false | Description of the tool that is passed to the agent. | | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/dataplex/dataplex-lookup-entry.md: -------------------------------------------------------------------------------- ```markdown --- title: "dataplex-lookup-entry" type: docs weight: 1 description: > A "dataplex-lookup-entry" tool returns details of a particular entry in Dataplex Catalog. aliases: - /resources/tools/dataplex-lookup-entry --- ## About A `dataplex-lookup-entry` tool returns details of a particular entry in Dataplex Catalog. It's compatible with the following sources: - [dataplex](../sources/dataplex.md) `dataplex-lookup-entry` takes a required `name` parameter which contains the project and location to which the request should be attributed in the following form: projects/{project}/locations/{location} and also a required `entry` parameter which is the resource name of the entry in the following form: projects/{project}/locations/{location}/entryGroups/{entryGroup}/entries/{entry}. It also optionally accepts following parameters: - `view` - View to control which parts of an entry the service should return. It takes integer values from 1-4 corresponding to type of view - BASIC, FULL, CUSTOM, ALL - `aspectTypes` - Limits the aspects returned to the provided aspect types in the format `projects/{project}/locations/{location}/aspectTypes/{aspectType}`. It only works for CUSTOM view. - `paths` - Limits the aspects returned to those associated with the provided paths within the Entry. It only works for CUSTOM view. ## Requirements ### IAM Permissions Dataplex uses [Identity and Access Management (IAM)][iam-overview] to control user and group access to Dataplex resources. Toolbox will use your [Application Default Credentials (ADC)][adc] to authorize and authenticate when interacting with [Dataplex][dataplex-docs]. In addition to [setting the ADC for your server][set-adc], you need to ensure the IAM identity has been given the correct IAM permissions for the tasks you intend to perform. See [Dataplex Universal Catalog IAM permissions][iam-permissions] and [Dataplex Universal Catalog IAM roles][iam-roles] for more information on applying IAM permissions and roles to an identity. [iam-overview]: https://cloud.google.com/dataplex/docs/iam-and-access-control [adc]: https://cloud.google.com/docs/authentication#adc [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc [iam-permissions]: https://cloud.google.com/dataplex/docs/iam-permissions [iam-roles]: https://cloud.google.com/dataplex/docs/iam-roles ## Example ```yaml tools: lookup_entry: kind: dataplex-lookup-entry source: my-dataplex-source description: Use this tool to retrieve a specific entry in Dataplex Catalog. ``` ## Reference | **field** | **type** | **required** | **description** | |-------------|:--------:|:------------:|----------------------------------------------------| | kind | string | true | Must be "dataplex-lookup-entry". | | source | string | true | Name of the source the tool should execute on. | | description | string | true | Description of the tool that is passed to the LLM. | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-create-user.md: -------------------------------------------------------------------------------- ```markdown --- title: alloydb-create-user type: docs weight: 2 description: "The \"alloydb-create-user\" tool creates a new database user within a specified AlloyDB cluster.\n" aliases: [/resources/tools/alloydb-create-user] --- ## About The `alloydb-create-user` tool creates a new database user (`ALLOYDB_BUILT_IN` or `ALLOYDB_IAM_USER`) within a specified cluster. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source. **Permissions & APIs Required:** Before using, ensure the following on your GCP project: 1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled. 2. The user or service account executing the tool has one of the following IAM roles: - `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role) - `roles/owner` (the Owner basic IAM role) - `roles/editor` (the Editor basic IAM role) The tool takes the following input parameters: | Parameter | Type | Description | Required | | :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------ | :------- | | `project` | string | The GCP project ID where the cluster exists. | Yes | | `cluster` | string | The ID of the existing cluster where the user will be created. | Yes | | `location` | string | The GCP location where the cluster exists (e.g., `us-central1`). | Yes | | `user` | string | The name for the new user. Must be unique within the cluster. | Yes | | `userType` | string | The type of user. Valid values: `ALLOYDB_BUILT_IN` and `ALLOYDB_IAM_USER`. `ALLOYDB_IAM_USER` is recommended. | Yes | | `password` | string | A secure password for the user. Required only if `userType` is `ALLOYDB_BUILT_IN`. | No | | `databaseRoles` | array(string) | Optional. A list of database roles to grant to the new user (e.g., `pg_read_all_data`). | No | ## Example ```yaml tools: create_user: kind: alloydb-create-user source: alloydb-admin-source description: Use this tool to create a new database user for an AlloyDB cluster. ``` ## Reference | **field** | **type** | **required** | **description** | | ----------- | :------: | :----------: | ---------------------------------------------------- | | kind | string | true | Must be alloydb-create-user. | | source | string | true | The name of an `alloydb-admin` source. | | description | string | false | Description of the tool that is passed to the agent. | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerquery/lookerquery_test.go: -------------------------------------------------------------------------------- ```go // Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package lookerquery_test import ( "strings" "testing" yaml "github.com/goccy/go-yaml" "github.com/google/go-cmp/cmp" "github.com/googleapis/genai-toolbox/internal/server" "github.com/googleapis/genai-toolbox/internal/testutils" lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerquery" ) func TestParseFromYamlLookerQuery(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string want server.ToolConfigs }{ { desc: "basic example", in: ` tools: example_tool: kind: looker-query source: my-instance description: some description `, want: server.ToolConfigs{ "example_tool": lkr.Config{ Name: "example_tool", Kind: "looker-query", Source: "my-instance", Description: "some description", AuthRequired: []string{}, }, }, }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err != nil { t.Fatalf("unable to unmarshal: %s", err) } if diff := cmp.Diff(tc.want, got.Tools); diff != "" { t.Fatalf("incorrect parse: diff %v", diff) } }) } } func TestFailParseFromYamlLookerQuery(t *testing.T) { ctx, err := testutils.ContextWithNewLogger() if err != nil { t.Fatalf("unexpected error: %s", err) } tcs := []struct { desc string in string err string }{ { desc: "Invalid method", in: ` tools: example_tool: kind: looker-query source: my-instance method: GOT description: some description `, err: "unable to parse tool \"example_tool\" as kind \"looker-query\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-query\n> 4 | method: GOT\n ^\n 5 | source: my-instance", }, } for _, tc := range tcs { t.Run(tc.desc, func(t *testing.T) { got := struct { Tools server.ToolConfigs `yaml:"tools"` }{} // Parse contents err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) if err == nil { t.Fatalf("expect parsing to fail") } errStr := err.Error() if !strings.Contains(errStr, tc.err) { t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) } }) } } ```