MCP stdio
Run handup mcp in the project directory. It uses rmcp and supports MCP versions 2025-11-25 and 2026-07-28. 2025-11-25 clients initialize, send notifications/initialized, then tools/list or tools/call. 2026-07-28 clients (Claude Code 2.1.285+) skip initialize: they may probe with server/discover and send the protocol version, clientInfo and client capabilities in each request’s _meta. tools/list returns the cache hints ttlMs: 0 and cacheScope: "public". No logs go to stdout.
MCP is the simplest way to use handup from any client, including omp: the model asks explicitly and no extension is needed. It does not intercept a client’s built-in approval prompts; omp’s native bridge extension that forwards those is optional.
MCP tools: request_approval, ask_question, check_request, wait_requests, cancel_request, list_requests.
request_approval: title (required), summary, kind, risk, previews [{type, path OR content OR email, lang?}], input (editable object, returned edited in decisionfields), options [{id,label,outcome,style?}], timeout (duration string or"none"), on_timeout (deny|expire|approve, timed requests only), dedupe_key (identical pending requests share one id), callback_url (callbacks), session, session_title, and wait (default true). Relative paths use the server cwd; files upload as immutable blobs. With a progressToken, blocking calls emit progress notifications every long-poll cycle.ask_question: question and allow_free_text (both required, even with choices), choices, session, session_title, wait. Example: {“question”:“Which region?”,“choices”:[“eu”,“us”],“allow_free_text”:true}. It creates akind: questionrequest with one question (idanswer) and Submit/Decline options. Userequest_approvalwithkind: "question"andinput.questionsfor several questions, multi-select or a timeout.check_request{“id”}: current decision JSON without waiting.wait_requests{“ids”:[…], “max_wait_seconds”?}: returns every decided request among ids at once, otherwise long-polls until the first decision ormax_wait_seconds(default 25, max 300, within omp’s 30s and Codex’s 60s tool timeouts). Result:{"decided":[decision JSON], "pending":[ids], "message"}. Act on each decision and call it again with the pending ids until none remain, before ending the turn: without a shell, answers reach the agent only this way.cancel_request{“id”}: retract a pending request you no longer need (plan changed, task abandoned). Never cancel to dodge a decision. Cancelling a request that is no longer pending is an error.list_requests{“status”?, “session”?, “agent”?, “limit”? (default 20, max 200)}: compact rows{id,status,title,kind,risk,created_at,…}to recover your own ids after context loss; pass your session id.
Decision JSON (MCP shape): {id, status, option, feedback, content_hash} plus fields (approvals; edited input) or answers (questions: answers.answer = {"selected":["eu"],"text":"…"}, text only when typed). It omits expires_at and other request fields; use handup status ID --json for the full request. Denials and declines return status: denied with optional feedback, isError=false. Blocking calls (wait true) stop after 300s and return status: pending with the id: pending is neither approval nor an answer. With wait=false save the id and call wait_requests until it is decided; the same id recovers the decision after an MCP reconnect, so never create a duplicate request when a client stops waiting.
Email drafts use {"type":"email","email":{…}} previews plus input {to,cc,bcc,subject,body}; see email drafts.
Example: {“title”:“Deploy staging”,“kind”:“command”,“previews”:[{“type”:“command”,“content”:“./deploy staging”}],“wait”:false}.
Desktop Run results
Section titled “Desktop Run results”For command requests, the human may choose Run or Run as admin in the
local desktop app. The resulting decision has status: "approved" and a
top-level run_result in MCP responses, including request_approval,
check_request and each decided entry from wait_requests. CLI wait JSON
also returns it at the top level; HTTP and CLI status JSON nest it under
decision.run_result.
run_result field |
Meaning |
|---|---|
exit_code |
Command exit status, or absent/null when it never started or a signal ended it |
stdout_tail, stderr_tail |
Redacted last bytes of each stream, at most 64 KiB each |
duration_ms |
Wall time in milliseconds |
truncated |
Earlier output was dropped from either tail |
elevated |
Execution used pkexec (Linux) or osascript’s administrator prompt (macOS) |
error |
Optional launch/authentication failure, timeout, cancellation or signal explanation |
A completed run returns an approve outcome regardless of exit code or error:
approval here means the human chose execution, not that it succeeded.
If nothing starts (including administrator authentication refusal), the claim
is released and the request stays pending; agent cancellation instead leaves
it cancelled. A result that cannot be submitted is reported in the desktop.
Inspect exit_code and error, consume the returned output and do not run
the command again, even if it failed. Ordinary approval without run_result
still permits the agent to execute the reviewed action. Native permission
hooks deny/block the original tool call with a run summary and appended human
feedback when a result is present, preventing a second execution; that block
is not a human denial. CLI ask --wait, wait, status and show return
exit 5 for decisions carrying a result, not the command’s own exit code.
Phones, browsers and the relay never execute commands; paired devices cannot
submit a result. See Desktop for Run, Cancel,
the 10-minute execution timeout and administrator setup.
The MCP initialize clientInfo.name (for 2026-07-28 clients, the
io.modelcontextprotocol/clientInfo name in request _meta) supplies
source.agent (fallback mcp); this is self-declared identity, not a verified
source.integration marker. If an initialized client advertises roots, handup
uses the first local file:// root for source.cwd, decoding escaped paths. A
roots error, no usable root, a response taking more than 500 ms, or a 2026-07-28
client (no roots/list) falls back to the server process directory. Preview paths
remain relative to the server process directory.
Both request_approval and ask_question accept optional session (stable ID)
and session_title (display name). Automatically pass your current ID and title
when known. The UI displays the title, falling back to the ID; session-scoped
allow rules still use only the ID. An absent title is omitted from request JSON.
Requests wait for the human by default: requests.default_timeout is none.
Omit timeout to use that configuration, or pass "none" to disable a configured
deadline. An explicit duration (for example "10m") opts into expiry; on_timeout
defaults to deny for timed requests. wait controls tool waiting, not request
expiry. A client tool timeout does not approve or expire the pending request.
Ask before destructive/irreversible actions, external publication or messages, costly resource use, credential changes/access, or ambiguous intent. Include the exact proposed action and its effects. Do not execute while pending. Only approved/answered permits the approved action; bind execution to content_hash and re-request if the action changes. A deny is a normal human answer, not a transport error. Read feedback, stop, and revise only when requested. Never retry the same request or treat expired, cancelled, or error as permission.
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"inspector","version":"1"}}}{"jsonrpc":"2.0","method":"notifications/initialized"}{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"request_approval","arguments":{"title":"Review deploy","wait":false}}}Tool cancellation stops waiting but preserves the pending request; use wait_requests to resume, or cancel_request to retract it. Daemon/API failures return isError=true, never approval. MCP path previews cannot read stdin. For content use UTF-8 text; use path for binary media.
Sources: https://modelcontextprotocol.io/specification/2025-11-25/server/tools and https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress