Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/oci-faaas-mcp-server/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -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.
41 changes: 41 additions & 0 deletions src/oci-faaas-mcp-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# OCI Fusion Applications (FAaaS) MCP Server

## Overview

This server provides tools for interacting with Oracle Cloud Infrastructure (OCI) Fusion Applications (FAaaS) via the OCI Python SDK `oci.fusion_apps.FusionApplicationsClient`.

## Running the server

```sh
uv run oracle.oci-faaas-mcp-server
```

## Tools

| Tool Name | Description |
| --- | --- |
| list_fusion_environment_families | List Fusion Environment Families in a compartment |
| list_fusion_environments | List Fusion Environments in a compartment (optionally by family) |
| get_fusion_environment | Get details of a Fusion Environment by OCID |
| get_fusion_environment_status | Get status of a Fusion Environment by OCID |

Notes:
- All list tools handle pagination.
- Responses are converted to plain dictionaries using best-effort conversion of OCI SDK models.

⚠️ NOTE: 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.

## Third-Party APIs

Developers choosing to distribute a binary implementation of this project are responsible for obtaining and providing all required licenses and copyright notices for the third-party code used in order to ensure compliance with their respective open source licenses.

## Disclaimer

Users are responsible for their local environment and credential safety. Different language model selections may yield different results and performance.

## License

Copyright (c) 2025 Oracle and/or its affiliates.

Released under the Universal Permissive License v1.0 as shown at
https://oss.oracle.com/licenses/upl/
5 changes: 5 additions & 0 deletions src/oci-faaas-mcp-server/oracle/__init__.py
Original file line number Diff line number Diff line change
@@ -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.
"""
Original file line number Diff line number Diff line change
@@ -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-faaas-mcp-server"
__version__ = "1.0.0"
175 changes: 175 additions & 0 deletions src/oci-faaas-mcp-server/oracle/oci_faaas_mcp_server/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
"""
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

import oci
from fastmcp import FastMCP

from . import __project__, __version__

logger = Logger(__name__, level="INFO")

mcp = FastMCP(name=__project__)


def get_faaas_client():
"""Initialize and return an OCI Fusion Applications client using security token auth."""
logger.info("entering get_faaas_client")
# Load config from default OCI config file (~/.oci/config) or as per env
config = oci.config.from_file(
profile_name=os.getenv("OCI_CONFIG_PROFILE", oci.config.DEFAULT_PROFILE)
)

# Append project/version user-agent like other servers
user_agent_name = __project__.split("oracle.", 1)[1].split("-server", 1)[0]
config["additional_user_agent"] = f"{user_agent_name}/{__version__}"

# Use security token signer (matches pattern used in other OCI MCP servers)
private_key = oci.signer.load_private_key_from_file(config["key_file"])
token_file = config["security_token_file"]
token = None
with open(token_file, "r") as f:
token = f.read()
signer = oci.auth.signers.SecurityTokenSigner(token, private_key)

# Return Fusion Applications client
return oci.fusion_apps.FusionApplicationsClient(config, signer=signer)


def _to_dict(obj):
"""Best-effort conversion of OCI SDK model objects to plain dicts."""
try:
return oci.util.to_dict(obj)
except Exception:
try:
return obj.__dict__
except Exception:
return obj


def _append_items_from_response_data(accum: list[dict], data_obj):
"""
Normalize list responses across SDK collections/lists:
- If response.data has 'items', iterate it
- If it's already a list, iterate directly
- Else treat it as a single object
"""
try:
items = getattr(data_obj, "items", None)
if items is not None:
for it in items:
accum.append(_to_dict(it))
return

if isinstance(data_obj, list):
for it in data_obj:
accum.append(_to_dict(it))
return

# Fallback: single object
accum.append(_to_dict(data_obj))
except Exception as e:
logger.error(f"Error converting response data to dicts: {e}")
raise


@mcp.tool(
description="Returns a list of Fusion Environment Families in the specified compartment."
)
def list_fusion_environment_families(
compartment_id: Annotated[str, "The ID of the compartment in which to list resources."],
display_name: Annotated[str, "Filter to match entire display name."] = None,
lifecycle_state: Annotated[
str,
"Filter by lifecycle state. Allowed: CREATING, UPDATING, ACTIVE, DELETING, DELETED, FAILED",
] = None,
) -> list[dict]:
client = get_faaas_client()

families: list[dict] = []
next_page: str | None = None
has_next_page = True

while has_next_page:
response: oci.response.Response = client.list_fusion_environment_families(
compartment_id=compartment_id,
display_name=display_name,
lifecycle_state=lifecycle_state,
page=next_page,
)
_append_items_from_response_data(families, response.data)
has_next_page = getattr(response, "has_next_page", False)
next_page = getattr(response, "next_page", None)

logger.info(f"Found {len(families)} Fusion Environment Families")
return families


@mcp.tool(
description="Returns a list of Fusion Environments in the specified compartment (optionally filtered by family)."
)
def list_fusion_environments(
compartment_id: Annotated[str, "The ID of the compartment in which to list resources."],
fusion_environment_family_id: Annotated[str, "Optional Fusion Environment Family OCID"] = None,
display_name: Annotated[str, "Filter to match entire display name."] = None,
lifecycle_state: Annotated[
str,
"Filter by lifecycle state. Allowed: CREATING, UPDATING, ACTIVE, INACTIVE, DELETING, DELETED, FAILED",
] = None,
) -> list[dict]:
client = get_faaas_client()

environments: list[dict] = []
next_page: str | None = None
has_next_page = True

while has_next_page:
response: oci.response.Response = client.list_fusion_environments(
compartment_id=compartment_id,
fusion_environment_family_id=fusion_environment_family_id,
display_name=display_name,
lifecycle_state=lifecycle_state,
page=next_page,
)
_append_items_from_response_data(environments, response.data)
has_next_page = getattr(response, "has_next_page", False)
next_page = getattr(response, "next_page", None)

logger.info(f"Found {len(environments)} Fusion Environments")
return environments


@mcp.tool(description="Gets a Fusion Environment by OCID.")
def get_fusion_environment(
fusion_environment_id: Annotated[str, "Unique FusionEnvironment identifier (OCID)"],
) -> dict:
client = get_faaas_client()
response: oci.response.Response = client.get_fusion_environment(
fusion_environment_id
)
return _to_dict(response.data)


@mcp.tool(description="Gets the status of a Fusion Environment by OCID.")
def get_fusion_environment_status(
fusion_environment_id: Annotated[str, "Unique FusionEnvironment identifier (OCID)"],
) -> dict:
client = get_faaas_client()
response: oci.response.Response = client.get_fusion_environment_status(
fusion_environment_id
)
return _to_dict(response.data)


def main():
mcp.run()


if __name__ == "__main__":
main()
51 changes: 51 additions & 0 deletions src/oci-faaas-mcp-server/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[project]
name = "oracle.oci-faaas-mcp-server"
version = "1.0.0"
description = "OCI Fusion Applications (FAaaS) MCP server"
readme = "README.md"
requires-python = ">=3.13"
license = "UPL-1.0"
license-files = ["LICENSE.txt"]
authors = [
{name = "Oracle MCP", email = "[email protected]"},
]
dependencies = [
"fastmcp==2.12.2",
"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-faaas-mcp-server" = "oracle.oci_faaas_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",
"pytest-cov>=7.0.0",
]

[tool.coverage.run]
omit = [
"**/__init__.py",
"**/tests/*",
"dist/*",
".venv/*",
]

[tool.coverage.report]
precision = 2
fail_under = 75
Loading
Loading