mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-13 08:35:15 -05:00
feat: add Oracle MCP documentation and integration tests for wallet and OCI connections
This commit is contained in:
@@ -689,8 +689,7 @@ See [Usage Examples](../reference/cli.md#examples).
|
||||
* **Permissions:**
|
||||
* **Database-level permissions** are required to execute Cypher queries.
|
||||
* **Tools:**
|
||||
* `ORACLE_WALLET`: The path to Oracle DB Wallet file for the databases that support this authentication type
|
||||
* `USE_OCI`: true or false, The flag if the Oracle Database is deployed in cloud deployment
|
||||
|
||||
|
||||
## Oracle
|
||||
|
||||
@@ -698,6 +697,11 @@ See [Usage Examples](../reference/cli.md#examples).
|
||||
* **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
|
||||
* `ORACLE_PASSWORD`: The
|
||||
* `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
|
||||
|
||||
* **Permissions:**
|
||||
* Database-level permissions (e.g., `SELECT`, `INSERT`) are required to
|
||||
@@ -710,6 +714,11 @@ See [Usage Examples](../reference/cli.md#examples).
|
||||
* `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
|
||||
* `--prebuilt` value: `cloud-healthcare`
|
||||
|
||||
@@ -15,27 +15,26 @@
|
||||
sources:
|
||||
oracle-source:
|
||||
kind: "oracle"
|
||||
connectionString: $ORACLE_CONNECTION_STRING
|
||||
walletLocation: $ORACLE_WALLET
|
||||
user: $ORACLE_USER
|
||||
password: $ORACLE_PASSWORD
|
||||
useOCI: $USE_OCI:false
|
||||
tools:
|
||||
execute_sql:
|
||||
kind: oracle-execute-sql
|
||||
source: oracle-source
|
||||
description: Use this tool to execute a single SQL query or DML statement.
|
||||
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. SELECT table_name from user_tables;"
|
||||
|
||||
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.
|
||||
SELECT
|
||||
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,
|
||||
@@ -53,18 +52,19 @@ tools:
|
||||
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;"
|
||||
FETCH FIRST COALESCE(10) ROWS ONLY;
|
||||
|
||||
get_query_plan:
|
||||
kind: oracle-execute-sql
|
||||
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}};
|
||||
SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());"
|
||||
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
|
||||
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,
|
||||
@@ -74,12 +74,13 @@ tools:
|
||||
elapsed_time / 1000000 AS elapsed_seconds
|
||||
FROM
|
||||
v$sql
|
||||
FETCH FIRST 5 ROWS ONLY;"
|
||||
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.
|
||||
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,
|
||||
@@ -93,7 +94,7 @@ tools:
|
||||
GROUP BY
|
||||
t.tablespace_name, t.total_bytes
|
||||
ORDER BY
|
||||
used_pct DESC"
|
||||
used_pct DESC;
|
||||
|
||||
list_invalid_objects:
|
||||
kind: oracle-sql
|
||||
@@ -111,7 +112,7 @@ tools:
|
||||
status = 'INVALID'
|
||||
AND owner NOT IN ('SYS', 'SYSTEM') -- Exclude system schemas for clarity
|
||||
ORDER BY
|
||||
owner, object_type, object_name;
|
||||
owner, object_type, object_name;
|
||||
|
||||
toolsets:
|
||||
oracle_database_tools:
|
||||
@@ -121,4 +122,4 @@ toolsets:
|
||||
- get_query_plan
|
||||
- list_top_sql_by_resource
|
||||
- list_tablespace_usage
|
||||
- list_invalid_objects
|
||||
- list_invalid_objects
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -96,8 +97,8 @@ func TestOracleTools(t *testing.T) {
|
||||
defer teardownTable1(t)
|
||||
|
||||
// set up data for auth tool
|
||||
createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := getOracleAuthToolInfo(tableNameAuth)
|
||||
teardownTable2 := setupOracleTable(t, ctx, db, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams)
|
||||
createAuthTableStmt, insertAuthTableStmt, authToolStmt, authTestParams := getOracleAuthToolInfo(t, tableNameAuth)
|
||||
teardownTable2 := setupOracleTable(t, ctx, db, createAuthTableStmt, insertAuthTableStmt, tableNameAuth, authTestParams...)
|
||||
defer teardownTable2(t)
|
||||
|
||||
// Write config into a file and pass it to command
|
||||
@@ -150,6 +151,125 @@ func TestOracleTools(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
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, oraclePassword, oracleService, 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)
|
||||
}
|
||||
|
||||
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, oraclePassword, oracleService, 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)
|
||||
}
|
||||
|
||||
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, oraclePassword, "", "", // 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)
|
||||
}
|
||||
|
||||
func setupOracleTable(t *testing.T, ctx context.Context, pool *sql.DB, createStatement, insertStatement, tableName string, params []any) func(*testing.T) {
|
||||
err := pool.PingContext(ctx)
|
||||
if err != nil {
|
||||
@@ -205,7 +325,7 @@ func getOracleParamToolInfo(tableName string) (string, string, string, string, s
|
||||
}
|
||||
|
||||
// getOracleAuthToolInfo returns statements and params for my-auth-tool for Oracle SQL
|
||||
func getOracleAuthToolInfo(tableName string) (string, string, string, []any) {
|
||||
func getOracleAuthToolInfo(t *testing.T, tableName string) (string, string, string, []any) {
|
||||
createStatement := fmt.Sprintf(`CREATE TABLE %s ("id" NUMBER GENERATED AS IDENTITY PRIMARY KEY, "name" VARCHAR2(255), "email" VARCHAR2(255))`, tableName)
|
||||
|
||||
// MODIFIED: Use a PL/SQL block for multiple inserts
|
||||
|
||||
Reference in New Issue
Block a user