Wymcp. Telemetry
(Wymcp v0.1.1)
View Source
Telemetry events emitted by Wymcp.
Consuming applications can attach handlers to these events for monitoring, logging, and metrics.
Events
[:wymcp, :session, :start]— session created during initialize- Measurements:
%{system_time: integer()} - Metadata:
%{session_id: String.t(), client_info: map(), era: :legacy}— always:legacy: session machinery is the legacy lane
- Measurements:
[:wymcp, :session, :expired]— session terminated due to idle timeout- Measurements:
%{system_time: integer()} - Metadata:
%{session_id: String.t(), era: :legacy}— always:legacy: session machinery is the legacy lane
- Measurements:
[:wymcp, :session, :not_found]— request bearing an unrecognisedMcp-Session-Idrejected with HTTP 404- Measurements:
%{system_time: integer()}
session machinery is the legacy laneMetadata: `%{session_id: String.t(), request_id: term() nil, method: String.t() nil, era: :legacy} — always:legacy`:
- Measurements:
[:wymcp, :tool, :start]— tool execution starting- Measurements:
%{system_time: integer()} Metadata:
%{tool_name: String.t(), action: String.t() | nil, session_id: String.t() | nil, era: :modern | :legacy}
- Measurements:
[:wymcp, :tool, :stop]— tool execution completed- Measurements:
%{duration: integer()}(native time units) Metadata: `%{tool_name: String.t(), action: String.t() nil, session_id: String.t() nil, is_error: boolean(), error_kind: :dispatch | :tool | nil, era: :modern | :legacy}`
- Measurements:
[:wymcp, :tool, :error]— tool raised an exception- Measurements:
%{duration: integer()} Metadata: `%{tool_name: String.t(), action: String.t() nil, session_id: String.t() nil, request_id: term(), exception: String.t(), error: String.t(), era: :modern :legacy}`
- Measurements:
For tool events, action is the raw "action" string from the call
arguments — what the client actually sent — or nil when the arguments
carried none (a help call's arguments carry the target action, which is
echoed here). On [:wymcp, :tool, :stop], is_error mirrors the MCP
result's isError flag for tool-returned errors, and error_kind
classifies that error's origin: :dispatch — a gate rejected the call
before the tool's action handler ran (wymcp's dispatch gate, or a
hand-written run/2 classifying its own gate rejection); :tool — the
tool ran and answered with an error.
error_kind is nil exactly when is_error is false. Both keys are
:stop-only — neither [:wymcp, :tool, :start] nor
[:wymcp, :tool, :error] carries them, so read them with Map.get/3
from a handler attached to more than one of these events. The
error_kind vocabulary may grow; match it with a fallback clause, never
exhaustively.
Every event above carries era (docs/glossary.md, era) naming the
lane that served the request — except the two auth events: the auth
check is an era-invariant wire check and runs before era classification
exists on the conn, so those two carry no era key. A tool called
in-process, on a Wymcp.Context.t/0 that never reached the wire, had
no lane to name; those calls report :modern. That covers the caller,
not the vocabulary — the value set stays :modern | :legacy on every
path, and carries no growth clause. Read era with Map.get/3 from any
handler attached across event families.
era is legacy-only: it exists only because two eras do, and the key
goes at the legacy decommission.
[:wymcp, :help, :called]— the help tool answered a call (including error answers), emitted after the answer is resolved so the metadata carries the outcome. The authoritative introspection record; the same call also emits the generic tool events above withtool_name: "help". Two cases drop this event. A call rejected by help's arguments gate never resolves a target, so it emits nothing here and surfaces only as[:wymcp, :tool, :stop]witherror_kind: :dispatch— reconciling the two streams will show:tool-level rows with no:calledrow, and that is the reason. A raise inside help also drops it — the call still surfaces as[:wymcp, :tool, :error]withtool_name: "help"(the target tool echo is lost;actionstill carries the target action, per the note above).- Measurements:
%{system_time: integer()} Metadata:
%{tool: String.t() | nil, action: String.t() | nil, level: :index | :tool | :action, session_id: String.t() | nil, is_error: boolean(), era: :modern | :legacy}—tool/actionecho the requested target exactly as sent, even when they name nothing (a probe for a nonexistent target is itself signal);levelis which answer level the parameter shape addressed;is_erroris whether the answer was an error answer.
- Measurements:
[:wymcp, :auth, :reject]— auth module returned{:error, reason}- Measurements:
%{system_time: integer()} - Metadata:
%{auth_module: module(), reason: String.t(), request_id: term() | nil, method: String.t() | nil, http_method: String.t()}
- Measurements:
[:wymcp, :auth, :error]— auth module raised an exception- Measurements:
%{system_time: integer()} - Metadata:
%{auth_module: module(), exception: String.t(), error: String.t(), request_id: term() | nil, method: String.t() | nil, http_method: String.t()}
- Measurements:
For auth events, http_method is the conn's HTTP verb ("POST",
"GET", "DELETE") — as this library observed it, so an upstream
rewriter such as Plug.Head reports its rewritten verb.
request_id and method come from the parsed
request body and are nil off POST — http_method is what
distinguishes a GET/DELETE reject from a POST reject whose body did
not parse.
[:wymcp, :server, :reject]— the consumer'sWymcp.Server.init/2returned{:error, reason}; the session is terminated and thenotifications/initializedrequest answered with a JSON-RPC internal_error- Measurements:
%{system_time: integer()} - Metadata:
%{server: module(), session_id: String.t(), reason: term(), request_id: term() | nil, era: :legacy}—reasonis the raw term the callback returned;erais always:legacy(session machinery)
- Measurements:
[:wymcp, :server, :error]— the consumer'sWymcp.Server.init/2raised, exited, or threw; treated as a rejection (same session termination and internal_error answer)- Measurements:
%{system_time: integer()} - Metadata:
%{server: module(), session_id: String.t(), exception: String.t(), error: String.t(), request_id: term() | nil, era: :legacy}—exceptionis the exception struct name for a raise, or"exit"/"throw"for the other kinds;erais always:legacy(session machinery)
- Measurements: