Skip to content

Commit 4071d67

Browse files
FAaaS MCP server
1 parent 78c1742 commit 4071d67

File tree

9 files changed

+2033
-0
lines changed

9 files changed

+2033
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Copyright (c) 2025 Oracle and/or its affiliates.
2+
3+
The Universal Permissive License (UPL), Version 1.0
4+
5+
Subject to the condition set forth below, permission is hereby granted to any
6+
person obtaining a copy of this software, associated documentation and/or data
7+
(collectively the "Software"), free of charge and under any and all copyright
8+
rights in the Software, and any and all patent rights owned or freely
9+
licensable by each licensor hereunder covering either (i) the unmodified
10+
Software as contributed to or provided by such licensor, or (ii) the Larger
11+
Works (as defined below), to deal in both
12+
13+
(a) the Software, and
14+
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15+
one is included with the Software (each a "Larger Work" to which the Software
16+
is contributed by such licensors),
17+
18+
without restriction, including without limitation the rights to copy, create
19+
derivative works of, display, perform, and distribute the Software and make,
20+
use, sell, offer for sale, import, export, have made, and have sold the
21+
Software and the Larger Work(s), and to sublicense the foregoing rights on
22+
either these or other terms.
23+
24+
This license is subject to the following condition:
25+
The above copyright notice and either this complete permission notice or at
26+
a minimum a reference to the UPL must be included in all copies or
27+
substantial portions of the Software.
28+
29+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35+
SOFTWARE.

