mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-14 09:05:04 -05:00
Compare commits
22 Commits
httplog
...
pr/dumians
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
251ef22839 | ||
|
|
8dc4bd7dd6 | ||
|
|
a00d0edcd5 | ||
|
|
d1eb1799a0 | ||
|
|
2b81d6099a | ||
|
|
53865b6e21 | ||
|
|
6843b46328 | ||
|
|
05152f732d | ||
|
|
a48101b3c5 | ||
|
|
418d6d791e | ||
|
|
452d686750 | ||
|
|
8fb74263a7 | ||
|
|
09f3bc7959 | ||
|
|
7970b8787e | ||
|
|
0b7a86ae58 | ||
|
|
0797142103 | ||
|
|
f26750e834 | ||
|
|
c46d7d6fa0 | ||
|
|
5e2034d146 | ||
|
|
e2272ccdbc | ||
|
|
97f68129f5 | ||
|
|
fea96fed03 |
@@ -943,7 +943,7 @@ steps:
|
|||||||
# Install the C compiler and Oracle SDK headers needed for cgo
|
# Install the C compiler and Oracle SDK headers needed for cgo
|
||||||
dnf install -y gcc oracle-instantclient-devel
|
dnf install -y gcc oracle-instantclient-devel
|
||||||
# Install Go
|
# Install Go
|
||||||
curl -L -o go.tar.gz "https://go.dev/dl/go1.25.1.linux-amd64.tar.gz"
|
curl -L -o go.tar.gz "https://go.dev/dl/go1.25.5.linux-amd64.tar.gz"
|
||||||
tar -C /usr/local -xzf go.tar.gz
|
tar -C /usr/local -xzf go.tar.gz
|
||||||
export PATH="/usr/local/go/bin:$$PATH"
|
export PATH="/usr/local/go/bin:$$PATH"
|
||||||
|
|
||||||
|
|||||||
1
.github/release-please.yml
vendored
1
.github/release-please.yml
vendored
@@ -37,6 +37,7 @@ extraFiles: [
|
|||||||
"docs/en/how-to/connect-ide/postgres_mcp.md",
|
"docs/en/how-to/connect-ide/postgres_mcp.md",
|
||||||
"docs/en/how-to/connect-ide/neo4j_mcp.md",
|
"docs/en/how-to/connect-ide/neo4j_mcp.md",
|
||||||
"docs/en/how-to/connect-ide/sqlite_mcp.md",
|
"docs/en/how-to/connect-ide/sqlite_mcp.md",
|
||||||
|
"docs/en/how-to/connect-ide/oracle_mcp.md",
|
||||||
"gemini-extension.json",
|
"gemini-extension.json",
|
||||||
{
|
{
|
||||||
"type": "json",
|
"type": "json",
|
||||||
|
|||||||
499
cmd/root_test.go
499
cmd/root_test.go
@@ -618,6 +618,505 @@ func TestSingleEdit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrebuiltTools(t *testing.T) {
|
||||||
|
// Get prebuilt configs
|
||||||
|
alloydb_omni_config, _ := prebuiltconfigs.Get("alloydb-omni")
|
||||||
|
alloydb_admin_config, _ := prebuiltconfigs.Get("alloydb-postgres-admin")
|
||||||
|
alloydb_config, _ := prebuiltconfigs.Get("alloydb-postgres")
|
||||||
|
bigquery_config, _ := prebuiltconfigs.Get("bigquery")
|
||||||
|
clickhouse_config, _ := prebuiltconfigs.Get("clickhouse")
|
||||||
|
cloudsqlpg_config, _ := prebuiltconfigs.Get("cloud-sql-postgres")
|
||||||
|
cloudsqlpg_admin_config, _ := prebuiltconfigs.Get("cloud-sql-postgres-admin")
|
||||||
|
cloudsqlmysql_config, _ := prebuiltconfigs.Get("cloud-sql-mysql")
|
||||||
|
cloudsqlmysql_admin_config, _ := prebuiltconfigs.Get("cloud-sql-mysql-admin")
|
||||||
|
cloudsqlmssql_config, _ := prebuiltconfigs.Get("cloud-sql-mssql")
|
||||||
|
cloudsqlmssql_admin_config, _ := prebuiltconfigs.Get("cloud-sql-mssql-admin")
|
||||||
|
dataplex_config, _ := prebuiltconfigs.Get("dataplex")
|
||||||
|
firestoreconfig, _ := prebuiltconfigs.Get("firestore")
|
||||||
|
mysql_config, _ := prebuiltconfigs.Get("mysql")
|
||||||
|
mssql_config, _ := prebuiltconfigs.Get("mssql")
|
||||||
|
looker_config, _ := prebuiltconfigs.Get("looker")
|
||||||
|
lookerca_config, _ := prebuiltconfigs.Get("looker-conversational-analytics")
|
||||||
|
postgresconfig, _ := prebuiltconfigs.Get("postgres")
|
||||||
|
spanner_config, _ := prebuiltconfigs.Get("spanner")
|
||||||
|
spannerpg_config, _ := prebuiltconfigs.Get("spanner-postgres")
|
||||||
|
mindsdb_config, _ := prebuiltconfigs.Get("mindsdb")
|
||||||
|
sqlite_config, _ := prebuiltconfigs.Get("sqlite")
|
||||||
|
neo4jconfig, _ := prebuiltconfigs.Get("neo4j")
|
||||||
|
alloydbobsvconfig, _ := prebuiltconfigs.Get("alloydb-postgres-observability")
|
||||||
|
cloudsqlpgobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-postgres-observability")
|
||||||
|
cloudsqlmysqlobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-mysql-observability")
|
||||||
|
cloudsqlmssqlobsvconfig, _ := prebuiltconfigs.Get("cloud-sql-mssql-observability")
|
||||||
|
serverless_spark_config, _ := prebuiltconfigs.Get("serverless-spark")
|
||||||
|
cloudhealthcare_config, _ := prebuiltconfigs.Get("cloud-healthcare")
|
||||||
|
snowflake_config, _ := prebuiltconfigs.Get("snowflake")
|
||||||
|
|
||||||
|
// Set environment variables
|
||||||
|
t.Setenv("API_KEY", "your_api_key")
|
||||||
|
|
||||||
|
t.Setenv("BIGQUERY_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("DATAPLEX_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("FIRESTORE_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("FIRESTORE_DATABASE", "your_firestore_db_name")
|
||||||
|
|
||||||
|
t.Setenv("SPANNER_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("SPANNER_INSTANCE", "your_spanner_instance")
|
||||||
|
t.Setenv("SPANNER_DATABASE", "your_spanner_db")
|
||||||
|
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_REGION", "your_gcp_region")
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_CLUSTER", "your_alloydb_cluster")
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_INSTANCE", "your_alloydb_instance")
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_DATABASE", "your_alloydb_db")
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_USER", "your_alloydb_user")
|
||||||
|
t.Setenv("ALLOYDB_POSTGRES_PASSWORD", "your_alloydb_password")
|
||||||
|
|
||||||
|
t.Setenv("ALLOYDB_OMNI_HOST", "localhost")
|
||||||
|
t.Setenv("ALLOYDB_OMNI_PORT", "5432")
|
||||||
|
t.Setenv("ALLOYDB_OMNI_DATABASE", "your_alloydb_db")
|
||||||
|
t.Setenv("ALLOYDB_OMNI_USER", "your_alloydb_user")
|
||||||
|
t.Setenv("ALLOYDB_OMNI_PASSWORD", "your_alloydb_password")
|
||||||
|
|
||||||
|
t.Setenv("CLICKHOUSE_PROTOCOL", "your_clickhouse_protocol")
|
||||||
|
t.Setenv("CLICKHOUSE_DATABASE", "your_clickhouse_database")
|
||||||
|
t.Setenv("CLICKHOUSE_PASSWORD", "your_clickhouse_password")
|
||||||
|
t.Setenv("CLICKHOUSE_USER", "your_clickhouse_user")
|
||||||
|
t.Setenv("CLICKHOUSE_HOST", "your_clickhosue_host")
|
||||||
|
t.Setenv("CLICKHOUSE_PORT", "8123")
|
||||||
|
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_PROJECT", "your_pg_project")
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_INSTANCE", "your_pg_instance")
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_DATABASE", "your_pg_db")
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_REGION", "your_pg_region")
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_USER", "your_pg_user")
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_PASS", "your_pg_pass")
|
||||||
|
|
||||||
|
t.Setenv("CLOUD_SQL_MYSQL_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("CLOUD_SQL_MYSQL_REGION", "your_gcp_region")
|
||||||
|
t.Setenv("CLOUD_SQL_MYSQL_INSTANCE", "your_instance")
|
||||||
|
t.Setenv("CLOUD_SQL_MYSQL_DATABASE", "your_cloudsql_mysql_db")
|
||||||
|
t.Setenv("CLOUD_SQL_MYSQL_USER", "your_cloudsql_mysql_user")
|
||||||
|
t.Setenv("CLOUD_SQL_MYSQL_PASSWORD", "your_cloudsql_mysql_password")
|
||||||
|
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_REGION", "your_gcp_region")
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_INSTANCE", "your_cloudsql_mssql_instance")
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_DATABASE", "your_cloudsql_mssql_db")
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_IP_ADDRESS", "127.0.0.1")
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_USER", "your_cloudsql_mssql_user")
|
||||||
|
t.Setenv("CLOUD_SQL_MSSQL_PASSWORD", "your_cloudsql_mssql_password")
|
||||||
|
t.Setenv("CLOUD_SQL_POSTGRES_PASSWORD", "your_cloudsql_pg_password")
|
||||||
|
|
||||||
|
t.Setenv("SERVERLESS_SPARK_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("SERVERLESS_SPARK_LOCATION", "your_gcp_location")
|
||||||
|
|
||||||
|
t.Setenv("POSTGRES_HOST", "localhost")
|
||||||
|
t.Setenv("POSTGRES_PORT", "5432")
|
||||||
|
t.Setenv("POSTGRES_DATABASE", "your_postgres_db")
|
||||||
|
t.Setenv("POSTGRES_USER", "your_postgres_user")
|
||||||
|
t.Setenv("POSTGRES_PASSWORD", "your_postgres_password")
|
||||||
|
|
||||||
|
t.Setenv("MYSQL_HOST", "localhost")
|
||||||
|
t.Setenv("MYSQL_PORT", "3306")
|
||||||
|
t.Setenv("MYSQL_DATABASE", "your_mysql_db")
|
||||||
|
t.Setenv("MYSQL_USER", "your_mysql_user")
|
||||||
|
t.Setenv("MYSQL_PASSWORD", "your_mysql_password")
|
||||||
|
|
||||||
|
t.Setenv("MSSQL_HOST", "localhost")
|
||||||
|
t.Setenv("MSSQL_PORT", "1433")
|
||||||
|
t.Setenv("MSSQL_DATABASE", "your_mssql_db")
|
||||||
|
t.Setenv("MSSQL_USER", "your_mssql_user")
|
||||||
|
t.Setenv("MSSQL_PASSWORD", "your_mssql_password")
|
||||||
|
|
||||||
|
t.Setenv("MINDSDB_HOST", "localhost")
|
||||||
|
t.Setenv("MINDSDB_PORT", "47334")
|
||||||
|
t.Setenv("MINDSDB_DATABASE", "your_mindsdb_db")
|
||||||
|
t.Setenv("MINDSDB_USER", "your_mindsdb_user")
|
||||||
|
t.Setenv("MINDSDB_PASS", "your_mindsdb_password")
|
||||||
|
|
||||||
|
t.Setenv("LOOKER_BASE_URL", "https://your_company.looker.com")
|
||||||
|
t.Setenv("LOOKER_CLIENT_ID", "your_looker_client_id")
|
||||||
|
t.Setenv("LOOKER_CLIENT_SECRET", "your_looker_client_secret")
|
||||||
|
t.Setenv("LOOKER_VERIFY_SSL", "true")
|
||||||
|
|
||||||
|
t.Setenv("LOOKER_PROJECT", "your_project_id")
|
||||||
|
t.Setenv("LOOKER_LOCATION", "us")
|
||||||
|
|
||||||
|
t.Setenv("SQLITE_DATABASE", "test.db")
|
||||||
|
|
||||||
|
t.Setenv("NEO4J_URI", "bolt://localhost:7687")
|
||||||
|
t.Setenv("NEO4J_DATABASE", "neo4j")
|
||||||
|
t.Setenv("NEO4J_USERNAME", "your_neo4j_user")
|
||||||
|
t.Setenv("NEO4J_PASSWORD", "your_neo4j_password")
|
||||||
|
|
||||||
|
t.Setenv("CLOUD_HEALTHCARE_PROJECT", "your_gcp_project_id")
|
||||||
|
t.Setenv("CLOUD_HEALTHCARE_REGION", "your_gcp_region")
|
||||||
|
t.Setenv("CLOUD_HEALTHCARE_DATASET", "your_healthcare_dataset")
|
||||||
|
|
||||||
|
t.Setenv("SNOWFLAKE_ACCOUNT", "your_account")
|
||||||
|
t.Setenv("SNOWFLAKE_USER", "your_username")
|
||||||
|
t.Setenv("SNOWFLAKE_PASSWORD", "your_pass")
|
||||||
|
t.Setenv("SNOWFLAKE_DATABASE", "your_db")
|
||||||
|
t.Setenv("SNOWFLAKE_SCHEMA", "your_schema")
|
||||||
|
t.Setenv("SNOWFLAKE_WAREHOUSE", "your_wh")
|
||||||
|
t.Setenv("SNOWFLAKE_ROLE", "your_role")
|
||||||
|
|
||||||
|
t.Setenv("ORACLE_USERNAME", "your_oracle_db_username")
|
||||||
|
t.Setenv("ORACLE_PASS", "your_oracle_db_password")
|
||||||
|
t.Setenv("ORACLE_HOST", "your_oracle_db_host")
|
||||||
|
t.Setenv("ORACLE_PORT", "your_oracle_db_port")
|
||||||
|
t.Setenv("ORACLE_SERVICE_NAME", "your_oracle_service_name")
|
||||||
|
t.Setenv("ORACLE_USE_OCI", "your_oracle_db_use_oci")
|
||||||
|
t.Setenv("ORACLE_WALLET_LOCATION", "your_path_to_oracldb_wallet")
|
||||||
|
t.Setenv("ORACLE_TNS_ADMIN", "your_path_to_tns_admin")
|
||||||
|
|
||||||
|
ctx, err := testutils.ContextWithNewLogger()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
tcs := []struct {
|
||||||
|
name string
|
||||||
|
in []byte
|
||||||
|
wantToolset server.ToolsetConfigs
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "alloydb omni prebuilt tools",
|
||||||
|
in: alloydb_omni_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"alloydb_omni_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "alloydb_omni_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_columnar_configurations", "list_columnar_recommended_columns", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "alloydb postgres admin prebuilt tools",
|
||||||
|
in: alloydb_admin_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"alloydb_postgres_admin_tools": tools.ToolsetConfig{
|
||||||
|
Name: "alloydb_postgres_admin_tools",
|
||||||
|
ToolNames: []string{"create_cluster", "wait_for_operation", "create_instance", "list_clusters", "list_instances", "list_users", "create_user", "get_cluster", "get_instance", "get_user"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsql pg admin prebuilt tools",
|
||||||
|
in: cloudsqlpg_admin_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_postgres_admin_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_postgres_admin_tools",
|
||||||
|
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "postgres_upgrade_precheck", "clone_instance", "create_backup", "restore_backup"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsql mysql admin prebuilt tools",
|
||||||
|
in: cloudsqlmysql_admin_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_mysql_admin_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_mysql_admin_tools",
|
||||||
|
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "clone_instance", "create_backup", "restore_backup"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsql mssql admin prebuilt tools",
|
||||||
|
in: cloudsqlmssql_admin_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_mssql_admin_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_mssql_admin_tools",
|
||||||
|
ToolNames: []string{"create_instance", "get_instance", "list_instances", "create_database", "list_databases", "create_user", "wait_for_operation", "clone_instance", "create_backup", "restore_backup"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "alloydb prebuilt tools",
|
||||||
|
in: alloydb_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"alloydb_postgres_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "alloydb_postgres_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bigquery prebuilt tools",
|
||||||
|
in: bigquery_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"bigquery_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "bigquery_database_tools",
|
||||||
|
ToolNames: []string{"analyze_contribution", "ask_data_insights", "execute_sql", "forecast", "get_dataset_info", "get_table_info", "list_dataset_ids", "list_table_ids", "search_catalog"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clickhouse prebuilt tools",
|
||||||
|
in: clickhouse_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"clickhouse_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "clickhouse_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_databases", "list_tables"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsqlpg prebuilt tools",
|
||||||
|
in: cloudsqlpg_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_postgres_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_postgres_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsqlmysql prebuilt tools",
|
||||||
|
in: cloudsqlmysql_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_mysql_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_mysql_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables", "get_query_plan", "list_active_queries", "list_tables_missing_unique_indexes", "list_table_fragmentation"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsqlmssql prebuilt tools",
|
||||||
|
in: cloudsqlmssql_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_mssql_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_mssql_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dataplex prebuilt tools",
|
||||||
|
in: dataplex_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"dataplex_tools": tools.ToolsetConfig{
|
||||||
|
Name: "dataplex_tools",
|
||||||
|
ToolNames: []string{"search_entries", "lookup_entry", "search_aspect_types"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "serverless spark prebuilt tools",
|
||||||
|
in: serverless_spark_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"serverless_spark_tools": tools.ToolsetConfig{
|
||||||
|
Name: "serverless_spark_tools",
|
||||||
|
ToolNames: []string{"list_batches", "get_batch", "cancel_batch", "create_pyspark_batch", "create_spark_batch"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "firestore prebuilt tools",
|
||||||
|
in: firestoreconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"firestore_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "firestore_database_tools",
|
||||||
|
ToolNames: []string{"get_documents", "add_documents", "update_document", "list_collections", "delete_documents", "query_collection", "get_rules", "validate_rules"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mysql prebuilt tools",
|
||||||
|
in: mysql_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"mysql_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "mysql_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables", "get_query_plan", "list_active_queries", "list_tables_missing_unique_indexes", "list_table_fragmentation"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mssql prebuilt tools",
|
||||||
|
in: mssql_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"mssql_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "mssql_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "looker prebuilt tools",
|
||||||
|
in: looker_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"looker_tools": tools.ToolsetConfig{
|
||||||
|
Name: "looker_tools",
|
||||||
|
ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "add_dashboard_filter", "generate_embed_url", "health_pulse", "health_analyze", "health_vacuum", "dev_mode", "get_projects", "get_project_files", "get_project_file", "create_project_file", "update_project_file", "delete_project_file", "validate_project", "get_connections", "get_connection_schemas", "get_connection_databases", "get_connection_tables", "get_connection_table_columns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "looker-conversational-analytics prebuilt tools",
|
||||||
|
in: lookerca_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"looker_conversational_analytics_tools": tools.ToolsetConfig{
|
||||||
|
Name: "looker_conversational_analytics_tools",
|
||||||
|
ToolNames: []string{"ask_data_insights", "get_models", "get_explores"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "postgres prebuilt tools",
|
||||||
|
in: postgresconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"postgres_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "postgres_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "spanner prebuilt tools",
|
||||||
|
in: spanner_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"spanner-database-tools": tools.ToolsetConfig{
|
||||||
|
Name: "spanner-database-tools",
|
||||||
|
ToolNames: []string{"execute_sql", "execute_sql_dql", "list_tables", "list_graphs"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "spanner pg prebuilt tools",
|
||||||
|
in: spannerpg_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"spanner_postgres_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "spanner_postgres_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "execute_sql_dql", "list_tables"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mindsdb prebuilt tools",
|
||||||
|
in: mindsdb_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"mindsdb-tools": tools.ToolsetConfig{
|
||||||
|
Name: "mindsdb-tools",
|
||||||
|
ToolNames: []string{"mindsdb-execute-sql", "mindsdb-sql"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sqlite prebuilt tools",
|
||||||
|
in: sqlite_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"sqlite_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "sqlite_database_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "neo4j prebuilt tools",
|
||||||
|
in: neo4jconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"neo4j_database_tools": tools.ToolsetConfig{
|
||||||
|
Name: "neo4j_database_tools",
|
||||||
|
ToolNames: []string{"execute_cypher", "get_schema"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "alloydb postgres observability prebuilt tools",
|
||||||
|
in: alloydbobsvconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"alloydb_postgres_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||||
|
Name: "alloydb_postgres_cloud_monitoring_tools",
|
||||||
|
ToolNames: []string{"get_system_metrics", "get_query_metrics"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsql postgres observability prebuilt tools",
|
||||||
|
in: cloudsqlpgobsvconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_postgres_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_postgres_cloud_monitoring_tools",
|
||||||
|
ToolNames: []string{"get_system_metrics", "get_query_metrics"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsql mysql observability prebuilt tools",
|
||||||
|
in: cloudsqlmysqlobsvconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_mysql_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_mysql_cloud_monitoring_tools",
|
||||||
|
ToolNames: []string{"get_system_metrics", "get_query_metrics"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloudsql mssql observability prebuilt tools",
|
||||||
|
in: cloudsqlmssqlobsvconfig,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_sql_mssql_cloud_monitoring_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_sql_mssql_cloud_monitoring_tools",
|
||||||
|
ToolNames: []string{"get_system_metrics"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cloud healthcare prebuilt tools",
|
||||||
|
in: cloudhealthcare_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"cloud_healthcare_dataset_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_healthcare_dataset_tools",
|
||||||
|
ToolNames: []string{"get_dataset", "list_dicom_stores", "list_fhir_stores"},
|
||||||
|
},
|
||||||
|
"cloud_healthcare_fhir_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_healthcare_fhir_tools",
|
||||||
|
ToolNames: []string{"get_fhir_store", "get_fhir_store_metrics", "get_fhir_resource", "fhir_patient_search", "fhir_patient_everything", "fhir_fetch_page"},
|
||||||
|
},
|
||||||
|
"cloud_healthcare_dicom_tools": tools.ToolsetConfig{
|
||||||
|
Name: "cloud_healthcare_dicom_tools",
|
||||||
|
ToolNames: []string{"get_dicom_store", "get_dicom_store_metrics", "search_dicom_studies", "search_dicom_series", "search_dicom_instances", "retrieve_rendered_dicom_instance"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Snowflake prebuilt tool",
|
||||||
|
in: snowflake_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"snowflake_tools": tools.ToolsetConfig{
|
||||||
|
Name: "snowflake_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Oracle prebuilt tool",
|
||||||
|
in: oracle_config,
|
||||||
|
wantToolset: server.ToolsetConfigs{
|
||||||
|
"oracle_tools": tools.ToolsetConfig{
|
||||||
|
Name: "oracle_tools",
|
||||||
|
ToolNames: []string{"execute_sql", "list_tables","list_active_sessions","list_top_sql_by_resource","get_query_plan","list_tablespace_usage"},
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tcs {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
toolsFile, err := parseToolsFile(ctx, tc.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse input: %v", err)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(tc.wantToolset, toolsFile.Toolsets); diff != "" {
|
||||||
|
t.Fatalf("incorrect tools parse: diff %v", diff)
|
||||||
|
}
|
||||||
|
// Prebuilt configs do not have prompts, so assert empty maps.
|
||||||
|
if len(toolsFile.Prompts) != 0 {
|
||||||
|
t.Fatalf("expected empty prompts map for prebuilt config, got: %v", toolsFile.Prompts)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMutuallyExclusiveFlags(t *testing.T) {
|
func TestMutuallyExclusiveFlags(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|||||||
335
docs/en/how-to/connect-ide/oracle_mcp.md
Normal file
335
docs/en/how-to/connect-ide/oracle_mcp.md
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
---
|
||||||
|
title: "Oracle using MCP"
|
||||||
|
type: docs
|
||||||
|
weight: 2
|
||||||
|
description: >
|
||||||
|
Connect your IDE to Oracle using Toolbox.
|
||||||
|
---
|
||||||
|
|
||||||
|
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is
|
||||||
|
an open protocol for connecting Large Language Models (LLMs) to data sources
|
||||||
|
like Oracle. This guide covers how to use [MCP Toolbox for Databases][toolbox]
|
||||||
|
to expose your developer assistant tools to an Oracle instance:
|
||||||
|
|
||||||
|
* [Cursor][cursor]
|
||||||
|
* [Windsurf][windsurf] (Codium)
|
||||||
|
* [Visual Studio Code][vscode] (Copilot)
|
||||||
|
* [Cline][cline] (VS Code extension)
|
||||||
|
* [Claude desktop][claudedesktop]
|
||||||
|
* [Claude code][claudecode]
|
||||||
|
* [Gemini CLI][geminicli]
|
||||||
|
* [Gemini Code Assist][geminicodeassist]
|
||||||
|
|
||||||
|
[toolbox]: https://github.com/googleapis/genai-toolbox
|
||||||
|
[cursor]: #configure-your-mcp-client
|
||||||
|
[windsurf]: #configure-your-mcp-client
|
||||||
|
[vscode]: #configure-your-mcp-client
|
||||||
|
[cline]: #configure-your-mcp-client
|
||||||
|
[claudedesktop]: #configure-your-mcp-client
|
||||||
|
[claudecode]: #configure-your-mcp-client
|
||||||
|
[geminicli]: #configure-your-mcp-client
|
||||||
|
[geminicodeassist]: #configure-your-mcp-client
|
||||||
|
|
||||||
|
## Set up the database
|
||||||
|
|
||||||
|
1. Create or select an Oracle instance.
|
||||||
|
|
||||||
|
1. Create or reuse a database user and have the username and password ready.
|
||||||
|
|
||||||
|
## Install MCP Toolbox
|
||||||
|
|
||||||
|
1. Download the latest version of Toolbox as a binary. Select the [correct
|
||||||
|
binary](https://github.com/googleapis/genai-toolbox/releases) corresponding
|
||||||
|
to your OS and CPU architecture. You are required to use Toolbox version
|
||||||
|
V0.26.0+:
|
||||||
|
|
||||||
|
<!-- {x-release-please-start-version} -->
|
||||||
|
{{< tabpane persist=header >}}
|
||||||
|
{{< tab header="linux/amd64" lang="bash" >}}
|
||||||
|
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/linux/amd64/toolbox
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab header="darwin/arm64" lang="bash" >}}
|
||||||
|
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/darwin/arm64/toolbox
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab header="darwin/amd64" lang="bash" >}}
|
||||||
|
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/darwin/amd64/toolbox
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab header="windows/amd64" lang="bash" >}}
|
||||||
|
curl -O https://storage.googleapis.com/genai-toolbox/v0.26.0/windows/amd64/toolbox.exe
|
||||||
|
{{< /tab >}}
|
||||||
|
{{< /tabpane >}}
|
||||||
|
<!-- {x-release-please-end} -->
|
||||||
|
|
||||||
|
1. Make the binary executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x toolbox
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Verify the installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./toolbox --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configure your MCP Client
|
||||||
|
|
||||||
|
{{< tabpane text=true >}}
|
||||||
|
{{% tab header="Claude code" lang="en" %}}
|
||||||
|
|
||||||
|
1. Install [Claude
|
||||||
|
Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview).
|
||||||
|
1. Create a `.mcp.json` file in your project root if it doesn't exist.
|
||||||
|
1. Add the following configuration, replace the environment variables with your
|
||||||
|
values, and save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": "",
|
||||||
|
"ORACLE_WALLET_LOCATION": "",
|
||||||
|
"ORACLE_USE_OCI": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Restart Claude code to apply the new configuration.
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Claude desktop" lang="en" %}}
|
||||||
|
|
||||||
|
1. Open Claude desktop and navigate to Settings.
|
||||||
|
1. Under the Developer tab, tap Edit Config to open the configuration file.
|
||||||
|
1. Add the following configuration, replace the environment variables with your
|
||||||
|
values, and save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": "",
|
||||||
|
"ORACLE_WALLET_LOCATION": "",
|
||||||
|
"ORACLE_USE_OCI": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Restart Claude desktop.
|
||||||
|
1. From the new chat screen, you should see a hammer (MCP) icon appear with the
|
||||||
|
new MCP server available.
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Cline" lang="en" %}}
|
||||||
|
|
||||||
|
1. Open the Cline extension in VS Code and tap
|
||||||
|
the **MCP Servers** icon.
|
||||||
|
1. Tap Configure MCP Servers to open the configuration file.
|
||||||
|
1. Add the following configuration, replace the environment variables with your
|
||||||
|
values, and save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": "",
|
||||||
|
"ORACLE_WALLET_LOCATION": "",
|
||||||
|
"ORACLE_USE_OCI": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. You should see a green active status after the server is successfully
|
||||||
|
connected.
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Cursor" lang="en" %}}
|
||||||
|
|
||||||
|
1. Create a `.cursor` directory in your project root if it doesn't exist.
|
||||||
|
1. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
|
||||||
|
1. Add the following configuration, replace the environment variables with your
|
||||||
|
values, and save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": "",
|
||||||
|
"ORACLE_WALLET_LOCATION": "",
|
||||||
|
"ORACLE_USE_OCI": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Cursor and navigate to **Settings > Cursor
|
||||||
|
Settings > MCP**. You should see a green active status after the server is
|
||||||
|
successfully connected.
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Visual Studio Code (Copilot)" lang="en" %}}
|
||||||
|
|
||||||
|
1. Open VS Code and
|
||||||
|
create a `.vscode` directory in your project root if it doesn't exist.
|
||||||
|
1. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
|
||||||
|
1. Add the following configuration, replace the environment variables with your
|
||||||
|
values, and save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"servers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracle","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Windsurf" lang="en" %}}
|
||||||
|
|
||||||
|
1. Open Windsurf and navigate to the
|
||||||
|
Cascade assistant.
|
||||||
|
1. Tap on the hammer (MCP) icon, then Configure to open the configuration file.
|
||||||
|
1. Add the following configuration, replace the environment variables with your
|
||||||
|
values, and save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": "",
|
||||||
|
"ORACLE_WALLET": "",
|
||||||
|
"ORACLE_WALLET_PASSWORD": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Gemini CLI" lang="en" %}}
|
||||||
|
|
||||||
|
1. Install the Gemini CLI.
|
||||||
|
1. In your working directory, create a folder named `.gemini`. Within it, create a `settings.json` file.
|
||||||
|
1. Add the following configuration, replace the environment variables with your values, and then save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /tab %}}
|
||||||
|
|
||||||
|
{{% tab header="Gemini Code Assist" lang="en" %}}
|
||||||
|
|
||||||
|
1. Install the Gemini Code Assist extension in Visual Studio Code.
|
||||||
|
1. Enable Agent Mode in Gemini Code Assist chat.
|
||||||
|
1. In your working directory, create a folder named `.gemini`. Within it, create a `settings.json` file.
|
||||||
|
1. Add the following configuration, replace the environment variables with your values, and then save:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"oracle": {
|
||||||
|
"command": "./PATH/TO/toolbox",
|
||||||
|
"args": ["--prebuilt","oracledb","--stdio"],
|
||||||
|
"env": {
|
||||||
|
"ORACLE_HOST": "",
|
||||||
|
"ORACLE_PORT": "1521",
|
||||||
|
"ORACLE_SERVICE": "",
|
||||||
|
"ORACLE_USER": "",
|
||||||
|
"ORACLE_PASSWORD": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /tab %}}
|
||||||
|
{{< /tabpane >}}
|
||||||
|
|
||||||
|
## Use Tools
|
||||||
|
|
||||||
|
Your AI tool is now connected to Oracle using MCP. Try asking your AI
|
||||||
|
assistant to list tables, create a table, or define and execute other SQL
|
||||||
|
statements.
|
||||||
|
|
||||||
|
The following tools are available to the LLM:
|
||||||
|
|
||||||
|
1. **list_tables**: lists tables and descriptions
|
||||||
|
1. **execute_sql**: execute any SQL statement
|
||||||
|
|
||||||
|
{{< notice note >}}
|
||||||
|
Prebuilt tools are pre-1.0, so expect some tool changes between versions. LLMs
|
||||||
|
will adapt to the tools available, so this shouldn't affect most users.
|
||||||
|
{{< /notice >}}
|
||||||
@@ -692,6 +692,34 @@ See [Usage Examples](../reference/cli.md#examples).
|
|||||||
* `execute_cypher`: Executes a Cypher query.
|
* `execute_cypher`: Executes a Cypher query.
|
||||||
* `get_schema`: Retrieves the schema of the Neo4j database.
|
* `get_schema`: Retrieves the schema of the Neo4j database.
|
||||||
|
|
||||||
|
|
||||||
|
## Oracle
|
||||||
|
|
||||||
|
* `--prebuilt` value: `oracle`
|
||||||
|
* **Environment Variables:**
|
||||||
|
* `ORACLE_HOST`: The hostname or IP address of the Oracle server.
|
||||||
|
* `ORACLE_PORT`: The port number for the Oracle server (Default: 1521).
|
||||||
|
* `ORACLE_CONNECTION_STRING`: The
|
||||||
|
* `ORACLE_USER`: The username for the Oracle DB instance.
|
||||||
|
* `ORACLE_PASSWORD`: The password for the Oracle DB instance
|
||||||
|
* `ORACLE_WALLET`: The path to Oracle DB Wallet file for the databases that support this authentication type
|
||||||
|
* `ORACLE_USE_OCI`: true or false, The flag if the Oracle Database is deployed in cloud deployment. Default is false.
|
||||||
|
* **Permissions:**
|
||||||
|
* Database-level permissions (e.g., `SELECT`, `INSERT`) are required to execute queries.
|
||||||
|
* **Tools:**
|
||||||
|
* `execute_sql`: Executes a SQL query.
|
||||||
|
* `list_tables`: Lists tables in the database.
|
||||||
|
* `list_active_sessions`: Lists active database sessions.
|
||||||
|
* `get_query_plan`: Gets the execution plan for a SQL statement.
|
||||||
|
* `list_top_sql_by_resource`: Lists top SQL statements by resource usage.
|
||||||
|
* `list_tablespace_usage`: Lists tablespace usage.
|
||||||
|
* `list_invalid_objects`: Lists invalid objects.
|
||||||
|
* `list_active_sessions`: Lists active database sessions.
|
||||||
|
* `get_query_plan`: Gets the execution plan for a SQL statement.
|
||||||
|
* `list_top_sql_by_resource`: Lists top SQL statements by resource usage.
|
||||||
|
* `list_tablespace_usage`: Lists tablespace usage.
|
||||||
|
* `list_invalid_objects`: Lists invalid objects.
|
||||||
|
|
||||||
## Google Cloud Healthcare API
|
## Google Cloud Healthcare API
|
||||||
* `--prebuilt` value: `cloud-healthcare`
|
* `--prebuilt` value: `cloud-healthcare`
|
||||||
* **Environment Variables:**
|
* **Environment Variables:**
|
||||||
|
|||||||
@@ -87,28 +87,41 @@ using a TNS (Transparent Network Substrate) alias.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
This example demonstrates the four connection methods you could choose from:
|
### 1. Basic Connection (Host, Port, and Service Name)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
kind: sources
|
sources:
|
||||||
name: my-oracle-source
|
my-oracle-source:
|
||||||
type: oracle
|
kind: oracle
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 1521
|
||||||
|
serviceName: XEPDB1
|
||||||
|
user: ${USER_NAME}
|
||||||
|
password: ${PASSWORD}
|
||||||
|
```
|
||||||
|
|
||||||
# --- Choose one connection method ---
|
### 2. Direct Connection String
|
||||||
# 1. Host, Port, and Service Name
|
|
||||||
host: 127.0.0.1
|
|
||||||
port: 1521
|
|
||||||
serviceName: XEPDB1
|
|
||||||
|
|
||||||
# 2. Direct Connection String
|
```yaml
|
||||||
connectionString: "127.0.0.1:1521/XEPDB1"
|
sources:
|
||||||
|
my-oracle-source:
|
||||||
|
kind: oracle
|
||||||
|
connectionString: "127.0.0.1:1521/XEPDB1"
|
||||||
|
user: ${USER_NAME}
|
||||||
|
password: ${PASSWORD}
|
||||||
|
```
|
||||||
|
|
||||||
# 3. TNS Alias (requires tnsnames.ora)
|
### 3. TNS Alias (requires tnsnames.ora)
|
||||||
tnsAlias: "MY_DB_ALIAS"
|
|
||||||
tnsAdmin: "/opt/oracle/network/admin" # Optional: overrides TNS_ADMIN env var
|
|
||||||
|
|
||||||
user: ${USER_NAME}
|
```yaml
|
||||||
password: ${PASSWORD}
|
sources:
|
||||||
|
my-oracle-source:
|
||||||
|
kind: oracle
|
||||||
|
tnsAlias: "MY_DB_ALIAS"
|
||||||
|
tnsAdmin: "/opt/oracle/network/admin" # Optional: overrides TNS_ADMIN env var
|
||||||
|
user: ${USER_NAME}
|
||||||
|
password: ${PASSWORD}
|
||||||
|
useOCI: true # tnsAlias requires useOCI to be true
|
||||||
|
|
||||||
# Optional: Set to true to use the OCI-based driver for advanced features (Requires Oracle Instant Client)
|
# Optional: Set to true to use the OCI-based driver for advanced features (Requires Oracle Instant Client)
|
||||||
```
|
```
|
||||||
@@ -168,3 +181,4 @@ instead of hardcoding your secrets into the configuration file.
|
|||||||
| tnsAlias | string | false | A TNS alias from a `tnsnames.ora` file. Use as an alternative to `host`/`port` or `connectionString`. |
|
| tnsAlias | string | false | A TNS alias from a `tnsnames.ora` file. Use as an alternative to `host`/`port` or `connectionString`. |
|
||||||
| tnsAdmin | string | false | Path to the directory containing the `tnsnames.ora` file. This overrides the `TNS_ADMIN` environment variable if it is set. |
|
| tnsAdmin | string | false | Path to the directory containing the `tnsnames.ora` file. This overrides the `TNS_ADMIN` environment variable if it is set. |
|
||||||
| useOCI | bool | false | If true, uses the OCI-based driver (godror) which supports Oracle Wallet/Kerberos but requires the Oracle Instant Client libraries to be installed. Defaults to false (pure Go driver). |
|
| useOCI | bool | false | If true, uses the OCI-based driver (godror) which supports Oracle Wallet/Kerberos but requires the Oracle Instant Client libraries to be installed. Defaults to false (pure Go driver). |
|
||||||
|
| walletLocation | string | false | Path to the directory containing the wallet files for the pure Go driver (`useOCI: false`). |
|
||||||
|
|||||||
7
docs/en/samples/oracle/_index.md
Normal file
7
docs/en/samples/oracle/_index.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: "OracleDB"
|
||||||
|
type: docs
|
||||||
|
weight: 1
|
||||||
|
description: >
|
||||||
|
How to get started with Toolbox using Oracle Database.
|
||||||
|
---
|
||||||
@@ -47,6 +47,7 @@ var expectedToolSources = []string{
|
|||||||
"mysql",
|
"mysql",
|
||||||
"neo4j",
|
"neo4j",
|
||||||
"oceanbase",
|
"oceanbase",
|
||||||
|
"oracledb",
|
||||||
"postgres",
|
"postgres",
|
||||||
"serverless-spark",
|
"serverless-spark",
|
||||||
"singlestore",
|
"singlestore",
|
||||||
@@ -131,6 +132,8 @@ func TestGetPrebuiltTool(t *testing.T) {
|
|||||||
neo4jconfig := getOrFatal(t, "neo4j")
|
neo4jconfig := getOrFatal(t, "neo4j")
|
||||||
healthcare_config := getOrFatal(t, "cloud-healthcare")
|
healthcare_config := getOrFatal(t, "cloud-healthcare")
|
||||||
snowflake_config := getOrFatal(t, "snowflake")
|
snowflake_config := getOrFatal(t, "snowflake")
|
||||||
|
oracle_config := getOrFatal(t,"oracledb")
|
||||||
|
|
||||||
if len(alloydb_omni_config) <= 0 {
|
if len(alloydb_omni_config) <= 0 {
|
||||||
t.Fatalf("unexpected error: could not fetch alloydb omni prebuilt tools yaml")
|
t.Fatalf("unexpected error: could not fetch alloydb omni prebuilt tools yaml")
|
||||||
}
|
}
|
||||||
@@ -230,6 +233,10 @@ func TestGetPrebuiltTool(t *testing.T) {
|
|||||||
if len(snowflake_config) <= 0 {
|
if len(snowflake_config) <= 0 {
|
||||||
t.Fatalf("unexpected error: could not fetch snowflake prebuilt tools yaml")
|
t.Fatalf("unexpected error: could not fetch snowflake prebuilt tools yaml")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(oracle_config) <= 0 {
|
||||||
|
t.Fatalf("unexpected error: could not fetch oracle prebuilt tools yaml")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFailGetPrebuiltTool(t *testing.T) {
|
func TestFailGetPrebuiltTool(t *testing.T) {
|
||||||
|
|||||||
121
internal/prebuiltconfigs/tools/oracledb.yaml
Normal file
121
internal/prebuiltconfigs/tools/oracledb.yaml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# Copyright 2026 Google LLC
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
sources:
|
||||||
|
oracle-source:
|
||||||
|
kind: "oracle"
|
||||||
|
connectionString: ${ORACLE_CONNECTION_STRING}
|
||||||
|
walletLocation: ${ORACLE_WALLET:}
|
||||||
|
user: ${ORACLE_USER}
|
||||||
|
password: ${ORACLE_PASSWORD}
|
||||||
|
useOCI: ${ORACLE_USE_OCI:false}
|
||||||
|
|
||||||
|
tools:
|
||||||
|
|
||||||
|
list_tables:
|
||||||
|
kind: oracle-sql
|
||||||
|
source: oracle-source
|
||||||
|
description: "Lists all user tables in the connected schema, including segment size, row count, and last analyzed date. Filters by a comma-separated list of names. If names are omitted, lists all tables in the current user's schema."
|
||||||
|
statement: SELECT table_name from user_tables;
|
||||||
|
|
||||||
|
list_active_sessions:
|
||||||
|
kind: oracle-sql
|
||||||
|
source: oracle-source
|
||||||
|
description: "List the top N (default 50) currently running database sessions (STATUS='ACTIVE'), showing SID, OS User, Program, and the current SQL statement text."
|
||||||
|
statement: SELECT
|
||||||
|
s.sid,
|
||||||
|
s.serial#,
|
||||||
|
s.username,
|
||||||
|
s.osuser,
|
||||||
|
s.program,
|
||||||
|
s.status,
|
||||||
|
s.wait_class,
|
||||||
|
s.event,
|
||||||
|
sql.sql_text
|
||||||
|
FROM
|
||||||
|
v$session s,
|
||||||
|
v$sql sql
|
||||||
|
WHERE
|
||||||
|
s.status = 'ACTIVE'
|
||||||
|
AND s.sql_id = sql.sql_id (+)
|
||||||
|
AND s.audsid != userenv('sessionid') -- Exclude current session
|
||||||
|
ORDER BY s.last_call_et DESC
|
||||||
|
FETCH FIRST COALESCE(10) ROWS ONLY;
|
||||||
|
|
||||||
|
get_query_plan:
|
||||||
|
kind: oracle-sql
|
||||||
|
source: oracle-source
|
||||||
|
description: "Generate a full execution plan for a single SQL statement. This can be used to analyze query performance without execution. Requires the SQL statement as input. following is an example EXPLAIN PLAN FOR {{&query}};"
|
||||||
|
statement: SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());
|
||||||
|
|
||||||
|
list_top_sql_by_resource:
|
||||||
|
kind: oracle-sql
|
||||||
|
source: oracle-source
|
||||||
|
description: "List the top N SQL statements from the library cache based on a chosen resource metric (CPU, I/O, or Elapsed Time), following is an example of the sql"
|
||||||
|
statement: SELECT
|
||||||
|
sql_id,
|
||||||
|
executions,
|
||||||
|
buffer_gets,
|
||||||
|
disk_reads,
|
||||||
|
cpu_time / 1000000 AS cpu_seconds,
|
||||||
|
elapsed_time / 1000000 AS elapsed_seconds
|
||||||
|
FROM
|
||||||
|
v$sql
|
||||||
|
FETCH FIRST 5 ROWS ONLY;
|
||||||
|
|
||||||
|
list_tablespace_usage:
|
||||||
|
kind: oracle-sql
|
||||||
|
source: oracle-source
|
||||||
|
description: "List tablespace names, total size, free space, and used percentage to monitor storage utilization."
|
||||||
|
statement: SELECT
|
||||||
|
t.tablespace_name,
|
||||||
|
TO_CHAR(t.total_bytes / 1024 / 1024, '99,999.00') AS total_mb,
|
||||||
|
TO_CHAR(SUM(d.bytes) / 1024 / 1024, '99,999.00') AS free_mb,
|
||||||
|
TO_CHAR((t.total_bytes - SUM(d.bytes)) / t.total_bytes * 100, '99.00') AS used_pct
|
||||||
|
FROM
|
||||||
|
(SELECT tablespace_name, SUM(bytes) AS total_bytes FROM dba_data_files GROUP BY tablespace_name) t,
|
||||||
|
dba_free_space d
|
||||||
|
WHERE
|
||||||
|
t.tablespace_name = d.tablespace_name (+)
|
||||||
|
GROUP BY
|
||||||
|
t.tablespace_name, t.total_bytes
|
||||||
|
ORDER BY
|
||||||
|
used_pct DESC;
|
||||||
|
|
||||||
|
list_invalid_objects:
|
||||||
|
kind: oracle-sql
|
||||||
|
source: oracle-source
|
||||||
|
description: "Lists all database objects that are in an invalid state, requiring recompilation (e.g., procedures, functions, views)."
|
||||||
|
statement: SELECT
|
||||||
|
owner,
|
||||||
|
object_type,
|
||||||
|
object_name,
|
||||||
|
status
|
||||||
|
FROM
|
||||||
|
dba_objects
|
||||||
|
WHERE
|
||||||
|
status = 'INVALID'
|
||||||
|
AND owner NOT IN ('SYS', 'SYSTEM') -- Exclude system schemas for clarity
|
||||||
|
ORDER BY
|
||||||
|
owner, object_type, object_name;
|
||||||
|
|
||||||
|
toolsets:
|
||||||
|
oracle_database_tools:
|
||||||
|
- execute_sql
|
||||||
|
- list_tables
|
||||||
|
- list_active_sessions
|
||||||
|
- get_query_plan
|
||||||
|
- list_top_sql_by_resource
|
||||||
|
- list_tablespace_usage
|
||||||
|
- list_invalid_objects
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// Copyright © 2025, Oracle and/or its affiliates.
|
// Copyright © 2025, Oracle and/or its affiliates.
|
||||||
|
|
||||||
package oracle
|
package oracle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -9,23 +9,31 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/googleapis/genai-toolbox/internal/sources/oracle"
|
||||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||||
"github.com/googleapis/genai-toolbox/tests"
|
"github.com/googleapis/genai-toolbox/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
OracleSourceType = "oracle"
|
OracleSourceType = "oracle"
|
||||||
OracleToolType = "oracle-sql"
|
OracleToolType = "oracle-sql"
|
||||||
OracleHost = os.Getenv("ORACLE_HOST")
|
OracleHost = os.Getenv("ORACLE_HOST")
|
||||||
|
OraclePort = os.Getenv("ORACLE_PORT")
|
||||||
OracleUser = os.Getenv("ORACLE_USER")
|
OracleUser = os.Getenv("ORACLE_USER")
|
||||||
OraclePass = os.Getenv("ORACLE_PASS")
|
OraclePass = os.Getenv("ORACLE_PASS")
|
||||||
OracleServerName = os.Getenv("ORACLE_SERVER_NAME")
|
OracleServerName = os.Getenv("ORACLE_SERVER_NAME")
|
||||||
|
OracleUseOCI = os.Getenv("ORACLE_USE_OCI")
|
||||||
|
OracleWalletLocation = os.Getenv("ORACLE_WALLET_LOCATION")
|
||||||
|
OracleTnsAdmin = os.Getenv("ORACLE_TNS_ADMIN")
|
||||||
|
|
||||||
OracleConnStr = fmt.Sprintf(
|
OracleConnStr = fmt.Sprintf(
|
||||||
"%s:%s/%s", OracleHost, "1521", OracleServerName)
|
"%s:%s/%s", OracleHost, "1521", OracleServerName) // Default port 1521??
|
||||||
)
|
)
|
||||||
|
|
||||||
func getOracleVars(t *testing.T) map[string]any {
|
func getOracleVars(t *testing.T) map[string]any {
|
||||||
@@ -43,12 +51,77 @@ func getOracleVars(t *testing.T) map[string]any {
|
|||||||
return map[string]any{
|
return map[string]any{
|
||||||
"type": OracleSourceType,
|
"type": OracleSourceType,
|
||||||
"connectionString": OracleConnStr,
|
"connectionString": OracleConnStr,
|
||||||
"useOCI": true,
|
"useOCI": OracleUseOCI,
|
||||||
|
"walletLocation": OracleWalletLocation,
|
||||||
|
"tnsAdmin": OracleTnsAdmin,
|
||||||
|
"host": OracleHost,
|
||||||
|
"port": OraclePort,
|
||||||
|
"service": OracleServerName,
|
||||||
"user": OracleUser,
|
"user": OracleUser,
|
||||||
"password": OraclePass,
|
"password": OraclePass,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOracleConfigFromEnv constructs an oracle.Config from environment variables.
|
||||||
|
func getOracleConfigFromEnv(t *testing.T) oracle.Config {
|
||||||
|
t.Helper()
|
||||||
|
vars := getOracleVars(t)
|
||||||
|
|
||||||
|
port, err := strconv.Atoi(vars["port"].(string))
|
||||||
|
if err != nil && vars["port"].(string) != "" {
|
||||||
|
t.Fatalf("invalid ORACLE_PORT: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
useOCI, err := strconv.ParseBool(vars["useOCI"].(string))
|
||||||
|
if err != nil && vars["useOCI"].(string) != "" {
|
||||||
|
useOCI = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return oracle.Config{
|
||||||
|
Name: "test-oracle-instance",
|
||||||
|
Kind: vars["type"].(string),
|
||||||
|
User: vars["user"].(string),
|
||||||
|
Password: vars["password"].(string),
|
||||||
|
Host: vars["host"].(string),
|
||||||
|
Port: port,
|
||||||
|
ServiceName: vars["service"].(string),
|
||||||
|
WalletLocation: vars["walletLocation"].(string),
|
||||||
|
TnsAdmin: vars["tnsAdmin"].(string),
|
||||||
|
UseOCI: useOCI,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setOracleEnv sets Oracle-related environment variables for testing and returns a cleanup function.
|
||||||
|
func setOracleEnv(t *testing.T, host, user, password, service, port, connStr, tnsAlias, tnsAdmin, walletLocation string, useOCI bool) func() {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
original := map[string]string{
|
||||||
|
"ORACLE_HOST": os.Getenv("ORACLE_HOST"),
|
||||||
|
"ORACLE_USER": os.Getenv("ORACLE_USER"),
|
||||||
|
"ORACLE_PASSWORD": os.Getenv("ORACLE_PASSWORD"),
|
||||||
|
"ORACLE_SERVICE": os.Getenv("ORACLE_SERVICE"),
|
||||||
|
"ORACLE_PORT": os.Getenv("ORACLE_PORT"),
|
||||||
|
"ORACLE_TNS_ADMIN": os.Getenv("ORACLE_TNS_ADMIN"),
|
||||||
|
"ORACLE_WALLET_LOCATION": os.Getenv("ORACLE_WALLET_LOCATION"),
|
||||||
|
"ORACLE_USE_OCI": os.Getenv("ORACLE_USE_OCI"),
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("ORACLE_HOST", host)
|
||||||
|
os.Setenv("ORACLE_USER", user)
|
||||||
|
os.Setenv("ORACLE_PASSWORD", password)
|
||||||
|
os.Setenv("ORACLE_SERVICE", service)
|
||||||
|
os.Setenv("ORACLE_PORT", port)
|
||||||
|
os.Setenv("ORACLE_TNS_ADMIN", tnsAdmin)
|
||||||
|
os.Setenv("ORACLE_WALLET_LOCATION", walletLocation)
|
||||||
|
os.Setenv("ORACLE_USE_OCI", fmt.Sprintf("%v", useOCI))
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
for k, v := range original {
|
||||||
|
os.Setenv(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copied over from oracle.go
|
// Copied over from oracle.go
|
||||||
func initOracleConnection(ctx context.Context, user, pass, connStr string) (*sql.DB, error) {
|
func initOracleConnection(ctx context.Context, user, pass, connStr string) (*sql.DB, error) {
|
||||||
// Build the full Oracle connection string for godror driver
|
// Build the full Oracle connection string for godror driver
|
||||||
@@ -68,7 +141,9 @@ func initOracleConnection(ctx context.Context, user, pass, connStr string) (*sql
|
|||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestOracleSimpleToolEndpoints tests Oracle SQL tool endpoints
|
||||||
func TestOracleSimpleToolEndpoints(t *testing.T) {
|
func TestOracleSimpleToolEndpoints(t *testing.T) {
|
||||||
|
|
||||||
sourceConfig := getOracleVars(t)
|
sourceConfig := getOracleVars(t)
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -135,6 +210,130 @@ func TestOracleSimpleToolEndpoints(t *testing.T) {
|
|||||||
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam)
|
tests.RunToolInvokeWithTemplateParameters(t, tableNameTemplateParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TestOracleConnectionPureGoWithWallet tests pure Go driver connection with wallet
|
||||||
|
func TestOracleConnectionPureGoWithWallet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// This test expects the connection to fail because the wallet file won't exist.
|
||||||
|
// It verifies that the walletLocation parameter is correctly passed to the pure Go driver.
|
||||||
|
|
||||||
|
// Save original env vars and restore them at the end
|
||||||
|
cleanup := setOracleEnv(t,
|
||||||
|
OracleHost, OracleUser, OraclePass, OracleServerName, OraclePort, // Use existing base connection details
|
||||||
|
"", // connectionString
|
||||||
|
"", // tnsAlias
|
||||||
|
"", // tnsAdmin
|
||||||
|
"/tmp/nonexistent_wallet", // walletLocation
|
||||||
|
false, // useOCI
|
||||||
|
)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfg := getOracleConfigFromEnv(t)
|
||||||
|
_, err := cfg.Initialize(ctx, nil) // Pass nil for tracer as it's not critical for this test
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected connection to fail with non-existent wallet, but it succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for error message indicating wallet usage or connection failure related to wallet
|
||||||
|
// The exact error message might vary depending on the go-ora version and OS.
|
||||||
|
// We are looking for an error that suggests the wallet path was attempted.
|
||||||
|
expectedErrorSubstring := "wallet"
|
||||||
|
if !strings.Contains(strings.ToLower(err.Error()), expectedErrorSubstring) {
|
||||||
|
t.Errorf("Expected error message to contain '%s' (case-insensitive) but got: %v", expectedErrorSubstring, err)
|
||||||
|
}
|
||||||
|
t.Logf("Connection failed as expected (Pure Go with Wallet): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOracleConnectionOCI tests OCI driver connection without wallet
|
||||||
|
func TestOracleConnectionOCI(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// This test verifies that the useOCI=true parameter is correctly passed to the OCI driver.
|
||||||
|
// It will likely fail if Oracle Instant Client is not installed or configured.
|
||||||
|
|
||||||
|
// Save original env vars and restore them at the end
|
||||||
|
cleanup := setOracleEnv(t,
|
||||||
|
OracleHost, OracleUser, OraclePass, OracleServerName, OraclePort, // Use existing base connection details
|
||||||
|
"", // connectionString
|
||||||
|
"", // tnsAlias
|
||||||
|
"", // tnsAdmin
|
||||||
|
"", // walletLocation
|
||||||
|
true, // useOCI
|
||||||
|
)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfg := getOracleConfigFromEnv(t)
|
||||||
|
_, err := cfg.Initialize(ctx, nil)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected connection to fail (OCI driver without Instant Client), but it succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for error message indicating OCI driver usage or connection failure related to OCI.
|
||||||
|
// Common errors include "OCI environment not initialized", "driver: bad connection", etc.
|
||||||
|
expectedErrorSubstrings := []string{"oci", "driver", "connection"}
|
||||||
|
foundExpectedError := false
|
||||||
|
for _, sub := range expectedErrorSubstrings {
|
||||||
|
if strings.Contains(strings.ToLower(err.Error()), sub) {
|
||||||
|
foundExpectedError = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundExpectedError {
|
||||||
|
t.Errorf("Expected error message to contain one of %v (case-insensitive) but got: %v", expectedErrorSubstrings, err)
|
||||||
|
}
|
||||||
|
t.Logf("Connection failed as expected (OCI Driver): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOracleConnectionOCIWithWallet tests OCI driver connection with TNS Admin and Wallet
|
||||||
|
func TestOracleConnectionOCIWithWallet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
// This test verifies that useOCI=true and tnsAdmin parameters are correctly passed for OCI wallet.
|
||||||
|
// It will likely fail due to missing tnsnames.ora and wallet files.
|
||||||
|
|
||||||
|
// Save original env vars and restore them at the end
|
||||||
|
cleanup := setOracleEnv(t,
|
||||||
|
"", OracleUser, OraclePass, "", "", // Unset host/port/service for TNS alias, but keep user/pass
|
||||||
|
"", // connectionString
|
||||||
|
"MY_TNS_ALIAS", // tnsAlias
|
||||||
|
"/tmp/nonexistent_tns_admin", // tnsAdmin
|
||||||
|
"", // walletLocation
|
||||||
|
true, // useOCI
|
||||||
|
)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfg := getOracleConfigFromEnv(t)
|
||||||
|
_, err := cfg.Initialize(ctx, nil)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected connection to fail (OCI driver with TNS Admin/Wallet), but it succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for error message indicating TNS Admin/Wallet usage or connection failure.
|
||||||
|
expectedErrorSubstrings := []string{"tns", "wallet", "oci", "driver", "connection"}
|
||||||
|
foundExpectedError := false
|
||||||
|
for _, sub := range expectedErrorSubstrings {
|
||||||
|
if strings.Contains(strings.ToLower(err.Error()), sub) {
|
||||||
|
foundExpectedError = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundExpectedError {
|
||||||
|
t.Errorf("Expected error message to contain one of %v (case-insensitive) but got: %v", expectedErrorSubstrings, err)
|
||||||
|
}
|
||||||
|
t.Logf("Connection failed as expected (OCI Driver with TNS Admin/Wallet): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//test utils
|
||||||
func setupOracleTable(t *testing.T, ctx context.Context, pool *sql.DB, createStatement, insertStatement, tableName string, params []any) func(*testing.T) {
|
func setupOracleTable(t *testing.T, ctx context.Context, pool *sql.DB, createStatement, insertStatement, tableName string, params []any) func(*testing.T) {
|
||||||
err := pool.PingContext(ctx)
|
err := pool.PingContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
248
tests/oracle/oracle_test.go
Normal file
248
tests/oracle/oracle_test.go
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
// Copyright 2025 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package oracle_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
yaml "github.com/goccy/go-yaml"
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/googleapis/genai-toolbox/internal/server"
|
||||||
|
"github.com/googleapis/genai-toolbox/internal/sources/oracle"
|
||||||
|
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseFromYamlOracle(t *testing.T) {
|
||||||
|
tcs := []struct {
|
||||||
|
desc string
|
||||||
|
in string
|
||||||
|
want server.SourceConfigs
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "connection string and useOCI=true",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-cs:
|
||||||
|
kind: oracle
|
||||||
|
connectionString: "my-host:1521/XEPDB1"
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
useOCI: true
|
||||||
|
`,
|
||||||
|
want: server.SourceConfigs{
|
||||||
|
"my-oracle-cs": oracle.Config{
|
||||||
|
Name: "my-oracle-cs",
|
||||||
|
Kind: oracle.SourceKind,
|
||||||
|
ConnectionString: "my-host:1521/XEPDB1",
|
||||||
|
User: "my_user",
|
||||||
|
Password: "my_pass",
|
||||||
|
UseOCI: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "host/port/serviceName and default useOCI=false",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-host:
|
||||||
|
kind: oracle
|
||||||
|
host: my-host
|
||||||
|
port: 1521
|
||||||
|
serviceName: ORCLPDB
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
`,
|
||||||
|
want: server.SourceConfigs{
|
||||||
|
"my-oracle-host": oracle.Config{
|
||||||
|
Name: "my-oracle-host",
|
||||||
|
Kind: oracle.SourceKind,
|
||||||
|
Host: "my-host",
|
||||||
|
Port: 1521,
|
||||||
|
ServiceName: "ORCLPDB",
|
||||||
|
User: "my_user",
|
||||||
|
Password: "my_pass",
|
||||||
|
UseOCI: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "tnsAlias and TnsAdmin specified with explicit useOCI=true",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-tns-oci:
|
||||||
|
kind: oracle
|
||||||
|
tnsAlias: FINANCE_DB
|
||||||
|
tnsAdmin: /opt/oracle/network/admin
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
useOCI: true
|
||||||
|
`,
|
||||||
|
want: server.SourceConfigs{
|
||||||
|
"my-oracle-tns-oci": oracle.Config{
|
||||||
|
Name: "my-oracle-tns-oci",
|
||||||
|
Kind: oracle.SourceKind,
|
||||||
|
TnsAlias: "FINANCE_DB",
|
||||||
|
TnsAdmin: "/opt/oracle/network/admin",
|
||||||
|
User: "my_user",
|
||||||
|
Password: "my_pass",
|
||||||
|
UseOCI: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "connection string and walletLocation",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-wallet:
|
||||||
|
kind: oracle
|
||||||
|
connectionString: "my-host:1521/XEPDB1"
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
walletLocation: "/path/to/wallet"
|
||||||
|
`,
|
||||||
|
want: server.SourceConfigs{
|
||||||
|
"my-oracle-wallet": oracle.Config{
|
||||||
|
Name: "my-oracle-wallet",
|
||||||
|
Kind: oracle.SourceKind,
|
||||||
|
ConnectionString: "my-host:1521/XEPDB1",
|
||||||
|
User: "my_user",
|
||||||
|
Password: "my_pass",
|
||||||
|
WalletLocation: "/path/to/wallet",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tcs {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
got := struct {
|
||||||
|
Sources server.SourceConfigs `yaml:"sources"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to unmarshal: %s", err)
|
||||||
|
}
|
||||||
|
if !cmp.Equal(tc.want, got.Sources) {
|
||||||
|
t.Fatalf("incorrect parse:\nwant: %v\ngot: %v\ndiff: %s", tc.want, got.Sources, cmp.Diff(tc.want, got.Sources))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFailParseFromYamlOracle(t *testing.T) {
|
||||||
|
tcs := []struct {
|
||||||
|
desc string
|
||||||
|
in string
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "extra field",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-instance:
|
||||||
|
kind: oracle
|
||||||
|
host: my-host
|
||||||
|
serviceName: ORCL
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
extraField: value
|
||||||
|
`,
|
||||||
|
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": [1:1] unknown field \"extraField\"\n> 1 | extraField: value\n ^\n 2 | host: my-host\n 3 | kind: oracle\n 4 | password: my_pass\n 5 | ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing required password field",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-instance:
|
||||||
|
kind: oracle
|
||||||
|
host: my-host
|
||||||
|
serviceName: ORCL
|
||||||
|
user: my_user
|
||||||
|
`,
|
||||||
|
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing connection method fields (validate fails)",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-instance:
|
||||||
|
kind: oracle
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
`,
|
||||||
|
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": invalid Oracle configuration: must provide one of: 'tns_alias', 'connection_string', or both 'host' and 'service_name'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "multiple connection methods provided (validate fails)",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-instance:
|
||||||
|
kind: oracle
|
||||||
|
host: my-host
|
||||||
|
serviceName: ORCL
|
||||||
|
connectionString: "my-host:1521/XEPDB1"
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
`,
|
||||||
|
err: "unable to parse source \"my-oracle-instance\" as \"oracle\": invalid Oracle configuration: provide only one connection method: 'tns_alias', 'connection_string', or 'host'+'service_name'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "fail on tnsAdmin with useOCI=false",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-fail:
|
||||||
|
kind: oracle
|
||||||
|
tnsAlias: FINANCE_DB
|
||||||
|
tnsAdmin: /opt/oracle/network/admin
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
useOCI: false
|
||||||
|
`,
|
||||||
|
err: "unable to parse source \"my-oracle-fail\" as \"oracle\": invalid Oracle configuration: `tnsAdmin` can only be used when `UseOCI` is true, or use `walletLocation` instead",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "fail on walletLocation with useOCI=true",
|
||||||
|
in: `
|
||||||
|
sources:
|
||||||
|
my-oracle-fail:
|
||||||
|
kind: oracle
|
||||||
|
connectionString: "my-host:1521/XEPDB1"
|
||||||
|
user: my_user
|
||||||
|
password: my_pass
|
||||||
|
walletLocation: "/path/to/wallet"
|
||||||
|
useOCI: true
|
||||||
|
`,
|
||||||
|
err: "unable to parse source \"my-oracle-fail\" as \"oracle\": invalid Oracle configuration: when using an OCI driver, use `tnsAdmin` to specify credentials file location instead",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tcs {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
got := struct {
|
||||||
|
Sources server.SourceConfigs `yaml:"sources"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := yaml.Unmarshal(testutils.FormatYaml(tc.in), &got)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expect parsing to fail")
|
||||||
|
}
|
||||||
|
errStr := strings.ReplaceAll(err.Error(), "\r", "")
|
||||||
|
|
||||||
|
if errStr != tc.err {
|
||||||
|
t.Fatalf("unexpected error:\ngot:\n%q\nwant:\n%q\n", errStr, tc.err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user