hex: noizu_mcp · open source · MIT

MCP, native to the BEAM.

A full Model Context Protocol SDK for Elixir — servers and clients, compile-time tool schemas, stdio and Streamable HTTP. Not a JSON-RPC wrapper.

{:noizu_mcp, "~> 0.4.0"} mix.exs
  • 0.4.0on hex
  • 2025-11-25MCP spec (negotiates to 2025-06-18)
  • stdio + Streamable HTTPtransports
  • 1,500+tests

Temporary sandbox · GitHub + Google login · OAuth required

One session. Your assistant, files and Postgres.

Hosted sign-in requires a configured GitHub or Google provider. If sign-in is unavailable, provider setup is still pending; the setup instructions below remain available.

mcp.elixirmcp.dev gives each authorization its own in-memory playground. Sign in with GitHub or Google, approve access, then browse the demo documents and the virtual device controls — the hosted sandbox is read-only for security. Your real files and databases are never connected.

Connect your MCP client

Add https://mcp.elixirmcp.dev/mcp as a remote connector. The browser flow takes you through provider login and MCP consent, using OAuth client registration and PKCE.

Ask: “List the demo files, then read /README.md and /workspace/notes.txt.” On the hosted instance these are read operations; writes become available when you run the demo locally (see below).

Files and virtual devices

/README.md, /catalog.json, /workspace and /dev — on the hosted instance everything is read-only for security. /workspace and /dev come alive in write mode when you run the demo locally.

Install the VFS daemon →

Read the same session from Postgres

The local engine and pg_mcp project the session as foreign tables. Reuse one OAuth token across the daemon and engine to see the same notes.

Install the extension and engine →

Sandbox data is temporary and isolated by authorization, not shared with other visitors. Refresh keeps the same session; a new authorization creates a fresh one. Data expires after one hour or a server restart. The /dev endpoints are virtual control files over WebSocket, not operating-system devices or local Unix sockets.

Set up the filesystem sandbox

Use Linux or macOS with Erlang/OTP and Python 3. The daemon materializes regular local files and synchronizes changes over an authenticated WebSocket; FUSE is not required. Latest release — mcp-mount escript plus mcp-fuse binaries for macOS and Linux.

1. Download the client

curl -fSLo mcp-mount.tar.gz https://github.com/noizu-labs-ml/elixir-mcp-lib/releases/latest/download/mcp-mount.tar.gz
tar -xzf mcp-mount.tar.gz
mkdir -p "$HOME/.local/bin"
install -m 755 mcp-mount/mcp-mount "$HOME/.local/bin/mcp-mount"

2. Sign in, authorize and connect

Review the OAuth helper, then sign in with a configured GitHub or Google provider. Credentials are saved to a private file. /mcp and /vfs are two transports for one protected resource: reuse this token file for both.

curl -fSLo oauth-token.py https://elixirmcp.dev/downloads/oauth-token.py
python3 oauth-token.py --resource /mcp --output "$HOME/.config/elixirmcp/demo-token.json"
mkdir -p "$HOME/elixirmcp-demo"
export MCP_MOUNT_TOKEN="$(python3 -c 'import json,pathlib; print(json.loads((pathlib.Path.home()/".config/elixirmcp/demo-token.json").read_text())["access_token"])')"
mcp-mount --url wss://mcp.elixirmcp.dev/vfs --mount "$HOME/elixirmcp-demo"
# In another terminal, once the initial sync completes:
cat "$HOME/elixirmcp-demo/README.md"
cat "$HOME/elixirmcp-demo/workspace/notes.txt"
cat "$HOME/elixirmcp-demo/dev/settings.json"
# Writes (workspace notes, /dev settings and echo) need local write mode — see step 3.

The downloaded escript runs on any host with Erlang/OTP — no Elixir compiler or source checkout needed. The escript cannot bundle its native filesystem watcher, so live write-back support varies by platform; add --ro for a download-only client. The hosted server itself is read-only, so --ro is client-side belt-and-braces, not the write barrier.

Settings accept theme (light or dark) and uppercase_echo (true or false), and /dev/echo replies to messages written to it. On the hosted instance you can read the current settings and echo state; changing them is a write-mode feature — run the demo locally to flip themes or echo messages. These controls affect only your sandbox.

