Skip to content

Commit 3e6e8b8

Browse files
authored
[CHORE] Improved oci-compute-instance-agent-mcp-server consistency (#72)
Signed-off-by: will.shope <[email protected]>
1 parent c123701 commit 3e6e8b8

File tree

6 files changed

+578
-180
lines changed

6 files changed

+578
-180
lines changed

src/oci-compute-instance-agent-mcp-server/oracle/oci_compute_instance_agent_mcp_server/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
"""
66

77
__project__ = "oracle.oci-compute-instance-agent-mcp-server"
8-
__version__ = "1.0.1"
8+
__version__ = "2.0.0"
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
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 Literal, Optional, Union
9+
10+
import oci
11+
from pydantic import BaseModel, Field
12+
13+
# Nested OCI models represented as Pydantic classes
14+
15+
16+
def _oci_to_dict(obj):
17+
"""Best-effort conversion of OCI SDK model objects to plain dicts."""
18+
if obj is None:
19+
return None
20+
try:
21+
from oci.util import to_dict as oci_to_dict
22+
23+
return oci_to_dict(obj)
24+
except Exception:
25+
pass
26+
if isinstance(obj, dict):
27+
return obj
28+
if hasattr(obj, "__dict__"):
29+
return {k: v for k, v in obj.__dict__.items() if not k.startswith("_")}
30+
return None
31+
32+
33+
# region InstanceAgentCommandExecutionOutputContent
34+
35+
36+
class InstanceAgentCommandExecutionOutputViaTextDetails(BaseModel):
37+
"""The execution output from a command when returned in plain text."""
38+
39+
output_type: Literal["TEXT"] = Field(
40+
"TEXT", description="The output destination type for the command."
41+
)
42+
exit_code: int = Field(
43+
...,
44+
description="The exit code for the command. Exit code `0` indicates success.",
45+
)
46+
message: Optional[str] = Field(
47+
None,
48+
description="An optional status message that Oracle Cloud Agent "
49+
"can populate for additional troubleshooting.",
50+
)
51+
text: Optional[str] = Field(None, description="The command output.")
52+
text_sha256: Optional[str] = Field(
53+
None, description="SHA-256 checksum value of the text content."
54+
)
55+
56+
57+
class InstanceAgentCommandExecutionOutputViaObjectStorageUriDetails(BaseModel):
58+
"""The execution output from a command when saved to an Object Storage URL."""
59+
60+
output_type: Literal["OBJECT_STORAGE_URI"] = Field(
61+
"OBJECT_STORAGE_URI", description="The output destination type for the command."
62+
)
63+
exit_code: int = Field(
64+
...,
65+
description="The exit code for the command. Exit code `0` indicates success.",
66+
)
67+
message: Optional[str] = Field(
68+
None,
69+
description="An optional status message that Oracle Cloud Agent "
70+
"can populate for additional troubleshooting.",
71+
)
72+
output_uri: str = Field(
73+
...,
74+
description="The Object Storage URL or pre-authenticated request (PAR) for the command output.",
75+
)
76+
77+
78+
class InstanceAgentCommandExecutionOutputViaObjectStorageTupleDetails(BaseModel):
79+
"""The execution output from a command when saved to an Object Storage bucket."""
80+
81+
output_type: Literal["OBJECT_STORAGE_TUPLE"] = Field(
82+
"OBJECT_STORAGE_TUPLE",
83+
description="The output destination type for the command.",
84+
)
85+
exit_code: int = Field(
86+
...,
87+
description="The exit code for the command. Exit code `0` indicates success.",
88+
)
89+
message: Optional[str] = Field(
90+
None,
91+
description="An optional status message that Oracle Cloud Agent "
92+
"can populate for additional troubleshooting.",
93+
)
94+
bucket_name: str = Field(
95+
..., description="The Object Storage bucket for the command output."
96+
)
97+
namespace_name: str = Field(
98+
..., description="The Object Storage namespace for the command output."
99+
)
100+
object_name: str = Field(
101+
..., description="The Object Storage object name for the command output."
102+
)
103+
104+
105+
OutputContent = Union[
106+
InstanceAgentCommandExecutionOutputViaTextDetails,
107+
InstanceAgentCommandExecutionOutputViaObjectStorageUriDetails,
108+
InstanceAgentCommandExecutionOutputViaObjectStorageTupleDetails,
109+
]
110+
111+
112+
# Based on oci.compute_instance_agent.models.InstanceAgentCommandExecution
113+
class InstanceAgentCommandExecution(BaseModel):
114+
"""
115+
Pydantic model mirroring the fields of oci.compute_instance_agent.models.InstanceAgentCommandExecution.
116+
Nested OCI model types are represented as Pydantic classes (above).
117+
"""
118+
119+
instance_agent_command_id: str = Field(..., description="The OCID of the command.")
120+
instance_id: str = Field(..., description="The OCID of the instance.")
121+
delivery_state: Literal[
122+
"VISIBLE", "PENDING", "ACKED", "ACKED_CANCELED", "EXPIRED"
123+
] = Field(
124+
...,
125+
description=(
126+
"Specifies the command delivery state. "
127+
"* `VISIBLE` - The command is visible to instance. "
128+
"* `PENDING` - The command is pending ack from the instance. "
129+
"* `ACKED` - The command has been received and acked by the instance. "
130+
"* `ACKED_CANCELED` - The canceled command has been received and "
131+
"acked by the instance. "
132+
"* `EXPIRED` - The instance has not requested for commands and "
133+
"its delivery has expired."
134+
),
135+
)
136+
lifecycle_state: Literal[
137+
"ACCEPTED", "IN_PROGRESS", "SUCCEEDED", "FAILED", "TIMED_OUT", "CANCELED"
138+
] = Field(
139+
...,
140+
description=(
141+
"Command execution life cycle state. "
142+
"* `ACCEPTED` - The command execution has been accepted to run. "
143+
"* `IN_PROGRESS` - The command execution is in progress. "
144+
"* `SUCCEEDED` - The command execution is successful. "
145+
"* `FAILED` - The command execution has failed. "
146+
"* `TIMED_OUT` - The command execution has timedout. "
147+
"* `CANCELED` - The command execution has canceled."
148+
),
149+
)
150+
time_created: datetime = Field(..., description="The command creation date.")
151+
time_updated: datetime = Field(..., description="The command last updated at date.")
152+
sequence_number: int = Field(
153+
...,
154+
description="The large non-consecutive number that Run Command Service "
155+
"assigns to each created command.",
156+
)
157+
display_name: Optional[str] = Field(
158+
None, description="The user friendly display name of the command."
159+
)
160+
content: OutputContent = Field(..., description="The command output details.")
161+
162+
163+
def map_text_output(
164+
content,
165+
) -> InstanceAgentCommandExecutionOutputViaTextDetails | None:
166+
if not content:
167+
return None
168+
data = _oci_to_dict(content) or {}
169+
return InstanceAgentCommandExecutionOutputViaTextDetails(
170+
exit_code=data.get("exit_code", getattr(content, "exit_code", None)),
171+
message=data.get("message", getattr(content, "message", None)),
172+
text=data.get("text", getattr(content, "text", None)),
173+
text_sha256=data.get("text_sha256", getattr(content, "text_sha256", None)),
174+
)
175+
176+
177+
def map_uri_output(
178+
content,
179+
) -> InstanceAgentCommandExecutionOutputViaObjectStorageUriDetails | None:
180+
if not content:
181+
return None
182+
data = _oci_to_dict(content) or {}
183+
return InstanceAgentCommandExecutionOutputViaObjectStorageUriDetails(
184+
exit_code=data.get("exit_code", getattr(content, "exit_code", None)),
185+
message=data.get("message", getattr(content, "message", None)),
186+
output_uri=data.get("output_uri", getattr(content, "output_uri", None)),
187+
)
188+
189+
190+
def map_tuple_output(
191+
content,
192+
) -> InstanceAgentCommandExecutionOutputViaObjectStorageTupleDetails | None:
193+
if not content:
194+
return None
195+
data = _oci_to_dict(content) or {}
196+
return InstanceAgentCommandExecutionOutputViaObjectStorageTupleDetails(
197+
exit_code=data.get("exit_code", getattr(content, "exit_code", None)),
198+
message=data.get("message", getattr(content, "message", None)),
199+
bucket_name=data.get("bucket_name", getattr(content, "bucket_name", None)),
200+
namespace_name=data.get(
201+
"namespace_name", getattr(content, "namespace_name", None)
202+
),
203+
object_name=data.get("object_name", getattr(content, "object_name", None)),
204+
)
205+
206+
207+
def map_output_content(
208+
content: oci.compute_instance_agent.models.InstanceAgentCommandExecutionOutputContent,
209+
) -> OutputContent | None:
210+
if not content:
211+
return None
212+
output_type = getattr(content, "output_type", None)
213+
if output_type == "TEXT":
214+
return map_text_output(content)
215+
elif output_type == "OBJECT_STORAGE_URI":
216+
return map_uri_output(content)
217+
elif output_type == "OBJECT_STORAGE_TUPLE":
218+
return map_tuple_output(content)
219+
return None
220+
221+
222+
def map_instance_agent_command_execution(
223+
command_execution: oci.compute_instance_agent.models.InstanceAgentCommandExecution,
224+
) -> InstanceAgentCommandExecution:
225+
"""
226+
Convert an oci.compute_instance_agent.models.InstanceAgentCommandExecution to
227+
oracle.oci_compute_instance_agent_mcp_server.models.InstanceAgentCommandExecution,
228+
including all nested types.
229+
"""
230+
return InstanceAgentCommandExecution(
231+
instance_agent_command_id=getattr(
232+
command_execution, "instance_agent_command_id", None
233+
),
234+
instance_id=getattr(command_execution, "instance_id", None),
235+
delivery_state=getattr(command_execution, "delivery_state", None),
236+
lifecycle_state=getattr(command_execution, "lifecycle_state", None),
237+
time_created=getattr(command_execution, "time_created", None),
238+
time_updated=getattr(command_execution, "time_updated", None),
239+
sequence_number=getattr(command_execution, "sequence_number", None),
240+
display_name=getattr(command_execution, "display_name", None),
241+
content=map_output_content(getattr(command_execution, "content", None)),
242+
)
243+
244+
245+
# endregion
246+
247+
# region InstanceAgentCommandSummary
248+
249+
250+
class InstanceAgentCommandSummary(BaseModel):
251+
"""
252+
Pydantic model mirroring the fields of oci.compute_instance_agent.models.InstanceAgentCommandSummary.
253+
"""
254+
255+
instance_agent_command_id: str = Field(..., description="The OCID of the command.")
256+
display_name: Optional[str] = Field(
257+
None, description="A user-friendly name. Does not have to be unique."
258+
)
259+
compartment_id: str = Field(
260+
..., description="The OCID of the compartment containing the command."
261+
)
262+
time_created: datetime = Field(
263+
..., description="The date and time the command was created (RFC3339)."
264+
)
265+
time_updated: datetime = Field(
266+
..., description="The date and time the command was last updated (RFC3339)."
267+
)
268+
is_canceled: Optional[bool] = Field(
269+
None,
270+
description="Whether a request was made to cancel the command. "
271+
"Canceling a command is a best-effort attempt.",
272+
)
273+
274+
275+
def map_instance_agent_command_summary(
276+
command_summary: oci.compute_instance_agent.models.InstanceAgentCommandSummary,
277+
) -> InstanceAgentCommandSummary:
278+
"""
279+
Convert an oci.compute_instance_agent.models.InstanceAgentCommandSummary to
280+
oracle.oci_compute_instance_agent_mcp_server.models.InstanceAgentCommandSummary.
281+
"""
282+
return InstanceAgentCommandSummary(
283+
instance_agent_command_id=getattr(
284+
command_summary, "instance_agent_command_id", None
285+
),
286+
display_name=getattr(command_summary, "display_name", None),
287+
compartment_id=getattr(command_summary, "compartment_id", None),
288+
time_created=getattr(command_summary, "time_created", None),
289+
time_updated=getattr(command_summary, "time_updated", None),
290+
is_canceled=getattr(command_summary, "is_canceled", None),
291+
)
292+
293+
294+
# endregion
295+
296+
# region InstanceAgentCommandExecutionSummary
297+
298+
299+
class InstanceAgentCommandExecutionSummary(BaseModel):
300+
"""
301+
Pydantic model mirroring the fields of
302+
oci.compute_instance_agent.models.InstanceAgentCommandExecutionSummary.
303+
Nested OCI model types are represented as Pydantic classes (above).
304+
"""
305+
306+
instance_agent_command_id: str = Field(..., description="The OCID of the command.")
307+
instance_id: str = Field(..., description="The OCID of the instance.")
308+
delivery_state: Literal[
309+
"VISIBLE", "PENDING", "ACKED", "ACKED_CANCELED", "EXPIRED"
310+
] = Field(
311+
...,
312+
description="The command delivery state. "
313+
"* `VISIBLE` - The command is visible to the instance. "
314+
"* `PENDING` - The command is pending acknowledgment from the instance. "
315+
"* `ACKED` - The command has been received and acknowledged by the instance. "
316+
"* `ACKED_CANCELED` - The canceled command has been received and acknowledged by the instance. "
317+
"* `EXPIRED` - The instance has not requested for commands and the command's delivery has expired.",
318+
)
319+
lifecycle_state: Literal[
320+
"ACCEPTED", "IN_PROGRESS", "SUCCEEDED", "FAILED", "TIMED_OUT", "CANCELED"
321+
] = Field(
322+
...,
323+
description="The command execution lifecycle state. "
324+
"* `ACCEPTED` - The command has been accepted to run. "
325+
"* `IN_PROGRESS` - The command is in progress. "
326+
"* `SUCCEEDED` - The command was successfully executed. "
327+
"* `FAILED` - The command failed to execute. "
328+
"* `TIMED_OUT` - The command execution timed out. "
329+
"* `CANCELED` - The command execution was canceled.",
330+
)
331+
time_created: datetime = Field(
332+
..., description="The date and time the command was created (RFC3339)."
333+
)
334+
time_updated: datetime = Field(
335+
..., description="The date and time the command was last updated (RFC3339)."
336+
)
337+
sequence_number: int = Field(
338+
...,
339+
description="A large, non-consecutive number that Oracle Cloud Agent "
340+
"assigns to each created command.",
341+
)
342+
display_name: Optional[str] = Field(
343+
None, description="A user-friendly name. Does not have to be unique."
344+
)
345+
content: Optional[OutputContent] = Field(
346+
None, description="The execution output from a command."
347+
)
348+
349+
350+
def map_instance_agent_command_execution_summary(
351+
command_execution_summary: oci.compute_instance_agent.models.InstanceAgentCommandExecutionSummary,
352+
) -> InstanceAgentCommandExecutionSummary:
353+
"""
354+
Convert an oci.compute_instance_agent.models.InstanceAgentCommandExecutionSummary to
355+
oracle.oci_compute_instance_agent_mcp_server.models.InstanceAgentCommandExecutionSummary,
356+
including all nested types.
357+
"""
358+
return InstanceAgentCommandExecutionSummary(
359+
instance_agent_command_id=getattr(
360+
command_execution_summary, "instance_agent_command_id", None
361+
),
362+
instance_id=getattr(command_execution_summary, "instance_id", None),
363+
delivery_state=getattr(command_execution_summary, "delivery_state", None),
364+
lifecycle_state=getattr(command_execution_summary, "lifecycle_state", None),
365+
time_created=getattr(command_execution_summary, "time_created", None),
366+
time_updated=getattr(command_execution_summary, "time_updated", None),
367+
sequence_number=getattr(command_execution_summary, "sequence_number", None),
368+
display_name=getattr(command_execution_summary, "display_name", None),
369+
content=map_output_content(getattr(command_execution_summary, "content", None)),
370+
)
371+
372+
373+
# endregion

0 commit comments

Comments
 (0)