Skip to content

Commit a4691b4

Browse files
authored
Merge pull request #49 from gebhardtr/rigebha/malformed
fix: properly handle malformed command output
2 parents f0a0dde + d9a787f commit a4691b4

File tree

2 files changed

+67
-23
lines changed

2 files changed

+67
-23
lines changed

src/oci-api-mcp-server/oracle/oci_api_mcp_server/server.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,17 +161,30 @@ def run_oci_command(
161161
check=True,
162162
shell=False,
163163
)
164-
return (
165-
json.loads(result.stdout)
166-
if result.stdout
167-
else {
168-
"error": result.stderr,
169-
}
170-
)
164+
165+
result.check_returncode()
166+
167+
response = {
168+
"command": command,
169+
"output": result.stdout,
170+
"error": result.stderr,
171+
"returncode": result.returncode,
172+
}
173+
174+
try:
175+
response["output"] = json.loads(result.stdout)
176+
except TypeError:
177+
pass
178+
except json.JSONDecodeError:
179+
pass
180+
181+
return response
171182
except subprocess.CalledProcessError as e:
172183
return {
173-
"error": e.stderr,
184+
"command": command,
174185
"output": e.stdout,
186+
"error": e.stderr,
187+
"returncode": e.returncode,
175188
}
176189

177190

src/oci-api-mcp-server/oracle/oci_api_mcp_server/tests/test_oci_api_tools.py

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66

77
import importlib.metadata
8+
import json
89
import subprocess
910
from unittest.mock import ANY, MagicMock, patch
1011

@@ -72,47 +73,77 @@ async def test_get_oci_command_help_failure(self, mock_run):
7273

7374
@pytest.mark.asyncio
7475
@patch("oracle.oci_api_mcp_server.server.subprocess.run")
75-
@patch("oracle.oci_api_mcp_server.server.json.loads")
76-
async def test_run_oci_command_success(self, mock_json_loads, mock_run):
76+
async def test_run_oci_command_success(self, mock_run):
77+
command = "compute instance list"
78+
7779
mock_result = MagicMock()
7880
mock_result.stdout = '{"key": "value"}'
7981
mock_result.stderr = ""
82+
mock_result.returncode = 0
8083
mock_run.return_value = mock_result
81-
mock_json_loads.return_value = {"key": "value"}
8284

8385
async with Client(mcp) as client:
8486
result = (
85-
await client.call_tool(
86-
"run_oci_command", {"command": "compute instance list"}
87-
)
87+
await client.call_tool("run_oci_command", {"command": command})
88+
).data
89+
90+
assert result == {
91+
"command": command,
92+
"output": json.loads(mock_result.stdout),
93+
"error": mock_result.stderr,
94+
"returncode": mock_result.returncode,
95+
}
96+
97+
@pytest.mark.asyncio
98+
@patch("oracle.oci_api_mcp_server.server.subprocess.run")
99+
async def test_run_oci_command_string_success(self, mock_run):
100+
command = "compute instance list"
101+
102+
mock_result = MagicMock()
103+
mock_result.stdout = "This is not JSON"
104+
mock_result.stderr = ""
105+
mock_result.returncode = 0
106+
mock_run.return_value = mock_result
107+
108+
async with Client(mcp) as client:
109+
result = (
110+
await client.call_tool("run_oci_command", {"command": command})
88111
).data
89112

90-
assert result == {"key": "value"}
91-
mock_json_loads.assert_called_once_with('{"key": "value"}')
113+
assert result == {
114+
"command": command,
115+
"output": mock_result.stdout,
116+
"error": mock_result.stderr,
117+
"returncode": mock_result.returncode,
118+
}
92119

93120
@pytest.mark.asyncio
94121
@patch("oracle.oci_api_mcp_server.server.subprocess.run")
95122
async def test_run_oci_command_failure(self, mock_run):
123+
command = "compute instance list"
124+
96125
mock_result = MagicMock()
97126
mock_result.stdout = "Some output"
98127
mock_result.stderr = "Some error"
128+
mock_result.returncode = 1
129+
99130
mock_run.side_effect = subprocess.CalledProcessError(
100-
returncode=1,
101-
cmd=["oci", "compute", "instance", "list"],
131+
returncode=mock_result.returncode,
132+
cmd=["oci"] + command.split(),
102133
output=mock_result.stdout,
103134
stderr=mock_result.stderr,
104135
)
105136

106137
async with Client(mcp) as client:
107138
result = (
108-
await client.call_tool(
109-
"run_oci_command", {"command": "compute instance list"}
110-
)
139+
await client.call_tool("run_oci_command", {"command": command})
111140
).data
112141

113142
assert result == {
114-
"error": "Some error",
115-
"output": "Some output",
143+
"command": command,
144+
"output": mock_result.stdout,
145+
"error": mock_result.stderr,
146+
"returncode": mock_result.returncode,
116147
}
117148

118149
@pytest.mark.asyncio

0 commit comments

Comments
 (0)