Summary
Maka can negotiate MCP 2026-07-28, but it deliberately rejects the
revision's input_required result today. This means an MCP Tool cannot pause
to ask the user for missing information and then continue the same logical
operation.
I propose supporting the first user-facing MRTR slice:
tools/call may return resultType: "input_required";- the initial supported embedded request is form-mode
elicitation/create; - Runtime Host projects the form through its existing durable Interaction
authority; - Desktop and TUI render and answer that same Host-owned Interaction; and
McpClientManager retries the original Tool call with the collected bare
inputResponses, a fresh JSON-RPC request id, and a byte-exact echo of the
opaque requestState.
This proposal is based on
064c3b299.
The scope is intentionally narrower than “all MRTR.” URL-mode elicitation,
sampling, roots, prompts/get, resources/read, and process-crash resumption
have different authority and security requirements and are explicit non-goals
for the initial rollout.
Is this an MCP 2026-07-28 feature?
Yes. SEP-2322 replaces an in-flight server-to-client request with a stateless
multi-round exchange:
Client -> Server: tools/call (JSON-RPC id 1)
Server -> Client: input_required(inputRequests, requestState)
Client -> User: collect the requested input
Client -> Server: tools/call (JSON-RPC id 2,
original params,
inputResponses,
exact requestState)
Server -> Client: complete Tool result
The second tools/call is a new protocol request, not a held-open continuation
of id 1. Maka should preserve one logical Tool invocation and one Runtime
Interaction lineage across those protocol legs without pretending the first
HTTP/SSE request remains alive.
Current behavior and problem
McpClientManager currently configures SDK v2 with
inputRequired.autoFulfill: false. A modern input_required result is then
normalized to McpToolCallError("server returned an unsupported deferred tool result"). The regression test also asserts that Maka sends exactly one
tools/call and does not retry silently.
This is the correct safe default while Maka has no end-to-end interaction
owner. Enabling SDK auto-fulfilment alone would not provide one:
- the MCP connection lives in the Desktop or TUI client process;
- the model Tool executes under Runtime Host;
- Client Capability currently carries call, progress, cancellation, and final
result frames, but no nested user-interaction round trip; and - Runtime Host is already the canonical owner of pending questions, answers,
cancellation, restart closure, and surface projection.
The missing feature is therefore not just a result union in @maka/mcp. It is
an end-to-end continuation across these existing owners:
Model Tool call
|
v
ToolRuntime / Runtime Host Interaction authority
|
v
Client Capability invocation
|
v
Desktop or TUI MCP provider -> McpClientManager -> MCP server
The post-V3 roadmap tracker #4329 explicitly leaves input_required handling
out of its scope, so this focused interaction/continuation design does not
duplicate that roadmap.
User scenario
For example, a deployment Tool may discover during execution that the target
environment is missing:
{
"resultType": "input_required",
"inputRequests": {
"target": {
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Choose the deployment target",
"requestedSchema": {
"type": "object",
"properties": {
"environment": {
"type": "string",
"enum": ["staging", "production"]
},
"confirm": { "type": "boolean" }
},
"required": ["environment", "confirm"]
}
}
}
},
"requestState": "opaque-server-state"
}Maka shows a Host-owned form that identifies the Client Capability provider,
MCP server, and Tool. If the user accepts with staging and true, the MCP
adapter retries the original call with:
{
"name": "deploy",
"arguments": { "artifact": "build-42" },
"inputResponses": {
"target": {
"action": "accept",
"content": { "environment": "staging", "confirm": true }
}
},
"requestState": "opaque-server-state"
}The retry receives a fresh JSON-RPC id. requestState never enters the model
context, renderer-owned state, Runtime Interaction payload, logs, or telemetry.
Authority boundaries
The design keeps one owner for each fact or side effect:
| Concern | Owner |
|---|
MCP wire decoding, original params, round count, inputResponses, and exact requestState echo | McpClientManager |
| Live MCP connection and the actual retry side effect | McpClientManager plus the selected SDK transport |
| Client Capability invocation identity and provider connection | Runtime Host Client Capability coordinator/broker |
| Pending user form, canonical answer, stale-answer rejection, and closure | Runtime Host Interaction authority |
| Tool/Turn cancellation | ToolRuntime and the exact hosted Run binding |
| Form rendering and user gestures | Desktop/TUI adapters over the Host projection |
An MCP server supplies untrusted form copy and schema. A Client Capability
provider supplies untrusted display provenance. Runtime Host binds both to the
already-authorized exact invocation; neither can mint a Tool, grant a
permission, select another Session, or resume another invocation.
Proposed design
1. Add a provider-neutral form Interaction
Do not flatten an MCP form into the current multiple-choice question kind.
The existing question contract permits a small list of string answers, while
MCP form elicitation has typed strings, numbers/integers, booleans,
single-select and multi-select values, required fields, defaults, and bounded
validation constraints.
Extend the existing core Interaction union with a repository-owned form shape,
illustratively:
typeInteractionFormField=|{kind: "string";name: string;label: string;required: boolean;description?: string;default?: string;minLength?: number;maxLength?: number;format?: "email"|"uri"|"date"|"date-time"}|{kind: "number"|"integer";name: string;label: string;required: boolean;description?: string;default?: number;minimum?: number;maximum?: number}|{kind: "boolean";name: string;label: string;required: boolean;description?: string;default?: boolean}|{kind: "single_select";name: string;label: string;required: boolean;options: readonlyInteractionFormOption[];default?: string}|{kind: "multi_select";name: string;label: string;required: boolean;options: readonlyInteractionFormOption[];default?: readonlystring[];minItems?: number;maxItems?: number};typeInteractionFormValue=string|number|boolean|readonlystring[];interfaceInteractionFormRequest{kind: "form";toolUseId: string;message: string;requester: InteractionRequesterProjection;fields: readonlyInteractionFormField[];}typeInteractionFormAnswer=|{kind: "form";action: "accept";values: Readonly<Record<string,InteractionFormValue>>}|{kind: "form";action: "decline"|"cancel"};These names are illustrative. The implementation should extend the existing
InteractionRequest, InteractionAnswer, canonical outcome, store decoder,
Host coordinator, and session projection rather than create an MCP-specific
store or queue.
Core owns exact-shape decoding and repository caps for request bytes, answer
bytes, field count, enum options, labels, and descriptions. Runtime Host
validates an accepted value against the stored canonical form before committing
the answer. The MCP adapter validates it again against the original MCP schema
before sending it to the server.
MCP exposes one coarse elicitation.form capability rather than separate
claims per primitive or field count. Maka must therefore implement the complete
bounded form subset the revision specifies before advertising it; it must not
ship a one-field or string-only claim under the same capability bit.
The form UI must show the requester provenance and give three distinct actions:
submit (accept), explicitly decline (decline), and dismiss (cancel). It
must allow review and edits before submit.
2. Add one nested Interaction seam to Client Capability
Extend a live Client Capability invocation with one provider-neutral nested
Interaction request/response pair. The exact wire names can follow the existing
protocol naming, for example:
// Client -> Host, only for an accepted live invocation.interfaceClientCapabilityInteractionRequestFrame{kind: "client.capability.interaction_request";invocationId: string;interactionId: string;request: ClientCapabilityFormRequest;}// Host -> the same Client connection and invocation.interfaceClientCapabilityInteractionResultFrame{kind: "client.capability.interaction_result";invocationId: string;interactionId: string;result: ClientCapabilityFormResult;}ClientCapabilityProvider.call() receives a callback such as
requestInteraction(form). The client channel sends the request frame and
awaits the matching Host result. The Host broker projects it through the same
ToolRuntime form callback that local Tools use.
The broker, not the provider, binds the form to the current Session, Turn,
Tool call, invocation, registration, and connection. It allows at most one
pending nested Interaction per invocation. Duplicate, late, cross-connection,
or post-cancellation frames fail closed.
The frame's interactionId is only provider-local reverse-call correlation.
ToolRuntime creates the canonical Runtime Interaction id; an untrusted
provider cannot choose or collide with a Host identity.
This seam is generic because the ownership problem is generic: a Tool owned by
a remote Client Capability provider may need user input while Runtime Host owns
the execution. MCP is the first adapter using it. The seam is not a generic
arbitrary callback tunnel; it accepts only the closed repository form contract.
3. Drive MRTR manually inside McpClientManager
Keep ClientOptions.inputRequired.autoFulfill disabled. The manager opts an
individual tools/call into SDK manual mode and owns a bounded loop:
- send the original call with its validated Tool definition;
- accept either a complete result or a validated
InputRequiredResult; - if only
requestState is present, retry after a small abortable pacing
delay without opening a user form; - reject unsupported embedded methods or elicitation modes before publishing
an Interaction; - project every form-mode
elicitation/create through the injected form
handler and collect the bare ElicitResult values under the original keys; - retry the original method and unchanged method-specific params with the current round's
inputResponses and byte-exact requestState on a fresh request id; and - repeat up to a repository-owned round cap, then fail with one stable
McpToolCallError.
The manager retains the original validated Tool binding across every leg. A
tool refresh, reconnect, server replacement, provider loss, abort, or deadline
must not silently move the continuation to another binding or connection.
Capability advertisement is per eligible modern call, not a global Client
claim. The dual-era SDK Client must not add elicitation to its base capabilities,
because a legacy negotiation would then falsely advertise support for legacy
push-style elicitation. When and only when a modern tools/call has a complete
form handler, the manager merges { elicitation: { form: {} } } into that
request's self-describing _meta client capabilities; every retry carries the
same claim. A legacy call or a call without the form handler advertises no
elicitation capability and retains the current rejection behavior.
Multiple input requests in one result are fulfilled in deterministic key order
with a bounded count. Responses are sent together on the next retry. An
unsupported sibling request fails the whole round before any partial retry.
4. Compose Desktop and TUI through the same path
Desktop's native capability provider and TUI's MCP capability provider both
adapt the Client Capability requestInteraction callback into the
McpClientManager.callTool() input handler.
Desktop renderer and TUI read the same active Interaction projection and send
the same interaction.answer operation already used for Host-owned questions.
No surface keeps a second authoritative pending-form list. Renderer/TUI reload
that does not retire the exact Client Capability provider can rehydrate the
pending form from the canonical Host snapshot.
Lifecycle and failure semantics
Execution timeout versus user wait
The current Client Capability invocation has a fixed execution timeout. A
human Interaction must not consume that entire provider execution budget.
When an accepted invocation publishes a valid nested Interaction, the broker
enters awaiting_interaction and suspends its provider-execution timer. After a
canonical answer is delivered, it resumes with a fresh bounded provider leg.
Round count, a bounded pending Interaction, and Tool/Turn cancellation prevent
an unbounded loop. Global Stop aborts the Interaction and the MCP call; it does
not synthesize an MCP cancel answer or send another Tool request.
The form's explicit cancel action is different: it is a user response and is
returned to the MCP server as { action: "cancel" }. decline is likewise
returned as { action: "decline" }. The server may then finish, fail, or ask a
later bounded round.
Provider or connection loss
Provider disconnect, registration retirement, server reconnect/replacement,
or invalidation of the frozen Tool binding aborts the pending form and settles
the Tool through the existing Client Capability lost/outcome-unknown rules.
The answer must never be replayed to a replacement provider or MCP connection.
If a provider returns a final result while its nested Interaction is still
pending, the broker closes that Interaction and rejects the inconsistent
invocation rather than leaving an orphan or accepting both outcomes.
Host and surface restart
The initial implementation preserves current Runtime Interaction recovery:
- a view-only reconnect within the same Host epoch can query and answer the
pending canonical form (for example, a Desktop renderer reload while the
main-process provider remains alive); - restarting a TUI that also owns the MCP provider retires that provider and
therefore cannot resume its old invocation; - a Runtime Host restart closes a stored pending form as
host_restarted; - the old in-memory Client Capability invocation and MCP continuation are not
reconstructed; and - Maka does not automatically repeat a Tool call whose external outcome may be
unknown.
Crash-resuming MRTR would require a durable invocation record containing the
exact Tool binding, original args, provider identity, round state, delivery
acknowledgements, and a safe replay policy. Adding that state only for MRTR
would conflict with the existing Tool recovery boundary, so it is deferred to
a broader durable Tool continuation design.
Security and privacy requirements
- Advertise MCP form elicitation capability only on a modern
tools/call whose
complete manager/provider/Host/surface path is installed. Never advertise it
globally to a dual-era Client or on a legacy connection. - The UI identifies both the Client Capability provider and the claimed MCP
server/Tool. Claimed server copy is display-only and never authorization. - Form-mode copy and schema are untrusted. Decode exact shapes, enforce flat
primitive schemas and repository limits, and reject unknown/unsupported
forms before publication. - The form UI warns that passwords, API keys, access tokens, and payment
credentials must not be entered. MCP requires sensitive flows to use URL
mode, which is out of scope here. - Accepted values do not enter model-visible Tool arguments. They are sent only
to the exact MCP continuation; a later Tool result may still contain data the
server chooses to return and follows existing result handling. - Form requests and answers are Runtime control facts, not user messages, and
are not added to model context as conversational content. - Canonical form requests and answers follow the existing InteractionStore
retention boundary. They are not copied into logs or telemetry. requestState is opaque and untrusted. Maka does not parse, mutate, display,
persist, log, or use it for authorization; it only echoes the exact string to
the same manager-owned continuation.- The manager bounds
requestState bytes, input-request count, total embedded
request bytes, and rounds before retaining state or opening a Host
Interaction. - Stop, stale answer, duplicate answer, cross-Session answer, provider loss,
and round-limit exhaustion fail closed with no extra MCP retry. - An intermediate
input_required result does not settle the Runtime Tool
operation or its durable T1/T2 boundary. Only the final complete result (or
terminal failure) settles the one logical Tool invocation.
Rollout
This can be implemented as a stack without creating a second authority:
- Provider-neutral form Interaction: extend Core, Runtime,
InteractionStore, Host coordination/projection, Desktop, and TUI; prove
canonical answer/closure/reload behavior with a synthetic local Tool. - Client Capability nested Interaction: add the bounded reverse frames and
provider callback; prove exact invocation/connection binding, timer
suspension, cancellation, and provider-loss behavior with a synthetic
Client Capability Tool. - MCP form MRTR adapter: add the manual
McpClientManager loop, capability
advertisement, Desktop/TUI composition, modern MCP fixtures, and end-to-end
acceptance coverage.
Each child PR should be independently reviewable and remain stacked until its
parent lands. None should replace Runtime Host Interaction authority with a
surface-owned modal or an MCP-specific store.
Acceptance criteria
- A modern MCP Tool can return one form elicitation, receive an accepted
typed answer, and complete through a second tools/call. - The retry uses a fresh JSON-RPC id, deep-equal original Tool name/arguments,
current-round bare inputResponses, and byte-identical requestState. - A two-round Tool preserves one logical Runtime Tool call and produces one
final Tool result. accept, decline, and cancel remain distinct end to end.- Multiple bounded form requests in one round are collected under the exact
server keys; malformed, unknown, or over-limit input fails before retry. - A result containing only
requestState can retry, but the round cap still
terminates a non-progressing server. - Global Stop while a form is pending sends no retry and leaves no pending
Host Interaction or Client Capability invocation. - Provider disconnect, registration replacement, MCP reconnect, and stale
Tool binding cannot receive a previously committed answer. - A view-only reconnect in the same Host epoch rehydrates the form while the
exact provider remains live. TUI/provider restart and Host restart close the
old continuation and do not replay the Tool; Host restart records
host_restarted. - The provider execution timeout is suspended only while the exact canonical
Interaction is pending and is re-armed after settlement. - Unsupported URL elicitation, sampling, roots,
prompts/get, and
resources/read fail with stable local errors and no partial retry. requestState is absent from Runtime events, Interaction records, IPC/UI
payloads, logs, telemetry, and model-visible output.- Legacy MCP behavior and a modern Tool that returns a complete result remain
behaviorally unchanged at the manager boundary. - Desktop and TUI pass the same real-server end-to-end form fixture.
- A dual-era auto connection advertises form elicitation on eligible modern
Tool calls but never advertises legacy push elicitation after falling back. - Provider final-result, failure, cancellation, and release paths close every
nested Interaction; no invocation can settle while leaving an orphan form.
Non-goals
- legacy-era push-style server-to-client elicitation;
- URL-mode elicitation or opening external authorization/payment pages;
- MCP sampling or roots input requests;
- MRTR for
prompts/get or resources/read; - MCP Tasks, Apps, or a generic workflow engine;
- nested objects, arbitrary JSON Schema, or secret-entry fields;
- changing Tool authorization, sandbox, or Client Capability trust policy;
- exposing
requestState to the model or user; - resuming an accepted Client Capability/MCP side effect after Host or provider
process restart; or - a second surface-local Interaction store.
Alternatives considered
Enable SDK auto-fulfilment and register handlers
The SDK driver correctly handles fresh request ids and retry fields, but its
handlers are registered on the shared Client while Maka's Host continuation is
per Tool invocation. Routing concurrent calls would require hidden ambient
context or serializing an entire MCP connection. The SDK driver also owns the
retry internally, so McpClientManager cannot place its current Tool-binding,
connection-generation, and provider-liveness fences immediately before every
new network side effect. Manual mode still uses the SDK's modern wire decoder;
it keeps the small retry policy explicit at Maka's existing manager seam.
Show a modal directly in Desktop/TUI
This is smaller locally but creates separate pending state per surface, cannot
work consistently with a remote Runtime Host, and can deliver an answer after
the authoritative Tool invocation has disappeared.
Return a Tool error and let the model ask the user/retry
That changes a protocol continuation into a new model decision, cannot safely
preserve opaque requestState, may expose protocol state to the model, and can
repeat side effects or choose different arguments.
Reuse the current multiple-choice question by stringifying fields
This loses numeric/boolean/enum types, validation constraints, defaults, and
the distinct accept/decline/cancel actions. A small closed form contract is
deeper and safer than MCP-specific encoding conventions inside question text.
Persist a complete MRTR continuation immediately
This promises crash recovery before Maka can prove whether an accepted remote
Tool leg is safe to replay. The initial design instead follows the current
host_restarted close/fail-closed boundary and can be deepened with the general
Tool continuation architecture later.
References
Summary
Maka can negotiate MCP
2026-07-28, but it deliberately rejects therevision's
input_requiredresult today. This means an MCP Tool cannot pauseto ask the user for missing information and then continue the same logical
operation.
I propose supporting the first user-facing MRTR slice:
tools/callmay returnresultType: "input_required";elicitation/create;authority;
McpClientManagerretries the original Tool call with the collected bareinputResponses, a fresh JSON-RPC request id, and a byte-exact echo of theopaque
requestState.This proposal is based on
064c3b299.The scope is intentionally narrower than “all MRTR.” URL-mode elicitation,
sampling, roots,
prompts/get,resources/read, and process-crash resumptionhave different authority and security requirements and are explicit non-goals
for the initial rollout.
Is this an MCP
2026-07-28feature?Yes. SEP-2322 replaces an in-flight server-to-client request with a stateless
multi-round exchange:
The second
tools/callis a new protocol request, not a held-open continuationof id 1. Maka should preserve one logical Tool invocation and one Runtime
Interaction lineage across those protocol legs without pretending the first
HTTP/SSE request remains alive.
Current behavior and problem
McpClientManagercurrently configures SDK v2 withinputRequired.autoFulfill: false. A moderninput_requiredresult is thennormalized to
McpToolCallError("server returned an unsupported deferred tool result"). The regression test also asserts that Maka sends exactly onetools/calland does not retry silently.This is the correct safe default while Maka has no end-to-end interaction
owner. Enabling SDK auto-fulfilment alone would not provide one:
result frames, but no nested user-interaction round trip; and
cancellation, restart closure, and surface projection.
The missing feature is therefore not just a result union in
@maka/mcp. It isan end-to-end continuation across these existing owners:
The post-V3 roadmap tracker #4329 explicitly leaves
input_requiredhandlingout of its scope, so this focused interaction/continuation design does not
duplicate that roadmap.
User scenario
For example, a deployment Tool may discover during execution that the target
environment is missing:
{ "resultType": "input_required", "inputRequests": { "target": { "method": "elicitation/create", "params": { "mode": "form", "message": "Choose the deployment target", "requestedSchema": { "type": "object", "properties": { "environment": { "type": "string", "enum": ["staging", "production"] }, "confirm": { "type": "boolean" } }, "required": ["environment", "confirm"] } } } }, "requestState": "opaque-server-state" }Maka shows a Host-owned form that identifies the Client Capability provider,
MCP server, and Tool. If the user accepts with
stagingandtrue, the MCPadapter retries the original call with:
{ "name": "deploy", "arguments": { "artifact": "build-42" }, "inputResponses": { "target": { "action": "accept", "content": { "environment": "staging", "confirm": true } } }, "requestState": "opaque-server-state" }The retry receives a fresh JSON-RPC id.
requestStatenever enters the modelcontext, renderer-owned state, Runtime Interaction payload, logs, or telemetry.
Authority boundaries
The design keeps one owner for each fact or side effect:
inputResponses, and exactrequestStateechoMcpClientManagerMcpClientManagerplus the selected SDK transportToolRuntimeand the exact hosted Run bindingAn MCP server supplies untrusted form copy and schema. A Client Capability
provider supplies untrusted display provenance. Runtime Host binds both to the
already-authorized exact invocation; neither can mint a Tool, grant a
permission, select another Session, or resume another invocation.
Proposed design
1. Add a provider-neutral form Interaction
Do not flatten an MCP form into the current multiple-choice
questionkind.The existing question contract permits a small list of string answers, while
MCP form elicitation has typed strings, numbers/integers, booleans,
single-select and multi-select values, required fields, defaults, and bounded
validation constraints.
Extend the existing core Interaction union with a repository-owned form shape,
illustratively:
These names are illustrative. The implementation should extend the existing
InteractionRequest,InteractionAnswer, canonical outcome, store decoder,Host coordinator, and session projection rather than create an MCP-specific
store or queue.
Core owns exact-shape decoding and repository caps for request bytes, answer
bytes, field count, enum options, labels, and descriptions. Runtime Host
validates an accepted value against the stored canonical form before committing
the answer. The MCP adapter validates it again against the original MCP schema
before sending it to the server.
MCP exposes one coarse
elicitation.formcapability rather than separateclaims per primitive or field count. Maka must therefore implement the complete
bounded form subset the revision specifies before advertising it; it must not
ship a one-field or string-only claim under the same capability bit.
The form UI must show the requester provenance and give three distinct actions:
submit (
accept), explicitly decline (decline), and dismiss (cancel). Itmust allow review and edits before submit.
2. Add one nested Interaction seam to Client Capability
Extend a live Client Capability invocation with one provider-neutral nested
Interaction request/response pair. The exact wire names can follow the existing
protocol naming, for example:
ClientCapabilityProvider.call()receives a callback such asrequestInteraction(form). The client channel sends the request frame andawaits the matching Host result. The Host broker projects it through the same
ToolRuntimeform callback that local Tools use.The broker, not the provider, binds the form to the current Session, Turn,
Tool call, invocation, registration, and connection. It allows at most one
pending nested Interaction per invocation. Duplicate, late, cross-connection,
or post-cancellation frames fail closed.
The frame's
interactionIdis only provider-local reverse-call correlation.ToolRuntimecreates the canonical Runtime Interaction id; an untrustedprovider cannot choose or collide with a Host identity.
This seam is generic because the ownership problem is generic: a Tool owned by
a remote Client Capability provider may need user input while Runtime Host owns
the execution. MCP is the first adapter using it. The seam is not a generic
arbitrary callback tunnel; it accepts only the closed repository form contract.
3. Drive MRTR manually inside
McpClientManagerKeep
ClientOptions.inputRequired.autoFulfilldisabled. The manager opts anindividual
tools/callinto SDK manual mode and owns a bounded loop:InputRequiredResult;requestStateis present, retry after a small abortable pacingdelay without opening a user form;
an Interaction;
elicitation/createthrough the injected formhandler and collect the bare
ElicitResultvalues under the original keys;inputResponsesand byte-exactrequestStateon a fresh request id; andMcpToolCallError.The manager retains the original validated Tool binding across every leg. A
tool refresh, reconnect, server replacement, provider loss, abort, or deadline
must not silently move the continuation to another binding or connection.
Capability advertisement is per eligible modern call, not a global Client
claim. The dual-era SDK Client must not add elicitation to its base capabilities,
because a legacy negotiation would then falsely advertise support for legacy
push-style elicitation. When and only when a modern
tools/callhas a completeform handler, the manager merges
{ elicitation: { form: {} } }into thatrequest's self-describing
_metaclient capabilities; every retry carries thesame claim. A legacy call or a call without the form handler advertises no
elicitation capability and retains the current rejection behavior.
Multiple input requests in one result are fulfilled in deterministic key order
with a bounded count. Responses are sent together on the next retry. An
unsupported sibling request fails the whole round before any partial retry.
4. Compose Desktop and TUI through the same path
Desktop's native capability provider and TUI's MCP capability provider both
adapt the Client Capability
requestInteractioncallback into theMcpClientManager.callTool()input handler.Desktop renderer and TUI read the same active Interaction projection and send
the same
interaction.answeroperation already used for Host-owned questions.No surface keeps a second authoritative pending-form list. Renderer/TUI reload
that does not retire the exact Client Capability provider can rehydrate the
pending form from the canonical Host snapshot.
Lifecycle and failure semantics
Execution timeout versus user wait
The current Client Capability invocation has a fixed execution timeout. A
human Interaction must not consume that entire provider execution budget.
When an accepted invocation publishes a valid nested Interaction, the broker
enters
awaiting_interactionand suspends its provider-execution timer. After acanonical answer is delivered, it resumes with a fresh bounded provider leg.
Round count, a bounded pending Interaction, and Tool/Turn cancellation prevent
an unbounded loop. Global Stop aborts the Interaction and the MCP call; it does
not synthesize an MCP
cancelanswer or send another Tool request.The form's explicit
cancelaction is different: it is a user response and isreturned to the MCP server as
{ action: "cancel" }.declineis likewisereturned as
{ action: "decline" }. The server may then finish, fail, or ask alater bounded round.
Provider or connection loss
Provider disconnect, registration retirement, server reconnect/replacement,
or invalidation of the frozen Tool binding aborts the pending form and settles
the Tool through the existing Client Capability lost/outcome-unknown rules.
The answer must never be replayed to a replacement provider or MCP connection.
If a provider returns a final result while its nested Interaction is still
pending, the broker closes that Interaction and rejects the inconsistent
invocation rather than leaving an orphan or accepting both outcomes.
Host and surface restart
The initial implementation preserves current Runtime Interaction recovery:
pending canonical form (for example, a Desktop renderer reload while the
main-process provider remains alive);
therefore cannot resume its old invocation;
host_restarted;reconstructed; and
unknown.
Crash-resuming MRTR would require a durable invocation record containing the
exact Tool binding, original args, provider identity, round state, delivery
acknowledgements, and a safe replay policy. Adding that state only for MRTR
would conflict with the existing Tool recovery boundary, so it is deferred to
a broader durable Tool continuation design.
Security and privacy requirements
tools/callwhosecomplete manager/provider/Host/surface path is installed. Never advertise it
globally to a dual-era Client or on a legacy connection.
server/Tool. Claimed server copy is display-only and never authorization.
primitive schemas and repository limits, and reject unknown/unsupported
forms before publication.
credentials must not be entered. MCP requires sensitive flows to use URL
mode, which is out of scope here.
to the exact MCP continuation; a later Tool result may still contain data the
server chooses to return and follows existing result handling.
are not added to model context as conversational content.
retention boundary. They are not copied into logs or telemetry.
requestStateis opaque and untrusted. Maka does not parse, mutate, display,persist, log, or use it for authorization; it only echoes the exact string to
the same manager-owned continuation.
requestStatebytes, input-request count, total embeddedrequest bytes, and rounds before retaining state or opening a Host
Interaction.
and round-limit exhaustion fail closed with no extra MCP retry.
input_requiredresult does not settle the Runtime Tooloperation or its durable T1/T2 boundary. Only the final complete result (or
terminal failure) settles the one logical Tool invocation.
Rollout
This can be implemented as a stack without creating a second authority:
InteractionStore, Host coordination/projection, Desktop, and TUI; prove
canonical answer/closure/reload behavior with a synthetic local Tool.
provider callback; prove exact invocation/connection binding, timer
suspension, cancellation, and provider-loss behavior with a synthetic
Client Capability Tool.
McpClientManagerloop, capabilityadvertisement, Desktop/TUI composition, modern MCP fixtures, and end-to-end
acceptance coverage.
Each child PR should be independently reviewable and remain stacked until its
parent lands. None should replace Runtime Host Interaction authority with a
surface-owned modal or an MCP-specific store.
Acceptance criteria
typed answer, and complete through a second
tools/call.current-round bare
inputResponses, and byte-identicalrequestState.final Tool result.
accept,decline, andcancelremain distinct end to end.server keys; malformed, unknown, or over-limit input fails before retry.
requestStatecan retry, but the round cap stillterminates a non-progressing server.
Host Interaction or Client Capability invocation.
Tool binding cannot receive a previously committed answer.
exact provider remains live. TUI/provider restart and Host restart close the
old continuation and do not replay the Tool; Host restart records
host_restarted.Interaction is pending and is re-armed after settlement.
prompts/get, andresources/readfail with stable local errors and no partial retry.requestStateis absent from Runtime events, Interaction records, IPC/UIpayloads, logs, telemetry, and model-visible output.
behaviorally unchanged at the manager boundary.
Tool calls but never advertises legacy push elicitation after falling back.
nested Interaction; no invocation can settle while leaving an orphan form.
Non-goals
prompts/getorresources/read;requestStateto the model or user;process restart; or
Alternatives considered
Enable SDK auto-fulfilment and register handlers
The SDK driver correctly handles fresh request ids and retry fields, but its
handlers are registered on the shared Client while Maka's Host continuation is
per Tool invocation. Routing concurrent calls would require hidden ambient
context or serializing an entire MCP connection. The SDK driver also owns the
retry internally, so
McpClientManagercannot place its current Tool-binding,connection-generation, and provider-liveness fences immediately before every
new network side effect. Manual mode still uses the SDK's modern wire decoder;
it keeps the small retry policy explicit at Maka's existing manager seam.
Show a modal directly in Desktop/TUI
This is smaller locally but creates separate pending state per surface, cannot
work consistently with a remote Runtime Host, and can deliver an answer after
the authoritative Tool invocation has disappeared.
Return a Tool error and let the model ask the user/retry
That changes a protocol continuation into a new model decision, cannot safely
preserve opaque
requestState, may expose protocol state to the model, and canrepeat side effects or choose different arguments.
Reuse the current multiple-choice question by stringifying fields
This loses numeric/boolean/enum types, validation constraints, defaults, and
the distinct accept/decline/cancel actions. A small closed form contract is
deeper and safer than MCP-specific encoding conventions inside question text.
Persist a complete MRTR continuation immediately
This promises crash recovery before Maka can prove whether an accepted remote
Tool leg is safe to replay. The initial design instead follows the current
host_restartedclose/fail-closed boundary and can be deepened with the generalTool continuation architecture later.
References
2026-07-28release announcement2026-07-28elicitation2026-07-28migration notesMcpClientManagerinput-required policy