# Hermes.Server Quick Reference ## `use Hermes.Server` ```elixir use Hermes.Server ``` **What it does:** - Injects Hermes.Server behaviour - Requires callback implementation - Provides compile-time validation **Required callbacks:** ```elixir @impl Hermes.Server def server_info() :: %{name: String.t(), version: String.t()} @impl Hermes.Server def server_capabilities() :: map() @impl Hermes.Server def supported_protocol_versions() :: [String.t()] @impl Hermes.Server def init(args, frame) :: {:ok, state} | {:stop, reason} @impl Hermes.Server def handle_tool_call(name, arguments, state) :: {:reply, result, state} | {:error, error, state} @impl Hermes.Server def handle_resource_read(uri, state) :: {:reply, result, state} | {:error, error, state} @impl Hermes.Server def handle_request(request, state) :: {:reply, result, state} | {:noreply, state} ``` ## `init/2` Arguments ### Argument 1: `args` | Property | Value | |----------|-------| | **Type** | `any()` (usually keyword list or map) | | **Purpose** | Server configuration | | **Source** | Passed when server starts | | **Examples** | `[port: 4000, debug: true]`
`%{db_url: "postgres://..."}` | **Common contents:** - Configuration settings - Database URLs - API keys - Feature flags - Environment variables ### Argument 2: `frame` | Property | Value | |----------|-------| | **Type** | `any()` (usually map or struct) | | **Purpose** | Communication context | | **Source** | Provided by transport layer | | **Examples** | See below | **Common structure:** ```elixir %{ transport: :http | :stdio | :websocket, client_info: %{name: String.t(), version: String.t()}, session_id: String.t(), protocol_version: String.t(), request_id: integer(), headers: map(), connection_pid: pid() } ``` ## Return Values ### Success ```elixir {:ok, state} ``` - `state` can be any Elixir term - Commonly a map: `%{counter: 0, config: ...}` - Available in all subsequent callbacks ### Failure ```elixir {:stop, reason} ``` - Aborts server startup - `reason` can be any term - Logged for debugging ## Usage Examples ### Simple Init ```elixir def init(_args, _frame) do {:ok, %{counter: 0}} end ``` ### With Configuration ```elixir def init(args, frame) do config = %{ port: Keyword.get(args, :port, 4000), debug: Keyword.get(args, :debug, false) } {:ok, %{config: config, counter: 0}} end ``` ### With Session Info ```elixir def init(args, frame) do state = %{ session_id: frame.session_id, client: frame.client_info.name, counter: 0 } {:ok, state} end ``` ### With Validation ```elixir def init(args, frame) do case validate_config(args) do :ok -> {:ok, %{config: args, counter: 0}} {:error, reason} -> {:stop, {:invalid_config, reason}} end end ``` ## Complete Minimal Example ```elixir defmodule MyMCPServer do use Hermes.Server @impl Hermes.Server def server_info do %{name: "my-server", version: "1.0.0"} end @impl Hermes.Server def server_capabilities do %{tools: %{}, resources: %{}} end @impl Hermes.Server def supported_protocol_versions do ["2024-11-05"] end @impl Hermes.Server def init(args, frame) do {:ok, %{counter: 0}} end @impl Hermes.Server def handle_tool_call(name, args, state) do {:error, %{code: -32601, message: "Unknown tool"}, state} end @impl Hermes.Server def handle_resource_read(uri, state) do {:error, %{code: -32002, message: "Not found"}, state} end @impl Hermes.Server def handle_request(_request, state) do {:noreply, state} end end ``` ## Common Patterns ### Extract config from args ```elixir port = Keyword.get(args, :port, 4000) # or port = args[:port] || 4000 # or port = Map.get(args, :port, 4000) ``` ### Extract info from frame ```elixir session_id = frame.session_id client_name = get_in(frame, [:client_info, :name]) ``` ### Conditional initialization ```elixir state = if frame.transport == :stdio do %{buffered: true, counter: 0} else %{buffered: false, counter: 0} end ``` ## See Also - Full documentation: `docs/HERMES_SERVER_EXPLAINED.md` - Implementation: `lib/my_mcp_server.ex`