Stdio-first MCP servers exit the moment they see EOF on fd 0. Under the
launchd agent the daemon's stdin is /dev/null, so a server that also
speaks HTTP still shuts down seconds after binding its port, and there
was no way to ask xy for anything else.
Adds a per-server `stdin` mode: `inherit` (the default, unchanged),
`null`, or `keep-open`. Under `keep-open` the child gets a pipe whose
write end RealChild holds and never writes to, so a read blocks.
The handle has to live on RealChild rather than on the TokioChild:
`Child::wait()` opens with `drop(self.stdin.take())`, so leaving it
where tokio put it reproduces the original bug the instant supervision
starts.
Refs #2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TWLFEoRaRafJm1SpdhWQ6F
In KDL, a `{ }` block's children are newline- or semicolon-separated,
so a block like `restart { policy "always" backoff-initial "10ms" }`
written on one line parses as a SINGLE node named `policy` whose
remaining words become extra arguments, not sibling keys. The parser
only ever read the first argument of each child node, so every key
after the first was silently discarded with no error.
Add `single_arg`, applied to every restart/stop/wait-for key that
takes exactly one value (all except `wait-for`'s `args`, which
legitimately takes zero or more), so a one-line block with 2+ keys
now fails to parse instead of silently keeping defaults.
`optional_string_map` (the `env { }` block) has the identical trap
but is out of scope here: it returns a bare BTreeMap with no error
path and would need a signature change to report a config error.
A second `command` node silently overwrote the first via a scalar
Option<PathBuf>, and a second `args` node silently overwrote the first
via a plain Vec, so duplicate keys last-won instead of tripping the
exactly-one-condition rule. Track args as Option<Vec<String>> and
error immediately on a repeated command or args key.
Adds kdl_parse module with parse_server_config() that deserialises a
KDL document into ServerConfig, with full validation of name, types,
durations, and restart/stop blocks. Also derives Default on
RestartPolicy to satisfy clippy.