Packages

Model Context Protocol server library for Elixir with Plug integration

Current section

Files

Jump to
wymcp README.md
Raw

README.md

# Wymcp
MCP (Model Context Protocol) server library for Elixir. A Plug-based
implementation of the MCP JSON-RPC 2.0 protocol with support for tools and
optional Bearer token authentication.
> ### API Changes {: .warning}
> This project is a work in progress and the API will change until we reach version 1.0.0.
<div data-toc />
## Supported MCP protocol versions
Wymcp is a **dual-era** server: it serves the modern era (`2026-07-28`) and
the legacy era (`2025-11-25`) on the same endpoint. `docs/glossary.md`
defines the two eras; which revisions each accepts, why `2024-11-05` is
refused, and what a client asking for something else is answered are all
documented at `Wymcp.ProtocolVersion`; how a request is sorted into a lane is
at `Wymcp.Plugs.Era`.
## Getting started
### 1. Add dependency
In `mix.exs`:
```elixir
defp deps do
[
{:wymcp, "~> 0.1.0"}
]
end
```
### 2. Create a tool
```elixir
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
end
```
Two framework behaviours a tool author meets next, both documented in full at
their modules: every server exposes a `help` tool that answers at three levels
(`Wymcp.Help`), and a tool can suggest follow-up actions by returning hints
(`Wymcp.Hint`).
### 3. Add config
In `config.exs`:
```elixir
config :wymcp,
name: "My MCP Server",
version: Mix.Project.config()[:version] || "0.1.0"
```
### 4. Add route
In `router.ex`:
```elixir
forward "/mcp", Wymcp.Router,
tools: [MyApp.Tools.Calculator]
```
### 5. (Optional) Add authentication
Implement the `Wymcp.Auth` behaviour and pass it to the router:
```elixir
defmodule MyApp.McpAuth do
@behaviour Wymcp.Auth
@impl Wymcp.Auth
def authenticate(conn) do
with ["Bearer " <> token] <- Plug.Conn.get_req_header(conn, "authorization"),
{:ok, user} <- MyApp.Accounts.fetch_user_by_api_token(token) do
{:ok, Plug.Conn.assign(conn, :current_user, user)}
else
_ -> {:error, "Invalid or missing Bearer token"}
end
end
end
```
```elixir
forward "/mcp", Wymcp.Router,
tools: [MyApp.Tools.Calculator],
auth: MyApp.McpAuth
```
Authentication runs per request on every MCP route — POST, the GET stream,
and DELETE. `Wymcp.Auth` documents the contract and the 401 challenge; `Wymcp.Router`'s `:www_authenticate` option
adds the RFC 9728 discovery hints a spec-following client looks for.
### 6. (Optional) Restrict browser origins
```elixir
forward "/mcp", Wymcp.Router,
tools: [MyApp.Tools.Calculator],
origin: ["http://localhost:4000"]
```
`origin:` is an allowlist of `Origin` header values — DNS-rebinding protection
for browser-based clients. `Wymcp.Plugs.OriginCheck` documents which requests
pass, which are refused, and the one case the default configuration leaves
unenforced.
## Documentation
Wymcp's documentation is published at
[hexdocs.pm/wymcp](https://hexdocs.pm/wymcp) — or build it locally with
`mix docs`:
- [`Wymcp`](https://hexdocs.pm/wymcp/Wymcp.html) — the map: every module,
what it owns, and why it exists, with the request-flow diagram.
- [Glossary](https://hexdocs.pm/wymcp/glossary.html) — canonical domain
terms and where each one is defined.
- [MCP 2026-07-28 overview](https://hexdocs.pm/wymcp/mcp-spec-2026-07-28-overview.html)
— the modern era's conformance map.
- [MCP 2025-11-25 overview](https://hexdocs.pm/wymcp/mcp-spec-2025-11-25-overview.html)
— the legacy era's conformance map.
The last two are maintainer yardsticks for planning wymcp's next revision, and
they are filed under **Development** in the sidebar; the glossary sits beside
this README, for every reader.