Keep the daemon terminal open. It takes an initial snapshot, subscribes to this session’s changes and uploads local edits. Stop with Ctrl+C; downloaded files remain on disk. Access tokens expire within 15 minutes. Use python3 oauth-token.py --refresh --output "$HOME/.config/elixirmcp/demo-token.json", reload MCP_MOUNT_TOKEN and restart the daemon and engine. After the one-hour window, authorize again for a fresh sandbox. For the long-form walkthrough, see the VFS Mount Guide.

3. Run the demo locally (write mode)

The hosted sandbox is read-only; a local server defaults to read/write, so /workspace and /dev are editable. Clone the repository, run the app and issue a token for localhost — the OAuth helper accepts plain HTTP there.

git clone https://github.com/noizu-labs-ml/elixirmcp.dev.git
cd elixirmcp.dev/app
mix deps.get
mix phx.server
# In another terminal, from the same app/ checkout:
python3 scripts/oauth-token.py --issuer http://localhost:8080 --resource /mcp \
  --output "$HOME/.config/elixirmcp/demo-token.json"
export MCP_MOUNT_TOKEN="$(python3 -c 'import json,pathlib; print(json.loads((pathlib.Path.home()/".config/elixirmcp/demo-token.json").read_text())["access_token"])')"
mcp-mount --url ws://localhost:8080/vfs --mount "$HOME/elixirmcp-demo"
# /workspace and /dev are writable on a local server:
printf 'Hello from my filesystem\n' > "$HOME/elixirmcp-demo/workspace/notes.txt"
printf '%s' '{"theme":"dark","uppercase_echo":true}' > "$HOME/elixirmcp-demo/dev/settings.json"
printf 'hello device' > "$HOME/elixirmcp-demo/dev/echo"

Dev and test builds start in write mode (DEMO_READ_ONLY unset). To reproduce the hosted behavior locally, run with DEMO_READ_ONLY=true; a prod-mode local run opts back into writes with DEMO_READ_ONLY=false.

Connect Postgres through the engine

pg_mcp (v0.3.0) is the pgrx-based PostgreSQL extension behind this demo: it surfaces MCP servers as foreign data wrapper tables you can read and write with plain SQL. Every upstream call is brokered by the Noizu.MCP.Engine HTTP plug — Postgres never talks to MCP servers directly, and credentials stay in the engine. Supports PostgreSQL 16, 17 and 18. Source: pg/pg_mcp · extension guide.

1. Install the extension

Docker — layer the compiled extension onto a Postgres image (the fastest path):

# From the repository root (no registry needed for a stock postgres:17 base):
git clone https://github.com/noizu-labs-ml/elixir-mcp-lib.git
cd elixir-mcp-lib
docker build -f pg/docker/Dockerfile \
  --build-arg BASE_IMAGE=postgres:17 --build-arg FINAL_USER=root \
  -t pg_mcp:pg17 .
docker run --name pg17-mcp -e POSTGRES_HOST_AUTH_METHOD=trust -p 5432:5432 pg_mcp:pg17
# The image layer ships the extension (.so + control + SQL), so:
psql -h localhost -U postgres -c "CREATE EXTENSION pg_mcp;"
psql -h localhost -U postgres -X -f pg/docker/smoke-production.sql

From source — pgrx 0.19.2 (pinned by ADR-002), PostgreSQL 18 by default, 16/17 via --features flags:

# Requires Rust + a local Postgres 18 dev install with matching pg_config
# and headers. (PG 16/17: build with --features pg16 / --features pg17.)
git clone https://github.com/noizu-labs-ml/elixir-mcp-lib.git
cd elixir-mcp-lib
cd pg/pg_mcp
cargo install --locked [email protected]
cargo pgrx init --pg18 "$(command -v pg_config)"
cargo pgrx install -c "$(command -v pg_config)"

2. Start the engine with your existing session

The engine source includes HTTP bearer authentication. Use the demo launcher. The launcher reads the same token file as your VFS daemon and binds only to 127.0.0.1:4040.

