diff --git a/src/dbtools-mcp-server/dbtools-mcp-server.py b/src/dbtools-mcp-server/dbtools-mcp-server.py index 5bc1cd83..97926fb7 100755 --- a/src/dbtools-mcp-server/dbtools-mcp-server.py +++ b/src/dbtools-mcp-server/dbtools-mcp-server.py @@ -805,7 +805,7 @@ def bootstrap_reports(dbtools_connection_display_name: str) -> str: }) @mcp.tool() -def create_report(dbtools_connection_display_name: str, name: str, sql_query: str, description: str = None, bind_parameters: list = None) -> str: +def create_report(dbtools_connection_display_name: str, name: str, sql_query: str, description: str | None = None, bind_parameters: list | None = None) -> str: """ Create a new report definition in the report_definitions table. @@ -914,7 +914,7 @@ def create_report(dbtools_connection_display_name: str, name: str, sql_query: st }) @mcp.tool() -def execute_report(dbtools_connection_display_name: str, report_name: str, bind_values: dict = None) -> str: +def execute_report(dbtools_connection_display_name: str, report_name: str, bind_values: dict | None = None) -> str: """ Execute a report by its name with optional bind parameter values. diff --git a/src/dbtools-mcp-server/test_null_inputs.py b/src/dbtools-mcp-server/test_null_inputs.py new file mode 100644 index 00000000..9e2ae31f --- /dev/null +++ b/src/dbtools-mcp-server/test_null_inputs.py @@ -0,0 +1,70 @@ +""" +Copyright (c) 2026, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +import importlib.util +import sys +import unittest +from contextlib import ExitStack +from pathlib import Path +from unittest.mock import patch + +from fastmcp import Client +from fastmcp.exceptions import ToolError +from jsonschema import Draft202012Validator + + +class TestNullInputs(unittest.IsolatedAsyncioTestCase): + @classmethod + def setUpClass(cls): + # The script creates OCI clients at import; keep those external boundaries offline. + config = dict.fromkeys(("tenancy", "user", "fingerprint", "key_file", "pass_phrase"), "sample") + with ExitStack() as stack: + stack.enter_context(patch("oci.config.from_file", return_value=config)) + for target in ( + "oci.identity.IdentityClient", "oci.resource_search.ResourceSearchClient", + "oci.database.DatabaseClient", "oci.database_tools.DatabaseToolsClient", + "oci.vault.VaultsClient", "oci.secrets.SecretsClient", + "oci.object_storage.ObjectStorageClient", "oci.signer.Signer", + ): + stack.enter_context(patch(target)) + spec = importlib.util.spec_from_file_location( + "dbtools_null_inputs", Path(__file__).with_name("dbtools-mcp-server.py") + ) + cls.server = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = cls.server + spec.loader.exec_module(cls.server) + + @classmethod + def tearDownClass(cls): + sys.modules.pop("dbtools_null_inputs", None) + + async def test_null_defaults_are_accepted_by_advertised_schemas(self): + async with Client(self.server.mcp) as client: + for tool in await client.list_tools(): + validator = Draft202012Validator(tool.inputSchema) + for name, schema in tool.inputSchema["properties"].items(): + if "default" in schema and schema["default"] is None: + self.assertTrue(validator.evolve(schema=schema).is_valid(None), f"{tool.name}.{name}") + + async def test_optional_report_inputs(self): + cases = [ + ("create_report", {"dbtools_connection_display_name": "sample", "name": "sample", + "sql_query": "SELECT 1"}, "description", "Sample report"), + ("create_report", {"dbtools_connection_display_name": "sample", "name": "sample", + "sql_query": "SELECT 1"}, "bind_parameters", ["id"]), + ("execute_report", {"dbtools_connection_display_name": "sample", "report_name": "sample"}, + "bind_values", {"id": 1}), + ] + with patch.object(self.server, "_resolve_connection_or_error", return_value=(None, '{"error": "Offline test"}')): + async with Client(self.server.mcp) as client: + for name, required, parameter, valid in cases: + with self.subTest(tool=name, parameter=parameter): + omitted = await client.call_tool(name, required) + for value in (None, valid): + result = await client.call_tool(name, {**required, parameter: value}) + self.assertEqual(result.content, omitted.content) + with self.assertRaisesRegex(ToolError, "validation error"): + await client.call_tool(name, {**required, parameter: 7}) diff --git a/src/mysql-mcp-server/oracle/mysql_mcp_server/server.py b/src/mysql-mcp-server/oracle/mysql_mcp_server/server.py index 6520f099..3a987381 100755 --- a/src/mysql-mcp-server/oracle/mysql_mcp_server/server.py +++ b/src/mysql-mcp-server/oracle/mysql_mcp_server/server.py @@ -246,7 +246,7 @@ def list_all_connections() -> str: @mcp.tool() def execute_sql_tool_by_connection_id( - connection_id: str, sql_script: str, params: list = None + connection_id: str, sql_script: str, params: Optional[list] = None ) -> str: """ Execute a SQL script on the specified database connection. @@ -1021,7 +1021,7 @@ def _get_compartment_by_name( @mcp.tool() def object_storage_list_buckets( - compartment_name: str = None, compartment_id: str = None + compartment_name: Optional[str] = None, compartment_id: Optional[str] = None ) -> str: """ [MCP Tool] List all accessible Object Storage buckets in a compartment. diff --git a/src/mysql-mcp-server/oracle/mysql_mcp_server/tests/test_null_inputs.py b/src/mysql-mcp-server/oracle/mysql_mcp_server/tests/test_null_inputs.py new file mode 100644 index 00000000..fe524fe5 --- /dev/null +++ b/src/mysql-mcp-server/oracle/mysql_mcp_server/tests/test_null_inputs.py @@ -0,0 +1,50 @@ +""" +Copyright (c) 2026, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +import importlib +import unittest +from unittest.mock import patch + +from fastmcp import Client +from fastmcp.exceptions import ToolError +from jsonschema import Draft202012Validator + + +class TestNullInputs(unittest.IsolatedAsyncioTestCase): + @classmethod + def setUpClass(cls): + from oracle.mysql_mcp_server import utils + + with patch.object(utils, "load_mysql_config", side_effect=RuntimeError("Offline test")), \ + patch.object(utils, "OciInfo", side_effect=RuntimeError("Offline test")): + cls.server = importlib.import_module("oracle.mysql_mcp_server.server") + + async def test_null_defaults_are_accepted_by_advertised_schemas(self): + async with Client(self.server.mcp) as client: + for tool in await client.list_tools(): + validator = Draft202012Validator(tool.inputSchema) + for name, schema in tool.inputSchema["properties"].items(): + if "default" in schema and schema["default"] is None: + self.assertTrue(validator.evolve(schema=schema).is_valid(None), f"{tool.name}.{name}") + + async def test_optional_inputs(self): + cases = [ + ("execute_sql_tool_by_connection_id", {"connection_id": "sample", "sql_script": "SELECT 1"}, + "params", [1]), + ("object_storage_list_buckets", {}, "compartment_name", "sample"), + ("object_storage_list_buckets", {}, "compartment_id", "sample"), + ] + with patch.object(self.server, "_get_db_connection", side_effect=RuntimeError("Offline test")), \ + patch.object(self.server, "oci_error_msg", '{"error": "Offline test"}'): + async with Client(self.server.mcp) as client: + for name, required, parameter, valid in cases: + with self.subTest(tool=name, parameter=parameter): + omitted = await client.call_tool(name, required) + for value in (None, valid): + result = await client.call_tool(name, {**required, parameter: value}) + self.assertEqual(result.content, omitted.content) + with self.assertRaisesRegex(ToolError, "validation error"): + await client.call_tool(name, {**required, parameter: 7}) diff --git a/src/oci-cloud-guard-mcp-server/CHANGELOG.md b/src/oci-cloud-guard-mcp-server/CHANGELOG.md index d35e4d2b..964e83f7 100644 --- a/src/oci-cloud-guard-mcp-server/CHANGELOG.md +++ b/src/oci-cloud-guard-mcp-server/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixed + +- Allow explicit null for the optional comment in update_problem_status, matching omission. + ### Security - Updated locked AnyIO to 4.15.1; updated locked multidict to 6.9.1; updated PyJWT to 2.15.1 and urllib3 to 2.8.0; updated `cryptography` to 50.0.1 to prevent PKCS#7 EnvelopedData decryption from exposing a Bleichenbacher oracle through distinguishable errors and timing (CVE-2026-69247). diff --git a/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/server.py b/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/server.py index 6c5647aa..8d664c1e 100644 --- a/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/server.py +++ b/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/server.py @@ -156,7 +156,7 @@ def update_problem_status( "OPEN", description="Action taken by user. Allowed values are: OPEN, RESOLVED, DISMISSED, CLOSED", ), - comment: str = Field(None, description="A comment from the user"), + comment: Optional[str] = Field(None, description="A comment from the user"), ) -> Problem: updated_problem_status = oci.cloud_guard.models.UpdateProblemStatusDetails(status=status, comment=comment) response = get_cloud_guard_client().update_problem_status( diff --git a/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/tests/test_null_inputs.py b/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/tests/test_null_inputs.py new file mode 100644 index 00000000..35815c64 --- /dev/null +++ b/src/oci-cloud-guard-mcp-server/oracle/oci_cloud_guard_mcp_server/tests/test_null_inputs.py @@ -0,0 +1,49 @@ +""" +Copyright (c) 2026, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +from unittest.mock import MagicMock + +import oci +import pytest +from fastmcp import Client +from fastmcp.exceptions import ToolError +from jsonschema import Draft202012Validator + +from oracle.oci_cloud_guard_mcp_server import server + + +@pytest.mark.asyncio +async def test_null_defaults_are_accepted_by_advertised_schemas(): + async with Client(server.mcp) as client: + for tool in await client.list_tools(): + validator = Draft202012Validator(tool.inputSchema) + for name, schema in tool.inputSchema["properties"].items(): + if "default" in schema and schema["default"] is None: + assert validator.evolve(schema=schema).is_valid(None), f"{tool.name}.{name}" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("comment_args", [{}, {"comment": None}, {"comment": "Investigated"}]) +async def test_update_problem_accepts_optional_comment(monkeypatch, comment_args): + sdk = MagicMock() + sdk.update_problem_status.return_value.data = oci.cloud_guard.models.Problem( + id="sample", lifecycle_detail="OPEN", comment=comment_args.get("comment") + ) + monkeypatch.setattr(server, "get_cloud_guard_client", lambda: sdk) + + async with Client(server.mcp) as client: + result = await client.call_tool("update_problem_status", {"problem_id": "sample", **comment_args}) + assert result.structured_content["id"] == "sample" + assert result.structured_content["comment"] == comment_args.get("comment") + details = sdk.update_problem_status.call_args.kwargs["update_problem_status_details"] + assert details.comment == comment_args.get("comment") + + +@pytest.mark.asyncio +async def test_update_problem_rejects_invalid_comment(): + async with Client(server.mcp) as client: + with pytest.raises(ToolError, match="valid string"): + await client.call_tool("update_problem_status", {"problem_id": "sample", "comment": 7}) diff --git a/src/oci-database-mcp-server/CHANGELOG.md b/src/oci-database-mcp-server/CHANGELOG.md index f8abca6a..f38f3963 100644 --- a/src/oci-database-mcp-server/CHANGELOG.md +++ b/src/oci-database-mcp-server/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to OCI Database MCP Server are documented in this file. ## Unreleased +### Fixed + +- Allow explicit null for optional region arguments so calls use the configured default region, matching omission. + ### Security - Updated locked AnyIO to 4.15.1; updated locked multidict to 6.9.1; updated PyJWT to 2.15.1 and urllib3 to 2.8.0; updated `cryptography` to 50.0.1 to prevent PKCS#7 EnvelopedData decryption from exposing a Bleichenbacher oracle through distinguishable errors and timing (CVE-2026-69247). diff --git a/src/oci-database-mcp-server/oracle/oci_database_mcp_server/server.py b/src/oci-database-mcp-server/oracle/oci_database_mcp_server/server.py index 593ea124..0e1271a5 100644 --- a/src/oci-database-mcp-server/oracle/oci_database_mcp_server/server.py +++ b/src/oci-database-mcp-server/oracle/oci_database_mcp_server/server.py @@ -365,7 +365,7 @@ def get_public_ip_for_database( "The database `OCID` ", ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Optional[str]: @@ -458,7 +458,7 @@ def delete_pluggable_database( Optional[str], "Unique identifier for the request." ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Any: @@ -484,7 +484,7 @@ def get_pluggable_database( "The database `OCID` ", ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> PluggableDatabase: @@ -515,7 +515,7 @@ def update_pluggable_database( "For optimistic concurrency control. In the PUT or DELETE call for a resource, set the `if-match` parameter to the value of the etag from a previous GET or POST response for that resource. The resource will be updated or deleted only if the etag you provide matches the resource's current etag value.", ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> PluggableDatabase: @@ -792,7 +792,7 @@ def list_application_vips( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ApplicationVipSummary]: @@ -837,7 +837,7 @@ def list_autonomous_container_database_dataguard_associations( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousContainerDatabaseDataguardAssociation]: @@ -892,7 +892,7 @@ def list_autonomous_container_database_versions( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousContainerDatabaseVersionSummary]: @@ -1005,7 +1005,7 @@ def list_autonomous_container_databases( Optional[Any], ("The cloud Autonomous VM Cluster `OCID`.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousContainerDatabaseSummary]: @@ -1109,7 +1109,7 @@ def list_autonomous_database_backups( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabaseBackupSummary]: @@ -1175,7 +1175,7 @@ def list_autonomous_database_character_sets( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabaseCharacterSets]: @@ -1265,7 +1265,7 @@ def list_autonomous_database_clones( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabaseSummary]: @@ -1314,7 +1314,7 @@ def list_autonomous_database_dataguard_associations( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabaseDataguardAssociation]: @@ -1356,7 +1356,7 @@ def list_autonomous_database_peers( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabasePeerCollection]: @@ -1398,7 +1398,7 @@ def list_autonomous_database_refreshable_clones( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[RefreshableCloneCollection]: @@ -1478,7 +1478,7 @@ def list_autonomous_database_software_images( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabaseSoftwareImageCollection]: @@ -1648,7 +1648,7 @@ def list_autonomous_databases( ("The database `OCID` of the resourcepool Leader Autonomous" "Database."), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDatabaseSummary]: @@ -1734,7 +1734,7 @@ def list_autonomous_db_preview_versions( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDbPreviewVersionSummary]: @@ -1789,7 +1789,7 @@ def list_autonomous_db_versions( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousDbVersionSummary]: @@ -1845,7 +1845,7 @@ def list_autonomous_virtual_machines( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousVirtualMachineSummary]: @@ -1928,7 +1928,7 @@ def list_autonomous_vm_clusters( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousVmClusterSummary]: @@ -1981,7 +1981,7 @@ def list_backup_destination( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[BackupDestinationSummary]: @@ -2029,7 +2029,7 @@ def list_backups( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[BackupSummary]: @@ -2119,7 +2119,7 @@ def list_cloud_autonomous_vm_clusters( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[CloudAutonomousVmClusterSummary]: @@ -2214,7 +2214,7 @@ def list_cloud_exadata_infrastructures( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[CloudExadataInfrastructureSummary]: @@ -2273,7 +2273,7 @@ def list_cloud_vm_cluster_updates( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[UpdateSummary]: @@ -2356,7 +2356,7 @@ def list_cloud_vm_clusters( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[CloudVmClusterSummary]: @@ -2393,7 +2393,7 @@ def list_cloud_vm_clusters( def list_console_connections( db_node_id: Annotated[Optional[Any], ("The database node `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ConsoleConnectionSummary]: @@ -2454,7 +2454,7 @@ def list_console_histories( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ConsoleHistoryCollection]: @@ -2505,7 +2505,7 @@ def list_container_database_patches( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[AutonomousPatchSummary]: @@ -2539,7 +2539,7 @@ def list_data_guard_associations( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DataGuardAssociationSummary]: @@ -2636,7 +2636,7 @@ def list_database_software_images( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DatabaseSoftwareImageSummary]: @@ -2727,7 +2727,7 @@ def list_databases( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DatabaseSummary]: @@ -2772,7 +2772,7 @@ def list_db_home_patch_history_entries( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[PatchHistoryEntrySummary]: @@ -2803,7 +2803,7 @@ def list_db_home_patches( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[PatchSummary]: @@ -2891,7 +2891,7 @@ def list_db_homes( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbHomeSummary]: @@ -2977,7 +2977,7 @@ def list_db_nodes( Optional[Any], ("The `OCID` of the Exacc Db server.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbNodeSummary]: @@ -3059,7 +3059,7 @@ def list_db_servers( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbServerSummary]: @@ -3107,7 +3107,7 @@ def list_db_system_compute_performances( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbSystemComputePerformanceSummary]: @@ -3137,7 +3137,7 @@ def list_db_system_patches( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[PatchSummary]: @@ -3175,7 +3175,7 @@ def list_db_system_shapes( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbSystemShapeSummary]: @@ -3221,7 +3221,7 @@ def list_db_system_storage_performances( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbSystemStoragePerformanceSummary]: @@ -3310,7 +3310,7 @@ def list_db_systems( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbSystemSummary]: @@ -3392,7 +3392,7 @@ def list_db_versions( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[DbVersionSummary]: @@ -3483,7 +3483,7 @@ def list_exadata_infrastructures( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExadataInfrastructureSummary]: @@ -3549,7 +3549,7 @@ def list_exadb_vm_cluster_updates( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExadbVmClusterUpdateSummary]: @@ -3635,7 +3635,7 @@ def list_exadb_vm_clusters( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExadbVmClusterSummary]: @@ -3723,7 +3723,7 @@ def list_exascale_db_storage_vaults( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExascaleDbStorageVaultSummary]: @@ -3811,7 +3811,7 @@ def list_execution_actions( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExecutionActionSummary]: @@ -3898,7 +3898,7 @@ def list_execution_windows( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExecutionWindowSummary]: @@ -3979,7 +3979,7 @@ def list_external_container_databases( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExternalContainerDatabaseSummary]: @@ -4064,7 +4064,7 @@ def list_external_database_connectors( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExternalDatabaseConnectorSummary]: @@ -4146,7 +4146,7 @@ def list_external_non_container_databases( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExternalNonContainerDatabaseSummary]: @@ -4230,7 +4230,7 @@ def list_external_pluggable_databases( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ExternalPluggableDatabaseSummary]: @@ -4308,7 +4308,7 @@ def list_flex_components( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[FlexComponentCollection]: @@ -4393,7 +4393,7 @@ def list_gi_version_minor_versions( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[GiMinorVersionSummary]: @@ -4454,7 +4454,7 @@ def list_gi_versions( ("The target availability domain. Only passed if the limit is" "AD-specific."), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[GiVersionSummary]: @@ -4492,7 +4492,7 @@ def list_key_stores( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[KeyStoreSummary]: @@ -4589,7 +4589,7 @@ def list_maintenance_run_history( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[MaintenanceRunHistorySummary]: @@ -4698,7 +4698,7 @@ def list_maintenance_runs( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[MaintenanceRunSummary]: @@ -4780,7 +4780,7 @@ def list_oneoff_patches( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[OneoffPatchSummary]: @@ -4860,7 +4860,7 @@ def list_pluggable_databases( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[PluggableDatabaseSummary]: @@ -4957,7 +4957,7 @@ def list_scheduled_actions( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ScheduledActionCollection]: @@ -5059,7 +5059,7 @@ def list_scheduling_plans( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[SchedulingPlanCollection]: @@ -5142,7 +5142,7 @@ def list_scheduling_policies( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[SchedulingPolicySummary]: @@ -5220,7 +5220,7 @@ def list_scheduling_windows( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[SchedulingWindowSummary]: @@ -5278,7 +5278,7 @@ def list_system_versions( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[SystemVersionCollection]: @@ -5359,7 +5359,7 @@ def list_vm_cluster_networks( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[VmClusterNetworkSummary]: @@ -5404,7 +5404,7 @@ def list_vm_cluster_patches( Optional[Any], ("The pagination token to continue listing from.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[PatchSummary]: @@ -5457,7 +5457,7 @@ def list_vm_cluster_updates( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[VmClusterUpdateSummary]: @@ -5538,7 +5538,7 @@ def list_vm_clusters( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[VmClusterSummary]: @@ -5604,7 +5604,7 @@ def resource_pool_shapes( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[ResourcePoolShapeCollection]: @@ -5641,7 +5641,7 @@ def get_application_vip( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ApplicationVip: @@ -5666,7 +5666,7 @@ def get_autonomous_container_database( Optional[Any], ("The Autonomous Container Database `OCID`.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousContainerDatabase: @@ -5701,7 +5701,7 @@ def get_autonomous_container_database_dataguard_association( ), ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousContainerDatabaseDataguardAssociation: @@ -5736,7 +5736,7 @@ def get_autonomous_container_database_resource_usage( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousContainerDatabaseResourceUsage: @@ -5764,7 +5764,7 @@ def get_autonomous_database( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousDatabase: @@ -5792,7 +5792,7 @@ def get_autonomous_database_backup( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousDatabaseBackup: @@ -5830,7 +5830,7 @@ def get_autonomous_database_dataguard_association( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousDatabaseDataguardAssociation: @@ -5860,7 +5860,7 @@ def get_autonomous_database_regional_wallet( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousDatabaseWallet: @@ -5891,7 +5891,7 @@ def get_autonomous_database_software_image( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousDatabaseSoftwareImage: @@ -5921,7 +5921,7 @@ def get_autonomous_database_wallet( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousDatabaseWallet: @@ -5953,7 +5953,7 @@ def get_autonomous_exadata_infrastructure( Optional[Any], ("The Autonomous Exadata Infrastructure `OCID`.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousExadataInfrastructure: @@ -5976,7 +5976,7 @@ def get_autonomous_exadata_infrastructure( def get_autonomous_patch( autonomous_patch_id: Annotated[Optional[Any], ("The autonomous patch `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousPatch: @@ -6000,7 +6000,7 @@ def get_autonomous_virtual_machine( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousVirtualMachine: @@ -6034,7 +6034,7 @@ def get_autonomous_vm_cluster( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousVmCluster: @@ -6064,7 +6064,7 @@ def get_autonomous_vm_cluster_resource_usage( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> AutonomousVmClusterResourceUsage: @@ -6087,7 +6087,7 @@ def get_autonomous_vm_cluster_resource_usage( def get_backup( backup_id: Annotated[Optional[Any], ("The backup `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Backup: @@ -6116,7 +6116,7 @@ def get_backup_destination( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> BackupDestination: @@ -6148,7 +6148,7 @@ def get_cloud_autonomous_vm_cluster( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> CloudAutonomousVmCluster: @@ -6181,7 +6181,7 @@ def get_cloud_autonomous_vm_cluster_resource_usage( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> CloudAutonomousVmClusterResourceUsage: @@ -6217,7 +6217,7 @@ def get_cloud_exadata_infrastructure( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> CloudExadataInfrastructure: @@ -6253,7 +6253,7 @@ def get_cloud_exadata_infrastructure_unallocated_resources( Optional[Any], ("The list of `OCIDs` of the Db servers.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> CloudExadataInfrastructureUnallocatedResources: @@ -6289,7 +6289,7 @@ def get_cloud_vm_cluster( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> CloudVmCluster: @@ -6318,7 +6318,7 @@ def get_cloud_vm_cluster_iorm_config( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadataIormConfig: @@ -6350,7 +6350,7 @@ def get_cloud_vm_cluster_update( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Update: @@ -6383,7 +6383,7 @@ def get_cloud_vm_cluster_update_history_entry( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> UpdateHistoryEntry: @@ -6412,7 +6412,7 @@ def get_console_connection( Optional[Any], ("The OCID of the console connection.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ConsoleConnection: @@ -6438,7 +6438,7 @@ def get_console_history( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ConsoleHistory: @@ -6468,7 +6468,7 @@ def get_console_history_content( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> list[Any]: @@ -6497,7 +6497,7 @@ def get_data_guard_association( Optional[Any], ("The Data Guard association's `OCID`.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DataGuardAssociation: @@ -6517,7 +6517,7 @@ def get_data_guard_association( def get_database( database_id: Annotated[Optional[Any], ("The database `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Database: @@ -6536,7 +6536,7 @@ def get_database( def get_database_software_image( database_software_image_id: Annotated[Optional[Any], ("The DB system `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DatabaseSoftwareImage: @@ -6561,7 +6561,7 @@ def get_database_upgrade_history_entry( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DatabaseUpgradeHistoryEntry: @@ -6585,7 +6585,7 @@ def get_database_upgrade_history_entry( def get_db_home( db_home_id: Annotated[Optional[Any], ("The Database Home `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DbHome: @@ -6605,7 +6605,7 @@ def get_db_home_patch( db_home_id: Annotated[Optional[Any], ("The Database Home `OCID`.")], patch_id: Annotated[Optional[Any], ("The `OCID` of the patch.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Patch: @@ -6630,7 +6630,7 @@ def get_db_home_patch_history_entry( Optional[Any], ("The `OCID` of the patch history entry.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> PatchHistoryEntry: @@ -6652,7 +6652,7 @@ def get_db_home_patch_history_entry( def get_db_node( db_node_id: Annotated[Optional[Any], ("The database node `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DbNode: @@ -6677,7 +6677,7 @@ def get_db_server( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DbServer: @@ -6699,7 +6699,7 @@ def get_db_server( def get_db_system( db_system_id: Annotated[Optional[Any], ("The DB system `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DbSystem: @@ -6719,7 +6719,7 @@ def get_db_system_patch( db_system_id: Annotated[Optional[Any], ("The DB system `OCID`.")], patch_id: Annotated[Optional[Any], ("The `OCID` of the patch.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Patch: @@ -6746,7 +6746,7 @@ def get_db_system_patch_history_entry( Optional[Any], ("The `OCID` of the patch history entry.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> PatchHistoryEntry: @@ -6779,7 +6779,7 @@ def get_db_system_upgrade_history_entry( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> DbSystemUpgradeHistoryEntry: @@ -6820,7 +6820,7 @@ def get_exadata_infrastructure( ), ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadataInfrastructure: @@ -6853,7 +6853,7 @@ def get_exadata_infrastructure_ocpus( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> OCPUs: @@ -6891,7 +6891,7 @@ def get_exadata_infrastructure_un_allocated_resources( Optional[Any], ("The list of `OCIDs` of the Db servers.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadataInfrastructureUnAllocatedResources: @@ -6925,7 +6925,7 @@ def get_exadata_iorm_config( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadataIormConfig: @@ -6957,7 +6957,7 @@ def get_exadb_vm_cluster( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadbVmCluster: @@ -6989,7 +6989,7 @@ def get_exadb_vm_cluster_update( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadbVmClusterUpdate: @@ -7024,7 +7024,7 @@ def get_exadb_vm_cluster_update_history_entry( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExadbVmClusterUpdateHistoryEntry: @@ -7058,7 +7058,7 @@ def get_exascale_db_storage_vault( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExascaleDbStorageVault: @@ -7082,7 +7082,7 @@ def get_execution_action( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExecutionAction: @@ -7106,7 +7106,7 @@ def get_execution_window( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExecutionWindow: @@ -7127,7 +7127,7 @@ def get_execution_window( def get_external_backup_job( backup_id: Annotated[Optional[Any], ("The backup `OCID`.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExternalBackupJob: @@ -7153,7 +7153,7 @@ def get_external_container_database( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExternalContainerDatabase: @@ -7187,7 +7187,7 @@ def get_external_database_connector( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExternalDatabaseConnector: @@ -7217,7 +7217,7 @@ def get_external_non_container_database( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExternalNonContainerDatabase: @@ -7247,7 +7247,7 @@ def get_external_pluggable_database( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ExternalPluggableDatabase: @@ -7290,7 +7290,7 @@ def get_infrastructure_target_versions( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> InfrastructureTargetVersion: @@ -7320,7 +7320,7 @@ def get_key_store( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> KeyStore: @@ -7341,7 +7341,7 @@ def get_key_store( def get_maintenance_run( maintenance_run_id: Annotated[Optional[Any], ("The maintenance run OCID.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> MaintenanceRun: @@ -7362,7 +7362,7 @@ def get_maintenance_run_history( Optional[Any], ("The maintenance run history OCID.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> MaintenanceRunHistory: @@ -7384,7 +7384,7 @@ def get_oneoff_patch( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> OneoffPatch: @@ -7416,7 +7416,7 @@ def get_pdb_conversion_history_entry( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> PdbConversionHistoryEntry: @@ -7443,7 +7443,7 @@ def get_scheduled_action( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> ScheduledAction: @@ -7467,7 +7467,7 @@ def get_scheduling_plan( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> SchedulingPlan: @@ -7491,7 +7491,7 @@ def get_scheduling_policy( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> SchedulingPolicy: @@ -7516,7 +7516,7 @@ def get_scheduling_window( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> SchedulingWindow: @@ -7546,7 +7546,7 @@ def get_vm_cluster( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> VmCluster: @@ -7578,7 +7578,7 @@ def get_vm_cluster_network( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> VmClusterNetwork: @@ -7601,7 +7601,7 @@ def get_vm_cluster_patch( vm_cluster_id: Annotated[Optional[Any], ("The VM cluster `OCID`.")], patch_id: Annotated[Optional[Any], ("The `OCID` of the patch.")], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> Patch: @@ -7628,7 +7628,7 @@ def get_vm_cluster_patch_history_entry( Optional[Any], ("The `OCID` of the patch history entry.") ], region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> PatchHistoryEntry: @@ -7659,7 +7659,7 @@ def get_vm_cluster_update( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> VmClusterUpdate: @@ -7692,7 +7692,7 @@ def get_vm_cluster_update_history_entry( Optional[Any], ("Unique identifier for the request.") ] = None, region: Annotated[ - str, + Optional[str], "Region to execute the request (Use proper region identifiers like us-ashburn-1, eu-zurich-1), if unspecified then default region will be picked", ] = None, ) -> VmClusterUpdateHistoryEntry: diff --git a/src/oci-database-mcp-server/oracle/oci_database_mcp_server/tests/test_null_inputs.py b/src/oci-database-mcp-server/oracle/oci_database_mcp_server/tests/test_null_inputs.py new file mode 100644 index 00000000..50763a12 --- /dev/null +++ b/src/oci-database-mcp-server/oracle/oci_database_mcp_server/tests/test_null_inputs.py @@ -0,0 +1,55 @@ +""" +Copyright (c) 2026, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest +from fastmcp import Client +from fastmcp.exceptions import ToolError +from jsonschema import Draft202012Validator + +from oracle.oci_database_mcp_server import server + + +@pytest.mark.asyncio +async def test_null_defaults_are_accepted_by_advertised_schemas(): + async with Client(server.mcp) as client: + for tool in await client.list_tools(): + validator = Draft202012Validator(tool.inputSchema) + for name, schema in tool.inputSchema["properties"].items(): + if "default" in schema and schema["default"] is None: + assert validator.evolve(schema=schema).is_valid(None), f"{tool.name}.{name}" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("region_args", [{}, {"region": None}, {"region": "us-ashburn-1"}]) +async def test_public_ip_accepts_default_and_explicit_regions(monkeypatch, region_args): + database = MagicMock() + database.get_database.return_value.data = SimpleNamespace( + db_system_id="sample", vm_cluster_id=None, compartment_id="sample" + ) + database.list_db_nodes.return_value.data = [] + monkeypatch.setattr( + server, "build_auth_context", + lambda: SimpleNamespace(config={"region": "us-phoenix-1"}, signer=None), + ) + factory = MagicMock(return_value=database) + monkeypatch.setattr(server.oci.database, "DatabaseClient", factory) + + async with Client(server.mcp) as client: + result = await client.call_tool( + "get_public_ip_for_database", {"database_id": "sample", **region_args} + ) + assert not result.is_error + assert factory.call_args.args[0]["region"] == (region_args.get("region") or "us-phoenix-1") + + +@pytest.mark.asyncio +async def test_public_ip_rejects_invalid_region(): + async with Client(server.mcp) as client: + with pytest.raises(ToolError, match="valid string"): + await client.call_tool("get_public_ip_for_database", {"database_id": "sample", "region": 7}) diff --git a/src/oci-networking-mcp-server/CHANGELOG.md b/src/oci-networking-mcp-server/CHANGELOG.md index 702ca2d5..281e4f83 100644 --- a/src/oci-networking-mcp-server/CHANGELOG.md +++ b/src/oci-networking-mcp-server/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixed + +- Allow explicit null for optional VCN and VLAN filters in list_subnets, list_security_lists, and list_network_security_groups. + ### Security - Updated locked multidict to 6.9.1; updated locked AnyIO to 4.15.1; updated locked `PyJWT` to 2.15.1 and `urllib3` to 2.8.0; updated `cryptography` to 50.0.1 to prevent PKCS#7 EnvelopedData decryption from exposing a Bleichenbacher oracle through distinguishable errors and timing (CVE-2026-69247). diff --git a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/server.py b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/server.py index 0d14dbda..40dae708 100644 --- a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/server.py +++ b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/server.py @@ -179,7 +179,7 @@ def create_vcn(compartment_id: str, cidr_block: str, display_name: str) -> Vcn: @mcp.tool( description="Lists the subnets in the specified compartment. Optionally filter by VCN." ) -def list_subnets(compartment_id: str, vcn_id: str = None) -> list[Subnet]: +def list_subnets(compartment_id: str, vcn_id: str | None = None) -> list[Subnet]: subnets: list[Subnet] = [] try: @@ -254,7 +254,7 @@ def create_subnet( ) def list_security_lists( compartment_id: Annotated[str, "Compartment ocid"], - vcn_id: Annotated[str, "VCN ocid"] = None, + vcn_id: Annotated[str | None, "VCN ocid"] = None, ) -> list[SecurityList]: security_lists: list[SecurityList] = [] @@ -306,8 +306,8 @@ def get_security_list(security_list_id: Annotated[str, "security list id"]): ) def list_network_security_groups( compartment_id: Annotated[str, "compartment ocid"], - vlan_id: Annotated[str, "vlan ocid"] = None, - vcn_id: Annotated[str, "vcn ocid"] = None, + vlan_id: Annotated[str | None, "vlan ocid"] = None, + vcn_id: Annotated[str | None, "vcn ocid"] = None, ) -> list[NetworkSecurityGroup]: nsgs: list[NetworkSecurityGroup] = [] diff --git a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_null_inputs.py b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_null_inputs.py new file mode 100644 index 00000000..c01a390b --- /dev/null +++ b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_null_inputs.py @@ -0,0 +1,53 @@ +""" +Copyright (c) 2026, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +from unittest.mock import MagicMock + +import pytest +from fastmcp import Client +from fastmcp.exceptions import ToolError +from jsonschema import Draft202012Validator + +from oracle.oci_networking_mcp_server import server + + +@pytest.mark.asyncio +async def test_null_defaults_are_accepted_by_advertised_schemas(): + async with Client(server.mcp) as client: + for tool in await client.list_tools(): + validator = Draft202012Validator(tool.inputSchema) + for name, schema in tool.inputSchema["properties"].items(): + if "default" in schema and schema["default"] is None: + assert validator.evolve(schema=schema).is_valid(None), f"{tool.name}.{name}" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("tool_name", "filters"), + [("list_subnets", ("vcn_id",)), ("list_security_lists", ("vcn_id",)), + ("list_network_security_groups", ("vcn_id", "vlan_id"))], +) +@pytest.mark.parametrize("value", [None, "sample", 7]) +async def test_optional_network_filters(monkeypatch, tool_name, filters, value): + sdk = MagicMock() + response = getattr(sdk, tool_name).return_value + response.data = [] + response.has_next_page = False + response.next_page = None + monkeypatch.setattr(server, "get_networking_client", lambda: sdk) + + async with Client(server.mcp) as client: + omitted = await client.call_tool(tool_name, {"compartment_id": "sample"}) + assert omitted.structured_content["result"] == [] + arguments = {"compartment_id": "sample", **dict.fromkeys(filters, value)} + if isinstance(value, int): + with pytest.raises(ToolError, match="valid string"): + await client.call_tool(tool_name, arguments) + else: + result = await client.call_tool(tool_name, arguments) + assert result.structured_content["result"] == [] + for name in filters: + assert getattr(sdk, tool_name).call_args.kwargs[name] == value diff --git a/src/oracle-data-studio-mcp-server/CHANGELOG.md b/src/oracle-data-studio-mcp-server/CHANGELOG.md index a6ba8263..385ad625 100644 --- a/src/oracle-data-studio-mcp-server/CHANGELOG.md +++ b/src/oracle-data-studio-mcp-server/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixed + +- Allow explicit null for optional tool inputs across Essbase, ADP, and Data Transforms, matching omission. + ### Changed - Raised the server's unit-test coverage gate to 90%, including branch coverage, with focused offline tests for connection and SDK failures, partial responses, mutation confirmation, and Data Transforms builders. diff --git a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tests/test_null_inputs.py b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tests/test_null_inputs.py new file mode 100644 index 00000000..3af132b9 --- /dev/null +++ b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tests/test_null_inputs.py @@ -0,0 +1,43 @@ +""" +Copyright (c) 2026, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +import pytest +from jsonschema import Draft202012Validator +from mcp.server.fastmcp import FastMCP +from mcp.server.fastmcp.exceptions import ToolError + +from oracle.data_studio_mcp_server.tools import adp_tools, dt_tools, essbase_tools + + +def test_null_defaults_are_accepted_by_advertised_schemas(): + mcp = FastMCP("test") + for module in (adp_tools, dt_tools, essbase_tools): + module.register_tools(mcp) + for tool in mcp._tool_manager.list_tools(): + validator = Draft202012Validator(tool.parameters) + assert "ctx" not in tool.parameters["properties"] + for name, schema in tool.parameters["properties"].items(): + if "default" in schema and schema["default"] is None: + assert validator.evolve(schema=schema).is_valid(None), f"{tool.name}.{name}" + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("module", "connection", "tool_name", "required", "parameter", "valid", "invalid"), + [(adp_tools, "get_adp", "adp_build_analytic_view", {"fact_table": "FACT"}, "av_name", "AV", 7), + (essbase_tools, "get_essbase", "essbase_outline_metadata", + {"app_name": "Sample", "db_name": "Basic"}, "dimension_name", "Measures", 7), + (dt_tools, "get_dt", "dt_manage_connection", {"action": "list"}, "port", 1521, "invalid")], +) +async def test_optional_tool_inputs(monkeypatch, module, connection, tool_name, required, parameter, valid, invalid): + mcp = FastMCP("test") + module.register_tools(mcp) + monkeypatch.setattr(module, connection, lambda ctx: None) + omitted = await mcp.call_tool(tool_name, required) + for value in (None, valid): + assert await mcp.call_tool(tool_name, {**required, parameter: value}) == omitted + with pytest.raises(ToolError, match="validation error"): + await mcp.call_tool(tool_name, {**required, parameter: invalid}) diff --git a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/adp_tools.py b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/adp_tools.py index fb660a73..855d2de6 100644 --- a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/adp_tools.py +++ b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/adp_tools.py @@ -30,7 +30,7 @@ def register_tools(mcp: FastMCP): # ── adp_build_analytic_view ───────────────────────────────────── @mcp.tool() def adp_build_analytic_view(fact_table: str, - av_name: str = None, + av_name: str | None = None, ctx: Context = None) -> str: """Create an Analytic View from a fact table, compile it, and return metadata with a data preview. @@ -107,7 +107,7 @@ def adp_build_analytic_view(fact_table: str, @mcp.tool() def adp_query_analytic_view(av_name: str, show_sql: bool = False, - owner: str = None, + owner: str | None = None, max_rows: int = 1000, ctx: Context = None) -> str: """Query data from an Analytic View with auto-discovered dimensions and measures. @@ -157,7 +157,7 @@ def adp_query_analytic_view(av_name: str, # ── 19. adp_analyze_analytic_view ───────────────────────────────── @mcp.tool() - def adp_analyze_analytic_view(av_name: str, owner: str = None, + def adp_analyze_analytic_view(av_name: str, owner: str | None = None, ctx: Context = None) -> str: """Get a full health report for an Analytic View: metadata, measures, dimensions, quality issues, errors. @@ -299,11 +299,11 @@ def adp_search(search_term: str, include_ddl: bool = False, # ── 23. adp_load_from_cloud ─────────────────────────────────────── @mcp.tool() - def adp_load_from_cloud(storage_link: str = None, - object_name: str = None, - target_table: str = None, + def adp_load_from_cloud(storage_link: str | None = None, + object_name: str | None = None, + target_table: str | None = None, consumer_group: str = 'LOW', - request_id: str = None, + request_id: str | None = None, ctx: Context = None) -> str: """Load a cloud storage object into the database as a table. @@ -385,7 +385,7 @@ def adp_load_from_cloud(storage_link: str = None, # ── 24. adp_browse_catalog ────────────────────────────────────────── @mcp.tool() - def adp_browse_catalog(action: str, catalog_name: str = None, + def adp_browse_catalog(action: str, catalog_name: str | None = None, ctx: Context = None) -> str: """Browse data catalogs (read-only): list catalogs, get entities, preview data, list database links, list databases, or check a database link. @@ -439,8 +439,8 @@ def adp_browse_catalog(action: str, catalog_name: str = None, # ── 24b. adp_manage_catalog ────────────────────────────────────── @mcp.tool() - def adp_manage_catalog(action: str, catalog_name: str = None, - confirm: str = None, + def adp_manage_catalog(action: str, catalog_name: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage data catalogs (admin-only): enable, disable, or unmount catalogs. @@ -479,12 +479,12 @@ def adp_manage_catalog(action: str, catalog_name: str = None, # ── 25. adp_manage_sharing ──────────────────────────────────────── @mcp.tool() - def adp_manage_sharing(action: str, share_name: str = None, - tables: str = None, - recipient_name: str = None, - email: str = None, - new_name: str = None, - confirm: str = None, + def adp_manage_sharing(action: str, share_name: str | None = None, + tables: str | None = None, + recipient_name: str | None = None, + email: str | None = None, + new_name: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage data sharing: list shares, create share, publish, delete, manage recipients, providers, and more. @@ -640,9 +640,9 @@ def adp_manage_sharing(action: str, share_name: str = None, # ── 26. adp_manage_analytic_views ──────────────────────────────── @mcp.tool() def adp_manage_analytic_views(action: str, - av_name: str = None, - owner: str = None, - confirm: str = None, + av_name: str | None = None, + owner: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Analytic Views: list all AVs or drop one. @@ -683,17 +683,17 @@ def adp_manage_analytic_views(action: str, # ── 27. adp_manage_credentials ─────────────────────────────────── @mcp.tool() def adp_manage_credentials(action: str, - credential_name: str = None, - username: str = None, - password: str = None, - user_ocid: str = None, - tenancy_ocid: str = None, - private_key: str = None, - fingerprint: str = None, - storage_link_name: str = None, - uri: str = None, - description: str = None, - confirm: str = None, + credential_name: str | None = None, + username: str | None = None, + password: str | None = None, + user_ocid: str | None = None, + tenancy_ocid: str | None = None, + private_key: str | None = None, + fingerprint: str | None = None, + storage_link_name: str | None = None, + uri: str | None = None, + description: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage cloud credentials and storage links for data loading. @@ -793,8 +793,8 @@ def adp_manage_credentials(action: str, @mcp.tool() def adp_ai_chat(question: str, mode: str = 'chat', - tables: str = None, - profile_name: str = None, + tables: str | None = None, + profile_name: str | None = None, max_rows: int = 1000, ctx: Context = None) -> str: """Chat with the database using Oracle Select AI in different modes. @@ -872,10 +872,10 @@ def adp_ai_chat(question: str, # ── 29. adp_manage_insights ────────────────────────────────────── @mcp.tool() def adp_manage_insights(action: str, - request_name: str = None, - insight_name: str = None, - viz_id: int = None, - confirm: str = None, + request_name: str | None = None, + insight_name: str | None = None, + viz_id: int | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage AI-generated insights: list requests, view results, check status. @@ -934,10 +934,10 @@ def adp_manage_insights(action: str, # ── 30. adp_manage_db_links ──────────────────────────────────────── @mcp.tool() def adp_manage_db_links(action: str, - db_link_name: str = None, - tables: str = None, + db_link_name: str | None = None, + tables: str | None = None, consumer_group: str = 'LOW', - confirm: str = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage database links and copy/link tables from remote databases. @@ -1014,8 +1014,8 @@ def adp_manage_db_links(action: str, @mcp.tool() def adp_get_annotations(object_name: str, object_type: str = 'TABLE', - annotation_owner: str = None, - column_name: str = None, + annotation_owner: str | None = None, + column_name: str | None = None, ctx: Context = None) -> str: """Fetch Oracle 23ai annotations on a table/view and its columns — useful for NL→SQL. diff --git a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/dt_tools.py b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/dt_tools.py index ed6ac4e6..ba7bf234 100644 --- a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/dt_tools.py +++ b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/dt_tools.py @@ -134,7 +134,7 @@ def dt_describe_project(project_name: str, # ── 28b. dt_manage_project ──────────────────────────────────────── @mcp.tool() def dt_manage_project(action: str, project_name: str, - confirm: str = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Data Transforms projects (admin-only). @@ -230,7 +230,7 @@ def dt_create_pipeline(project_name: str, target_connection: str, target_schema: str, target_table: str, - dataflow_name: str = None, + dataflow_name: str | None = None, integration_type: str = 'CONTROL_APPEND', ctx: Context = None) -> str: """Create a simple source-to-target dataflow in a Data Transforms project. @@ -371,7 +371,7 @@ def dt_check_health(ctx: Context) -> str: # ── 32. dt_browse_data ──────────────────────────────────────────── @mcp.tool() - def dt_browse_data(connection_name: str, schema: str = None, + def dt_browse_data(connection_name: str, schema: str | None = None, ctx: Context = None) -> str: """Browse schemas and tables available through a Data Transforms connection. @@ -452,15 +452,15 @@ def dt_manage_dataflow(project_name: str, dataflow_name: str, # ── 34. dt_manage_schedule ──────────────────────────────────────── @mcp.tool() def dt_manage_schedule(action: str, - schedule_name: str = None, - project_name: str = None, - resource_name: str = None, + schedule_name: str | None = None, + project_name: str | None = None, + resource_name: str | None = None, resource_type: str = 'dataflow', - frequency: str = None, - time: str = None, - days: str = None, + frequency: str | None = None, + time: str | None = None, + days: str | None = None, status: str = 'INACTIVE', - confirm: str = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Data Transforms schedules: list, create, or delete. @@ -576,8 +576,8 @@ def dt_manage_schedule(action: str, # ── 35. dt_manage_variables ─────────────────────────────────────── @mcp.tool() - def dt_manage_variables(action: str, variable_name: str = None, - value: str = None, + def dt_manage_variables(action: str, variable_name: str | None = None, + value: str | None = None, ctx: Context = None) -> str: """Manage Data Transforms variables: list, create, or update. @@ -653,16 +653,16 @@ def dt_run_pipeline(project_name: str, # ── 37. dt_manage_connection ───────────────────────────────────── @mcp.tool() def dt_manage_connection(action: str, - connection_name: str = None, - connection_type: str = None, - host: str = None, - port: int = None, - service_name: str = None, - user: str = None, - password: str = None, - wallet_path: str = None, - jdbc_url: str = None, - confirm: str = None, + connection_name: str | None = None, + connection_type: str | None = None, + host: str | None = None, + port: int | None = None, + service_name: str | None = None, + user: str | None = None, + password: str | None = None, + wallet_path: str | None = None, + jdbc_url: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Data Transforms connections: list, create, delete, or test. @@ -746,12 +746,12 @@ def dt_manage_connection(action: str, @mcp.tool() def dt_manage_dataload(action: str, project_name: str, - dataload_name: str = None, - source_connection: str = None, - source_schema: str = None, - target_connection: str = None, - target_schema: str = None, - tables: str = None, + dataload_name: str | None = None, + source_connection: str | None = None, + source_schema: str | None = None, + target_connection: str | None = None, + target_schema: str | None = None, + tables: str | None = None, load_type: str = 'TRUNCATE', ctx: Context = None) -> str: """Manage dataloads in a Data Transforms project. @@ -838,10 +838,10 @@ def dt_manage_dataload(action: str, # ── 39. dt_manage_data_entities ────────────────────────────────── @mcp.tool() def dt_manage_data_entities(action: str, - connection_name: str = None, - schema_name: str = None, - entity_name: str = None, - entity_id: str = None, + connection_name: str | None = None, + schema_name: str | None = None, + entity_name: str | None = None, + entity_id: str | None = None, ctx: Context = None) -> str: """Manage data entities in Data Transforms: list, get details, or import from a connection. @@ -899,7 +899,7 @@ def dt_manage_data_entities(action: str, @mcp.tool() def dt_manage_workflow(action: str, project_name: str, - workflow_name: str = None, + workflow_name: str | None = None, ctx: Context = None) -> str: """Manage workflows in a Data Transforms project: list, get details, or check existence. diff --git a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/essbase_tools.py b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/essbase_tools.py index 7993c11c..f9728ca4 100644 --- a/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/essbase_tools.py +++ b/src/oracle-data-studio-mcp-server/oracle/data_studio_mcp_server/tools/essbase_tools.py @@ -174,7 +174,7 @@ def essbase_query(app_name: str, db_name: str, mdx: str, # ── 4. essbase_browse_outline ───────────────────────────────────── @mcp.tool() def essbase_browse_outline(app_name: str, db_name: str, - parent: str = None, depth: int = 2, + parent: str | None = None, depth: int = 2, ctx: Context = None) -> str: """Browse the cube outline hierarchy. @@ -258,8 +258,8 @@ def essbase_search_members(app_name: str, db_name: str, # ── 6. essbase_run_calculation ──────────────────────────────────── @mcp.tool() def essbase_run_calculation(app_name: str, db_name: str, - script_name: str = None, - calc_script: str = None, + script_name: str | None = None, + calc_script: str | None = None, ctx: Context = None) -> str: """Run a calculation on an Essbase database — fire-and-forget with automatic wait. @@ -312,8 +312,8 @@ def essbase_run_calculation(app_name: str, db_name: str, # ── 7. essbase_load_data ────────────────────────────────────────── @mcp.tool() def essbase_load_data(app_name: str, db_name: str, - rule_file: str = None, - data_file: str = None, + rule_file: str | None = None, + data_file: str | None = None, ctx: Context = None) -> str: """Load data into an Essbase database. @@ -390,11 +390,11 @@ def essbase_deploy_workbook(app_name: str, file_path: str, # ── 9. essbase_manage_variables ─────────────────────────────────── @mcp.tool() def essbase_manage_variables(action: str, scope: str, - app_name: str = None, - db_name: str = None, - variable_name: str = None, - value: str = None, - confirm: str = None, + app_name: str | None = None, + db_name: str | None = None, + variable_name: str | None = None, + value: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Essbase substitution variables at any scope. @@ -515,8 +515,8 @@ def essbase_get_script(app_name: str, db_name: str, # ── 11. essbase_manage_security ─────────────────────────────────── @mcp.tool() - def essbase_manage_security(username: str = None, - group_name: str = None, + def essbase_manage_security(username: str | None = None, + group_name: str | None = None, ctx: Context = None) -> str: """Get the complete security profile for a user or group. @@ -620,8 +620,8 @@ def essbase_server_health(ctx: Context) -> str: # ── 13. essbase_export_data ─────────────────────────────────────── @mcp.tool() def essbase_export_data(app_name: str, db_name: str, - mdx: str = None, - report_name: str = None, + mdx: str | None = None, + report_name: str | None = None, max_rows: int = 1000, ctx: Context = None) -> str: """Export data from an Essbase database. @@ -659,10 +659,10 @@ def essbase_export_data(app_name: str, db_name: str, # ── 14. essbase_manage_application ───────────────────────────────── @mcp.tool() def essbase_manage_application(action: str, app_name: str, - db_name: str = None, + db_name: str | None = None, db_type: str = 'BSO', - new_name: str = None, - confirm: str = None, + new_name: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Create, delete, copy, rename, start, or stop an Essbase application. @@ -736,9 +736,9 @@ def essbase_manage_application(action: str, app_name: str, def essbase_manage_script(action: str, app_name: str, db_name: str, script_name: str, - content: str = None, - new_name: str = None, - confirm: str = None, + content: str | None = None, + new_name: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage calc scripts: list, create, update, delete, or validate. @@ -827,10 +827,10 @@ def essbase_manage_script(action: str, app_name: str, # ── 16. essbase_manage_files ─────────────────────────────────────── @mcp.tool() - def essbase_manage_files(action: str, path: str = None, - target_path: str = None, - content: str = None, - confirm: str = None, + def essbase_manage_files(action: str, path: str | None = None, + target_path: str | None = None, + content: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage files in the Essbase file catalog: list, upload, download, copy, move, delete, create_folder. @@ -918,14 +918,14 @@ def essbase_manage_files(action: str, path: str = None, # ── 17. essbase_manage_connections ────────────────────────────────── @mcp.tool() def essbase_manage_connections(action: str, - connection_name: str = None, - host: str = None, - port: int = None, - service_name: str = None, - user: str = None, - password: str = None, + connection_name: str | None = None, + host: str | None = None, + port: int | None = None, + service_name: str | None = None, + user: str | None = None, + password: str | None = None, db_type: str = 'oracle', - confirm: str = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Essbase global connections: list, get, create, update, test, or delete. @@ -1031,7 +1031,7 @@ def essbase_manage_connections(action: str, @mcp.tool() def essbase_manage_locks(app_name: str, db_name: str, action: str = 'list', - object_name: str = None, + object_name: str | None = None, ctx: Context = None) -> str: """View and manage locks on an Essbase database. @@ -1094,12 +1094,12 @@ def essbase_manage_locks(app_name: str, db_name: str, @mcp.tool() def essbase_manage_filters(action: str, app_name: str, db_name: str, - filter_name: str = None, - filter_rows: str = None, - user_or_group: str = None, + filter_name: str | None = None, + filter_rows: str | None = None, + user_or_group: str | None = None, access: str = 'read', - new_name: str = None, - confirm: str = None, + new_name: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage security filters for row-level access control on an Essbase database. @@ -1262,9 +1262,9 @@ def essbase_manage_filters(action: str, app_name: str, # ── 20. essbase_manage_jobs ──────────────────────────────────────── @mcp.tool() def essbase_manage_jobs(action: str = 'list', - job_id: int = None, + job_id: int | None = None, limit: int = 50, - confirm: str = None, + confirm: str | None = None, ctx: Context = None) -> str: """Monitor and manage Essbase jobs: list recent jobs, check status, or rerun a failed job. @@ -1323,15 +1323,15 @@ def essbase_manage_jobs(action: str = 'list', def essbase_edit_outline(app_name: str, db_name: str, action: str, member_name: str, - parent_name: str = None, - new_name: str = None, - formula: str = None, - consolidation: str = None, - data_storage: str = None, + parent_name: str | None = None, + new_name: str | None = None, + formula: str | None = None, + consolidation: str | None = None, + data_storage: str | None = None, alias_table: str = 'Default', - alias_value: str = None, - uda_value: str = None, - confirm: str = None, + alias_value: str | None = None, + uda_value: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Edit the cube outline: add, remove, move, or rename members and set properties. @@ -1451,11 +1451,11 @@ def essbase_edit_outline(app_name: str, db_name: str, # ── essbase_manage_datasources ──────────────────────────────────── @mcp.tool() def essbase_manage_datasources(action: str, - datasource_name: str = None, - connection: str = None, - query: str = None, - columns: str = None, - confirm: str = None, + datasource_name: str | None = None, + connection: str | None = None, + query: str | None = None, + columns: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage global datasources for data loads and drill-through. @@ -1534,12 +1534,12 @@ def essbase_manage_datasources(action: str, def essbase_manage_drill_through(action: str, app_name: str, db_name: str, - report_name: str = None, - connection: str = None, - sql_query: str = None, - columns: str = None, - drillable_regions: str = None, - confirm: str = None, + report_name: str | None = None, + connection: str | None = None, + sql_query: str | None = None, + columns: str | None = None, + drillable_regions: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage drill-through reports that link cube cells to detail data. @@ -1638,10 +1638,10 @@ def essbase_manage_drill_through(action: str, @mcp.tool() def essbase_manage_database(action: str, app_name: str, db_name: str, - new_name: str = None, - target_app: str = None, - target_db: str = None, - confirm: str = None, + new_name: str | None = None, + target_app: str | None = None, + target_db: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage databases within an Essbase application: delete, copy, or rename. @@ -1708,11 +1708,11 @@ def essbase_manage_database(action: str, app_name: str, # ── 25. essbase_manage_users ─────────────────────────────────────── @mcp.tool() def essbase_manage_users(action: str, - user_id: str = None, - password: str = None, - role: str = None, - app_name: str = None, - confirm: str = None, + user_id: str | None = None, + password: str | None = None, + role: str | None = None, + app_name: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Essbase users: list, get details, create, update, delete, provision, deprovision roles, or list roles. @@ -1828,9 +1828,9 @@ def essbase_manage_users(action: str, # ── 26. essbase_manage_groups ────────────────────────────────────── @mcp.tool() def essbase_manage_groups(action: str, - group_id: str = None, - user_ids: str = None, - confirm: str = None, + group_id: str | None = None, + user_ids: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Essbase groups: list, get details, create, delete, add/remove users, get users, add/remove subgroups. @@ -1955,8 +1955,8 @@ def essbase_manage_groups(action: str, # ── 27. essbase_manage_sessions ──────────────────────────────────── @mcp.tool() def essbase_manage_sessions(action: str = 'list', - session_id: str = None, - confirm: str = None, + session_id: str | None = None, + confirm: str | None = None, ctx: Context = None) -> str: """Manage Essbase sessions: list active sessions, get current session, kill a session, or kill all sessions. @@ -2010,7 +2010,7 @@ def essbase_manage_sessions(action: str = 'list', @mcp.tool() def essbase_manage_db_settings(app_name: str, db_name: str, category: str = 'all', - settings_json: str = None, + settings_json: str | None = None, ctx: Context = None) -> str: """Get or update detailed database settings and tuning information for performance analysis. @@ -2160,7 +2160,7 @@ def essbase_get_logs(app_name: str, @mcp.tool() def essbase_outline_metadata(app_name: str, db_name: str, category: str = 'all', - dimension_name: str = None, + dimension_name: str | None = None, ctx: Context = None) -> str: """Get detailed outline metadata: dimensions, generations, levels, smart lists, and outline settings.