Skip to content

Windows: MCP server hangs on initialize when launched by Hermes/AnyIO stdio client #635

Open

Description

@Gavinluo

Version

codebase-memory-mcp:0.8.1

Platform

Windows (x64)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

standard

What happened, and what did you expect?

Hi, I’m seeing a Windows-specific stdio startup issue with codebase-memory-mcp.

Environment
OS: Windows 11 / Windows x64
Binary: codebase-memory-mcp.exe
Binary path: E:\setup\codebase-memory-mcp-windows-amd64\codebase-memory-mcp.exe
codebase-memory-mcp --help reports: 0.8.1
MCP initialize response reports server version: 0.10.0
Client that fails: Hermes Agent MCP client, using Python MCP SDK / AnyIO stdio transport
Client that works: Qwen Code
Problem
When Hermes launches codebase-memory-mcp.exe directly as a stdio MCP server, the process starts but never responds to the MCP initialize request. Hermes eventually times out.

Hermes config:

mcp_servers:
  CODEBASE_MEMORY:
    command: E:\setup\codebase-memory-mcp-windows-amd64\codebase-memory-mcp.exe
    args: []
    timeout: 3600000
    connect_timeout: 180
    enabled: true

Hermes test result:

Testing 'CODEBASE_MEMORY'...
Transport: stdio → E:\setup\codebase-memory-mcp-windows-amd64\codebase-memory-mcp.exe
Auth: none
✗ Connection failed (42703ms): MCP call timed out after 40.7s

The process starts and logs:

level=info msg=mem.init budget_mb=16218 total_ram_mb=32437
But Hermes never receives the initialize response.

Important Observation
The same binary responds correctly when launched with normal Python subprocess.Popen and sent a newline-delimited JSON-RPC initialize request manually.

Example request:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"manual-test","version":"0"}}}
Response:

