Skip to content

fix(mcp): flush stdout/stderr before exiting the CLI to stop large output truncation#8613

Closed
JSONbored wants to merge 1 commit into
mainfrom
claude/kind-nash-9932cd
Closed

fix(mcp): flush stdout/stderr before exiting the CLI to stop large output truncation#8613
JSONbored wants to merge 1 commit into
mainfrom
claude/kind-nash-9932cd

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • loopover-mcp changelog --json truncated its output mid-write whenever the packaged CHANGELOG.md grew past the OS pipe buffer size (~64KB) — test/unit/mcp-cli-profiles.test.ts's "reports package status and prints the packaged changelog" test was failing deterministically on main as a result, blocking a green test:ci gate for every branch.
  • Root cause: the CLI dispatcher called process.exit() immediately after a command finished. Writes to a piped process.stdout are asynchronous on POSIX, so process.exit() could terminate the process before the OS finished draining a large write.
  • Fix: added waitForStdioFlush() (packages/loopover-mcp/lib/cli-error.ts) and await it before each process.exit() call. Setting process.exitCode and relying on a natural exit instead isn't safe either — it was tried and reproducibly deadlocked whenever this CLI is spawned via child_process with default (piped, unclosed) stdio, e.g. Node's own execFile, which is exactly how this repo's own CLI test harness spawns it.

Scope

  • The PR title follows type(scope): short summary Conventional Commit format.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • Linked issue — not applicable; this is a maintainer PR fixing a bug discovered directly while diagnosing a failing test on main.

Validation

  • git diff --check
  • npm run typecheck
  • npm run build:mcp
  • npm run test:mcp-pack
  • Targeted tests: test/unit/mcp-cli-error.test.ts (new waitForStdioFlush unit tests, both branches + empty-list edge case) and the full test/unit/mcp-cli-*.test.ts + mcp-tool-rename-aliases.test.ts suite (76 files / 426 tests) — all green.
  • Scoped coverage check on the diff: waitForStdioFlush is 100% line/branch/function covered per coverage/lcov.info. The dispatcher's own 4 touched lines (the await waitForStdioFlush(...) call sites) remain structurally uncoverable — they only execute in a real subprocess spawn, same as the rest of this file's CLI dispatch block (see the existing v8 ignore comment and codecov.yml's note on packages/loopover-mcp/bin/loopover-mcp.ts).
  • npm run test:ci (full local gate) run twice end-to-end against an earlier version of this fix (before extracting waitForStdioFlush into lib/cli-error.ts for testability) — both green aside from 3 (then 2) flakes, each independently confirmed unrelated: CPU contention from concurrent test runs in sibling worktrees, and a local ui-kit build-order gap in manual invocation.
  • Skipped as not applicable to this change: npm run actionlint (no workflow files touched), npm run test:workers (no worker code touched), npm run ui:* (no UI code touched), npm audit (no dependency changes — verified via git diff --name-only against the rebase base).

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • N/A: no auth/cookie/CORS/GitHub App/Cloudflare/session changes.
  • N/A: no API/OpenAPI/MCP protocol-surface behavior changed (CLI process-exit timing only).
  • N/A: no UI changes.
  • N/A: no public docs/changelog changes needed for this fix.

Notes

  • Reproduced the truncation and the deadlock independently before settling on this fix; both are described in the commit message.

…tput truncation

process.exit() right after a command finished could race an in-flight, asynchronous
POSIX write to a piped stdout -- the packaged CHANGELOG.md (>64KB, past the OS pipe
buffer size) was getting cut off mid-write when `changelog --json` piped its output
to a consumer, breaking JSON parsing on the other end.

Add waitForStdioFlush() (packages/loopover-mcp/lib/cli-error.ts) and await it before
each process.exit() call in the CLI dispatcher. Setting process.exitCode and relying
on a natural exit instead isn't safe either: a parent that spawns this CLI via
child_process with default stdio (e.g. Node's own execFile) leaves the child's stdin
pipe open, which keeps the event loop alive forever.
@JSONbored JSONbored self-assigned this Jul 25, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored

Copy link
Copy Markdown
Owner Author

Closing as duplicate: the identical bug (process.exit() racing an in-flight async POSIX pipe write, truncating loopover-mcp CLI output over ~64KB) was independently found and fixed in #8612, already merged into main. Verified main's fix resolves the originally-failing test and the full mcp-cli test suite stays green.

@JSONbored JSONbored closed this Jul 25, 2026
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant