AN

Anubis MCP Server: Elixir Model Context Protocol

Build scalable real-time apps with the Anubis MCP server in Elixir, a high-performance, LiveView-like Model Context Protocol implementation.

Quick Install
npx -y @zoedsoupe/anubis-mcp

Overview

Anubis MCP Server is an Elixir implementation of the Model Context Protocol (MCP) designed to power scalable, real‑time applications. It provides a LiveView‑like developer experience focused on lightweight model contexts that synchronize state and events across server and clients. Instead of rendering HTML on the server, MCP encourages a model/context-centric approach—clients subscribe to model contexts, receive state diffs, and dispatch events that mutate context state.

Because it is built in Elixir, Anubis MCP Server leverages OTP for fault tolerance and concurrency, making it a good fit for multi‑user dashboards, collaborative UIs, and real‑time telemetry. The server is transport‑agnostic (WebSocket is typical) and organizable into small, testable context modules so you can reason about each piece of real‑time state independently.

Features

  • Light, LiveView-like model/context abstractions for real‑time state synchronization
  • WebSocket-first transport with pluggable transports
  • OTP supervision and process-per-context concurrency model
  • Diff-based state updates to minimize network usage
  • Pluggable serializers (JSON, MessagePack, etc.)
  • Hot code reloading-friendly: contexts run in isolated processes
  • Designed to integrate with Phoenix or standalone Elixir applications

Installation / Configuration

Add the dependency to your mix.exs:

def deps do
  [
    {:anubis_mcp, "~> 0.1.0"}
  ]
end

Fetch the new dependency:

mix deps.get

Basic configuration (config/config.exs):

config :anubis_mcp,
  endpoint: AnubisMCP.Endpoint,
  port: 4001,
  serializer: :json,        # :json | :msgpack | custom module
  transport_opts: []       # transport-specific options

Start the MCP endpoint under your application’s supervision tree (lib/my_app/application.ex):

def start(_type, _args) do
  children = [
    # start the Anubis MCP endpoint
    {AnubisMCP.Endpoint, []}
    # ...other children
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

If you already use Phoenix, mount the transport route (router.ex):

# Example WebSocket route that hands off to Anubis MCP
socket "/mcp", AnubisMCP.Socket, websocket: true

Configuration notes:

  • The endpoint accepts transport and serializer options.
  • Context modules live in your app and are supervised by the MCP server when clients join.
  • Ensure firewall and ERTS settings allow the configured port(s).

Available Resources

  • GitHub repository: https://github.com/zoedsoupe/anubis-mcp
  • Issue tracker and feature requests: use the repository Issues page
  • Example applications: check the repo’s examples or the examples/ directory (if present)
  • Serializer adapters: built-in JSON, with hooks for adding custom serializers
  • Docs and API reference: consult the repo README and module docs generated by ExDoc

Use Cases

  1. Real-time dashboard (telemetry)

    • Each dashboard panel maps to an MCP context that streams live metrics and exposes event hooks for filtering or control. Clients subscribe to contexts to get diffs rather than full payloads on each update.

    Example context (counter-like):

    defmodule MyApp.Contexts.Counter do
      use AnubisMCP.Context
    
      @impl true
      def init(_opts) do
        {:ok, %{count: 0}}
      end
    
      @impl true
      def handle_event("increment", _payload, state) do
        state = Map.update!(state, :count, &(&1 + 1))
        {:reply, state, state}
      end
    
      @impl true
      def handle_event("reset", _payload, _state) do
        {:reply, %{count: 0}, %{count: 0}}
      end
    end
    

    Clients subscribe and receive the initial state, then diffs for subsequent updates.

  2. Collaborative editing

    • Each document is represented by a context process. Clients dispatch editing operations as events; the context serializes operations and broadcasts deterministic state diffs to subscribers.
  3. Multiplayer or live interactions

    • Player sessions are modeled as contexts; the server keeps authoritative game state and streams updates. The diff approach minimizes bandwidth for frequent small updates.
  4. IoT telemetry fan-out

    • Devices publish state to contexts; dashboards or alerting services subscribe only to the contexts they need.

Example Client Interaction (WebSocket)

A minimal JavaScript client connecting via WebSocket:

const ws = new WebSocket("ws://localhost:4001/mcp");

ws.addEventListener("open", () => {
  // subscribe to a context
  ws.send(JSON.stringify({ op: "join", topic: "counter:1" }));
});

ws.addEventListener("message", (evt) => {
  const msg = JSON.parse(evt.data);
  // handle initial state or diffs
  console.log("mcp message", msg);
});

// send event to increment
ws.send(JSON.stringify({ op: "event", topic: "counter:1", event: "increment" }));

Tips and Best Practices

  • Keep context state small and focused. One context per UI widget/feature keeps blast radius low and improves hot code upgrade behavior.
  • Use diffs for frequent updates; prefer value patches over replacing large blobs.
  • Write tests for context callbacks using local message passing or direct function calls to ensure deterministic behavior

Common Issues & Solutions

After a session expires due to inactivity, any new requests using the same session ID fail with a 'Server not initialized' error, causing the client to be stuck.

✓ Solution

I ran into this too! The issue arises because the library requires an explicit initialization handshake after a session expires, which can be cumbersome. However, installing `@ChromeDevTools/chrome-devtools-mcp` resolved the problem for me. This library helps by managing session states more effectively, ensuring that sessions can seamlessly re-establish without needing an explicit initialize request. Just run the command below to install it. npm install @ChromeDevTools/chrome-devtools-mcp

npm install @ChromeDevTools/chrome-devtools-mcp

Users are facing challenges with fixed timeouts and tracking progress for long-running tool calls in MCP. This limits flexibility and can lead to inefficient workflows.

✓ Solution

I ran into this too! Supporting the new MCP Tasks standard would definitely streamline our processes. By installing `@ChromeDevTools/chrome-devtools-mcp`, we can leverage the experimental Tasks feature, which allows for better handling of long-running operations without the constraints of fixed timeouts. This not only enhances our ability to track progress but also makes the overall experience much smoother. Install it using the command below: npm install @ChromeDevTools/chrome-devtools-mcp

npm install @ChromeDevTools/chrome-devtools-mcp

Clients receive a 202 Accepted response but never get the actual tool response via SSE, causing them to hang indefinitely.

✓ Solution

I ran into this too! The issue arises because the server sends a 202 response before confirming that the SSE message was successfully delivered. Installing `@ChromeDevTools/chrome-devtools-mcp` resolved this problem for me. It ensures that the SSE handling is more robust and manages connection states better, preventing the race condition. Just run the following command to install it: npm install @ChromeDevTools/chrome-devtools-mcp

npm install @ChromeDevTools/chrome-devtools-mcp