工程
Elixir的MCP(模型上下文协议)服务器库。基于插件 MCP JSON-RPC 2.0协议的实现,支持工具和 可选的承载令牌身份验证。
基于 温哥华 并受到启发 anubis mcp.
### API更改{:.warning} 这个项目正在进行中,API将更改,直到我们达到1.0.0版本。
支持的MCP协议版本
Wymcp接受MCP规范的三个当前版本。 当客户端请求未知版本时,wymcp计数器会建议 符合规范的最新支持版本(InitializeResult.protocolVersion 包含反建议;客户端决定是否断开连接)。
| 版本 | 状态 | 注释 |
|---|---|---|
2025-11-25 | 支持(默认) | 最新。所有已实现的功能均可用。 |
2025-06-18 | 支持 | 所有已实现的功能均可用。 |
2025-03-26 | 支撑(地板) | 工具 title, outputSchema / structuredContent, serverInfo 扩展、启发和 MCP-Protocol-Version 在2025-03-26会话中,标头被版本门控并省略。 |
2024-11-05 | 不支持 | 早于Streamable HTTP,并使用wymcp未实现的拆分端点HTTP+SSE传输。反建议 2025-11-25 在...期间 initialize. |
接受哪些版本和哪些版本的唯一真理来源 功能由版本控制 Wymcp.ProtocolVersion康知道 解析器 Wymcp.Session.negotiated_version/1 是什么 Methods.Initialize, Methods.ToolsList, Methods.ToolsCall,以及 Wymcp.Context.elicit/4 所有咨询。
入门
1.添加依赖关系
在 mix.exs:
defp deps do
[
{:wymcp, git: "git@github.com:kristiangronberg/wymcp.git", tag: "v0.1.1"}
]
end2.创建工具
defmodule MyApp.Tools.Calculator do
use Wymcp.Tool
@impl true
def name, do: "calculator"
@impl true
def description, do: "Basic arithmetic"
@impl true
def actions do
%{
add: %{
description: "Add two numbers",
properties: %{
"a" => %{"type" => "number"},
"b" => %{"type" => "number"}
},
required: ["a", "b"],
defaults: %{}
}
}
end
@impl Wymcp.Tool
def run_action(:add, %{"a" => a, "b" => b}, _context) do
{:ok, %{result: a + b}}
end
end2b。模式和自我文档(可选)
默认情况下, Wymcp.Tool 发出满 oneOf 架构中 tools/list,给予 MCP为每个动作提供完整的输入合同。适用于多种工具 操作可能很大(每个工具约7-10KB,约2000个令牌)。
以(权力)否决 schema_mode/0 要改用苗条模式:
defmodule MyApp.Tools.Tasks do
use Wymcp.Tool
@impl true
def schema_mode, do: :slim # ~7x smaller tools/list payload
# ...
end在纤薄模式下, tools/list 返回一个包含操作枚举的紧凑模式 一行描述。我们的想法是,法学硕士可以发现更多的细节 在使用过程中逐渐:
帮助 --“我能做什么?”/“这需要什么参数?”
{"action": "help"} → summary of all actions
{"action": "help", "data": {"topic": "create"}} → slim schema (names + types)描述 --“告诉我关于这次行动的一切”
{"action": "describe", "data": {"topic": "create"}} → full schema + examples这两种操作都可以在完整和精简模式下工作。此外,通过以下方式调用操作 缺少必填字段将在错误响应中返回操作模式--a 自信的LLM可以尝试调用并从错误中学习,而无需显式的 帮助往返。
action_context --动态运行时信息
工具可以覆盖 action_context/1 将运行时上下文注入到任何 response——帮助、描述或正常操作调用:
@impl Wymcp.Tool
def action_context(:list) do
case MyApp.Tasks.count_overdue() do
0 -> nil
n -> %{tip: "#{n} tasks overdue — try actionable=true"}
end
end
def action_context(_action), do: nil当非nil时,映射出现在 "context" 在响应中键入。
帮助与描述:什么去哪里
- 帮助 是可操作的:“如何调用此操作。”模式、参数类型、,
必填字段,以及“先搜索以获取ID”等简短提示。想想看 作为函数签名。
- 描述 是上下文相关的:“关于这个域你应该知道什么。”日程安排
模式、良好的Jira过滤器示例、实时运行时上下文(如逾期计数)。 引用有助于获取参数详细信息,而不是重复。把它想象成 文档评论。
在实践中:如果信息有助于LLM构造有效的调用,请将其插入 help。如果这有助于法学硕士做出更好的决定 *是否* 或 *怎么* 到 打电话,把信息放进去 describe (通过 :notes 在动作模式中键入)。
2c。提示(后续行动建议)
工具可以通过从返回一个三元素元组来建议后续操作 run_action/2该框架称 hints/2 回调并注入 结果为响应:
@impl Wymcp.Tool
def run_action(:create, %{"name" => name}, _context) do
task = MyApp.Tasks.create!(name)
{:ok, %{message: "Created #{name}"}, %{id: task.id}}
end
@impl Wymcp.Tool
def hints(:create, %{id: id}) do
[
Wymcp.Hint.new(
tool: "tasks",
action: "get",
description: "View the created task",
example: %{data: %{id: id}}
)
]
end提示也适用于错误响应。返回 {:error, reason, hint_context} 为错误添加提示:
def run_action(:delete, %{"id" => id}, _context) do
case MyApp.Tasks.delete(id) do
:ok -> {:ok, %{message: "Deleted"}}
{:error, :not_found} -> {:error, :not_found, %{id: id}}
end
end每一个提示都是 Wymcp.Hint 结构体:
tool(必填,字符串)--工具名称action(必填,字符串)--动作名称description(必填,字符串)--为什么此操作相关example(可选,map)--示例data有效载荷
3.添加配置
在 config.exs:
config :wymcp,
name: "My MCP Server",
version: Mix.Project.config()[:version] || "0.1.0"4.添加路线
在 router.ex:
forward "/mcp", Wymcp.Router,
tools: [MyApp.Tools.Calculator]5.(可选)添加身份验证
实施 Wymcp.Auth 行为:
defmodule MyApp.McpAuth do
@behaviour Wymcp.Auth
@impl Wymcp.Auth
def authenticate(conn) do
with ["Bearer " <> token] {:error, "Invalid or missing Bearer token"}
end
end
end然后将其传递到路由器中:
forward "/mcp", Wymcp.Router,
tools: [MyApp.Tools.Calculator],
auth: MyApp.McpAuth当身份验证失败时,Wymcp返回HTTP 401,并返回 WWW-Authenticate: Bearer 根据MCP 2025-11-25规范的报头。
建筑
flowchart LR
CA(Consumer App) -->|implements| Tool
CA -->|implements| Auth
CA -->|implements| Server
Router --> Pipeline["Plugs.Pipeline"]
Pipeline --> Auth
Pipeline --> Validate["Plugs.Validate"]
Pipeline --> Dispatch["Plugs.Dispatch"]
Dispatch --> Methods["Methods.*"]
Methods --> Tool
Methods --> Session
Tool --> Schema["Tool.Schema"]
Tool --> Context
Tool --> Hint
Context --> Session
Router --> Session
Router --> StreamManager["Transport.StreamManager"]
StreamManager --> Stream["Transport.Stream"]
StreamManager --> Session
Stream --> SSE["Transport.SSE"]
Session --> Telemetry
Session --> Server
Validate --> JsonRpc模块
Wymcp.Router 是插头入口点。它接受 :tools (列表 Wymcp.Tool 模块)和可选 :auth 模块,然后 通过JSON解析、身份验证、MCP模式验证运行请求, 以及方法调度。消费应用程序将路由转发到此模块 不要直接与内部塞管道相互作用。
Wymcp.Tool 是消费应用程序的行为 实现以向LLM公开功能。每个工具都声明一个名称、描述、, actions/0 映射(模式)和a run_action/2 回拨。这 use Wymcp.Tool 宏生成 input_schema/0, run/2,以及 definition/0两个内置 行动提供渐进的自我记录: help 返回操作摘要 或精简每个动作模式(名称、类型、必填字段); describe 回报 完整的模式,包括示例、模式和约束。以(权力)否决 schema_mode/0 返回 :slim 减少约7倍 tools/list 以成本为代价的有效载荷 help/describe 不熟悉动作的往返。 现有 missing_required_fields 错误响应还返回模式 细节,因此自信的LLM通常可以跳过显式 help 完全通话。 返回 {:error, reason, hint_context} 从 run_action/2 为附加提示 错误响应——框架调用 hints/2 和 action_context/1 关于错误 成功也是如此。
Wymcp.Context 是 %Context{} 结构体作为 第三个论点 run_action/3 回拨。它承载着会议 引用、请求元数据,以及 assigns --每个请求的合并映射 conn.assigns (由auth等上游插件设置)和每个会话状态(由 之前的工具调用或初始化期间)。会话分配优先 在按键冲突时,累积的工具状态不会被插件默认值覆盖。 内部wymcp键被过滤掉,在中不可见 ctx.assignsThe 模块还提供纯结果构建器-- text/1, json/1, image/2, audio/2 --生成符合MCP的内容数组,供工具返回。 工具通过返回来更新会话持久状态 {:ok, content, assigns_updates},其中地图被合并到会话的 为未来的请求分配。
Wymcp.Hint 是后续行动建议的结构。 每个提示代表LLM可以采取的具体下一个动作, 动作名称、描述和可选的示例有效载荷。结构体验证 在构建时需要字段,拒绝原子 tool 和 action (强制JSON线格式),并实现 JSON.Encoder 用于序列化。 工具通过返回提示 hints/2 回调,由三个元素元组触发 从 run_action/2.
Wymcp.Auth 是Bearer代币的行为 身份验证。消费应用程序实施 authenticate/1 验证 凭据来自 Authorization 头球当否 :auth 提供选项 对于路由器, Wymcp.Auth.Noop 被用作通行证。认证 故障产生HTTP 401 WWW-Authenticate: Bearer 根据MCP规范。
Wymcp.Server 是挂钩的可选行为 MCP会话生命周期。实施 init/2 在会话时运行逻辑 准备就绪(之后 notifications/initialized 握手)和 terminate/2 关闭。这两个回调都有工作默认值,通过 use Wymcp.Server故意不 handle_request/2 回拨: 每个请求的关注点,如日志记录、速率限制和指标,都属于插件 放置在前面的中间件 forward "/mcp", Wymcp.Router,他们组成 当然,主机应用程序管道的其余部分也是如此。
Wymcp.Telemetry 文件 :telemetry 事件 由Wymcp发出,因此消费应用程序可以附加处理程序进行监控, 记录和度量。事件涵盖会话生命周期 ([:wymcp, :session, :start | :expired])工具执行 ([:wymcp, :tool, :start | :stop | :error])带有测量值和元数据 适用于 :telemetry_metrics.
Wymcp.Session 是保存状态的GenServer 单MCP会话:协商的协议版本、客户端和服务器 功能、服务器配置和每个会话分配。会话已创建 在...期间 initialize 握手并持续到客户端断开连接、发送 删除,或可配置的空闲定时器到期(默认30分钟)。每 会话是它自己的GenServer,而不是ETS行,因为采样和 启发需要在SSE流过程和 生成的工具任务。会话ID是32字节URL安全的base64字符串 仅满足MCP对可见ASCII字符的要求。
Wymcp.JsonRpc 处理JSON-RPC 2.0信封 构建和MCP协议模式验证。它编译MCP JSON模式 (priv/schema.json,2020-12方言)在构建时使用JSV,因此传入 请求在没有运行时的情况下根据官方协议定义进行验证 模式解析。
Wymcp.Response 是中的最低级别输出模块 管道。每个MCP响应——成功的工具结果、JSON-RPC错误,或 身份验证拒绝--通过 send_json/2,保存任何 之前设置了HTTP状态代码并停止连接,因此下游插件也会这样做 在发送响应后不执行。
Wymcp.Transport.StreamManager 是 拥有单个MCP会话的分块SSE连接的GenServer。它在运行 在...之下 Wymcp.StreamSupervisor 一 Task.Supervisor),发送keepalive评论 在可配置的计时器上防止代理超时,并推送服务器启动 会话调用时的SSE事件 push_event/2StreamManager和会话 互相监控——如果其中一个崩溃,另一个会清理干净。只有一个活跃的SSE 支持每个会话的流;新的GET替换了之前的流。
Wymcp.Transport.Stream 打开并管理 插头连接上的SSE响应。包裹 Plug.Conn.send_chunked/2 随着 SSE内容类型标头,并公开助手以将JSON-RPC消息作为SSE推送 事件。呼叫者负责通过以下方式安排keepalive评论 push_keepalive/1 (例如每15秒)以防止代理空闲断开连接。
Wymcp.Transport.SSE 是纯SSE事件编码 根据MCP流式HTTP传输规范。无进程状态,无侧 效果——只是转换JSON-RPC消息的字符串格式,可选 事件id进入 id: …\ndata: …\n\n 线格式。
Wymcp.Testing 提供测试助手 消费应用程序。功能如下 text_response/1, json_response/1,以及 error_response/1 打开MCP响应信封并断言内容类型, 从工具测试中删除样板。
