Skip to content

MCP stdio audit fixes: daslib/logger + popen_argv_pipe + stdin isolation - #2908

Merged
borisbat merged 1 commit into
masterfrom
claude/bold-carson-3bd9d8
May 28, 2026
Merged

MCP stdio audit fixes: daslib/logger + popen_argv_pipe + stdin isolation#2908
borisbat merged 1 commit into
masterfrom
claude/bold-carson-3bd9d8

Conversation

@borisbat

Copy link
Copy Markdown
Collaborator

Summary

Closes the stdio-leak audit thread that started from MCP server hangs. Every byte the MCP child process writes is now contained; every byte of the parent's stdin is unreachable to children.

Changes

daslib/logger (new)

JSON Lines structured logger for daslang tools (MCP, daslang-live, etc). One JSON object per line to {get_das_root()}/logs/<name>.log with ISO 8601 UTC timestamp, level name, dotted category, message, optional fields object.

  • All public API prefixed (logger_init, logger_info, logger_warning, logger_set_min_level, logger_install_hook, ...) so short verbs don't collide unqualified.
  • logger_install_hook() registers a global DapiDebugAgent that diverts print() / to_log() into the log file — critical for stdio-transport processes where any accidental stdout write corrupts JSON-RPC framing. One install covers main + every new_thread worker.

src/builtin/module_builtin_fio.cpp

  • Add iso8601_now() builtin (UTC, ms precision, ISO 8601). Used by daslib/logger; also generally useful.
  • Add popen_argv_pipe() builtin — bidirectional subprocess pipes (writable stdin + readable stdout). Same shell-bypass semantics as popen_argv.
  • Harden popen_argv: explicitly isolate child stdin from parent. Windows: open NUL for read, pass as STARTUPINFO.hStdInput. Linux: open /dev/null, dup2(STDIN_FILENO) in child. Prevents a child from silently consuming bytes intended for the parent's JSON-RPC input.

