Wymcp. JsonRpc
(Wymcp v0.6.1)
View Source
JSON-RPC 2.0 envelopes and MCP protocol schema validation — the wire-format floor both eras share.
success_response/2 and the three error_response arities build the
envelope every answer is sent as; Wymcp.Response does the sending. The
error map is the one home for wymcp's JSON-RPC error codes: each
error_type/0 atom pairs with its {code, message} tuple, so a call site
names the condition and never the number. The four-arity form overrides that
message and nothing else — the code still comes from the table — for a
condition whose wording has to name what it refused. This module owns the
envelope's shape; it owns no decision about which condition applies — that
is each plug's and each method module's.
Message and output-schema validation
One self-contained MCP protocol schema per era —
priv/schema-2025-11-25.json and priv/schema-2026-07-28.json, JSON Schema
2020-12 — is compiled to a JSV.Root at build time through a module
attribute, so an inbound request is validated against the official protocol
definition with no runtime schema parsing. Both schemas name the same entry
point, JSONRPCMessage; validate_mcp_request/2 selects the era's root from
the era Wymcp.Plugs.Validate passes it. @external_resource on both paths
makes an edit to either schema file recompile this module.
A rejection is answered as a distilled map — a one-line message plus
an errors list of %{at:, problems:} entries, one per instance location,
locations sorted, each location's problems deduplicated — never JSV.normalize_error/1's
full tree. It is a shape, not one function's return value. This module
distills it on two paths: validate_mcp_request/2 on the -32600
message path, whose reader is the human debugging a client, and
validate_schema/2 on a tool's output-schema failure, which is logged and
never reaches the wire. Wymcp.Methods.ToolsCall builds a conforming one
by hand for the -32602 arguments path, whose reader is the calling LLM.
The full tree is JSON-encodable and was what previously reached the wire;
it is simply too verbose for either reader — 2353 bytes against 435 for one
garbage message, measured 2026-08-15.
Summary
Types
The atom naming one row of the error-code table: the JSON-RPC errors
wymcp answers, each pairing its atom with a {code, message} tuple.
All three error_response arities accept exactly these atoms.
Distinct from telemetry's error_kind, which classifies a tool
error's origin, not a JSON-RPC error.
Functions
The error envelope with a caller-supplied message in place of the
error-code table's fixed one.
Types
@type error_type() ::
:parse_error
| :invalid_request
| :method_not_found
| :invalid_params
| :internal_error
| :session_not_found
| :unsupported_protocol_version
| :header_mismatch
The atom naming one row of the error-code table: the JSON-RPC errors
wymcp answers, each pairing its atom with a {code, message} tuple.
All three error_response arities accept exactly these atoms.
Distinct from telemetry's error_kind, which classifies a tool
error's origin, not a JSON-RPC error.
Functions
The error envelope with a caller-supplied message in place of the
error-code table's fixed one.
A fixed message is right where one condition has one wording, which is every
row but one. Wymcp.Plugs.HeaderBinding refuses four different headers under
three conditions and its message names which and what to do about it, so the
wording cannot live in the table — but the code still does, which is what
keeps that call site naming the condition and never the number.