This is page 6 of 45. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .ci │ ├── continuous.release.cloudbuild.yaml │ ├── generate_release_table.sh │ ├── integration.cloudbuild.yaml │ ├── quickstart_test │ │ ├── go.integration.cloudbuild.yaml │ │ ├── js.integration.cloudbuild.yaml │ │ ├── py.integration.cloudbuild.yaml │ │ ├── run_go_tests.sh │ │ ├── run_js_tests.sh │ │ ├── run_py_tests.sh │ │ └── setup_hotels_sample.sql │ ├── test_with_coverage.sh │ └── versioned.release.cloudbuild.yaml ├── .github │ ├── auto-label.yaml │ ├── blunderbuss.yml │ ├── CODEOWNERS │ ├── header-checker-lint.yml │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── label-sync.yml │ ├── labels.yaml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── release-please.yml │ ├── renovate.json5 │ ├── sync-repo-settings.yaml │ └── workflows │ ├── cloud_build_failure_reporter.yml │ ├── deploy_dev_docs.yaml │ ├── deploy_previous_version_docs.yaml │ ├── deploy_versioned_docs.yaml │ ├── docs_deploy.yaml │ ├── docs_preview_clean.yaml │ ├── docs_preview_deploy.yaml │ ├── lint.yaml │ ├── schedule_reporter.yml │ ├── sync-labels.yaml │ └── tests.yaml ├── .gitignore ├── .gitmodules ├── .golangci.yaml ├── .hugo │ ├── archetypes │ │ └── default.md │ ├── assets │ │ ├── icons │ │ │ └── logo.svg │ │ └── scss │ │ ├── _styles_project.scss │ │ └── _variables_project.scss │ ├── go.mod │ ├── go.sum │ ├── hugo.toml │ ├── layouts │ │ ├── _default │ │ │ └── home.releases.releases │ │ ├── index.llms-full.txt │ │ ├── index.llms.txt │ │ ├── partials │ │ │ ├── hooks │ │ │ │ └── head-end.html │ │ │ ├── navbar-version-selector.html │ │ │ ├── page-meta-links.html │ │ │ └── td │ │ │ └── render-heading.html │ │ ├── robot.txt │ │ └── shortcodes │ │ ├── include.html │ │ ├── ipynb.html │ │ └── regionInclude.html │ ├── package-lock.json │ ├── package.json │ └── static │ ├── favicons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── favicon.ico │ └── js │ └── w3.js ├── CHANGELOG.md ├── cmd │ ├── options_test.go │ ├── options.go │ ├── root_test.go │ ├── root.go │ └── version.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEVELOPER.md ├── Dockerfile ├── docs │ └── en │ ├── _index.md │ ├── about │ │ ├── _index.md │ │ └── faq.md │ ├── concepts │ │ ├── _index.md │ │ └── telemetry │ │ ├── index.md │ │ ├── telemetry_flow.png │ │ └── telemetry_traces.png │ ├── getting-started │ │ ├── _index.md │ │ ├── colab_quickstart.ipynb │ │ ├── configure.md │ │ ├── introduction │ │ │ ├── _index.md │ │ │ └── architecture.png │ │ ├── local_quickstart_go.md │ │ ├── local_quickstart_js.md │ │ ├── local_quickstart.md │ │ ├── mcp_quickstart │ │ │ ├── _index.md │ │ │ ├── inspector_tools.png │ │ │ └── inspector.png │ │ └── quickstart │ │ ├── go │ │ │ ├── genAI │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ ├── genkit │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ ├── langchain │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ ├── openAI │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ └── quickstart_test.go │ │ ├── golden.txt │ │ ├── js │ │ │ ├── genAI │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ ├── genkit │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ ├── langchain │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ ├── llamaindex │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ └── quickstart.test.js │ │ ├── python │ │ │ ├── __init__.py │ │ │ ├── adk │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ ├── core │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ ├── langchain │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ ├── llamaindex │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ └── quickstart_test.py │ │ └── shared │ │ ├── cloud_setup.md │ │ ├── configure_toolbox.md │ │ └── database_setup.md │ ├── how-to │ │ ├── _index.md │ │ ├── connect_via_geminicli.md │ │ ├── connect_via_mcp.md │ │ ├── connect-ide │ │ │ ├── _index.md │ │ │ ├── alloydb_pg_admin_mcp.md │ │ │ ├── alloydb_pg_mcp.md │ │ │ ├── bigquery_mcp.md │ │ │ ├── cloud_sql_mssql_admin_mcp.md │ │ │ ├── cloud_sql_mssql_mcp.md │ │ │ ├── cloud_sql_mysql_admin_mcp.md │ │ │ ├── cloud_sql_mysql_mcp.md │ │ │ ├── cloud_sql_pg_admin_mcp.md │ │ │ ├── cloud_sql_pg_mcp.md │ │ │ ├── firestore_mcp.md │ │ │ ├── looker_mcp.md │ │ │ ├── mssql_mcp.md │ │ │ ├── mysql_mcp.md │ │ │ ├── neo4j_mcp.md │ │ │ ├── postgres_mcp.md │ │ │ ├── spanner_mcp.md │ │ │ └── sqlite_mcp.md │ │ ├── deploy_docker.md │ │ ├── deploy_gke.md │ │ ├── deploy_toolbox.md │ │ ├── export_telemetry.md │ │ └── toolbox-ui │ │ ├── edit-headers.gif │ │ ├── edit-headers.png │ │ ├── index.md │ │ ├── optional-param-checked.png │ │ ├── optional-param-unchecked.png │ │ ├── run-tool.gif │ │ ├── tools.png │ │ └── toolsets.png │ ├── reference │ │ ├── _index.md │ │ ├── cli.md │ │ └── prebuilt-tools.md │ ├── resources │ │ ├── _index.md │ │ ├── authServices │ │ │ ├── _index.md │ │ │ └── google.md │ │ ├── sources │ │ │ ├── _index.md │ │ │ ├── alloydb-admin.md │ │ │ ├── alloydb-pg.md │ │ │ ├── bigquery.md │ │ │ ├── bigtable.md │ │ │ ├── cassandra.md │ │ │ ├── clickhouse.md │ │ │ ├── cloud-monitoring.md │ │ │ ├── cloud-sql-admin.md │ │ │ ├── cloud-sql-mssql.md │ │ │ ├── cloud-sql-mysql.md │ │ │ ├── cloud-sql-pg.md │ │ │ ├── couchbase.md │ │ │ ├── dataplex.md │ │ │ ├── dgraph.md │ │ │ ├── firebird.md │ │ │ ├── firestore.md │ │ │ ├── http.md │ │ │ ├── looker.md │ │ │ ├── mongodb.md │ │ │ ├── mssql.md │ │ │ ├── mysql.md │ │ │ ├── neo4j.md │ │ │ ├── oceanbase.md │ │ │ ├── oracle.md │ │ │ ├── postgres.md │ │ │ ├── redis.md │ │ │ ├── spanner.md │ │ │ ├── sqlite.md │ │ │ ├── tidb.md │ │ │ ├── trino.md │ │ │ ├── valkey.md │ │ │ └── yugabytedb.md │ │ └── tools │ │ ├── _index.md │ │ ├── alloydb │ │ │ ├── _index.md │ │ │ ├── alloydb-create-cluster.md │ │ │ ├── alloydb-create-instance.md │ │ │ ├── alloydb-create-user.md │ │ │ ├── alloydb-get-cluster.md │ │ │ ├── alloydb-get-instance.md │ │ │ ├── alloydb-get-user.md │ │ │ ├── alloydb-list-clusters.md │ │ │ ├── alloydb-list-instances.md │ │ │ ├── alloydb-list-users.md │ │ │ └── alloydb-wait-for-operation.md │ │ ├── alloydbainl │ │ │ ├── _index.md │ │ │ └── alloydb-ai-nl.md │ │ ├── bigquery │ │ │ ├── _index.md │ │ │ ├── bigquery-analyze-contribution.md │ │ │ ├── bigquery-conversational-analytics.md │ │ │ ├── bigquery-execute-sql.md │ │ │ ├── bigquery-forecast.md │ │ │ ├── bigquery-get-dataset-info.md │ │ │ ├── bigquery-get-table-info.md │ │ │ ├── bigquery-list-dataset-ids.md │ │ │ ├── bigquery-list-table-ids.md │ │ │ ├── bigquery-search-catalog.md │ │ │ └── bigquery-sql.md │ │ ├── bigtable │ │ │ ├── _index.md │ │ │ └── bigtable-sql.md │ │ ├── cassandra │ │ │ ├── _index.md │ │ │ └── cassandra-cql.md │ │ ├── clickhouse │ │ │ ├── _index.md │ │ │ ├── clickhouse-execute-sql.md │ │ │ ├── clickhouse-list-databases.md │ │ │ ├── clickhouse-list-tables.md │ │ │ └── clickhouse-sql.md │ │ ├── cloudmonitoring │ │ │ ├── _index.md │ │ │ └── cloud-monitoring-query-prometheus.md │ │ ├── cloudsql │ │ │ ├── _index.md │ │ │ ├── cloudsqlcreatedatabase.md │ │ │ ├── cloudsqlcreateusers.md │ │ │ ├── cloudsqlgetinstances.md │ │ │ ├── cloudsqllistdatabases.md │ │ │ ├── cloudsqllistinstances.md │ │ │ ├── cloudsqlmssqlcreateinstance.md │ │ │ ├── cloudsqlmysqlcreateinstance.md │ │ │ ├── cloudsqlpgcreateinstances.md │ │ │ └── cloudsqlwaitforoperation.md │ │ ├── couchbase │ │ │ ├── _index.md │ │ │ └── couchbase-sql.md │ │ ├── dataform │ │ │ ├── _index.md │ │ │ └── dataform-compile-local.md │ │ ├── dataplex │ │ │ ├── _index.md │ │ │ ├── dataplex-lookup-entry.md │ │ │ ├── dataplex-search-aspect-types.md │ │ │ └── dataplex-search-entries.md │ │ ├── dgraph │ │ │ ├── _index.md │ │ │ └── dgraph-dql.md │ │ ├── firebird │ │ │ ├── _index.md │ │ │ ├── firebird-execute-sql.md │ │ │ └── firebird-sql.md │ │ ├── firestore │ │ │ ├── _index.md │ │ │ ├── firestore-add-documents.md │ │ │ ├── firestore-delete-documents.md │ │ │ ├── firestore-get-documents.md │ │ │ ├── firestore-get-rules.md │ │ │ ├── firestore-list-collections.md │ │ │ ├── firestore-query-collection.md │ │ │ ├── firestore-query.md │ │ │ ├── firestore-update-document.md │ │ │ └── firestore-validate-rules.md │ │ ├── http │ │ │ ├── _index.md │ │ │ └── http.md │ │ ├── looker │ │ │ ├── _index.md │ │ │ ├── looker-add-dashboard-element.md │ │ │ ├── looker-conversational-analytics.md │ │ │ ├── looker-get-dashboards.md │ │ │ ├── looker-get-dimensions.md │ │ │ ├── looker-get-explores.md │ │ │ ├── looker-get-filters.md │ │ │ ├── looker-get-looks.md │ │ │ ├── looker-get-measures.md │ │ │ ├── looker-get-models.md │ │ │ ├── looker-get-parameters.md │ │ │ ├── looker-health-analyze.md │ │ │ ├── looker-health-pulse.md │ │ │ ├── looker-health-vacuum.md │ │ │ ├── looker-make-dashboard.md │ │ │ ├── looker-make-look.md │ │ │ ├── looker-query-sql.md │ │ │ ├── looker-query-url.md │ │ │ ├── looker-query.md │ │ │ └── looker-run-look.md │ │ ├── mongodb │ │ │ ├── _index.md │ │ │ ├── mongodb-aggregate.md │ │ │ ├── mongodb-delete-many.md │ │ │ ├── mongodb-delete-one.md │ │ │ ├── mongodb-find-one.md │ │ │ ├── mongodb-find.md │ │ │ ├── mongodb-insert-many.md │ │ │ ├── mongodb-insert-one.md │ │ │ ├── mongodb-update-many.md │ │ │ └── mongodb-update-one.md │ │ ├── mssql │ │ │ ├── _index.md │ │ │ ├── mssql-execute-sql.md │ │ │ ├── mssql-list-tables.md │ │ │ └── mssql-sql.md │ │ ├── mysql │ │ │ ├── _index.md │ │ │ ├── mysql-execute-sql.md │ │ │ ├── mysql-list-active-queries.md │ │ │ ├── mysql-list-table-fragmentation.md │ │ │ ├── mysql-list-tables-missing-unique-indexes.md │ │ │ ├── mysql-list-tables.md │ │ │ └── mysql-sql.md │ │ ├── neo4j │ │ │ ├── _index.md │ │ │ ├── neo4j-cypher.md │ │ │ ├── neo4j-execute-cypher.md │ │ │ └── neo4j-schema.md │ │ ├── oceanbase │ │ │ ├── _index.md │ │ │ ├── oceanbase-execute-sql.md │ │ │ └── oceanbase-sql.md │ │ ├── oracle │ │ │ ├── _index.md │ │ │ ├── oracle-execute-sql.md │ │ │ └── oracle-sql.md │ │ ├── postgres │ │ │ ├── _index.md │ │ │ ├── postgres-execute-sql.md │ │ │ ├── postgres-list-active-queries.md │ │ │ ├── postgres-list-available-extensions.md │ │ │ ├── postgres-list-installed-extensions.md │ │ │ ├── postgres-list-tables.md │ │ │ └── postgres-sql.md │ │ ├── redis │ │ │ ├── _index.md │ │ │ └── redis.md │ │ ├── spanner │ │ │ ├── _index.md │ │ │ ├── spanner-execute-sql.md │ │ │ ├── spanner-list-tables.md │ │ │ └── spanner-sql.md │ │ ├── sqlite │ │ │ ├── _index.md │ │ │ ├── sqlite-execute-sql.md │ │ │ └── sqlite-sql.md │ │ ├── tidb │ │ │ ├── _index.md │ │ │ ├── tidb-execute-sql.md │ │ │ └── tidb-sql.md │ │ ├── trino │ │ │ ├── _index.md │ │ │ ├── trino-execute-sql.md │ │ │ └── trino-sql.md │ │ ├── utility │ │ │ ├── _index.md │ │ │ └── wait.md │ │ ├── valkey │ │ │ ├── _index.md │ │ │ └── valkey.md │ │ └── yuagbytedb │ │ ├── _index.md │ │ └── yugabytedb-sql.md │ ├── samples │ │ ├── _index.md │ │ ├── alloydb │ │ │ ├── _index.md │ │ │ ├── ai-nl │ │ │ │ ├── alloydb_ai_nl.ipynb │ │ │ │ └── index.md │ │ │ └── mcp_quickstart.md │ │ ├── bigquery │ │ │ ├── _index.md │ │ │ ├── colab_quickstart_bigquery.ipynb │ │ │ ├── local_quickstart.md │ │ │ └── mcp_quickstart │ │ │ ├── _index.md │ │ │ ├── inspector_tools.png │ │ │ └── inspector.png │ │ └── looker │ │ ├── _index.md │ │ ├── looker_gemini_oauth │ │ │ ├── _index.md │ │ │ ├── authenticated.png │ │ │ ├── authorize.png │ │ │ └── registration.png │ │ ├── looker_gemini.md │ │ └── looker_mcp_inspector │ │ ├── _index.md │ │ ├── inspector_tools.png │ │ └── inspector.png │ └── sdks │ ├── _index.md │ ├── go-sdk.md │ ├── js-sdk.md │ └── python-sdk.md ├── gemini-extension.json ├── go.mod ├── go.sum ├── internal │ ├── auth │ │ ├── auth.go │ │ └── google │ │ └── google.go │ ├── log │ │ ├── handler.go │ │ ├── log_test.go │ │ ├── log.go │ │ └── logger.go │ ├── prebuiltconfigs │ │ ├── prebuiltconfigs_test.go │ │ ├── prebuiltconfigs.go │ │ └── tools │ │ ├── alloydb-postgres-admin.yaml │ │ ├── alloydb-postgres-observability.yaml │ │ ├── alloydb-postgres.yaml │ │ ├── bigquery.yaml │ │ ├── clickhouse.yaml │ │ ├── cloud-sql-mssql-admin.yaml │ │ ├── cloud-sql-mssql-observability.yaml │ │ ├── cloud-sql-mssql.yaml │ │ ├── cloud-sql-mysql-admin.yaml │ │ ├── cloud-sql-mysql-observability.yaml │ │ ├── cloud-sql-mysql.yaml │ │ ├── cloud-sql-postgres-admin.yaml │ │ ├── cloud-sql-postgres-observability.yaml │ │ ├── cloud-sql-postgres.yaml │ │ ├── dataplex.yaml │ │ ├── firestore.yaml │ │ ├── looker-conversational-analytics.yaml │ │ ├── looker.yaml │ │ ├── mssql.yaml │ │ ├── mysql.yaml │ │ ├── neo4j.yaml │ │ ├── oceanbase.yaml │ │ ├── postgres.yaml │ │ ├── spanner-postgres.yaml │ │ ├── spanner.yaml │ │ └── sqlite.yaml │ ├── server │ │ ├── api_test.go │ │ ├── api.go │ │ ├── common_test.go │ │ ├── config.go │ │ ├── mcp │ │ │ ├── jsonrpc │ │ │ │ ├── jsonrpc_test.go │ │ │ │ └── jsonrpc.go │ │ │ ├── mcp.go │ │ │ ├── util │ │ │ │ └── lifecycle.go │ │ │ ├── v20241105 │ │ │ │ ├── method.go │ │ │ │ └── types.go │ │ │ ├── v20250326 │ │ │ │ ├── method.go │ │ │ │ └── types.go │ │ │ └── v20250618 │ │ │ ├── method.go │ │ │ └── types.go │ │ ├── mcp_test.go │ │ ├── mcp.go │ │ ├── server_test.go │ │ ├── server.go │ │ ├── static │ │ │ ├── assets │ │ │ │ └── mcptoolboxlogo.png │ │ │ ├── css │ │ │ │ └── style.css │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ ├── auth.js │ │ │ │ ├── loadTools.js │ │ │ │ ├── mainContent.js │ │ │ │ ├── navbar.js │ │ │ │ ├── runTool.js │ │ │ │ ├── toolDisplay.js │ │ │ │ ├── tools.js │ │ │ │ └── toolsets.js │ │ │ ├── tools.html │ │ │ └── toolsets.html │ │ ├── web_test.go │ │ └── web.go │ ├── sources │ │ ├── alloydbadmin │ │ │ ├── alloydbadmin_test.go │ │ │ └── alloydbadmin.go │ │ ├── alloydbpg │ │ │ ├── alloydb_pg_test.go │ │ │ └── alloydb_pg.go │ │ ├── bigquery │ │ │ ├── bigquery_test.go │ │ │ └── bigquery.go │ │ ├── bigtable │ │ │ ├── bigtable_test.go │ │ │ └── bigtable.go │ │ ├── cassandra │ │ │ ├── cassandra_test.go │ │ │ └── cassandra.go │ │ ├── clickhouse │ │ │ ├── clickhouse_test.go │ │ │ └── clickhouse.go │ │ ├── cloudmonitoring │ │ │ ├── cloud_monitoring_test.go │ │ │ └── cloud_monitoring.go │ │ ├── cloudsqladmin │ │ │ ├── cloud_sql_admin_test.go │ │ │ └── cloud_sql_admin.go │ │ ├── cloudsqlmssql │ │ │ ├── cloud_sql_mssql_test.go │ │ │ └── cloud_sql_mssql.go │ │ ├── cloudsqlmysql │ │ │ ├── cloud_sql_mysql_test.go │ │ │ └── cloud_sql_mysql.go │ │ ├── cloudsqlpg │ │ │ ├── cloud_sql_pg_test.go │ │ │ └── cloud_sql_pg.go │ │ ├── couchbase │ │ │ ├── couchbase_test.go │ │ │ └── couchbase.go │ │ ├── dataplex │ │ │ ├── dataplex_test.go │ │ │ └── dataplex.go │ │ ├── dgraph │ │ │ ├── dgraph_test.go │ │ │ └── dgraph.go │ │ ├── dialect.go │ │ ├── firebird │ │ │ ├── firebird_test.go │ │ │ └── firebird.go │ │ ├── firestore │ │ │ ├── firestore_test.go │ │ │ └── firestore.go │ │ ├── http │ │ │ ├── http_test.go │ │ │ └── http.go │ │ ├── ip_type.go │ │ ├── looker │ │ │ ├── looker_test.go │ │ │ └── looker.go │ │ ├── mongodb │ │ │ ├── mongodb_test.go │ │ │ └── mongodb.go │ │ ├── mssql │ │ │ ├── mssql_test.go │ │ │ └── mssql.go │ │ ├── mysql │ │ │ ├── mysql_test.go │ │ │ └── mysql.go │ │ ├── neo4j │ │ │ ├── neo4j_test.go │ │ │ └── neo4j.go │ │ ├── oceanbase │ │ │ ├── oceanbase_test.go │ │ │ └── oceanbase.go │ │ ├── oracle │ │ │ └── oracle.go │ │ ├── postgres │ │ │ ├── postgres_test.go │ │ │ └── postgres.go │ │ ├── redis │ │ │ ├── redis_test.go │ │ │ └── redis.go │ │ ├── sources.go │ │ ├── spanner │ │ │ ├── spanner_test.go │ │ │ └── spanner.go │ │ ├── sqlite │ │ │ ├── sqlite_test.go │ │ │ └── sqlite.go │ │ ├── tidb │ │ │ ├── tidb_test.go │ │ │ └── tidb.go │ │ ├── trino │ │ │ ├── trino_test.go │ │ │ └── trino.go │ │ ├── util.go │ │ ├── valkey │ │ │ ├── valkey_test.go │ │ │ └── valkey.go │ │ └── yugabytedb │ │ ├── yugabytedb_test.go │ │ └── yugabytedb.go │ ├── telemetry │ │ ├── instrumentation.go │ │ └── telemetry.go │ ├── testutils │ │ └── testutils.go │ ├── tools │ │ ├── alloydb │ │ │ ├── alloydbcreatecluster │ │ │ │ ├── alloydbcreatecluster_test.go │ │ │ │ └── alloydbcreatecluster.go │ │ │ ├── alloydbcreateinstance │ │ │ │ ├── alloydbcreateinstance_test.go │ │ │ │ └── alloydbcreateinstance.go │ │ │ ├── alloydbcreateuser │ │ │ │ ├── alloydbcreateuser_test.go │ │ │ │ └── alloydbcreateuser.go │ │ │ ├── alloydbgetcluster │ │ │ │ ├── alloydbgetcluster_test.go │ │ │ │ └── alloydbgetcluster.go │ │ │ ├── alloydbgetinstance │ │ │ │ ├── alloydbgetinstance_test.go │ │ │ │ └── alloydbgetinstance.go │ │ │ ├── alloydbgetuser │ │ │ │ ├── alloydbgetuser_test.go │ │ │ │ └── alloydbgetuser.go │ │ │ ├── alloydblistclusters │ │ │ │ ├── alloydblistclusters_test.go │ │ │ │ └── alloydblistclusters.go │ │ │ ├── alloydblistinstances │ │ │ │ ├── alloydblistinstances_test.go │ │ │ │ └── alloydblistinstances.go │ │ │ ├── alloydblistusers │ │ │ │ ├── alloydblistusers_test.go │ │ │ │ └── alloydblistusers.go │ │ │ └── alloydbwaitforoperation │ │ │ ├── alloydbwaitforoperation_test.go │ │ │ └── alloydbwaitforoperation.go │ │ ├── alloydbainl │ │ │ ├── alloydbainl_test.go │ │ │ └── alloydbainl.go │ │ ├── bigquery │ │ │ ├── bigqueryanalyzecontribution │ │ │ │ ├── bigqueryanalyzecontribution_test.go │ │ │ │ └── bigqueryanalyzecontribution.go │ │ │ ├── bigquerycommon │ │ │ │ ├── table_name_parser_test.go │ │ │ │ ├── table_name_parser.go │ │ │ │ └── util.go │ │ │ ├── bigqueryconversationalanalytics │ │ │ │ ├── bigqueryconversationalanalytics_test.go │ │ │ │ └── bigqueryconversationalanalytics.go │ │ │ ├── bigqueryexecutesql │ │ │ │ ├── bigqueryexecutesql_test.go │ │ │ │ └── bigqueryexecutesql.go │ │ │ ├── bigqueryforecast │ │ │ │ ├── bigqueryforecast_test.go │ │ │ │ └── bigqueryforecast.go │ │ │ ├── bigquerygetdatasetinfo │ │ │ │ ├── bigquerygetdatasetinfo_test.go │ │ │ │ └── bigquerygetdatasetinfo.go │ │ │ ├── bigquerygettableinfo │ │ │ │ ├── bigquerygettableinfo_test.go │ │ │ │ └── bigquerygettableinfo.go │ │ │ ├── bigquerylistdatasetids │ │ │ │ ├── bigquerylistdatasetids_test.go │ │ │ │ └── bigquerylistdatasetids.go │ │ │ ├── bigquerylisttableids │ │ │ │ ├── bigquerylisttableids_test.go │ │ │ │ └── bigquerylisttableids.go │ │ │ ├── bigquerysearchcatalog │ │ │ │ ├── bigquerysearchcatalog_test.go │ │ │ │ └── bigquerysearchcatalog.go │ │ │ └── bigquerysql │ │ │ ├── bigquerysql_test.go │ │ │ └── bigquerysql.go │ │ ├── bigtable │ │ │ ├── bigtable_test.go │ │ │ └── bigtable.go │ │ ├── cassandra │ │ │ └── cassandracql │ │ │ ├── cassandracql_test.go │ │ │ └── cassandracql.go │ │ ├── clickhouse │ │ │ ├── clickhouseexecutesql │ │ │ │ ├── clickhouseexecutesql_test.go │ │ │ │ └── clickhouseexecutesql.go │ │ │ ├── clickhouselistdatabases │ │ │ │ ├── clickhouselistdatabases_test.go │ │ │ │ └── clickhouselistdatabases.go │ │ │ ├── clickhouselisttables │ │ │ │ ├── clickhouselisttables_test.go │ │ │ │ └── clickhouselisttables.go │ │ │ └── clickhousesql │ │ │ ├── clickhousesql_test.go │ │ │ └── clickhousesql.go │ │ ├── cloudmonitoring │ │ │ ├── cloudmonitoring_test.go │ │ │ └── cloudmonitoring.go │ │ ├── cloudsql │ │ │ ├── cloudsqlcreatedatabase │ │ │ │ ├── cloudsqlcreatedatabase_test.go │ │ │ │ └── cloudsqlcreatedatabase.go │ │ │ ├── cloudsqlcreateusers │ │ │ │ ├── cloudsqlcreateusers_test.go │ │ │ │ └── cloudsqlcreateusers.go │ │ │ ├── cloudsqlgetinstances │ │ │ │ ├── cloudsqlgetinstances_test.go │ │ │ │ └── cloudsqlgetinstances.go │ │ │ ├── cloudsqllistdatabases │ │ │ │ ├── cloudsqllistdatabases_test.go │ │ │ │ └── cloudsqllistdatabases.go │ │ │ ├── cloudsqllistinstances │ │ │ │ ├── cloudsqllistinstances_test.go │ │ │ │ └── cloudsqllistinstances.go │ │ │ └── cloudsqlwaitforoperation │ │ │ ├── cloudsqlwaitforoperation_test.go │ │ │ └── cloudsqlwaitforoperation.go │ │ ├── cloudsqlmssql │ │ │ └── cloudsqlmssqlcreateinstance │ │ │ ├── cloudsqlmssqlcreateinstance_test.go │ │ │ └── cloudsqlmssqlcreateinstance.go │ │ ├── cloudsqlmysql │ │ │ └── cloudsqlmysqlcreateinstance │ │ │ ├── cloudsqlmysqlcreateinstance_test.go │ │ │ └── cloudsqlmysqlcreateinstance.go │ │ ├── cloudsqlpg │ │ │ └── cloudsqlpgcreateinstances │ │ │ ├── cloudsqlpgcreateinstances_test.go │ │ │ └── cloudsqlpgcreateinstances.go │ │ ├── common_test.go │ │ ├── common.go │ │ ├── couchbase │ │ │ ├── couchbase_test.go │ │ │ └── couchbase.go │ │ ├── dataform │ │ │ └── dataformcompilelocal │ │ │ ├── dataformcompilelocal_test.go │ │ │ └── dataformcompilelocal.go │ │ ├── dataplex │ │ │ ├── dataplexlookupentry │ │ │ │ ├── dataplexlookupentry_test.go │ │ │ │ └── dataplexlookupentry.go │ │ │ ├── dataplexsearchaspecttypes │ │ │ │ ├── dataplexsearchaspecttypes_test.go │ │ │ │ └── dataplexsearchaspecttypes.go │ │ │ └── dataplexsearchentries │ │ │ ├── dataplexsearchentries_test.go │ │ │ └── dataplexsearchentries.go │ │ ├── dgraph │ │ │ ├── dgraph_test.go │ │ │ └── dgraph.go │ │ ├── firebird │ │ │ ├── firebirdexecutesql │ │ │ │ ├── firebirdexecutesql_test.go │ │ │ │ └── firebirdexecutesql.go │ │ │ └── firebirdsql │ │ │ ├── firebirdsql_test.go │ │ │ └── firebirdsql.go │ │ ├── firestore │ │ │ ├── firestoreadddocuments │ │ │ │ ├── firestoreadddocuments_test.go │ │ │ │ └── firestoreadddocuments.go │ │ │ ├── firestoredeletedocuments │ │ │ │ ├── firestoredeletedocuments_test.go │ │ │ │ └── firestoredeletedocuments.go │ │ │ ├── firestoregetdocuments │ │ │ │ ├── firestoregetdocuments_test.go │ │ │ │ └── firestoregetdocuments.go │ │ │ ├── firestoregetrules │ │ │ │ ├── firestoregetrules_test.go │ │ │ │ └── firestoregetrules.go │ │ │ ├── firestorelistcollections │ │ │ │ ├── firestorelistcollections_test.go │ │ │ │ └── firestorelistcollections.go │ │ │ ├── firestorequery │ │ │ │ ├── firestorequery_test.go │ │ │ │ └── firestorequery.go │ │ │ ├── firestorequerycollection │ │ │ │ ├── firestorequerycollection_test.go │ │ │ │ └── firestorequerycollection.go │ │ │ ├── firestoreupdatedocument │ │ │ │ ├── firestoreupdatedocument_test.go │ │ │ │ └── firestoreupdatedocument.go │ │ │ ├── firestorevalidaterules │ │ │ │ ├── firestorevalidaterules_test.go │ │ │ │ └── firestorevalidaterules.go │ │ │ └── util │ │ │ ├── converter_test.go │ │ │ ├── converter.go │ │ │ ├── validator_test.go │ │ │ └── validator.go │ │ ├── http │ │ │ ├── http_test.go │ │ │ └── http.go │ │ ├── http_method.go │ │ ├── looker │ │ │ ├── lookeradddashboardelement │ │ │ │ ├── lookeradddashboardelement_test.go │ │ │ │ └── lookeradddashboardelement.go │ │ │ ├── lookercommon │ │ │ │ ├── lookercommon_test.go │ │ │ │ └── lookercommon.go │ │ │ ├── lookerconversationalanalytics │ │ │ │ ├── lookerconversationalanalytics_test.go │ │ │ │ └── lookerconversationalanalytics.go │ │ │ ├── lookergetdashboards │ │ │ │ ├── lookergetdashboards_test.go │ │ │ │ └── lookergetdashboards.go │ │ │ ├── lookergetdimensions │ │ │ │ ├── lookergetdimensions_test.go │ │ │ │ └── lookergetdimensions.go │ │ │ ├── lookergetexplores │ │ │ │ ├── lookergetexplores_test.go │ │ │ │ └── lookergetexplores.go │ │ │ ├── lookergetfilters │ │ │ │ ├── lookergetfilters_test.go │ │ │ │ └── lookergetfilters.go │ │ │ ├── lookergetlooks │ │ │ │ ├── lookergetlooks_test.go │ │ │ │ └── lookergetlooks.go │ │ │ ├── lookergetmeasures │ │ │ │ ├── lookergetmeasures_test.go │ │ │ │ └── lookergetmeasures.go │ │ │ ├── lookergetmodels │ │ │ │ ├── lookergetmodels_test.go │ │ │ │ └── lookergetmodels.go │ │ │ ├── lookergetparameters │ │ │ │ ├── lookergetparameters_test.go │ │ │ │ └── lookergetparameters.go │ │ │ ├── lookerhealthanalyze │ │ │ │ ├── lookerhealthanalyze_test.go │ │ │ │ └── lookerhealthanalyze.go │ │ │ ├── lookerhealthpulse │ │ │ │ ├── lookerhealthpulse_test.go │ │ │ │ └── lookerhealthpulse.go │ │ │ ├── lookerhealthvacuum │ │ │ │ ├── lookerhealthvacuum_test.go │ │ │ │ └── lookerhealthvacuum.go │ │ │ ├── lookermakedashboard │ │ │ │ ├── lookermakedashboard_test.go │ │ │ │ └── lookermakedashboard.go │ │ │ ├── lookermakelook │ │ │ │ ├── lookermakelook_test.go │ │ │ │ └── lookermakelook.go │ │ │ ├── lookerquery │ │ │ │ ├── lookerquery_test.go │ │ │ │ └── lookerquery.go │ │ │ ├── lookerquerysql │ │ │ │ ├── lookerquerysql_test.go │ │ │ │ └── lookerquerysql.go │ │ │ ├── lookerqueryurl │ │ │ │ ├── lookerqueryurl_test.go │ │ │ │ └── lookerqueryurl.go │ │ │ └── lookerrunlook │ │ │ ├── lookerrunlook_test.go │ │ │ └── lookerrunlook.go │ │ ├── mongodb │ │ │ ├── mongodbaggregate │ │ │ │ ├── mongodbaggregate_test.go │ │ │ │ └── mongodbaggregate.go │ │ │ ├── mongodbdeletemany │ │ │ │ ├── mongodbdeletemany_test.go │ │ │ │ └── mongodbdeletemany.go │ │ │ ├── mongodbdeleteone │ │ │ │ ├── mongodbdeleteone_test.go │ │ │ │ └── mongodbdeleteone.go │ │ │ ├── mongodbfind │ │ │ │ ├── mongodbfind_test.go │ │ │ │ └── mongodbfind.go │ │ │ ├── mongodbfindone │ │ │ │ ├── mongodbfindone_test.go │ │ │ │ └── mongodbfindone.go │ │ │ ├── mongodbinsertmany │ │ │ │ ├── mongodbinsertmany_test.go │ │ │ │ └── mongodbinsertmany.go │ │ │ ├── mongodbinsertone │ │ │ │ ├── mongodbinsertone_test.go │ │ │ │ └── mongodbinsertone.go │ │ │ ├── mongodbupdatemany │ │ │ │ ├── mongodbupdatemany_test.go │ │ │ │ └── mongodbupdatemany.go │ │ │ └── mongodbupdateone │ │ │ ├── mongodbupdateone_test.go │ │ │ └── mongodbupdateone.go │ │ ├── mssql │ │ │ ├── mssqlexecutesql │ │ │ │ ├── mssqlexecutesql_test.go │ │ │ │ └── mssqlexecutesql.go │ │ │ ├── mssqllisttables │ │ │ │ ├── mssqllisttables_test.go │ │ │ │ └── mssqllisttables.go │ │ │ └── mssqlsql │ │ │ ├── mssqlsql_test.go │ │ │ └── mssqlsql.go │ │ ├── mysql │ │ │ ├── mysqlcommon │ │ │ │ └── mysqlcommon.go │ │ │ ├── mysqlexecutesql │ │ │ │ ├── mysqlexecutesql_test.go │ │ │ │ └── mysqlexecutesql.go │ │ │ ├── mysqllistactivequeries │ │ │ │ ├── mysqllistactivequeries_test.go │ │ │ │ └── mysqllistactivequeries.go │ │ │ ├── mysqllisttablefragmentation │ │ │ │ ├── mysqllisttablefragmentation_test.go │ │ │ │ └── mysqllisttablefragmentation.go │ │ │ ├── mysqllisttables │ │ │ │ ├── mysqllisttables_test.go │ │ │ │ └── mysqllisttables.go │ │ │ ├── mysqllisttablesmissinguniqueindexes │ │ │ │ ├── mysqllisttablesmissinguniqueindexes_test.go │ │ │ │ └── mysqllisttablesmissinguniqueindexes.go │ │ │ └── mysqlsql │ │ │ ├── mysqlsql_test.go │ │ │ └── mysqlsql.go │ │ ├── neo4j │ │ │ ├── neo4jcypher │ │ │ │ ├── neo4jcypher_test.go │ │ │ │ └── neo4jcypher.go │ │ │ ├── neo4jexecutecypher │ │ │ │ ├── classifier │ │ │ │ │ ├── classifier_test.go │ │ │ │ │ └── classifier.go │ │ │ │ ├── neo4jexecutecypher_test.go │ │ │ │ └── neo4jexecutecypher.go │ │ │ └── neo4jschema │ │ │ ├── cache │ │ │ │ ├── cache_test.go │ │ │ │ └── cache.go │ │ │ ├── helpers │ │ │ │ ├── helpers_test.go │ │ │ │ └── helpers.go │ │ │ ├── neo4jschema_test.go │ │ │ ├── neo4jschema.go │ │ │ └── types │ │ │ └── types.go │ │ ├── oceanbase │ │ │ ├── oceanbaseexecutesql │ │ │ │ ├── oceanbaseexecutesql_test.go │ │ │ │ └── oceanbaseexecutesql.go │ │ │ └── oceanbasesql │ │ │ ├── oceanbasesql_test.go │ │ │ └── oceanbasesql.go │ │ ├── oracle │ │ │ ├── oracleexecutesql │ │ │ │ └── oracleexecutesql.go │ │ │ └── oraclesql │ │ │ └── oraclesql.go │ │ ├── parameters_test.go │ │ ├── parameters.go │ │ ├── postgres │ │ │ ├── postgresexecutesql │ │ │ │ ├── postgresexecutesql_test.go │ │ │ │ └── postgresexecutesql.go │ │ │ ├── postgreslistactivequeries │ │ │ │ ├── postgreslistactivequeries_test.go │ │ │ │ └── postgreslistactivequeries.go │ │ │ ├── postgreslistavailableextensions │ │ │ │ ├── postgreslistavailableextensions_test.go │ │ │ │ └── postgreslistavailableextensions.go │ │ │ ├── postgreslistinstalledextensions │ │ │ │ ├── postgreslistinstalledextensions_test.go │ │ │ │ └── postgreslistinstalledextensions.go │ │ │ ├── postgreslisttables │ │ │ │ ├── postgreslisttables_test.go │ │ │ │ └── postgreslisttables.go │ │ │ └── postgressql │ │ │ ├── postgressql_test.go │ │ │ └── postgressql.go │ │ ├── redis │ │ │ ├── redis_test.go │ │ │ └── redis.go │ │ ├── spanner │ │ │ ├── spannerexecutesql │ │ │ │ ├── spannerexecutesql_test.go │ │ │ │ └── spannerexecutesql.go │ │ │ ├── spannerlisttables │ │ │ │ ├── spannerlisttables_test.go │ │ │ │ └── spannerlisttables.go │ │ │ └── spannersql │ │ │ ├── spanner_test.go │ │ │ └── spannersql.go │ │ ├── sqlite │ │ │ ├── sqliteexecutesql │ │ │ │ ├── sqliteexecutesql_test.go │ │ │ │ └── sqliteexecutesql.go │ │ │ └── sqlitesql │ │ │ ├── sqlitesql_test.go │ │ │ └── sqlitesql.go │ │ ├── tidb │ │ │ ├── tidbexecutesql │ │ │ │ ├── tidbexecutesql_test.go │ │ │ │ └── tidbexecutesql.go │ │ │ └── tidbsql │ │ │ ├── tidbsql_test.go │ │ │ └── tidbsql.go │ │ ├── tools_test.go │ │ ├── tools.go │ │ ├── toolsets.go │ │ ├── trino │ │ │ ├── trinoexecutesql │ │ │ │ ├── trinoexecutesql_test.go │ │ │ │ └── trinoexecutesql.go │ │ │ └── trinosql │ │ │ ├── trinosql_test.go │ │ │ └── trinosql.go │ │ ├── utility │ │ │ └── wait │ │ │ ├── wait_test.go │ │ │ └── wait.go │ │ ├── valkey │ │ │ ├── valkey_test.go │ │ │ └── valkey.go │ │ └── yugabytedbsql │ │ ├── yugabytedbsql_test.go │ │ └── yugabytedbsql.go │ └── util │ └── util.go ├── LICENSE ├── logo.png ├── main.go ├── MCP-TOOLBOX-EXTENSION.md ├── README.md └── tests ├── alloydb │ ├── alloydb_integration_test.go │ └── alloydb_wait_for_operation_test.go ├── alloydbainl │ └── alloydb_ai_nl_integration_test.go ├── alloydbpg │ └── alloydb_pg_integration_test.go ├── auth.go ├── bigquery │ └── bigquery_integration_test.go ├── bigtable │ └── bigtable_integration_test.go ├── cassandra │ └── cassandra_integration_test.go ├── clickhouse │ └── clickhouse_integration_test.go ├── cloudmonitoring │ └── cloud_monitoring_integration_test.go ├── cloudsql │ ├── cloud_sql_create_database_test.go │ ├── cloud_sql_create_users_test.go │ ├── cloud_sql_get_instances_test.go │ ├── cloud_sql_list_databases_test.go │ ├── cloudsql_list_instances_test.go │ └── cloudsql_wait_for_operation_test.go ├── cloudsqlmssql │ ├── cloud_sql_mssql_create_instance_integration_test.go │ └── cloud_sql_mssql_integration_test.go ├── cloudsqlmysql │ ├── cloud_sql_mysql_create_instance_integration_test.go │ └── cloud_sql_mysql_integration_test.go ├── cloudsqlpg │ ├── cloud_sql_pg_create_instances_test.go │ └── cloud_sql_pg_integration_test.go ├── common.go ├── couchbase │ └── couchbase_integration_test.go ├── dataform │ └── dataform_integration_test.go ├── dataplex │ └── dataplex_integration_test.go ├── dgraph │ └── dgraph_integration_test.go ├── firebird │ └── firebird_integration_test.go ├── firestore │ └── firestore_integration_test.go ├── http │ └── http_integration_test.go ├── looker │ └── looker_integration_test.go ├── mongodb │ └── mongodb_integration_test.go ├── mssql │ └── mssql_integration_test.go ├── mysql │ └── mysql_integration_test.go ├── neo4j │ └── neo4j_integration_test.go ├── oceanbase │ └── oceanbase_integration_test.go ├── option.go ├── oracle │ └── oracle_integration_test.go ├── postgres │ └── postgres_integration_test.go ├── redis │ └── redis_test.go ├── server.go ├── source.go ├── spanner │ └── spanner_integration_test.go ├── sqlite │ └── sqlite_integration_test.go ├── tidb │ └── tidb_integration_test.go ├── tool.go ├── trino │ └── trino_integration_test.go ├── utility │ └── wait_integration_test.go ├── valkey │ └── valkey_test.go └── yugabytedb └── yugabytedb_integration_test.go ``` # Files -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-wait-for-operation.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: alloydb-wait-for-operation 3 | type: docs 4 | weight: 10 5 | description: "Wait for a long-running AlloyDB operation to complete.\n" 6 | --- 7 | 8 | The `alloydb-wait-for-operation` tool is a utility tool that waits for a 9 | long-running AlloyDB operation to complete. It does this by polling the AlloyDB 10 | Admin API operation status endpoint until the operation is finished, using 11 | exponential backoff. It is compatible with 12 | [alloydb-admin](../../sources/alloydb-admin.md) source. 13 | 14 | | Parameter | Type | Description | Required | 15 | | :---------- | :----- | :--------------------------------------------------- | :------- | 16 | | `project` | string | The GCP project ID. | Yes | 17 | | `location` | string | The location of the operation (e.g., 'us-central1'). | Yes | 18 | | `operation` | string | The ID of the operation to wait for. | Yes | 19 | 20 | {{< notice info >}} 21 | This tool is intended for developer assistant workflows with human-in-the-loop 22 | and shouldn't be used for production agents. 23 | {{< /notice >}} 24 | 25 | ## Example 26 | 27 | ```yaml 28 | tools: 29 | wait_for_operation: 30 | kind: alloydb-wait-for-operation 31 | source: my-alloydb-admin-source 32 | 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." 33 | delay: 1s 34 | maxDelay: 4m 35 | multiplier: 2 36 | maxRetries: 10 37 | ``` 38 | 39 | ## Reference 40 | 41 | | **field** | **type** | **required** | **description** | 42 | | ----------- | :------: | :----------: | ---------------------------------------------------------------------------------------------------------------- | 43 | | kind | string | true | Must be "alloydb-wait-for-operation". | 44 | | source | string | true | The name of a `alloydb-admin` source to use for authentication. | 45 | | description | string | false | A description of the tool. | 46 | | delay | duration | false | The initial delay between polling requests (e.g., `3s`). Defaults to 3 seconds. | 47 | | maxDelay | duration | false | The maximum delay between polling requests (e.g., `4m`). Defaults to 4 minutes. | 48 | | multiplier | float | false | The multiplier for the polling delay. The delay is multiplied by this value after each request. Defaults to 2.0. | 49 | | maxRetries | int | false | The maximum number of polling attempts before giving up. Defaults to 10. | 50 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/bigquery/bigquery-search-catalog.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "bigquery-search-catalog" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "bigquery-search-catalog" tool allows to search for entries based on the provided query. 7 | --- 8 | 9 | ## About 10 | 11 | A `bigquery-search-catalog` tool returns all entries in Dataplex Catalog (e.g. 12 | tables, views, models) with system=bigquery that matches given user query. 13 | It's compatible with the following sources: 14 | 15 | - [bigquery](../../sources/bigquery.md) 16 | 17 | `bigquery-search-catalog` takes a required `query` parameter based on which 18 | entries are filtered and returned to the user. It also optionally accepts 19 | following parameters: 20 | 21 | - `datasetIds` - The IDs of the bigquery dataset. 22 | - `projectIds` - The IDs of the bigquery project. 23 | - `types` - The type of the data. Accepted values are: CONNECTION, POLICY, 24 | DATASET, MODEL, ROUTINE, TABLE, VIEW. 25 | - `pageSize` - Number of results in the search page. Defaults to `5`. 26 | 27 | ## Requirements 28 | 29 | ### IAM Permissions 30 | 31 | Bigquery uses [Identity and Access Management (IAM)][iam-overview] to control 32 | user and group access to Dataplex resources. Toolbox will use your 33 | [Application Default Credentials (ADC)][adc] to authorize and authenticate when 34 | interacting with [Dataplex][dataplex-docs]. 35 | 36 | In addition to [setting the ADC for your server][set-adc], you need to ensure 37 | the IAM identity has been given the correct IAM permissions for the tasks you 38 | intend to perform. See [Dataplex Universal Catalog IAM permissions][iam-permissions] 39 | and [Dataplex Universal Catalog IAM roles][iam-roles] for more information on 40 | applying IAM permissions and roles to an identity. 41 | 42 | [iam-overview]: https://cloud.google.com/dataplex/docs/iam-and-access-control 43 | [adc]: https://cloud.google.com/docs/authentication#adc 44 | [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc 45 | [iam-permissions]: https://cloud.google.com/dataplex/docs/iam-permissions 46 | [iam-roles]: https://cloud.google.com/dataplex/docs/iam-roles 47 | 48 | ## Example 49 | 50 | ```yaml 51 | tools: 52 | search_catalog: 53 | kind: bigquery-search-catalog 54 | source: bigquery-source 55 | description: Use this tool to find tables, views, models, routines or connections. 56 | ``` 57 | 58 | ## Reference 59 | 60 | | **field** | **type** | **required** | **description** | 61 | |-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------| 62 | | kind | string | true | Must be "bigquery-search-catalog". | 63 | | source | string | true | Name of the source the tool should execute on. | 64 | | description | string | true | Description of the tool that is passed to the LLM. | 65 | ``` -------------------------------------------------------------------------------- /.ci/quickstart_test/run_go_tests.sh: -------------------------------------------------------------------------------- ```bash 1 | # Copyright 2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #!/bin/bash 16 | 17 | set -e 18 | 19 | TABLE_NAME="hotels_go" 20 | QUICKSTART_GO_DIR="docs/en/getting-started/quickstart/go" 21 | SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql" 22 | 23 | PROXY_PID="" 24 | TOOLBOX_PID="" 25 | 26 | install_system_packages() { 27 | apt-get update && apt-get install -y \ 28 | postgresql-client \ 29 | wget \ 30 | gettext-base \ 31 | netcat-openbsd 32 | } 33 | 34 | start_cloud_sql_proxy() { 35 | 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 36 | chmod +x /usr/local/bin/cloud-sql-proxy 37 | cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" & 38 | PROXY_PID=$! 39 | 40 | for i in {1..30}; do 41 | if nc -z 127.0.0.1 5432; then 42 | echo "Cloud SQL Proxy is up and running." 43 | return 44 | fi 45 | sleep 1 46 | done 47 | 48 | echo "Cloud SQL Proxy failed to start within the timeout period." 49 | exit 1 50 | } 51 | 52 | setup_toolbox() { 53 | TOOLBOX_YAML="/tools.yaml" 54 | echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML" 55 | if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi 56 | wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox" 57 | chmod +x "/toolbox" 58 | /toolbox --tools-file "$TOOLBOX_YAML" & 59 | TOOLBOX_PID=$! 60 | sleep 2 61 | } 62 | 63 | setup_orch_table() { 64 | export TABLE_NAME 65 | envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME" 66 | } 67 | 68 | run_orch_test() { 69 | local orch_dir="$1" 70 | local orch_name 71 | orch_name=$(basename "$orch_dir") 72 | 73 | if [ "$orch_name" == "openAI" ]; then 74 | echo -e "\nSkipping framework '${orch_name}': Temporarily excluded." 75 | return 76 | fi 77 | 78 | ( 79 | set -e 80 | setup_orch_table 81 | 82 | echo "--- Preparing module for $orch_name ---" 83 | cd "$orch_dir" 84 | 85 | if [ -f "go.mod" ]; then 86 | go mod tidy 87 | fi 88 | 89 | cd .. 90 | 91 | export ORCH_NAME="$orch_name" 92 | 93 | echo "--- Running tests for $orch_name ---" 94 | go test -v ./... 95 | ) 96 | } 97 | 98 | cleanup_all() { 99 | echo "--- Final cleanup: Shutting down processes and dropping table ---" 100 | if [ -n "$TOOLBOX_PID" ]; then 101 | kill $TOOLBOX_PID || true 102 | fi 103 | if [ -n "$PROXY_PID" ]; then 104 | kill $PROXY_PID || true 105 | fi 106 | } 107 | trap cleanup_all EXIT 108 | 109 | # Main script execution 110 | install_system_packages 111 | start_cloud_sql_proxy 112 | 113 | export PGHOST=127.0.0.1 114 | export PGPORT=5432 115 | export PGPASSWORD="$DB_PASSWORD" 116 | export GOOGLE_API_KEY="$GOOGLE_API_KEY" 117 | 118 | setup_toolbox 119 | 120 | for ORCH_DIR in "$QUICKSTART_GO_DIR"/*/; do 121 | if [ ! -d "$ORCH_DIR" ]; then 122 | continue 123 | fi 124 | run_orch_test "$ORCH_DIR" 125 | done 126 | ``` -------------------------------------------------------------------------------- /docs/en/how-to/export_telemetry.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "Export Telemetry" 3 | type: docs 4 | weight: 5 5 | description: > 6 | How to set up and configure Toolbox to use the Otel Collector. 7 | --- 8 | 9 | 10 | ## About 11 | 12 | The [OpenTelemetry Collector][about-collector] offers a vendor-agnostic 13 | implementation of how to receive, process and export telemetry data. It removes 14 | the need to run, operate, and maintain multiple agents/collectors. 15 | 16 | [about-collector]: https://opentelemetry.io/docs/collector/ 17 | 18 | ## Configure the Collector 19 | 20 | To configure the collector, you will have to provide a configuration file. The 21 | configuration file consists of four classes of pipeline component that access 22 | telemetry data. 23 | 24 | - `Receivers` 25 | - `Processors` 26 | - `Exporters` 27 | - `Connectors` 28 | 29 | Example of setting up the classes of pipeline components (in this example, we 30 | don't use connectors): 31 | 32 | ```yaml 33 | receivers: 34 | otlp: 35 | protocols: 36 | http: 37 | endpoint: "127.0.0.1:4553" 38 | 39 | exporters: 40 | googlecloud: 41 | project: <YOUR_GOOGLE_CLOUD_PROJECT> 42 | 43 | processors: 44 | batch: 45 | send_batch_size: 200 46 | ``` 47 | 48 | After each pipeline component is configured, you will enable it within the 49 | `service` section of the configuration file. 50 | 51 | ```yaml 52 | service: 53 | pipelines: 54 | traces: 55 | receivers: ["otlp"] 56 | processors: ["batch"] 57 | exporters: ["googlecloud"] 58 | ``` 59 | 60 | ## Running the Collector 61 | 62 | There are a couple of steps to run and use a Collector. 63 | 64 | 1. [Install the 65 | Collector](https://opentelemetry.io/docs/collector/installation/) binary. 66 | Pull a binary or Docker image for the OpenTelemetry contrib collector. 67 | 68 | 1. Set up credentials for telemetry backend. 69 | 70 | 1. Set up the Collector config. Below are some examples for setting up the 71 | Collector config: 72 | - [Google Cloud Exporter][google-cloud-exporter] 73 | - [Google Managed Service for Prometheus Exporter][google-prometheus-exporter] 74 | 75 | 1. Run the Collector with the configuration file. 76 | 77 | ```bash 78 | ./otelcol-contrib --config=collector-config.yaml 79 | ``` 80 | 81 | 1. Run toolbox with the `--telemetry-otlp` flag. Configure it to send them to 82 | `http://127.0.0.1:4553` (for HTTP) or the Collector's URL. 83 | 84 | ```bash 85 | ./toolbox --telemetry-otlp=http://127.0.0.1:4553 86 | ``` 87 | 88 | 1. Once telemetry datas are collected, you can view them in your telemetry 89 | backend. If you are using GCP exporters, telemetry will be visible in GCP 90 | dashboard at [Metrics Explorer][metrics-explorer] and [Trace 91 | Explorer][trace-explorer]. 92 | 93 | {{< notice note >}} 94 | If you are exporting to Google Cloud monitoring, we recommend that you use 95 | the Google Cloud Exporter for traces and the Google Managed Service for 96 | Prometheus Exporter for metrics. 97 | {{< /notice >}} 98 | 99 | [google-cloud-exporter]: 100 | https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/googlecloudexporter 101 | [google-prometheus-exporter]: 102 | https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/googlemanagedprometheusexporter#example-configuration 103 | [metrics-explorer]: https://console.cloud.google.com/monitoring/metrics-explorer 104 | [trace-explorer]: https://console.cloud.google.com/traces 105 | ``` -------------------------------------------------------------------------------- /docs/en/getting-started/quickstart/js/genkit/quickstart.js: -------------------------------------------------------------------------------- ```javascript 1 | import { ToolboxClient } from "@toolbox-sdk/core"; 2 | import { genkit } from "genkit"; 3 | import { googleAI } from '@genkit-ai/googleai'; 4 | 5 | const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY || 'your-api-key'; // Replace it with your API key 6 | 7 | const systemPrompt = ` 8 | You're a helpful hotel assistant. You handle hotel searching, booking, and 9 | cancellations. When the user searches for a hotel, mention its name, id, 10 | location and price tier. Always mention hotel ids while performing any 11 | searches. This is very important for any operations. For any bookings or 12 | cancellations, please provide the appropriate confirmation. Be sure to 13 | update checkin or checkout dates if mentioned by the user. 14 | Don't ask for confirmations from the user. 15 | `; 16 | 17 | const queries = [ 18 | "Find hotels in Basel with Basel in its name.", 19 | "Can you book the Hilton Basel for me?", 20 | "Oh wait, this is too expensive. Please cancel it and book the Hyatt Regency instead.", 21 | "My check in dates would be from April 10, 2024 to April 19, 2024.", 22 | ]; 23 | 24 | export async function main() { 25 | const toolboxClient = new ToolboxClient("http://127.0.0.1:5000"); 26 | 27 | const ai = genkit({ 28 | plugins: [ 29 | googleAI({ 30 | apiKey: process.env.GEMINI_API_KEY || GOOGLE_API_KEY 31 | }) 32 | ], 33 | model: googleAI.model('gemini-2.0-flash'), 34 | }); 35 | 36 | const toolboxTools = await toolboxClient.loadToolset("my-toolset"); 37 | const toolMap = Object.fromEntries( 38 | toolboxTools.map((tool) => { 39 | const definedTool = ai.defineTool( 40 | { 41 | name: tool.getName(), 42 | description: tool.getDescription(), 43 | inputSchema: tool.getParamSchema(), 44 | }, 45 | tool 46 | ); 47 | return [tool.getName(), definedTool]; 48 | }) 49 | ); 50 | const tools = Object.values(toolMap); 51 | 52 | let conversationHistory = [{ role: "system", content: [{ text: systemPrompt }] }]; 53 | 54 | for (const query of queries) { 55 | conversationHistory.push({ role: "user", content: [{ text: query }] }); 56 | const response = await ai.generate({ 57 | messages: conversationHistory, 58 | tools: tools, 59 | }); 60 | conversationHistory.push(response.message); 61 | 62 | const toolRequests = response.toolRequests; 63 | if (toolRequests?.length > 0) { 64 | // Execute tools concurrently and collect their responses. 65 | const toolResponses = await Promise.all( 66 | toolRequests.map(async (call) => { 67 | try { 68 | const toolOutput = await toolMap[call.name].invoke(call.input); 69 | return { role: "tool", content: [{ toolResponse: { name: call.name, output: toolOutput } }] }; 70 | } catch (e) { 71 | console.error(`Error executing tool ${call.name}:`, e); 72 | return { role: "tool", content: [{ toolResponse: { name: call.name, output: { error: e.message } } }] }; 73 | } 74 | }) 75 | ); 76 | 77 | conversationHistory.push(...toolResponses); 78 | 79 | // Call the AI again with the tool results. 80 | response = await ai.generate({ messages: conversationHistory, tools }); 81 | conversationHistory.push(response.message); 82 | } 83 | 84 | console.log(response.text); 85 | } 86 | } 87 | 88 | main(); 89 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-create-cluster.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: alloydb-create-cluster 3 | type: docs 4 | weight: 1 5 | description: "The \"alloydb-create-cluster\" tool creates a new AlloyDB for PostgreSQL cluster in a specified project and location.\n" 6 | aliases: [/resources/tools/alloydb-create-cluster] 7 | --- 8 | 9 | ## About 10 | 11 | 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. 12 | This tool provisions a cluster with a **private IP address** within the specified VPC network. 13 | 14 | **Permissions & APIs Required:** 15 | Before using, ensure the following on your GCP project: 16 | 17 | 1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled. 18 | 2. The user or service account executing the tool has one of the following IAM roles: 19 | 20 | - `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role) 21 | - `roles/owner` (the Owner basic IAM role) 22 | - `roles/editor` (the Editor basic IAM role) 23 | 24 | The tool takes the following input parameters: 25 | 26 | | Parameter | Type | Description | Required | 27 | | :--------- | :----- | :------------------------------------------------------------------------------------------------------------------------ | :------- | 28 | | `project` | string | The GCP project ID where the cluster will be created. | Yes | 29 | | `cluster` | string | A unique identifier for the new AlloyDB cluster. | Yes | 30 | | `password` | string | A secure password for the initial user. | Yes | 31 | | `location` | string | The GCP location where the cluster will be created. Default: `us-central1`. If quota is exhausted then use other regions. | No | 32 | | `network` | string | The name of the VPC network to connect the cluster to. Default: `default`. | No | 33 | | `user` | string | The name for the initial superuser. Default: `postgres`. | No | 34 | 35 | ## Example 36 | 37 | ```yaml 38 | tools: 39 | create_cluster: 40 | kind: alloydb-create-cluster 41 | source: alloydb-admin-source 42 | description: Use this tool to create a new AlloyDB cluster in a given project and location. 43 | ``` 44 | 45 | ## Reference 46 | 47 | | **field** | **type** | **required** | **description** | | 48 | | ----------- | :------: | :----------: | ---------------------------------------------------- | - | 49 | | kind | string | true | Must be alloydb-create-cluster. | | 50 | | source | string | true | The name of an `alloydb-admin` source. | | 51 | | description | string | false | Description of the tool that is passed to the agent. | | 52 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/dataplex/dataplex-lookup-entry.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "dataplex-lookup-entry" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "dataplex-lookup-entry" tool returns details of a particular entry in Dataplex Catalog. 7 | aliases: 8 | - /resources/tools/dataplex-lookup-entry 9 | --- 10 | 11 | ## About 12 | 13 | A `dataplex-lookup-entry` tool returns details of a particular entry in Dataplex 14 | Catalog. It's compatible with the following sources: 15 | 16 | - [dataplex](../sources/dataplex.md) 17 | 18 | `dataplex-lookup-entry` takes a required `name` parameter which contains the 19 | project and location to which the request should be attributed in the following 20 | form: projects/{project}/locations/{location} and also a required `entry` 21 | parameter which is the resource name of the entry in the following form: 22 | projects/{project}/locations/{location}/entryGroups/{entryGroup}/entries/{entry}. 23 | It also optionally accepts following parameters: 24 | 25 | - `view` - View to control which parts of an entry the service should return. 26 | It takes integer values from 1-4 corresponding to type of view - BASIC, 27 | FULL, CUSTOM, ALL 28 | - `aspectTypes` - Limits the aspects returned to the provided aspect types in 29 | the format 30 | `projects/{project}/locations/{location}/aspectTypes/{aspectType}`. It only 31 | works for CUSTOM view. 32 | - `paths` - Limits the aspects returned to those associated with the provided 33 | paths within the Entry. It only works for CUSTOM view. 34 | 35 | ## Requirements 36 | 37 | ### IAM Permissions 38 | 39 | Dataplex uses [Identity and Access Management (IAM)][iam-overview] to control 40 | user and group access to Dataplex resources. Toolbox will use your 41 | [Application Default Credentials (ADC)][adc] to authorize and authenticate when 42 | interacting with [Dataplex][dataplex-docs]. 43 | 44 | In addition to [setting the ADC for your server][set-adc], you need to ensure 45 | the IAM identity has been given the correct IAM permissions for the tasks you 46 | intend to perform. See [Dataplex Universal Catalog IAM permissions][iam-permissions] 47 | and [Dataplex Universal Catalog IAM roles][iam-roles] for more information on 48 | applying IAM permissions and roles to an identity. 49 | 50 | [iam-overview]: https://cloud.google.com/dataplex/docs/iam-and-access-control 51 | [adc]: https://cloud.google.com/docs/authentication#adc 52 | [set-adc]: https://cloud.google.com/docs/authentication/provide-credentials-adc 53 | [iam-permissions]: https://cloud.google.com/dataplex/docs/iam-permissions 54 | [iam-roles]: https://cloud.google.com/dataplex/docs/iam-roles 55 | 56 | ## Example 57 | 58 | ```yaml 59 | tools: 60 | lookup_entry: 61 | kind: dataplex-lookup-entry 62 | source: my-dataplex-source 63 | description: Use this tool to retrieve a specific entry in Dataplex Catalog. 64 | ``` 65 | 66 | ## Reference 67 | 68 | | **field** | **type** | **required** | **description** | 69 | |-------------|:--------:|:------------:|----------------------------------------------------| 70 | | kind | string | true | Must be "dataplex-lookup-entry". | 71 | | source | string | true | Name of the source the tool should execute on. | 72 | | description | string | true | Description of the tool that is passed to the LLM. | 73 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-create-user.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: alloydb-create-user 3 | type: docs 4 | weight: 2 5 | description: "The \"alloydb-create-user\" tool creates a new database user within a specified AlloyDB cluster.\n" 6 | aliases: [/resources/tools/alloydb-create-user] 7 | --- 8 | 9 | ## About 10 | 11 | The `alloydb-create-user` tool creates a new database user (`ALLOYDB_BUILT_IN` 12 | or `ALLOYDB_IAM_USER`) within a specified cluster. It is compatible with 13 | [alloydb-admin](../../sources/alloydb-admin.md) source. 14 | 15 | **Permissions & APIs Required:** 16 | Before using, ensure the following on your GCP project: 17 | 18 | 1. The [AlloyDB 19 | API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) 20 | is enabled. 21 | 2. The user or service account executing the tool has one of the following IAM 22 | roles: 23 | - `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role) 24 | - `roles/owner` (the Owner basic IAM role) 25 | - `roles/editor` (the Editor basic IAM role) 26 | 27 | The tool takes the following input parameters: 28 | 29 | | Parameter | Type | Description | Required | 30 | | :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------ | :------- | 31 | | `project` | string | The GCP project ID where the cluster exists. | Yes | 32 | | `cluster` | string | The ID of the existing cluster where the user will be created. | Yes | 33 | | `location` | string | The GCP location where the cluster exists (e.g., `us-central1`). | Yes | 34 | | `user` | string | The name for the new user. Must be unique within the cluster. | Yes | 35 | | `userType` | string | The type of user. Valid values: `ALLOYDB_BUILT_IN` and `ALLOYDB_IAM_USER`. `ALLOYDB_IAM_USER` is recommended. | Yes | 36 | | `password` | string | A secure password for the user. Required only if `userType` is `ALLOYDB_BUILT_IN`. | No | 37 | | `databaseRoles` | array(string) | Optional. A list of database roles to grant to the new user (e.g., `pg_read_all_data`). | No | 38 | 39 | ## Example 40 | 41 | ```yaml 42 | tools: 43 | create_user: 44 | kind: alloydb-create-user 45 | source: alloydb-admin-source 46 | description: Use this tool to create a new database user for an AlloyDB cluster. 47 | ``` 48 | 49 | ## Reference 50 | 51 | | **field** | **type** | **required** | **description** | 52 | | ----------- | :------: | :----------: | ---------------------------------------------------- | 53 | | kind | string | true | Must be alloydb-create-user. | 54 | | source | string | true | The name of an `alloydb-admin` source. | 55 | | description | string | false | Description of the tool that is passed to the agent. | 56 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerquery/lookerquery_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookerquery_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerquery" 26 | ) 27 | 28 | func TestParseFromYamlLookerQuery(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-query 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-query", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerQuery(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-query 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | 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", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerrunlook/lookerrunlook_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookerrunlook_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerrunlook" 26 | ) 27 | 28 | func TestParseFromYamlLookerRunLook(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-run-look 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-run-look", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerRunLook(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-run-look 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-run-look\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-run-look\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /.ci/quickstart_test/run_js_tests.sh: -------------------------------------------------------------------------------- ```bash 1 | # Copyright 2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | #!/bin/bash 16 | 17 | set -e 18 | 19 | TABLE_NAME="hotels_js" 20 | QUICKSTART_JS_DIR="docs/en/getting-started/quickstart/js" 21 | SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql" 22 | 23 | # Initialize process IDs to empty at the top of the script 24 | PROXY_PID="" 25 | TOOLBOX_PID="" 26 | 27 | install_system_packages() { 28 | apt-get update && apt-get install -y \ 29 | postgresql-client \ 30 | wget \ 31 | gettext-base \ 32 | netcat-openbsd 33 | } 34 | 35 | start_cloud_sql_proxy() { 36 | 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 37 | chmod +x /usr/local/bin/cloud-sql-proxy 38 | cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" & 39 | PROXY_PID=$! 40 | 41 | for i in {1..30}; do 42 | if nc -z 127.0.0.1 5432; then 43 | echo "Cloud SQL Proxy is up and running." 44 | return 45 | fi 46 | sleep 1 47 | done 48 | 49 | echo "Cloud SQL Proxy failed to start within the timeout period." 50 | exit 1 51 | } 52 | 53 | setup_toolbox() { 54 | TOOLBOX_YAML="/tools.yaml" 55 | echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML" 56 | if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi 57 | wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox" 58 | chmod +x "/toolbox" 59 | /toolbox --tools-file "$TOOLBOX_YAML" & 60 | TOOLBOX_PID=$! 61 | sleep 2 62 | } 63 | 64 | setup_orch_table() { 65 | export TABLE_NAME 66 | envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME" 67 | } 68 | 69 | run_orch_test() { 70 | local orch_dir="$1" 71 | local orch_name 72 | orch_name=$(basename "$orch_dir") 73 | 74 | ( 75 | set -e 76 | echo "--- Preparing environment for $orch_name ---" 77 | setup_orch_table 78 | 79 | cd "$orch_dir" 80 | if [ -f "package.json" ]; then 81 | echo "Installing dependencies for $orch_name..." 82 | npm install 83 | fi 84 | 85 | cd .. 86 | 87 | echo "--- Running tests for $orch_name ---" 88 | export ORCH_NAME="$orch_name" 89 | node --test quickstart.test.js 90 | 91 | echo "--- Cleaning environment for $orch_name ---" 92 | rm -rf "${orch_name}/node_modules" 93 | ) 94 | } 95 | 96 | cleanup_all() { 97 | echo "--- Final cleanup: Shutting down processes and dropping table ---" 98 | if [ -n "$TOOLBOX_PID" ]; then 99 | kill $TOOLBOX_PID || true 100 | fi 101 | if [ -n "$PROXY_PID" ]; then 102 | kill $PROXY_PID || true 103 | fi 104 | } 105 | trap cleanup_all EXIT 106 | 107 | # Main script execution 108 | install_system_packages 109 | start_cloud_sql_proxy 110 | 111 | export PGHOST=127.0.0.1 112 | export PGPORT=5432 113 | export PGPASSWORD="$DB_PASSWORD" 114 | export GOOGLE_API_KEY="$GOOGLE_API_KEY" 115 | 116 | setup_toolbox 117 | 118 | for ORCH_DIR in "$QUICKSTART_JS_DIR"/*/; do 119 | if [ ! -d "$ORCH_DIR" ]; then 120 | continue 121 | fi 122 | run_orch_test "$ORCH_DIR" 123 | done ``` -------------------------------------------------------------------------------- /internal/sources/firebird/firebird.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firebird 16 | 17 | import ( 18 | "context" 19 | "database/sql" 20 | "fmt" 21 | "time" 22 | 23 | "github.com/goccy/go-yaml" 24 | "github.com/googleapis/genai-toolbox/internal/sources" 25 | "go.opentelemetry.io/otel/trace" 26 | ) 27 | 28 | const SourceKind string = "firebird" 29 | 30 | var _ sources.SourceConfig = Config{} 31 | 32 | func init() { 33 | if !sources.Register(SourceKind, newConfig) { 34 | panic(fmt.Sprintf("source kind %q already registered", SourceKind)) 35 | } 36 | } 37 | 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { 39 | actual := Config{Name: name} 40 | if err := decoder.DecodeContext(ctx, &actual); err != nil { 41 | return nil, err 42 | } 43 | return actual, nil 44 | } 45 | 46 | type Config struct { 47 | Name string `yaml:"name" validate:"required"` 48 | Kind string `yaml:"kind" validate:"required"` 49 | Host string `yaml:"host" validate:"required"` 50 | Port string `yaml:"port" validate:"required"` 51 | User string `yaml:"user" validate:"required"` 52 | Password string `yaml:"password" validate:"required"` 53 | Database string `yaml:"database" validate:"required"` 54 | } 55 | 56 | func (r Config) SourceConfigKind() string { 57 | return SourceKind 58 | } 59 | 60 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { 61 | pool, err := initFirebirdConnectionPool(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database) 62 | if err != nil { 63 | return nil, fmt.Errorf("unable to create pool: %w", err) 64 | } 65 | 66 | err = pool.PingContext(ctx) 67 | if err != nil { 68 | return nil, fmt.Errorf("unable to connect successfully: %w", err) 69 | } 70 | 71 | s := &Source{ 72 | Name: r.Name, 73 | Kind: SourceKind, 74 | Db: pool, 75 | } 76 | return s, nil 77 | } 78 | 79 | var _ sources.Source = &Source{} 80 | 81 | type Source struct { 82 | Name string `yaml:"name"` 83 | Kind string `yaml:"kind"` 84 | Db *sql.DB 85 | } 86 | 87 | func (s *Source) SourceKind() string { 88 | return SourceKind 89 | } 90 | 91 | func (s *Source) FirebirdDB() *sql.DB { 92 | return s.Db 93 | } 94 | 95 | func initFirebirdConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname string) (*sql.DB, error) { 96 | _, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) 97 | defer span.End() 98 | 99 | // urlExample := "user:password@host:port/path/to/database.fdb" 100 | dsn := fmt.Sprintf("%s:%s@%s:%s/%s", user, pass, host, port, dbname) 101 | 102 | db, err := sql.Open("firebirdsql", dsn) 103 | if err != nil { 104 | return nil, fmt.Errorf("unable to create connection pool: %w", err) 105 | } 106 | 107 | // Configure connection pool to prevent deadlocks 108 | db.SetMaxOpenConns(5) 109 | db.SetMaxIdleConns(2) 110 | db.SetConnMaxLifetime(5 * time.Minute) 111 | db.SetConnMaxIdleTime(1 * time.Minute) 112 | 113 | return db, nil 114 | } 115 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/alloydb/alloydb-create-instance.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: alloydb-create-instance 3 | type: docs 4 | weight: 1 5 | description: "The \"alloydb-create-instance\" tool creates a new AlloyDB instance within a specified cluster.\n" 6 | aliases: [/resources/tools/alloydb-create-instance] 7 | --- 8 | 9 | ## About 10 | 11 | The `alloydb-create-instance` tool creates a new AlloyDB instance (PRIMARY or 12 | READ_POOL) within a specified cluster. It is compatible with 13 | [alloydb-admin](../../sources/alloydb-admin.md) source. 14 | This tool provisions a new instance with a **public IP address**. 15 | 16 | **Permissions & APIs Required:** 17 | Before using, ensure the following on your GCP project: 18 | 19 | 1. The [AlloyDB 20 | API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) 21 | is enabled. 22 | 2. The user or service account executing the tool has one of the following IAM 23 | roles: 24 | 25 | 26 | - `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role) 27 | - `roles/owner` (the Owner basic IAM role) 28 | - `roles/editor` (the Editor basic IAM role) 29 | 30 | The tool takes the following input parameters: 31 | 32 | | Parameter | Type | Description | Required | 33 | | :------------- | :----- | :------------------------------------------------------------------------------------------------ | :------- | 34 | | `project` | string | The GCP project ID where the cluster exists. | Yes | 35 | | `location` | string | The GCP location where the cluster exists (e.g., `us-central1`). | Yes | 36 | | `cluster` | string | The ID of the existing cluster to add this instance to. | Yes | 37 | | `instance` | string | A unique identifier for the new AlloyDB instance. | Yes | 38 | | `instanceType` | string | The type of instance. Valid values are: `PRIMARY` and `READ_POOL`. Default: `PRIMARY` | No | 39 | | `displayName` | string | An optional, user-friendly name for the instance. | No | 40 | | `nodeCount` | int | The number of nodes for a read pool. Required only if `instanceType` is `READ_POOL`. Default: `1` | No | 41 | 42 | > Note 43 | > The tool sets the `password.enforce_complexity` database flag to `on`, 44 | > requiring new database passwords to meet complexity rules. 45 | 46 | ## Example 47 | 48 | ```yaml 49 | tools: 50 | create_instance: 51 | kind: alloydb-create-instance 52 | source: alloydb-admin-source 53 | description: Use this tool to create a new AlloyDB instance within a specified cluster. 54 | ``` 55 | 56 | ## Reference 57 | 58 | | **field** | **type** | **required** | **description** | 59 | | ----------- | :------: | :----------: | ---------------------------------------------------- | 60 | | kind | string | true | Must be alloydb-create-instance. | 61 | | source | string | true | The name of an `alloydb-admin` source. | 62 | | description | string | false | Description of the tool that is passed to the agent. | 63 | ``` -------------------------------------------------------------------------------- /internal/sources/bigtable/bigtable_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package bigtable_test 16 | 17 | import ( 18 | "testing" 19 | 20 | yaml "github.com/goccy/go-yaml" 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/googleapis/genai-toolbox/internal/server" 23 | "github.com/googleapis/genai-toolbox/internal/sources" 24 | "github.com/googleapis/genai-toolbox/internal/sources/bigtable" 25 | "github.com/googleapis/genai-toolbox/internal/testutils" 26 | ) 27 | 28 | func TestParseFromYamlBigtableDb(t *testing.T) { 29 | tcs := []struct { 30 | desc string 31 | in string 32 | want server.SourceConfigs 33 | }{ 34 | { 35 | desc: "can configure with a bigtable table", 36 | in: ` 37 | sources: 38 | my-bigtable-instance: 39 | kind: bigtable 40 | project: my-project 41 | instance: my-instance 42 | `, 43 | want: map[string]sources.SourceConfig{ 44 | "my-bigtable-instance": bigtable.Config{ 45 | Name: "my-bigtable-instance", 46 | Kind: bigtable.SourceKind, 47 | Project: "my-project", 48 | Instance: "my-instance", 49 | }, 50 | }, 51 | }, 52 | } 53 | for _, tc := range tcs { 54 | t.Run(tc.desc, func(t *testing.T) { 55 | got := struct { 56 | Sources server.SourceConfigs `yaml:"sources"` 57 | }{} 58 | // Parse contents 59 | err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) 60 | if err != nil { 61 | t.Fatalf("unable to unmarshal: %s", err) 62 | } 63 | if !cmp.Equal(tc.want, got.Sources) { 64 | t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources) 65 | } 66 | }) 67 | } 68 | 69 | } 70 | 71 | func TestFailParseFromYaml(t *testing.T) { 72 | tcs := []struct { 73 | desc string 74 | in string 75 | err string 76 | }{ 77 | { 78 | desc: "extra field", 79 | in: ` 80 | sources: 81 | my-bigtable-instance: 82 | kind: bigtable 83 | project: my-project 84 | instance: my-instance 85 | foo: bar 86 | `, 87 | err: "unable to parse source \"my-bigtable-instance\" as \"bigtable\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | instance: my-instance\n 3 | kind: bigtable\n 4 | project: my-project", 88 | }, 89 | { 90 | desc: "missing required field", 91 | in: ` 92 | sources: 93 | my-bigtable-instance: 94 | kind: bigtable 95 | project: my-project 96 | `, 97 | err: "unable to parse source \"my-bigtable-instance\" as \"bigtable\": Key: 'Config.Instance' Error:Field validation for 'Instance' failed on the 'required' tag", 98 | }, 99 | } 100 | for _, tc := range tcs { 101 | t.Run(tc.desc, func(t *testing.T) { 102 | got := struct { 103 | Sources server.SourceConfigs `yaml:"sources"` 104 | }{} 105 | // Parse contents 106 | err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) 107 | if err == nil { 108 | t.Fatalf("expect parsing to fail") 109 | } 110 | errStr := err.Error() 111 | if errStr != tc.err { 112 | t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err) 113 | } 114 | }) 115 | } 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetlooks/lookergetlooks_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetlooks_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetlooks" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetLooks(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-looks 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-looks", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetLooks(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-looks 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-looks\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-looks\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookermakelook/lookermakelook_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookermakelook_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookermakelook" 26 | ) 27 | 28 | func TestParseFromYamlLookerMakeLook(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-make-look 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-make-look", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerMakeLook(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-make-look 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-make-look\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-make-look\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerquerysql/lookerquerysql_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookerquerysql_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerquerysql" 26 | ) 27 | 28 | func TestParseFromYamlLookerQuerySql(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-query-sql 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-query-sql", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerQuerySql(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-query-sql 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-query-sql\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-query-sql\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerqueryurl/lookerqueryurl_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookerqueryurl_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerqueryurl" 26 | ) 27 | 28 | func TestParseFromYamlLookerQueryUrl(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-query-url 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-query-url", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerQueryUrl(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-query-url 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-query-url\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-query-url\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/mongodb/mongodbinsertmany/mongodbinsertmany_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mongodbinsertmany_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | "github.com/googleapis/genai-toolbox/internal/tools/mongodb/mongodbinsertmany" 22 | 23 | yaml "github.com/goccy/go-yaml" 24 | "github.com/google/go-cmp/cmp" 25 | "github.com/googleapis/genai-toolbox/internal/server" 26 | "github.com/googleapis/genai-toolbox/internal/testutils" 27 | ) 28 | 29 | func TestParseFromYamlMongoQuery(t *testing.T) { 30 | ctx, err := testutils.ContextWithNewLogger() 31 | if err != nil { 32 | t.Fatalf("unexpected error: %s", err) 33 | } 34 | tcs := []struct { 35 | desc string 36 | in string 37 | want server.ToolConfigs 38 | }{ 39 | { 40 | desc: "basic example", 41 | in: ` 42 | tools: 43 | example_tool: 44 | kind: mongodb-insert-many 45 | source: my-instance 46 | description: some description 47 | database: test_db 48 | collection: test_coll 49 | canonical: true 50 | `, 51 | want: server.ToolConfigs{ 52 | "example_tool": mongodbinsertmany.Config{ 53 | Name: "example_tool", 54 | Kind: "mongodb-insert-many", 55 | Source: "my-instance", 56 | AuthRequired: []string{}, 57 | Database: "test_db", 58 | Collection: "test_coll", 59 | Description: "some description", 60 | Canonical: true, 61 | }, 62 | }, 63 | }, 64 | } 65 | for _, tc := range tcs { 66 | t.Run(tc.desc, func(t *testing.T) { 67 | got := struct { 68 | Tools server.ToolConfigs `yaml:"tools"` 69 | }{} 70 | // Parse contents 71 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 72 | if err != nil { 73 | t.Fatalf("unable to unmarshal: %s", err) 74 | } 75 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 76 | t.Fatalf("incorrect parse: diff %v", diff) 77 | } 78 | }) 79 | } 80 | 81 | } 82 | 83 | func TestFailParseFromYamlMongoQuery(t *testing.T) { 84 | ctx, err := testutils.ContextWithNewLogger() 85 | if err != nil { 86 | t.Fatalf("unexpected error: %s", err) 87 | } 88 | tcs := []struct { 89 | desc string 90 | in string 91 | err string 92 | }{ 93 | { 94 | desc: "Invalid method", 95 | in: ` 96 | tools: 97 | example_tool: 98 | kind: mongodb-insert-many 99 | source: my-instance 100 | description: some description 101 | collection: test_coll 102 | `, 103 | err: `unable to parse tool "example_tool" as kind "mongodb-insert-many"`, 104 | }, 105 | } 106 | for _, tc := range tcs { 107 | t.Run(tc.desc, func(t *testing.T) { 108 | got := struct { 109 | Tools server.ToolConfigs `yaml:"tools"` 110 | }{} 111 | // Parse contents 112 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 113 | if err == nil { 114 | t.Fatalf("expect parsing to fail") 115 | } 116 | errStr := err.Error() 117 | if !strings.Contains(errStr, tc.err) { 118 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 119 | } 120 | }) 121 | } 122 | 123 | } 124 | ``` -------------------------------------------------------------------------------- /.hugo/layouts/partials/page-meta-links.html: -------------------------------------------------------------------------------- ```html 1 | {{/* cSpell:ignore querify subdir */ -}} 2 | {{/* Class names ending with `--KIND` are deprecated in favor of `__KIND`, but we're keeping them for a few releases after 0.9.0 */ -}} 3 | 4 | {{ if .File -}} 5 | {{ $path := urls.JoinPath $.Language.Lang $.File.Path -}} 6 | {{ $gh_repo := $.Param "github_repo" -}} 7 | {{ $gh_url := $.Param "github_url" -}} 8 | {{ $gh_subdir := $.Param "github_subdir" | default "" -}} 9 | {{ $gh_project_repo := $.Param "github_project_repo" -}} 10 | {{ $gh_branch := $.Param "github_branch" | default "main" -}} 11 | <div class="td-page-meta ms-2 pb-1 pt-2 mb-0"> 12 | {{ if $gh_url -}} 13 | {{ warnf "Warning: use of `github_url` is deprecated. For details, see https://www.docsy.dev/docs/adding-content/repository-links/#github_url-optional" -}} 14 | <a href="{{ $gh_url }}" target="_blank"><i class="fa-solid fa-pen-to-square fa-fw"></i> {{ T "post_edit_this" }}</a> 15 | {{ else if $gh_repo -}} 16 | 17 | {{/* Adjust $path based on path_base_for_github_subdir */ -}} 18 | {{ $ghs_base := $.Param "path_base_for_github_subdir" -}} 19 | {{ $ghs_rename := "" -}} 20 | {{ if reflect.IsMap $ghs_base -}} 21 | {{ $ghs_rename = $ghs_base.to -}} 22 | {{ $ghs_base = $ghs_base.from -}} 23 | {{ end -}} 24 | {{ with $ghs_base -}} 25 | {{ $path = replaceRE . $ghs_rename $path -}} 26 | {{ end -}} 27 | 28 | {{ $gh_repo_path := printf "%s/%s/%s" $gh_branch $gh_subdir $path -}} 29 | {{ $gh_repo_path = replaceRE "//+" "/" $gh_repo_path -}} 30 | 31 | {{ $viewURL := printf "%s/tree/%s" $gh_repo $gh_repo_path -}} 32 | {{ $editURL := printf "%s/edit/%s" $gh_repo $gh_repo_path -}} 33 | {{ $issuesURL := printf "%s/issues/new?title=%s" $gh_repo (safeURL $.Title ) -}} 34 | {{ $newPageStub := resources.Get "stubs/new-page-template.md" -}} 35 | {{ $newPageQS := querify "value" $newPageStub.Content "filename" "change-me.md" | safeURL -}} 36 | {{ $newPageURL := printf "%s/new/%s?%s" $gh_repo (path.Dir $gh_repo_path) $newPageQS -}} 37 | 38 | <a href="{{ $viewURL }}" class="td-page-meta--view td-page-meta__view" target="_blank" rel="noopener"><i class="fa-solid fa-file-lines fa-fw"></i> {{ T "post_view_this" }}</a> 39 | <a href="{{ $editURL }}" class="td-page-meta--edit td-page-meta__edit" target="_blank" rel="noopener"><i class="fa-solid fa-pen-to-square fa-fw"></i> {{ T "post_edit_this" }}</a> 40 | <a href="{{ $newPageURL }}" class="td-page-meta--child td-page-meta__child" target="_blank" rel="noopener"><i class="fa-solid fa-pen-to-square fa-fw"></i> {{ T "post_create_child_page" }}</a> 41 | <a href="{{ $issuesURL }}" class="td-page-meta--issue td-page-meta__issue" target="_blank" rel="noopener"><i class="fa-solid fa-list-check fa-fw"></i> {{ T "post_create_issue" }}</a> 42 | {{ with $gh_project_repo -}} 43 | {{ $project_issueURL := printf "%s/issues/new" . -}} 44 | <a href="{{ $project_issueURL }}" class="td-page-meta--project td-page-meta__project-issue" target="_blank" rel="noopener"><i class="fa-solid fa-list-check fa-fw"></i> {{ T "post_create_project_issue" }}</a> 45 | {{ end -}} 46 | 47 | {{ end -}} 48 | {{ with .CurrentSection.AlternativeOutputFormats.Get "print" -}} 49 | <a id="print" href="{{ .RelPermalink | safeURL }}"><i class="fa-solid fa-print fa-fw"></i> {{ T "print_entire_section" }}</a> 50 | {{ end }} 51 | </div> 52 | {{ end -}} 53 | ``` -------------------------------------------------------------------------------- /internal/sources/neo4j/neo4j.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package neo4j 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "github.com/goccy/go-yaml" 22 | "github.com/googleapis/genai-toolbox/internal/sources" 23 | "github.com/neo4j/neo4j-go-driver/v5/neo4j" 24 | "go.opentelemetry.io/otel/trace" 25 | ) 26 | 27 | const SourceKind string = "neo4j" 28 | 29 | // validate interface 30 | var _ sources.SourceConfig = Config{} 31 | 32 | func init() { 33 | if !sources.Register(SourceKind, newConfig) { 34 | panic(fmt.Sprintf("source kind %q already registered", SourceKind)) 35 | } 36 | } 37 | 38 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { 39 | actual := Config{Name: name, Database: "neo4j"} // Default database 40 | if err := decoder.DecodeContext(ctx, &actual); err != nil { 41 | return nil, err 42 | } 43 | return actual, nil 44 | } 45 | 46 | type Config struct { 47 | Name string `yaml:"name" validate:"required"` 48 | Kind string `yaml:"kind" validate:"required"` 49 | Uri string `yaml:"uri" validate:"required"` 50 | User string `yaml:"user" validate:"required"` 51 | Password string `yaml:"password" validate:"required"` 52 | Database string `yaml:"database" validate:"required"` 53 | } 54 | 55 | func (r Config) SourceConfigKind() string { 56 | return SourceKind 57 | } 58 | 59 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { 60 | driver, err := initNeo4jDriver(ctx, tracer, r.Uri, r.User, r.Password, r.Name) 61 | if err != nil { 62 | return nil, fmt.Errorf("unable to create driver: %w", err) 63 | } 64 | 65 | err = driver.VerifyConnectivity(ctx) 66 | if err != nil { 67 | return nil, fmt.Errorf("unable to connect successfully: %w", err) 68 | } 69 | 70 | if r.Database == "" { 71 | r.Database = "neo4j" 72 | } 73 | s := &Source{ 74 | Name: r.Name, 75 | Kind: SourceKind, 76 | Database: r.Database, 77 | Driver: driver, 78 | } 79 | return s, nil 80 | } 81 | 82 | var _ sources.Source = &Source{} 83 | 84 | type Source struct { 85 | Name string `yaml:"name"` 86 | Kind string `yaml:"kind"` 87 | Database string `yaml:"database"` 88 | Driver neo4j.DriverWithContext 89 | } 90 | 91 | func (s *Source) SourceKind() string { 92 | return SourceKind 93 | } 94 | 95 | func (s *Source) Neo4jDriver() neo4j.DriverWithContext { 96 | return s.Driver 97 | } 98 | 99 | func (s *Source) Neo4jDatabase() string { 100 | return s.Database 101 | } 102 | 103 | func initNeo4jDriver(ctx context.Context, tracer trace.Tracer, uri, user, password, name string) (neo4j.DriverWithContext, error) { 104 | //nolint:all // Reassigned ctx 105 | ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) 106 | defer span.End() 107 | 108 | auth := neo4j.BasicAuth(user, password, "") 109 | driver, err := neo4j.NewDriverWithContext(uri, auth) 110 | if err != nil { 111 | return nil, fmt.Errorf("unable to create connection driver: %w", err) 112 | } 113 | return driver, nil 114 | } 115 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetmodels/lookergetmodels_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetmodels_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetmodels" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetModels(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-models 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-models", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetModels(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-models 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-models\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-models\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/mongodb/mongodbinsertone/mongodbinsertone_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mongodbinsertone_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | "github.com/googleapis/genai-toolbox/internal/tools/mongodb/mongodbinsertone" 22 | 23 | yaml "github.com/goccy/go-yaml" 24 | "github.com/google/go-cmp/cmp" 25 | "github.com/googleapis/genai-toolbox/internal/server" 26 | "github.com/googleapis/genai-toolbox/internal/testutils" 27 | ) 28 | 29 | func TestParseFromYamlMongoQuery(t *testing.T) { 30 | ctx, err := testutils.ContextWithNewLogger() 31 | if err != nil { 32 | t.Fatalf("unexpected error: %s", err) 33 | } 34 | tcs := []struct { 35 | desc string 36 | in string 37 | want server.ToolConfigs 38 | }{ 39 | { 40 | desc: "basic example", 41 | in: ` 42 | tools: 43 | example_tool: 44 | kind: mongodb-insert-one 45 | source: my-instance 46 | description: some description 47 | database: test_db 48 | collection: test_coll 49 | canonical: true 50 | `, 51 | want: server.ToolConfigs{ 52 | "example_tool": mongodbinsertone.Config{ 53 | Name: "example_tool", 54 | Kind: "mongodb-insert-one", 55 | Source: "my-instance", 56 | AuthRequired: []string{}, 57 | Database: "test_db", 58 | Collection: "test_coll", 59 | Canonical: true, 60 | Description: "some description", 61 | }, 62 | }, 63 | }, 64 | } 65 | for _, tc := range tcs { 66 | t.Run(tc.desc, func(t *testing.T) { 67 | got := struct { 68 | Tools server.ToolConfigs `yaml:"tools"` 69 | }{} 70 | // Parse contents 71 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 72 | if err != nil { 73 | t.Fatalf("unable to unmarshal: %s", err) 74 | } 75 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 76 | t.Fatalf("incorrect parse: diff %v", diff) 77 | } 78 | }) 79 | } 80 | 81 | } 82 | 83 | func TestFailParseFromYamlMongoQuery(t *testing.T) { 84 | ctx, err := testutils.ContextWithNewLogger() 85 | if err != nil { 86 | t.Fatalf("unexpected error: %s", err) 87 | } 88 | tcs := []struct { 89 | desc string 90 | in string 91 | err string 92 | }{ 93 | { 94 | desc: "Invalid method", 95 | in: ` 96 | tools: 97 | example_tool: 98 | kind: mongodb-insert-one 99 | source: my-instance 100 | description: some description 101 | collection: test_coll 102 | canonical: true 103 | `, 104 | err: `unable to parse tool "example_tool" as kind "mongodb-insert-one"`, 105 | }, 106 | } 107 | for _, tc := range tcs { 108 | t.Run(tc.desc, func(t *testing.T) { 109 | got := struct { 110 | Tools server.ToolConfigs `yaml:"tools"` 111 | }{} 112 | // Parse contents 113 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 114 | if err == nil { 115 | t.Fatalf("expect parsing to fail") 116 | } 117 | errStr := err.Error() 118 | if !strings.Contains(errStr, tc.err) { 119 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 120 | } 121 | }) 122 | } 123 | 124 | } 125 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetfilters/lookergetfilters_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetfilters_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetfilters" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetFilters(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-filters 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-filters", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetFilters(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-filters 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-filters\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-filters\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetexplores/lookergetexplores_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetexplores_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetexplores" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetExplores(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-explores 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-explores", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetFilters(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-explores 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-explores\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-explores\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetmeasures/lookergetmeasures_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetmeasures_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetmeasures" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetMeasures(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-measures 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-measures", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetMeasures(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-measures 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-measures\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-measures\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /docs/en/resources/sources/postgres.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "PostgreSQL" 3 | type: docs 4 | weight: 1 5 | description: > 6 | PostgreSQL is a powerful, open source object-relational database. 7 | 8 | --- 9 | 10 | ## About 11 | 12 | [PostgreSQL][pg-docs] is a powerful, open source object-relational database 13 | system with over 35 years of active development that has earned it a strong 14 | reputation for reliability, feature robustness, and performance. 15 | 16 | [pg-docs]: https://www.postgresql.org/ 17 | 18 | ## Available Tools 19 | 20 | - [`postgres-sql`](../tools/postgres/postgres-sql.md) 21 | Execute SQL queries as prepared statements in PostgreSQL. 22 | 23 | - [`postgres-execute-sql`](../tools/postgres/postgres-execute-sql.md) 24 | Run parameterized SQL statements in PostgreSQL. 25 | 26 | - [`postgres-list-tables`](../tools/postgres/postgres-list-tables.md) 27 | List tables in a PostgreSQL database. 28 | 29 | - [`postgres-list-active-queries`](../tools/postgres/postgres-list-active-queries.md) 30 | List active queries in a PostgreSQL database. 31 | 32 | - [`postgres-list-available-extensions`](../tools/postgres/postgres-list-available-extensions.md) 33 | List available extensions for installation in a PostgreSQL database. 34 | 35 | - [`postgres-list-installed-extensions`](../tools/postgres/postgres-list-installed-extensions.md) 36 | List installed extensions in a PostgreSQL database. 37 | 38 | ### Pre-built Configurations 39 | 40 | - [PostgreSQL using MCP](https://googleapis.github.io/genai-toolbox/how-to/connect-ide/postgres_mcp/) 41 | Connect your IDE to PostgreSQL using Toolbox. 42 | 43 | ## Requirements 44 | 45 | ### Database User 46 | 47 | This source only uses standard authentication. You will need to [create a 48 | PostgreSQL user][pg-users] to login to the database with. 49 | 50 | [pg-users]: https://www.postgresql.org/docs/current/sql-createuser.html 51 | 52 | ## Example 53 | 54 | ```yaml 55 | sources: 56 | my-pg-source: 57 | kind: postgres 58 | host: 127.0.0.1 59 | port: 5432 60 | database: my_db 61 | user: ${USER_NAME} 62 | password: ${PASSWORD} 63 | ``` 64 | 65 | {{< notice tip >}} 66 | Use environment variable replacement with the format ${ENV_NAME} 67 | instead of hardcoding your secrets into the configuration file. 68 | {{< /notice >}} 69 | 70 | ## Reference 71 | 72 | | **field** | **type** | **required** | **description** | 73 | |-------------|:------------------:|:------------:|------------------------------------------------------------------------| 74 | | kind | string | true | Must be "postgres". | 75 | | host | string | true | IP address to connect to (e.g. "127.0.0.1") | 76 | | port | string | true | Port to connect to (e.g. "5432") | 77 | | database | string | true | Name of the Postgres database to connect to (e.g. "my_db"). | 78 | | user | string | true | Name of the Postgres user to connect as (e.g. "my-pg-user"). | 79 | | password | string | true | Password of the Postgres user (e.g. "my-password"). | 80 | | queryParams | map[string]string | false | Raw query to be added to the db connection string. | 81 | ``` -------------------------------------------------------------------------------- /docs/en/resources/sources/valkey.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "Valkey" 3 | linkTitle: "Valkey" 4 | type: docs 5 | weight: 1 6 | description: > 7 | Valkey is an open-source, in-memory data structure store, forked from Redis. 8 | 9 | --- 10 | 11 | ## About 12 | 13 | Valkey is an open-source, in-memory data structure store that originated as a 14 | fork of Redis. It's designed to be used as a database, cache, and message 15 | broker, supporting a wide range of data structures like strings, hashes, lists, 16 | sets, sorted sets with range queries, bitmaps, hyperloglogs, and geospatial 17 | indexes with radius queries. 18 | 19 | If you're new to Valkey, you can find installation and getting started guides on 20 | the [official Valkey website](https://valkey.io/topics/quickstart/). 21 | 22 | ## Available Tools 23 | 24 | - [`valkey`](../tools/valkey/valkey.md) 25 | Issue Valkey (Redis-compatible) commands. 26 | 27 | ## Example 28 | 29 | ```yaml 30 | sources: 31 | my-valkey-instance: 32 | kind: valkey 33 | address: 34 | - 127.0.0.1:6379 35 | username: ${YOUR_USERNAME} 36 | password: ${YOUR_PASSWORD} 37 | # database: 0 38 | # useGCPIAM: false 39 | # disableCache: false 40 | ``` 41 | 42 | {{< notice tip >}} 43 | Use environment variable replacement with the format ${ENV_NAME} 44 | instead of hardcoding your secrets into the configuration file. 45 | {{< /notice >}} 46 | 47 | ### IAM Authentication 48 | 49 | If you are using GCP's Memorystore for Valkey, you can connect using IAM 50 | authentication. Grant your account the required [IAM role][iam] and set 51 | `useGCPIAM` to `true`: 52 | 53 | ```yaml 54 | sources: 55 | my-valkey-instance: 56 | kind: valkey 57 | address: 58 | - 127.0.0.1:6379 59 | useGCPIAM: true 60 | ``` 61 | 62 | [iam]: https://cloud.google.com/memorystore/docs/valkey/about-iam-auth 63 | 64 | ## Reference 65 | 66 | | **field** | **type** | **required** | **description** | 67 | |--------------|:--------:|:------------:|----------------------------------------------------------------------------------------------------------------------------------| 68 | | kind | string | true | Must be "valkey". | 69 | | address | []string | true | Endpoints for the Valkey instance to connect to. | 70 | | username | string | false | If you are using a non-default user, specify the user name here. If you are using Memorystore for Valkey, leave this field blank | 71 | | password | string | false | Password for the Valkey instance | 72 | | database | int | false | The Valkey database to connect to. Not applicable for cluster enabled instances. The default database is `0`. | 73 | | useGCPIAM | bool | false | Set it to `true` if you are using GCP's IAM authentication. Defaults to `false`. | 74 | | disableCache | bool | false | Set it to `true` if you want to enable client-side caching. Defaults to `false`. | 75 | ``` -------------------------------------------------------------------------------- /docs/en/resources/sources/couchbase.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "couchbase" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "couchbase" source connects to a Couchbase database. 7 | --- 8 | 9 | ## About 10 | 11 | A `couchbase` source establishes a connection to a Couchbase database cluster, 12 | allowing tools to execute SQL queries against it. 13 | 14 | ## Available Tools 15 | 16 | - [`couchbase-sql`](../tools/couchbase/couchbase-sql.md) 17 | Run SQL++ statements on Couchbase with parameterized input. 18 | 19 | ## Example 20 | 21 | ```yaml 22 | sources: 23 | my-couchbase-instance: 24 | kind: couchbase 25 | connectionString: couchbase://localhost 26 | bucket: travel-sample 27 | scope: inventory 28 | username: Administrator 29 | password: password 30 | ``` 31 | 32 | {{< notice note >}} 33 | For more details about alternate addresses and custom ports refer to [Managing Connections](https://docs.couchbase.com/java-sdk/current/howtos/managing-connections.html). 34 | {{< /notice >}} 35 | 36 | ## Reference 37 | 38 | | **field** | **type** | **required** | **description** | 39 | |----------------------|:--------:|:------------:|---------------------------------------------------------| 40 | | kind | string | true | Must be "couchbase". | 41 | | connectionString | string | true | Connection string for the Couchbase cluster. | 42 | | bucket | string | true | Name of the bucket to connect to. | 43 | | scope | string | true | Name of the scope within the bucket. | 44 | | username | string | false | Username for authentication. | 45 | | password | string | false | Password for authentication. | 46 | | clientCert | string | false | Path to client certificate file for TLS authentication. | 47 | | clientCertPassword | string | false | Password for the client certificate. | 48 | | clientKey | string | false | Path to client key file for TLS authentication. | 49 | | clientKeyPassword | string | false | Password for the client key. | 50 | | caCert | string | false | Path to CA certificate file. | 51 | | noSslVerify | boolean | false | If true, skip server certificate verification. **Warning:** This option should only be used in development or testing environments. Disabling SSL verification poses significant security risks in production as it makes your connection vulnerable to man-in-the-middle attacks. | 52 | | profile | string | false | Name of the connection profile to apply. | 53 | | queryScanConsistency | integer | false | Query scan consistency. Controls the consistency guarantee for index scanning. Values: 1 for "not_bounded" (fastest option, but results may not include the most recent operations), 2 for "request_plus" (highest consistency level, includes all operations up until the query started, but incurs a performance penalty). If not specified, defaults to the Couchbase Go SDK default. | 54 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookermakedashboard/lookermakedashboard_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookermakedashboard_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookermakedashboard" 26 | ) 27 | 28 | func TestParseFromYamlLookerMakeDashboard(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-make-dashboard 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-make-dashboard", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlMakeDashboard(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-make-dashboard 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-make-dashboard\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-make-dashboard\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetdashboards/lookergetdashboards_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetdashboards_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetdashboards" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetDashboards(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-dashboards 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-dashboards", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetDashboards(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-dashboards 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-dashboards\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-dashboards\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookergetdimensions/lookergetdimensions_test.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package lookergetdimensions_test 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | 21 | yaml "github.com/goccy/go-yaml" 22 | "github.com/google/go-cmp/cmp" 23 | "github.com/googleapis/genai-toolbox/internal/server" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | lkr "github.com/googleapis/genai-toolbox/internal/tools/looker/lookergetdimensions" 26 | ) 27 | 28 | func TestParseFromYamlLookerGetDimensions(t *testing.T) { 29 | ctx, err := testutils.ContextWithNewLogger() 30 | if err != nil { 31 | t.Fatalf("unexpected error: %s", err) 32 | } 33 | tcs := []struct { 34 | desc string 35 | in string 36 | want server.ToolConfigs 37 | }{ 38 | { 39 | desc: "basic example", 40 | in: ` 41 | tools: 42 | example_tool: 43 | kind: looker-get-dimensions 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lkr.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-get-dimensions", 51 | Source: "my-instance", 52 | Description: "some description", 53 | AuthRequired: []string{}, 54 | }, 55 | }, 56 | }, 57 | } 58 | for _, tc := range tcs { 59 | t.Run(tc.desc, func(t *testing.T) { 60 | got := struct { 61 | Tools server.ToolConfigs `yaml:"tools"` 62 | }{} 63 | // Parse contents 64 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 65 | if err != nil { 66 | t.Fatalf("unable to unmarshal: %s", err) 67 | } 68 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 69 | t.Fatalf("incorrect parse: diff %v", diff) 70 | } 71 | }) 72 | } 73 | 74 | } 75 | 76 | func TestFailParseFromYamlLookerGetDimensions(t *testing.T) { 77 | ctx, err := testutils.ContextWithNewLogger() 78 | if err != nil { 79 | t.Fatalf("unexpected error: %s", err) 80 | } 81 | tcs := []struct { 82 | desc string 83 | in string 84 | err string 85 | }{ 86 | { 87 | desc: "Invalid method", 88 | in: ` 89 | tools: 90 | example_tool: 91 | kind: looker-get-dimensions 92 | source: my-instance 93 | method: GOT 94 | description: some description 95 | `, 96 | err: "unable to parse tool \"example_tool\" as kind \"looker-get-dimensions\": [4:1] unknown field \"method\"\n 1 | authRequired: []\n 2 | description: some description\n 3 | kind: looker-get-dimensions\n> 4 | method: GOT\n ^\n 5 | source: my-instance", 97 | }, 98 | } 99 | for _, tc := range tcs { 100 | t.Run(tc.desc, func(t *testing.T) { 101 | got := struct { 102 | Tools server.ToolConfigs `yaml:"tools"` 103 | }{} 104 | // Parse contents 105 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 106 | if err == nil { 107 | t.Fatalf("expect parsing to fail") 108 | } 109 | errStr := err.Error() 110 | if !strings.Contains(errStr, tc.err) { 111 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 112 | } 113 | }) 114 | } 115 | 116 | } 117 | ```