diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f3a4532..9bb7485c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases) +## 0.4.5 (2026-04-12) + +- Fix: MCP server no longer crashes with `ValidationError` on blank lines sent between JSON messages by some clients (#201) + ## 0.4.4 (2026-04-12) - Fix: `watch` now preserves INFERRED/AMBIGUOUS edges (code↔doc rationale links) across rebuilds — previously all cross-type edges were dropped (#261) diff --git a/graphify/serve.py b/graphify/serve.py index 279b5d31..a0778343 100644 --- a/graphify/serve.py +++ b/graphify/serve.py @@ -108,6 +108,36 @@ def _find_node(G: nx.Graph, label: str) -> list[str]: if term in d.get("label", "").lower() or term == nid.lower()] +def _filter_blank_stdin() -> None: + """Filter blank lines from stdin before MCP reads it. + + Some MCP clients (Claude Desktop, etc.) send blank lines between JSON + messages. The MCP stdio transport tries to parse every line as a + JSONRPCMessage, so a bare newline triggers a Pydantic ValidationError. + This installs an OS-level pipe that relays stdin while dropping blanks. + """ + import os + import threading + + r_fd, w_fd = os.pipe() + saved_fd = os.dup(sys.stdin.fileno()) + + def _relay() -> None: + try: + with open(saved_fd, "rb") as src, open(w_fd, "wb") as dst: + for line in src: + if line.strip(): + dst.write(line) + dst.flush() + except Exception: + pass + + threading.Thread(target=_relay, daemon=True).start() + os.dup2(r_fd, sys.stdin.fileno()) + os.close(r_fd) + sys.stdin = open(0, "r", closefd=False) + + def serve(graph_path: str = "graphify-out/graph.json") -> None: """Start the MCP server. Requires pip install mcp.""" try: @@ -325,6 +355,7 @@ def serve(graph_path: str = "graphify-out/graph.json") -> None: async with stdio_server() as streams: await server.run(streams[0], streams[1], server.create_initialization_options()) + _filter_blank_stdin() asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index 3c9f34f9..baae916c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "graphifyy" -version = "0.4.4" +version = "0.4.5" description = "AI coding assistant skill (Claude Code, Codex, OpenCode, Cursor, OpenClaw, Factory Droid, Trae) - turn any folder of code, docs, papers, images, or videos into a queryable knowledge graph" readme = "README.md" license = { file = "LICENSE" }