🇺🇸 English · 🇧🇷 Português

mead

CI Lint Coverage: 93%+ License: MIT Elixir 1.20+ Hex.pm

Runtime for LLM-backed assistants on Elixir/OTP: supervised sessions, a local/cloud model router, and an MCP client and server.

Quickstart

Requires Elixir 1.20+ on Erlang/OTP 29. Add mead to your dependencies in mix.exs:

def deps do
  [{:mead, "~> 0.1"}]
end

To work on mead itself, see CONTRIBUTING.md.

The default turn handler echoes, so a session runs without any API key (iex -S mix):

{:ok, session_id} = Mead.start_session()
Mead.submit_turn(session_id, "hello")
#=> {:ok, "hello"}

To route to real models, configure the router slots:

config :mead, :turn_handler, Mead.Router

config :mead, :router,
  slots: [
    local: {Mead.Provider.Ollama, model: "llama3.1"},
    cloud: {Mead.Provider.Anthropic, model: "claude-sonnet-5-5"}
  ]

Persistence, the MCP hub and server, and the deck endpoint are each enabled by their own config :mead key. docs/ARCHITECTURE.md lists every option.

Overview

Flow of a turn: a client prompt reaches a supervised session, the router sends it to a local or a cloud model, and tool calls pass through the MCP hub, which denies by default. The event log is written before sending and the ledger counts each turn.
  • Sessions. One supervised process group per conversation; a failure in a turn stays in that conversation.
  • Router. A stateless function that classifies each turn as local or cloud. route: :local or route: :cloud overrides it.
  • MCP. A client hub for the servers you configure, and a server that exposes sessions as nine tools.
  • Ledger. Tokens, tool calls and latency per session and per route, journaled to disk.

Guarantees

  • A local turn is never sent to the cloud; an empty local slot returns :local_unavailable.
  • MCP tools are denied unless the policy allows them; on the deck, :effect tools ask the person first.
  • The MCP server and the deck endpoint do not start without a bearer token.
  • Nothing is persisted or trimmed unless you configure storage and max_turns.

Design notes

DecisionTrade-off
Deck events are written to the log before they are sentOne synced write per event; a reconnecting client replays exactly what it missed.
The turn clock pauses while a permission request waitsA slow answer does not time the turn out; the wait has its own limit, permission_timeout_ms.
The ledger updates ETS in the emitting processA turn killed by timeout emits no event and is not counted.
History is never trimmed by defaultThe conversation file grows until you set max_turns.

agent-swarm-deck is the Flutter client that talks to mead over the deck WebSocket, and open-butler holds the assistant's domain logic. Neither is needed to run mead.

Contributing and security

See CONTRIBUTING.md, and SECURITY.md to report a vulnerability privately.

License

MIT © Alberto Cavalcanti


Built by Alberto Cavalcanti · Connect on LinkedIn · Docs · Architecture