service-manager 0.11 writes Disabled: true into every plist carrying KeepAlive, and our RestartPolicy::Always guarantees KeepAlive, so every agent we installed was born disabled. launchctl load honours the key while still exiting 0, so install reported success on an agent that would never start — not then and not at the next login. service::start now removes the Disabled key itself before loading, via a pure enable_plist() that rewrites nothing when the key is absent and preserves every other key, including the EnvironmentVariables PATH snapshot. That makes start self-healing for plists left disabled by an earlier build. The crate's own start() is still not used, since without Disabled it degrades to launchctl start, which fails on an unloaded job. Because launchctl load exits 0 on failure, start also checks a post-condition: it asks launchd whether the job now exists and reports a diagnostic if it does not. stop keeps no such check, since a benign unload of an already-stopped job also prints a failure while exiting 0. status now treats the plist on disk as the definition of installed, as the spec says: a plist that exists but is not loaded reports stopped with its program, PATH and snapshot date intact instead of collapsing to not-installed with every field cleared. That is precisely the state the Disabled bug left users in, so it is the state status most needs to describe. status also gains the log path the spec always listed, and the daemon's own log is renamed xy.log -> daemon.log so a supervised server named xy cannot share a file, and two rotation counters, with the daemon. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EGntTHCW3sEPy1VBRopNNp
49 lines
1.5 KiB
Markdown
49 lines
1.5 KiB
Markdown
# xy — HTTP MCP server supervisor
|
|
|
|
Daemon + CLI that launches and supervises HTTP-based MCP servers.
|
|
|
|
## Build
|
|
|
|
cargo build --release
|
|
|
|
## Run
|
|
|
|
target/release/xy daemon # foreground
|
|
|
|
Drop a server definition into `$XDG_CONFIG_HOME/xy/servers/<name>.kdl`
|
|
(see `examples/insikt.kdl`) and `xy reload`.
|
|
|
|
Commands:
|
|
|
|
xy list
|
|
xy status <name>
|
|
xy start <name|--all>
|
|
xy stop <name|--all>
|
|
xy restart <name|--all>
|
|
xy reload
|
|
xy logs <name> [--tail N] [--follow]
|
|
|
|
Exit codes: 0 success, 1 operational error, 2 daemon unreachable, 3 config invalid.
|
|
|
|
## Start on login (macOS)
|
|
|
|
xy service install # write the LaunchAgent, load it, start the daemon
|
|
xy service uninstall # unload and remove the agent
|
|
xy service start # load an installed agent
|
|
xy service stop # unload until the next login
|
|
xy service status # show agent state
|
|
|
|
`xy service install` snapshots the current `PATH` into the agent, because a
|
|
launchd agent otherwise inherits only `/usr/bin:/bin:/usr/sbin:/sbin` and
|
|
supervised servers would not find their toolchains. Re-run with `--force`
|
|
after installing a new toolchain to refresh the snapshot.
|
|
|
|
`xy service stop` lasts until the next login. To disable start-on-login
|
|
permanently, use `xy service uninstall`.
|
|
|
|
The daemon writes to `$XDG_STATE_HOME/xy/logs/daemon.log`. Failures that happen
|
|
before the daemon starts logging — a missing binary, a malformed plist — are
|
|
visible only to launchd:
|
|
|
|
launchctl print gui/$UID/se.aceofba.xy
|