Skip to content

No test asserts the MCP stdio server keeps stdout free of osw output #192

Closed

Description

@LukasGold

#170 asked for two things: keep osw log records off stdout, and add "a test asserting that a tool call writes nothing to stdout". The first is done. The second is not.

The protection that exists

Two mechanisms keep the JSON-RPC channel clean, both outside src/osw/mcp/server.py:

  • enable_logging() defaults its handler stream to sys.stderr (src/osw/__init__.py:149). Its docstring names the MCP stdio server as the reason for that default.
  • The MCP SDK's stdio_server claims file descriptor 1 and diverts it, then re-wraps the original as UTF-8 text, so a stray print() cannot tear a frame.

Only the first is osw's own code, and a single edit to enable_logging's default would undo it.

The gap

tests/test_mcp_server.py does not contain the string stdout anywhere. Its capsys tests assert on .err only:

  • test_build_server_is_quiet_by_default (line 164): assert "[osw]" not in capsys.readouterr().err
  • test_main_is_quiet_on_a_successful_start (line 205): same assertion
  • test_build_server_writes_the_report_into_the_given_buffer (line 174): assert capsys.readouterr().err == ""

tests/test_service_context.py:152-165 covers Context.guard() reassigning sys.stdout. That is a different mechanism: guard() rebinds the name sys.stdout, which a logging.StreamHandler built earlier does not follow, so those tests do not cover the handler case #170 described.

Proposed test

Emit an osw log record during a tool call on a built server, and assert capsys.readouterr().out == "". A unit-level test is enough. The thing that can regress is the handler's stream, not the SDK's descriptor handling.

Related: #170, whose diagnosis is now out of date. It states that enable_logging() defaults to sys.stdout, which was true when it was written and is no longer true.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions