Hermes.Server.Component (hermes_mcp v0.10.1)
High-level API for defining MCP server components (tools, prompts, resources).
This module provides a simplified way to define server components with automatic schema validation using Peri, reducing boilerplate while maintaining type safety.
Component Types
- Tools: Functions that can be invoked by clients with parameters
- Prompts: Templates that generate messages based on arguments
- Resources: Data providers identified by URIs
Usage
Tool Example
defmodule MyServer.Tools.Calculator do
@moduledoc "Performs arithmetic operations on two numbers"
use Hermes.Server.Component, type: :tool
schema %{
operation: {:required, {:enum, ["add", "subtract", "multiply", "divide"]}},
a: {:required, :float},
b: {:required, :float}
}
@impl true
def execute(%{operation: "add", a: a, b: b}, frame) do
{:ok, a + b, frame}
end
@impl true
def execute(%{operation: "divide", a: a, b: 0}, frame) do
{:error, "Cannot divide by zero"}
end
end
Prompt Example
defmodule MyServer.Prompts.CodeReview do
@moduledoc "Generate code review prompts for various programming languages"
use Hermes.Server.Component, type: :prompt
schema %{
language: {:required, :string},
code: {:required, :string},
focus_areas: {:string, {:default, "general quality"}}
}
@impl true
def get_messages(%{language: lang, code: code, focus_areas: focus}, frame) do
messages = [
%{
"role" => "user",
"content" => %{
"type" => "text",
"text" => "Review this #{lang} code focusing on #{focus}..."
}
}
]
{:ok, messages, frame}
end
end
Resource Example
defmodule MyServer.Resources.Config do
@moduledoc "Application configuration file"
use Hermes.Server.Component,
type: :resource,
uri: "file:///config/app.json",
mime_type: "application/json"
@impl true
def read(_params, frame) do
case File.read("config/app.json") do
{:ok, content} -> {:ok, content, frame}
{:error, reason} -> {:error, "Failed to read config: #{inspect(reason)}"}
end
end
end
# Resource with query parameters
defmodule MyServer.Resources.UserData do
@moduledoc "User data with filtering support"
use Hermes.Server.Component,
type: :resource,
uri: "users://data"
schema %{
user_id: {:required, :string},
include_metadata: {:boolean, {:default, false}}
}
@impl true
def read(%{user_id: id, include_metadata: meta?}, frame) do
data = fetch_user_data(id, meta?)
{:ok, Jason.encode!(data), frame}
end
end
Schema Validation
Components can define parameter schemas using the schema/1
macro, which leverages
Peri for validation. The schema is automatically:
- Validated before callback execution
- Converted to JSON Schema for the MCP protocol
- Used to generate helpful error messages
Automatic Features
- Description: Extracted from
@moduledoc
- Validation: Parameters/arguments validated before execution
- Error Handling: Detailed validation errors with paths
- JSON Schema: Automatic conversion for protocol compliance
Summary
Functions
Checks if a module is a valid component.
Defines a field with metadata for JSON Schema generation.
Extracts the description from a component module's moduledoc.
Gets the component type (:tool, :prompt, or :resource).
Defines the parameter schema for the component.
Functions
Checks if a module is a valid component.
Parameters
module
- The module atom to check
Returns
true
if the module usesHermes.Server.Component
false
otherwise
Examples
iex> defmodule MyTool do
...> use Hermes.Server.Component, type: :tool
...> end
iex> Hermes.Server.Component.component?(MyTool)
true
iex> defmodule NotAComponent do
...> def hello, do: :world
...> end
iex> Hermes.Server.Component.component?(NotAComponent)
false
Defines a field with metadata for JSON Schema generation.
Supports both simple fields and nested objects with their own fields.
Examples
# Simple field
field :email, {:required, :string}, format: "email", description: "User's email address"
field :age, :integer, description: "Age in years"
# Nested field
field :user do
field :name, {:required, :string}
field :email, :string, format: "email"
end
# Nested field with metadata
field :profile, description: "User profile information" do
field :bio, :string, description: "Short biography"
field :avatar_url, :string, format: "uri"
end
Extracts the description from a component module's moduledoc.
Parameters
module
- The component module atom
Returns
- The module's
@moduledoc
content as a string - Empty string if no moduledoc is defined
Examples
iex> defmodule MyTool do
...> @moduledoc "A helpful tool"
...> use Hermes.Server.Component, type: :tool
...> end
iex> Hermes.Server.Component.get_description(MyTool)
"A helpful tool"
Gets the component type (:tool, :prompt, or :resource).
Parameters
module
- The component module atom
Returns
:tool
- If the module is a tool component:prompt
- If the module is a prompt component:resource
- If the module is a resource component
Examples
iex> defmodule MyTool do
...> use Hermes.Server.Component, type: :tool
...> end
iex> Hermes.Server.Component.get_type(MyTool)
:tool
Defines the parameter schema for the component.
The schema uses Peri's validation DSL and is automatically validated before the component's callback is executed.
Examples
schema do
%{
query: {:required, :string},
limit: {:integer, {:default, 10}},
filters: %{
status: {:enum, ["active", "inactive", "pending"]},
created_after: :datetime
}
}
end
# With field metadata for JSON Schema (no braces needed!)
schema do
field(:email, {:required, :string}, format: "email", description: "User's email address")
field(:age, :integer, description: "Age in years")
field :address, description: "User's address" do
field(:street, {:required, :string})
field(:city, :string)
field(:country, :string, description: "ISO 3166-1 alpha-2 code")
end
end