A minimal Elixir framework for building APIs.
One concept. Every transport. Built-in devtools.
Mooncore replaces controllers, routes, and transport-specific handlers with a single idea: named operations called actions. Write the logic once, call it over HTTP, WebSocket, or directly from code — and watch it run in a live dashboard.
When you build a typical API, the same business logic ends up scattered: one version for HTTP routes, another for WebSocket handlers, test helpers that fake HTTP requests. Add a new transport and you're duplicating again.
Mooncore collapses this. You define operations by name — "task.create", "user.login" — and the framework handles how they're called. HTTP, WebSocket, or direct Elixir function call: the same handler, unchanged.
An action is just a named function
"task.create"
A map of input data
That's it
Mooncore routes any transport — HTTP POST, WebSocket message, AI agent call — into that same function. No controllers, no route files, no "which layer owns this logic" decisions.
Mooncore includes a built-in Model Context Protocol (MCP) server that exposes your application's internals to AI tools, IDE extensions, and custom integrations. It implements the Streamable HTTP transport with JSON-RPC 2.0.
Set MOONCORE_DEV_MODE=true environment variable on your system and enable mooncore_dev_tools in your config:
config :mooncore,
mooncore_dev_tools: true,
mcp_port: 4040 # default
The MCP endpoint is available at http://localhost:4040/mcp when the server is running.
Mooncore implements the MCP specification (protocol version
2025-03-26) over HTTP POST with JSON-RPC 2.0:
POST http://localhost:4040/mcp
Content-Type: application/json
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}}
Supported JSON-RPC methods include:
Example initialize response:
{
"protocolVersion": "2025-03-26",
"capabilities": { "tools": {}, "resources": {} },
"serverInfo": { "name": "mooncore", "version": "0.2.0" }
}
Tools are callable operations that can modify state or execute code.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "run_action",
"arguments": {
"action": "task.create",
"params": {"title": "New task"},
"auth": {"roles": ["user"], "user": "alice"}
}
}
}
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "eval",
"arguments": { "code": "Enum.map(1..5, & &1 * 2)" }
}
}
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "read_logs",
"arguments": { "tag": "action", "since_id": 42 }
}
}
Read-only resources provide context about the running application.
{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/read",
"params": { "uri": "mooncore://actions" }
}
Examples: mooncore://actions, mooncore://apps, mooncore://clients, mooncore://config.
Add the MCP server to your editor integration in .vscode/mcp.json:
{
"servers": {
"mooncore": {
"type": "http",
"url": "http://localhost:4040/mcp"
}
}
}
The dev server also exposes a simpler JSON API on the same port for direct HTTP access.
POST /api/mcp — Generic MCP request
POST /api/eval — Evaluate Elixir code
POST /api/action — Execute an action
GET /api/logs — Read logs (query: tag, since)
GET /api/actions— List all actions
GET /api/config — Get server config
The Watcher collects in-memory logs (ring buffer) used by the dashboard and MCP log tools.
Mooncore.MCP.Watcher.log(:custom, %{message: "Something happened"})
Mooncore.MCP.Watcher.read(:action)
Mooncore.MCP.Watcher.read_since(42)
Mooncore.MCP.Server.list_actions()
Mooncore.MCP.Server.list_clients()
Mooncore.MCP.Server.server_info()
Mooncore.MCP.Server.run_action("task.create", %{"title" => "Test"}, nil)
Mooncore.MCP.Server.eval_code("1 + 1")
Mooncore.MCP.Server.read_logs(%{"tag" => "action"})
All functions require mooncore_dev_tools: true and will error when disabled.
HTTP, WebSocket, Elixir call, MCP, message queues — one handler, every transport.
Run actions, inspect logs, browse files, read guides — all in the browser at localhost:4040.
AI agents connect directly. Discover actions, call them, iterate — no HTTP client setup required.
JWT (RS256) with role-based access control. Add to router in one line.
No ORM, no templates, no asset pipeline. Bring your own database and tools.
Parameters in, result out. No hidden state, no magic, easy to test.
Mooncore fits developers building:
Mobile apps, SPAs, microservices
WebSocket as a first-class requirement
Built-in tenant isolation
Where agents need to call into your application
Note: If you need server-rendered HTML, Phoenix is the better choice. Mooncore handles the API layer and gets out of the way of everything else.
Copy paste this prompt to your favorite AI coding agent and watch it build a working app:
Your API is on port 4000, devtools on port 4040.
Start building with Mooncore today. Minimal, focused, and AI-ready.