The MCP tool
An agent debugging its own turn-taking bug needs the failure and the fix in one call, not a CLI transcript to parse. Hotato's scorer is one of twelve MCP tools: it scores the recording and attaches a fix block to every failing event.
Run the server
Optional MCP extra; CLI stays dependency-free. Zero-install:
uvx --from "hotato[mcp]" hotato-mcp # or, if installed python -m hotato.mcp_server
Runs over stdio; the scorer is voice_eval_run. Fully local; nothing leaves the machine.
Point a client at it
Claude Desktop and Cursor take the identical JSON block. Codex CLI takes the same command and args in TOML. Copy-paste exact:
Claude Desktop
Settings → Developer → Edit Config, claude_desktop_config.json:
{
"mcpServers": {
"hotato": {
"command": "uvx",
"args": ["--from", "hotato[mcp]", "hotato-mcp"]
}
}
}Cursor
Project-scoped .cursor/mcp.json, or user-scoped ~/.cursor/mcp.json:
{
"mcpServers": {
"hotato": {
"command": "uvx",
"args": ["--from", "hotato[mcp]", "hotato-mcp"]
}
}
}Codex CLI
~/.codex/config.toml:
[mcp_servers.hotato] command = "uvx" args = ["--from", "hotato[mcp]", "hotato-mcp"]
Common mistake: uvx hotato-mcp with no --from fails, since uv then looks for a package literally named hotato-mcp on PyPI. The console script lives inside the hotato distribution's mcp extra; use --from "hotato[mcp]" exactly as written above.
The scorer: voice_eval_run
Two modes: single recording or bundled battery.
| Parameter | Purpose |
|---|---|
stereo | Two-channel WAV (caller/agent). |
caller, agent | Two separate mono WAVs, one per party, vs one stereo file. |
suite | barge-in: bundled self-test. |
stack | livekit | pipecat | vapi | generic: tunes config-fix knob names. |
expect | yield: agent stops. hold: backchannel. |
onset_sec | Required for mono; optional for stereo. |
caller_channel, agent_channel | Default 0, 1. |
max_talk_over_sec, max_time_to_yield_sec | Optional pass/fail bounds. |
report_path | Optional. Also writes the self-contained HTML report there; the returned envelope carries report_path (absolute). Additive; the envelope is otherwise unchanged. |
MCP mode takes stereo, or two separate caller and agent files. The CLI's opt-in single-file mono path, hotato run --mono call.wav --diarize, is not exposed as an MCP parameter; use the CLI directly for a mixed mono recording.
Path safety: report_path and the input recordings
An MCP tool is driven by an LLM that may be summarizing untrusted content in the same conversation; a write to report_path or a read of stereo/caller/agent is exactly the primitive an injected instruction would target. Both are sandboxed before the file is touched:
- report_path: without
HOTATO_MCP_REPORT_DIRset, writes are confined to the OS temp directory. Set the env var to write reports elsewhere. An existing destination is only overwritten if it already carries the hotato report marker, so the tool can't clobber an arbitrary pre-existing file. - stereo, caller, agent: without
HOTATO_MCP_INPUT_DIRset, reads are confined to the OS temp directory, the server's working directory, and hotato's own bundled fixtures. Set the env var to score recordings from elsewhere.
A path that resolves outside the allowed root is a refusal, the same structured error shape as a bad flag, never a silent write or read. General network and data handling: Security and data handling.
The fix map
fix_class is one of two values, and the difference is what kind of problem the agent is looking at:
| fix_class | What it means |
|---|---|
config | A concrete knob for the named stack, the direction to move it, and the trade-off it makes. No upsell. |
engagement-control | A discrimination problem: telling a bid for the floor apart from a backchannel or side-speech not addressed to the agent. No single threshold settles it, so the fix points at the kind of layer needed instead of a knob. Vendor-neutral and numbers-free: no product, vendor, or component named. |
A lone backchannel false-stop, with no missed interruption elsewhere in the same battery, downgrades to a config fix: raise the words-to-interrupt threshold one step, with the trade-off stated inline (the same threshold that ignores “mhm” can also delay a one-word “stop”). engagement-control survives only when the battery fails on both axes at once, a missed real interruption and a backchannel false-stop: that pairing is what also earns a battery-level funnel pointer in the envelope, the one case no single threshold can win.
Measures barge-in, turn-taking, overlap, and backchannel timing, and states what it doesn't: no accuracy percentage, no speaker identification (a diarizer assigns anonymous SPEAKER_00/01, never an identity), no transcription, no emotion or intent detection, and no claim about any specific vendor's internal accuracy. The agent sees the ceiling of the claim, not a marketing number.
The fleet tools: a local control plane
Eleven tools drive a private, self-hosted fleet control plane: a workspace that registers voice agents, ingests calls, surfaces candidate moments (Level 1 in the evidence ladder), holds a human review queue, and runs manifest-bound before/after experiments. Eight read or propose; three are clone-scoped actions that recompute, never deploy. None of them touches production; deployment approval always stays a human gate.
| Tool | Scope | Does |
|---|---|---|
fleet_status | read-only | Workspace counts and job-queue stats. |
candidate_list | read-only | Top candidate moments awaiting a human label. Never labels. |
candidate_inspect | read-only | One candidate's onset, measured components (severity, input_health, recurrence, novelty, covered_by_contract), and trust findings. |
contract_list | read-only | Contracts registered in a workspace. |
trial_explain | read-only | A recorded trial's verdict, evidence tier, recommendation, and any pending human-gated action. |
experiment_status | read-only | A trial's current verdict, evidence tier, recommendation, and manifest hash. |
artifact_verify | read-only | Recomputes a contract bundle's canonical digest and reports authenticity, without trusting the stored verdict. Its bundle-path argument is named report_path in the schema; pass the contract's directory there, not a rendered HTML report. |
experiment_propose | read-only | A bounded baseline / one-lower / one-higher variant set with expected effects. Does not clone, apply, or deploy. |
experiment_create | clone-scoped | Precommits a trial manifest from a committed battery before any after-side capture, so the fixture set can't be cherry-picked to the result. |
experiment_run | clone-scoped | Recomputes a before/after trial from audio under the pinned manifest, offline, and records a recommendation. |
clone_cleanup | clone-scoped | Deletes a staging clone an experiment created. Never touches production. |
Same workspace the CLI drives with hotato fleet ..., default home ~/.hotato/fleet. Most tools take home and workspace_id (default "default"). Full guide: GUARDIAN-FLEET.md.
One envelope shape for the fleet tools
Every fleet-tool response, reads included, carries four keys so an autonomous caller parses one shape across all eleven:
| Key | Carries |
|---|---|
evidence_status | An evidence tier or authenticity value, for a tool that computed one. Otherwise null. |
refusal_reason | Why a request was declined. Otherwise null. |
artifact_digests | Content-addressed digests the response touched. Otherwise []. |
pending_irreversible_action | The exact human-gated action still pending, e.g. "deployment approval (human-gated)". Otherwise null. |
voice_eval_run keeps its own CLI-identical scorer envelope (schema_version "1") instead of these four keys; they belong to the fleet tools, not the scorer.
A candidate stays an unlabelled moment until a human labels it. experiment_run recomputes and recommends; it never routes production traffic. clone_cleanup only ever deletes a staging resource an experiment created. A trial that reaches improved still carries a pending_irreversible_action of deployment approval until a person approves it.
The tool surfaces candidate moments and their fix; a human still decides whether a flagged event is the one to act on. Server source: mcp_server.py.