Wymcp.Help (Wymcp v0.8.7)

View Source

The framework-owned introspection tool — the server's entire introspection surface, injected by Wymcp.Router into every server under the reserved tool name help.

Help answers at three levels: a bare call returns the server index (every tool with its action summaries); tool returns that tool complete (all action schemas with notes, related actions, and examples); tool plus action returns one action complete, with the target tool's Wymcp.Tool.action_context/2 output under "context". Resolution order is tool first, then action — action without tool is an error, and unknown targets error naming the valid ones and pointing at the help index (isError: true content the calling LLM can self-correct from), never a silent fallback to a broader answer. Those answers quote the requested name under the rule Wymcp.Bound states in "What a refusal quotes".

The index renders through Wymcp.Tool.Schema.action_summaries/1, which shares its content source with the tools/list description builder (Wymcp.Tool.Schema.build/1) rather than being called by it, so the two cannot drift. Server-level prose does not live here — it belongs in the instructions router option.

Help answers from ctx.tools — the request's tool list, which Wymcp.Methods.ToolsCall sets on every context.

It will not render around a gap, at any of the three levels. Help obtains every action schema it renders through Wymcp.Tool.Actions.fetch_schema/2 or Wymcp.Tool.Actions.fetch_schemas!/1, which check the mandatory keys where the schema is obtained — the read-side corollary stated in Wymcp.Tool.Actions — so a schema missing one raises ArgumentError naming the tool, the action and the key before anything is rendered. Publishing the entry instead would assert something false about the action: silence about :properties reads as a claim that it takes none, and a null description reaches the wire as the action's description.

An action's properties are published with the closure dispatch enforces: Wymcp.Tool.KeySet.close_properties/1 writes additionalProperties: false into every nested object schema whose key set is closed, and leaves one whose author wrote the keyword as written. Under JSON Schema an object without the keyword is open, so the schema published verbatim would tell a reader that dispatch accepts keys it refuses (the rule: "Dispatch errors and self-correction" in Wymcp.Tool).

This module implements the tool wire contract by hand (input_schema/0, run/2) rather than through use Wymcp.Tool: help has no action dispatch, its two parameters live at the top level of arguments, and its input schema sets additionalProperties: false so a misspelled parameter is a stated contract violation rather than a silent answer to the wrong question. Publishing that is one half; the other is the gate pair in run/2: vocabulary through the same Wymcp.Tool.check_arguments/4 a generated tool's dispatch uses, and types through help's own check_argument_types/2 — a tool that hand-writes its schema owns the types of the keys it declares, and the framework's two type checks cover only its own action/data. Both valid sets derive from the schema above. Both halves are needed: argument validation in Wymcp.Methods.ToolsCall checks structure only — a stray name is a dispatch gate's answer everywhere in wymcp, never a -32602 naming no key — so without the gate a misspelled tol would silently answer the index. Its tools/list definition is assembled by the same Wymcp.Tool.build_definition/1 every other tool's is, so a definition key added there reaches help without hand-sync.

It is the framework's only @behaviour Wymcp.Tool tool, and the only reason that path exists. Wymcp.Tool's run/2 and input_schema/0 come from __before_compile__ and are not defoverridable, so a tool without action dispatch cannot use the macro — the path is an internal accommodation, not a consumer contract; a consumer tool uses the macro. Being behaviour-only, this module owes every required callback by hand: actions/0, run_action/3, hints/2 and handle_error/1 are dead stubs carried for that reason alone, and Wymcp.Router.init/1 checks them while a mount module compiles (the callback-surface invariant, homed in Wymcp.Tool).

Every call that reaches an answer emits [:wymcp, :help, :called] after that answer is resolved — see Wymcp.Telemetry. A call rejected by the arguments gate above does not: it resolved no target, so the event's tool, action and level would describe an index call that never happened. Such a rejection stays observable as [:wymcp, :tool, :stop] with error_kind: :dispatch, like any other tool's gate rejection. (A mistyped action or data never gets this far: ToolsCall's argument validation answers it -32602 before any tool runs, unobserved by tool telemetry — a property of the -32602 path, not of help.)

flowchart TD
    H[Wymcp.Help] --> R["run/2"]
    subgraph External
        R -->|"action_summaries/1"| SC[Tool.Schema]
        R -->|"fetch_schemas!/1, fetch_schema/2"| AC[Tool.Actions]
        R -->|"close_properties/1"| KS[Tool.KeySet]
        R -->|"shared tool-surface helpers"| WT[Wymcp.Tool]
        WT -->|"action_context/2"| T(Target tool)
        R --> TE[Telemetry]
    end

Summary

Functions

True when module claims the reserved tool name.

Functions

uses_reserved_name?(module)

True when module claims the reserved tool name.

The reserved name is "help" — the tool name only the framework may use: a consumer tool may not claim it at a mount module's compile (Wymcp.Router.init/1), whose raise routes through this predicate.