From e7b050a7a0e688e150ad12f012dda825b05e0ccf Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Wed, 15 Oct 2025 15:02:16 -0700 Subject: [PATCH 01/10] Integrate Limits MCP server Signed-off-by: Mayur Khandave --- src/oci-limits-mcp-server/LICENSE.txt | 35 ++ src/oci-limits-mcp-server/README.md | 46 +++ src/oci-limits-mcp-server/oracle/__init__.py | 5 + .../oracle/oci_limits_mcp_server/__init__.py | 8 + .../oracle/oci_limits_mcp_server/server.py | 320 ++++++++++++++++++ .../tests/test_limit_tool.py | 205 +++++++++++ .../oracle/oci_limits_mcp_server/utils.py | 125 +++++++ src/oci-limits-mcp-server/pyproject.toml | 26 ++ 8 files changed, 770 insertions(+) create mode 100644 src/oci-limits-mcp-server/LICENSE.txt create mode 100644 src/oci-limits-mcp-server/README.md create mode 100644 src/oci-limits-mcp-server/oracle/__init__.py create mode 100644 src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py create mode 100644 src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py create mode 100644 src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py create mode 100644 src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py create mode 100644 src/oci-limits-mcp-server/pyproject.toml diff --git a/src/oci-limits-mcp-server/LICENSE.txt b/src/oci-limits-mcp-server/LICENSE.txt new file mode 100644 index 0000000..8dc7c07 --- /dev/null +++ b/src/oci-limits-mcp-server/LICENSE.txt @@ -0,0 +1,35 @@ +Copyright (c) 2025 Oracle and/or its affiliates. + +The Universal Permissive License (UPL), Version 1.0 + +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or data +(collectively the "Software"), free of charge and under any and all copyright +rights in the Software, and any and all patent rights owned or freely +licensable by each licensor hereunder covering either (i) the unmodified +Software as contributed to or provided by such licensor, or (ii) the Larger +Works (as defined below), to deal in both + +(a) the Software, and +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +one is included with the Software (each a "Larger Work" to which the Software +is contributed by such licensors), + +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. + +This license is subject to the following condition: +The above copyright notice and either this complete permission notice or at +a minimum a reference to the UPL must be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/oci-limits-mcp-server/README.md b/src/oci-limits-mcp-server/README.md new file mode 100644 index 0000000..8e29924 --- /dev/null +++ b/src/oci-limits-mcp-server/README.md @@ -0,0 +1,46 @@ +# OCI Limits MCP Server + +## Overview + +This MCP server exposes Oracle Cloud Infrastructure Limits APIs through tools mirroring public endpoints. + +## Running the Server + +To run the server: +```sh +uv run oracle.oci-limits-mcp-server +``` + +## Tools + +| Tool Name | Description | Parameters | +| --- | --- | --- | +| list_services | Returns supported services | compartment_id, sort_by='name', sort_order='ASC', limit=100, page=None, subscription_id=None | +| list_limit_definitions | Returns limit definitions | compartment_id, service_name=None, name=None, sort_by='name', sort_order='ASC', limit=100, page=None, subscription_id=None | +| list_limit_values | Returns limit values | compartment_id, service_name, scope_type=None, availability_domain=None, name=None, sort_by='name', sort_order='ASC', limit=100, page=None, subscription_id=None, external_location=None | +| get_resource_availability | Returns usage/availability for a specific limit | service_name, limit_name, compartment_id, availability_domain=None, subscription_id=None, external_location=None | + +## Authentication + +Follows the same pattern as other OCI MCP servers: +- Reads OCI config from ~/.oci/config +- Uses security token authentication +- Adds custom user agent header + +## Notes + +- For AD-scoped limits, `availability_domain` is required. +- Tools return dicts aligned with Swagger models. + +## License + +Copyright (c) 2025 Oracle and/or its affiliates. +Released under the Universal Permissive License v1.0. + +## Third-Party APIs + +Developers distributing binary implementations must obtain and provide required third-party licenses and copyright notices. + +## Disclaimer + +Users are responsible for local environment and credential safety. Different language models may yield different results. diff --git a/src/oci-limits-mcp-server/oracle/__init__.py b/src/oci-limits-mcp-server/oracle/__init__.py new file mode 100644 index 0000000..d9dff09 --- /dev/null +++ b/src/oci-limits-mcp-server/oracle/__init__.py @@ -0,0 +1,5 @@ +""" +Copyright (c) 2025, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py new file mode 100644 index 0000000..ee8e50c --- /dev/null +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py @@ -0,0 +1,8 @@ +""" +Copyright (c) 2025, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +__project__ = "oracle.oci-limits-mcp-server" +__version__ = "1.0.0" \ No newline at end of file diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py new file mode 100644 index 0000000..019b226 --- /dev/null +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py @@ -0,0 +1,320 @@ +""" +Copyright (c) 2025, Oracle and/or its affiliates. +Licensed under the Universal Permissive License v1.0 as shown at +https://oss.oracle.com/licenses/upl. +""" + +import os +from logging import Logger +from typing import Annotated, Optional + +import oci +from fastmcp import FastMCP + +from . import __project__, __version__ +from .utils import list_services_with_pagination, list_limit_definitions_with_pagination, list_limit_values_with_pagination + +logger = Logger(__name__, level="INFO") + +mcp = FastMCP(name=__project__) + + +def get_limits_client(): + """ + Build an OCI LimitsClient using Security Token auth (consistent with other servers in this repo). + Honors OCI_CONFIG_PROFILE if set. Adds a product-specific user agent. + """ + config = oci.config.from_file( + profile_name=os.getenv("OCI_CONFIG_PROFILE", oci.config.DEFAULT_PROFILE) + ) + user_agent_name = __project__.split("oracle.", 1)[1].split("-server", 1)[0] + config["additional_user_agent"] = f"{user_agent_name}/{__version__}" + + # Security token signer (same pattern as compute/identity) + private_key = oci.signer.load_private_key_from_file(config["key_file"]) + token_file = config["security_token_file"] + with open(token_file, "r") as f: + token = f.read() + signer = oci.auth.signers.SecurityTokenSigner(token, private_key) + + # Limits client + return oci.limits.LimitsClient(config, signer=signer) + + +def get_identity_client(): + """ + Build an OCI IdentityClient using Security Token auth (consistent with other servers in this repo). + Honors OCI_CONFIG_PROFILE if set. Adds a product-specific user agent. + """ + config = oci.config.from_file( + profile_name=os.getenv("OCI_CONFIG_PROFILE", oci.config.DEFAULT_PROFILE) + ) + user_agent_name = __project__.split("oracle.", 1)[1].split("-server", 1)[0] + config["additional_user_agent"] = f"{user_agent_name}/{__version__}" + + # Security token signer (same pattern as compute/identity) + private_key = oci.signer.load_private_key_from_file(config["key_file"]) + token_file = config["security_token_file"] + with open(token_file, "r") as f: + token = f.read() + signer = oci.auth.signers.SecurityTokenSigner(token, private_key) + + # Identity client + return oci.identity.IdentityClient(config, signer=signer) + + +# ---------------------------- +# Mappers to stable dict shapes +# ---------------------------- +def map_service_summary(svc: "oci.limits.models.ServiceSummary") -> dict: + return { + "name": getattr(svc, "name", None), + "description": getattr(svc, "description", None), + "supported_subscriptions": getattr(svc, "supported_subscriptions", None), + } + + +def map_limit_definition_summary( + defn: "oci.limits.models.LimitDefinitionSummary", +) -> dict: + return { + "name": getattr(defn, "name", None), + "serviceName": getattr(defn, "service_name", None), + "description": getattr(defn, "description", None), + "scopeType": getattr(defn, "scope_type", None), + "areQuotasSupported": getattr(defn, "are_quotas_supported", None), + "isResourceAvailabilitySupported": getattr( + defn, "is_resource_availability_supported", None + ), + "isDeprecated": getattr(defn, "is_deprecated", None), + "isEligibleForLimitIncrease": getattr( + defn, "is_eligible_for_limit_increase", None + ), + "isDynamic": getattr(defn, "is_dynamic", None), + "externalLocationSupportedSubscriptions": getattr( + defn, "external_location_supported_subscriptions", None + ), + "supportedSubscriptions": getattr(defn, "supported_subscriptions", None), + "supportedQuotaFamilies": getattr(defn, "supported_quota_families", None), + } + + +def map_limit_value_summary(val: "oci.limits.models.LimitValueSummary") -> dict: + return { + "name": getattr(val, "name", None), + "scopeType": getattr(val, "scope_type", None), + "availabilityDomain": getattr(val, "availability_domain", None), + "value": getattr(val, "value", None), + } + + +def map_resource_availability(ra: "oci.limits.models.ResourceAvailability") -> dict: + return { + "used": getattr(ra, "used", None), + "available": getattr(ra, "available", None), + "fractionalUsage": getattr(ra, "fractional_usage", None), + "fractionalAvailability": getattr(ra, "fractional_availability", None), + "effectiveQuotaValue": getattr(ra, "effective_quota_value", None), + } + + +def list_availability_domains(compartment_id: str) -> list[dict]: + client = get_identity_client() + response = client.list_availability_domains(compartment_id=compartment_id) + data = response.data + return [{"name": ad.name, "id": ad.id} for ad in data] + + +# ---------------------------- +# Tools +# ---------------------------- + + +@mcp.tool( + description="List availability domains for a given compartment needed for limits" +) +def provide_availability_domains_for_limits( + compartment_id: Annotated[str, "OCID of the compartment"], +) -> list[dict]: + return list_availability_domains(compartment_id) + + +@mcp.tool( + description="Returns the list of supported services that have resource limits exposed" +) +def list_services( + compartment_id: Annotated[str, "OCID of the root compartment (tenancy)"], + sort_by: Annotated[str, "Sort field: name or description"] = "name", + sort_order: Annotated[str, "Sort order: ASC or DESC"] = "ASC", + limit: Annotated[Optional[int], "Max items per page (1-1000)"] = 100, + page: Annotated[Optional[str], "Pagination token from a previous call"] = None, + subscription_id: Annotated[Optional[str], "Subscription OCID filter"] = None, +) -> list[dict]: + """ + Maps to GET /20190729/services + """ + try: + client = get_limits_client() + services = list_services_with_pagination( + client, + compartment_id=compartment_id, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + page=page, + subscription_id=subscription_id, + ) + service_summary = [map_service_summary(svc) for svc in services] + return service_summary + except Exception as e: + logger.error(f"Error in list_services: {e}") + raise + + +@mcp.tool(description="Get the list of resource limit definitions for a service") +def list_limit_definitions( + compartment_id: Annotated[str, "OCID of the root compartment (tenancy)"], + service_name: Annotated[Optional[str], "Target service name filter"] = None, + name: Annotated[Optional[str], "Specific resource limit name filter"] = None, + sort_by: Annotated[str, "Sort field: name or description"] = "name", + sort_order: Annotated[str, "Sort order: ASC or DESC"] = "ASC", + limit: Annotated[Optional[int], "Max items per page (1-1000)"] = 100, + page: Annotated[Optional[str], "Pagination token from a previous call"] = None, + subscription_id: Annotated[Optional[str], "Subscription OCID filter"] = None, +) -> list[dict]: + """ + Maps to GET /20190729/limitDefinitions + """ + try: + client = get_limits_client() + items = list_limit_definitions_with_pagination( + client, + compartment_id=compartment_id, + service_name=service_name, + name=name, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + page=page, + subscription_id=subscription_id, + ) + return [map_limit_definition_summary(d) for d in items] + except Exception as e: + logger.error(f"Error in list_limit_definitions: {e}") + raise + + +@mcp.tool( + description="Get the full list of resource limit values for the given service" +) +def get_limit_value( + compartment_id: Annotated[str, "OCID of the root compartment (tenancy)"], + service_name: Annotated[str, "Target service name"], + name: Annotated[str, "Specific resource limit name filter"], + scope_type: Annotated[ + str, "Filter by scope type: GLOBAL, REGION, or AD" + ], + availability_domain: Annotated[ + Optional[str], "If scope_type is AD, filter by availability domain" + ] = None, + sort_by: Annotated[str, "Sort field: name"] = "name", + sort_order: Annotated[str, "Sort order: ASC or DESC"] = "ASC", + limit: Annotated[Optional[int], "Max items per page (1-1000)"] = 100, + page: Annotated[Optional[str], "Pagination token from a previous call"] = None, + subscription_id: Annotated[Optional[str], "Subscription OCID filter"] = None, +) -> list[dict]: + """ + Maps to GET /20190729/limitValues + """ + try: + client = get_limits_client() + items = list_limit_values_with_pagination( + client, + compartment_id=compartment_id, + service_name=service_name, + scope_type=scope_type, + availability_domain=availability_domain, + name=name, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + page=page, + subscription_id=subscription_id, + ) + return [map_limit_value_summary(d) for d in items] + except Exception as e: + logger.error(f"Error in get_limit_value: {e}") + raise + + +@mcp.tool( + description="Get the availability and usage within a compartment for a given resource limit" +) +def get_resource_availability( + service_name: Annotated[str, "Service name of the target limit"], + limit_name: Annotated[str, "Limit name"], + compartment_id: Annotated[str, "OCID of the compartment to evaluate"], + availability_domain: Annotated[ + Optional[str], + "Required if the limit scopeType is AD; omit otherwise. Example: 'US-ASHBURN-AD-1'", + ] = None, + subscription_id: Annotated[Optional[str], "Subscription OCID filter"] = None, +) -> list[dict]: + """ + Maps to GET /20190729/services/{serviceName}/limits/{limitName}/resourceAvailability + """ + try: + client = get_limits_client() + limits = list_limit_definitions_with_pagination( + client, + compartment_id=compartment_id, + service_name=service_name, + name=limit_name, + ) + if len(limits) == 0: + return {"message": f"Limit '{limit_name}' not found for service '{service_name}'"} + + limit_definition = limits[0] + if not limit_definition.is_resource_availability_supported: + return { + "message": f"Resource availability not supported for limit '{limit_name}'. Consider calling get_limit_value to get the limit value." + } + + if limit_definition.scope_type == 'AD': + availability_domains = list_availability_domains(compartment_id) + resource_availability = [] + for ad in availability_domains: + response: oci.response.Response = client.get_resource_availability( + service_name=service_name, + limit_name=limit_name, + compartment_id=compartment_id, + availability_domain=ad['name'], + subscription_id=subscription_id, + ) + data: oci.limits.models.ResourceAvailability = response.data + resource_availability.append({ + "availabilityDomain": ad['name'], + "resourceAvailability": map_resource_availability(data) + }) + return resource_availability + else: + response: oci.response.Response = client.get_resource_availability( + service_name=service_name, + limit_name=limit_name, + compartment_id=compartment_id, + availability_domain=availability_domain, + subscription_id=subscription_id, + ) + data: oci.limits.models.ResourceAvailability = response.data + return [map_resource_availability(data)] + except Exception as e: + logger.error(f"Error in get_resource_availability: {e}") + raise + + +def main() -> None: + mcp.run() + + +if __name__ == "__main__": + main() diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py new file mode 100644 index 0000000..a65a945 --- /dev/null +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py @@ -0,0 +1,205 @@ +""" +Copyright (c) 2025, 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, create_autospec, patch + +import oci +import pytest +from fastmcp import Client +from oracle.oci_limits_mcp_server.server import mcp + + +class TestLimitsTools: + @pytest.mark.asyncio + @patch("oracle.oci_limits_mcp_server.server.get_identity_client") + async def test_provide_availability_domains_for_limits(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_response = create_autospec(oci.response.Response) + mock_response.data = [ + oci.identity.models.AvailabilityDomain(name="AD1", id="US-ASHBURN-AD-1") + ] + mock_response.has_next_page = False + mock_response.next_page = None + mock_client.list_availability_domains.return_value = mock_response + + async with Client(mcp) as client: + result = ( + await client.call_tool( + "provide_availability_domains_for_limits", + { + "compartment_id": "ocid1.compartment.oc1..xxxx", + }, + ) + ).structured_content["result"] + + assert len(result) == 1 + assert result[0]["name"] == "AD1" + + @pytest.mark.asyncio + @patch("oracle.oci_limits_mcp_server.server.get_limits_client") + async def test_list_services(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_response = create_autospec(oci.response.Response) + mock_response.data = [ + oci.limits.models.ServiceSummary(name="service1", description="Service 1") + ] + mock_response.has_next_page = False + mock_response.next_page = None + mock_client.list_services.return_value = mock_response + + async with Client(mcp) as client: + result = ( + await client.call_tool( + "list_services", + { + "compartment_id": "ocid1.compartment.oc1..xxxx", + }, + ) + ).structured_content["result"] + + assert len(result) == 1 + assert result[0]["name"] == "service1" + + @pytest.mark.asyncio + @patch("oracle.oci_limits_mcp_server.server.get_limits_client") + async def test_list_limit_definitions(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_response = create_autospec(oci.response.Response) + mock_response.data = [ + oci.limits.models.LimitDefinitionSummary( + name="limit1", service_name="service1", description="Limit 1" + ) + ] + mock_response.has_next_page = False + mock_response.next_page = None + mock_client.list_limit_definitions.return_value = mock_response + + async with Client(mcp) as client: + result = ( + await client.call_tool( + "list_limit_definitions", + { + "compartment_id": "ocid1.compartment.oc1..xxxx", + }, + ) + ).structured_content["result"] + + assert len(result) == 1 + assert result[0]["name"] == "limit1" + + @pytest.mark.asyncio + @patch("oracle.oci_limits_mcp_server.server.get_limits_client") + async def test_get_limit_value(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_response = create_autospec(oci.response.Response) + mock_response.data = [ + oci.limits.models.LimitValueSummary( + name="limit_value1", scope_type="GLOBAL", value=10 + ) + ] + mock_response.has_next_page = False + mock_response.next_page = None + mock_client.list_limit_values.return_value = mock_response + + async with Client(mcp) as client: + result = ( + await client.call_tool( + "get_limit_value", + { + "compartment_id": "ocid1.compartment.oc1..xxxx", + "service_name": "service1", + "name": "limit_value1", + "scope_type": "GLOBAL" + }, + ) + ).structured_content["result"] + + assert len(result) == 1 + assert result[0]["name"] == "limit_value1" + + @pytest.mark.asyncio + @patch("oracle.oci_limits_mcp_server.server.get_limits_client") + @patch("oracle.oci_limits_mcp_server.server.list_availability_domains") + async def test_get_resource_availability_ad_scope(self, mock_list_ad, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_limit_definition = create_autospec(oci.limits.models.LimitDefinitionSummary) + mock_limit_definition.is_resource_availability_supported = True + mock_limit_definition.scope_type = 'AD' + + mock_response = create_autospec(oci.response.Response) + mock_response.data = [mock_limit_definition] + mock_response.has_next_page = False + mock_response.next_page = None + mock_client.list_limit_definitions.return_value = mock_response + + mock_list_ad.return_value = [{"name": "AD1"}, {"name": "AD2"}] + + mock_response_ad1 = create_autospec(oci.response.Response) + mock_response_ad1.data = oci.limits.models.ResourceAvailability(used=10, available=100) + mock_response_ad2 = create_autospec(oci.response.Response) + mock_response_ad2.data = oci.limits.models.ResourceAvailability(used=20, available=200) + + mock_client.get_resource_availability.side_effect = [mock_response_ad1, mock_response_ad2] + + async with Client(mcp) as client: + result = await client.call_tool( + "get_resource_availability", + { + "service_name": "service1", + "limit_name": "limit1", + "compartment_id": "ocid1.compartment.oc1..xxxx", + }, + ) + + assert len(result.structured_content['result']) == 2 + assert result.structured_content['result'][0]["availabilityDomain"] == "AD1" + assert result.structured_content['result'][0]["resourceAvailability"]["used"] == 10 + assert result.structured_content['result'][1]["availabilityDomain"] == "AD2" + assert result.structured_content['result'][1]["resourceAvailability"]["used"] == 20 + + @pytest.mark.asyncio + @patch("oracle.oci_limits_mcp_server.server.get_limits_client") + async def test_get_resource_availability_non_ad_scope(self, mock_get_client): + mock_client = MagicMock() + mock_get_client.return_value = mock_client + + mock_limit_definition = create_autospec(oci.limits.models.LimitDefinitionSummary) + mock_limit_definition.is_resource_availability_supported = True + mock_limit_definition.scope_type = 'REGION' + + mock_response = create_autospec(oci.response.Response) + mock_response.data = [mock_limit_definition] + mock_response.has_next_page = False + mock_response.next_page = None + mock_client.list_limit_definitions.return_value = mock_response + + mock_response = create_autospec(oci.response.Response) + mock_response.data = oci.limits.models.ResourceAvailability(used=10, available=100) + mock_client.get_resource_availability.return_value = mock_response + + async with Client(mcp) as client: + result = await client.call_tool( + "get_resource_availability", + { + "service_name": "service1", + "limit_name": "limit1", + "compartment_id": "ocid1.compartment.oc1..xxxx", + }, + ) + + assert len(result.structured_content['result']) == 1 + assert result.structured_content['result'][0]["used"] == 10 + assert result.structured_content['result'][0]["available"] == 100 diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py new file mode 100644 index 0000000..af5264e --- /dev/null +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py @@ -0,0 +1,125 @@ +from typing import Optional, List, Dict, Any +import oci +from logging import Logger + +logger = Logger(__name__, level="INFO") + +def list_services_with_pagination( + client: oci.limits.LimitsClient, + compartment_id: str, + sort_by: str = "name", + sort_order: str = "ASC", + limit: Optional[int] = 100, + page: Optional[str] = None, + subscription_id: Optional[str] = None, +) -> List[oci.limits.models.ServiceSummary]: + try: + items: List[oci.limits.models.ServiceSummary] = [] + next_page = page + has_next_page = True + + while has_next_page: + response = client.list_services( + compartment_id=compartment_id, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + page=next_page, + subscription_id=subscription_id, + ) + data = response.data or [] + items.extend(data) + has_next_page = response.has_next_page + next_page = response.next_page + + if page is not None: + break + + return items + except Exception as e: + logger.error(f"Error in list_services: {e}") + raise + +def list_limit_definitions_with_pagination( + client: oci.limits.LimitsClient, + compartment_id: str, + service_name: Optional[str] = None, + name: Optional[str] = None, + sort_by: str = "name", + sort_order: str = "ASC", + limit: Optional[int] = 100, + page: Optional[str] = None, + subscription_id: Optional[str] = None, +) -> List[oci.limits.models.LimitDefinitionSummary]: + try: + items: List[oci.limits.models.LimitDefinitionSummary] = [] + next_page = page + has_next_page = True + + while has_next_page: + response = client.list_limit_definitions( + compartment_id=compartment_id, + service_name=service_name, + name=name, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + page=next_page, + subscription_id=subscription_id, + ) + data = response.data or [] + items.extend(data) + has_next_page = response.has_next_page + next_page = response.next_page + + if page is not None: + break + + return items + except Exception as e: + logger.error(f"Error in list_limit_definitions: {e}") + raise + +def list_limit_values_with_pagination( + client: oci.limits.LimitsClient, + compartment_id: str, + service_name: str, + scope_type: str, + availability_domain: Optional[str] = None, + name: Optional[str] = None, + sort_by: str = "name", + sort_order: str = "ASC", + limit: Optional[int] = 100, + page: Optional[str] = None, + subscription_id: Optional[str] = None, +) -> List[oci.limits.models.LimitValueSummary]: + try: + items: List[oci.limits.models.LimitValueSummary] = [] + next_page = page + has_next_page = True + + while has_next_page: + response = client.list_limit_values( + compartment_id=compartment_id, + service_name=service_name, + scope_type=scope_type, + availability_domain=availability_domain, + name=name, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + page=next_page, + subscription_id=subscription_id, + ) + data = response.data or [] + items.extend(data) + has_next_page = response.has_next_page + next_page = response.next_page + + if page is not None: + break + + return items + except Exception as e: + logger.error(f"Error in list_limit_values: {e}") + raise diff --git a/src/oci-limits-mcp-server/pyproject.toml b/src/oci-limits-mcp-server/pyproject.toml new file mode 100644 index 0000000..f87bde9 --- /dev/null +++ b/src/oci-limits-mcp-server/pyproject.toml @@ -0,0 +1,26 @@ +[project] +name = "oracle.oci-limits-mcp-server" +version = "0.1.0" +description = "OCI Limits MCP server" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "fastmcp==2.12.2", + "oci==2.160.0" +] + +[project.scripts] +"oracle.oci-limits-mcp-server" = "oracle.oci_limits_mcp_server.server:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["oracle"] + +[dependency-groups] +dev = [ + "pytest>=8.4.2", + "pytest-asyncio>=1.2.0", +] From 26a91eead7033ee64235c6f4def6920e4f2bfcfd Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Wed, 15 Oct 2025 15:18:38 -0700 Subject: [PATCH 02/10] Fix Lint issues Signed-off-by: Mayur Khandave --- .../oracle/oci_limits_mcp_server/__init__.py | 2 +- .../oracle/oci_limits_mcp_server/server.py | 30 ++++++---- .../tests/test_limit_tool.py | 57 +++++++++++++------ .../oracle/oci_limits_mcp_server/utils.py | 8 ++- src/oci-limits-mcp-server/pyproject.toml | 9 ++- 5 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py index ee8e50c..053c238 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-limits-mcp-server" -__version__ = "1.0.0" \ No newline at end of file +__version__ = "1.0.0" diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py index 019b226..a4d967e 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py @@ -12,7 +12,11 @@ from fastmcp import FastMCP from . import __project__, __version__ -from .utils import list_services_with_pagination, list_limit_definitions_with_pagination, list_limit_values_with_pagination +from .utils import ( + list_limit_definitions_with_pagination, + list_limit_values_with_pagination, + list_services_with_pagination, +) logger = Logger(__name__, level="INFO") @@ -164,7 +168,7 @@ def list_services( page=page, subscription_id=subscription_id, ) - service_summary = [map_service_summary(svc) for svc in services] + service_summary = [map_service_summary(svc) for svc in services] return service_summary except Exception as e: logger.error(f"Error in list_services: {e}") @@ -211,9 +215,7 @@ def get_limit_value( compartment_id: Annotated[str, "OCID of the root compartment (tenancy)"], service_name: Annotated[str, "Target service name"], name: Annotated[str, "Specific resource limit name filter"], - scope_type: Annotated[ - str, "Filter by scope type: GLOBAL, REGION, or AD" - ], + scope_type: Annotated[str, "Filter by scope type: GLOBAL, REGION, or AD"], availability_domain: Annotated[ Optional[str], "If scope_type is AD, filter by availability domain" ] = None, @@ -272,7 +274,9 @@ def get_resource_availability( name=limit_name, ) if len(limits) == 0: - return {"message": f"Limit '{limit_name}' not found for service '{service_name}'"} + return { + "message": f"Limit '{limit_name}' not found for service '{service_name}'" + } limit_definition = limits[0] if not limit_definition.is_resource_availability_supported: @@ -280,7 +284,7 @@ def get_resource_availability( "message": f"Resource availability not supported for limit '{limit_name}'. Consider calling get_limit_value to get the limit value." } - if limit_definition.scope_type == 'AD': + if limit_definition.scope_type == "AD": availability_domains = list_availability_domains(compartment_id) resource_availability = [] for ad in availability_domains: @@ -288,14 +292,16 @@ def get_resource_availability( service_name=service_name, limit_name=limit_name, compartment_id=compartment_id, - availability_domain=ad['name'], + availability_domain=ad["name"], subscription_id=subscription_id, ) data: oci.limits.models.ResourceAvailability = response.data - resource_availability.append({ - "availabilityDomain": ad['name'], - "resourceAvailability": map_resource_availability(data) - }) + resource_availability.append( + { + "availabilityDomain": ad["name"], + "resourceAvailability": map_resource_availability(data), + } + ) return resource_availability else: response: oci.response.Response = client.get_resource_availability( diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py index a65a945..8860722 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/tests/test_limit_tool.py @@ -120,7 +120,7 @@ async def test_get_limit_value(self, mock_get_client): "compartment_id": "ocid1.compartment.oc1..xxxx", "service_name": "service1", "name": "limit_value1", - "scope_type": "GLOBAL" + "scope_type": "GLOBAL", }, ) ).structured_content["result"] @@ -131,13 +131,17 @@ async def test_get_limit_value(self, mock_get_client): @pytest.mark.asyncio @patch("oracle.oci_limits_mcp_server.server.get_limits_client") @patch("oracle.oci_limits_mcp_server.server.list_availability_domains") - async def test_get_resource_availability_ad_scope(self, mock_list_ad, mock_get_client): + async def test_get_resource_availability_ad_scope( + self, mock_list_ad, mock_get_client + ): mock_client = MagicMock() mock_get_client.return_value = mock_client - mock_limit_definition = create_autospec(oci.limits.models.LimitDefinitionSummary) + mock_limit_definition = create_autospec( + oci.limits.models.LimitDefinitionSummary + ) mock_limit_definition.is_resource_availability_supported = True - mock_limit_definition.scope_type = 'AD' + mock_limit_definition.scope_type = "AD" mock_response = create_autospec(oci.response.Response) mock_response.data = [mock_limit_definition] @@ -148,11 +152,18 @@ async def test_get_resource_availability_ad_scope(self, mock_list_ad, mock_get_c mock_list_ad.return_value = [{"name": "AD1"}, {"name": "AD2"}] mock_response_ad1 = create_autospec(oci.response.Response) - mock_response_ad1.data = oci.limits.models.ResourceAvailability(used=10, available=100) + mock_response_ad1.data = oci.limits.models.ResourceAvailability( + used=10, available=100 + ) mock_response_ad2 = create_autospec(oci.response.Response) - mock_response_ad2.data = oci.limits.models.ResourceAvailability(used=20, available=200) + mock_response_ad2.data = oci.limits.models.ResourceAvailability( + used=20, available=200 + ) - mock_client.get_resource_availability.side_effect = [mock_response_ad1, mock_response_ad2] + mock_client.get_resource_availability.side_effect = [ + mock_response_ad1, + mock_response_ad2, + ] async with Client(mcp) as client: result = await client.call_tool( @@ -164,11 +175,17 @@ async def test_get_resource_availability_ad_scope(self, mock_list_ad, mock_get_c }, ) - assert len(result.structured_content['result']) == 2 - assert result.structured_content['result'][0]["availabilityDomain"] == "AD1" - assert result.structured_content['result'][0]["resourceAvailability"]["used"] == 10 - assert result.structured_content['result'][1]["availabilityDomain"] == "AD2" - assert result.structured_content['result'][1]["resourceAvailability"]["used"] == 20 + assert len(result.structured_content["result"]) == 2 + assert result.structured_content["result"][0]["availabilityDomain"] == "AD1" + assert ( + result.structured_content["result"][0]["resourceAvailability"]["used"] + == 10 + ) + assert result.structured_content["result"][1]["availabilityDomain"] == "AD2" + assert ( + result.structured_content["result"][1]["resourceAvailability"]["used"] + == 20 + ) @pytest.mark.asyncio @patch("oracle.oci_limits_mcp_server.server.get_limits_client") @@ -176,9 +193,11 @@ async def test_get_resource_availability_non_ad_scope(self, mock_get_client): mock_client = MagicMock() mock_get_client.return_value = mock_client - mock_limit_definition = create_autospec(oci.limits.models.LimitDefinitionSummary) + mock_limit_definition = create_autospec( + oci.limits.models.LimitDefinitionSummary + ) mock_limit_definition.is_resource_availability_supported = True - mock_limit_definition.scope_type = 'REGION' + mock_limit_definition.scope_type = "REGION" mock_response = create_autospec(oci.response.Response) mock_response.data = [mock_limit_definition] @@ -187,7 +206,9 @@ async def test_get_resource_availability_non_ad_scope(self, mock_get_client): mock_client.list_limit_definitions.return_value = mock_response mock_response = create_autospec(oci.response.Response) - mock_response.data = oci.limits.models.ResourceAvailability(used=10, available=100) + mock_response.data = oci.limits.models.ResourceAvailability( + used=10, available=100 + ) mock_client.get_resource_availability.return_value = mock_response async with Client(mcp) as client: @@ -200,6 +221,6 @@ async def test_get_resource_availability_non_ad_scope(self, mock_get_client): }, ) - assert len(result.structured_content['result']) == 1 - assert result.structured_content['result'][0]["used"] == 10 - assert result.structured_content['result'][0]["available"] == 100 + assert len(result.structured_content["result"]) == 1 + assert result.structured_content["result"][0]["used"] == 10 + assert result.structured_content["result"][0]["available"] == 100 diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py index af5264e..fcc07dc 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py @@ -1,9 +1,11 @@ -from typing import Optional, List, Dict, Any -import oci from logging import Logger +from typing import Any, Dict, List, Optional + +import oci logger = Logger(__name__, level="INFO") + def list_services_with_pagination( client: oci.limits.LimitsClient, compartment_id: str, @@ -40,6 +42,7 @@ def list_services_with_pagination( logger.error(f"Error in list_services: {e}") raise + def list_limit_definitions_with_pagination( client: oci.limits.LimitsClient, compartment_id: str, @@ -80,6 +83,7 @@ def list_limit_definitions_with_pagination( logger.error(f"Error in list_limit_definitions: {e}") raise + def list_limit_values_with_pagination( client: oci.limits.LimitsClient, compartment_id: str, diff --git a/src/oci-limits-mcp-server/pyproject.toml b/src/oci-limits-mcp-server/pyproject.toml index f87bde9..06909ee 100644 --- a/src/oci-limits-mcp-server/pyproject.toml +++ b/src/oci-limits-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-limits-mcp-server" -version = "0.1.0" +version = "1.0.0" description = "OCI Limits MCP server" readme = "README.md" requires-python = ">=3.13" @@ -9,6 +9,13 @@ dependencies = [ "oci==2.160.0" ] +classifiers = [ + "License :: OSI Approved :: Universal Permissive License (UPL)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.13", +] + [project.scripts] "oracle.oci-limits-mcp-server" = "oracle.oci_limits_mcp_server.server:main" From 29cb609cae16399cbe5e81fa71e46ed1b760fe6c Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Wed, 15 Oct 2025 15:36:17 -0700 Subject: [PATCH 03/10] Fix Lint issues Signed-off-by: Mayur Khandave --- src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py index fcc07dc..82a232e 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/utils.py @@ -1,5 +1,5 @@ from logging import Logger -from typing import Any, Dict, List, Optional +from typing import List, Optional import oci From 09c310bedb673197534ac348002f69d4522e3028 Mon Sep 17 00:00:00 2001 From: Richard Gebhardt Date: Wed, 15 Oct 2025 15:55:02 -0700 Subject: [PATCH 04/10] chore: release 1.0.1 (#33) Signed-off-by: Richard Gebhardt --- src/oci-api-mcp-server/oracle/oci_api_mcp_server/__init__.py | 2 +- src/oci-api-mcp-server/pyproject.toml | 2 +- src/oci-api-mcp-server/uv.lock | 2 +- .../oracle/oci_compute_instance_agent_mcp_server/__init__.py | 2 +- src/oci-compute-instance-agent-mcp-server/pyproject.toml | 2 +- src/oci-compute-instance-agent-mcp-server/uv.lock | 2 +- .../oracle/oci_compute_mcp_server/__init__.py | 2 +- src/oci-compute-mcp-server/pyproject.toml | 2 +- src/oci-compute-mcp-server/uv.lock | 2 +- .../oracle/oci_identity_mcp_server/__init__.py | 2 +- src/oci-identity-mcp-server/pyproject.toml | 2 +- src/oci-identity-mcp-server/uv.lock | 2 +- .../oracle/oci_logging_mcp_server/__init__.py | 2 +- src/oci-logging-mcp-server/pyproject.toml | 2 +- src/oci-logging-mcp-server/uv.lock | 2 +- .../oracle/oci_migration_mcp_server/__init__.py | 2 +- src/oci-migration-mcp-server/pyproject.toml | 2 +- src/oci-migration-mcp-server/uv.lock | 2 +- .../oracle/oci_monitoring_mcp_server/__init__.py | 2 +- src/oci-monitoring-mcp-server/pyproject.toml | 2 +- src/oci-monitoring-mcp-server/uv.lock | 2 +- .../oracle/oci_network_load_balancer_mcp_server/__init__.py | 2 +- src/oci-network-load-balancer-mcp-server/pyproject.toml | 2 +- src/oci-network-load-balancer-mcp-server/uv.lock | 2 +- .../oracle/oci_networking_mcp_server/__init__.py | 2 +- src/oci-networking-mcp-server/pyproject.toml | 2 +- src/oci-networking-mcp-server/uv.lock | 2 +- .../oracle/oci_object_storage_mcp_server/__init__.py | 2 +- src/oci-object-storage-mcp-server/pyproject.toml | 2 +- src/oci-object-storage-mcp-server/uv.lock | 2 +- .../oracle/oci_registry_mcp_server/__init__.py | 2 +- src/oci-registry-mcp-server/pyproject.toml | 2 +- src/oci-registry-mcp-server/uv.lock | 2 +- .../oracle/oci_resource_search_mcp_server/__init__.py | 2 +- src/oci-resource-search-mcp-server/pyproject.toml | 2 +- src/oci-resource-search-mcp-server/uv.lock | 2 +- .../oracle/oci_usage_mcp_server/__init__.py | 2 +- src/oci-usage-mcp-server/pyproject.toml | 2 +- src/oci-usage-mcp-server/uv.lock | 2 +- 39 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/oci-api-mcp-server/oracle/oci_api_mcp_server/__init__.py b/src/oci-api-mcp-server/oracle/oci_api_mcp_server/__init__.py index c14e0e2..0a03a74 100644 --- a/src/oci-api-mcp-server/oracle/oci_api_mcp_server/__init__.py +++ b/src/oci-api-mcp-server/oracle/oci_api_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-api-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-api-mcp-server/pyproject.toml b/src/oci-api-mcp-server/pyproject.toml index a0ab65e..cee1fa2 100644 --- a/src/oci-api-mcp-server/pyproject.toml +++ b/src/oci-api-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-api-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI CLI MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-api-mcp-server/uv.lock b/src/oci-api-mcp-server/uv.lock index 94e4fed..05fb54c 100644 --- a/src/oci-api-mcp-server/uv.lock +++ b/src/oci-api-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-api-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-compute-instance-agent-mcp-server/oracle/oci_compute_instance_agent_mcp_server/__init__.py b/src/oci-compute-instance-agent-mcp-server/oracle/oci_compute_instance_agent_mcp_server/__init__.py index a37feb6..adfee27 100644 --- a/src/oci-compute-instance-agent-mcp-server/oracle/oci_compute_instance_agent_mcp_server/__init__.py +++ b/src/oci-compute-instance-agent-mcp-server/oracle/oci_compute_instance_agent_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-compute-instance-agent-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-compute-instance-agent-mcp-server/pyproject.toml b/src/oci-compute-instance-agent-mcp-server/pyproject.toml index c019ece..9329b80 100644 --- a/src/oci-compute-instance-agent-mcp-server/pyproject.toml +++ b/src/oci-compute-instance-agent-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-compute-instance-agent-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Compute Instance Agent MCP Server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-compute-instance-agent-mcp-server/uv.lock b/src/oci-compute-instance-agent-mcp-server/uv.lock index 331e2b2..f65d5b7 100644 --- a/src/oci-compute-instance-agent-mcp-server/uv.lock +++ b/src/oci-compute-instance-agent-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-compute-instance-agent-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, 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 aca223a..d87ac4b 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.0" +__version__ = "1.0.1" diff --git a/src/oci-compute-mcp-server/pyproject.toml b/src/oci-compute-mcp-server/pyproject.toml index 4f98db9..7efb057 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.0" +version = "1.0.1" 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 98ffdda..4bc8620 100644 --- a/src/oci-compute-mcp-server/uv.lock +++ b/src/oci-compute-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-compute-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-identity-mcp-server/oracle/oci_identity_mcp_server/__init__.py b/src/oci-identity-mcp-server/oracle/oci_identity_mcp_server/__init__.py index f6c7d90..ee2c1ad 100644 --- a/src/oci-identity-mcp-server/oracle/oci_identity_mcp_server/__init__.py +++ b/src/oci-identity-mcp-server/oracle/oci_identity_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-identity-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-identity-mcp-server/pyproject.toml b/src/oci-identity-mcp-server/pyproject.toml index 04fc687..308dddd 100644 --- a/src/oci-identity-mcp-server/pyproject.toml +++ b/src/oci-identity-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-identity-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Identity Service MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-identity-mcp-server/uv.lock b/src/oci-identity-mcp-server/uv.lock index fb990a2..5db278b 100644 --- a/src/oci-identity-mcp-server/uv.lock +++ b/src/oci-identity-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-identity-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-logging-mcp-server/oracle/oci_logging_mcp_server/__init__.py b/src/oci-logging-mcp-server/oracle/oci_logging_mcp_server/__init__.py index 6ad18c2..1bf9657 100644 --- a/src/oci-logging-mcp-server/oracle/oci_logging_mcp_server/__init__.py +++ b/src/oci-logging-mcp-server/oracle/oci_logging_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-logging-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-logging-mcp-server/pyproject.toml b/src/oci-logging-mcp-server/pyproject.toml index 64094be..bade405 100644 --- a/src/oci-logging-mcp-server/pyproject.toml +++ b/src/oci-logging-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-logging-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Logging Service MCP server" readme = "README.md" authors = [ diff --git a/src/oci-logging-mcp-server/uv.lock b/src/oci-logging-mcp-server/uv.lock index 76ed954..16dcc88 100644 --- a/src/oci-logging-mcp-server/uv.lock +++ b/src/oci-logging-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-logging-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-migration-mcp-server/oracle/oci_migration_mcp_server/__init__.py b/src/oci-migration-mcp-server/oracle/oci_migration_mcp_server/__init__.py index 7209b8a..5ae0cb7 100644 --- a/src/oci-migration-mcp-server/oracle/oci_migration_mcp_server/__init__.py +++ b/src/oci-migration-mcp-server/oracle/oci_migration_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-migration-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-migration-mcp-server/pyproject.toml b/src/oci-migration-mcp-server/pyproject.toml index b6ac9bb..086e143 100644 --- a/src/oci-migration-mcp-server/pyproject.toml +++ b/src/oci-migration-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-migration-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "Add your description here" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-migration-mcp-server/uv.lock b/src/oci-migration-mcp-server/uv.lock index 35ee64f..04d4e43 100644 --- a/src/oci-migration-mcp-server/uv.lock +++ b/src/oci-migration-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-migration-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-monitoring-mcp-server/oracle/oci_monitoring_mcp_server/__init__.py b/src/oci-monitoring-mcp-server/oracle/oci_monitoring_mcp_server/__init__.py index db7d509..d29f66f 100644 --- a/src/oci-monitoring-mcp-server/oracle/oci_monitoring_mcp_server/__init__.py +++ b/src/oci-monitoring-mcp-server/oracle/oci_monitoring_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-monitoring-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-monitoring-mcp-server/pyproject.toml b/src/oci-monitoring-mcp-server/pyproject.toml index 0f91630..df07cab 100644 --- a/src/oci-monitoring-mcp-server/pyproject.toml +++ b/src/oci-monitoring-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-monitoring-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Monitoring Service MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-monitoring-mcp-server/uv.lock b/src/oci-monitoring-mcp-server/uv.lock index 8c334cf..881bb18 100644 --- a/src/oci-monitoring-mcp-server/uv.lock +++ b/src/oci-monitoring-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-monitoring-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-network-load-balancer-mcp-server/oracle/oci_network_load_balancer_mcp_server/__init__.py b/src/oci-network-load-balancer-mcp-server/oracle/oci_network_load_balancer_mcp_server/__init__.py index cdc7ea9..1d2122d 100644 --- a/src/oci-network-load-balancer-mcp-server/oracle/oci_network_load_balancer_mcp_server/__init__.py +++ b/src/oci-network-load-balancer-mcp-server/oracle/oci_network_load_balancer_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-network-load-balancer-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-network-load-balancer-mcp-server/pyproject.toml b/src/oci-network-load-balancer-mcp-server/pyproject.toml index 77df5ac..2e15016 100644 --- a/src/oci-network-load-balancer-mcp-server/pyproject.toml +++ b/src/oci-network-load-balancer-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-network-load-balancer-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Network Load Balancer MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-network-load-balancer-mcp-server/uv.lock b/src/oci-network-load-balancer-mcp-server/uv.lock index 04da08e..cc7dadb 100644 --- a/src/oci-network-load-balancer-mcp-server/uv.lock +++ b/src/oci-network-load-balancer-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-network-load-balancer-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, 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 1d717f4..568ef14 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.0" +__version__ = "1.0.1" diff --git a/src/oci-networking-mcp-server/pyproject.toml b/src/oci-networking-mcp-server/pyproject.toml index 6229b6b..58e6169 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.0" +version = "1.0.1" description = "OCI Networking Service MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-networking-mcp-server/uv.lock b/src/oci-networking-mcp-server/uv.lock index 1441dae..ae7097b 100644 --- a/src/oci-networking-mcp-server/uv.lock +++ b/src/oci-networking-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-networking-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-object-storage-mcp-server/oracle/oci_object_storage_mcp_server/__init__.py b/src/oci-object-storage-mcp-server/oracle/oci_object_storage_mcp_server/__init__.py index dbec3fd..aaade87 100644 --- a/src/oci-object-storage-mcp-server/oracle/oci_object_storage_mcp_server/__init__.py +++ b/src/oci-object-storage-mcp-server/oracle/oci_object_storage_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-object-storage-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-object-storage-mcp-server/pyproject.toml b/src/oci-object-storage-mcp-server/pyproject.toml index 4da7d01..4cb4ae1 100644 --- a/src/oci-object-storage-mcp-server/pyproject.toml +++ b/src/oci-object-storage-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-object-storage-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Object Storage Service MCP server" readme = "README.md" authors = [ diff --git a/src/oci-object-storage-mcp-server/uv.lock b/src/oci-object-storage-mcp-server/uv.lock index 2ceaec0..f391169 100644 --- a/src/oci-object-storage-mcp-server/uv.lock +++ b/src/oci-object-storage-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-object-storage-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-registry-mcp-server/oracle/oci_registry_mcp_server/__init__.py b/src/oci-registry-mcp-server/oracle/oci_registry_mcp_server/__init__.py index 59e5d4f..5683ea8 100644 --- a/src/oci-registry-mcp-server/oracle/oci_registry_mcp_server/__init__.py +++ b/src/oci-registry-mcp-server/oracle/oci_registry_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-registry-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-registry-mcp-server/pyproject.toml b/src/oci-registry-mcp-server/pyproject.toml index 70e0a0a..7e2cca6 100644 --- a/src/oci-registry-mcp-server/pyproject.toml +++ b/src/oci-registry-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-registry-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Registry Service MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-registry-mcp-server/uv.lock b/src/oci-registry-mcp-server/uv.lock index 61826f0..c1ca203 100644 --- a/src/oci-registry-mcp-server/uv.lock +++ b/src/oci-registry-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-registry-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-resource-search-mcp-server/oracle/oci_resource_search_mcp_server/__init__.py b/src/oci-resource-search-mcp-server/oracle/oci_resource_search_mcp_server/__init__.py index 4c24024..5c14795 100644 --- a/src/oci-resource-search-mcp-server/oracle/oci_resource_search_mcp_server/__init__.py +++ b/src/oci-resource-search-mcp-server/oracle/oci_resource_search_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-resource-search-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-resource-search-mcp-server/pyproject.toml b/src/oci-resource-search-mcp-server/pyproject.toml index a4f5391..019f9a4 100644 --- a/src/oci-resource-search-mcp-server/pyproject.toml +++ b/src/oci-resource-search-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-resource-search-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Resource Search Service MCP server" readme = "README.md" requires-python = ">=3.13" diff --git a/src/oci-resource-search-mcp-server/uv.lock b/src/oci-resource-search-mcp-server/uv.lock index ed871af..9cc7c3d 100644 --- a/src/oci-resource-search-mcp-server/uv.lock +++ b/src/oci-resource-search-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-resource-search-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, diff --git a/src/oci-usage-mcp-server/oracle/oci_usage_mcp_server/__init__.py b/src/oci-usage-mcp-server/oracle/oci_usage_mcp_server/__init__.py index a14ad25..ddfb6c1 100644 --- a/src/oci-usage-mcp-server/oracle/oci_usage_mcp_server/__init__.py +++ b/src/oci-usage-mcp-server/oracle/oci_usage_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-usage-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-usage-mcp-server/pyproject.toml b/src/oci-usage-mcp-server/pyproject.toml index 8936686..0156175 100644 --- a/src/oci-usage-mcp-server/pyproject.toml +++ b/src/oci-usage-mcp-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oracle.oci-usage-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Usage MCP server" readme = "README.md" license = "UPL-1.0" diff --git a/src/oci-usage-mcp-server/uv.lock b/src/oci-usage-mcp-server/uv.lock index 452d714..7f9767c 100644 --- a/src/oci-usage-mcp-server/uv.lock +++ b/src/oci-usage-mcp-server/uv.lock @@ -582,7 +582,7 @@ wheels = [ [[package]] name = "oracle-oci-usage-mcp-server" -version = "0.1.0" +version = "1.0.1" source = { editable = "." } dependencies = [ { name = "fastmcp" }, From 909c145fc87800c0f1519d216d9b1d9ce6435f16 Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Thu, 16 Oct 2025 22:25:33 -0700 Subject: [PATCH 05/10] Fix Lint issues Signed-off-by: Mayur Khandave --- .../oracle/oci_limits_mcp_server/server.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py index a4d967e..68f4d7e 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py @@ -274,15 +274,15 @@ def get_resource_availability( name=limit_name, ) if len(limits) == 0: - return { + return [{ "message": f"Limit '{limit_name}' not found for service '{service_name}'" - } + }] limit_definition = limits[0] if not limit_definition.is_resource_availability_supported: - return { + return [{ "message": f"Resource availability not supported for limit '{limit_name}'. Consider calling get_limit_value to get the limit value." - } + }] if limit_definition.scope_type == "AD": availability_domains = list_availability_domains(compartment_id) From c4f46d8b641685cd2f3524243f017e99937abfa2 Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Thu, 16 Oct 2025 22:36:16 -0700 Subject: [PATCH 06/10] Fix Lint issues Signed-off-by: Mayur Khandave --- .../oracle/oci_limits_mcp_server/server.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py index 68f4d7e..af1ce5d 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py @@ -274,15 +274,19 @@ def get_resource_availability( name=limit_name, ) if len(limits) == 0: - return [{ - "message": f"Limit '{limit_name}' not found for service '{service_name}'" - }] + return [ + { + "message": f"Limit '{limit_name}' not found for service '{service_name}'" + } + ] limit_definition = limits[0] if not limit_definition.is_resource_availability_supported: - return [{ - "message": f"Resource availability not supported for limit '{limit_name}'. Consider calling get_limit_value to get the limit value." - }] + return [ + { + "message": f"Resource availability not supported for limit '{limit_name}'. Consider calling get_limit_value to get the limit value." + } + ] if limit_definition.scope_type == "AD": availability_domains = list_availability_domains(compartment_id) From b160c885cba20ce696daf5b328114b9603f7d455 Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Thu, 16 Oct 2025 22:39:44 -0700 Subject: [PATCH 07/10] Fix Lint issues Signed-off-by: Mayur Khandave --- .../oracle/oci_limits_mcp_server/server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py index af1ce5d..4bb9dcb 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py @@ -284,7 +284,8 @@ def get_resource_availability( if not limit_definition.is_resource_availability_supported: return [ { - "message": f"Resource availability not supported for limit '{limit_name}'. Consider calling get_limit_value to get the limit value." + "message": f"Resource availability not supported for limit '{limit_name}'. " + f"Consider calling get_limit_value to get the limit value." } ] From a900f768e107b28d3022bd4f1a0e28e954f775aa Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Thu, 16 Oct 2025 22:42:43 -0700 Subject: [PATCH 08/10] Fix Lint issues Signed-off-by: Mayur Khandave --- .../oracle/oci_limits_mcp_server/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py index 4bb9dcb..9c61d34 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/server.py @@ -285,7 +285,7 @@ def get_resource_availability( return [ { "message": f"Resource availability not supported for limit '{limit_name}'. " - f"Consider calling get_limit_value to get the limit value." + f"Consider calling get_limit_value to get the limit value." } ] From fedf1e21678c1c7ba3d2f958dce3c13eb077aeff Mon Sep 17 00:00:00 2001 From: Will Shope Date: Fri, 17 Oct 2025 14:54:05 -0700 Subject: [PATCH 09/10] [CHORE] Update readme for PyPi packages (#32) Signed-off-by: will.shope --- README.md | 136 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index bbe7c13..8bb6ebb 100644 --- a/README.md +++ b/README.md @@ -27,40 +27,33 @@ Always see the respective `src//README.md` for detailed setup instructio ## Quick Start -1. **Clone this repository:** - ```sh - git clone https://github.com/oracle/mcp.git - cd mcp - ``` +Follow these instructions to get started as quickly as possible. Once finished, look [here](#local-development) to set up your local development environment if you wish to [contribute](#contributing) changes. -2. **List available MCP servers:** - ```sh - ls src/ - ``` +1. Install `uv` from [here](https://docs.astral.sh/uv/getting-started/installation/) +2. Install python with `uv python install 3.13` +3. If you are using OCI servers, configure your [OCI authentication](#authentication) +4. Add desired servers to your [MCP client configuration](#client-configuration) -3. **Read the appropriate server's README for setup instructions:** - ```sh - cat src//README.md - ``` - - Example: For the Python-based DBTools MCP server: - ``` - cd src/dbtools-mcp-server/ - cat README.md - ``` +Below is an example MCP client configuration for a typical python server -4. **Typical Python Server Setup Example:** - ```sh - python3 -m venv venv - source venv/bin/activate # On Windows: venv\Scripts\activate - pip install -r requirements-dev.txt - ``` - *(For Node.js/Java/other servers, follow respective instructions in that server’s README)* +*(For Node.js/Java/other servers, follow respective instructions in that server’s README)* -5. **Build and Install servers in the current virtual environment** - ```sh - make build - make install - ``` +For macOS/Linux: +``` +{ + "mcpServers": { + "oracle-oci-api-mcp-server": { + "command": "uvx", + "args": [ + "oracle.oci-api-mcp-server@latest" + ], + "env": { + "FASTMCP_LOG_LEVEL": "ERROR" + } + } + } +} +``` ## Authentication @@ -72,8 +65,10 @@ For OCI MCP servers, you'll need to install and authenticate using the OCI CLI. oci session authenticate --region= --tenancy-name= ``` where: - is the region you would like to authenticate in (e.g. `us-phoenix-1`) - is the name of your OCI tenancy +`` is the region you would like to authenticate in (e.g. `us-phoenix-1`) +`` is the name of your OCI tenancy + +Some MCP servers may not work with token-based authentication alone. See more about API key-based authentication [here](https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/clitoken.htm). All actions are performed with the permissions of the configured OCI CLI profile. We advise least-privilege IAM setup, secure credential management, safe network practices, secure logging, and warn against exposing secrets. @@ -82,6 +77,8 @@ Remember to refresh the session once it expires with: oci session authenticate --profile-name --region --auth security_token ``` +`` is the profile that you set up in the steps above. You can view a list of your profiles by running `cat ~/.oci/config` on macOS/Linux if you forget which profile you have set up. + ## Client configuration Each MCP server exposes endpoints that your client can connect to. To enable this connection, just add the relevant server to your MCP client’s configuration file. You can find the list of servers under the `src` folder. @@ -93,25 +90,24 @@ Refer to the sections below for client-specific configuration instructions.
Setup -Before continuing, make sure you have already followed the steps above in the [Getting Started](#getting-started) section. +Before continuing, make sure you have already followed the steps above in the [Quick start](#quick-start) section. 1. If using Visual Studio Code, install the [Cline VS Code Extension](https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev) (or equivalent extension for your preferred IDE). 2. Once installed, click the extension to open it. 3. Click the **MCP Servers** button near the top of the the extension's panel. 4. Select the **Installed** tab. 5. Click **Configure MCP Servers** to open the `cline_mcp_settings.json` file. -6. In the `cline_mcp_settings.json` file, add your desired MCP servers in the `mcpServers` object. Below is an example for for the compute OCI MCP server. Make sure to save the file after editing. +6. In the `cline_mcp_settings.json` file, add your desired MCP servers in the `mcpServers` object. Below is an example for for the generic OCI API MCP server. Make sure to save the file after editing. `` is the profile that you set up during the [authentication](#authentication) steps. -For macOS/Linux +For macOS/Linux: ```json { "mcpServers": { "oracle-oci-api-mcp-server": { "type": "stdio", - "command": "uv", + "command": "uvx", "args": [ - "run", - "oracle.oci-api-mcp-server" + "oracle.oci-api-mcp-server@latest" ], "env": { "OCI_CONFIG_PROFILE": "", @@ -134,7 +130,7 @@ For Windows - **TODO**
Setup -Before continuing, make sure you have already followed the steps above in the [Getting Started](#getting-started) section. +Before continuing, make sure you have already followed the steps above in the [Quick start](#quick-start) section. 1. You can place MCP configurations in two locations, depending on your use case: @@ -145,15 +141,13 @@ Before continuing, make sure you have already followed the steps above in the [G **`.cursor/mcp.json`** For macOS/Linux: - ```json { "mcpServers": { "oracle-oci-api-mcp-server": { "type": "stdio", - "command": "uv", + "command": "uvx", "args": [ - "run", "oracle.oci-api-mcp-server" ], "env": { @@ -165,6 +159,8 @@ For macOS/Linux: } ``` +`` is the profile that you set up during the [authentication](#authentication) steps. + For Windows - **TODO** 2. In your **Cursor Settings**, check your **Installed Servers** under the **MCP** tab to ensure that your `.cursor/mcp.json` was properly configured. @@ -176,7 +172,7 @@ For Windows - **TODO**
Setup -Before continuing, make sure you have already followed the steps above in the [Getting Started](#getting-started) section. +Before continuing, make sure you have already followed the steps above in the [Quick start](#quick-start) section. 1. Download [Ollama](https://ollama.com/download) 2. Start the Ollama server @@ -195,20 +191,18 @@ For Linux: `sudo systemctl start ollama` 8. Create an mcphost configuration file (e.g. `./mcphost.json`) 9. Add your desired server to the `mcpServers` object. Below is an example for for the compute OCI MCP server. Make sure to save the file after editing. -For macOS/Linux - +For macOS/Linux: ```json { "mcpServers": { "oracle-oci-api-mcp-server": { "type": "stdio", - "command": "uv", + "command": "uvx", "args": [ - "run", "oracle.oci-api-mcp-server" ], "env": { - "VIRTUAL_ENV": "/oci-mcp/.venv", + "OCI_CONFIG_PROFILE": "", "FASTMCP_LOG_LEVEL": "ERROR" } } @@ -216,6 +210,8 @@ For macOS/Linux } ``` +`` is the profile that you set up during the [authentication](#authentication) steps. + For Windows - **TODO** 10. Start `mcphost` with `OCI_CONFIG_PROFILE= mcphost -m ollama: --config ` @@ -225,6 +221,50 @@ For Windows - **TODO**
+## Local development + +This section will help you set up your environment to prepare it for local development if you wish to [contribute](#contributing) changes. + +1. Set up python virtual environment and install dev requirements + ```sh + python3 -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + pip install -r requirements-dev.txt + ``` + +2. Locally build and install servers within the virtual environment + ```sh + make build + make install + ``` + +3. Add desired servers to your MCP client configuration, but run them using the locally installed server package instead + +Below is an example MCP client configuration for a typical python server using the local server package + +*(For Node.js/Java/other servers, follow respective instructions in that server’s README)* + +For macOS/Linux: +``` +{ + "mcpServers": { + "oracle-oci-api-mcp-server": { + "command": "uv", + "args": [ + "run" + "oracle.oci-api-mcp-server" + ], + "env": { + "VIRTUAL_ENV": "/mcp/venv", + "FASTMCP_LOG_LEVEL": "ERROR" + } + } + } +} +``` + +where `` is the absolute path to wherever you cloned this repo that will help point to the venv created above (e.g. `/Users/myuser/dev/mcp/venv`) + ## Directory Structure ``` From e2831a5d36cf7f84a05dbf5c4f77fe14321e617a Mon Sep 17 00:00:00 2001 From: Mayur Khandave Date: Wed, 29 Oct 2025 12:40:28 -0700 Subject: [PATCH 10/10] Add Author details --- .../oracle/oci_limits_mcp_server/__init__.py | 2 +- src/oci-limits-mcp-server/pyproject.toml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py index 053c238..fc04a8c 100644 --- a/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py +++ b/src/oci-limits-mcp-server/oracle/oci_limits_mcp_server/__init__.py @@ -5,4 +5,4 @@ """ __project__ = "oracle.oci-limits-mcp-server" -__version__ = "1.0.0" +__version__ = "1.0.1" diff --git a/src/oci-limits-mcp-server/pyproject.toml b/src/oci-limits-mcp-server/pyproject.toml index 06909ee..113ae7d 100644 --- a/src/oci-limits-mcp-server/pyproject.toml +++ b/src/oci-limits-mcp-server/pyproject.toml @@ -1,9 +1,12 @@ [project] name = "oracle.oci-limits-mcp-server" -version = "1.0.0" +version = "1.0.1" description = "OCI Limits MCP server" readme = "README.md" requires-python = ">=3.13" +authors = [ + {name = "Oracle MCP", email = "237432095+oracle-mcp@users.noreply.github.com"}, +] dependencies = [ "fastmcp==2.12.2", "oci==2.160.0"