Skip to content

OpenCode times out during MCP tool handshake without wrapper due to mixed raw-fd polling and buffered stdio reads #100

@heraque

Description

@heraque

Summary

OpenCode still requires a wrapper when using codebase-memory-mcp directly from the binary.

After deeper debugging, the root cause is not protocol version negotiation, roots/list, or raw JSON newline handling by itself.

The actual problem is in the MCP stdio event loop implementation.

Problem

In cbm_mcp_server_run(...), the server mixed:

  • readiness polling on the raw stdin file descriptor
  • buffered reads from FILE *

This is unsafe for MCP stdio transport.

If stdio has already pre-buffered bytes internally, the raw fd may appear idle even though the next MCP message is already available in the stdio buffer. In that case, the server can stall during the handshake and OpenCode times out while trying to fetch the MCP tools.

User-visible behavior

When OpenCode is configured to launch the binary directly:

  • the MCP process starts
  • initialize appears to begin normally
  • OpenCode waits for tool resolution
  • the handshake times out
  • OpenCode fails unless a wrapper is placed in front of the binary

Root cause

The blocking behavior comes from mixing raw-fd polling with buffered stdio parsing in the MCP server loop.

This transport bug is especially visible in OpenCode because its handshake depends on reliable framed stdio exchange during tool discovery.

Fix

Refactor the stdio loop so that message parsing is driven directly from the stdio stream, without mixing poll() on the raw fd with buffered FILE * reads.

The fix must preserve support for both:

  • Content-Length framed MCP transport
  • raw JSON stream parsing

Validation

Validated locally after the fix with:

  • scripts/test.sh
  • scripts/lint.sh

Direct OpenCode validation without wrapper:

  • OpenCode was pointed directly to the built local binary
  • MCP tool discovery completed successfully
  • list_projects succeeded
  • OpenCode no longer required the wrapper

Notes

A wrapper may still normalize transport behavior, but it should no longer be required for the binary to work correctly with OpenCode.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions