# 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/.kdl` (see `examples/insikt.kdl`) and `xy reload`. Commands: xy list xy status xy start xy stop xy restart xy reload xy logs [--tail N] [--follow] Exit codes: 0 success, 1 operational error, 2 daemon unreachable, 3 config invalid. ## Waiting on a dependency A server can declare a precondition that must hold before it is started: wait-for { path "~/.orbstack/run/docker.sock" timeout "180s" interval "1s" } Exactly one condition per block — `path "

"` (the path exists), `tcp ""` (a connection succeeds), or `command ""` with optional `args` (the process exits 0). `timeout` defaults to 120s and `interval` to 1s. While waiting the server reports state `waiting`, and `xy status ` shows which condition it is blocked on and for how long. If the timeout expires the server is marked `failed` and is never spawned. Waiting does **not** consume the restart budget: a slow dependency costs patience, not retries. This is what stops a Docker-backed server from being marked failed at login while the Docker daemon is still starting. ## Servers that read stdin MCP servers are stdio-first by convention, and several that also speak HTTP still start their stdio transport unconditionally. Under the launchd agent the daemon's stdin is `/dev/null`, so such a server sees EOF on its first read and shuts down seconds after binding its port. `stdin` picks what the child gets on fd 0: stdin "keep-open" - `inherit` (default) — the child inherits the daemon's stdin. - `null` — `/dev/null`; a read sees EOF immediately. - `keep-open` — a pipe that `xy` holds open for the child's lifetime and never writes to, so a read blocks instead of seeing EOF. Use `keep-open` for a server you want supervised in HTTP mode that insists on running its stdio transport anyway. ## 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