The connector asked for a sudo password on every privileged call and
failed whenever nobody answered it, which is every unattended run.
- Always probe `sudo -n` first, even for aliases set to sudo="prompt".
`_sudo_prefix` used to elicit unconditionally, so a host granting this
user NOPASSWD still opened an Agent Inbox prompt; with no human there
it hit the client's 300s ELICITATION_DEADLINE, got back `cancel`, and
surfaced as "sudo password required (user declined or timed out)".
sudo refuses before running anything when it wants a password, so the
probe is side-effect free.
- Strip a leading `sudo` from `command` and turn it into sudo=true.
Agents write `exec(command="sudo systemctl restart x")`: with
sudo=false that ran a tty-less sudo, with sudo=true it nested
`sudo -S ... sudo ...` whose inner prompt had no tty either. Handles
-u/-n/-S/-E/-H/-i/-k/-p/--; an unknown flag leaves the command alone.
sudo_user now implies sudo=true.
- Run privileged commands as `sh -c '<command>'`, so `&&`, pipes and
redirections are elevated too instead of only the first word.
- Add SSH_MCP_SUDO_PASSWORD (optional, secret) for unattended runs. It
is consulted only after `sudo -n` proved a password is needed, so on a
NOPASSWD host it never lands in the command's own stdin.
- Actionable errors for every sudo failure mode, and a `hint` on a
nested sudo we could not peel off.
General review of the same server:
- Drain stdout and stderr together and make timeout_sec a real
wall-clock deadline. Both streams share one SSH channel window, so
reading stdout to EOF first stalled once a chatty stderr filled it.
Command stdin is now closed after the optional password.
- Queue messages that arrive while awaiting an elicitation reply instead
of discarding them, so a concurrent tools/call is not lost.
- Record the client's `elicitation` capability at initialize and fail
fast when it is absent rather than blocking on a prompt nobody can
answer.
- Tolerate null/string integer arguments (depth, max_results,
context_lines, timeout_sec).
- Realign the version across both manifests: fragment.json said 5/1.0.4
while connector.json said 2/1.0.1, so the feed was permanently ahead
of the installed version and offered an update forever.
Also lands the pending docs work: CONNECTOR_MANIFEST_GUIDE.md as the
single source of truth, docs/connector.manifest_guide.md retired to a
pointer, CLAUDE.md audited against the repo, compile.py docstring fixed,
and an opencode.json config.
CLAUDE.md and SKALD.md had drifted apart. CLAUDE.md still described a
hand-maintained connectors.json (pre-compile.py), an rsync deploy, 5
connectors, and a files[] that excluded connector.json.
CLAUDE.md is now the single working document, carrying every SKALD.md
section — full schemas for connectors.json / fragment.json /
connector.json, reserved enums, the auth/deliver/env/verify fields,
placeholder syntax, friendly tool names, icon conventions, file
integrity, local workflow and deploy — corrected against the actual
repo state: 18 connectors, the scripts/compile.py pipeline, and
connector.json included in the hashed files[].
It also names CONNECTOR_MANIFEST_GUIDE.md (repo root) as the
authoritative connector-authoring spec.