Passeur
具有OAuth 2.1和动态客户端注册(DCR)的自托管Elixir MCP服务器框架。构建与claude.ai、claude Desktop和claude Code配合使用的MCP服务器。
特性
- OAuth 2.1 支持PKCE的授权服务器(通过 博鲁塔)
- 动态客户端注册 (RFC 7591)用于自动客户入职
- MCP流式HTTP 运输(通过 hermes_mcp)
- 单用户管理员身份验证 使用Argon2密码哈希
- 众所周知的元数据 端点(RFC 8414、RFC 9728)
- 承载令牌保护 对于MCP端点
- CORS支持 适用于跨源MCP客户端
- 外行/博士后 用于OAuth客户端和令牌存储
需求
- 用于OAuth DCR和令牌存储的Postgres。
- 通过nginx、Caddy、Cloudflare隧道等进行外部SSL终止。
- Docker,如果你想将其部署为容器
快速开始
- 创建新的Elixir项目:
mix new my_mcp_server --sup- 在中添加passeur作为依赖项
mix.exs:
defp deps do
[
{:passeur, path: "../passeur"} # or from hex when published
]
end- 使用工具定义您的MCP服务器:
defmodule MyServer.MCPServer do
use Hermes.Server,
name: "MyServer",
version: "0.1.0",
capabilities: [:tools]
component MyServer.Tools.MyTool
@impl true
def init(_client_info, frame), do: {:ok, frame}
end- 定义一个工具:
defmodule MyServer.Tools.MyTool do
@moduledoc "Description of what this tool does"
use Hermes.Server.Component, type: :tool
schema do
field :input, {:required, :string}, description: "Input parameter"
end
@impl true
def execute(%{input: input}, frame) do
{:reply,
Hermes.Server.Response.tool()
|> Hermes.Server.Response.text("Result: #{input}"),
frame}
end
end- 在中配置passeur
config/config.exs:
config :passeur,
ecto_repos: [Passeur.Repo],
mcp_server: MyServer.MCPServer
config :boruta, Boruta.Oauth,
repo: Passeur.Repo,
contexts: [
resource_owners: Passeur.ResourceOwners
]- 在中配置环境
config/dev.exs:
config :passeur,
port: 4000,
admin_username: "admin",
admin_password_hash: "$argon2id$...", # generate with: mix passeur.hash_password
secret_key_base: "change_me_...",
server_url: "https://your-server.example.com"
config :passeur, Passeur.Repo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "my_mcp_server_dev"
config :boruta, Boruta.Oauth,
issuer: "https://your-server.example.com"- 设置数据库:
mix deps.get
mix ecto.create
mix ecto.migrate
mix passeur.hash_password # generate admin password hash- 运行服务器:
mix run --no-halt生产部署
HTTPS要求
OAuth 2.1需要HTTPS。Passeur提供纯HTTP,并期望反向代理来处理SSL终止。选项包括:
- Nginx代理管理器 --用于管理SSL证书和反向代理的web UI
- Cloudflare 隧道 --通过Cloudflare暴露您的服务器,而无需打开端口
- nginx/caddy --使用Let's Encrypt的传统反向代理
集 SERVER_URL 到您的公共HTTPS URL(例如。 https://mcp.example.com).反向代理应将流量转发到passeur的HTTP端口。
注: 如果使用Cloudflare Access,则必须为MCP服务器域创建绕过策略。Cloudflare Access拦截401个响应并中断OAuth发现流。
环境变量
所有配置都是从中的环境变量读取的 config/runtime.exs:
| 变量 | 必填 | 描述 |
|---|---|---|
DATABASE_URL | 是 | Postgres连接URL |
SECRET_KEY_BASE | 是 | 64+个字符的随机字符串 |
ADMIN_USERNAME | 是 | 管理员登录用户名 |
ADMIN_PASSWORD_HASH | 是 | Argon2哈希(来自 mix passeur.hash_password) |
SERVER_URL | 是 | 公共URL(例如。 https://mcp.example.com) |
PORT | 无 | HTTP端口(默认值:4000) |
POOL_SIZE | 无 | 数据库池大小(默认值:10) |
PASSEUR_STATIC_BEARER_TOKENS | 否 | 与Boruta颁发的OAuth令牌一起接受逗号分隔的静态承载令牌(见下文) |
静态承载令牌
除了嵌入式Boruta授权服务器颁发的OAuth访问令牌外,passeur还可以接受配置的静态承载令牌列表 /mcp 终点。这对于不运行OAuth流的服务到服务调用者非常有用——为每个服务提供自己的令牌,并通过重新部署并附加新值来轮换,然后删除旧值。
PASSEUR_STATIC_BEARER_TOKENS=svc-a-xxxxxxxx,svc-b-yyyyyyyy在落入Boruta之前,静态令牌会通过恒定时间比较进行检查。分配使用静态令牌进行身份验证的请求 %{static: true} 而不是OAuth令牌结构。保持变量未设置以禁用。
组成多个MCP服务器
由于每个MCP服务器都是一个实现 Hermes.Server,您可以从多个包中组合工具:
defmodule MyServer.MCPServer do
use Hermes.Server,
name: "MyServer",
version: "0.1.0",
capabilities: [:tools]
# Your own tools
component MyServer.Tools.MyTool
# Tools from other packages
component SomePackage.Tools.OtherTool
@impl true
def init(_client_info, frame), do: {:ok, frame}
end端点
| 端点 | 方法 | 描述 |
|---|---|---|
/health | GET | 健康检查 |
/mcp | POST/GET/DELETE | MCP可流式传输HTTP端点 |
/oauth/authorize | GET | OAuth授权 |
/oauth/token | POST | 令牌交换 |
/oauth/register | POST | 动态客户端注册 |
/oauth/revoke | POST | 令牌撤销 |
/login | GET/POST | 管理员登录 |
/.well-known/oauth-authorization-server | GET | OAuth元数据(RFC 8414) |
/.well-known/oauth-protected-resource | GET | 资源元数据(RFC 9728) |
许可证
麻省理工学院
