Skip to content

Commit 245deab

Browse files
committed
Record MCP connection failures as startup warnings
make_session_with_mcp printed per-server MCP failures to stderr before the TUI started, where prompt_toolkit's screen handling could scroll them away. The failures now land in session.startup_warnings and are rendered in the TUI banner like the default_agent warning.
1 parent f991a9a commit 245deab

2 files changed

Lines changed: 63 additions & 6 deletions

File tree

‎python_agent_harness/cli.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,14 @@ def make_session_with_mcp(
143143
144144
Wraps ``make_session``: the session's MCP servers are connected and
145145
their tools registered before the session is returned (discovery
146-
happens once, at session start). Per-server failures are printed
147-
to stderr and never prevent the session from running — the agent
148-
keeps working with the built-in tools.
146+
happens once, at session start). Per-server failures are recorded
147+
as startup warnings (rendered by the TUI banner) and never prevent
148+
the session from running — the agent keeps working with the
149+
built-in tools.
149150
"""
150151
session = make_session(project_dir, config_path=config_path, model=model, stream=stream)
151-
failures = session.connect_mcp()
152-
for server, err in failures:
153-
print(f"python-agent-harness: [{server}] {err}", file=sys.stderr)
152+
for server, err in session.connect_mcp():
153+
session.startup_warnings.append(f"MCP [{server}]: {err}")
154154
return session
155155

156156

‎tests/test_custom_agents.py‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import json
77
import os
8+
import sys
89
import tempfile
910
import unittest
1011
from pathlib import Path
@@ -283,6 +284,62 @@ def test_valid_default_agent_no_warning(self):
283284
self.assertEqual(session.startup_warnings, [])
284285

285286

287+
class TestMcpFailuresAsStartupWarnings(unittest.TestCase):
288+
"""make_session_with_mcp records MCP connection failures as startup
289+
warnings so the TUI banner renders them (instead of a stderr print
290+
that is invisible inside the interface)."""
291+
292+
def test_mcp_failure_recorded_as_startup_warning(self):
293+
from python_agent_harness import cli
294+
from python_agent_harness.mcp.config import MCPConfig, MCPServerConfig
295+
from python_agent_harness.mcp.manager import MCPManager
296+
from python_agent_harness.session import Session as RealSession
297+
298+
class WarnSession(RealSession):
299+
# swap in a manager whose only server fails to connect
300+
def __init__(self, *args, **kwargs):
301+
kwargs.pop("mcp", None)
302+
super().__init__(*args, **kwargs)
303+
self.mcp_manager = MCPManager(
304+
MCPConfig(
305+
servers={
306+
"ghost": MCPServerConfig(
307+
name="ghost",
308+
transport="stdio",
309+
command=sys.executable,
310+
args=["-c", "import sys; sys.exit(1)"],
311+
timeout=15,
312+
)
313+
}
314+
)
315+
)
316+
317+
saved = {k: os.environ.get(k) for k in ENV_KEYS}
318+
for k in ENV_KEYS:
319+
os.environ.pop(k, None)
320+
try:
321+
with (
322+
mock.patch.object(cli, "Session", WarnSession),
323+
tempfile.TemporaryDirectory() as d,
324+
):
325+
p = Path(d) / "config.json"
326+
p.write_text(json.dumps({"llm": {"model": "test-model"}}), encoding="utf-8")
327+
session = cli.make_session_with_mcp("/tmp", config_path=str(p))
328+
try:
329+
self.assertEqual(len(session.startup_warnings), 1)
330+
self.assertIn("MCP [ghost]", session.startup_warnings[0])
331+
# built-in tools still work after the failed server
332+
self.assertIn("Read", session.registry._tools)
333+
finally:
334+
session.close()
335+
finally:
336+
for k, v in saved.items():
337+
if v is None:
338+
os.environ.pop(k, None)
339+
else:
340+
os.environ[k] = v
341+
342+
286343
class TestReviewerAgentContent(unittest.TestCase):
287344
"""Verify the bundled reviewer.md agent has expected content."""
288345

0 commit comments

Comments
 (0)