diff --git a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/__init__.py b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/__init__.py index 68e38fa..35feb52 100644 --- a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/__init__.py +++ b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-compute-mcp-server" -__version__ = "1.0.2" +__version__ = "1.1.0" diff --git a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/models.py b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/models.py index e59ddcf..8d0614e 100644 --- a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/models.py +++ b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/models.py @@ -781,3 +781,104 @@ def map_response(resp: oci.response.Response) -> Response | None: # endregion + +# region VnicAttachment + + +class VnicAttachment(BaseModel): + """ + Pydantic model mirroring the fields of oci.core.models.VnicAttachment. + """ + + availability_domain: Optional[str] = Field( + None, + description="The availability domain of the instance. Example: `Uocm:PHX-AD-1`", + ) + compartment_id: Optional[str] = Field( + None, + description=( + "The OCID of the compartment the VNIC attachment is in, which is the " + "same compartment the instance is in." + ), + ) + display_name: Optional[str] = Field( + None, + description=( + "A user-friendly name. Does not have to be unique, and it's changeable. " + "Avoid entering confidential information." + ), + ) + id: Optional[str] = Field(None, description="The OCID of the VNIC attachment.") + instance_id: Optional[str] = Field(None, description="The OCID of the instance.") + lifecycle_state: Optional[ + Literal["ATTACHING", "ATTACHED", "DETACHING", "DETACHED", "UNKNOWN_ENUM_VALUE"] + ] = Field(None, description="The current state of the VNIC attachment.") + nic_index: Optional[int] = Field( + None, + description=( + "Which physical network interface card (NIC) the VNIC uses. Certain bare " + "metal instance shapes have two active physical NICs (0 and 1). If you add " + "a secondary VNIC to one of these instances, you can specify which NIC " + "the VNIC will use. For more information, see Virtual Network Interface " + "Cards (VNICs)." + ), + ) + subnet_id: Optional[str] = Field( + None, description="The OCID of the subnet to create the VNIC in." + ) + vlan_id: Optional[str] = Field( + None, + description=( + "The OCID of the VLAN to create the VNIC in. Creating the VNIC in a VLAN " + "(instead of a subnet) is possible only if you are an Oracle Cloud VMware " + "Solution customer. See Vlan. An error is returned if the instance already " + "has a VNIC attached to it from this VLAN." + ), + ) + time_created: Optional[datetime] = Field( + None, + description=( + "The date and time the VNIC attachment was created, in the format defined " + "by RFC3339. Example: `2016-08-25T21:10:29.600Z`" + ), + ) + vlan_tag: Optional[int] = Field( + None, + description=( + "The Oracle-assigned VLAN tag of the attached VNIC. Available after the " + "attachment process is complete. However, if the VNIC belongs to a VLAN " + "as part of the Oracle Cloud VMware Solution, the `vlanTag` value is " + "instead the value of the `vlanTag` attribute for the VLAN. See Vlan. " + "Example: `0`" + ), + ) + vnic_id: Optional[str] = Field( + None, + description=( + "The OCID of the VNIC. Available after the attachment process is " + "complete." + ), + ) + + +def map_vnic_attachment(va: oci.core.models.VnicAttachment) -> VnicAttachment: + """ + Convert an oci.core.models.VnicAttachment to oracle.oci_compute_mcp_server.models.VnicAttachment. + """ + return VnicAttachment( + availability_domain=getattr(va, "availability_domain", None), + compartment_id=getattr(va, "compartment_id", None), + display_name=getattr(va, "display_name", None), + id=getattr(va, "id", None), + instance_id=getattr(va, "instance_id", None), + lifecycle_state=getattr(va, "lifecycle_state", None), + nic_index=getattr(va, "nic_index", None), + subnet_id=getattr(va, "subnet_id", None), + vlan_id=getattr(va, "vlan_id", None), + time_created=getattr(va, "time_created", None), + vlan_tag=getattr(va, "vlan_tag", None), + vnic_id=getattr(va, "vnic_id", None), + ) + + +# endregion diff --git a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py index 29f6aba..5826e41 100644 --- a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py +++ b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/server.py @@ -20,9 +20,11 @@ Image, Instance, Response, + VnicAttachment, map_image, map_instance, map_response, + map_vnic_attachment, ) from pydantic import Field @@ -88,7 +90,7 @@ def list_instances( "limit": limit, } - if lifecycle_state is not None: + if lifecycle_state: kwargs["lifecycle_state"] = lifecycle_state response = client.list_instances(**kwargs) @@ -272,6 +274,11 @@ def list_images( operating_system: Optional[str] = Field( None, description="The operating system to filter with" ), + limit: Optional[int] = Field( + None, + description="The maximum amount of resources to return. If None, there is no limit.", + ge=1, + ), ) -> list[Image]: images: list[Image] = [] @@ -282,8 +289,14 @@ def list_images( has_next_page = True next_page: str = None - while has_next_page: - response = client.list_images(compartment_id=compartment_id, page=next_page) + while has_next_page and (limit is None or len(images) < limit): + kwargs = { + "compartment_id": compartment_id, + "page": next_page, + "limit": limit, + } + + response = client.list_images(**kwargs) has_next_page = response.has_next_page next_page = response.next_page if hasattr(response, "next_page") else None @@ -345,6 +358,78 @@ def instance_action( raise e +@mcp.tool( + description="List vnic attachments in a given compartment and/or on a given instance. " +) +def list_vnic_attachments( + compartment_id: str = Field( + ..., + description="The OCID of the compartment. " + "If an instance_id is passed in, but no compartment_id is passed in," + "then the compartment OCID of the instance may be used as a default.", + ), + instance_id: Optional[str] = Field(None, description="The OCID of the instance"), + limit: Optional[int] = Field( + None, + description="The maximum amount of resources to return. If None, there is no limit.", + ge=1, + ), +) -> list[VnicAttachment]: + vnic_attachments: list[VnicAttachment] = [] + + try: + client = get_compute_client() + + response: oci.response.Response = None + has_next_page = True + next_page: str = None + + while has_next_page and (limit is None or len(vnic_attachments) < limit): + kwargs = { + "compartment_id": compartment_id, + "page": next_page, + "limit": limit, + } + + if instance_id: + kwargs["instance_id"] = instance_id + + response = client.list_vnic_attachments(**kwargs) + has_next_page = response.has_next_page + next_page = response.next_page if hasattr(response, "next_page") else None + + data: list[oci.core.models.VnicAttachment] = response.data + + for d in data: + vnic_attachments.append(map_vnic_attachment(d)) + + logger.info(f"Found {len(vnic_attachments)} Vnic Attachments") + return vnic_attachments + + except Exception as e: + logger.error(f"Error in list_vnic_attachments tool: {str(e)}") + raise e + + +@mcp.tool(description="Get Vnic Attachment with a given OCID") +def get_vnic_attachment( + vnic_attachment_id: str = Field(..., description="The OCID of the vnic attachment") +) -> VnicAttachment: + try: + client = get_compute_client() + + response: oci.response.Response = client.get_vnic_attachment( + vnic_attachment_id=vnic_attachment_id + ) + data: oci.core.models.VnicAttachment = response.data + logger.info("Found Vnic Attachment") + return map_vnic_attachment(data) + + except Exception as e: + logger.error(f"Error in get_vnic_attachment tool: {str(e)}") + raise e + + def main() -> None: mcp.run() diff --git a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/tests/test_compute_tools.py b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/tests/test_compute_tools.py index 8a0248d..44a3c2e 100644 --- a/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/tests/test_compute_tools.py +++ b/src/oci-compute-mcp-server/oracle/oci_compute_mcp_server/tests/test_compute_tools.py @@ -227,3 +227,55 @@ async def test_instance_action(self, mock_get_client): assert result["id"] == "instance1" assert result["lifecycle_state"] == "STOPPING" + + @pytest.mark.asyncio + @patch("oracle.oci_compute_mcp_server.server.get_compute_client") + async def test_list_vnic_attachments(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_list_response = create_autospec(oci.response.Response) + mock_list_response.data = [ + oci.core.models.VnicAttachment( + id="vnicattachment1", + display_name="VNIC attachment 1", + lifecycle_state="ATTACHED", + ) + ] + mock_list_response.has_next_page = False + mock_list_response.next_page = None + mock_client.list_vnic_attachments.return_value = mock_list_response + + async with Client(mcp) as client: + call_tool_result = await client.call_tool( + "list_vnic_attachments", + { + "compartment_id": "test_compartment", + }, + ) + result = call_tool_result.structured_content["result"] + + assert len(result) == 1 + assert result[0]["id"] == "vnicattachment1" + + @pytest.mark.asyncio + @patch("oracle.oci_compute_mcp_server.server.get_compute_client") + async def test_get_vnic_attachment(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_get_response = create_autospec(oci.response.Response) + mock_get_response.data = oci.core.models.VnicAttachment( + id="vnicattachment1", + display_name="VNIC attachment 1", + lifecycle_state="ATTACHED", + ) + mock_client.get_vnic_attachment.return_value = mock_get_response + + async with Client(mcp) as client: + call_tool_result = await client.call_tool( + "get_vnic_attachment", {"vnic_attachment_id": "vnicattachment1"} + ) + result = call_tool_result.structured_content + + assert result["id"] == "vnicattachment1" diff --git a/src/oci-compute-mcp-server/pyproject.toml b/src/oci-compute-mcp-server/pyproject.toml index 66463ef..0b5aafd 100644 --- a/src/oci-compute-mcp-server/pyproject.toml +++ b/src/oci-compute-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-compute-mcp-server" -version = "1.0.2" +version = "1.1.0" description = "OCI Compute Service MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-compute-mcp-server/uv.lock b/src/oci-compute-mcp-server/uv.lock index 4bc8620..04ad9fa 100644 --- a/src/oci-compute-mcp-server/uv.lock +++ b/src/oci-compute-mcp-server/uv.lock @@ -582,11 +582,12 @@ wheels = [ [[package]] name = "oracle-oci-compute-mcp-server" -version = "1.0.1" +version = "1.1.0" source = { editable = "." } dependencies = [ { name = "fastmcp" }, { name = "oci" }, + { name = "pydantic" }, ] [package.dev-dependencies] @@ -599,6 +600,7 @@ dev = [ requires-dist = [ { name = "fastmcp", specifier = "==2.12.2" }, { name = "oci", specifier = "==2.160.0" }, + { name = "pydantic", specifier = "==2.12.3" }, ] [package.metadata.requires-dev] @@ -654,7 +656,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.11.9" +version = "2.12.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -662,9 +664,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/5d/09a551ba512d7ca404d785072700d3f6727a02f6f3c24ecfd081c7cf0aa8/pydantic-2.11.9.tar.gz", hash = "sha256:6b8ffda597a14812a7975c90b82a8a2e777d9257aba3453f973acd3c032a18e2", size = 788495, upload-time = "2025-09-13T11:26:39.325Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/1e/4f0a3233767010308f2fd6bd0814597e3f63f1dc98304a9112b8759df4ff/pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74", size = 819383, upload-time = "2025-10-17T15:04:21.222Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/d3/108f2006987c58e76691d5ae5d200dd3e0f532cb4e5fa3560751c3a1feba/pydantic-2.11.9-py3-none-any.whl", hash = "sha256:c42dd626f5cfc1c6950ce6205ea58c93efa406da65f479dcb4029d5934857da2", size = 444855, upload-time = "2025-09-13T11:26:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/83661fa77dcefa195ad5f8cd9af3d1a7450fd57cc883ad04d65446ac2029/pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf", size = 462431, upload-time = "2025-10-17T15:04:19.346Z" }, ] [package.optional-dependencies] @@ -674,30 +676,51 @@ email = [ [[package]] name = "pydantic-core" -version = "2.33.2" +version = "2.41.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, - { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, - { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, - { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, - { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, - { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, - { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, - { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, - { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, - { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, - { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, - { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, - { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, - { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557, upload-time = "2025-10-14T10:23:47.909Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/d0/c20adabd181a029a970738dfe23710b52a31f1258f591874fcdec7359845/pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746", size = 2105688, upload-time = "2025-10-14T10:20:54.448Z" }, + { url = "https://files.pythonhosted.org/packages/00/b6/0ce5c03cec5ae94cca220dfecddc453c077d71363b98a4bbdb3c0b22c783/pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced", size = 1910807, upload-time = "2025-10-14T10:20:56.115Z" }, + { url = "https://files.pythonhosted.org/packages/68/3e/800d3d02c8beb0b5c069c870cbb83799d085debf43499c897bb4b4aaff0d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a", size = 1956669, upload-time = "2025-10-14T10:20:57.874Z" }, + { url = "https://files.pythonhosted.org/packages/60/a4/24271cc71a17f64589be49ab8bd0751f6a0a03046c690df60989f2f95c2c/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02", size = 2051629, upload-time = "2025-10-14T10:21:00.006Z" }, + { url = "https://files.pythonhosted.org/packages/68/de/45af3ca2f175d91b96bfb62e1f2d2f1f9f3b14a734afe0bfeff079f78181/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1", size = 2224049, upload-time = "2025-10-14T10:21:01.801Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/ae4e1ff84672bf869d0a77af24fd78387850e9497753c432875066b5d622/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2", size = 2342409, upload-time = "2025-10-14T10:21:03.556Z" }, + { url = "https://files.pythonhosted.org/packages/18/62/273dd70b0026a085c7b74b000394e1ef95719ea579c76ea2f0cc8893736d/pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84", size = 2069635, upload-time = "2025-10-14T10:21:05.385Z" }, + { url = "https://files.pythonhosted.org/packages/30/03/cf485fff699b4cdaea469bc481719d3e49f023241b4abb656f8d422189fc/pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d", size = 2194284, upload-time = "2025-10-14T10:21:07.122Z" }, + { url = "https://files.pythonhosted.org/packages/f9/7e/c8e713db32405dfd97211f2fc0a15d6bf8adb7640f3d18544c1f39526619/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d", size = 2137566, upload-time = "2025-10-14T10:21:08.981Z" }, + { url = "https://files.pythonhosted.org/packages/04/f7/db71fd4cdccc8b75990f79ccafbbd66757e19f6d5ee724a6252414483fb4/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2", size = 2316809, upload-time = "2025-10-14T10:21:10.805Z" }, + { url = "https://files.pythonhosted.org/packages/76/63/a54973ddb945f1bca56742b48b144d85c9fc22f819ddeb9f861c249d5464/pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab", size = 2311119, upload-time = "2025-10-14T10:21:12.583Z" }, + { url = "https://files.pythonhosted.org/packages/f8/03/5d12891e93c19218af74843a27e32b94922195ded2386f7b55382f904d2f/pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c", size = 1981398, upload-time = "2025-10-14T10:21:14.584Z" }, + { url = "https://files.pythonhosted.org/packages/be/d8/fd0de71f39db91135b7a26996160de71c073d8635edfce8b3c3681be0d6d/pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4", size = 2030735, upload-time = "2025-10-14T10:21:16.432Z" }, + { url = "https://files.pythonhosted.org/packages/72/86/c99921c1cf6650023c08bfab6fe2d7057a5142628ef7ccfa9921f2dda1d5/pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564", size = 1973209, upload-time = "2025-10-14T10:21:18.213Z" }, + { url = "https://files.pythonhosted.org/packages/36/0d/b5706cacb70a8414396efdda3d72ae0542e050b591119e458e2490baf035/pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4", size = 1877324, upload-time = "2025-10-14T10:21:20.363Z" }, + { url = "https://files.pythonhosted.org/packages/de/2d/cba1fa02cfdea72dfb3a9babb067c83b9dff0bbcb198368e000a6b756ea7/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2", size = 1884515, upload-time = "2025-10-14T10:21:22.339Z" }, + { url = "https://files.pythonhosted.org/packages/07/ea/3df927c4384ed9b503c9cc2d076cf983b4f2adb0c754578dfb1245c51e46/pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf", size = 2042819, upload-time = "2025-10-14T10:21:26.683Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ee/df8e871f07074250270a3b1b82aad4cd0026b588acd5d7d3eb2fcb1471a3/pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2", size = 1995866, upload-time = "2025-10-14T10:21:28.951Z" }, + { url = "https://files.pythonhosted.org/packages/fc/de/b20f4ab954d6d399499c33ec4fafc46d9551e11dc1858fb7f5dca0748ceb/pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89", size = 1970034, upload-time = "2025-10-14T10:21:30.869Z" }, + { url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1", size = 2102022, upload-time = "2025-10-14T10:21:32.809Z" }, + { url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac", size = 1905495, upload-time = "2025-10-14T10:21:34.812Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554", size = 1956131, upload-time = "2025-10-14T10:21:36.924Z" }, + { url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e", size = 2052236, upload-time = "2025-10-14T10:21:38.927Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616", size = 2223573, upload-time = "2025-10-14T10:21:41.574Z" }, + { url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af", size = 2342467, upload-time = "2025-10-14T10:21:44.018Z" }, + { url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12", size = 2063754, upload-time = "2025-10-14T10:21:46.466Z" }, + { url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d", size = 2196754, upload-time = "2025-10-14T10:21:48.486Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad", size = 2137115, upload-time = "2025-10-14T10:21:50.63Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a", size = 2317400, upload-time = "2025-10-14T10:21:52.959Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025", size = 2312070, upload-time = "2025-10-14T10:21:55.419Z" }, + { url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e", size = 1982277, upload-time = "2025-10-14T10:21:57.474Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894", size = 2024608, upload-time = "2025-10-14T10:21:59.557Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d", size = 1967614, upload-time = "2025-10-14T10:22:01.847Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da", size = 1876904, upload-time = "2025-10-14T10:22:04.062Z" }, + { url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e", size = 1882538, upload-time = "2025-10-14T10:22:06.39Z" }, + { url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa", size = 2041183, upload-time = "2025-10-14T10:22:08.812Z" }, + { url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d", size = 1993542, upload-time = "2025-10-14T10:22:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" }, ] [[package]] @@ -1026,14 +1049,14 @@ wheels = [ [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] diff --git a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/__init__.py b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/__init__.py index 568ef14..d5d14ec 100644 --- a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/__init__.py +++ b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-networking-mcp-server" -__version__ = "1.0.1" +__version__ = "1.1.0" diff --git a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/models.py b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/models.py index 2eeddb7..298fc8a 100644 --- a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/models.py +++ b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/models.py @@ -787,6 +787,8 @@ def _map_response_data(data: Any) -> Any: return map_security_list(data) if isinstance(data, oci.core.models.NetworkSecurityGroup): return map_network_security_group(data) + if isinstance(data, oci.core.models.Vnic): + return map_vnic(data) except Exception: # Ignore import/type detection issues and fall through to generic handling pass @@ -826,3 +828,164 @@ def map_response(resp: oci.response.Response) -> Response | None: # endregion + +# region Vnic + + +class Vnic(BaseModel): + """ + Pydantic model mirroring the fields of oci.core.models.Vnic. + """ + + availability_domain: Optional[str] = Field( + None, description="The VNIC's availability domain. Example: `Uocm:PHX-AD-1`" + ) + compartment_id: Optional[str] = Field( + None, description="The OCID of the compartment containing the VNIC." + ) + defined_tags: Optional[Dict[str, Dict[str, Any]]] = Field( + None, + description=( + "Defined tags for this resource. Each key is predefined and scoped to a " + 'namespace. Example: `{"Operations": {"CostCenter": "42"}}`', + ), + ) + display_name: Optional[str] = Field( + None, + description="A user-friendly name. Does not have to be unique, and it's changeable. " + "Avoid entering confidential information.", + ) + security_attributes: Optional[Dict[str, Dict[str, Any]]] = Field( + None, + description="Security attributes for Zero Trust Packet Routing (ZPR), labeled by namespace. " + 'Example: `{"Oracle-DataSecurity-ZPR": {"MaxEgressCount": {"value":"42","mode":"audit"}}}`', + ) + freeform_tags: Optional[Dict[str, str]] = Field( + None, + description=( + "Free-form tags for this resource. Each tag is a simple key-value pair with no " + 'predefined name, type, or namespace. Example: `{"Department": "Finance"}`', + ), + ) + hostname_label: Optional[str] = Field( + None, + description=( + "The hostname for the VNIC's primary private IP. Used for DNS. The value is the " + "hostname portion of the primary private IP's fully qualified domain name (FQDN) " + "(for example, `bminstance1` in FQDN `bminstance1.subnet123.vcn1.oraclevcn.com`). " + "Must be unique across all VNICs in the subnet and comply with RFC 952 and RFC 1123. " + "Example: `bminstance1`", + ), + ) + id: Optional[str] = Field(None, description="The OCID of the VNIC.") + is_primary: Optional[bool] = Field( + None, + description="Whether the VNIC is the primary VNIC " + "(the VNIC that is automatically created and attached during instance launch).", + ) + lifecycle_state: Optional[ + Literal[ + "PROVISIONING", + "AVAILABLE", + "TERMINATING", + "TERMINATED", + "UNKNOWN_ENUM_VALUE", + ] + ] = Field(None, description="The current state of the VNIC.") + mac_address: Optional[str] = Field( + None, + description=( + "The MAC address of the VNIC. If the VNIC belongs to a VLAN as part of the Oracle " + "Cloud VMware Solution, the MAC address is learned. If the VNIC belongs to a subnet, " + "the MAC address is a static, Oracle-provided value. Example: `00:00:00:00:00:01`", + ), + ) + nsg_ids: Optional[List[str]] = Field( + None, + description=( + "A list of the OCIDs of the network security groups that the VNIC belongs to. " + "If the VNIC belongs to a VLAN as part of the Oracle Cloud VMware Solution " + "(instead of belonging to a subnet), the value of the `nsgIds` attribute is ignored. " + "Instead, the VNIC belongs to the NSGs that are associated with the VLAN itself. " + "See Vlan.", + ), + ) + vlan_id: Optional[str] = Field( + None, + description="If the VNIC belongs to a VLAN as part of the Oracle Cloud VMware Solution " + "(instead of belonging to a subnet), the `vlanId` is the OCID of the VLAN the VNIC is in. " + "See Vlan. If the VNIC is instead in a subnet, `subnetId` has a value.", + ) + private_ip: Optional[str] = Field( + None, + description="The private IP address of the primary `privateIp` object on the VNIC. " + "The address is within the CIDR of the VNIC's subnet. Example: `10.0.3.3`", + ) + public_ip: Optional[str] = Field( + None, description="The public IP address of the VNIC, if one is assigned." + ) + skip_source_dest_check: Optional[bool] = Field( + None, + description=( + "Whether the source/destination check is disabled on the VNIC. Defaults to `false`, " + "which means the check is performed. For information about why you would skip the " + "source/destination check, see Using a Private IP as a Route Target. " + "If the VNIC belongs to a VLAN as part of the Oracle Cloud VMware Solution " + "(instead of belonging to a subnet), the `skipSourceDestCheck` attribute is `true`. " + "This is because the source/destination check is always disabled for VNICs in a VLAN. " + "Example: `true`", + ), + ) + subnet_id: Optional[str] = Field( + None, description="The OCID of the subnet the VNIC is in." + ) + time_created: Optional[datetime] = Field( + None, + description=( + "The date and time the VNIC was created, in the format defined by RFC3339. " + "Example: `2016-08-25T21:10:29.600Z`", + ), + ) + ipv6_addresses: Optional[List[str]] = Field( + None, + description="List of IPv6 addresses assigned to the VNIC. Example: `2001:DB8::`", + ) + route_table_id: Optional[str] = Field( + None, + description="The OCID of the route table the IP address or VNIC will use. " + "For more information, see Per-resource Routing.", + ) + + +def map_vnic(vnic: oci.core.models.Vnic) -> Vnic | None: + """ + Convert an oci.core.models.Vnic to oracle.oci_networking_mcp_server.models.Vnic. + """ + if vnic is None: + return None + + return Vnic( + availability_domain=getattr(vnic, "availability_domain", None), + compartment_id=getattr(vnic, "compartment_id", None), + defined_tags=getattr(vnic, "defined_tags", None), + display_name=getattr(vnic, "display_name", None), + security_attributes=getattr(vnic, "security_attributes", None), + freeform_tags=getattr(vnic, "freeform_tags", None), + hostname_label=getattr(vnic, "hostname_label", None), + id=getattr(vnic, "id", None), + is_primary=getattr(vnic, "is_primary", None), + lifecycle_state=getattr(vnic, "lifecycle_state", None), + mac_address=getattr(vnic, "mac_address", None), + nsg_ids=getattr(vnic, "nsg_ids", None), + vlan_id=getattr(vnic, "vlan_id", None), + private_ip=getattr(vnic, "private_ip", None), + public_ip=getattr(vnic, "public_ip", None), + skip_source_dest_check=getattr(vnic, "skip_source_dest_check", None), + subnet_id=getattr(vnic, "subnet_id", None), + time_created=getattr(vnic, "time_created", None), + ipv6_addresses=getattr(vnic, "ipv6_addresses", None), + route_table_id=getattr(vnic, "route_table_id", None), + ) + + +# endregion 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 dd91c96..5507af3 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 @@ -16,12 +16,15 @@ SecurityList, Subnet, Vcn, + Vnic, map_network_security_group, map_response, map_security_list, map_subnet, map_vcn, + map_vnic, ) +from pydantic import Field from . import __project__, __version__ @@ -312,6 +315,21 @@ def get_network_security_group( raise +@mcp.tool(description="Get Vnic with a given OCID") +def get_vnic(vnic_id: str = Field(..., description="The OCID of the vnic")) -> Vnic: + try: + client = get_networking_client() + + response: oci.response.Response = client.get_vnic(vnic_id=vnic_id) + data: oci.core.models.Vnic = response.data + logger.info("Found Vnic") + return map_vnic(data) + + except Exception as e: + logger.error(f"Error in get_vnic tool: {str(e)}") + raise e + + def main(): mcp.run() diff --git a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_networking_tools.py b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_networking_tools.py index 507caf0..5fb2c05 100644 --- a/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_networking_tools.py +++ b/src/oci-networking-mcp-server/oracle/oci_networking_mcp_server/tests/test_networking_tools.py @@ -307,3 +307,21 @@ async def test_get_network_security_group(self, mock_get_client): result = call_tool_result.structured_content assert result["id"] == "nsg1" + + @pytest.mark.asyncio + @patch("oracle.oci_networking_mcp_server.server.get_networking_client") + async def get_get_vnic(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_get_response = create_autospec(oci.response.Response) + mock_get_response.data = oci.core.models.Vnic( + id="vnic1", display_name="VNIC 1", lifecycle_state="AVAILABLE" + ) + mock_client.get_vnic.return_value = mock_get_response + + async with Client(mcp) as client: + call_tool_result = await client.call_tool("get_vnic", {"vnic_id": "vnic1"}) + result = call_tool_result.structured_content + + assert result["id"] == "vnic1" diff --git a/src/oci-networking-mcp-server/pyproject.toml b/src/oci-networking-mcp-server/pyproject.toml index 58e6169..cec74c9 100644 --- a/src/oci-networking-mcp-server/pyproject.toml +++ b/src/oci-networking-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-networking-mcp-server" -version = "1.0.1" +version = "1.1.0" description = "OCI Networking Service MCP server" readme = "README.md" requires-python = ">=3.13"