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

This project is a work in progress and the API will change until we reach version 1.0.0.

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:

defp deps do
  [
    {:wymcp, "~> 0.1.0"}
  ]
end

2. Create a tool

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:

config :wymcp,
  name: "My MCP Server",
  version: Mix.Project.config()[:version] || "0.1.0"

4. Add route

In router.ex:

forward "/mcp", Wymcp.Router,
  tools: [MyApp.Tools.Calculator]

5. (Optional) Add authentication

Implement the Wymcp.Auth behaviour and pass it to the router:

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
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

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 — or build it locally with mix docs:

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.