Elixir 的模型上下文协议(MCP)服务器
概览
一个最小化的Elixir MCP(模型上下文协议)服务器。 将会话存储初始化添加到应用的启动/2: 你可以通过实现EMCP。SessionStore 行为来使用自定义会话存储。 把StreamableHTTP传输添加到你的Phoenix路由器上。把它挂载在任何管道之外,因为EMCP本身负责内容协商: 提示词是可重复使用的模板,返回结构化消息: 资源暴露客户端可读取的数据。静态资源有固定的URI: 资源模板使用URI模式,使客户端可以请求动态内容: 当客户端调用资源/读取时,服务器首先尝试对静态资源进行精确的URI匹配。如果没有匹配,则按顺序尝试每个资源模板,直到有一个处理完URI。
概述(AI 翻译)
一个最小化的Elixir MCP(模型上下文协议)服务器。
布置
1.初始化会话存储
将会话存储初始化添加到你的应用程序 start/2:
你可以通过实现 EMCP.SessionStore 行为来使用自定义会话存储。
2.定义服务器
3.登上运输车
把StreamableHTTP传输添加到你的Phoenix路由器上。把它挂载在任何管道之外,因为EMCP本身负责内容协商:
4.在服务器上添加工具
提示
提示词是可重复使用的模板,返回结构化消息:
资源
资源暴露客户端可读取的数据。静态资源有固定的URI:
资源模板使用URI模式,使客户端可以请求动态内容:
当客户端调用resources/read时,服务器首先尝试对静态资源进行精确的URI匹配。如果没有匹配,则按顺序尝试每个资源模板,直到其中一个处理完URI。
起源验证
为了防止DNS重绑攻击,你可以在传输上启用起源验证。启用后,只有与允许列表匹配的Origin头请求才会被接受。没有Origin头的请求(例如来自CLI工具)始终被允许。
本页由 AI 翻译,技术细节请以原文为准。
README
以下为原文 README 节选,完整内容请查看原文。
EMCP
A minimal Elixir MCP (Model Context Protocol) server.
Setup
1. Initialize the session store
Add the session store initialization to your application’s start/2:
defmodule MyApp.Application do
use Application
def start(_type, _args) do
EMCP.SessionStore.ETS.init()
children = [
# ...
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
You can use a custom session store by implementing the EMCP.SessionStore behaviour.
2. Define a server
defmodule MyApp.MCPServer do
use EMCP.Server,
name: "my-app",
version: "1.0.0",
tools: [MyApp.Tools.Echo],
prompts: [MyApp.Prompts.CodeReview],
resources: [MyApp.Resources.Readme],
resource_templates: [MyApp.ResourceTemplates.UserProfile],
# You can optionally add these meta fields as well:
title: "My App",
description: "MCP server for My App",
instructions: "Use the tools to interact with My App.",
end
3. Mount the transport
Add the StreamableHTTP transport to your Phoenix router. Mount it outside any pipeline since EMCP handles content negotiation itself:
scope "/mcp" do
forward "/", EMCP.Transport.StreamableHTTP, server: MyApp.MCPServer
end
4. Add tools to your server
defmodule MyApp.Tools.Echo do
@behaviour EMCP.Tool
@impl EMCP.Tool
def name, do: "echo"
@impl EMCP.Tool
def description, do: "Echoes back the provided message"
@impl EMCP.Tool
def input_schema do
%{
type: :object,
properties: %{
message: %{type: :string}
},
required: [:message]
}
end
@impl EMCP.Tool
def call(_conn, %{"message" => message}) do
EMCP.Tool.response([%{"type" => "text", "text" => message}])
end
# Return errors with:
# EMCP.Tool.error("something went wrong")
end
Prompts
Prompts are reusable templates that return structured messages:
defmodule MyApp.Prompts.CodeReview do
@behaviour EMCP.Prompt
@impl EMCP.Prompt
def name, do: "code_review"
@impl EMCP.Prompt
def description, do: "Reviews code with optional focus area"
@impl EMCP.Prompt
def arguments do
[
%{name: "code", description: "The code to review", required: true},
%{name: "focus", description: "Optional area to focus on"}
]
end
@impl EMCP.Prompt
def template(_conn, %{"code" => code} = args) do
focus = args["focus"]
user_text =
if focus,
do: "Review this code, focusing on #{focus}:\n\n#{code}",
else: "Review this code:\n\n#{code}"
%{
"description" => "Code review prompt",
"messages" => [
%{"role" => "user", "content" => %{"type" => "text", "text" => user_text}},
%{"role" => "assistant", "content" => %{"type" => "text", "text" => "I'll review the code you've provided."}}
]
}
end
end
Resources
Resources expose data that clients can read. A static resource has a fixed URI:
安装
This server does not publish a one-line install command.
Open the repository installation guide 新手?先看 mcpskill.org 教程 →配置
{
"mcpServers": {
"my-app": {
"command": "mix",
"args": ["run", "--no-halt"],
"cwd": "/path/to/your/elixir/project"
}
}
}