# From the root of the elixir-mcp-lib checkout (cloned in step 1):
mix deps.get
curl -fSLo oauth-token.py https://elixirmcp.dev/downloads/oauth-token.py
curl -fSLo demo-engine.exs https://elixirmcp.dev/downloads/demo-engine.exs
# Reuse the SAME token file created for the VFS to share one sandbox.
DEMO_TOKEN_FILE="$HOME/.config/elixirmcp/demo-token.json" mix run --no-halt demo-engine.exs

The local bridge trusts processes on your own machine; keep it on loopback. The hosted upstream always requires OAuth. A separate Claude connector authorization gets a separate sandbox; it does not automatically share this helper’s token.

3. Read your session notes with SQL

Run this once in a local development database. The required URI filter selects the resource to read. Change the notes through the VFS or MCP, then run the SELECT again.

CREATE EXTENSION pg_mcp;
CREATE SERVER elixirmcp_demo FOREIGN DATA WRAPPER mcp_fdw
  OPTIONS (url 'http://127.0.0.1:4040', auth 'none', timeout_ms '15000');
CREATE SCHEMA demo;
IMPORT FOREIGN SCHEMA mcp FROM SERVER elixirmcp_demo INTO demo;
SELECT uri, text FROM demo.resource_contents
  WHERE uri = 'mcp+engine://demo/demo://workspace';

This demo uses a read-through foreign table. For durable local tables, see opt-in PostgreSQL cache synchronization: local indexed reads, SQL write-back and explicit conflict handling for sources that support conditional writes and durable operation replay. The hosted session sandbox does not provide durable synchronization. See the extension guide for supported versions and build details.

Why Elixir MCP

The whole Model Context Protocol surface, expressed in plain Elixir — not a thin wrapper around JSON.

Compile-time tools

Define tools with @mcp annotations and a compile-time schema DSL; JSON Schema (2020-12) is emitted for you via JSV. Schema mistakes die at compile time, not in a model loop at 2am.

Every transport that matters

stdio and Streamable HTTP out of the box, over Plug (Phoenix or standalone Bandit) — server side and client side. Plus a VFS surface over unix sockets and WebSockets.

Fully bidirectional

Sampling, elicitation, roots, OAuth 2.1 with PKCE, resource subscriptions with RFC 6570 templates. The whole MCP surface, not a subset.

Features

Everything a production MCP deployment needs, in one package.

Toolkits & discovery catalogs

Group tools into toolkits and expose discoverable catalogs.

Resources & prompts with completion

Expose resources and prompt templates, with argument completion.

VFS filesystem mounting

Mount virtual filesystems over unix sockets and WebSockets.

OAuth 2.1 resource server + client

Authorization with PKCE, built in — both directions of the flow.

Noizu.MCP.Test in-memory transport

Test servers and clients without sockets or subprocesses.

Concurrent per-session requests

Requests run concurrently within a session, on the BEAM.

Define a tool once.

No hand-written JSON Schema, no protocol plumbing — just Elixir.

defmodule MyApp.Tools.Search do
  use Noizu.MCP.Server.Tool,
    name: "search_docs",                       # module-derived default name
    description: "Full-text search over project documentation",
    annotations: [read_only_hint: true, idempotent_hint: true]

  input do
    field :query, :string, required: true, min_length: 2,
      description: "Search terms"
    field :limit, :integer, min: 1, max: 50, default: 10
    field :scope, :enum, values: [:all, :guides, :api], default: :all
  end

  @impl true
  def call(%{query: query, limit: limit, scope: scope}, _ctx) do
    {:ok, "#{length(run_search(query, limit, scope))} hits"}
  end
end

Compile-time schemas via the declarative DSL — no hand-written JSON Schema.

Before you mix dep

Is this hex elixir_mcp?

No. That package is skill bundling. This product is Elixir MCP; the hex package is noizu_mcp because elixir_mcp was already taken.

Do I need Phoenix?

No. stdio runs from a supervisor. Streamable HTTP is Plug — Phoenix or standalone Bandit.

Is 0.4.0 stable?

Pre-1.0: APIs may still move. Toolset/ACL/persistence interfaces have been frozen since 0.3.0 (ADR).

Ship your first MCP server today.

Follow the getting-started guide — a working stdio server in a few minutes, Streamable HTTP when you need it.