utils/mcp/* (MCP migration)

  • main.das + protocol.das: replace bespoke log_open/log_write/log_path infrastructure with daslib/logger. Dotted categories (mcp / mcp.req / mcp.res / mcp.tool / mcp.live / mcp.heap).
  • tools/live.das: replace two system() calls (powershell launcher, chmod+sh wrapper) with run_and_capture / popen_argv. system() inherited MCP's stdio; any launcher chatter (PowerShell errors, COM messages) would corrupt JSON-RPC. Output now captured + logged.

daslib/command_line.das

  • Add missing module command_line shared public declaration. Without it, daslang's AOT codegen emitted only an empty stub for the module, triggering error[50101] hash desync when anything required it under AOT. This unblocks AOT for any future test/utility that uses get_das_exe.

Tests

  • tests/daslib/test_logger.das — 6 [test] / 12 subtests: JSON Lines write, level filter, dotted-category prefix override, JSON escaping + round-trip, fields serialization, no-op when unconfigured.
  • tests/mcp/test_mcp_jsonrpc.das — spawns daslang.exe utils/mcp/main.das via popen_argv_pipe, drives initialize / tools/list / tools/call / method-not-found through JSON-RPC. Multi-chunk read loop for >16KB responses.
  • tests/mcp/test_popen_argv_pipe.das — unit tests for the new builtin (echo round-trip, exit code propagation, stdin-EOF-after-block).
  • tests/fio/popen_argv.das — new test confirming child stdin isolation (spawn echo fixture, no input → exits cleanly, no output).
  • All new test dirs registered in tests/aot/CMakeLists.txt.

Docs

  • daslib/logger added to das2rst.das with 5 groups (Setup / Level filters / Output control / Log calls / Stdout hook) + handmade module-logger.rst + sec_io.rst toctree entry.
  • iso8601_now categorized under "Time and date"; popen_argv_pipe under "OS specific routines" in builtin/fio doc groups. Handmade descriptions for both.
  • skills/daslib_modules.md updated with logger entry.

Test plan

  • Interpreter test suite: 9079 tests, 9073 passed, 6 skipped
  • AOT test suite: 8423 tests, 8417 passed, 6 skipped
  • Lint: 0 issues across all 12 changed .das files
  • Format: all changed .das files already-formatted
  • Sphinx HTML build: build succeeded, no warnings
  • MCP server smoke: starts, logs JSON Lines to logs/mcp_server.log, exits cleanly on stdin EOF
  • JIT smoke (array.das, test_bulk_create.das): no verifier failures
  • Pre-push hook (formatter --verify + lint changed files): passed

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 28, 2026 00:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens MCP stdio behavior by adding structured logging, introducing bidirectional argv-based subprocess pipes, isolating child stdin, and migrating MCP launch/log paths away from inherited stdio.

Changes:

  • Adds daslib/logger with JSON Lines logging and runtime stdout hook support.
  • Adds/updates fio builtins for iso8601_now, popen_argv_pipe, and child stdin isolation.
  • Adds MCP subprocess tests, logger tests, AOT registration, and related docs/metadata.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
daslib/logger.das New structured logger module and debug-agent hook.
daslib/command_line.das Marks command_line as shared public.
src/builtin/module_builtin_fio.cpp Adds timestamp and bidirectional subprocess pipe builtins; hardens popen_argv.
include/daScript/simulate/aot_builtin_time.h Declares iso8601_now.
include/daScript/simulate/aot_builtin_fio.h Declares popen_argv_pipe.
utils/mcp/main.das Migrates MCP startup/shutdown logging to logger.
utils/mcp/protocol.das Replaces bespoke MCP logging with logger wrappers.
utils/mcp/tools/live.das Replaces system() launcher calls with captured argv subprocess calls.
tests/daslib/test_logger.das Adds logger behavior tests.
tests/mcp/test_mcp_jsonrpc.das Adds MCP JSON-RPC subprocess smoke tests.
tests/mcp/test_popen_argv_pipe.das Adds unit tests for bidirectional pipe builtin.
tests/mcp/_fixture_echo.das Adds echo fixture for subprocess pipe tests.
tests/mcp/_fixture_exit.das Adds exit-code fixture for subprocess pipe tests.
tests/fio/popen_argv.das Adds child stdin isolation regression test.
tests/aot/CMakeLists.txt Registers logger and MCP tests for AOT.
doc/reflections/das2rst.das Adds logger docs generation and builtin grouping updates.
doc/source/stdlib/sec_io.rst Adds logger to I/O docs toctree.
doc/source/stdlib/handmade/module-logger.rst Adds logger module documentation.
doc/source/stdlib/handmade/function-fio-popen_argv_pipe-0x2641eff84d0ce246.rst Adds popen_argv_pipe docs.
doc/source/stdlib/handmade/function-builtin-iso8601_now-0xc27a33e76b6556df.rst Adds iso8601_now docs.
skills/daslib_modules.md Adds logger module skill entry.
.gitignore Ignores generated logs directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/builtin/module_builtin_fio.cpp
Comment thread src/builtin/module_builtin_fio.cpp Outdated
Comment thread src/builtin/module_builtin_fio.cpp
Comment thread daslib/logger.das
Comment thread src/builtin/module_builtin_fio.cpp

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 22 changed files in this pull request and generated 5 comments.

Comment thread daslib/logger.das
Comment thread tests/daslib/test_logger.das Outdated
Comment thread src/builtin/module_builtin_fio.cpp
Comment thread src/builtin/module_builtin_fio.cpp
Comment thread src/builtin/module_builtin_fio.cpp
@borisbat
borisbat force-pushed the claude/bold-carson-3bd9d8 branch from 7f200e8 to f185fca Compare May 28, 2026 01:09
Closes the stdio-leak audit thread that started from MCP server hangs.
Every byte the MCP child process writes is now contained; every byte
of the parent's stdin is unreachable to children.

daslib/logger (new)
  - JSON Lines structured logger for daslang tools (MCP, daslang-live, etc).
    One JSON object per line to {get_das_root()}/logs/<name>.log with
    ISO 8601 UTC timestamp, level name, dotted category, message,
    optional fields object. All public API prefixed (logger_init,
    logger_info, logger_warning, logger_set_min_level,
    logger_install_hook, ...) so short verbs don't collide unqualified.
  - logger_install_hook() registers a global DapiDebugAgent that diverts
    print() / to_log() into the log file -- critical for stdio-transport
    processes where any accidental stdout write corrupts JSON-RPC framing.
    One install covers main + every new_thread worker.

src/builtin/module_builtin_fio.cpp
  - Add iso8601_now() builtin (UTC, ms precision, ISO 8601). Used by
    daslib/logger; also generally useful.
  - Add popen_argv_pipe() builtin -- bidirectional subprocess pipes
    (writable stdin + readable stdout). Same shell-bypass semantics as
    popen_argv.
  - Harden popen_argv: explicitly isolate child stdin from parent.
    Windows: open NUL for read, pass as STARTUPINFO.hStdInput. Linux:
    open /dev/null, dup2(STDIN_FILENO) in child. Prevents a child from
    silently consuming bytes intended for the parent's JSON-RPC input.

include/daScript/simulate/aot_builtin_{fio,time}.h
  - AOT-side declarations for the two new builtins so generated .cpp links.

utils/mcp/* (MCP migration)
  - main.das + protocol.das: replace bespoke log_open/log_write/log_path
    infrastructure with daslib/logger. Dotted categories (mcp / mcp.req /
    mcp.res / mcp.tool / mcp.live / mcp.heap).
  - tools/live.das: replace two system() calls (powershell launcher,
    chmod+sh wrapper) with run_and_capture / popen_argv. system() inherited
    MCP's stdio; any launcher chatter (PowerShell errors, COM messages)
    would corrupt JSON-RPC. Output now captured + logged.

daslib/command_line.das
  - Add missing `module command_line shared public` declaration. Without
    it, daslang's AOT codegen emitted only an empty stub for the module,
    triggering error[50101] hash desync when anything required it under
    AOT. This unblocks AOT for any future test/utility that uses
    get_das_exe.

Tests
  - tests/daslib/test_logger.das -- 6 [test] / 12 subtests: JSON Lines
    write, level filter, dotted-category prefix override, JSON escaping
    + round-trip, fields serialization, no-op when unconfigured.
  - tests/mcp/test_mcp_jsonrpc.das -- spawns daslang.exe utils/mcp/main.das
    via popen_argv_pipe, drives initialize / tools/list / tools/call /
    method-not-found through JSON-RPC. Multi-chunk read loop for >16KB
    responses.
  - tests/mcp/test_popen_argv_pipe.das -- unit tests for the new builtin
    (echo round-trip, exit code propagation, stdin-EOF-after-block).
  - tests/fio/popen_argv.das -- new test confirming child stdin isolation
    (spawn echo fixture, no input -> exits cleanly, no output).
  - All new test dirs registered in tests/aot/CMakeLists.txt.

Docs
  - daslib/logger added to das2rst.das with 5 groups (Setup / Level
    filters / Output control / Log calls / Stdout hook) + handmade
    module-logger.rst + sec_io.rst toctree entry.
  - iso8601_now categorized under "Time and date"; popen_argv_pipe
    under "OS specific routines" in builtin/fio doc groups. Handmade
    descriptions for both.
  - skills/daslib_modules.md updated with logger entry.

Verified locally
  - Interpreter test suite: 9079 tests, 9073 passed (6 skipped).
  - AOT test suite: 8423 tests, 8417 passed (6 skipped).
  - Lint: 0 issues across all changed .das files.
  - Format: all changed .das files already-formatted.
  - Sphinx HTML build: build succeeded, no warnings.
  - MCP server smoke: starts, logs JSON Lines, exits cleanly on stdin EOF.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@borisbat
borisbat force-pushed the claude/bold-carson-3bd9d8 branch from f185fca to e07fcd6 Compare May 28, 2026 01:37
@borisbat
borisbat merged commit 6c077b4 into master May 28, 2026
31 checks passed
@borisbat
borisbat deleted the claude/bold-carson-3bd9d8 branch May 30, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants