Wymcp. Plugs. Dispatch
(Wymcp v0.6.2)
View Source
The method surface of both eras: routes a validated message to the module
that answers it. The last plug in Wymcp.Plugs.Pipeline.
Three branches, in clause order. A JSON-RPC response — a client's answer to a server-initiated request — is delivered to the session that minted its id. A modern-classified message takes the modern lane. Everything else takes the legacy lane. The response branch is legacy-only: a client's answer to a server-initiated request reaches wymcp only on the legacy lane. The contract survives via MRTR on the modern lane, and the branch goes at the legacy decommission.
The modern lane
server/discover, tools/list and tools/call — the whole modern method
surface. An unknown modern method answers HTTP 404 plus -32601: the
spec's MUST, and deliberately distinguishable from a legacy HTTP+SSE
server's 404. Wymcp.Response.send_json/2 preserves a previously-set status,
which is why one method module serves both eras' unknown-method bodies while
only this lane sets 404. That 404 is a
rejection, so this plug records it through
Wymcp.Response.record_rejection/5 before the body is built; the legacy
lane's 200 is not one.
A modern notification never reaches a method: the modern core defines no
client→server notification over HTTP, and JSON-RPC forbids an error response
to a notification — so it is answered 202 and dropped, whatever its body.
That is also why Wymcp.Plugs.Validate lets it through unvalidated.
The legacy lane
initialize, notifications/initialized, ping, tools/list,
tools/call, logging/setLevel and notifications/cancelled. Anything else
falls through to the same unknown-method module with no status override, so
it answers -32601 in a 200 envelope.
The whole lane is legacy-only: these methods exist only because the
legacy era does, and the section goes with them at the legacy
decommission.
The tool list this plug passes on is the mount module's mount tools —
its :tools option — together with the definitions Wymcp.Router.init/1
built for them, which are tools/list's alone: tools/call is handed the
tool list and nothing else. Wymcp.Router copies its init options into
conn.assigns[:wymcp], and this plug reads both from there. That list is a
starting point, not the effective one — each method module resolves the
effective list itself. On the legacy lane the tools/list and tools/call
modules re-read the session's registered tools, so a runtime
Wymcp.Session.register_tool/2 wins there; the modern lane is stateless,
has no session to consult, and serves the mount list as passed. A tool
registered on a live session has no stored definition — no registration
moment saw it — so tools/list builds that one itself: a legacy-only
path, since only the legacy lane resolves its tools through a session, and
it goes with the era branch at the decommission.