Skip to content

Add stop_server + status MCP tools; TDQS-grade tool descriptions - #49

Merged
cuihtlauac merged 2 commits into
mainfrom
mcp-stop-status
Sep 22, 2026
Merged

cuihtlauac merged 2 commits into
mainfrom
mcp-stop-status

Conversation

@cuihtlauac

Copy link
Copy Markdown
Member

Why

Glama scores this server 3.9/5.0 on TDQS. The per-tool feedback: update_host behavior 2/5 (update semantics undocumented), start_server behavior 3/5 (blocking/readiness unstated), execute missing timeout/return/error info — and server coherence 3/5: "no stop/status tool… no way to read hosts even though update_host implies a host registry". This PR addresses all of it: the three descriptions are rewritten and stop_server / status become real tools. stop_server also serves the session-handoff workflow (shut the approval daemon down at wrap-up).

Protocol: control actions

New optional action field on the wire request: exec (default), stop, ping. Compatibility rests on two existing facts: every Response is version-stamped, and validation rejects an empty pipeline before any prompt.

  • New clients send control requests with "pipeline": [] — an old daemon parses them (no deny_unknown_fields), rejects at validate without prompting the human, and its error reply still reveals its version, which the new tools use for a friendly "predates this feature" message.
  • Exec requests from new clients omit action entirely, staying byte-identical to the pre-1.1 wire format; old clients never send it and default to exec.
  • Control actions pass every gate an exec does (peer-uid, field sanitization, freshness, replay), so a captured stop request cannot be replayed later.

Daemon

  • stop: prints a dim notice on the TTY ("sudo-proxy stopped by ") — no approval prompt, since a same-UID peer can already SIGTERM the daemon, so a gate would add friction without a security boundary — replies ok, then shuts the accept loop down cleanly and removes the socket. The terminal window (and SSH tunnel, remotely) closes with it.
  • ping: replies ok/"pong" without prompting.
  • The shutdown flag becomes an Arc<AtomicBool> so handler threads can set it; main now cleans up the socket on clean return.

MCP tools

  • stop_server { host? } — "not running" is a non-error result; on success polls briefly for the socket to disappear and removes a leftover tunnel socket; against a pre-1.1 daemon returns an error asking for a manual q/Ctrl+C.
  • status { host? } — with no args, checks the local daemon plus every registry host; per host reports socket presence, readiness, live daemon version via ping, and registry metadata. Read-only except refreshing the hosts.json last-connected/version cache.
  • All three existing descriptions rewritten to document blocking behavior, timeout default/clamp, return format, denial/timeout errors, partial-update semantics, and auto-registration (the TDQS gaps).

Tests & docs

  • Protocol serde/validate tests (old-JSON compat both directions, empty-pipeline rules, control-field sanitization).
  • New server-level integration tests: stop shuts run down without a prompt; ping answers without a prompt and the daemon keeps serving; a stale stop is rejected and the daemon survives.
  • Introspection test now expects five tools; docs/mcp.md, docs/protocol.md, README updated.
  • Version 1.0.0 → 1.1.0 (tag to be applied on merge per CLAUDE.md).

Verified live: full status → stop_server → status cycle against a 1.1.0 daemon (clean exit, socket removed), and against a real 0.12.0 daemon — status reports "running (sudo-proxy 0.12.0, predates ping)", stop_server returns the manual-stop message, old daemon unaffected, no prompt ever shown.

Note: burst_connections_above_cap_get_busy_response is flaky when run standalone in --release on unmodified main as well — pre-existing, unrelated.

🤖 Generated with Claude Code

cuihtlauac and others added 2 commits September 15, 2026 16:40
Protocol: new optional `action` field on Request (exec|stop|ping,
default exec, omitted on the wire for exec). Control requests are sent
with an empty pipeline so a pre-1.1 daemon rejects them at validation —
before any prompt — and its version-stamped error reply lets new
clients detect the old peer. Control actions pass every gate an exec
does (peer uid, sanitization, freshness, replay).

Daemon: `stop` prints a notice on the TTY (no approval prompt), replies
ok, and shuts the accept loop down cleanly, removing the socket on the
way out; `ping` replies ok/pong without prompting. The shutdown flag is
now an Arc so handler threads can set it.

MCP: new `stop_server` and `status` tools ({host} optional). status
probes socket presence + readiness, then pings for the live daemon
version (refreshing the hosts.json cache); down hosts are a non-error
result. stop_server confirms shutdown and cleans up stale tunnel
sockets; both degrade gracefully against pre-1.1 daemons. All three
existing tool descriptions rewritten to document blocking behavior,
timeouts, return format, update semantics, and error cases (Glama TDQS
feedback).

Tests: protocol serde/validate coverage for actions, server-level
stop/ping/stale-stop integration tests, introspection tool list now
five tools. Docs: mcp.md, protocol.md, README. Version 1.0.0 -> 1.1.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The crates.io package installs four binaries (sudo-proxy, sudo-request,
pkexec-cache, sudo-proxy-mcp) but the MCP registry's cargo convention runs
the binary named after the crate — sudo-proxy, the CLI, not the MCP server.
There is no server.json field to select a specific binary, so probe-based
directory sites launch the wrong binary and their tools/list fails, listing
zero tools.

Add an mcpb package entry alongside cargo. An .mcpb bundle is a zip whose
manifest.json pins sudo-proxy-mcp as the entry point, so clients and probes
launch the MCP server directly.

- packaging/mcpb/manifest.json: MCPB 0.3 binary-server manifest
- packaging/mcpb/build-mcpb.sh: build the .mcpb from the musl binary
- server.json: mcpb package entry (fileSha256 injected at publish time)
- release.yml: build + minisign-sign + upload the .mcpb asset
- publish.yml: wait for the asset, inject its sha256 into server.json
  before mcp-publisher publish (registry HEAD-validates the URL)

Verified locally: the bundle builds, and an initialize + tools/list probe
against the bundled binary returns the tool set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cuihtlauac
cuihtlauac merged commit 2a7810f into main Sep 22, 2026
16 checks passed
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.

1 participant