自托管MCP服务器框架

Passeur是一个自托管的Elixir MCP服务器框架,支持OAuth 2.1和动态客户端注册(DCR),适用于构建与claude.ai、Claude Desktop和Claude Code兼容的MCP服务器。

  • by jfim
  • Elixir
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 jfim/passeur

使用说明

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,如果你想将其部署为容器

快速开始

  1. 创建新的Elixir项目:
mix new my_mcp_server --sup
  1. 在中添加passeur作为依赖项 mix.exs:
defp deps do
  [
    {:passeur, path: "../passeur"}  # or from hex when published
  ]
end
  1. 使用工具定义您的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
  1. 定义一个工具:
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
  1. 在中配置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
  ]
  1. 在中配置环境 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"
  1. 设置数据库:
mix deps.get
mix ecto.create
mix ecto.migrate
mix passeur.hash_password  # generate admin password hash
  1. 运行服务器:
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)

许可证

麻省理工学院