Skip to content

Interaction contracts

A handler returns either Finish(result) or AwaitInput(input). Input plus a non-nil error is invalid; the private cause remains available through the failure. Plain calls support no interaction modes. The host declares its actual facilities using WithInputSupport(FormInput, URLInput); a handler can inspect them through Invocation.Supports. An unsupported request returns confirmation_required with guidance to use the application's authorized interface. A handler can provide a more specific authorized link through its ordinary result/failure path.

Prompts and responses

NewFormInput snapshots a message, schema and private JSON state. Use state for an application intent/job key, not credentials. The zero input is invalid; nil state is invalid JSON, while explicit null is valid. Form() returns detached schema bytes without application state. The profile accepts declared flat properties: strings, numbers/integers, booleans, string enums and bounded string-enum multiselect arrays. Titled single choices use oneOf with string const/title; titled multiselect items use anyOf of those choices. Legacy enumNames must match the choice count. Arrays require maxItems; arbitrary objects, arrays, references and combinators fail.

Answers may contain only declared fields. Root additionalProperties may be omitted or false; true is unsupported. Required names must exist in properties. Numeric and count constraints use the registry's exact compiler profile. The supported email, uri, date and date-time formats are asserted. Defaults remain schema data and are never inserted. No coercion or automatic boolean confirmation occurs: the handler must check both action and answer. Adapters must additionally check the connected client's supported profile before presenting it.

NewURLInput requires absolute HTTPS without user information. There is no loopback-HTTP exception in this slice. The library never opens, fetches or redirects to the URL. The handler supplies an authorized application route; acceptance means navigation consent, and the application must separately verify completion of its external flow. URL() exposes only message and URL. The MCP elicitation protocol is the reference for the supported display shapes and distinction between form and URL modes.

Construct an InputResponse with Accept, Decline or Cancel. Form acceptance needs object JSON answers, including {} when the form has no required fields. URL acceptance, decline and cancel require nil answers. The handler receives negative actions explicitly through Resume.Action() and chooses the appropriate application response.

The host resubmits the original operation and arguments with WithResume(pending.Continuation(), response). Only Registry.Invoke can supply a verified Resume; its state and answer accessors detach mutable data. Original and canonical answer bytes are bounded before retention. Duplicate JSON keys fail, numbers normalize exactly, and the gateway checks answers against the protected original form before execution authorization and handler dispatch.

Replay, lifetime and capacity

Tokens contain 256 random bits encoded as 43 URL-safe characters. They reference a private registry ledger; application state is never encoded in the token. Binding includes registry ownership, operation, canonical original arguments, the hash of the host's scope key, serving lifetime, original prompt, round and expiry. Do not log tokens or reuse them across principals. A registry restart loses them. An internal adapter lifetime facility isolates server shutdown from other servers and direct calls; actual SDK lifecycle wiring remains an adapter acceptance task.

A prompt transition is serialized across policy and handler callbacks. A simultaneous request receives busy. No registry lock is held across application callbacks. Identical pending replay reauthorizes and returns the same next prompt, token and expiry without another handler call. Different actions or canonical answers cannot fork a branch. Completed replay invokes the handler with the same verified resume and RecoveryOnly() == true. Handler failures, panics, cancellation and rejected outcomes after answer acceptance also require recovery. Recovery may only Finish; it cannot issue another prompt. Final result bodies are not cached in the ledger.

This does not provide durable or exactly-once external effects. The application must use its own intent/job store to recover an existing outcome. Continuation expiry errors direct callers to that original application key. There are no automatic retries, job cancellation or background polling workers.

The default is ten prompts, with the tenth answer allowed to finish. An eleventh prompt fails interaction_limit. Lifetime defaults to ten minutes. WithInputExpiry can shorten it; subsequent prompts cannot extend the previous prompt's deadline. A shorter child may expire before its parent; replaying that parent cannot resurrect the child. Expiry runs during admission/access and server-lifetime shutdown. Running handlers remain charged until they return; shutdown prevents a new pending reply committing, but cannot forcibly stop application code.

Limit Default
Form schema, private state, accepted answers 64 KiB each
Input message / HTTPS URL Fixed 1 KiB / 4 KiB UTF-8 bytes
Accepted encoded continuation token ceiling 4 KiB
Retained chains across all server lifetimes 1,024
Ledger owned data plus reservations 64 MiB
Prompts per chain / confirmation lifetime 10 / 10 minutes

Public prompt encoding also fits the core result budget, including reserved token/expiry and failure overhead; the adapter must separately validate its complete wire representation. Private state counts only toward the ledger.

An initial call declaring input support reserves one chain and a maximum prompt before the handler runs, even if it ultimately finishes without input. A new answer reserves answer data and the next maximum prompt before execution; unused space is released on return. Recovery reuses its retained record. Capacity failures return busy without handler effects and never evict live prompts. Retained records include all schema/state/answer/argument bytes plus conservative fixed charges for structures and tokens. This is an owned-data admission budget, not a peak-memory or RSS bound; compiler work, concurrent request buffers and allocator high-water memory are separate.

Progress

WithProgressSink receives advisory snapshots during one invocation. Completed values must be finite, nonnegative and nondecreasing, including valid updates suppressed by throttling. An optional total must be finite and at least completed. Messages must be valid UTF-8 and at most 1 KiB. The optional total is copied before delivery.

Delivery is synchronous and throttled to at most one update per 100 ms. Concurrent/reentrant updates during a sink callback and updates inside that interval are suppressed; there is no timer worker or guaranteed final update. Invalid updates, sink errors and panics are recorded in a bounded private request diagnostic slot and do not alter the operation outcome. Forwarding that slot to host diagnostics remains adapter work. Sinks must honor context; invocation completion waits for an already-running delivery, and no delivery starts after the invocation closes progress. Background job status remains application-owned and is read through explicit polling.