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

component?(module)

Checks if a module is a valid component.

Parameters

  • module - The module atom to check

Returns

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

field(name, type \\ nil, opts \\ [])

(macro)

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

get_description(module)

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"

get_type(module)

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

schema(list)

(macro)

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