src/oci-faaas-mcp-server/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# OCI Fusion Applications (FAaaS) MCP Server
2+
3+
## Overview
4+
5+
This server provides tools for interacting with Oracle Cloud Infrastructure (OCI) Fusion Applications (FAaaS) via the OCI Python SDK `oci.fusion_apps.FusionApplicationsClient`.
6+
7+
## Running the server
8+
9+
```sh
10+
uv run oracle.oci-faaas-mcp-server
11+
```
12+
13+
## Tools
14+
15+
| Tool Name | Description |
16+
| --- | --- |
17+
| list_fusion_environment_families | List Fusion Environment Families in a compartment |
18+
| list_fusion_environments | List Fusion Environments in a compartment (optionally by family) |
19+
| get_fusion_environment | Get details of a Fusion Environment by OCID |
20+
| get_fusion_environment_status | Get status of a Fusion Environment by OCID |
21+
22+
Notes:
23+
- All list tools handle pagination.
24+
- Responses are converted to plain dictionaries using best-effort conversion of OCI SDK models.
25+
26+
⚠️ 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.
27+
28+
## Third-Party APIs
29+
30+
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.
31+
32+
## Disclaimer
33+
34+
Users are responsible for their local environment and credential safety. Different language model selections may yield different results and performance.
35+
36+
## License
37+
38+
Copyright (c) 2025 Oracle and/or its affiliates.
39+
40+
Released under the Universal Permissive License v1.0 as shown at
41+
https://oss.oracle.com/licenses/upl/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
Licensed under the Universal Permissive License v1.0 as shown at
4+
https://oss.oracle.com/licenses/upl.
5+
"""
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
Licensed under the Universal Permissive License v1.0 as shown at
4+
https://oss.oracle.com/licenses/upl.
5+
"""
6+
7+
__project__ = "oracle.oci-faaas-mcp-server"
8+
__version__ = "1.0.0"
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
"""
2+
Copyright (c) 2025, Oracle and/or its affiliates.
3+
Licensed under the Universal Permissive License v1.0 as shown at
4+
https://oss.oracle.com/licenses/upl.
5+
"""
6+
7+
from datetime import datetime
8+
from typing import Any, Dict, List, Optional
9+
10+
from pydantic import BaseModel, Field
11+
12+
13+
def _oci_to_dict(obj):
14+
"""Best-effort conversion of OCI SDK model objects to plain dicts."""
15+
if obj is None:
16+
return None
17+
try:
18+
from oci.util import to_dict as oci_to_dict
19+
20+
return oci_to_dict(obj)
21+
except Exception:
22+
pass
23+
if isinstance(obj, dict):
24+
return obj
25+
if hasattr(obj, "__dict__"):
26+
return {k: v for k, v in obj.__dict__.items() if not k.startswith("_")}
27+
return None
28+
29+
30+
class FusionEnvironmentFamily(BaseModel):
31+
"""Pydantic model representing a Fusion Environment Family."""
32+
33+
id: Optional[str] = Field(None, description="OCID of the Fusion Environment Family")
34+
display_name: Optional[str] = Field(None, description="Display name")
35+
lifecycle_state: Optional[str] = Field(
36+
None,
37+
description="Lifecycle state (e.g., CREATING, UPDATING, ACTIVE, DELETING, DELETED, FAILED)",
38+
)
39+
compartment_id: Optional[str] = Field(
40+
None, description="Compartment OCID containing this family"
41+
)
42+
time_created: Optional[datetime] = Field(
43+
None, description="Creation time (RFC3339)"
44+
)
45+
time_updated: Optional[datetime] = Field(
46+
None, description="Last update time (RFC3339)"
47+
)
48+
freeform_tags: Optional[Dict[str, str]] = Field(None, description="Freeform tags")
49+
defined_tags: Optional[Dict[str, Dict[str, Any]]] = Field(
50+
None, description="Defined tags"
51+
)
52+
53+
54+
class FusionEnvironment(BaseModel):
55+
"""Pydantic model representing a Fusion Environment."""
56+
57+
id: Optional[str] = Field(None, description="OCID of the Fusion Environment")
58+
display_name: Optional[str] = Field(None, description="Display name")
59+
compartment_id: Optional[str] = Field(
60+
None, description="Compartment OCID containing the environment"
61+
)
62+
fusion_environment_family_id: Optional[str] = Field(
63+
None, description="OCID of the parent Fusion Environment Family"
64+
)
65+
fusion_environment_type: Optional[str] = Field(
66+
None, description="Environment type (e.g., PRODUCTION, TEST)"
67+
)
68+
version: Optional[str] = Field(None, description="Fusion Apps version (e.g., 25C)")
69+
public_url: Optional[str] = Field(None, description="Primary public URL")
70+
idcs_domain_url: Optional[str] = Field(None, description="IDCS domain URL")
71+
domain_id: Optional[str] = Field(None, description="IDCS domain OCID")
72+
73+
lifecycle_state: Optional[str] = Field(None, description="Lifecycle state")
74+
lifecycle_details: Optional[str] = Field(
75+
None, description="Additional lifecycle details"
76+
)
77+
is_suspended: Optional[bool] = Field(None, description="Suspended flag")
78+
system_name: Optional[str] = Field(None, description="System name/code")
79+
environment_role: Optional[str] = Field(None, description="Environment role")
80+
81+
maintenance_policy: Optional[Dict[str, Any]] = Field(
82+
None, description="Maintenance policy details"
83+
)
84+
time_upcoming_maintenance: Optional[datetime] = Field(
85+
None, description="Upcoming maintenance window (RFC3339)"
86+
)
87+
applied_patch_bundles: Optional[List[str]] = Field(
88+
None, description="Applied patch bundles"
89+
)
90+
91+
subscription_ids: Optional[List[str]] = Field(
92+
None, description="Associated subscription OCIDs"
93+
)
94+
additional_language_packs: Optional[List[str]] = Field(
95+
None, description="Enabled language packs"
96+
)
97+
98+
kms_key_id: Optional[str] = Field(None, description="KMS key OCID")
99+
kms_key_info: Optional[Dict[str, Any]] = Field(None, description="KMS key info")
100+
101+
dns_prefix: Optional[str] = Field(None, description="DNS prefix")
102+
lockbox_id: Optional[str] = Field(None, description="Lockbox OCID")
103+
is_break_glass_enabled: Optional[bool] = Field(
104+
None, description="Break glass access enabled"
105+
)
106+
107+
refresh: Optional[Any] = Field(None, description="Refresh details")
108+
rules: Optional[List[Any]] = Field(None, description="Rules")
109+
time_created: Optional[datetime] = Field(
110+
None, description="Creation time (RFC3339)"
111+
)
112+
time_updated: Optional[datetime] = Field(
113+
None, description="Last update time (RFC3339)"
114+
)
115+
116+
freeform_tags: Optional[Dict[str, Any]] = Field(None, description="Freeform tags")
117+
defined_tags: Optional[Dict[str, Dict[str, Any]]] = Field(
118+
None, description="Defined tags"
119+
)
120+
121+
122+
class FusionEnvironmentStatus(BaseModel):
123+
"""Pydantic model representing the status of a Fusion Environment."""
124+
125+
fusion_environment_id: Optional[str] = Field(
126+
None, description="OCID of the Fusion Environment"
127+
)
128+
status: Optional[str] = Field(None, description="Status value")
129+
time_updated: Optional[datetime] = Field(
130+
None, description="Last status update time (RFC3339)"
131+
)
132+
time_created: Optional[datetime] = Field(
133+
None, description="Creation time if present (RFC3339)"
134+
)
135+
details: Optional[Dict[str, Any]] = Field(
136+
None, description="Additional status details"
137+
)
138+
139+
140+
def _get(data: Any, key: str) -> Any:
141+
"""Safe getter to support both dicts and SDK objects."""
142+
if isinstance(data, dict):
143+
return data.get(key)
144+
return getattr(data, key, None)
145+
146+
147+
def map_fusion_environment_family(data: Any) -> FusionEnvironmentFamily:
148+
"""Map SDK model or dict to FusionEnvironmentFamily."""
149+
return FusionEnvironmentFamily(
150+
id=_get(data, "id"),
151+
display_name=_get(data, "display_name"),
152+
lifecycle_state=_get(data, "lifecycle_state"),
153+
compartment_id=_get(data, "compartment_id"),
154+
time_created=_get(data, "time_created"),
155+
time_updated=_get(data, "time_updated"),
156+
freeform_tags=_get(data, "freeform_tags"),
157+
defined_tags=_get(data, "defined_tags"),
158+
)
159+
160+
161+
def map_fusion_environment(data: Any) -> FusionEnvironment:
162+
"""Map SDK model or dict to FusionEnvironment."""
163+
return FusionEnvironment(
164+
id=_get(data, "id"),
165+
display_name=_get(data, "display_name"),
166+
compartment_id=_get(data, "compartment_id"),
167+
fusion_environment_family_id=_get(data, "fusion_environment_family_id"),
168+
fusion_environment_type=_get(data, "fusion_environment_type"),
169+
version=_get(data, "version"),
170+
public_url=_get(data, "public_url"),
171+
idcs_domain_url=_get(data, "idcs_domain_url"),
172+
domain_id=_get(data, "domain_id"),
173+
lifecycle_state=_get(data, "lifecycle_state"),
174+
lifecycle_details=_get(data, "lifecycle_details"),
175+
is_suspended=_get(data, "is_suspended"),
176+
system_name=_get(data, "system_name"),
177+
environment_role=_get(data, "environment_role"),
178+
maintenance_policy=_get(data, "maintenance_policy"),
179+
time_upcoming_maintenance=_get(data, "time_upcoming_maintenance"),
180+
applied_patch_bundles=_get(data, "applied_patch_bundles"),
181+
subscription_ids=_get(data, "subscription_ids"),
182+
additional_language_packs=_get(data, "additional_language_packs"),
183+
kms_key_id=_get(data, "kms_key_id"),
184+
kms_key_info=_get(data, "kms_key_info"),
185+
dns_prefix=_get(data, "dns_prefix"),
186+
lockbox_id=_get(data, "lockbox_id"),
187+
is_break_glass_enabled=_get(data, "is_break_glass_enabled"),
188+
refresh=_get(data, "refresh"),
189+
rules=_get(data, "rules"),
190+
time_created=_get(data, "time_created"),
191+
time_updated=_get(data, "time_updated"),
192+
freeform_tags=_get(data, "freeform_tags"),
193+
defined_tags=_get(data, "defined_tags"),
194+
)
195+
196+
197+
def map_fusion_environment_status(data: Any) -> FusionEnvironmentStatus:
198+
"""Map SDK model or dict to FusionEnvironmentStatus."""
199+
# Some SDK responses may not have fusion_environment_id as key; try id as fallback
200+
fe_id = _get(data, "fusion_environment_id") or _get(data, "id")
201+
# Anything else goes to details as a dict (best-effort)
202+
coerced = _oci_to_dict(data) or {}
203+
details = {
204+
k: v
205+
for k, v in coerced.items()
206+
if k
207+
not in {"fusion_environment_id", "id", "status", "time_updated", "time_created"}
208+
} # noqa: E501
209+
return FusionEnvironmentStatus(
210+
fusion_environment_id=fe_id,
211+
status=_get(data, "status"),
212+
time_updated=_get(data, "time_updated"),
213+
time_created=_get(data, "time_created"),
214+
details=details or None,
215+
)

0 commit comments

Comments
 (0)