Skip to content

list_modules lists what a require can reach; the watchdog's --stdio front replaces the MCP python supervisor - #3987

Merged
borisbat merged 2 commits into
masterfrom
bbatkin/mcp-list-modules
Sep 10, 2026
Merged

list_modules lists what a require can reach; the watchdog's --stdio front replaces the MCP python supervisor#3987
borisbat merged 2 commits into
masterfrom
bbatkin/mcp-list-modules

Conversation

@borisbat

Copy link
Copy Markdown
Collaborator

Behavior change: .mcp.json entries written by utils/mcp/setup.das now spawn bin/watchdog --stdio instead of mcp_supervisor.py - re-run setup on a tree to pick it up.

Why. The MCP list_modules tool answered from the server's own compile and missed most of what a require can reach, and the two Python supervisors duplicated what the watchdog already does in daslang.

What changes.

  • list_modules lists the C++ modules the binary carries, the daslib modules by file, and every registered require path; project_root runs it in a subtool with that root's manifest loaded eagerly.
  • The watchdog gains --stdio: it answers initialize and ping itself, spawns the server on the first tools/* call in --cwd, forwards one request at a time, and respawns a child that died before a request was delivered.
  • utils/mcp/setup.das writes the watchdog front into a tree's .mcp.json (the daslang binary as the program on Unix, the .cmd wrapper on Windows) and a dasherd entry when that tool exists; mcp_supervisor.py is deleted.
  • The LSP supervisor port is ledgered in utils/lsp/ROADMAP.md with test_lsp_protocol.das as its acceptance test.
  • utils/watchdog/REVIEW.das is a new gate: every event the folder emits is in the README's list, and every listed one is emitted.

Observable behavior.

  • list_modules on this tree: a handful of modules -> 32 C++ modules, 156 daslib modules, 307 require paths, with a json form.
  • list_modules with project_root: ignored -> that root's .das_module descriptors and their daslib files are in the listing.
  • MCP client start-up: a Python child between client and server -> bin/watchdog --stdio, no Python; the server dying mid-session -> the next call respawns it unseen.
  • .mcp.json rewrite by setup.das: the old file removed before the new one lands -> moved aside and restored when the replacement fails.
  • The watchdog log under --stdio: child_started carries the command, child_exited carries code and answered, and child_noise / client_noise carry a non-JSON line.

Where to look. utils/watchdog/stdio_front.das (the one-child-per-tick loop and the respawn rule), utils/mcp/tools/list_modules.das, utils/mcp/setup.das (front_command, emit_mcp_config).

Validation, claims, ledger

Validation

  • tests/watchdog/test_stdio_front.das drives the front through both hosts from a temp directory; the exe arm skips where bin/watchdog is not built (CI lanes that build only daslang).
  • The preflight fast tier is red on this box on the Vulkan-less compile sweep (a pre-existing local condition); the lanes it skipped were run by name.
  • Two woodpecker rounds. The first found the missing stdio_front.das install line and the unguarded .mcp.json replacement, both fixed here; the second ran on the fixed tip.
  • utils/mcp/test_setup.das drives emit_mcp_config into scratch roots: a fresh file, a merge that keeps another server and defer_loading, the dasherd entry, two refused non-object files, and a move-aside that cannot happen (the old file stays, the new one waits in .tmp).
  • tests/watchdog/test_stdio_front.das also compares the front's initialize result with the server's own, and pulls the tool catalog through the front, which is longer than one fgets chunk; truncating the front's line reader hangs that round.

Claims - stated, not tested

  • The Windows .mcp.json shape (cmd.exe /c daslang-mcp-msvc.cmd as the front's --program) is written by setup.das but not exercised on a Windows box in this PR; a break shows as the MCP server failing to connect after setup.das on Windows.
  • The ladder's last arm, the new file refused after the old one moved aside, is not induced; a break shows as setup.das reporting failure with .mcp.json gone and .mcp.json.bak present.
  • The handshake replay to a respawned child is not observable: the server answers tool calls without initialize, so no test distinguishes a dead replay.
  • The front's re-send of a request to a child that died before delivery, and its give-up after three such spawns, run only when a child dies between requests, which no test induces; a break shows as the client's next call answered with a JSON-RPC error instead of a result.

Not done

  • The LSP supervisor (utils/lsp/lsp_supervisor.py) stays Python; its port to the watchdog is ledgered in utils/lsp/ROADMAP.md.
  • The MCP server as an -exe target for the front's --program (utils/mcp/ROADMAP.md).

…ess registered - a module with a file name was compiled from das and is not one - the daslib files under the das root instead of the cwd, and the require paths descriptors registered (module/path, from for_each_registered_native_path), which the tool never showed; with project_root or load_modules the listing comes from an eager child (-ignore-manifest, the new eager argv flag), since a deferred C++ module is not a registered one until something requires it; the pretend root gains a second module folder whose descriptor registers the tree's UnitTest C++ module and a require path of its own, and test_tools proves list_modules, find_symbol and compile_check see both under project_root, with a plain and an eager child run of the consumer; tests/lsp gains the same fixture through validate.das with -project_root; the mcp roadmap's fixture item closes
…cess is the client's newline-delimited JSON-RPC pipe and the child is the server - initialize and ping answered locally so the client connects before any child exists, the first tools request spawning the child, in --cwd, with the client's initialize replayed, one request forwarded at a time, a child that died before delivery respawned with the request re-sent once and one that died while answering reported instead, since a re-sent tool call could run twice; the child's non-JSON stdout is logged as child_noise and carried in that error; the log goes to its file only, since stdout is the protocol, and its child_started and child_exited spell their keys as the supervisor's do; one child lifetime per tick, so the exe host's loop and the interpreter host's loop both serve it; mcp_supervisor.py goes, and setup.das writes .mcp.json itself - the watchdog exe beside the binary when built, the interpreter host otherwise, the vcvars launcher as the child on Windows - preserving every other server, the entry's defer_loading and the das-herd shim, and moving the old file aside until the new one is in place; the utils/REVIEW.das ban on an exe form of mcp and lsp stays, since both still run interpreted under a supervisor, and says so; utils/watchdog/REVIEW.das is a new gate holding the README's event list to what the folder emits; stdio_front.das joins the watchdog's install list; tests/watchdog/test_stdio_front.das drives a scripted session through both hosts from a directory that is not the tree, the server's own initialize result as the front's, a tool catalog longer than one fgets chunk, the server's shutdown tool as the death and the next call as the respawn; utils/mcp/test_setup.das drives the .mcp.json rewrite into scratch roots; the LSP endpoint's port to the same shape is ledgered in utils/lsp/ROADMAP.md with the protocol test as its acceptance test
Copilot AI lite review requested due to automatic review settings September 10, 2026 06:50

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.

🟡 Changes recommended

utils/watchdog/stdio_front.das unconditionally deletes JsonValue? values that can be null, which risks runtime errors during protocol noise handling.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR updates the daslang MCP tooling so list_modules reflects what require can actually reach (including registered require paths and project-root descriptors), and replaces the Python MCP stdio supervisor with a native bin/watchdog --stdio front that lazily spawns/respawns the MCP server.

Changes:

  • Extend list_modules to report builtin C++ modules, daslib modules, and registered require paths; add project_root/load_modules support via an eager subtool run.
  • Add watchdog --stdio mode (StdioFront) to act as the JSON-RPC pipe, spawning/respawning the MCP server and answering initialize/ping locally.
  • Update setup/docs/tests/install rules to use the watchdog front and remove utils/mcp/mcp_supervisor.py.
File summaries
File Description
utils/watchdog/watchdog.das Adds --stdio flag plumbing to the watchdog config.
utils/watchdog/stdio_front.das Implements the stdio JSON-RPC front that spawns/respawns the MCP server.
utils/watchdog/REVIEW.md Updates review contract for logging/event naming across supervisor + stdio front.
utils/watchdog/REVIEW.das Adds a review gate verifying emitted log events match the README list.
utils/watchdog/README.md Documents --stdio behavior and expands log event/field descriptions.
utils/watchdog/main.das Routes --stdio to the front and integrates front lifecycle into host entrypoints.
utils/REVIEW.das Updates supervised-tools/internal-reference exemptions after removing Python supervisor.
utils/mcp/tools/list_modules.das Expands module listing and adds eager subtool path for project-root scanning.
utils/mcp/tools/common.das Adds eager option to subtool argv/run helper to force -ignore-manifest.
utils/mcp/tests/_pretend_root/probe_consumer.das Adds a consumer script fixture for project_root resolution tests.
utils/mcp/tests/_pretend_root/modules/dasUnitTest/daslib/hello.das Adds a fixture module providing a symbol via a registered native path.
utils/mcp/tests/_pretend_root/modules/dasUnitTest/.das_module Adds a descriptor fixture registering a C++ module + require path under project_root.
utils/mcp/test_tools.das Adds coverage for new list_modules semantics and project_root behavior.
utils/mcp/test_setup.das Adds tests for .mcp.json emission/merge/move-aside behavior without Python.
utils/mcp/subtools/list_modules.das Adds subprocess entrypoint used for eager list_modules scanning.
utils/mcp/setup.das Writes .mcp.json entries that spawn watchdog --stdio instead of Python supervisor.
utils/mcp/ROADMAP.md Updates follow-ups after adding the project_root fixture and watchdog front.
utils/mcp/REVIEW.md Updates install/listing guidance now that .mcp.json is written by setup.das.
utils/mcp/registry_das.das Updates tool schema/handler to accept project_root and load_modules.
utils/mcp/README.md Documents watchdog-front .mcp.json shape and updated restart/ignore-manifest guidance.
utils/mcp/mcp_supervisor.py Removes Python-based MCP stdio supervisor.
utils/lsp/ROADMAP.md Updates references/plan to align with watchdog-front approach and new helper naming.
utils/CMakeLists.txt Updates commentary about supervised tools now that MCP uses watchdog front.
tests/watchdog/test_stdio_front.das Adds end-to-end tests for watchdog --stdio behavior and respawn semantics.
tests/README.md Adds entries for new tests/lsp/ and tests/watchdog/ test files.
tests/lsp/test_lsp_project_root.das Adds test verifying LSP validate subtool respects -project_root descriptors.
skills/mcp_tools.md Updates MCP skill docs for watchdog-front config and restart semantics.
CMakeLists.txt Installs stdio_front.das and removes mcp_supervisor.py from installed MCP files.
ci/smoke_test_bundle.sh Adjusts internal-reference exclusions from mcp_supervisor.py to setup.das.
.codex/config.toml.example Updates example MCP server config to use bin/watchdog --stdio.
Review details

Suppressed comments (1)

utils/watchdog/stdio_front.das:158

  • read_json may return null for a non-JSON line, but this block deletes js unconditionally. Guard the delete so noise lines don’t risk a null delete.
                var js = read_json(line, err)
                let ok = js != null
                unsafe {
                    delete js
                }
                if (ok) return line
  • Files reviewed: 30/30 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread utils/watchdog/stdio_front.das
@borisbat
borisbat merged commit bbfc467 into master Sep 10, 2026
35 checks passed
@borisbat
borisbat deleted the bbatkin/mcp-list-modules branch September 10, 2026 07:48
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