Skip to content

fix(packaging): ship mcp_serve.py + mini_swe_runner.py — missing from py-modules (closes #80)#81

Merged
PowerCreek merged 1 commit into
mainfrom
issue-80-mcp-serve-packaging
May 24, 2026
Merged

fix(packaging): ship mcp_serve.py + mini_swe_runner.py — missing from py-modules (closes #80)#81
PowerCreek merged 1 commit into
mainfrom
issue-80-mcp-serve-packaging

Conversation

@PowerCreek

Copy link
Copy Markdown

Closes #80 layer 1 (packaging). Layer 2 (runtime auto-start of mcp_serve when worker spawns) deferred to a follow-up issue.

Root cause (Layer 1)

`[tool.setuptools]` `py-modules` list omitted `mcp_serve` (and `mini_swe_runner`). Setuptools' `packages.find` only picks up Python PACKAGES (dirs with `init.py`), not top-level modules. So both were silently dropped from the wheel → `from mcp_serve import run_mcp_server` raises ImportError at runtime → no MCP server can be started from the installed wheel.

Fix

Add `mcp_serve` + `mini_swe_runner` to the py-modules list.

End-to-end verification

Built the wheel + inspected zip contents:
```
mcp_serve.py in wheel: True
mini_swe_runner.py in wheel: True
```

Test plan

  • 5 contract tests in `tests/test_packaging_modules.py`:
    • `test_mcp_serve_in_py_modules` — the actual fix
    • `test_mini_swe_runner_in_py_modules`
    • `test_every_root_py_listed_in_py_modules` — catches the next module someone adds at root without updating py-modules (the regression-catcher for this entire bug class)
    • `test_no_dangling_py_modules_entries` — catches stale entries after a rename
    • `test_mcp_serve_actually_exists_at_root` — defensive
  • End-to-end `pip wheel` build inspection confirms both modules now ship.

Layer 2 (NOT in this PR — deferred)

Even with mcp_serve.py shipped, the MCP server doesn't auto-start when a worker session spawns. Architecture:

  • `mcp_serve.py` = MCP server (registers G2/G3/G4 tools)
  • `tools/mcp_tool.py` = MCP client (worker uses this to connect to running MCP servers per `.mcp.json` config)
  • `hermes_cli/mcp_config.py::run_mcp_server` = function that actually starts the server, only invoked from `hermes mcp serve` subcommand

So for a worker to access grafted_context_fetch / silo_query / confer_run / file_issue / lane_h_*:

  1. Operator must explicitly start mcp_serve in background (`hermes mcp serve &` or similar)
  2. Worker's `.mcp.json` must reference that server endpoint
  3. Worker session must successfully MCP-client-connect

None of this auto-happens for polynomial-explorer (or any vertical) worker today. Operator-side wiring is undocumented + undelivered. I'll file the Layer 2 issue after this lands.

Combined deploy chain (post-#80)

# PR Status What it fixes
1 devagentic#222 merged `/graphql` on port 6071
2 hermes-agent#77 merged plugin.yaml ships in wheel
3 hermes-agent#79 open `register(ctx)` in plugin `init.py`
4 hermes-agent#80 (this) open `mcp_serve.py` ships in wheel
5 follow-up (after this) not filed auto-start mcp_serve + worker .mcp.json wiring

@PowerCreek PowerCreek merged commit 2c21e52 into main May 24, 2026
@PowerCreek PowerCreek deleted the issue-80-mcp-serve-packaging branch May 24, 2026 07:32
PowerCreek added a commit that referenced this pull request May 24, 2026
Layer 2 of the #67 cascade: post-#79 (register-ctx) + #81 (mcp_serve
packaging), the bundled MCP server now ships and the G2/G3/G4 plugins
load — but no MCP server entry pointed at it, so workers booted with
zero MCP-side tools. This adds the missing wiring.

Three edits:

1. mcp_serve.py — `if __name__ == "__main__":` block so
   `python -m mcp_serve` runs the existing stdio server. Honors
   `--verbose`/`-v` for subprocess debugging.

2. hermes_cli/mcp_autowire.py (new) — ensure_internal_mcp_server()
   idempotently inserts a `hermes-internal` entry under `mcp_servers`
   in the active profile config. Uses sys.executable + [-m, mcp_serve]
   so the spawn survives venv-not-on-PATH installs (the typical
   container-deploy shape). Opt-out via HERMES_DISABLE_INTERNAL_MCP=1.

3. tui_gateway/entry.py — calls ensure_internal_mcp_server() BEFORE
   the read_raw_config()::mcp_servers gate, so the gate sees the
   freshly-written entry on the same boot and discover_mcp_tools()
   picks it up.

Issue body proposed `.mcp.json` (Claude-Code-style separate file);
the actual config file is ~/.hermes/config.yaml under the
`mcp_servers` key (the same location `tools/mcp_tool.py` reads via
`hermes_cli.config.load_config`). This patch targets the real config.

Tests (tests/test_internal_mcp_autowire.py — 20 cases):
- HERMES_DISABLE_INTERNAL_MCP truthy variants all opt out
- Idempotent: re-call is a no-op
- Existing operator MCP servers (Linear/Notion/...) preserved
- Creates `mcp_servers` key when absent
- Command is sys.executable (not literal "python")
- Args are [-m, mcp_serve] (catches the spawn-shape regression)
- Source-level: mcp_serve has __main__ guard invoking run_mcp_server
- Source-level: entry.py calls ensure_internal_mcp_server() BEFORE
  the read_raw_config gate (catches ordering regression)

Smoke verified: `timeout 3 python3 -m mcp_serve < /dev/null` → exit 0
(stdio handshake; closes loop cleanly on stdin EOF).

After container rebuild + worker spawn, poly-explorer should have
end-to-end tool access (final layer of the cascade started at #67).
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.

CRITICAL: mcp_serve.py absent from wheel + no auto-start path → MCP-only plugins (G2/G3/G4) inaccessible to workers

1 participant