This is page 5 of 45. Use http://codebase.md/googleapis/genai-toolbox?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .ci │ ├── continuous.release.cloudbuild.yaml │ ├── generate_release_table.sh │ ├── integration.cloudbuild.yaml │ ├── quickstart_test │ │ ├── go.integration.cloudbuild.yaml │ │ ├── js.integration.cloudbuild.yaml │ │ ├── py.integration.cloudbuild.yaml │ │ ├── run_go_tests.sh │ │ ├── run_js_tests.sh │ │ ├── run_py_tests.sh │ │ └── setup_hotels_sample.sql │ ├── test_with_coverage.sh │ └── versioned.release.cloudbuild.yaml ├── .github │ ├── auto-label.yaml │ ├── blunderbuss.yml │ ├── CODEOWNERS │ ├── header-checker-lint.yml │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── label-sync.yml │ ├── labels.yaml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── release-please.yml │ ├── renovate.json5 │ ├── sync-repo-settings.yaml │ └── workflows │ ├── cloud_build_failure_reporter.yml │ ├── deploy_dev_docs.yaml │ ├── deploy_previous_version_docs.yaml │ ├── deploy_versioned_docs.yaml │ ├── docs_deploy.yaml │ ├── docs_preview_clean.yaml │ ├── docs_preview_deploy.yaml │ ├── lint.yaml │ ├── schedule_reporter.yml │ ├── sync-labels.yaml │ └── tests.yaml ├── .gitignore ├── .gitmodules ├── .golangci.yaml ├── .hugo │ ├── archetypes │ │ └── default.md │ ├── assets │ │ ├── icons │ │ │ └── logo.svg │ │ └── scss │ │ ├── _styles_project.scss │ │ └── _variables_project.scss │ ├── go.mod │ ├── go.sum │ ├── hugo.toml │ ├── layouts │ │ ├── _default │ │ │ └── home.releases.releases │ │ ├── index.llms-full.txt │ │ ├── index.llms.txt │ │ ├── partials │ │ │ ├── hooks │ │ │ │ └── head-end.html │ │ │ ├── navbar-version-selector.html │ │ │ ├── page-meta-links.html │ │ │ └── td │ │ │ └── render-heading.html │ │ ├── robot.txt │ │ └── shortcodes │ │ ├── include.html │ │ ├── ipynb.html │ │ └── regionInclude.html │ ├── package-lock.json │ ├── package.json │ └── static │ ├── favicons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ └── favicon.ico │ └── js │ └── w3.js ├── CHANGELOG.md ├── cmd │ ├── options_test.go │ ├── options.go │ ├── root_test.go │ ├── root.go │ └── version.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEVELOPER.md ├── Dockerfile ├── docs │ └── en │ ├── _index.md │ ├── about │ │ ├── _index.md │ │ └── faq.md │ ├── concepts │ │ ├── _index.md │ │ └── telemetry │ │ ├── index.md │ │ ├── telemetry_flow.png │ │ └── telemetry_traces.png │ ├── getting-started │ │ ├── _index.md │ │ ├── colab_quickstart.ipynb │ │ ├── configure.md │ │ ├── introduction │ │ │ ├── _index.md │ │ │ └── architecture.png │ │ ├── local_quickstart_go.md │ │ ├── local_quickstart_js.md │ │ ├── local_quickstart.md │ │ ├── mcp_quickstart │ │ │ ├── _index.md │ │ │ ├── inspector_tools.png │ │ │ └── inspector.png │ │ └── quickstart │ │ ├── go │ │ │ ├── genAI │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ ├── genkit │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ ├── langchain │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ ├── openAI │ │ │ │ ├── go.mod │ │ │ │ ├── go.sum │ │ │ │ └── quickstart.go │ │ │ └── quickstart_test.go │ │ ├── golden.txt │ │ ├── js │ │ │ ├── genAI │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ ├── genkit │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ ├── langchain │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ ├── llamaindex │ │ │ │ ├── package-lock.json │ │ │ │ ├── package.json │ │ │ │ └── quickstart.js │ │ │ └── quickstart.test.js │ │ ├── python │ │ │ ├── __init__.py │ │ │ ├── adk │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ ├── core │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ ├── langchain │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ ├── llamaindex │ │ │ │ ├── quickstart.py │ │ │ │ └── requirements.txt │ │ │ └── quickstart_test.py │ │ └── shared │ │ ├── cloud_setup.md │ │ ├── configure_toolbox.md │ │ └── database_setup.md │ ├── how-to │ │ ├── _index.md │ │ ├── connect_via_geminicli.md │ │ ├── connect_via_mcp.md │ │ ├── connect-ide │ │ │ ├── _index.md │ │ │ ├── alloydb_pg_admin_mcp.md │ │ │ ├── alloydb_pg_mcp.md │ │ │ ├── bigquery_mcp.md │ │ │ ├── cloud_sql_mssql_admin_mcp.md │ │ │ ├── cloud_sql_mssql_mcp.md │ │ │ ├── cloud_sql_mysql_admin_mcp.md │ │ │ ├── cloud_sql_mysql_mcp.md │ │ │ ├── cloud_sql_pg_admin_mcp.md │ │ │ ├── cloud_sql_pg_mcp.md │ │ │ ├── firestore_mcp.md │ │ │ ├── looker_mcp.md │ │ │ ├── mssql_mcp.md │ │ │ ├── mysql_mcp.md │ │ │ ├── neo4j_mcp.md │ │ │ ├── postgres_mcp.md │ │ │ ├── spanner_mcp.md │ │ │ └── sqlite_mcp.md │ │ ├── deploy_docker.md │ │ ├── deploy_gke.md │ │ ├── deploy_toolbox.md │ │ ├── export_telemetry.md │ │ └── toolbox-ui │ │ ├── edit-headers.gif │ │ ├── edit-headers.png │ │ ├── index.md │ │ ├── optional-param-checked.png │ │ ├── optional-param-unchecked.png │ │ ├── run-tool.gif │ │ ├── tools.png │ │ └── toolsets.png │ ├── reference │ │ ├── _index.md │ │ ├── cli.md │ │ └── prebuilt-tools.md │ ├── resources │ │ ├── _index.md │ │ ├── authServices │ │ │ ├── _index.md │ │ │ └── google.md │ │ ├── sources │ │ │ ├── _index.md │ │ │ ├── alloydb-admin.md │ │ │ ├── alloydb-pg.md │ │ │ ├── bigquery.md │ │ │ ├── bigtable.md │ │ │ ├── cassandra.md │ │ │ ├── clickhouse.md │ │ │ ├── cloud-monitoring.md │ │ │ ├── cloud-sql-admin.md │ │ │ ├── cloud-sql-mssql.md │ │ │ ├── cloud-sql-mysql.md │ │ │ ├── cloud-sql-pg.md │ │ │ ├── couchbase.md │ │ │ ├── dataplex.md │ │ │ ├── dgraph.md │ │ │ ├── firebird.md │ │ │ ├── firestore.md │ │ │ ├── http.md │ │ │ ├── looker.md │ │ │ ├── mongodb.md │ │ │ ├── mssql.md │ │ │ ├── mysql.md │ │ │ ├── neo4j.md │ │ │ ├── oceanbase.md │ │ │ ├── oracle.md │ │ │ ├── postgres.md │ │ │ ├── redis.md │ │ │ ├── spanner.md │ │ │ ├── sqlite.md │ │ │ ├── tidb.md │ │ │ ├── trino.md │ │ │ ├── valkey.md │ │ │ └── yugabytedb.md │ │ └── tools │ │ ├── _index.md │ │ ├── alloydb │ │ │ ├── _index.md │ │ │ ├── alloydb-create-cluster.md │ │ │ ├── alloydb-create-instance.md │ │ │ ├── alloydb-create-user.md │ │ │ ├── alloydb-get-cluster.md │ │ │ ├── alloydb-get-instance.md │ │ │ ├── alloydb-get-user.md │ │ │ ├── alloydb-list-clusters.md │ │ │ ├── alloydb-list-instances.md │ │ │ ├── alloydb-list-users.md │ │ │ └── alloydb-wait-for-operation.md │ │ ├── alloydbainl │ │ │ ├── _index.md │ │ │ └── alloydb-ai-nl.md │ │ ├── bigquery │ │ │ ├── _index.md │ │ │ ├── bigquery-analyze-contribution.md │ │ │ ├── bigquery-conversational-analytics.md │ │ │ ├── bigquery-execute-sql.md │ │ │ ├── bigquery-forecast.md │ │ │ ├── bigquery-get-dataset-info.md │ │ │ ├── bigquery-get-table-info.md │ │ │ ├── bigquery-list-dataset-ids.md │ │ │ ├── bigquery-list-table-ids.md │ │ │ ├── bigquery-search-catalog.md │ │ │ └── bigquery-sql.md │ │ ├── bigtable │ │ │ ├── _index.md │ │ │ └── bigtable-sql.md │ │ ├── cassandra │ │ │ ├── _index.md │ │ │ └── cassandra-cql.md │ │ ├── clickhouse │ │ │ ├── _index.md │ │ │ ├── clickhouse-execute-sql.md │ │ │ ├── clickhouse-list-databases.md │ │ │ ├── clickhouse-list-tables.md │ │ │ └── clickhouse-sql.md │ │ ├── cloudmonitoring │ │ │ ├── _index.md │ │ │ └── cloud-monitoring-query-prometheus.md │ │ ├── cloudsql │ │ │ ├── _index.md │ │ │ ├── cloudsqlcreatedatabase.md │ │ │ ├── cloudsqlcreateusers.md │ │ │ ├── cloudsqlgetinstances.md │ │ │ ├── cloudsqllistdatabases.md │ │ │ ├── cloudsqllistinstances.md │ │ │ ├── cloudsqlmssqlcreateinstance.md │ │ │ ├── cloudsqlmysqlcreateinstance.md │ │ │ ├── cloudsqlpgcreateinstances.md │ │ │ └── cloudsqlwaitforoperation.md │ │ ├── couchbase │ │ │ ├── _index.md │ │ │ └── couchbase-sql.md │ │ ├── dataform │ │ │ ├── _index.md │ │ │ └── dataform-compile-local.md │ │ ├── dataplex │ │ │ ├── _index.md │ │ │ ├── dataplex-lookup-entry.md │ │ │ ├── dataplex-search-aspect-types.md │ │ │ └── dataplex-search-entries.md │ │ ├── dgraph │ │ │ ├── _index.md │ │ │ └── dgraph-dql.md │ │ ├── firebird │ │ │ ├── _index.md │ │ │ ├── firebird-execute-sql.md │ │ │ └── firebird-sql.md │ │ ├── firestore │ │ │ ├── _index.md │ │ │ ├── firestore-add-documents.md │ │ │ ├── firestore-delete-documents.md │ │ │ ├── firestore-get-documents.md │ │ │ ├── firestore-get-rules.md │ │ │ ├── firestore-list-collections.md │ │ │ ├── firestore-query-collection.md │ │ │ ├── firestore-query.md │ │ │ ├── firestore-update-document.md │ │ │ └── firestore-validate-rules.md │ │ ├── http │ │ │ ├── _index.md │ │ │ └── http.md │ │ ├── looker │ │ │ ├── _index.md │ │ │ ├── looker-add-dashboard-element.md │ │ │ ├── looker-conversational-analytics.md │ │ │ ├── looker-get-dashboards.md │ │ │ ├── looker-get-dimensions.md │ │ │ ├── looker-get-explores.md │ │ │ ├── looker-get-filters.md │ │ │ ├── looker-get-looks.md │ │ │ ├── looker-get-measures.md │ │ │ ├── looker-get-models.md │ │ │ ├── looker-get-parameters.md │ │ │ ├── looker-health-analyze.md │ │ │ ├── looker-health-pulse.md │ │ │ ├── looker-health-vacuum.md │ │ │ ├── looker-make-dashboard.md │ │ │ ├── looker-make-look.md │ │ │ ├── looker-query-sql.md │ │ │ ├── looker-query-url.md │ │ │ ├── looker-query.md │ │ │ └── looker-run-look.md │ │ ├── mongodb │ │ │ ├── _index.md │ │ │ ├── mongodb-aggregate.md │ │ │ ├── mongodb-delete-many.md │ │ │ ├── mongodb-delete-one.md │ │ │ ├── mongodb-find-one.md │ │ │ ├── mongodb-find.md │ │ │ ├── mongodb-insert-many.md │ │ │ ├── mongodb-insert-one.md │ │ │ ├── mongodb-update-many.md │ │ │ └── mongodb-update-one.md │ │ ├── mssql │ │ │ ├── _index.md │ │ │ ├── mssql-execute-sql.md │ │ │ ├── mssql-list-tables.md │ │ │ └── mssql-sql.md │ │ ├── mysql │ │ │ ├── _index.md │ │ │ ├── mysql-execute-sql.md │ │ │ ├── mysql-list-active-queries.md │ │ │ ├── mysql-list-table-fragmentation.md │ │ │ ├── mysql-list-tables-missing-unique-indexes.md │ │ │ ├── mysql-list-tables.md │ │ │ └── mysql-sql.md │ │ ├── neo4j │ │ │ ├── _index.md │ │ │ ├── neo4j-cypher.md │ │ │ ├── neo4j-execute-cypher.md │ │ │ └── neo4j-schema.md │ │ ├── oceanbase │ │ │ ├── _index.md │ │ │ ├── oceanbase-execute-sql.md │ │ │ └── oceanbase-sql.md │ │ ├── oracle │ │ │ ├── _index.md │ │ │ ├── oracle-execute-sql.md │ │ │ └── oracle-sql.md │ │ ├── postgres │ │ │ ├── _index.md │ │ │ ├── postgres-execute-sql.md │ │ │ ├── postgres-list-active-queries.md │ │ │ ├── postgres-list-available-extensions.md │ │ │ ├── postgres-list-installed-extensions.md │ │ │ ├── postgres-list-tables.md │ │ │ └── postgres-sql.md │ │ ├── redis │ │ │ ├── _index.md │ │ │ └── redis.md │ │ ├── spanner │ │ │ ├── _index.md │ │ │ ├── spanner-execute-sql.md │ │ │ ├── spanner-list-tables.md │ │ │ └── spanner-sql.md │ │ ├── sqlite │ │ │ ├── _index.md │ │ │ ├── sqlite-execute-sql.md │ │ │ └── sqlite-sql.md │ │ ├── tidb │ │ │ ├── _index.md │ │ │ ├── tidb-execute-sql.md │ │ │ └── tidb-sql.md │ │ ├── trino │ │ │ ├── _index.md │ │ │ ├── trino-execute-sql.md │ │ │ └── trino-sql.md │ │ ├── utility │ │ │ ├── _index.md │ │ │ └── wait.md │ │ ├── valkey │ │ │ ├── _index.md │ │ │ └── valkey.md │ │ └── yuagbytedb │ │ ├── _index.md │ │ └── yugabytedb-sql.md │ ├── samples │ │ ├── _index.md │ │ ├── alloydb │ │ │ ├── _index.md │ │ │ ├── ai-nl │ │ │ │ ├── alloydb_ai_nl.ipynb │ │ │ │ └── index.md │ │ │ └── mcp_quickstart.md │ │ ├── bigquery │ │ │ ├── _index.md │ │ │ ├── colab_quickstart_bigquery.ipynb │ │ │ ├── local_quickstart.md │ │ │ └── mcp_quickstart │ │ │ ├── _index.md │ │ │ ├── inspector_tools.png │ │ │ └── inspector.png │ │ └── looker │ │ ├── _index.md │ │ ├── looker_gemini_oauth │ │ │ ├── _index.md │ │ │ ├── authenticated.png │ │ │ ├── authorize.png │ │ │ └── registration.png │ │ ├── looker_gemini.md │ │ └── looker_mcp_inspector │ │ ├── _index.md │ │ ├── inspector_tools.png │ │ └── inspector.png │ └── sdks │ ├── _index.md │ ├── go-sdk.md │ ├── js-sdk.md │ └── python-sdk.md ├── gemini-extension.json ├── go.mod ├── go.sum ├── internal │ ├── auth │ │ ├── auth.go │ │ └── google │ │ └── google.go │ ├── log │ │ ├── handler.go │ │ ├── log_test.go │ │ ├── log.go │ │ └── logger.go │ ├── prebuiltconfigs │ │ ├── prebuiltconfigs_test.go │ │ ├── prebuiltconfigs.go │ │ └── tools │ │ ├── alloydb-postgres-admin.yaml │ │ ├── alloydb-postgres-observability.yaml │ │ ├── alloydb-postgres.yaml │ │ ├── bigquery.yaml │ │ ├── clickhouse.yaml │ │ ├── cloud-sql-mssql-admin.yaml │ │ ├── cloud-sql-mssql-observability.yaml │ │ ├── cloud-sql-mssql.yaml │ │ ├── cloud-sql-mysql-admin.yaml │ │ ├── cloud-sql-mysql-observability.yaml │ │ ├── cloud-sql-mysql.yaml │ │ ├── cloud-sql-postgres-admin.yaml │ │ ├── cloud-sql-postgres-observability.yaml │ │ ├── cloud-sql-postgres.yaml │ │ ├── dataplex.yaml │ │ ├── firestore.yaml │ │ ├── looker-conversational-analytics.yaml │ │ ├── looker.yaml │ │ ├── mssql.yaml │ │ ├── mysql.yaml │ │ ├── neo4j.yaml │ │ ├── oceanbase.yaml │ │ ├── postgres.yaml │ │ ├── spanner-postgres.yaml │ │ ├── spanner.yaml │ │ └── sqlite.yaml │ ├── server │ │ ├── api_test.go │ │ ├── api.go │ │ ├── common_test.go │ │ ├── config.go │ │ ├── mcp │ │ │ ├── jsonrpc │ │ │ │ ├── jsonrpc_test.go │ │ │ │ └── jsonrpc.go │ │ │ ├── mcp.go │ │ │ ├── util │ │ │ │ └── lifecycle.go │ │ │ ├── v20241105 │ │ │ │ ├── method.go │ │ │ │ └── types.go │ │ │ ├── v20250326 │ │ │ │ ├── method.go │ │ │ │ └── types.go │ │ │ └── v20250618 │ │ │ ├── method.go │ │ │ └── types.go │ │ ├── mcp_test.go │ │ ├── mcp.go │ │ ├── server_test.go │ │ ├── server.go │ │ ├── static │ │ │ ├── assets │ │ │ │ └── mcptoolboxlogo.png │ │ │ ├── css │ │ │ │ └── style.css │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ ├── auth.js │ │ │ │ ├── loadTools.js │ │ │ │ ├── mainContent.js │ │ │ │ ├── navbar.js │ │ │ │ ├── runTool.js │ │ │ │ ├── toolDisplay.js │ │ │ │ ├── tools.js │ │ │ │ └── toolsets.js │ │ │ ├── tools.html │ │ │ └── toolsets.html │ │ ├── web_test.go │ │ └── web.go │ ├── sources │ │ ├── alloydbadmin │ │ │ ├── alloydbadmin_test.go │ │ │ └── alloydbadmin.go │ │ ├── alloydbpg │ │ │ ├── alloydb_pg_test.go │ │ │ └── alloydb_pg.go │ │ ├── bigquery │ │ │ ├── bigquery_test.go │ │ │ └── bigquery.go │ │ ├── bigtable │ │ │ ├── bigtable_test.go │ │ │ └── bigtable.go │ │ ├── cassandra │ │ │ ├── cassandra_test.go │ │ │ └── cassandra.go │ │ ├── clickhouse │ │ │ ├── clickhouse_test.go │ │ │ └── clickhouse.go │ │ ├── cloudmonitoring │ │ │ ├── cloud_monitoring_test.go │ │ │ └── cloud_monitoring.go │ │ ├── cloudsqladmin │ │ │ ├── cloud_sql_admin_test.go │ │ │ └── cloud_sql_admin.go │ │ ├── cloudsqlmssql │ │ │ ├── cloud_sql_mssql_test.go │ │ │ └── cloud_sql_mssql.go │ │ ├── cloudsqlmysql │ │ │ ├── cloud_sql_mysql_test.go │ │ │ └── cloud_sql_mysql.go │ │ ├── cloudsqlpg │ │ │ ├── cloud_sql_pg_test.go │ │ │ └── cloud_sql_pg.go │ │ ├── couchbase │ │ │ ├── couchbase_test.go │ │ │ └── couchbase.go │ │ ├── dataplex │ │ │ ├── dataplex_test.go │ │ │ └── dataplex.go │ │ ├── dgraph │ │ │ ├── dgraph_test.go │ │ │ └── dgraph.go │ │ ├── dialect.go │ │ ├── firebird │ │ │ ├── firebird_test.go │ │ │ └── firebird.go │ │ ├── firestore │ │ │ ├── firestore_test.go │ │ │ └── firestore.go │ │ ├── http │ │ │ ├── http_test.go │ │ │ └── http.go │ │ ├── ip_type.go │ │ ├── looker │ │ │ ├── looker_test.go │ │ │ └── looker.go │ │ ├── mongodb │ │ │ ├── mongodb_test.go │ │ │ └── mongodb.go │ │ ├── mssql │ │ │ ├── mssql_test.go │ │ │ └── mssql.go │ │ ├── mysql │ │ │ ├── mysql_test.go │ │ │ └── mysql.go │ │ ├── neo4j │ │ │ ├── neo4j_test.go │ │ │ └── neo4j.go │ │ ├── oceanbase │ │ │ ├── oceanbase_test.go │ │ │ └── oceanbase.go │ │ ├── oracle │ │ │ └── oracle.go │ │ ├── postgres │ │ │ ├── postgres_test.go │ │ │ └── postgres.go │ │ ├── redis │ │ │ ├── redis_test.go │ │ │ └── redis.go │ │ ├── sources.go │ │ ├── spanner │ │ │ ├── spanner_test.go │ │ │ └── spanner.go │ │ ├── sqlite │ │ │ ├── sqlite_test.go │ │ │ └── sqlite.go │ │ ├── tidb │ │ │ ├── tidb_test.go │ │ │ └── tidb.go │ │ ├── trino │ │ │ ├── trino_test.go │ │ │ └── trino.go │ │ ├── util.go │ │ ├── valkey │ │ │ ├── valkey_test.go │ │ │ └── valkey.go │ │ └── yugabytedb │ │ ├── yugabytedb_test.go │ │ └── yugabytedb.go │ ├── telemetry │ │ ├── instrumentation.go │ │ └── telemetry.go │ ├── testutils │ │ └── testutils.go │ ├── tools │ │ ├── alloydb │ │ │ ├── alloydbcreatecluster │ │ │ │ ├── alloydbcreatecluster_test.go │ │ │ │ └── alloydbcreatecluster.go │ │ │ ├── alloydbcreateinstance │ │ │ │ ├── alloydbcreateinstance_test.go │ │ │ │ └── alloydbcreateinstance.go │ │ │ ├── alloydbcreateuser │ │ │ │ ├── alloydbcreateuser_test.go │ │ │ │ └── alloydbcreateuser.go │ │ │ ├── alloydbgetcluster │ │ │ │ ├── alloydbgetcluster_test.go │ │ │ │ └── alloydbgetcluster.go │ │ │ ├── alloydbgetinstance │ │ │ │ ├── alloydbgetinstance_test.go │ │ │ │ └── alloydbgetinstance.go │ │ │ ├── alloydbgetuser │ │ │ │ ├── alloydbgetuser_test.go │ │ │ │ └── alloydbgetuser.go │ │ │ ├── alloydblistclusters │ │ │ │ ├── alloydblistclusters_test.go │ │ │ │ └── alloydblistclusters.go │ │ │ ├── alloydblistinstances │ │ │ │ ├── alloydblistinstances_test.go │ │ │ │ └── alloydblistinstances.go │ │ │ ├── alloydblistusers │ │ │ │ ├── alloydblistusers_test.go │ │ │ │ └── alloydblistusers.go │ │ │ └── alloydbwaitforoperation │ │ │ ├── alloydbwaitforoperation_test.go │ │ │ └── alloydbwaitforoperation.go │ │ ├── alloydbainl │ │ │ ├── alloydbainl_test.go │ │ │ └── alloydbainl.go │ │ ├── bigquery │ │ │ ├── bigqueryanalyzecontribution │ │ │ │ ├── bigqueryanalyzecontribution_test.go │ │ │ │ └── bigqueryanalyzecontribution.go │ │ │ ├── bigquerycommon │ │ │ │ ├── table_name_parser_test.go │ │ │ │ ├── table_name_parser.go │ │ │ │ └── util.go │ │ │ ├── bigqueryconversationalanalytics │ │ │ │ ├── bigqueryconversationalanalytics_test.go │ │ │ │ └── bigqueryconversationalanalytics.go │ │ │ ├── bigqueryexecutesql │ │ │ │ ├── bigqueryexecutesql_test.go │ │ │ │ └── bigqueryexecutesql.go │ │ │ ├── bigqueryforecast │ │ │ │ ├── bigqueryforecast_test.go │ │ │ │ └── bigqueryforecast.go │ │ │ ├── bigquerygetdatasetinfo │ │ │ │ ├── bigquerygetdatasetinfo_test.go │ │ │ │ └── bigquerygetdatasetinfo.go │ │ │ ├── bigquerygettableinfo │ │ │ │ ├── bigquerygettableinfo_test.go │ │ │ │ └── bigquerygettableinfo.go │ │ │ ├── bigquerylistdatasetids │ │ │ │ ├── bigquerylistdatasetids_test.go │ │ │ │ └── bigquerylistdatasetids.go │ │ │ ├── bigquerylisttableids │ │ │ │ ├── bigquerylisttableids_test.go │ │ │ │ └── bigquerylisttableids.go │ │ │ ├── bigquerysearchcatalog │ │ │ │ ├── bigquerysearchcatalog_test.go │ │ │ │ └── bigquerysearchcatalog.go │ │ │ └── bigquerysql │ │ │ ├── bigquerysql_test.go │ │ │ └── bigquerysql.go │ │ ├── bigtable │ │ │ ├── bigtable_test.go │ │ │ └── bigtable.go │ │ ├── cassandra │ │ │ └── cassandracql │ │ │ ├── cassandracql_test.go │ │ │ └── cassandracql.go │ │ ├── clickhouse │ │ │ ├── clickhouseexecutesql │ │ │ │ ├── clickhouseexecutesql_test.go │ │ │ │ └── clickhouseexecutesql.go │ │ │ ├── clickhouselistdatabases │ │ │ │ ├── clickhouselistdatabases_test.go │ │ │ │ └── clickhouselistdatabases.go │ │ │ ├── clickhouselisttables │ │ │ │ ├── clickhouselisttables_test.go │ │ │ │ └── clickhouselisttables.go │ │ │ └── clickhousesql │ │ │ ├── clickhousesql_test.go │ │ │ └── clickhousesql.go │ │ ├── cloudmonitoring │ │ │ ├── cloudmonitoring_test.go │ │ │ └── cloudmonitoring.go │ │ ├── cloudsql │ │ │ ├── cloudsqlcreatedatabase │ │ │ │ ├── cloudsqlcreatedatabase_test.go │ │ │ │ └── cloudsqlcreatedatabase.go │ │ │ ├── cloudsqlcreateusers │ │ │ │ ├── cloudsqlcreateusers_test.go │ │ │ │ └── cloudsqlcreateusers.go │ │ │ ├── cloudsqlgetinstances │ │ │ │ ├── cloudsqlgetinstances_test.go │ │ │ │ └── cloudsqlgetinstances.go │ │ │ ├── cloudsqllistdatabases │ │ │ │ ├── cloudsqllistdatabases_test.go │ │ │ │ └── cloudsqllistdatabases.go │ │ │ ├── cloudsqllistinstances │ │ │ │ ├── cloudsqllistinstances_test.go │ │ │ │ └── cloudsqllistinstances.go │ │ │ └── cloudsqlwaitforoperation │ │ │ ├── cloudsqlwaitforoperation_test.go │ │ │ └── cloudsqlwaitforoperation.go │ │ ├── cloudsqlmssql │ │ │ └── cloudsqlmssqlcreateinstance │ │ │ ├── cloudsqlmssqlcreateinstance_test.go │ │ │ └── cloudsqlmssqlcreateinstance.go │ │ ├── cloudsqlmysql │ │ │ └── cloudsqlmysqlcreateinstance │ │ │ ├── cloudsqlmysqlcreateinstance_test.go │ │ │ └── cloudsqlmysqlcreateinstance.go │ │ ├── cloudsqlpg │ │ │ └── cloudsqlpgcreateinstances │ │ │ ├── cloudsqlpgcreateinstances_test.go │ │ │ └── cloudsqlpgcreateinstances.go │ │ ├── common_test.go │ │ ├── common.go │ │ ├── couchbase │ │ │ ├── couchbase_test.go │ │ │ └── couchbase.go │ │ ├── dataform │ │ │ └── dataformcompilelocal │ │ │ ├── dataformcompilelocal_test.go │ │ │ └── dataformcompilelocal.go │ │ ├── dataplex │ │ │ ├── dataplexlookupentry │ │ │ │ ├── dataplexlookupentry_test.go │ │ │ │ └── dataplexlookupentry.go │ │ │ ├── dataplexsearchaspecttypes │ │ │ │ ├── dataplexsearchaspecttypes_test.go │ │ │ │ └── dataplexsearchaspecttypes.go │ │ │ └── dataplexsearchentries │ │ │ ├── dataplexsearchentries_test.go │ │ │ └── dataplexsearchentries.go │ │ ├── dgraph │ │ │ ├── dgraph_test.go │ │ │ └── dgraph.go │ │ ├── firebird │ │ │ ├── firebirdexecutesql │ │ │ │ ├── firebirdexecutesql_test.go │ │ │ │ └── firebirdexecutesql.go │ │ │ └── firebirdsql │ │ │ ├── firebirdsql_test.go │ │ │ └── firebirdsql.go │ │ ├── firestore │ │ │ ├── firestoreadddocuments │ │ │ │ ├── firestoreadddocuments_test.go │ │ │ │ └── firestoreadddocuments.go │ │ │ ├── firestoredeletedocuments │ │ │ │ ├── firestoredeletedocuments_test.go │ │ │ │ └── firestoredeletedocuments.go │ │ │ ├── firestoregetdocuments │ │ │ │ ├── firestoregetdocuments_test.go │ │ │ │ └── firestoregetdocuments.go │ │ │ ├── firestoregetrules │ │ │ │ ├── firestoregetrules_test.go │ │ │ │ └── firestoregetrules.go │ │ │ ├── firestorelistcollections │ │ │ │ ├── firestorelistcollections_test.go │ │ │ │ └── firestorelistcollections.go │ │ │ ├── firestorequery │ │ │ │ ├── firestorequery_test.go │ │ │ │ └── firestorequery.go │ │ │ ├── firestorequerycollection │ │ │ │ ├── firestorequerycollection_test.go │ │ │ │ └── firestorequerycollection.go │ │ │ ├── firestoreupdatedocument │ │ │ │ ├── firestoreupdatedocument_test.go │ │ │ │ └── firestoreupdatedocument.go │ │ │ ├── firestorevalidaterules │ │ │ │ ├── firestorevalidaterules_test.go │ │ │ │ └── firestorevalidaterules.go │ │ │ └── util │ │ │ ├── converter_test.go │ │ │ ├── converter.go │ │ │ ├── validator_test.go │ │ │ └── validator.go │ │ ├── http │ │ │ ├── http_test.go │ │ │ └── http.go │ │ ├── http_method.go │ │ ├── looker │ │ │ ├── lookeradddashboardelement │ │ │ │ ├── lookeradddashboardelement_test.go │ │ │ │ └── lookeradddashboardelement.go │ │ │ ├── lookercommon │ │ │ │ ├── lookercommon_test.go │ │ │ │ └── lookercommon.go │ │ │ ├── lookerconversationalanalytics │ │ │ │ ├── lookerconversationalanalytics_test.go │ │ │ │ └── lookerconversationalanalytics.go │ │ │ ├── lookergetdashboards │ │ │ │ ├── lookergetdashboards_test.go │ │ │ │ └── lookergetdashboards.go │ │ │ ├── lookergetdimensions │ │ │ │ ├── lookergetdimensions_test.go │ │ │ │ └── lookergetdimensions.go │ │ │ ├── lookergetexplores │ │ │ │ ├── lookergetexplores_test.go │ │ │ │ └── lookergetexplores.go │ │ │ ├── lookergetfilters │ │ │ │ ├── lookergetfilters_test.go │ │ │ │ └── lookergetfilters.go │ │ │ ├── lookergetlooks │ │ │ │ ├── lookergetlooks_test.go │ │ │ │ └── lookergetlooks.go │ │ │ ├── lookergetmeasures │ │ │ │ ├── lookergetmeasures_test.go │ │ │ │ └── lookergetmeasures.go │ │ │ ├── lookergetmodels │ │ │ │ ├── lookergetmodels_test.go │ │ │ │ └── lookergetmodels.go │ │ │ ├── lookergetparameters │ │ │ │ ├── lookergetparameters_test.go │ │ │ │ └── lookergetparameters.go │ │ │ ├── lookerhealthanalyze │ │ │ │ ├── lookerhealthanalyze_test.go │ │ │ │ └── lookerhealthanalyze.go │ │ │ ├── lookerhealthpulse │ │ │ │ ├── lookerhealthpulse_test.go │ │ │ │ └── lookerhealthpulse.go │ │ │ ├── lookerhealthvacuum │ │ │ │ ├── lookerhealthvacuum_test.go │ │ │ │ └── lookerhealthvacuum.go │ │ │ ├── lookermakedashboard │ │ │ │ ├── lookermakedashboard_test.go │ │ │ │ └── lookermakedashboard.go │ │ │ ├── lookermakelook │ │ │ │ ├── lookermakelook_test.go │ │ │ │ └── lookermakelook.go │ │ │ ├── lookerquery │ │ │ │ ├── lookerquery_test.go │ │ │ │ └── lookerquery.go │ │ │ ├── lookerquerysql │ │ │ │ ├── lookerquerysql_test.go │ │ │ │ └── lookerquerysql.go │ │ │ ├── lookerqueryurl │ │ │ │ ├── lookerqueryurl_test.go │ │ │ │ └── lookerqueryurl.go │ │ │ └── lookerrunlook │ │ │ ├── lookerrunlook_test.go │ │ │ └── lookerrunlook.go │ │ ├── mongodb │ │ │ ├── mongodbaggregate │ │ │ │ ├── mongodbaggregate_test.go │ │ │ │ └── mongodbaggregate.go │ │ │ ├── mongodbdeletemany │ │ │ │ ├── mongodbdeletemany_test.go │ │ │ │ └── mongodbdeletemany.go │ │ │ ├── mongodbdeleteone │ │ │ │ ├── mongodbdeleteone_test.go │ │ │ │ └── mongodbdeleteone.go │ │ │ ├── mongodbfind │ │ │ │ ├── mongodbfind_test.go │ │ │ │ └── mongodbfind.go │ │ │ ├── mongodbfindone │ │ │ │ ├── mongodbfindone_test.go │ │ │ │ └── mongodbfindone.go │ │ │ ├── mongodbinsertmany │ │ │ │ ├── mongodbinsertmany_test.go │ │ │ │ └── mongodbinsertmany.go │ │ │ ├── mongodbinsertone │ │ │ │ ├── mongodbinsertone_test.go │ │ │ │ └── mongodbinsertone.go │ │ │ ├── mongodbupdatemany │ │ │ │ ├── mongodbupdatemany_test.go │ │ │ │ └── mongodbupdatemany.go │ │ │ └── mongodbupdateone │ │ │ ├── mongodbupdateone_test.go │ │ │ └── mongodbupdateone.go │ │ ├── mssql │ │ │ ├── mssqlexecutesql │ │ │ │ ├── mssqlexecutesql_test.go │ │ │ │ └── mssqlexecutesql.go │ │ │ ├── mssqllisttables │ │ │ │ ├── mssqllisttables_test.go │ │ │ │ └── mssqllisttables.go │ │ │ └── mssqlsql │ │ │ ├── mssqlsql_test.go │ │ │ └── mssqlsql.go │ │ ├── mysql │ │ │ ├── mysqlcommon │ │ │ │ └── mysqlcommon.go │ │ │ ├── mysqlexecutesql │ │ │ │ ├── mysqlexecutesql_test.go │ │ │ │ └── mysqlexecutesql.go │ │ │ ├── mysqllistactivequeries │ │ │ │ ├── mysqllistactivequeries_test.go │ │ │ │ └── mysqllistactivequeries.go │ │ │ ├── mysqllisttablefragmentation │ │ │ │ ├── mysqllisttablefragmentation_test.go │ │ │ │ └── mysqllisttablefragmentation.go │ │ │ ├── mysqllisttables │ │ │ │ ├── mysqllisttables_test.go │ │ │ │ └── mysqllisttables.go │ │ │ ├── mysqllisttablesmissinguniqueindexes │ │ │ │ ├── mysqllisttablesmissinguniqueindexes_test.go │ │ │ │ └── mysqllisttablesmissinguniqueindexes.go │ │ │ └── mysqlsql │ │ │ ├── mysqlsql_test.go │ │ │ └── mysqlsql.go │ │ ├── neo4j │ │ │ ├── neo4jcypher │ │ │ │ ├── neo4jcypher_test.go │ │ │ │ └── neo4jcypher.go │ │ │ ├── neo4jexecutecypher │ │ │ │ ├── classifier │ │ │ │ │ ├── classifier_test.go │ │ │ │ │ └── classifier.go │ │ │ │ ├── neo4jexecutecypher_test.go │ │ │ │ └── neo4jexecutecypher.go │ │ │ └── neo4jschema │ │ │ ├── cache │ │ │ │ ├── cache_test.go │ │ │ │ └── cache.go │ │ │ ├── helpers │ │ │ │ ├── helpers_test.go │ │ │ │ └── helpers.go │ │ │ ├── neo4jschema_test.go │ │ │ ├── neo4jschema.go │ │ │ └── types │ │ │ └── types.go │ │ ├── oceanbase │ │ │ ├── oceanbaseexecutesql │ │ │ │ ├── oceanbaseexecutesql_test.go │ │ │ │ └── oceanbaseexecutesql.go │ │ │ └── oceanbasesql │ │ │ ├── oceanbasesql_test.go │ │ │ └── oceanbasesql.go │ │ ├── oracle │ │ │ ├── oracleexecutesql │ │ │ │ └── oracleexecutesql.go │ │ │ └── oraclesql │ │ │ └── oraclesql.go │ │ ├── parameters_test.go │ │ ├── parameters.go │ │ ├── postgres │ │ │ ├── postgresexecutesql │ │ │ │ ├── postgresexecutesql_test.go │ │ │ │ └── postgresexecutesql.go │ │ │ ├── postgreslistactivequeries │ │ │ │ ├── postgreslistactivequeries_test.go │ │ │ │ └── postgreslistactivequeries.go │ │ │ ├── postgreslistavailableextensions │ │ │ │ ├── postgreslistavailableextensions_test.go │ │ │ │ └── postgreslistavailableextensions.go │ │ │ ├── postgreslistinstalledextensions │ │ │ │ ├── postgreslistinstalledextensions_test.go │ │ │ │ └── postgreslistinstalledextensions.go │ │ │ ├── postgreslisttables │ │ │ │ ├── postgreslisttables_test.go │ │ │ │ └── postgreslisttables.go │ │ │ └── postgressql │ │ │ ├── postgressql_test.go │ │ │ └── postgressql.go │ │ ├── redis │ │ │ ├── redis_test.go │ │ │ └── redis.go │ │ ├── spanner │ │ │ ├── spannerexecutesql │ │ │ │ ├── spannerexecutesql_test.go │ │ │ │ └── spannerexecutesql.go │ │ │ ├── spannerlisttables │ │ │ │ ├── spannerlisttables_test.go │ │ │ │ └── spannerlisttables.go │ │ │ └── spannersql │ │ │ ├── spanner_test.go │ │ │ └── spannersql.go │ │ ├── sqlite │ │ │ ├── sqliteexecutesql │ │ │ │ ├── sqliteexecutesql_test.go │ │ │ │ └── sqliteexecutesql.go │ │ │ └── sqlitesql │ │ │ ├── sqlitesql_test.go │ │ │ └── sqlitesql.go │ │ ├── tidb │ │ │ ├── tidbexecutesql │ │ │ │ ├── tidbexecutesql_test.go │ │ │ │ └── tidbexecutesql.go │ │ │ └── tidbsql │ │ │ ├── tidbsql_test.go │ │ │ └── tidbsql.go │ │ ├── tools_test.go │ │ ├── tools.go │ │ ├── toolsets.go │ │ ├── trino │ │ │ ├── trinoexecutesql │ │ │ │ ├── trinoexecutesql_test.go │ │ │ │ └── trinoexecutesql.go │ │ │ └── trinosql │ │ │ ├── trinosql_test.go │ │ │ └── trinosql.go │ │ ├── utility │ │ │ └── wait │ │ │ ├── wait_test.go │ │ │ └── wait.go │ │ ├── valkey │ │ │ ├── valkey_test.go │ │ │ └── valkey.go │ │ └── yugabytedbsql │ │ ├── yugabytedbsql_test.go │ │ └── yugabytedbsql.go │ └── util │ └── util.go ├── LICENSE ├── logo.png ├── main.go ├── MCP-TOOLBOX-EXTENSION.md ├── README.md └── tests ├── alloydb │ ├── alloydb_integration_test.go │ └── alloydb_wait_for_operation_test.go ├── alloydbainl │ └── alloydb_ai_nl_integration_test.go ├── alloydbpg │ └── alloydb_pg_integration_test.go ├── auth.go ├── bigquery │ └── bigquery_integration_test.go ├── bigtable │ └── bigtable_integration_test.go ├── cassandra │ └── cassandra_integration_test.go ├── clickhouse │ └── clickhouse_integration_test.go ├── cloudmonitoring │ └── cloud_monitoring_integration_test.go ├── cloudsql │ ├── cloud_sql_create_database_test.go │ ├── cloud_sql_create_users_test.go │ ├── cloud_sql_get_instances_test.go │ ├── cloud_sql_list_databases_test.go │ ├── cloudsql_list_instances_test.go │ └── cloudsql_wait_for_operation_test.go ├── cloudsqlmssql │ ├── cloud_sql_mssql_create_instance_integration_test.go │ └── cloud_sql_mssql_integration_test.go ├── cloudsqlmysql │ ├── cloud_sql_mysql_create_instance_integration_test.go │ └── cloud_sql_mysql_integration_test.go ├── cloudsqlpg │ ├── cloud_sql_pg_create_instances_test.go │ └── cloud_sql_pg_integration_test.go ├── common.go ├── couchbase │ └── couchbase_integration_test.go ├── dataform │ └── dataform_integration_test.go ├── dataplex │ └── dataplex_integration_test.go ├── dgraph │ └── dgraph_integration_test.go ├── firebird │ └── firebird_integration_test.go ├── firestore │ └── firestore_integration_test.go ├── http │ └── http_integration_test.go ├── looker │ └── looker_integration_test.go ├── mongodb │ └── mongodb_integration_test.go ├── mssql │ └── mssql_integration_test.go ├── mysql │ └── mysql_integration_test.go ├── neo4j │ └── neo4j_integration_test.go ├── oceanbase │ └── oceanbase_integration_test.go ├── option.go ├── oracle │ └── oracle_integration_test.go ├── postgres │ └── postgres_integration_test.go ├── redis │ └── redis_test.go ├── server.go ├── source.go ├── spanner │ └── spanner_integration_test.go ├── sqlite │ └── sqlite_integration_test.go ├── tidb │ └── tidb_integration_test.go ├── tool.go ├── trino │ └── trino_integration_test.go ├── utility │ └── wait_integration_test.go ├── valkey │ └── valkey_test.go └── yugabytedb └── yugabytedb_integration_test.go ``` # Files -------------------------------------------------------------------------------- /internal/prebuiltconfigs/prebuiltconfigs.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 prebuiltconfigs 16 | 17 | import ( 18 | "embed" 19 | "fmt" 20 | "path" 21 | "strings" 22 | ) 23 | 24 | var ( 25 | //go:embed tools/*.yaml 26 | prebuiltConfigsFS embed.FS 27 | 28 | // Map of sources to their prebuilt tools 29 | prebuiltToolYAMLs map[string][]byte 30 | // List of sources with prebuilt tools 31 | prebuiltToolsSources []string 32 | ) 33 | 34 | func init() { 35 | var err error 36 | prebuiltToolYAMLs, prebuiltToolsSources, err = loadPrebuiltToolYAMLs() 37 | if err != nil { 38 | panic(fmt.Sprintf("Unexpected Error: %v\n", err)) 39 | } 40 | } 41 | 42 | // Getter for the prebuiltToolsSources 43 | func GetPrebuiltSources() []string { 44 | return prebuiltToolsSources 45 | } 46 | 47 | // Get prebuilt tools for a source 48 | func Get(prebuiltSourceConfig string) ([]byte, error) { 49 | content, ok := prebuiltToolYAMLs[prebuiltSourceConfig] 50 | if !ok { 51 | prebuiltHelpSuffix := "no prebuilt configurations found." 52 | if len(prebuiltToolsSources) > 0 { 53 | prebuiltHelpSuffix = fmt.Sprintf("available: %s", strings.Join(prebuiltToolsSources, ", ")) 54 | } 55 | errMsg := fmt.Errorf("prebuilt source tool for '%s' not found. %s", prebuiltSourceConfig, prebuiltHelpSuffix) 56 | return nil, errMsg 57 | } 58 | return content, nil 59 | } 60 | 61 | // Load all available pre built tools 62 | func loadPrebuiltToolYAMLs() (map[string][]byte, []string, error) { 63 | toolYAMLs := make(map[string][]byte) 64 | var sourceTypes []string 65 | entries, err := prebuiltConfigsFS.ReadDir("tools") 66 | if err != nil { 67 | errMsg := fmt.Errorf("failed to read prebuilt tools %w", err) 68 | return nil, nil, errMsg 69 | } 70 | 71 | for _, entry := range entries { 72 | lowerName := strings.ToLower(entry.Name()) 73 | if !entry.IsDir() && (strings.HasSuffix(lowerName, ".yaml")) { 74 | filePathInFS := path.Join("tools", entry.Name()) 75 | content, err := prebuiltConfigsFS.ReadFile(filePathInFS) 76 | if err != nil { 77 | errMsg := fmt.Errorf("failed to read a prebuilt tool %w", err) 78 | return nil, nil, errMsg 79 | } 80 | sourceTypeKey := entry.Name()[:len(entry.Name())-len(".yaml")] 81 | 82 | sourceTypes = append(sourceTypes, sourceTypeKey) 83 | toolYAMLs[sourceTypeKey] = content 84 | } 85 | } 86 | if len(toolYAMLs) == 0 { 87 | errMsg := fmt.Errorf("no prebuilt tool configurations were loaded.%w", err) 88 | return nil, nil, errMsg 89 | } 90 | 91 | return toolYAMLs, sourceTypes, nil 92 | } 93 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mysql/mysql-list-table-fragmentation.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "mysql-list-table-fragmentation" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "mysql-list-table-fragmentation" tool lists top N fragemented tables in MySQL. 7 | aliases: 8 | - /resources/tools/mysql-list-table-fragmentation 9 | --- 10 | 11 | ## About 12 | 13 | A `mysql-list-table-fragmentation` tool checks table fragmentation of MySQL 14 | tables by calculating the size of the data and index files in bytes and 15 | comparing with free space allocated to each table. This tool calculates 16 | `fragmentation_percentage` which represents the proportion of free space 17 | relative to the total data and index size. It's compatible with 18 | 19 | - [cloud-sql-mysql](../../sources/cloud-sql-mysql.md) 20 | - [mysql](../../sources/mysql.md) 21 | 22 | `mysql-list-table-fragmentation` outputs detailed information as JSON , ordered 23 | by the fragmentation percentage in descending order. 24 | This tool takes 4 optional input parameters: 25 | 26 | - `table_schema` (optional): The database where fragmentation check is to be 27 | executed. Check all tables visible to the current user if not specified. 28 | - `table_name` (optional): Name of the table to be checked. Check all tables 29 | visible to the current user if not specified. 30 | - `data_free_threshold_bytes` (optional): Only show tables with at least this 31 | much free space in bytes. Default 1. 32 | - `limit` (optional): Max rows to return, default 10. 33 | 34 | ## Example 35 | 36 | ```yaml 37 | tools: 38 | list_table_fragmentation: 39 | kind: mysql-list-table-fragmentation 40 | source: my-mysql-instance 41 | description: List table fragmentation in MySQL, by calculating the size of the data and index files and free space allocated to each table. The query calculates fragmentation percentage which represents the proportion of free space relative to the total data and index size. Storage can be reclaimed for tables with high fragmentation using OPTIMIZE TABLE. 42 | ``` 43 | The response is a json array with the following fields: 44 | ```json 45 | { 46 | "table_schema": "The schema/database this table belongs to", 47 | "table_name": "Name of this table", 48 | "data_size": "Size of the table data in bytes", 49 | "index_size": "Size of the table's indexes in bytes", 50 | "data_free": "Free space (bytes) available in the table's data file", 51 | "fragmentation_percentage": "How much fragementation this table has", 52 | } 53 | ``` 54 | 55 | ## Reference 56 | 57 | | **field** | **type** | **required** | **description** | 58 | |-------------|:--------:|:------------:|----------------------------------------------------| 59 | | kind | string | true | Must be "mysql-list-table-fragmentation". | 60 | | source | string | true | Name of the source the SQL should execute on. | 61 | | description | string | true | Description of the tool that is passed to the LLM. | 62 | ``` -------------------------------------------------------------------------------- /internal/tools/neo4j/neo4jschema/neo4jschema_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 not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package neo4jschema 17 | 18 | import ( 19 | "testing" 20 | 21 | "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 | ) 26 | 27 | func TestParseFromYamlNeo4j(t *testing.T) { 28 | ctx, err := testutils.ContextWithNewLogger() 29 | exp := 30 30 | 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 with default cache expiration", 41 | in: ` 42 | tools: 43 | example_tool: 44 | kind: neo4j-schema 45 | source: my-neo4j-instance 46 | description: some tool description 47 | authRequired: 48 | - my-google-auth-service 49 | - other-auth-service 50 | `, 51 | want: server.ToolConfigs{ 52 | "example_tool": Config{ 53 | Name: "example_tool", 54 | Kind: "neo4j-schema", 55 | Source: "my-neo4j-instance", 56 | Description: "some tool description", 57 | AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, 58 | CacheExpireMinutes: nil, 59 | }, 60 | }, 61 | }, 62 | { 63 | desc: "cache expire minutes set explicitly", 64 | in: ` 65 | tools: 66 | example_tool: 67 | kind: neo4j-schema 68 | source: my-neo4j-instance 69 | description: some tool description 70 | cacheExpireMinutes: 30 71 | `, 72 | want: server.ToolConfigs{ 73 | "example_tool": Config{ 74 | Name: "example_tool", 75 | Kind: "neo4j-schema", 76 | Source: "my-neo4j-instance", 77 | Description: "some tool description", 78 | AuthRequired: []string{}, // Expect an empty slice, not nil. 79 | CacheExpireMinutes: &exp, 80 | }, 81 | }, 82 | }, 83 | } 84 | for _, tc := range tcs { 85 | t.Run(tc.desc, func(t *testing.T) { 86 | got := struct { 87 | Tools server.ToolConfigs `yaml:"tools"` 88 | }{} 89 | // Parse contents 90 | err = yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 91 | if err != nil { 92 | t.Fatalf("unable to unmarshal: %s", err) 93 | } 94 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 95 | t.Fatalf("incorrect parse: diff %v", diff) 96 | } 97 | }) 98 | } 99 | } 100 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mongodb/mongodb-delete-many.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "mongodb-delete-many" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "mongodb-delete-many" tool deletes all documents from a MongoDB collection that match a filter. 7 | aliases: 8 | - /resources/tools/mongodb-delete-many 9 | --- 10 | 11 | ## About 12 | 13 | The `mongodb-delete-many` tool performs a **bulk destructive operation**, 14 | deleting **ALL** documents from a collection that match a specified filter. 15 | 16 | The tool returns the total count of documents that were deleted. If the filter 17 | does not match any documents (i.e., the deleted count is 0), the tool will 18 | return an error. 19 | 20 | This tool is compatible with the following source kind: 21 | 22 | * [`mongodb`](../../sources/mongodb.md) 23 | 24 | --- 25 | 26 | ## Example 27 | 28 | Here is an example that performs a cleanup task by deleting all products from 29 | the `inventory` collection that belong to a discontinued brand. 30 | 31 | ```yaml 32 | tools: 33 | retire_brand_products: 34 | kind: mongodb-delete-many 35 | source: my-mongo-source 36 | description: Deletes all products from a specified discontinued brand. 37 | database: ecommerce 38 | collection: inventory 39 | filterPayload: | 40 | { "brand_name": {{json .brand_to_delete}} } 41 | filterParams: 42 | - name: brand_to_delete 43 | type: string 44 | description: The name of the discontinued brand whose products should be deleted. 45 | ``` 46 | 47 | ## Reference 48 | 49 | | **field** | **type** | **required** | **description** | 50 | |:--------------|:---------|:-------------|:--------------------------------------------------------------------------------------------------------------------| 51 | | kind | string | true | Must be `mongodb-delete-many`. | 52 | | source | string | true | The name of the `mongodb` source to use. | 53 | | description | string | true | A description of the tool that is passed to the LLM. | 54 | | database | string | true | The name of the MongoDB database containing the collection. | 55 | | collection | string | true | The name of the MongoDB collection from which to delete documents. | 56 | | filterPayload | string | true | The MongoDB query filter document to select the documents for deletion. Uses `{{json .param_name}}` for templating. | 57 | | filterParams | list | true | A list of parameter objects that define the variables used in the `filterPayload`. | 58 | ``` -------------------------------------------------------------------------------- /docs/en/getting-started/local_quickstart_js.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "JS Quickstart (Local)" 3 | type: docs 4 | weight: 3 5 | description: > 6 | How to get started running Toolbox locally with [JavaScript](https://github.com/googleapis/mcp-toolbox-sdk-js), PostgreSQL, and orchestration frameworks such as [LangChain](https://js.langchain.com/docs/introduction/), [GenkitJS](https://genkit.dev/docs/get-started/), [LlamaIndex](https://ts.llamaindex.ai/) and [GoogleGenAI](https://github.com/googleapis/js-genai). 7 | --- 8 | 9 | ## Before you begin 10 | 11 | This guide assumes you have already done the following: 12 | 13 | 1. Installed [Node.js (v18 or higher)]. 14 | 1. Installed [PostgreSQL 16+ and the `psql` client][install-postgres]. 15 | 16 | [Node.js (v18 or higher)]: https://nodejs.org/ 17 | [install-postgres]: https://www.postgresql.org/download/ 18 | 19 | ### Cloud Setup (Optional) 20 | 21 | {{< regionInclude "quickstart/shared/cloud_setup.md" "cloud_setup" >}} 22 | 23 | ## Step 1: Set up your database 24 | 25 | {{< regionInclude "quickstart/shared/database_setup.md" "database_setup" >}} 26 | 27 | ## Step 2: Install and configure Toolbox 28 | 29 | {{< regionInclude "quickstart/shared/configure_toolbox.md" "configure_toolbox" >}} 30 | 31 | ## Step 3: Connect your agent to Toolbox 32 | 33 | In this section, we will write and run an agent that will load the Tools 34 | from Toolbox. 35 | 36 | 1. (Optional) Initialize a Node.js project: 37 | 38 | ```bash 39 | npm init -y 40 | ``` 41 | 42 | 1. In a new terminal, install the 43 | [SDK](https://www.npmjs.com/package/@toolbox-sdk/core). 44 | 45 | ```bash 46 | npm install @toolbox-sdk/core 47 | ``` 48 | 49 | 1. Install other required dependencies 50 | 51 | {{< tabpane persist=header >}} 52 | {{< tab header="LangChain" lang="bash" >}} 53 | npm install langchain @langchain/google-genai 54 | {{< /tab >}} 55 | {{< tab header="GenkitJS" lang="bash" >}} 56 | npm install genkit @genkit-ai/googleai 57 | {{< /tab >}} 58 | {{< tab header="LlamaIndex" lang="bash" >}} 59 | npm install llamaindex @llamaindex/google @llamaindex/workflow 60 | {{< /tab >}} 61 | {{< tab header="GoogleGenAI" lang="bash" >}} 62 | npm install @google/genai 63 | {{< /tab >}} 64 | {{< /tabpane >}} 65 | 66 | 1. Create a new file named `hotelAgent.js` and copy the following code to create 67 | an agent: 68 | 69 | {{< tabpane persist=header >}} 70 | {{< tab header="LangChain" lang="js" >}} 71 | 72 | {{< include "quickstart/js/langchain/quickstart.js" >}} 73 | 74 | {{< /tab >}} 75 | 76 | {{< tab header="GenkitJS" lang="js" >}} 77 | 78 | {{< include "quickstart/js/genkit/quickstart.js" >}} 79 | 80 | {{< /tab >}} 81 | 82 | {{< tab header="LlamaIndex" lang="js" >}} 83 | 84 | {{< include "quickstart/js/llamaindex/quickstart.js" >}} 85 | 86 | {{< /tab >}} 87 | 88 | {{< tab header="GoogleGenAI" lang="js" >}} 89 | 90 | {{< include "quickstart/js/genAI/quickstart.js" >}} 91 | 92 | {{< /tab >}} 93 | 94 | {{< /tabpane >}} 95 | 96 | 1. Run your agent, and observe the results: 97 | 98 | ```sh 99 | node hotelAgent.js 100 | ``` 101 | 102 | {{< notice info >}} 103 | For more information, visit the [JS SDK 104 | repo](https://github.com/googleapis/mcp-toolbox-sdk-js). 105 | {{</ notice >}} 106 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/neo4j/neo4j-execute-cypher.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "neo4j-execute-cypher" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "neo4j-execute-cypher" tool executes any arbitrary Cypher statement against a Neo4j 7 | database. 8 | aliases: 9 | - /resources/tools/neo4j-execute-cypher 10 | --- 11 | 12 | ## About 13 | 14 | A `neo4j-execute-cypher` tool executes an arbitrary Cypher query provided as a 15 | string parameter against a Neo4j database. It's designed to be a flexible tool 16 | for interacting with the database when a pre-defined query is not sufficient. 17 | This tool is compatible with any of the following sources: 18 | 19 | - [neo4j](../../sources/neo4j.md) 20 | 21 | For security, the tool can be configured to be read-only. If the `readOnly` flag 22 | is set to `true`, the tool will analyze the incoming Cypher query and reject any 23 | write operations (like `CREATE`, `MERGE`, `DELETE`, etc.) before execution. 24 | 25 | The Cypher query uses standard [Neo4j 26 | Cypher](https://neo4j.com/docs/cypher-manual/current/queries/) syntax and 27 | supports all Cypher features, including pattern matching, filtering, and 28 | aggregation. 29 | 30 | `neo4j-execute-cypher` takes one input parameter `cypher` and run the cypher 31 | query against the `source`. 32 | 33 | > **Note:** This tool is intended for developer assistant workflows with 34 | > human-in-the-loop and shouldn't be used for production agents. 35 | 36 | ## Example 37 | 38 | ```yaml 39 | tools: 40 | query_neo4j: 41 | kind: neo4j-execute-cypher 42 | source: my-neo4j-prod-db 43 | readOnly: true 44 | description: | 45 | Use this tool to execute a Cypher query against the production database. 46 | Only read-only queries are allowed. 47 | Takes a single 'cypher' parameter containing the full query string. 48 | Example: 49 | {{ 50 | "cypher": "MATCH (m:Movie {title: 'The Matrix'}) RETURN m.released" 51 | }} 52 | ``` 53 | 54 | ## Reference 55 | 56 | | **field** | **type** | **required** | **description** | 57 | |-------------|:------------------------------------------:|:------------:|-------------------------------------------------------------------------------------------------| 58 | | kind | string | true | Must be "neo4j-cypher". | 59 | | source | string | true | Name of the source the Cypher query should execute on. | 60 | | description | string | true | Description of the tool that is passed to the LLM. | 61 | | readOnly | boolean | false | If set to `true`, the tool will reject any write operations in the Cypher query. Default is `false`. | 62 | ``` -------------------------------------------------------------------------------- /docs/en/samples/looker/looker_mcp_inspector/_index.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "Quickstart (MCP with Looker)" 3 | type: docs 4 | weight: 2 5 | description: > 6 | How to get started running Toolbox with MCP Inspector and Looker as the source. 7 | --- 8 | 9 | ## Overview 10 | 11 | [Model Context Protocol](https://modelcontextprotocol.io) is an open protocol 12 | that standardizes how applications provide context to LLMs. Check out this page 13 | on how to [connect to Toolbox via MCP](../../how-to/connect_via_mcp.md). 14 | 15 | ## Step 1: Get a Looker Client ID and Client Secret 16 | 17 | The Looker Client ID and Client Secret can be obtained from the Users page of 18 | your Looker instance. Refer to the documentation 19 | [here](https://cloud.google.com/looker/docs/api-auth#authentication_with_an_sdk). 20 | You may need to ask an administrator to get the Client ID and Client Secret 21 | for you. 22 | 23 | ## Step 2: Install and configure Toolbox 24 | 25 | In this section, we will download Toolbox and run the Toolbox server. 26 | 27 | 1. Download the latest version of Toolbox as a binary: 28 | 29 | {{< notice tip >}} 30 | Select the 31 | [correct binary](https://github.com/googleapis/genai-toolbox/releases) 32 | corresponding to your OS and CPU architecture. 33 | {{< /notice >}} 34 | <!-- {x-release-please-start-version} --> 35 | ```bash 36 | export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, or windows/amd64 37 | curl -O https://storage.googleapis.com/genai-toolbox/v0.17.0/$OS/toolbox 38 | ``` 39 | <!-- {x-release-please-end} --> 40 | 41 | 1. Make the binary executable: 42 | 43 | ```bash 44 | chmod +x toolbox 45 | ``` 46 | 47 | 1. Create a file `looker_env` with the settings for your 48 | Looker instance. Use the Client ID and Client Secret 49 | you obtained earlier. 50 | 51 | ```bash 52 | export LOOKER_BASE_URL=https://looker.example.com 53 | export LOOKER_VERIFY_SSL=true 54 | export LOOKER_CLIENT_ID=Q7ynZkRkvj9S9FHPm4Wj 55 | export LOOKER_CLIENT_SECRET=P5JvZstFnhpkhCYy2yNSfJ6x 56 | ``` 57 | 58 | In some instances you may need to append `:19999` to 59 | the LOOKER_BASE_URL. 60 | 61 | 1. Load the looker_env file into your environment. 62 | 63 | ```bash 64 | source looker_env 65 | ``` 66 | 67 | 1. Run the Toolbox server using the prebuilt Looker tools. 68 | 69 | ```bash 70 | ./toolbox --prebuilt looker 71 | ``` 72 | 73 | ## Step 3: Connect to MCP Inspector 74 | 75 | 1. Run the MCP Inspector: 76 | 77 | ```bash 78 | npx @modelcontextprotocol/inspector 79 | ``` 80 | 81 | 1. Type `y` when it asks to install the inspector package. 82 | 83 | 1. It should show the following when the MCP Inspector is up and running: 84 | 85 | ```bash 86 | 🔍 MCP Inspector is up and running at http://127.0.0.1:5173 🚀 87 | ``` 88 | 89 | 1. Open the above link in your browser. 90 | 91 | 1. For `Transport Type`, select `SSE`. 92 | 93 | 1. For `URL`, type in `http://127.0.0.1:5000/mcp/sse`. 94 | 95 | 1. Click Connect. 96 | 97 |  98 | 99 | 1. Select `List Tools`, you will see a list of tools. 100 | 101 |  102 | 103 | 1. Test out your tools here! 104 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mysql/mysql-list-active-queries.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "mysql-list-active-queries" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "mysql-list-active-queries" tool lists active queries in a MySQL database. 7 | aliases: 8 | - /resources/tools/mysql-list-active-queries 9 | --- 10 | 11 | ## About 12 | 13 | A `mysql-list-active-queries` tool retrieves information about active queries in 14 | a MySQL database. It's compatible with: 15 | 16 | - [cloud-sql-mysql](../../sources/cloud-sql-mysql.md) 17 | - [mysql](../../sources/mysql.md) 18 | 19 | `mysql-list-active-queries` outputs detailed information as JSON for current 20 | active queries, ordered by execution time in descending order. 21 | This tool takes 2 optional input parameters: 22 | 23 | - `min_duration_secs` (optional): Only show queries running for at least this 24 | long in seconds, default `0`. 25 | - `limit` (optional): max number of queries to return, default `10`. 26 | 27 | ## Example 28 | 29 | ```yaml 30 | tools: 31 | list_active_queries: 32 | kind: mysql-list-active-queries 33 | source: my-mysql-instance 34 | description: Lists top N (default 10) ongoing queries from processlist and innodb_trx, ordered by execution time in descending order. Returns detailed information of those queries in json format, including process id, query, transaction duration, transaction wait duration, process time, transaction state, process state, username with host, transaction rows locked, transaction rows modified, and db schema. 35 | ``` 36 | 37 | The response is a json array with the following fields: 38 | 39 | ```json 40 | { 41 | "proccess_id": "id of the MySQL process/connection this query belongs to", 42 | "query": "query text", 43 | "trx_started": "the time when the transaction (this query belongs to) started", 44 | "trx_duration_seconds": "the total elapsed time (in seconds) of the owning transaction so far", 45 | "trx_wait_duration_seconds": "the total wait time (in seconds) of the owning transaction so far", 46 | "query_time": "the time (in seconds) that the owning connection has been in its current state", 47 | "trx_state": "the transaction execution state", 48 | "proces_state": "the current state of the owning connection", 49 | "user": "the user who issued this query", 50 | "trx_rows_locked": "the approximate number of rows locked by the owning transaction", 51 | "trx_rows_modified": "the approximate number of rows modified by the owning transaction", 52 | "db": "the default database for the owning connection" 53 | } 54 | ``` 55 | 56 | ## Reference 57 | 58 | | **field** | **type** | **required** | **description** | 59 | |-------------|:--------:|:------------:|----------------------------------------------------| 60 | | kind | string | true | Must be "mysql-list-active-queries". | 61 | | source | string | true | Name of the source the SQL should execute on. | 62 | | description | string | true | Description of the tool that is passed to the LLM. | 63 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/looker/looker-health-pulse.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "looker-health-pulse" 3 | type: docs 4 | weight: 1 5 | description: > 6 | "looker-health-pulse" performs health checks on a Looker instance, with multiple actions available (e.g., checking database connections, dashboard performance, etc). 7 | aliases: 8 | - /resources/tools/looker-health-pulse 9 | --- 10 | 11 | ## About 12 | 13 | The `looker-health-pulse` tool performs health checks on a Looker instance. The `action` parameter selects the type of check to perform: 14 | 15 | - `check_db_connections`: Checks all database connections, runs supported tests, and reports query counts. 16 | - `check_dashboard_performance`: Finds dashboards with slow running queries in the last 7 days. 17 | - `check_dashboard_errors`: Lists dashboards with erroring queries in the last 7 days. 18 | - `check_explore_performance`: Lists the slowest explores in the last 7 days and reports average query runtime. 19 | - `check_schedule_failures`: Lists schedules that have failed in the last 7 days. 20 | - `check_legacy_features`: Lists enabled legacy features. (*To note, this function is not available in Looker Core. You will get an error running this command with a Core instance configured.*) 21 | 22 | ## Parameters 23 | 24 | | **field** | **type** | **required** | **description** | 25 | |---------------|:--------:|:------------:|---------------------------------------------| 26 | | kind | string | true | Must be "looker-health-pulse" | 27 | | source | string | true | Looker source name | 28 | | action | string | true | The health check to perform | 29 | 30 | ## Example 31 | 32 | ```yaml 33 | tools: 34 | pulse: 35 | kind: looker-health-pulse 36 | source: looker-source 37 | description: | 38 | Pulse Tool 39 | 40 | Performs health checks on Looker instance. 41 | Specify the `action` parameter to select the check. 42 | parameters: 43 | action: check_dashboard_performance 44 | ``` 45 | 46 | ## Reference 47 | 48 | | **action** | **description** | 49 | |---------------------------|--------------------------------------------------------------------------------| 50 | | check_db_connections | Checks all database connections and reports query counts and errors | 51 | | check_dashboard_performance | Finds dashboards with slow queries (>30s) in the last 7 days | 52 | | check_dashboard_errors | Lists dashboards with erroring queries in the last 7 days | 53 | | check_explore_performance | Lists slowest explores and average query runtime | 54 | | check_schedule_failures | Lists failed schedules in the last 7 days | 55 | | check_legacy_features | Lists enabled legacy features | ``` -------------------------------------------------------------------------------- /internal/sources/sqlite/sqlite.go: -------------------------------------------------------------------------------- ```go 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sqlite 16 | 17 | import ( 18 | "context" 19 | "database/sql" 20 | "fmt" 21 | 22 | "github.com/goccy/go-yaml" 23 | "github.com/googleapis/genai-toolbox/internal/sources" 24 | "go.opentelemetry.io/otel/trace" 25 | _ "modernc.org/sqlite" // Pure Go SQLite driver 26 | ) 27 | 28 | const SourceKind string = "sqlite" 29 | 30 | // validate interface 31 | var _ sources.SourceConfig = Config{} 32 | 33 | func init() { 34 | if !sources.Register(SourceKind, newConfig) { 35 | panic(fmt.Sprintf("source kind %q already registered", SourceKind)) 36 | } 37 | } 38 | 39 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { 40 | actual := Config{Name: name} 41 | if err := decoder.DecodeContext(ctx, &actual); err != nil { 42 | return nil, err 43 | } 44 | return actual, nil 45 | } 46 | 47 | type Config struct { 48 | Name string `yaml:"name" validate:"required"` 49 | Kind string `yaml:"kind" validate:"required"` 50 | Database string `yaml:"database" validate:"required"` // Path to SQLite database file 51 | } 52 | 53 | func (r Config) SourceConfigKind() string { 54 | return SourceKind 55 | } 56 | 57 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { 58 | db, err := initSQLiteConnection(ctx, tracer, r.Name, r.Database) 59 | if err != nil { 60 | return nil, fmt.Errorf("unable to create db connection: %w", err) 61 | } 62 | 63 | err = db.PingContext(context.Background()) 64 | if err != nil { 65 | return nil, fmt.Errorf("unable to connect successfully: %w", err) 66 | } 67 | 68 | s := &Source{ 69 | Name: r.Name, 70 | Kind: SourceKind, 71 | Db: db, 72 | } 73 | return s, nil 74 | } 75 | 76 | var _ sources.Source = &Source{} 77 | 78 | type Source struct { 79 | Name string `yaml:"name"` 80 | Kind string `yaml:"kind"` 81 | Db *sql.DB 82 | } 83 | 84 | func (s *Source) SourceKind() string { 85 | return SourceKind 86 | } 87 | 88 | func (s *Source) SQLiteDB() *sql.DB { 89 | return s.Db 90 | } 91 | 92 | func initSQLiteConnection(ctx context.Context, tracer trace.Tracer, name, dbPath string) (*sql.DB, error) { 93 | //nolint:all // Reassigned ctx 94 | ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) 95 | defer span.End() 96 | 97 | // Open database connection 98 | db, err := sql.Open("sqlite", dbPath) 99 | if err != nil { 100 | return nil, fmt.Errorf("sql.Open: %w", err) 101 | } 102 | 103 | // Set some reasonable defaults for SQLite 104 | db.SetMaxOpenConns(1) // SQLite only supports one writer at a time 105 | db.SetMaxIdleConns(1) 106 | 107 | return db, nil 108 | } 109 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/postgres/postgres-list-active-queries.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "postgres-list-active-queries" 3 | type: docs 4 | weight: 1 5 | description: > 6 | The "postgres-list-active-queries" tool lists currently active queries in a Postgres database. 7 | aliases: 8 | - /resources/tools/postgres-list-active-queries 9 | --- 10 | 11 | ## About 12 | 13 | The `postgres-list-active-queries` tool retrieves information about currently 14 | active queries in a Postgres database. It's compatible with any of the following 15 | sources: 16 | 17 | - [alloydb-postgres](../../sources/alloydb-pg.md) 18 | - [cloud-sql-postgres](../../sources/cloud-sql-pg.md) 19 | - [postgres](../../sources/postgres.md) 20 | 21 | `postgres-list-active-queries` lists detailed information as JSON for currently 22 | active queries. The tool takes the following input parameters: 23 | 24 | - `min_duraton` (optional): Only show queries running at least this long (e.g., 25 | '1 minute', '1 second', '2 seconds'). Default: '1 minute'. 26 | - `exclude_application_names` (optional): A comma-separated list of application 27 | names to exclude from the query results. This is useful for filtering out 28 | queries from specific applications (e.g., 'psql', 'pgAdmin', 'DBeaver'). The 29 | match is case-sensitive. Whitespace around commas and names is automatically 30 | handled. If this parameter is omitted, no applications are excluded. 31 | - `limit` (optional): The maximum number of rows to return. Default: `50`. 32 | 33 | ## Example 34 | 35 | ```yaml 36 | tools: 37 | list_active_queries: 38 | kind: postgres-list-active-queries 39 | source: postgres-source 40 | description: List the top N (default 50) currently running queries (state='active') from pg_stat_activity, ordered by longest-running first. Returns pid, user, database, application_name, client_addr, state, wait_event_type/wait_event, backend/xact/query start times, computed query_duration, and the SQL text. 41 | ``` 42 | 43 | The response is a json array with the following elements: 44 | 45 | ```json 46 | { 47 | "pid": "process id", 48 | "user": "database user name", 49 | "datname": "database name", 50 | "application_name": "connecting application name", 51 | "client_addr": "connecting client ip address", 52 | "state": "connection state", 53 | "wait_event_type": "connection wait event type", 54 | "wait_event": "connection wait event", 55 | "backend_start": "connection start time", 56 | "xact_start": "transaction start time", 57 | "query_start": "query start time", 58 | "query_duration": "query duration", 59 | "query": "query text" 60 | } 61 | ``` 62 | 63 | ## Reference 64 | 65 | | **field** | **type** | **required** | **description** | 66 | |-------------|:--------:|:------------:|----------------------------------------------------| 67 | | kind | string | true | Must be "postgres-list-active-queries". | 68 | | source | string | true | Name of the source the SQL should execute on. | 69 | | description | string | true | Description of the tool that is passed to the LLM. | 70 | ``` -------------------------------------------------------------------------------- /internal/sources/dataplex/dataplex_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 dataplex_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/dataplex" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | ) 26 | 27 | func TestParseFromYamlDataplex(t *testing.T) { 28 | tcs := []struct { 29 | desc string 30 | in string 31 | want server.SourceConfigs 32 | }{ 33 | { 34 | desc: "basic example", 35 | in: ` 36 | sources: 37 | my-instance: 38 | kind: dataplex 39 | project: my-project 40 | `, 41 | want: server.SourceConfigs{ 42 | "my-instance": dataplex.Config{ 43 | Name: "my-instance", 44 | Kind: dataplex.SourceKind, 45 | Project: "my-project", 46 | }, 47 | }, 48 | }, 49 | } 50 | for _, tc := range tcs { 51 | t.Run(tc.desc, func(t *testing.T) { 52 | got := struct { 53 | Sources server.SourceConfigs `yaml:"sources"` 54 | }{} 55 | // Parse contents 56 | err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) 57 | if err != nil { 58 | t.Fatalf("unable to unmarshal: %s", err) 59 | } 60 | if !cmp.Equal(tc.want, got.Sources) { 61 | t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources) 62 | } 63 | }) 64 | } 65 | 66 | } 67 | 68 | func TestFailParseFromYaml(t *testing.T) { 69 | tcs := []struct { 70 | desc string 71 | in string 72 | err string 73 | }{ 74 | { 75 | desc: "extra field", 76 | in: ` 77 | sources: 78 | my-instance: 79 | kind: dataplex 80 | project: my-project 81 | foo: bar 82 | `, 83 | err: "unable to parse source \"my-instance\" as \"dataplex\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | kind: dataplex\n 3 | project: my-project", 84 | }, 85 | { 86 | desc: "missing required field", 87 | in: ` 88 | sources: 89 | my-instance: 90 | kind: dataplex 91 | `, 92 | err: "unable to parse source \"my-instance\" as \"dataplex\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag", 93 | }, 94 | } 95 | for _, tc := range tcs { 96 | t.Run(tc.desc, func(t *testing.T) { 97 | got := struct { 98 | Sources server.SourceConfigs `yaml:"sources"` 99 | }{} 100 | // Parse contents 101 | err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) 102 | if err == nil { 103 | t.Fatalf("expect parsing to fail") 104 | } 105 | errStr := err.Error() 106 | if errStr != tc.err { 107 | t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err) 108 | } 109 | }) 110 | } 111 | } 112 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/clickhouse/clickhouse-sql.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "clickhouse-sql" 3 | type: docs 4 | weight: 2 5 | description: > 6 | A "clickhouse-sql" tool executes SQL queries as prepared statements in ClickHouse. 7 | aliases: 8 | - /resources/tools/clickhouse-sql 9 | --- 10 | 11 | ## About 12 | 13 | A `clickhouse-sql` tool executes SQL queries as prepared statements against a 14 | ClickHouse database. It's compatible with the 15 | [clickhouse](../../sources/clickhouse.md) source. 16 | 17 | This tool supports both template parameters (for SQL statement customization) 18 | and regular parameters (for prepared statement values), providing flexible 19 | query execution capabilities. 20 | 21 | ## Example 22 | 23 | ```yaml 24 | tools: 25 | my_analytics_query: 26 | kind: clickhouse-sql 27 | source: my-clickhouse-instance 28 | description: Get user analytics for a specific date range 29 | statement: | 30 | SELECT 31 | user_id, 32 | count(*) as event_count, 33 | max(timestamp) as last_event 34 | FROM events 35 | WHERE date >= ? AND date <= ? 36 | GROUP BY user_id 37 | ORDER BY event_count DESC 38 | LIMIT ? 39 | parameters: 40 | - name: start_date 41 | description: Start date for the query (YYYY-MM-DD format) 42 | - name: end_date 43 | description: End date for the query (YYYY-MM-DD format) 44 | - name: limit 45 | description: Maximum number of results to return 46 | ``` 47 | 48 | ## Template Parameters Example 49 | 50 | ```yaml 51 | tools: 52 | flexible_table_query: 53 | kind: clickhouse-sql 54 | source: my-clickhouse-instance 55 | description: Query any table with flexible columns 56 | statement: | 57 | SELECT {{columns}} 58 | FROM {{table_name}} 59 | WHERE created_date >= ? 60 | LIMIT ? 61 | templateParameters: 62 | - name: columns 63 | description: Comma-separated list of columns to select 64 | - name: table_name 65 | description: Name of the table to query 66 | parameters: 67 | - name: start_date 68 | description: Start date filter 69 | - name: limit 70 | description: Maximum number of results 71 | ``` 72 | 73 | ## Reference 74 | 75 | | **field** | **type** | **required** | **description** | 76 | |--------------------|:------------------:|:------------:|-------------------------------------------------------| 77 | | kind | string | true | Must be "clickhouse-sql". | 78 | | source | string | true | Name of the ClickHouse source to execute SQL against. | 79 | | description | string | true | Description of the tool that is passed to the LLM. | 80 | | statement | string | true | The SQL statement template to execute. | 81 | | parameters | array of Parameter | false | Parameters for prepared statement values. | 82 | | templateParameters | array of Parameter | false | Parameters for SQL statement template customization. | 83 | ``` -------------------------------------------------------------------------------- /internal/tools/alloydb/alloydbcreateuser/alloydbcreateuser_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 alloydbcreateuser_test 16 | 17 | import ( 18 | "testing" 19 | 20 | yaml "github.com/goccy/go-yaml" 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/googleapis/genai-toolbox/internal/server" 23 | "github.com/googleapis/genai-toolbox/internal/testutils" 24 | alloydbcreateuser "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateuser" 25 | ) 26 | 27 | func TestParseFromYaml(t *testing.T) { 28 | ctx, err := testutils.ContextWithNewLogger() 29 | if err != nil { 30 | t.Fatalf("unexpected error: %s", err) 31 | } 32 | tcs := []struct { 33 | desc string 34 | in string 35 | want server.ToolConfigs 36 | }{ 37 | { 38 | desc: "basic example", 39 | in: ` 40 | tools: 41 | create-my-user: 42 | kind: alloydb-create-user 43 | source: my-alloydb-admin-source 44 | description: some description 45 | `, 46 | want: server.ToolConfigs{ 47 | "create-my-user": alloydbcreateuser.Config{ 48 | Name: "create-my-user", 49 | Kind: "alloydb-create-user", 50 | Source: "my-alloydb-admin-source", 51 | Description: "some description", 52 | AuthRequired: []string{}, 53 | }, 54 | }, 55 | }, 56 | { 57 | desc: "with auth required", 58 | in: ` 59 | tools: 60 | create-my-user-auth: 61 | kind: alloydb-create-user 62 | source: my-alloydb-admin-source 63 | description: some description 64 | authRequired: 65 | - my-google-auth-service 66 | - other-auth-service 67 | `, 68 | want: server.ToolConfigs{ 69 | "create-my-user-auth": alloydbcreateuser.Config{ 70 | Name: "create-my-user-auth", 71 | Kind: "alloydb-create-user", 72 | Source: "my-alloydb-admin-source", 73 | Description: "some description", 74 | AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, 75 | }, 76 | }, 77 | }, 78 | } 79 | for _, tc := range tcs { 80 | t.Run(tc.desc, func(t *testing.T) { 81 | got := struct { 82 | Tools server.ToolConfigs `yaml:"tools"` 83 | }{} 84 | // Parse contents 85 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 86 | if err != nil { 87 | t.Fatalf("unable to unmarshal: %s", err) 88 | } 89 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 90 | t.Fatalf("incorrect parse: diff %v", diff) 91 | } 92 | }) 93 | } 94 | } 95 | ``` -------------------------------------------------------------------------------- /internal/tools/dgraph/dgraph_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 dgraph_test 16 | 17 | import ( 18 | "testing" 19 | 20 | yaml "github.com/goccy/go-yaml" 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/googleapis/genai-toolbox/internal/server" 23 | "github.com/googleapis/genai-toolbox/internal/testutils" 24 | "github.com/googleapis/genai-toolbox/internal/tools/dgraph" 25 | ) 26 | 27 | func TestParseFromYamlDgraph(t *testing.T) { 28 | ctx, err := testutils.ContextWithNewLogger() 29 | if err != nil { 30 | t.Fatalf("unexpected error: %s", err) 31 | } 32 | tcs := []struct { 33 | desc string 34 | in string 35 | want server.ToolConfigs 36 | }{ 37 | { 38 | desc: "basic query example", 39 | in: ` 40 | tools: 41 | example_tool: 42 | kind: dgraph-dql 43 | source: my-dgraph-instance 44 | description: some tool description 45 | isQuery: true 46 | timeout: 20s 47 | statement: | 48 | query {q(func: eq(email, "[email protected]")) {email}} 49 | `, 50 | want: server.ToolConfigs{ 51 | "example_tool": dgraph.Config{ 52 | Name: "example_tool", 53 | Kind: "dgraph-dql", 54 | Source: "my-dgraph-instance", 55 | AuthRequired: []string{}, 56 | Description: "some tool description", 57 | IsQuery: true, 58 | Timeout: "20s", 59 | Statement: "query {q(func: eq(email, \"[email protected]\")) {email}}\n", 60 | }, 61 | }, 62 | }, 63 | { 64 | desc: "basic mutation example", 65 | in: ` 66 | tools: 67 | example_tool: 68 | kind: dgraph-dql 69 | source: my-dgraph-instance 70 | description: some tool description 71 | statement: | 72 | mutation {set { _:a <name> "[email protected]" . _:b <email> "[email protected]" .}} 73 | `, 74 | want: server.ToolConfigs{ 75 | "example_tool": dgraph.Config{ 76 | Name: "example_tool", 77 | Kind: "dgraph-dql", 78 | Source: "my-dgraph-instance", 79 | Description: "some tool description", 80 | AuthRequired: []string{}, 81 | Statement: "mutation {set { _:a <name> \"[email protected]\" . _:b <email> \"[email protected]\" .}}\n", 82 | }, 83 | }, 84 | }, 85 | } 86 | for _, tc := range tcs { 87 | t.Run(tc.desc, func(t *testing.T) { 88 | got := struct { 89 | Tools server.ToolConfigs `yaml:"tools"` 90 | }{} 91 | // Parse contents 92 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 93 | if err != nil { 94 | t.Fatalf("unable to unmarshal: %s", err) 95 | } 96 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 97 | t.Fatalf("incorrect parse: diff %v", diff) 98 | } 99 | }) 100 | } 101 | 102 | } 103 | ``` -------------------------------------------------------------------------------- /docs/en/resources/sources/trino.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "Trino" 3 | type: docs 4 | weight: 1 5 | description: > 6 | Trino is a distributed SQL query engine for big data analytics. 7 | --- 8 | 9 | ## About 10 | 11 | [Trino][trino-docs] is a distributed SQL query engine designed for fast analytic 12 | queries against data of any size. It allows you to query data where it lives, 13 | including Hive, Cassandra, relational databases or even proprietary data stores. 14 | 15 | [trino-docs]: https://trino.io/docs/ 16 | 17 | ## Available Tools 18 | 19 | - [`trino-sql`](../tools/trino/trino-sql.md) 20 | Execute parameterized SQL queries against Trino. 21 | 22 | - [`trino-execute-sql`](../tools/trino/trino-execute-sql.md) 23 | Execute arbitrary SQL queries against Trino. 24 | 25 | ## Requirements 26 | 27 | ### Trino Cluster 28 | 29 | You need access to a running Trino cluster with appropriate user permissions for 30 | the catalogs and schemas you want to query. 31 | 32 | ## Example 33 | 34 | ```yaml 35 | sources: 36 | my-trino-source: 37 | kind: trino 38 | host: trino.example.com 39 | port: "8080" 40 | user: ${TRINO_USER} # Optional for anonymous access 41 | password: ${TRINO_PASSWORD} # Optional 42 | catalog: hive 43 | schema: default 44 | ``` 45 | 46 | {{< notice tip >}} 47 | Use environment variable replacement with the format ${ENV_NAME} 48 | instead of hardcoding your secrets into the configuration file. 49 | {{< /notice >}} 50 | 51 | ## Reference 52 | 53 | | **field** | **type** | **required** | **description** | 54 | |-----------------|:--------:|:------------:|------------------------------------------------------------------------------| 55 | | kind | string | true | Must be "trino". | 56 | | host | string | true | Trino coordinator hostname (e.g. "trino.example.com") | 57 | | port | string | true | Trino coordinator port (e.g. "8080", "8443") | 58 | | user | string | false | Username for authentication (e.g. "analyst"). Optional for anonymous access. | 59 | | password | string | false | Password for basic authentication | 60 | | catalog | string | true | Default catalog to use for queries (e.g. "hive") | 61 | | schema | string | true | Default schema to use for queries (e.g. "default") | 62 | | queryTimeout | string | false | Query timeout duration (e.g. "30m", "1h") | 63 | | accessToken | string | false | JWT access token for authentication | 64 | | kerberosEnabled | boolean | false | Enable Kerberos authentication (default: false) | 65 | | sslEnabled | boolean | false | Enable SSL/TLS (default: false) | 66 | ``` -------------------------------------------------------------------------------- /internal/tools/alloydb/alloydbcreatecluster/alloydbcreatecluster_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 alloydbcreatecluster_test 16 | 17 | import ( 18 | "testing" 19 | 20 | yaml "github.com/goccy/go-yaml" 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/googleapis/genai-toolbox/internal/server" 23 | "github.com/googleapis/genai-toolbox/internal/testutils" 24 | alloydbcreatecluster "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreatecluster" 25 | ) 26 | 27 | func TestParseFromYaml(t *testing.T) { 28 | ctx, err := testutils.ContextWithNewLogger() 29 | if err != nil { 30 | t.Fatalf("unexpected error: %s", err) 31 | } 32 | tcs := []struct { 33 | desc string 34 | in string 35 | want server.ToolConfigs 36 | }{ 37 | { 38 | desc: "basic example", 39 | in: ` 40 | tools: 41 | create-my-cluster: 42 | kind: alloydb-create-cluster 43 | source: my-alloydb-admin-source 44 | description: some description 45 | `, 46 | want: server.ToolConfigs{ 47 | "create-my-cluster": alloydbcreatecluster.Config{ 48 | Name: "create-my-cluster", 49 | Kind: "alloydb-create-cluster", 50 | Source: "my-alloydb-admin-source", 51 | Description: "some description", 52 | AuthRequired: []string{}, 53 | }, 54 | }, 55 | }, 56 | { 57 | desc: "with auth required", 58 | in: ` 59 | tools: 60 | create-my-cluster-auth: 61 | kind: alloydb-create-cluster 62 | source: my-alloydb-admin-source 63 | description: some description 64 | authRequired: 65 | - my-google-auth-service 66 | - other-auth-service 67 | `, 68 | want: server.ToolConfigs{ 69 | "create-my-cluster-auth": alloydbcreatecluster.Config{ 70 | Name: "create-my-cluster-auth", 71 | Kind: "alloydb-create-cluster", 72 | Source: "my-alloydb-admin-source", 73 | Description: "some description", 74 | AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, 75 | }, 76 | }, 77 | }, 78 | } 79 | for _, tc := range tcs { 80 | t.Run(tc.desc, func(t *testing.T) { 81 | got := struct { 82 | Tools server.ToolConfigs `yaml:"tools"` 83 | }{} 84 | // Parse contents 85 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 86 | if err != nil { 87 | t.Fatalf("unable to unmarshal: %s", err) 88 | } 89 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 90 | t.Fatalf("incorrect parse: diff %v", diff) 91 | } 92 | }) 93 | } 94 | } 95 | ``` -------------------------------------------------------------------------------- /docs/en/how-to/deploy_docker.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "Deploy using Docker Compose" 3 | type: docs 4 | weight: 4 5 | description: > 6 | How to deploy Toolbox using Docker Compose. 7 | --- 8 | 9 | <!-- Contributor: Sujith R Pillai <[email protected]> --> 10 | 11 | ## Before you begin 12 | 13 | 1. [Install Docker Compose.](https://docs.docker.com/compose/install/) 14 | 15 | ## Configure `tools.yaml` file 16 | 17 | Create a `tools.yaml` file that contains your configuration for Toolbox. For 18 | details, see the 19 | [configuration](https://github.com/googleapis/genai-toolbox/blob/main/README.md#configuration) 20 | section. 21 | 22 | ## Deploy using Docker Compose 23 | 24 | 1. Create a `docker-compose.yml` file, customizing as needed: 25 | 26 | ```yaml 27 | services: 28 | toolbox: 29 | # TODO: It is recommended to pin to a specific image version instead of latest. 30 | image: us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest 31 | hostname: toolbox 32 | platform: linux/amd64 33 | ports: 34 | - "5000:5000" 35 | volumes: 36 | - ./config:/config 37 | command: [ "toolbox", "--tools-file", "/config/tools.yaml", "--address", "0.0.0.0"] 38 | depends_on: 39 | db: 40 | condition: service_healthy 41 | networks: 42 | - tool-network 43 | db: 44 | # TODO: It is recommended to pin to a specific image version instead of latest. 45 | image: postgres 46 | hostname: db 47 | environment: 48 | POSTGRES_USER: toolbox_user 49 | POSTGRES_PASSWORD: my-password 50 | POSTGRES_DB: toolbox_db 51 | ports: 52 | - "5432:5432" 53 | volumes: 54 | - ./db:/var/lib/postgresql/data 55 | # This file can be used to bootstrap your schema if needed. 56 | # See "initialization scripts" on https://hub.docker.com/_/postgres/ for more info 57 | - ./config/init.sql:/docker-entrypoint-initdb.d/init.sql 58 | healthcheck: 59 | test: ["CMD-SHELL", "pg_isready -U toolbox_user -d toolbox_db"] 60 | interval: 10s 61 | timeout: 5s 62 | retries: 5 63 | networks: 64 | - tool-network 65 | networks: 66 | tool-network: 67 | 68 | ``` 69 | 70 | 1. Run the following command to bring up the Toolbox and Postgres instance 71 | 72 | ```bash 73 | docker-compose up -d 74 | ``` 75 | 76 | {{< notice tip >}} 77 | 78 | You can use this setup to quickly set up Toolbox + Postgres to follow along in our 79 | [Quickstart](../getting-started/local_quickstart.md) 80 | 81 | {{< /notice >}} 82 | 83 | ## Connecting with Toolbox Client SDK 84 | 85 | Next, we will use Toolbox with the Client SDKs: 86 | 87 | 1. The url for the Toolbox server running using docker-compose will be: 88 | 89 | ``` 90 | http://localhost:5000 91 | ``` 92 | 93 | 1. Import and initialize the client with the URL: 94 | 95 | {{< tabpane persist=header >}} 96 | {{< tab header="LangChain" lang="Python" >}} 97 | from toolbox_langchain import ToolboxClient 98 | 99 | # Replace with the cloud run service URL generated above 100 | 101 | async with ToolboxClient("http://$YOUR_URL") as toolbox: 102 | {{< /tab >}} 103 | {{< tab header="Llamaindex" lang="Python" >}} 104 | from toolbox_llamaindex import ToolboxClient 105 | 106 | # Replace with the cloud run service URL generated above 107 | 108 | async with ToolboxClient("http://$YOUR_URL") as toolbox: 109 | {{< /tab >}} 110 | {{< /tabpane >}} 111 | ``` -------------------------------------------------------------------------------- /internal/sources/mongodb/mongodb_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 mongodb_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/mongodb" 24 | "github.com/googleapis/genai-toolbox/internal/testutils" 25 | ) 26 | 27 | func TestParseFromYamlMongoDB(t *testing.T) { 28 | tcs := []struct { 29 | desc string 30 | in string 31 | want server.SourceConfigs 32 | }{ 33 | { 34 | desc: "basic example", 35 | in: ` 36 | sources: 37 | mongo-db: 38 | kind: "mongodb" 39 | uri: "mongodb+srv://username:password@host/dbname" 40 | `, 41 | want: server.SourceConfigs{ 42 | "mongo-db": mongodb.Config{ 43 | Name: "mongo-db", 44 | Kind: mongodb.SourceKind, 45 | Uri: "mongodb+srv://username:password@host/dbname", 46 | }, 47 | }, 48 | }, 49 | } 50 | for _, tc := range tcs { 51 | t.Run(tc.desc, func(t *testing.T) { 52 | got := struct { 53 | Sources server.SourceConfigs `yaml:"sources"` 54 | }{} 55 | // Parse contents 56 | err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) 57 | if err != nil { 58 | t.Fatalf("unable to unmarshal: %s", err) 59 | } 60 | if !cmp.Equal(tc.want, got.Sources) { 61 | t.Fatalf("incorrect parse: want %v, got %v", tc.want, got.Sources) 62 | } 63 | }) 64 | } 65 | 66 | } 67 | 68 | func TestFailParseFromYaml(t *testing.T) { 69 | tcs := []struct { 70 | desc string 71 | in string 72 | err string 73 | }{ 74 | { 75 | desc: "extra field", 76 | in: ` 77 | sources: 78 | mongo-db: 79 | kind: mongodb 80 | uri: "mongodb+srv://username:password@host/dbname" 81 | foo: bar 82 | `, 83 | err: "unable to parse source \"mongo-db\" as \"mongodb\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | kind: mongodb\n 3 | uri: mongodb+srv://username:password@host/dbname", 84 | }, 85 | { 86 | desc: "missing required field", 87 | in: ` 88 | sources: 89 | mongo-db: 90 | kind: mongodb 91 | `, 92 | err: "unable to parse source \"mongo-db\" as \"mongodb\": Key: 'Config.Uri' Error:Field validation for 'Uri' failed on the 'required' tag", 93 | }, 94 | } 95 | for _, tc := range tcs { 96 | t.Run(tc.desc, func(t *testing.T) { 97 | got := struct { 98 | Sources server.SourceConfigs `yaml:"sources"` 99 | }{} 100 | // Parse contents 101 | err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got) 102 | if err == nil { 103 | t.Fatalf("expect parsing to fail") 104 | } 105 | errStr := err.Error() 106 | if errStr != tc.err { 107 | t.Fatalf("unexpected error: got \n%q, want \n%q", errStr, tc.err) 108 | } 109 | }) 110 | } 111 | } 112 | ``` -------------------------------------------------------------------------------- /internal/tools/alloydb/alloydbcreateinstance/alloydbcreateinstance_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 alloydbcreateinstance_test 16 | 17 | import ( 18 | "testing" 19 | 20 | yaml "github.com/goccy/go-yaml" 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/googleapis/genai-toolbox/internal/server" 23 | "github.com/googleapis/genai-toolbox/internal/testutils" 24 | alloydbcreateinstance "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateinstance" 25 | ) 26 | 27 | func TestParseFromYaml(t *testing.T) { 28 | ctx, err := testutils.ContextWithNewLogger() 29 | if err != nil { 30 | t.Fatalf("unexpected error: %s", err) 31 | } 32 | tcs := []struct { 33 | desc string 34 | in string 35 | want server.ToolConfigs 36 | }{ 37 | { 38 | desc: "basic example", 39 | in: ` 40 | tools: 41 | create-my-instance: 42 | kind: alloydb-create-instance 43 | source: my-alloydb-admin-source 44 | description: some description 45 | `, 46 | want: server.ToolConfigs{ 47 | "create-my-instance": alloydbcreateinstance.Config{ 48 | Name: "create-my-instance", 49 | Kind: "alloydb-create-instance", 50 | Source: "my-alloydb-admin-source", 51 | Description: "some description", 52 | AuthRequired: []string{}, 53 | }, 54 | }, 55 | }, 56 | { 57 | desc: "with auth required", 58 | in: ` 59 | tools: 60 | create-my-instance-auth: 61 | kind: alloydb-create-instance 62 | source: my-alloydb-admin-source 63 | description: some description 64 | authRequired: 65 | - my-google-auth-service 66 | - other-auth-service 67 | `, 68 | want: server.ToolConfigs{ 69 | "create-my-instance-auth": alloydbcreateinstance.Config{ 70 | Name: "create-my-instance-auth", 71 | Kind: "alloydb-create-instance", 72 | Source: "my-alloydb-admin-source", 73 | Description: "some description", 74 | AuthRequired: []string{"my-google-auth-service", "other-auth-service"}, 75 | }, 76 | }, 77 | }, 78 | } 79 | for _, tc := range tcs { 80 | t.Run(tc.desc, func(t *testing.T) { 81 | got := struct { 82 | Tools server.ToolConfigs `yaml:"tools"` 83 | }{} 84 | // Parse contents 85 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 86 | if err != nil { 87 | t.Fatalf("unable to unmarshal: %s", err) 88 | } 89 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 90 | t.Fatalf("incorrect parse: diff %v", diff) 91 | } 92 | }) 93 | } 94 | } 95 | ``` -------------------------------------------------------------------------------- /internal/tools/clickhouse/clickhouselistdatabases/clickhouselistdatabases_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 clickhouse 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/testutils" 25 | "github.com/googleapis/genai-toolbox/internal/tools" 26 | ) 27 | 28 | func TestListDatabasesConfigToolConfigKind(t *testing.T) { 29 | cfg := Config{} 30 | if cfg.ToolConfigKind() != listDatabasesKind { 31 | t.Errorf("expected %q, got %q", listDatabasesKind, cfg.ToolConfigKind()) 32 | } 33 | } 34 | 35 | func TestListDatabasesConfigInitializeMissingSource(t *testing.T) { 36 | cfg := Config{ 37 | Name: "test-list-databases", 38 | Kind: listDatabasesKind, 39 | Source: "missing-source", 40 | Description: "Test list databases tool", 41 | } 42 | 43 | srcs := map[string]sources.Source{} 44 | _, err := cfg.Initialize(srcs) 45 | if err == nil { 46 | t.Error("expected error for missing source") 47 | } 48 | } 49 | 50 | func TestParseFromYamlClickHouseListDatabases(t *testing.T) { 51 | ctx, err := testutils.ContextWithNewLogger() 52 | if err != nil { 53 | t.Fatalf("unexpected error: %s", err) 54 | } 55 | tcs := []struct { 56 | desc string 57 | in string 58 | want server.ToolConfigs 59 | }{ 60 | { 61 | desc: "basic example", 62 | in: ` 63 | tools: 64 | example_tool: 65 | kind: clickhouse-list-databases 66 | source: my-instance 67 | description: some description 68 | `, 69 | want: server.ToolConfigs{ 70 | "example_tool": Config{ 71 | Name: "example_tool", 72 | Kind: "clickhouse-list-databases", 73 | Source: "my-instance", 74 | Description: "some description", 75 | AuthRequired: []string{}, 76 | }, 77 | }, 78 | }, 79 | } 80 | for _, tc := range tcs { 81 | t.Run(tc.desc, func(t *testing.T) { 82 | got := struct { 83 | Tools server.ToolConfigs `yaml:"tools"` 84 | }{} 85 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 86 | if err != nil { 87 | t.Fatalf("unable to unmarshal: %s", err) 88 | } 89 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 90 | t.Fatalf("incorrect parse: diff %v", diff) 91 | } 92 | }) 93 | } 94 | } 95 | 96 | func TestListDatabasesToolParseParams(t *testing.T) { 97 | tool := Tool{ 98 | Parameters: tools.Parameters{}, 99 | } 100 | 101 | params, err := tool.ParseParams(map[string]any{}, map[string]map[string]any{}) 102 | if err != nil { 103 | t.Errorf("unexpected error: %v", err) 104 | } 105 | 106 | if len(params) != 0 { 107 | t.Errorf("expected 0 parameters, got %d", len(params)) 108 | } 109 | } 110 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/cloudsql/cloudsqlmysqlcreateinstance.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: cloud-sql-mysql-create-instance 3 | type: docs 4 | weight: 2 5 | description: "Create a Cloud SQL for MySQL instance." 6 | --- 7 | 8 | The `cloud-sql-mysql-create-instance` tool creates a new Cloud SQL for MySQL 9 | instance in a specified Google Cloud project. 10 | 11 | {{< notice info >}} 12 | This tool uses the `cloud-sql-admin` source. 13 | {{< /notice >}} 14 | 15 | ## Configuration 16 | 17 | Here is an example of how to configure the `cloud-sql-mysql-create-instance` 18 | tool in your `tools.yaml` file: 19 | 20 | ```yaml 21 | sources: 22 | my-cloud-sql-admin-source: 23 | kind: cloud-sql-admin 24 | 25 | tools: 26 | create_my_mysql_instance: 27 | kind: cloud-sql-mysql-create-instance 28 | source: my-cloud-sql-admin-source 29 | description: "Creates a MySQL instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 16 GiB RAM, 100 GiB SSD configuration with Non-HA/zonal availability. For the `Production` template, it chooses an 8 vCPU, 64 GiB RAM, 250 GiB SSD configuration with HA/regional availability. The Enterprise Plus edition is used in both cases. The default database version is `MYSQL_8_4`. The agent should ask the user if they want to use a different version." 30 | ``` 31 | 32 | ## Parameters 33 | 34 | The `cloud-sql-mysql-create-instance` tool has the following parameters: 35 | 36 | | **field** | **type** | **required** | **description** | 37 | | --------------- | :------: | :----------: | --------------------------------------------------------------------------------------------------------------- | 38 | | project | string | true | The Google Cloud project ID. | 39 | | name | string | true | The name of the instance to create. | 40 | | databaseVersion | string | false | The database version for MySQL. If not specified, defaults to the latest available version (e.g., `MYSQL_8_4`). | 41 | | rootPassword | string | true | The root password for the instance. | 42 | | editionPreset | string | false | The edition of the instance. Can be `Production` or `Development`. Defaults to `Development`. | 43 | 44 | ## Reference 45 | 46 | | **field** | **type** | **required** | **description** | 47 | | ----------- | :------: | :----------: | -------------------------------------------------------------- | 48 | | kind | string | true | Must be `cloud-sql-mysql-create-instance`. | 49 | | source | string | true | The name of the `cloud-sql-admin` source to use for this tool. | 50 | | description | string | false | A description of the tool that is passed to the agent. | 51 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/cloudsql/cloudsqlpgcreateinstances.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: cloud-sql-postgres-create-instance 3 | type: docs 4 | weight: 10 5 | description: Create a Cloud SQL for PostgreSQL instance. 6 | --- 7 | 8 | The `cloud-sql-postgres-create-instance` tool creates a Cloud SQL for PostgreSQL 9 | instance using the Cloud SQL Admin API. 10 | 11 | {{< notice info >}} 12 | This tool uses a `source` of kind `cloud-sql-admin`. 13 | {{< /notice >}} 14 | 15 | ## Example 16 | 17 | ```yaml 18 | tools: 19 | create-sql-instance: 20 | kind: cloud-sql-postgres-create-instance 21 | source: cloud-sql-admin-source 22 | description: "Creates a Postgres instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 16 GiB RAM, 100 GiB SSD configuration with Non-HA/zonal availability. For the `Production` template, it chooses an 8 vCPU, 64 GiB RAM, 250 GiB SSD configuration with HA/regional availability. The Enterprise Plus edition is used in both cases. The default database version is `POSTGRES_17`. The agent should ask the user if they want to use a different version." 23 | ``` 24 | 25 | ## Reference 26 | 27 | ### Tool Configuration 28 | 29 | | **field** | **type** | **required** | **description** | 30 | | ----------- | :------: | :----------: | ------------------------------------------------ | 31 | | kind | string | true | Must be "cloud-sql-postgres-create-instance". | 32 | | source | string | true | The name of the `cloud-sql-admin` source to use. | 33 | | description | string | false | A description of the tool. | 34 | 35 | ### Tool Inputs 36 | 37 | | **parameter** | **type** | **required** | **description** | 38 | |-----------------|:--------:|:------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------| 39 | | project | string | true | The project ID. | 40 | | name | string | true | The name of the instance. | 41 | | databaseVersion | string | false | The database version for Postgres. If not specified, defaults to the latest available version (e.g., POSTGRES_17). | 42 | | rootPassword | string | true | The root password for the instance. | 43 | | editionPreset | string | false | The edition of the instance. Can be `Production` or `Development`. This determines the default machine type and availability. Defaults to `Development`. | 44 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/mongodb/mongodb-delete-one.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "mongodb-delete-one" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "mongodb-delete-one" tool deletes a single document from a MongoDB collection. 7 | aliases: 8 | - /resources/tools/mongodb-delete-one 9 | --- 10 | 11 | ## About 12 | 13 | The `mongodb-delete-one` tool performs a destructive operation, deleting the 14 | **first single document** that matches a specified filter from a MongoDB 15 | collection. 16 | 17 | If the filter matches multiple documents, only the first one found by the 18 | database will be deleted. This tool is useful for removing specific entries, 19 | such as a user account or a single item from an inventory based on a unique ID. 20 | 21 | The tool returns the number of documents deleted, which will be either `1` if a 22 | document was found and deleted, or `0` if no matching document was found. 23 | 24 | This tool is compatible with the following source kind: 25 | 26 | * [`mongodb`](../../sources/mongodb.md) 27 | 28 | --- 29 | 30 | ## Example 31 | 32 | Here is an example that deletes a specific user account from the `users` 33 | collection by matching their unique email address. This is a permanent action. 34 | 35 | ```yaml 36 | tools: 37 | delete_user_account: 38 | kind: mongodb-delete-one 39 | source: my-mongo-source 40 | description: Permanently deletes a user account by their email address. 41 | database: user_data 42 | collection: users 43 | filterPayload: | 44 | { "email": {{json .email_address}} } 45 | filterParams: 46 | - name: email_address 47 | type: string 48 | description: The email of the user account to delete. 49 | ``` 50 | 51 | ## Reference 52 | 53 | | **field** | **type** | **required** | **description** | 54 | |:--------------|:---------|:-------------|:-------------------------------------------------------------------------------------------------------------------| 55 | | kind | string | true | Must be `mongodb-delete-one`. | 56 | | source | string | true | The name of the `mongodb` source to use. | 57 | | description | string | true | A description of the tool that is passed to the LLM. | 58 | | database | string | true | The name of the MongoDB database containing the collection. | 59 | | collection | string | true | The name of the MongoDB collection from which to delete a document. | 60 | | filterPayload | string | true | The MongoDB query filter document to select the document for deletion. Uses `{{json .param_name}}` for templating. | 61 | | filterParams | list | true | A list of parameter objects that define the variables used in the `filterPayload`. | 62 | ``` -------------------------------------------------------------------------------- /internal/sources/bigtable/bigtable.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 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "cloud.google.com/go/bigtable" 22 | "github.com/goccy/go-yaml" 23 | "github.com/googleapis/genai-toolbox/internal/sources" 24 | "github.com/googleapis/genai-toolbox/internal/util" 25 | "go.opentelemetry.io/otel/trace" 26 | "google.golang.org/api/option" 27 | ) 28 | 29 | const SourceKind string = "bigtable" 30 | 31 | // validate interface 32 | var _ sources.SourceConfig = Config{} 33 | 34 | func init() { 35 | if !sources.Register(SourceKind, newConfig) { 36 | panic(fmt.Sprintf("source kind %q already registered", SourceKind)) 37 | } 38 | } 39 | 40 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { 41 | actual := Config{Name: name} 42 | if err := decoder.DecodeContext(ctx, &actual); err != nil { 43 | return nil, err 44 | } 45 | return actual, nil 46 | } 47 | 48 | type Config struct { 49 | Name string `yaml:"name" validate:"required"` 50 | Kind string `yaml:"kind" validate:"required"` 51 | Project string `yaml:"project" validate:"required"` 52 | Instance string `yaml:"instance" 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 | client, err := initBigtableClient(ctx, tracer, r.Name, r.Project, r.Instance) 61 | if err != nil { 62 | return nil, fmt.Errorf("unable to create client: %w", err) 63 | } 64 | 65 | s := &Source{ 66 | Name: r.Name, 67 | Kind: SourceKind, 68 | Client: client, 69 | } 70 | return s, nil 71 | } 72 | 73 | var _ sources.Source = &Source{} 74 | 75 | type Source struct { 76 | Name string `yaml:"name"` 77 | Kind string `yaml:"kind"` 78 | Client *bigtable.Client 79 | } 80 | 81 | func (s *Source) SourceKind() string { 82 | return SourceKind 83 | } 84 | 85 | func (s *Source) BigtableClient() *bigtable.Client { 86 | return s.Client 87 | } 88 | 89 | func initBigtableClient(ctx context.Context, tracer trace.Tracer, name, project, instance string) (*bigtable.Client, error) { 90 | //nolint:all // Reassigned ctx 91 | ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) 92 | defer span.End() 93 | 94 | // Set up Bigtable data operations client. 95 | poolSize := 10 96 | userAgent, err := util.UserAgentFromContext(ctx) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | client, err := bigtable.NewClient(ctx, project, instance, option.WithUserAgent(userAgent), option.WithGRPCConnectionPool(poolSize)) 102 | 103 | if err != nil { 104 | return nil, fmt.Errorf("unable to create bigtable.NewClient: %w", err) 105 | } 106 | 107 | return client, nil 108 | } 109 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/cloudsql/cloudsqlmssqlcreateinstance.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: cloud-sql-mssql-create-instance 3 | type: docs 4 | weight: 10 5 | description: "Create a Cloud SQL for SQL Server instance." 6 | --- 7 | 8 | The `cloud-sql-mssql-create-instance` tool creates a Cloud SQL for SQL Server 9 | instance using the Cloud SQL Admin API. 10 | 11 | {{< notice info dd>}} 12 | This tool uses a `source` of kind `cloud-sql-admin`. 13 | {{< /notice >}} 14 | 15 | ## Example 16 | 17 | ```yaml 18 | tools: 19 | create-sql-instance: 20 | kind: cloud-sql-mssql-create-instance 21 | source: cloud-sql-admin-source 22 | description: "Creates a SQL Server instance using `Production` and `Development` presets. For the `Development` template, it chooses a 2 vCPU, 8 GiB RAM (`db-custom-2-8192`) configuration with Non-HA/zonal availability. For the `Production` template, it chooses a 4 vCPU, 26 GiB RAM (`db-custom-4-26624`) configuration with HA/regional availability. The Enterprise edition is used in both cases. The default database version is `SQLSERVER_2022_STANDARD`. The agent should ask the user if they want to use a different version." 23 | ``` 24 | 25 | ## Reference 26 | 27 | ### Tool Configuration 28 | 29 | | **field** | **type** | **required** | **description** | 30 | | ----------- | :------: | :----------: | ------------------------------------------------ | 31 | | kind | string | true | Must be "cloud-sql-mssql-create-instance". | 32 | | source | string | true | The name of the `cloud-sql-admin` source to use. | 33 | | description | string | false | A description of the tool. | 34 | 35 | ### Tool Inputs 36 | 37 | | **parameter** | **type** | **required** | **description** | 38 | |-----------------|:--------:|:------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------| 39 | | project | string | true | The project ID. | 40 | | name | string | true | The name of the instance. | 41 | | databaseVersion | string | false | The database version for SQL Server. If not specified, defaults to the latest available version (e.g., SQLSERVER_2022_STANDARD). | 42 | | rootPassword | string | true | The root password for the instance. | 43 | | editionPreset | string | false | The edition of the instance. Can be `Production` or `Development`. This determines the default machine type and availability. Defaults to `Development`. | 44 | ``` -------------------------------------------------------------------------------- /.ci/quickstart_test/run_py_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_python" 20 | QUICKSTART_PYTHON_DIR="docs/en/getting-started/quickstart/python" 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 | python3-venv \ 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 | set -e 75 | setup_orch_table 76 | cd "$orch_dir" 77 | local VENV_DIR=".venv" 78 | python3 -m venv "$VENV_DIR" 79 | source "$VENV_DIR/bin/activate" 80 | pip install -r requirements.txt 81 | echo "--- Running tests for $orch_name ---" 82 | cd .. 83 | ORCH_NAME="$orch_name" pytest 84 | rm -rf "$VENV_DIR" 85 | ) 86 | } 87 | 88 | cleanup_all() { 89 | echo "--- Final cleanup: Shutting down processes and dropping table ---" 90 | if [ -n "$TOOLBOX_PID" ]; then 91 | kill $TOOLBOX_PID || true 92 | fi 93 | if [ -n "$PROXY_PID" ]; then 94 | kill $PROXY_PID || true 95 | fi 96 | } 97 | trap cleanup_all EXIT 98 | 99 | # Main script execution 100 | install_system_packages 101 | start_cloud_sql_proxy 102 | 103 | export PGHOST=127.0.0.1 104 | export PGPORT=5432 105 | export PGPASSWORD="$DB_PASSWORD" 106 | export GOOGLE_API_KEY="$GOOGLE_API_KEY" 107 | 108 | setup_toolbox 109 | 110 | for ORCH_DIR in "$QUICKSTART_PYTHON_DIR"/*/; do 111 | if [ ! -d "$ORCH_DIR" ]; then 112 | continue 113 | fi 114 | run_orch_test "$ORCH_DIR" 115 | done 116 | ``` -------------------------------------------------------------------------------- /docs/en/resources/tools/bigquery/bigquery-conversational-analytics.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | title: "bigquery-conversational-analytics" 3 | type: docs 4 | weight: 1 5 | description: > 6 | A "bigquery-conversational-analytics" tool allows conversational interaction with a BigQuery source. 7 | aliases: 8 | - /resources/tools/bigquery-conversational-analytics 9 | --- 10 | 11 | ## About 12 | 13 | A `bigquery-conversational-analytics` tool allows you to ask questions about 14 | your data in natural language. 15 | 16 | This function takes a user's question (which can include conversational history 17 | for context) and references to specific BigQuery tables, and sends them to a 18 | stateless conversational API. 19 | 20 | The API uses a GenAI agent to understand the question, generate and execute SQL 21 | queries and Python code, and formulate an answer. This function returns a 22 | detailed, sequential log of this entire process, which includes any generated 23 | SQL or Python code, the data retrieved, and the final text answer. 24 | 25 | **Note**: This tool requires additional setup in your project. Please refer to 26 | the official [Conversational Analytics API 27 | documentation](https://cloud.google.com/gemini/docs/conversational-analytics-api/overview) 28 | for instructions. 29 | 30 | It's compatible with the following sources: 31 | 32 | - [bigquery](../../sources/bigquery.md) 33 | 34 | `bigquery-conversational-analytics` accepts the following parameters: 35 | 36 | - **`user_query_with_context`:** The user's question, potentially including 37 | conversation history and system instructions for context. 38 | - **`table_references`:** A JSON string of a list of BigQuery tables to use as 39 | context. Each object in the list must contain `projectId`, `datasetId`, and 40 | `tableId`. Example: `'[{"projectId": "my-gcp-project", "datasetId": "my_dataset", "tableId": "my_table"}]'` 41 | 42 | The tool's behavior regarding these parameters is influenced by the `allowedDatasets` 43 | restriction on the `bigquery` source: 44 | - **Without `allowedDatasets` restriction:** The tool can use tables from any 45 | dataset specified in the `table_references` parameter. 46 | - **With `allowedDatasets` restriction:** Before processing the request, the tool 47 | verifies that every table in `table_references` belongs to a dataset in the allowed 48 | list. If any table is from a dataset that is not in the list, the request is denied. 49 | 50 | ## Example 51 | 52 | ```yaml 53 | tools: 54 | ask_data_insights: 55 | kind: bigquery-conversational-analytics 56 | source: my-bigquery-source 57 | description: | 58 | Use this tool to perform data analysis, get insights, or answer complex 59 | questions about the contents of specific BigQuery tables. 60 | ``` 61 | 62 | ## Reference 63 | 64 | | **field** | **type** | **required** | **description** | 65 | |-------------|:--------:|:------------:|----------------------------------------------------| 66 | | kind | string | true | Must be "bigquery-conversational-analytics". | 67 | | source | string | true | Name of the source for chat. | 68 | | description | string | true | Description of the tool that is passed to the LLM. | 69 | ``` -------------------------------------------------------------------------------- /internal/testutils/testutils.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 testutils 16 | 17 | import ( 18 | "bufio" 19 | "context" 20 | "fmt" 21 | "io" 22 | "os" 23 | "regexp" 24 | "strings" 25 | 26 | "github.com/googleapis/genai-toolbox/internal/log" 27 | "github.com/googleapis/genai-toolbox/internal/util" 28 | ) 29 | 30 | // formatYaml is a utility function for stripping out tabs in multiline strings 31 | func FormatYaml(in string) []byte { 32 | // removes any leading indentation(tabs) 33 | in = strings.ReplaceAll(in, "\n\t", "\n ") 34 | // converts remaining indentation 35 | in = strings.ReplaceAll(in, "\t", " ") 36 | return []byte(in) 37 | } 38 | 39 | // ContextWithNewLogger create a new context with new logger 40 | func ContextWithNewLogger() (context.Context, error) { 41 | ctx := context.Background() 42 | logger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info") 43 | if err != nil { 44 | return nil, fmt.Errorf("unable to create logger: %s", err) 45 | } 46 | return util.WithLogger(ctx, logger), nil 47 | } 48 | 49 | // WaitForString waits until the server logs a single line that matches the provided regex. 50 | // returns the output of whatever the server sent so far. 51 | func WaitForString(ctx context.Context, re *regexp.Regexp, pr io.ReadCloser) (string, error) { 52 | in := bufio.NewReader(pr) 53 | ctx, cancel := context.WithCancel(ctx) 54 | defer cancel() 55 | 56 | // read lines in background, sending result of each read over a channel 57 | // this allows us to use in.ReadString without blocking 58 | type result struct { 59 | s string 60 | err error 61 | } 62 | output := make(chan result) 63 | go func() { 64 | defer close(output) 65 | for { 66 | select { 67 | case <-ctx.Done(): 68 | // if the context is canceled, the orig thread will send back the error 69 | // so we can just exit the goroutine here 70 | return 71 | default: 72 | // otherwise read a line from the output 73 | s, err := in.ReadString('\n') 74 | if err != nil { 75 | output <- result{err: err} 76 | return 77 | } 78 | output <- result{s: s} 79 | // if that last string matched, exit the goroutine 80 | if re.MatchString(s) { 81 | return 82 | } 83 | } 84 | } 85 | }() 86 | 87 | // collect the output until the ctx is canceled, an error was hit, 88 | // or match was found (which is indicated the channel is closed) 89 | var sb strings.Builder 90 | for { 91 | select { 92 | case <-ctx.Done(): 93 | // if ctx is done, return that error 94 | return sb.String(), ctx.Err() 95 | case o, ok := <-output: 96 | if !ok { 97 | // match was found! 98 | return sb.String(), nil 99 | } 100 | if o.err != nil { 101 | // error was found! 102 | return sb.String(), o.err 103 | } 104 | sb.WriteString(o.s) 105 | } 106 | } 107 | } 108 | ``` -------------------------------------------------------------------------------- /internal/sources/mongodb/mongodb.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 mongodb 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/googleapis/genai-toolbox/internal/util" 24 | "go.mongodb.org/mongo-driver/mongo" 25 | "go.mongodb.org/mongo-driver/mongo/options" 26 | "go.opentelemetry.io/otel/trace" 27 | ) 28 | 29 | const SourceKind string = "mongodb" 30 | 31 | // validate interface 32 | var _ sources.SourceConfig = Config{} 33 | 34 | func init() { 35 | if !sources.Register(SourceKind, newConfig) { 36 | panic(fmt.Sprintf("source kind %q already registered", SourceKind)) 37 | } 38 | } 39 | 40 | func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) { 41 | actual := Config{Name: name} 42 | if err := decoder.DecodeContext(ctx, &actual); err != nil { 43 | return nil, err 44 | } 45 | return actual, nil 46 | } 47 | 48 | type Config struct { 49 | Name string `yaml:"name" validate:"required"` 50 | Kind string `yaml:"kind" validate:"required"` 51 | Uri string `yaml:"uri" validate:"required"` // MongoDB Atlas connection URI 52 | } 53 | 54 | func (r Config) SourceConfigKind() string { 55 | return SourceKind 56 | } 57 | 58 | func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) { 59 | client, err := initMongoDBClient(ctx, tracer, r.Name, r.Uri) 60 | if err != nil { 61 | return nil, fmt.Errorf("unable to create MongoDB client: %w", err) 62 | } 63 | 64 | // Verify the connection 65 | err = client.Ping(ctx, nil) 66 | if err != nil { 67 | return nil, fmt.Errorf("unable to connect successfully: %w", err) 68 | } 69 | 70 | s := &Source{ 71 | Name: r.Name, 72 | Kind: SourceKind, 73 | Client: client, 74 | } 75 | return s, nil 76 | } 77 | 78 | var _ sources.Source = &Source{} 79 | 80 | type Source struct { 81 | Name string `yaml:"name"` 82 | Kind string `yaml:"kind"` 83 | Client *mongo.Client 84 | } 85 | 86 | func (s *Source) SourceKind() string { 87 | return SourceKind 88 | } 89 | 90 | func (s *Source) MongoClient() *mongo.Client { 91 | return s.Client 92 | } 93 | 94 | func initMongoDBClient(ctx context.Context, tracer trace.Tracer, name, uri string) (*mongo.Client, error) { 95 | // Start a tracing span 96 | ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceKind, name) 97 | defer span.End() 98 | 99 | userAgent, err := util.UserAgentFromContext(ctx) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | // Create a new MongoDB client 105 | clientOpts := options.Client().ApplyURI(uri).SetAppName(userAgent) 106 | client, err := mongo.Connect(ctx, clientOpts) 107 | if err != nil { 108 | return nil, fmt.Errorf("unable to create MongoDB client: %w", err) 109 | } 110 | 111 | return client, nil 112 | } 113 | ``` -------------------------------------------------------------------------------- /internal/tools/tools_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 tools_test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-cmp/cmp" 21 | "github.com/googleapis/genai-toolbox/internal/tools" 22 | ) 23 | 24 | func TestGetMcpManifestMetadata(t *testing.T) { 25 | authServices := []tools.ParamAuthService{ 26 | { 27 | Name: "my-google-auth-service", 28 | Field: "auth_field", 29 | }, 30 | { 31 | Name: "other-auth-service", 32 | Field: "other_auth_field", 33 | }} 34 | tcs := []struct { 35 | desc string 36 | name string 37 | description string 38 | authInvoke []string 39 | params tools.Parameters 40 | wantMetadata map[string]any 41 | }{ 42 | { 43 | desc: "basic manifest without metadata", 44 | name: "basic", 45 | description: "foo bar", 46 | authInvoke: []string{}, 47 | params: tools.Parameters{tools.NewStringParameter("string-param", "string parameter")}, 48 | wantMetadata: nil, 49 | }, 50 | { 51 | desc: "with auth invoke metadata", 52 | name: "basic", 53 | description: "foo bar", 54 | authInvoke: []string{"auth1", "auth2"}, 55 | params: tools.Parameters{tools.NewStringParameter("string-param", "string parameter")}, 56 | wantMetadata: map[string]any{"toolbox/authInvoke": []string{"auth1", "auth2"}}, 57 | }, 58 | { 59 | desc: "with auth param metadata", 60 | name: "basic", 61 | description: "foo bar", 62 | authInvoke: []string{}, 63 | params: tools.Parameters{tools.NewStringParameterWithAuth("string-param", "string parameter", authServices)}, 64 | wantMetadata: map[string]any{ 65 | "toolbox/authParam": map[string][]string{ 66 | "string-param": []string{"my-google-auth-service", "other-auth-service"}, 67 | }, 68 | }, 69 | }, 70 | { 71 | desc: "with auth invoke and auth param metadata", 72 | name: "basic", 73 | description: "foo bar", 74 | authInvoke: []string{"auth1", "auth2"}, 75 | params: tools.Parameters{tools.NewStringParameterWithAuth("string-param", "string parameter", authServices)}, 76 | wantMetadata: map[string]any{ 77 | "toolbox/authInvoke": []string{"auth1", "auth2"}, 78 | "toolbox/authParam": map[string][]string{ 79 | "string-param": []string{"my-google-auth-service", "other-auth-service"}, 80 | }, 81 | }, 82 | }, 83 | } 84 | for _, tc := range tcs { 85 | t.Run(tc.name, func(t *testing.T) { 86 | got := tools.GetMcpManifest(tc.name, tc.description, tc.authInvoke, tc.params) 87 | gotM := got.Metadata 88 | if diff := cmp.Diff(tc.wantMetadata, gotM); diff != "" { 89 | t.Fatalf("unexpected metadata (-want +got):\n%s", diff) 90 | } 91 | }) 92 | } 93 | } 94 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerhealthpulse/lookerhealthpulse_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 lookerhealthpulse_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 | lhp "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerhealthpulse" 26 | ) 27 | 28 | func TestParseFromYamlLookerHealthPulse(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-health-pulse 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lhp.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-health-pulse", 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 | func TestFailParseFromYamlLookerHealthPulse(t *testing.T) { 76 | ctx, err := testutils.ContextWithNewLogger() 77 | if err != nil { 78 | t.Fatalf("unexpected error: %s", err) 79 | } 80 | tcs := []struct { 81 | desc string 82 | in string 83 | err string 84 | }{ 85 | { 86 | desc: "Invalid field", 87 | in: ` 88 | tools: 89 | example_tool: 90 | kind: looker-health-pulse 91 | source: my-instance 92 | invalid_field: true 93 | `, 94 | err: "unable to parse tool \"example_tool\" as kind \"looker-health-pulse\": [2:1] unknown field \"invalid_field\"", 95 | }, 96 | } 97 | for _, tc := range tcs { 98 | t.Run(tc.desc, func(t *testing.T) { 99 | got := struct { 100 | Tools server.ToolConfigs `yaml:"tools"` 101 | }{} 102 | // Parse contents 103 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 104 | if err == nil { 105 | t.Fatalf("expect parsing to fail") 106 | } 107 | errStr := err.Error() 108 | if !strings.Contains(errStr, tc.err) { 109 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 110 | } 111 | }) 112 | } 113 | } 114 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerhealthvacuum/lookerhealthvacuum_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 lookerhealthvacuum_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 | lhv "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerhealthvacuum" 26 | ) 27 | 28 | func TestParseFromYamlLookerHealthVacuum(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-health-vacuum 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lhv.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-health-vacuum", 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 | func TestFailParseFromYamlLookerHealthVacuum(t *testing.T) { 76 | ctx, err := testutils.ContextWithNewLogger() 77 | if err != nil { 78 | t.Fatalf("unexpected error: %s", err) 79 | } 80 | tcs := []struct { 81 | desc string 82 | in string 83 | err string 84 | }{ 85 | { 86 | desc: "Invalid field", 87 | in: ` 88 | tools: 89 | example_tool: 90 | kind: looker-health-vacuum 91 | source: my-instance 92 | invalid_field: true 93 | `, 94 | err: "unable to parse tool \"example_tool\" as kind \"looker-health-vacuum\": [2:1] unknown field \"invalid_field\"", 95 | }, 96 | } 97 | for _, tc := range tcs { 98 | t.Run(tc.desc, func(t *testing.T) { 99 | got := struct { 100 | Tools server.ToolConfigs `yaml:"tools"` 101 | }{} 102 | // Parse contents 103 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 104 | if err == nil { 105 | t.Fatalf("expect parsing to fail") 106 | } 107 | errStr := err.Error() 108 | if !strings.Contains(errStr, tc.err) { 109 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 110 | } 111 | }) 112 | } 113 | } 114 | ``` -------------------------------------------------------------------------------- /internal/tools/spanner/spannerlisttables/spannerlisttables_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 spannerlisttables_test 16 | 17 | import ( 18 | "testing" 19 | 20 | yaml "github.com/goccy/go-yaml" 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/googleapis/genai-toolbox/internal/server" 23 | "github.com/googleapis/genai-toolbox/internal/testutils" 24 | "github.com/googleapis/genai-toolbox/internal/tools/spanner/spannerlisttables" 25 | ) 26 | 27 | func TestParseFromYamlListTables(t *testing.T) { 28 | ctx, err := testutils.ContextWithNewLogger() 29 | if err != nil { 30 | t.Fatalf("unexpected error: %s", err) 31 | } 32 | tcs := []struct { 33 | desc string 34 | in string 35 | want server.ToolConfigs 36 | }{ 37 | { 38 | desc: "basic example", 39 | in: ` 40 | tools: 41 | example_tool: 42 | kind: spanner-list-tables 43 | source: my-spanner-instance 44 | description: Lists tables in the database 45 | `, 46 | want: server.ToolConfigs{ 47 | "example_tool": spannerlisttables.Config{ 48 | Name: "example_tool", 49 | Kind: "spanner-list-tables", 50 | Source: "my-spanner-instance", 51 | Description: "Lists tables in the database", 52 | AuthRequired: []string{}, 53 | }, 54 | }, 55 | }, 56 | { 57 | desc: "with auth required", 58 | in: ` 59 | tools: 60 | example_tool: 61 | kind: spanner-list-tables 62 | source: my-spanner-instance 63 | description: Lists tables in the database 64 | authRequired: 65 | - auth1 66 | - auth2 67 | `, 68 | want: server.ToolConfigs{ 69 | "example_tool": spannerlisttables.Config{ 70 | Name: "example_tool", 71 | Kind: "spanner-list-tables", 72 | Source: "my-spanner-instance", 73 | Description: "Lists tables in the database", 74 | AuthRequired: []string{"auth1", "auth2"}, 75 | }, 76 | }, 77 | }, 78 | { 79 | desc: "minimal config", 80 | in: ` 81 | tools: 82 | example_tool: 83 | kind: spanner-list-tables 84 | source: my-spanner-instance 85 | `, 86 | want: server.ToolConfigs{ 87 | "example_tool": spannerlisttables.Config{ 88 | Name: "example_tool", 89 | Kind: "spanner-list-tables", 90 | Source: "my-spanner-instance", 91 | Description: "", 92 | AuthRequired: []string{}, 93 | }, 94 | }, 95 | }, 96 | } 97 | for _, tc := range tcs { 98 | t.Run(tc.desc, func(t *testing.T) { 99 | got := struct { 100 | Tools server.ToolConfigs `yaml:"tools"` 101 | }{} 102 | // Parse contents 103 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 104 | if err != nil { 105 | t.Fatalf("unable to unmarshal: %s", err) 106 | } 107 | if diff := cmp.Diff(tc.want, got.Tools); diff != "" { 108 | t.Fatalf("incorrect parse: diff %v", diff) 109 | } 110 | }) 111 | } 112 | } 113 | ``` -------------------------------------------------------------------------------- /internal/tools/looker/lookerhealthanalyze/lookerhealthanalyze_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 lookerhealthanalyze_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 | lha "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerhealthanalyze" 26 | ) 27 | 28 | func TestParseFromYamlLookerHealthAnalyze(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-health-analyze 44 | source: my-instance 45 | description: some description 46 | `, 47 | want: server.ToolConfigs{ 48 | "example_tool": lha.Config{ 49 | Name: "example_tool", 50 | Kind: "looker-health-analyze", 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 | func TestFailParseFromYamlLookerHealthAnalyze(t *testing.T) { 76 | ctx, err := testutils.ContextWithNewLogger() 77 | if err != nil { 78 | t.Fatalf("unexpected error: %s", err) 79 | } 80 | tcs := []struct { 81 | desc string 82 | in string 83 | err string 84 | }{ 85 | { 86 | desc: "Invalid field", 87 | in: ` 88 | tools: 89 | example_tool: 90 | kind: looker-health-analyze 91 | source: my-instance 92 | invalid_field: true 93 | `, 94 | err: "unable to parse tool \"example_tool\" as kind \"looker-health-analyze\": [2:1] unknown field \"invalid_field\"", 95 | }, 96 | } 97 | for _, tc := range tcs { 98 | t.Run(tc.desc, func(t *testing.T) { 99 | got := struct { 100 | Tools server.ToolConfigs `yaml:"tools"` 101 | }{} 102 | // Parse contents 103 | err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got) 104 | if err == nil { 105 | t.Fatalf("expect parsing to fail") 106 | } 107 | errStr := err.Error() 108 | if !strings.Contains(errStr, tc.err) { 109 | t.Fatalf("unexpected error string: got %q, want substring %q", errStr, tc.err) 110 | } 111 | }) 112 | } 113 | } 114 | ``` -------------------------------------------------------------------------------- /.github/workflows/deploy_versioned_docs.yaml: -------------------------------------------------------------------------------- ```yaml 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 | name: "Deploy Versioned Docs" 16 | 17 | permissions: 18 | contents: write 19 | 20 | on: 21 | release: 22 | types: [published] 23 | 24 | jobs: 25 | deploy: 26 | runs-on: ubuntu-24.04 27 | # This shared concurrency group ensures only one docs deployment runs at a time. 28 | concurrency: 29 | group: docs-deployment 30 | cancel-in-progress: false 31 | steps: 32 | - name: Checkout Code at Tag 33 | uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 34 | with: 35 | ref: ${{ github.event.release.tag_name }} 36 | 37 | - name: Get Version from Release Tag 38 | run: echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV 39 | 40 | - name: Setup Hugo 41 | uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3 42 | with: 43 | hugo-version: "0.145.0" 44 | extended: true 45 | 46 | - name: Setup Node 47 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 48 | with: 49 | node-version: "22" 50 | 51 | - name: Install Dependencies 52 | run: npm ci 53 | working-directory: .hugo 54 | 55 | - name: Build Hugo Site 56 | run: hugo --minify 57 | working-directory: .hugo 58 | env: 59 | HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/${{ env.VERSION }}/ 60 | HUGO_RELATIVEURLS: false 61 | 62 | - name: Deploy 63 | uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4 64 | with: 65 | github_token: ${{ secrets.GITHUB_TOKEN }} 66 | publish_dir: .hugo/public 67 | publish_branch: versioned-gh-pages 68 | destination_dir: ./${{ env.VERSION }} 69 | keep_files: true 70 | commit_message: "deploy: docs for ${{ env.VERSION }}" 71 | 72 | - name: Clean Build Directory 73 | run: rm -rf .hugo/public 74 | 75 | - name: Build Hugo Site 76 | run: hugo --minify 77 | working-directory: .hugo 78 | env: 79 | HUGO_BASEURL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/ 80 | HUGO_RELATIVEURLS: false 81 | 82 | - name: Deploy to root 83 | uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4 84 | with: 85 | github_token: ${{ secrets.GITHUB_TOKEN }} 86 | publish_dir: .hugo/public 87 | publish_branch: versioned-gh-pages 88 | keep_files: true 89 | allow_empty_commit: true 90 | commit_message: "deploy: docs to root for ${{ env.VERSION }}" 91 | ```