Define an MCP tool as a module.
defmodule MyApp.MCP.GetWeather do
use Noizu.MCP.Server.Tool,
description: "Get current weather for a location",
annotations: [read_only_hint: true]
input do
field :location, :string, required: true, description: "City name or zip"
field :units, :enum, values: [:celsius, :fahrenheit], default: :celsius
field :days, :integer, min: 1, max: 14, default: 1
end
output do
field :temperature, :number, required: true
field :conditions, :string, required: true
end
@impl true
def call(%{location: location, units: units, days: days}, ctx) do
Noizu.MCP.Ctx.report_progress(ctx, 0.5)
{:ok, %{temperature: 21.0, conditions: "clear"}}
end
enduse options
:name— wire name; defaults to the module basename underscored (MyApp.MCP.GetWeather→"get_weather"):description— tells the model when and why to use the tool:title— human-readable display name:annotations— keyword list of behavior hints (:read_only_hint,:destructive_hint,:idempotent_hint,:open_world_hint,:title):icons,:meta— passed through to the wire definition:hidden— whentrue, the tool is omitted fromtools/listresponses but remains callable. Useful for internal or privileged tools.:category— free-form grouping label. Rides on the wire in_meta.category(merged into:meta) and is filterable through the built-inNoizu.MCP.Server.Tools.Catalogtool.
Need several small tools in one module? See Noizu.MCP.Server.Toolkit.
Schemas
input do ... end declares the input schema with the field DSL — see the
Tools & Schemas guide for field types. Arguments are validated against the
compiled JSON Schema before call/2 runs (failures become isError: true
results the model can correct), then delivered atom-keyed with defaults
applied and :enum values cast to atoms.
Prefer raw JSON Schema? Use the escape hatch — arguments are then validated but delivered string-keyed, uncast:
input_schema %{
"type" => "object",
"properties" => %{"query" => %{"type" => "string"}},
"required" => ["query"]
}input_schema/output_schema also accept the schema as raw JSON text
(decoded at compile time — malformed JSON is a compile error):
input_schema """
{"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}
"""output do ... end (or output_schema %{...}) declares outputSchema; map
return values are validated against it and sent as structuredContent.
Return values from call/2
{:ok, String.t()}— single text content block{:ok, map()}— structured content (requires/uses output schema if declared){:ok, Content.t() | [Content.t()]}— explicit content blocks{:ok, ToolResult.t()}— full control{:error, String.t() | Content.t() | [Content.t()]}— tool execution error (isError: true, visible to the model){:error, Noizu.MCP.Error.t()}— protocol error
Summary
Callbacks
Normalized runtime descriptor(s) — one-element list for classic tool modules.
Execute the tool. See the module docs for argument and return contracts.
The wire definition advertised by tools/list.
Functions
Declare the input schema with the field DSL.
Declare the input schema as a raw JSON Schema map (string keys).
Declare the output schema with the field DSL.
Declare the output schema as a raw JSON Schema map (string keys).
Callbacks
@callback __mcp_tools__() :: [Noizu.MCP.Server.Tool.Spec.t()]
Normalized runtime descriptor(s) — one-element list for classic tool modules.
@callback call(args :: map(), ctx :: Noizu.MCP.Ctx.t()) :: {:ok, term()} | {:error, term()}
Execute the tool. See the module docs for argument and return contracts.
@callback definition() :: Noizu.MCP.Types.Tool.t()
The wire definition advertised by tools/list.
Functions
Declare the input schema with the field DSL.
Declare the input schema as a raw JSON Schema map (string keys).
Declare the output schema with the field DSL.
Declare the output schema as a raw JSON Schema map (string keys).