Skip to content

Commit abc42c7

Browse files
committed
fix: properly handle malformed command output
Signed-off-by: Richard Gebhardt <richard.gebhardt@oracle.com>
1 parent a04bce9 commit abc42c7

File tree

2 files changed

+66
-23
lines changed

2 files changed

+66
-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: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,47 +72,77 @@ async def test_get_oci_command_help_failure(self, mock_run):
7272

7373
@pytest.mark.asyncio
7474
@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):
75+
async def test_run_oci_command_success(self, mock_run):
76+
command = "compute instance list"
77+
7778
mock_result = MagicMock()
7879
mock_result.stdout = '{"key": "value"}'
7980
mock_result.stderr = ""
81+
mock_result.returncode = 0
8082
mock_run.return_value = mock_result
81-
mock_json_loads.return_value = {"key": "value"}
8283

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

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

93119
@pytest.mark.asyncio
94120
@patch("oracle.oci_api_mcp_server.server.subprocess.run")
95121
async def test_run_oci_command_failure(self, mock_run):
122+
command = "compute instance list"
123+
96124
mock_result = MagicMock()
97125
mock_result.stdout = "Some output"
98126
mock_result.stderr = "Some error"
127+
mock_result.returncode = 1
128+
99129
mock_run.side_effect = subprocess.CalledProcessError(
100-
returncode=1,
101-
cmd=["oci", "compute", "instance", "list"],
130+
returncode=mock_result.returncode,
131+
cmd=["oci"] + command.split(),
102132
output=mock_result.stdout,
103133
stderr=mock_result.stderr,
104134
)
105135

106136
async with Client(mcp) as client:
107137
result = (
108-
await client.call_tool(
109-
"run_oci_command", {"command": "compute instance list"}
110-
)
138+
await client.call_tool("run_oci_command", {"command": command})
111139
).data
112140

113141
assert result == {
114-
"error": "Some error",
115-
"output": "Some output",
142+
"command": command,
143+
"output": mock_result.stdout,
144+
"error": mock_result.stderr,
145+
"returncode": mock_result.returncode,
116146
}
117147

118148
@pytest.mark.asyncio

0 commit comments

Comments
 (0)