Current section

Files

Jump to
hermes_mcp lib hermes server component prompt.ex
Raw

lib/hermes/server/component/prompt.ex

defmodule Hermes.Server.Component.Prompt do
@moduledoc """
Defines the behaviour for MCP prompts.
Prompts are reusable templates that generate messages based on provided arguments.
They help standardize common interactions and can be customized with parameters.
## Example
defmodule MyServer.Prompts.CodeReview do
@behaviour Hermes.Server.Behaviour.Prompt
alias Hermes.Server.Frame
@impl true
def name, do: "code_review"
@impl true
def description do
"Generate a code review prompt for the given programming language and code"
end
@impl true
def arguments do
[
%{
"name" => "language",
"description" => "The programming language of the code",
"required" => true
},
%{
"name" => "code",
"description" => "The code to review",
"required" => true
},
%{
"name" => "focus_areas",
"description" => "Specific areas to focus on (e.g., performance, security)",
"required" => false
}
]
end
@impl true
def get_messages(%{"language" => lang, "code" => code} = args, frame) do
focus = Map.get(args, "focus_areas", "general quality")
messages = [
%{
"role" => "user",
"content" => %{
"type" => "text",
"text" => \"\"\"
Please review the following \#{lang} code, focusing on \#{focus}:
```\#{lang}
\#{code}
```
Provide constructive feedback on:
1. Code quality and readability
2. Potential bugs or issues
3. Performance considerations
4. Best practices for \#{lang}
\"\"\"
}
}
]
# Can track prompt usage
new_frame = Frame.assign(frame, :last_prompt_used, "code_review")
{:ok, messages, new_frame}
end
end
"""
alias Hermes.MCP.Error
alias Hermes.Server.Frame
alias Hermes.Server.Response
@type arguments :: map()
@type message :: map()
@type argument_def :: %{
String.t() => String.t(),
optional(String.t()) => boolean()
}
@type t :: %__MODULE__{
name: String.t(),
description: String.t() | nil,
arguments: map | nil,
handler: module | nil,
validate_input: (map -> {:ok, map} | {:error, [Peri.Error.t()]}) | nil
}
defstruct [
:name,
description: nil,
arguments: nil,
handler: nil,
validate_input: nil
]
@doc """
Returns the list of arguments this prompt accepts.
Each argument should include:
- `"name"` - The argument name
- `"description"` - What the argument is for
- `"required"` - Whether the argument is required (optional, defaults to false)
## Example
[
%{
"name" => "topic",
"description" => "The topic to generate content about",
"required" => true
},
%{
"name" => "tone",
"description" => "The tone of voice (formal, casual, etc.)",
"required" => false
}
]
"""
@callback arguments() :: [argument_def()]
@doc """
Generates messages based on the provided arguments.
## Parameters
- `args` - The arguments provided by the client
- `frame` - The server frame containing context and state
## Return Values
- `{:ok, messages}` - Messages generated successfully, frame unchanged
- `{:ok, messages, new_frame}` - Messages generated with frame updates
- `{:error, reason}` - Failed to generate messages
## Message Format
Messages should follow the MCP message format:
%{
"role" => "user" | "assistant",
"content" => %{
"type" => "text",
"text" => "The message content"
}
}
Multiple messages can be returned to create a conversation context.
"""
@callback get_messages(args :: arguments(), frame :: Frame.t()) ::
{:reply, response :: Response.t(), new_state :: Frame.t()}
| {:noreply, new_state :: Frame.t()}
| {:error, error :: Error.t(), new_state :: Frame.t()}
defimpl JSON.Encoder, for: __MODULE__ do
alias Hermes.Server.Component.Prompt
def encode(%Prompt{} = prompt, _) do
prompt
|> Map.take([:name, :description, :arguments])
|> JSON.encode!()
end
end
end