Packages
Model Context Protocol (MCP) implementation in Elixir with Phoenix integration
Current section
Files
Jump to
Current section
Files
lib/backplane/mcp_protocol/server/component/prompt.ex
defmodule Backplane.McpProtocol.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 Backplane.McpProtocol.Server.Behaviour.Prompt
alias Backplane.McpProtocol.Server.{Frame, Response}
@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")
response =
Response.prompt()
|> Response.user_message(Enum.map_join(messages, "\n", & &1["content"]["text"]))
{:reply, response, new_frame}
end
end
"""
alias Backplane.McpProtocol.MCP.Error
alias Backplane.McpProtocol.Server.Frame
alias Backplane.McpProtocol.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(),
title: String.t() | nil,
description: String.t() | nil,
arguments: map | nil,
handler: module | nil,
validate_input: (map -> {:ok, map} | {:error, [Peri.Error.t()]}) | nil,
scopes: [String.t()]
}
defstruct [
:name,
title: nil,
description: nil,
arguments: nil,
handler: nil,
validate_input: nil,
scopes: []
]
@doc """
Returns the title that identifies this resource.
Intended for UI and end-user contexts — optimized to be human-readable and easily understood,
even by those unfamiliar with domain-specific terminology.
If not provided, the name should be used for display.
"""
@callback title() :: String.t()
@doc """
Returns the description of this prompt.
The description helps AI assistants understand what the prompt does and when to use it.
If not provided, the module's `@moduledoc` will be used automatically.
## Examples
def description do
"Generate a code review with best practices"
end
# With dynamic content
def description do
model = Application.get_env(:my_app, :analysis_model, "default")
"Analyze code using \#{model} model"
end
"""
@callback description() :: String.t()
@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
- `{:reply, %Response{}, frame}` - Messages generated successfully
- `{:noreply, frame}` - No reply needed
- `{:error, %Error{}, frame}` - Failed to generate messages
## Building Responses
Use `Response.prompt/0` to create a prompt response, then add messages with
`Response.user_message/2` or `Response.system_message/2`:
response =
Response.prompt()
|> Response.user_message("Please review this code")
|> Response.system_message("You are a code reviewer")
{:reply, response, frame}
"""
@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()}
@optional_callbacks title: 0, description: 0
defimpl JSON.Encoder, for: __MODULE__ do
alias Backplane.McpProtocol.Server.Component.Prompt
def encode(%Prompt{} = prompt, _) do
prompt
|> Map.take([:name, :description, :arguments])
|> JSON.encode!()
end
end
end