{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25","serverInfo":{"name":"codebase-memory-mcp","version":"0.10.0"},"capabilities":{"tools":{}}}}
tools/list also works manually and returns 14 tools.

So the MCP payload and protocol version appear to be valid.

Reproduction
Configure Hermes to launch codebase-memory-mcp.exe directly over stdio.
Stop any existing MCP process:
Get-Process codebase-memory-mcp -ErrorAction SilentlyContinue | Stop-Process -Force
Run:
hermes mcp test CODEBASE_MEMORY
Result: timeout during initialize.
Additional Evidence
Using a minimal AnyIO-based Python script to launch the binary also hangs waiting for stdout, even after writing the initialize request to stdin.

This suggests the problem is related to Windows async subprocess pipes / AnyIO / asyncio Proactor behavior.

By contrast, a normal blocking subprocess.Popen test works.

Source-Level Suspicion
In src/mcp/mcp.c, the Windows branch of the MCP server loop appears to wait on stdin like this:

HANDLE hStdin = (HANDLE)_get_osfhandle(fd);
DWORD wr = WaitForSingleObject(hStdin, STORE_IDLE_TIMEOUT_S * MCP_TIMEOUT_MS);
Then it calls cbm_getline().

On Windows, this WaitForSingleObject(stdin handle) path may not wake correctly for pipes created by Python AnyIO / asyncio Proactor subprocess transport.

The Unix branch has a more careful poll + FILE* buffer peek workaround for buffered getline() behavior, but the Windows branch does not appear to have an equivalent.

Workaround
A small Python stdio shim fixes the issue. Hermes launches Python, and Python launches codebase-memory-mcp.exe using normal blocking pipes, forwarding newline-delimited JSON-RPC messages.

Hermes config with shim:

mcp_servers:
  CODEBASE_MEMORY:
    command: C:\Users\Administrator\AppData\Roaming\uv\python\cpython-3.11.15-windows-x86_64-none\python.exe
    args:
      - E:\setup\codebase-memory-mcp-windows-amd64\codebase-memory-hermes-stdio-shim.py
      - E:\setup\codebase-memory-mcp-windows-amd64\codebase-memory-mcp.exe
      - '3600000'
    timeout: 3600000
    connect_timeout: 180
    enabled: true

After using the shim:

Testing 'CODEBASE_MEMORY'...
✓ Connected (2141ms)
✓ Tools discovered: 14
Why This Matters
Qwen Code can start this MCP server correctly, likely because it uses a different stdio process model. Hermes / Python MCP SDK / AnyIO direct launch fails on Windows.

So this looks like a compatibility issue between codebase-memory-mcp’s Windows stdio wait loop and some Windows async subprocess pipe implementations.

Request
Could you please check the Windows stdio event loop, especially the WaitForSingleObject usage on stdin handles?

A possible fix may be to avoid waiting directly on the stdin handle for pipe readiness on Windows, or to use a blocking read loop / overlapped-compatible approach instead.

Reproduction

only setup hermes to reproduction

Logs


Project scale (if relevant)

No response

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Activity

  1. DeusData commented on Jun 29, 2026

    @DeusData
    Owner

    Thanks for the report. I’ve triaged this under #394 and #395. We need a Hermes/AnyIO stdio reproduction that proves initialize never blocks and that stdout/stderr remain protocol-safe during startup.

  2. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Jun 30, 2026
  3. added
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Jul 8, 2026
  4. github-actions commented on Jul 8, 2026

    @github-actions

    Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:

    • the codebase-memory-mcp --version you're on
    • the exact steps or command you ran
    • a public repo (or a small dummy snippet) that shows the problem — please don't paste proprietary code

    Once that's here we'll pick it straight back up. Heads-up: issues left awaiting-reporter are automatically closed after a few weeks of silence, but a comment reopens the door anytime.

  5. DeusData commented on Jul 8, 2026

    @DeusData
    Owner

    v0.9.0 includes Windows runtime and MCP startup reliability fixes that may address this initialize hang. Could you retest the MCP handshake on v0.9.0 and confirm whether initialization still blocks? Marking awaiting-reporter.

  6. added this to the 0.9.1-rc milestone on Jul 8, 2026
  7. Gavinluo commented on Jul 10, 2026

    @Gavinluo
    Author

    Nice! Good job! I retested and passed.

  8. ako-v commented on Jul 19, 2026

    @ako-v

    I'm on codebase-memory-mcp 0.9.0 and Windows 11, but it does not work with hermes, when I try to test the connection it fails with this message:

      Testing 'codebase-memory-mcp'...
      Transport: stdio → C:\codebase-memory-mcp\codebase-memory-mcp.exe
      Auth: none
      ✗ Connection failed (41125ms): MCP call timed out after 41.1s (configured timeout: 40.0s)
    

    But as @Gavinluo said, it works with a shim for me!

  9. removed
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Jul 19, 2026
  10. DeusData commented on Jul 19, 2026

    @DeusData
    Owner

    Reopening because @ako-v confirms that direct Hermes launch still times out on Windows with v0.9.0 while the shim works. I removed awaiting-reporter and returned this high-priority bug to the 0.9.1-rc maintainer queue.

  11. wengecaitui commented on Aug 2, 2026

    @wengecaitui

    Repro for the Hermes/AnyIO initialize hang (Windows) — minimal, pure-stdlib

    Environment

    • OS: Windows 10
    • Binary: codebase-memory-mcp.exe — CLI --help reports 0.8.1, but the MCP initialize response reports serverInfo.version = 0.10.0 (version mismatch on this build)
    • Client: Hermes Agent, Python MCP SDK 1.26.0 (mcp.client.stdio.stdio_client), config command: <exe> with no args
    • Tried connect_timeout: 60 and 180 — identical outcome

    Symptom
    The client spawns the exe, keeps stdin open (normal long-lived MCP stdio), sends initialize, and never receives a response; after connect_timeout seconds the SDK surfaces an opaque CancelledError. stderr shows only level=info msg=mem.init budget_mb=... total_ram_mb=....

    Key experiment — the same exe answers instantly when stdin is closed right after the request:

    import subprocess, sys, threading, time
    
    EXE = sys.argv[1] if len(sys.argv) > 1 else r"C:\Users\Dujunyi\AppData\Local\codebase-memory-mcp\codebase-memory-mcp.exe"
    INIT = (b'{"jsonrpc":"2.0","id":1,"method":"initialize",'
            b'"params":{"protocolVersion":"2024-11-05","capabilities":{},'
            b'"clientInfo":{"name":"repro-635","version":"1.0"}}}\n')
    WAIT = 6.0
    
    def case(label: str, close_stdin: bool) -> str:
        p = subprocess.Popen([EXE], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
        p.stdin.write(INIT); p.stdin.flush()
        if close_stdin:
            p.stdin.close()
        got = {}
        def reader():
            try: got["data"] = p.stdout.read(512)
            except Exception as e: got["err"] = repr(e)
        t = threading.Thread(target=reader, daemon=True); t.start(); t.join(WAIT)
        if t.is_alive():
            p.kill(); return f"{label}: NO RESPONSE within {WAIT:.0f}s (stdin still open)  <-- BUG"
        p.kill()
        return f"{label}: responded in <{WAIT:.0f}s -> {got.get('data', b'')[:90]!r}"
    
    if __name__ == "__main__":
        print(case("A (EOF, stdin closed)   ", True))
        print(case("B (keep-alive, stdin open)", False))

    Output:

    A (EOF, stdin closed)   : responded in <6s -> '{"jsonrpc":"2.0","id":1,"result":{...serverInfo...}}'
    B (keep-alive, stdin open): NO RESPONSE within 6s (stdin still open)  <-- BUG
    

    Interpretation
    The stdio transport appears to read stdin until EOF before handling/flushing messages (read-all-then-handle), so any client that keeps the pipe open hangs forever. connect_timeout cannot fix this.

    Working workaround today
    The one-shot cli <tool> <json> subcommand responds reliably (fresh process per call). A small shim mapping MCP tools to cli calls is fully functional — happy to share it.

    Could you confirm whether the stdio transport reads stdin until EOF before processing messages? Happy to provide any further detail or a GitHub Actions repro.

  12. DeusData commented on Aug 14, 2026

    @DeusData
    Owner

    @wengecaitui, that A/B script is the entire diagnosis in twenty lines — "responds instantly with stdin closed, no response at all with stdin open" is the signature that mattered, and it lined up with @ako-v's confirmation that a shim works while a direct Hermes launch times out, and with @Gavinluo's original report.

    Your interpretation was correct, and it is now confirmed in code: on Windows the stdin wait uses WaitForSingleObject on a handle that is not waitable for an anonymous pipe (src/mcp/mcp.c), so a client that keeps the pipe open — which is exactly what MCP stdio is supposed to do — never gets an initialize response. That is being fixed, and it is in scope for tonight's v0.10.5, which is aimed at the issues that make cbm unusable rather than merely awkward.

    I am sorry this sat for as long as it did while people worked around it with shims. A server that never answers initialize is about as broken as an MCP server can be, and the version-mismatch detail you both flagged is noted too.

  13. DeusData commented on Sep 26, 2026

    @DeusData
    Owner

    Thank you, @Gavinluo, @ako-v and @wengecaitui! The stdin-open vs stdin-closed A/B script in particular was exactly the right experiment.

    A correction to the earlier update: since v0.10.0, a stdio session no longer runs the old WaitForSingleObject loop at all. It goes through the daemon frontend instead, which reads stdin with a blocking reader on its own thread and answers each message as it arrives, whether or not the client keeps the pipe open.

    Could one of you retest a direct Hermes launch (no shim) on v0.11.0 and tell us whether initialize now answers? If it still times out, the stderr from that launch plus the output of codebase-memory-mcp --version would be the key evidence.

  14. added
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Sep 26, 2026
  15. github-actions commented on Sep 26, 2026

    @github-actions

    Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:

    • the codebase-memory-mcp --version you're on
    • the exact steps or command you ran
    • a public repo (or a small dummy snippet) that shows the problem — please don't paste proprietary code

    Once that's here we'll pick it straight back up. Heads-up: issues left awaiting-reporter are automatically closed after a few weeks of silence, but a comment reopens the door anytime.

  16. github-actions commented on Oct 18, 2026

    @github-actions

    This issue has been waiting on more information for 21 days (a version, exact steps, or a public repro), so it's now marked stale. It will not be closed automatically — a maintainer reviews stale issues by hand. Adding a comment with the details clears the label and puts it straight back in the queue. We're happy to pick it back up the moment we can reproduce it.

  17. added
    staleNo activity after an info request; will be closed soon
    on Oct 18, 2026
  18. DeusData commented on Oct 19, 2026

    @DeusData
    Owner

    Thank you again for the direct-launch and shim comparisons. Checking back on the September retest request: does a current direct Hermes launch now complete initialization without the shim? If it still hangs, please include the CBM version and short stderr output; no broad new investigation is being requested.

  19. removed
    staleNo activity after an info request; will be closed soon
    on Oct 20, 2026
  20. DeusData commented on Oct 25, 2026

    @DeusData
    Owner

    Thank you again for the report and your patience. If you have already retried a direct Hermes launch, please share the version and whether initialization completes without the shim.

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

    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then closebugSomething isn't workingeditor/integrationEditor compatibility and CLI integrationpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.windowsWindows-specific issues

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions