Wymcp. Modern
(Wymcp v0.6.2)
View Source
Modern-lane result shapes — the two result types the lane answers and the framework defaults every one of them carries, with zero consumer surface.
complete_result/2 stamps resultType: "complete" on a finished
result. input_required_result/3 builds the input-required one a tools/call
answers when the run stops at an unanswered elicit; the tasks
extension's "task" arrives with a planned change. Both stamp the
io.modelcontextprotocol/serverInfo result _meta (built by
Wymcp.ServerInfo from the :server_info router option and app
config). with_cache_hints/3 merges the cache-hint defaults into the
two cacheable modern results:
| Result | ttlMs | cacheScope |
|---|---|---|
server/discover (:discover) | 3_600_000 | "private" |
tools/list (:tools_list) | 300_000 | "private" |
"private" is the never-wrong scope: the spec allows tool sets to vary
by authorization context, so "public" could silently become wrong for
a future consumer. Both results change only at deploy/upgrade — on the
modern lane the tool definitions tools/list serves are built once, at the
registration moment — so minutes of staleness is harmless; the values are
revisable without consumer churn. An input-required result takes no hints at
all: the spec forbids caching one, and no tools/call result carries
them in any case.
The requestState codec
requestState is the opaque string a client echoes back byte-exact on
every retry of a call it is answering. input_required_result/3
encodes the answers accumulated so far as JSON and then as unpadded
base64url; decode_request_state/1 reads them back on the next round,
and answers :error for every way the string can fail to be one — not
base64url, not JSON, or JSON that is not an object — with two absent
states read as the empty map: a missing field, and the empty string a
client sends when it marshals a field it never populated.
The value carries no integrity envelope — no signature, no expiry, no binding to a principal or to the arguments it was minted for. It holds only answers the client itself supplied, so altering one yields nothing that client could not have sent as a fresh answer, and a tool validates elicited content as the user input it is. That reasoning ends the day the state carries anything a principal could abuse; until then the envelope would buy nothing.
complete_result/2 and with_cache_hints/3 are era-guarded no-ops on
a legacy conn, so the shared serving methods (Wymcp.Methods.ToolsList,
Wymcp.Methods.ToolsCall) pipe through them unconditionally.
input_required_result/3 needs no guard: only the modern lane can stop
a run mid-way, because only there does an elicit have nowhere to block.
The guard is legacy-only: it exists only because a second era does, and
both no-ops go at the legacy decommission.
Summary
Functions
The interim tools/call result a run answers when it stops at an
unanswered elicit: resultType: "input_required", the run's input
requests, and the prior answers as requestState.
Functions
The interim tools/call result a run answers when it stops at an
unanswered elicit: resultType: "input_required", the run's input
requests, and the prior answers as requestState.
input_requests is keyed by the ids this server assigned, and each
entry is a bare {method, params} object carrying no JSON-RPC id: the
request travels inside a result, not as a message of its own. A client
answers the latest keys and echoes requestState back untouched. The
result carries no content and no isError — neither field is defined
for this result type — and no cache hints.