Compatibility Protocol regression Hex.pm Docs Elixir License: MIT

An Elixir library for building Model Context Protocol servers and clients. It speaks MCP 2026-07-28, with opt-in support for initialize-era clients (2025-11-25 and 2025-06-18) over HTTP and stdio.

snodo is at 0.x: the API may change between minor versions until 1.0.

  • Servers from inline blocks or ordinary modules, served over stdio, a built-in Streamable HTTP listener, or Plug and Bandit.
  • A client that calls any MCP server in process, over stdio, or over HTTP.
  • The 2026-07-28 surface: discovery, tools, resources and templates, prompts, completion, pagination, subscriptions/listen, progress, cancellation, and multi round-trip requests with elicitation.
  • No runtime dependencies in the core: it uses Elixir's built-in JSON and OTP. Optional sibling packages add Tasks, Plug, and full JSON Schema validation.

Packages

PackageAddsDocs
snodoProtocol core, router, server DSL, client, stdio and HTTP transportsHexDocs
snodo_plugSnodo.Transport.Plug for Plug and Bandit applicationsHexDocs
snodo_jsvFull JSON Schema 2020-12 validation through JSVHexDocs
snodo_tasksThe io.modelcontextprotocol/tasks extension with an application-owned store and runnerHexDocs
snodo_tasks_postgresPostgreSQL store for TasksHexDocs
snodo_tasks_sqliteSQLite store for TasksHexDocs

Add the packages you need to mix.exs. Each sibling brings snodo with it:

def deps do
  [
    {:snodo, "~> 0.3.1"},
    {:snodo_plug, "~> 0.3.1"}
  ]
end

Elixir 1.18 or later is required. The sibling packages live in this repository under integrations/ and extensions/.

Quick start

A server with one tool, one resource template, and one prompt:

defmodule Greeter do
  use Snodo.Server, name: "greeter", version: "0.1.0"

  tool "greet", description: "Create a greeting" do
    argument "name", :string, required: true

    @impl true
    def call(%{"name" => name}, _context), do: {:ok, "Hello, #{name}!"}
  end

  resource "profile", uri_template: "people://{name}/profile", mime_type: "application/json" do
    @impl true
    def read(%{"name" => name}, _context), do: {:ok, %{"name" => name}}
  end

  prompt "introduce", description: "Introduce someone" do
    argument "name", required: true

    @impl true
    def render(%{"name" => name}, _context), do: {:ok, "Introduce #{name} in one sentence."}
  end
end

Call it in process with Snodo.Client:

{:ok, client} = Snodo.Client.direct(Greeter.runtime())

{:ok, [%{"name" => "greet"}]} = Snodo.Client.list_tools(client)
{:ok, result} = Snodo.Client.call_tool(client, "greet", %{"name" => "Ada"})
result["content"]
#=> [%{"type" => "text", "text" => "Hello, Ada!"}]

Serve it over stdio from a script or release:

:ok = Snodo.Transport.Stdio.serve(Greeter.runtime())

or over HTTP, supervised:

children = [{Snodo.Transport.StreamableHTTP.Server, runtime: Greeter.runtime(), port: 4000}]

The same client connects to either:

{:ok, client} = Snodo.Client.connect({:stdio, "elixir", ["greeter.exs"]})
{:ok, client} = Snodo.Client.connect({:http, "http://127.0.0.1:4000/mcp"})

Guides

The examples are runnable scripts, each checked in CI.

Protocol support

2026-07-28 is the default and only required dialect. For clients that still send initialize, enable the older dialects on the server:

use Snodo.Server,
  name: "greeter",
  version: "0.1.0",
  protocols: [Snodo.Protocol.V2026_07_28, Snodo.Protocol.V2025_11_25, Snodo.Protocol.V2025_06_18]

They cover tools, resources, prompts, completion, and pagination, over HTTP without sessions and over stdio. They add no session storage.

Against the frozen official conformance suite, 32 of 37 2026-07-28 server scenarios pass. On the client side, 6 of 32 pass: 25 of the client scenarios cover OAuth, which Snodo.Client does not implement. The compliance guide lists what is measured and what is not. Design records from the project's history are in docs/history.

Development

mix setup            # fetch dependencies for every package; rerun after a mix.lock changes
mix quality          # format, compile, Credo, tests, examples, and every sibling package
mix quality.types    # Dialyzer across all six packages
mix snodo.contract   # the protocol contract inventory

Conformance and interop checks against the official TypeScript client live in conformance/ and interop/, and run in CI. Releases are made with release-please; see RELEASING.md.

Contributing

Open issues are labeled by priority, size, and area, and good first issue marks small, well-scoped starting points. CONTRIBUTING.md describes the workflow. AGENTS.md lists the setup, the gate commands CI runs, and the project's constraints, for people and coding agents alike. Report security problems privately, as described in SECURITY.md.

License

MIT. See LICENSE.