MCP stdio audit fixes: daslib/logger + popen_argv_pipe + stdin isolation - #2908
Merged
Conversation
Contributor
There was a problem hiding this comment.
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/loggerwith 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.
borisbat
force-pushed
the
claude/bold-carson-3bd9d8
branch
from
May 28, 2026 00:41
b8d89e4 to
7f200e8
Compare
borisbat
force-pushed
the
claude/bold-carson-3bd9d8
branch
from
May 28, 2026 01:09
7f200e8 to
f185fca
Compare
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
force-pushed
the
claude/bold-carson-3bd9d8
branch
from
May 28, 2026 01:37
f185fca to
e07fcd6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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>.logwith ISO 8601 UTC timestamp, level name, dotted category, message, optional fields object.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 globalDapiDebugAgentthat divertsprint()/to_log()into the log file — critical for stdio-transport processes where any accidental stdout write corrupts JSON-RPC framing. One install covers main + everynew_threadworker.src/builtin/module_builtin_fio.cppiso8601_now()builtin (UTC, ms precision, ISO 8601). Used bydaslib/logger; also generally useful.popen_argv_pipe()builtin — bidirectional subprocess pipes (writable stdin + readable stdout). Same shell-bypass semantics aspopen_argv.popen_argv: explicitly isolate child stdin from parent. Windows: openNULfor read, pass asSTARTUPINFO.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 bespokelog_open/log_write/log_pathinfrastructure withdaslib/logger. Dotted categories (mcp/mcp.req/mcp.res/mcp.tool/mcp.live/mcp.heap).tools/live.das: replace twosystem()calls (powershell launcher, chmod+sh wrapper) withrun_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.dasmodule command_line shared publicdeclaration. Without it, daslang's AOT codegen emitted only an empty stub for the module, triggeringerror[50101]hash desync when anything required it under AOT. This unblocks AOT for any future test/utility that usesget_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— spawnsdaslang.exe utils/mcp/main.dasviapopen_argv_pipe, drivesinitialize/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).tests/aot/CMakeLists.txt.Docs
daslib/loggeradded todas2rst.daswith 5 groups (Setup / Level filters / Output control / Log calls / Stdout hook) + handmademodule-logger.rst+sec_io.rsttoctree entry.iso8601_nowcategorized under "Time and date";popen_argv_pipeunder "OS specific routines" in builtin/fio doc groups. Handmade descriptions for both.skills/daslib_modules.mdupdated with logger entry.Test plan
9079 tests, 9073 passed, 6 skipped8423 tests, 8417 passed, 6 skipped0 issuesacross all 12 changed.dasfiles.dasfiles already-formattedbuild succeeded, no warningslogs/mcp_server.log, exits cleanly on stdin EOFarray.das,test_bulk_create.das): no verifier failures--verify+ lint changed files): passed🤖 Generated with Claude Code