Files
Ruofeng Yang 1c0ba03ce6 fix(mcp-servers,tests): defer stdio rebind to main(); fix test isolation so full suite runs under pytest fd-capture
Adding a full-suite CI job (prior commit) exposed two latent bugs that only
surface when the whole suite runs under pytest's DEFAULT fd-level capture —
which had never happened, because collection aborted early and most runs used
--capture=no.

1. Import-time stdio seizure (4 MCP servers). codex-image2, llm-chat,
   minimax-chat, and gemini-review each ran, at MODULE TOP LEVEL,
   `sys.stdout = os.fdopen(sys.stdout.fileno(), "wb")`. os.fdopen defaults to
   closefd=True, so importing the module SEIZES ownership of the current stdout
   fd — under pytest that is the capture tmpfile fd, and when the wrapper is GC'd
   it closes it, corrupting capture for every subsequent test ("OSError: Bad
   file descriptor" cascade; 395 spurious errors). Fixed by deferring the rebind
   into an idempotent `_init_stdio()` called at the top of each `main()` — the
   exact pattern manual-review already used. Real server launch
   (`python server.py`) is unchanged (main() runs it first); only import is now
   side-effect-free. test_codex_image2_server.py reverts to a plain import.

2. Module-name collision in tests (test_manual_review.py). It loaded the server
   via bare `import server as srv` (x9). Every mcp-server is named server.py, and
   test_minimax_chat_server does `sys.path.insert(0, <minimax dir>)` at
   collection time, so by run time a bare `import server` resolved to minimax's
   server → `AttributeError: module 'server' has no attribute 'create_thread'`
   (passed in isolation, failed in the full suite). Fixed to load by explicit
   path under a unique module name via spec_from_file_location, matching every
   other server test in the suite.

Full suite now: 406 passed, 16 skipped under default fd-capture (was: aborted /
395 errors / 9 failures).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 12:59:47 +08:00
..