WebSocket-first のライブ UI と 自動生成される HTTP MCP API を備えた、ファイルベースルーティングの Elixir フレームワーク。
リンク
概要
Dialup は Next.js のような開発体験でライブアプリを構築できる Elixir フレームワークです。
中核は二つあります。人間向け UI は最初から WebSocket-first で動き、HTTP MCP API は同じページ宣言から自動生成されます。
各ページは 1 つの監視されたサーバー側アクターです。UI を <.dialup_action> と
<.dialup_region> で書くと、Dialup が tools/list、tools/call、read_scene を導出します。
REST API の二重実装は不要です。
人間ブラウザ ──WebSocket──► UserSessionProcess ◄──HTTP JSON-RPC── AI エージェント
│
command / set / navigate / handle_event
│
declare_action / dialup_action特徴
- WebSocket-first の人間 UI —
/wsと idiomorph によるライブ DOM 更新 - 自動生成される HTTP MCP API — action / region 宣言がそのままエージェントツールになる
- 同じイベント経路 — ブラウザ操作とエージェントの
tools/callが同一UserSessionProcessで直列化(command/set/navigate/handle_event) - HTTP MCP —
POST /mcp(Bearer token)でinitialize/tools/list/tools/call(POST /agent/:tokenは legacy) - エージェント向け discovery —
/.well-known/dialup-agent、ページ埋め込み、/llms.txt - スコープ付きセッショントークン — 有効期限・投影・ capability を細かく制御
- ファイルベースルーティング — ファイル配置がそのまま URL になる
- サーバーサイドステート — 1 タブ = 1
UserSessionProcess - コロケーション CSS —
.exの隣に.css、コンパイル時に自動スコープ化
クイックスタート
1. ジェネレータのインストール
mix archive.install hex dialup_new
2. 新規プロジェクト作成
mix dialup.new my_app
cd my_app
mix deps.get
mix run --no-halt
http://localhost:4000 にアクセス
エージェント対応ページの最小例
defmodule Dialup.App.Page do
use Dialup.Page
declare_action name: :increment, desc: "カウンタを増やす", params: %{}
def mount(_params, assigns), do: {:ok, Map.put(assigns, :count, 0)}
def agent_state(assigns), do: %{count: assigns.count}
def handle_event(:increment, _, assigns) do
{:update, Map.update!(assigns, :count, &(&1 + 1))}
end
def render(assigns) do
~H"""
<p>Count: {@count}</p>
<.dialup_action name={:increment}>+1</.dialup_action>
"""
end
end同じ <.dialup_action> が、ブラウザでは WebSocket 経由のボタンになり、エージェントには生成された MCP ツールとして見えます。
ブラウザで操作することも、トークンを取得して API を呼び出すこともできます(詳細は HTTP MCP API):
curl -X POST http://localhost:4000/mcp \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer TOKEN' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_scene","arguments":{}}}'
ドキュメント
mix docs --open で全文を参照できます。
- Events — WebSocket-first UI のイベントと更新
- HTTP MCP API — ツール自動生成・discovery・バージョニング
- Agent-native アプリ開発 — 実装ワークフロー
- セッショントークン — ライブセッションへの接続
- Authentication — HTTP 認証・MCP 権限(シーケンス図)
- Getting Started — インストールと基本
その他は guides/ を参照(ルーティング、状態管理、デプロイなど)。
アーキテクチャ
1 タブにつき 1 つの UserSessionProcess が動きます。サーバー側セッションアクターがページ状態・
宣言済み action・エージェント grant の single source of truth です。人間と AI エージェントは
異なるトランスポート(ブラウザは WebSocket、エージェントは HTTP JSON-RPC)から同じアクターに
到達します。
全体像
flowchart LR
humanBrowser["Browser (human)"] -->|"WebSocket via dialup.js"| userSessionProcess["UserSessionProcess"]
aiAgent["AI agent (MCP client)"] -->|"HTTP JSON-RPC (POST /mcp)"| userSessionProcess
dialupAction["dialup_action / declare_action"] --> toolsList["tools/list"]
dialupRegion["dialup_region / declare_region"] --> toolsList
toolsList --> toolsCall["tools/call"]
toolsCall --> userSessionProcess
userSessionProcess --> render["render/1 + agent_state/1"]
render --> humanUI["idiomorph DOM update"]
render --> agentJSON["JSON response (no push)"]POST /agent/:token は同じハンドラへの legacy な path-token トランスポートです。
人間操作(WebSocket)
sequenceDiagram
participant Browser as ユーザー / ブラウザ
participant JS as dialup.js
participant USP as UserSessionProcess
Browser->>JS: click / input
JS->>USP: WebSocket event
USP->>USP: command / set / navigate / handle_event
USP->>USP: version++
USP->>JS: render diff
JS->>Browser: idiomorph morph(DOM 更新)エージェント操作(HTTP MCP)
sequenceDiagram
participant Agent as AI agent
participant MCP as POST /mcp
participant USP as UserSessionProcess
Agent->>MCP: initialize
MCP->>Agent: server capabilities
Agent->>MCP: tools/list
MCP->>Agent: declared actions + built-ins
Agent->>MCP: tools/call read_scene
MCP->>USP: read actor state
USP->>Agent: structuredContent + currentVersion
Agent->>MCP: tools/call mutation + _version
alt version stale
MCP->>Agent: isError
Agent->>MCP: tools/call read_scene
Note over Agent: 再取得してから fresh _version で retry
else version current
MCP->>USP: apply mutation
USP->>Agent: updated structuredContent
endエージェントには WebSocket トランスポートも server push もありません。read_scene で
ポーリングします。
Agent-first browser handoff
エージェントが headless セッションを開始し、後から人間が参加する場合、join は 3 フェーズの ハンドシェイクです。ブラウザ URL を開いただけでは join は完了しません。
sequenceDiagram
participant Agent as AI agent
participant API as Dialup HTTP
participant USP as UserSessionProcess
participant Browser as 人間ブラウザ
Agent->>API: POST /_dialup/agent-session
API->>USP: start session + grant token
Agent->>API: tools/call issue_browser_url
API->>Agent: browserUrl
Browser->>API: WebSocket attach (tab_id + join_token)
Note over Browser,USP: cookie 未設定
Browser->>API: POST /_dialup/finalize-join
Note over Browser,USP: cookie 設定、join token 消費
Browser->>USP: __reconnect
Note over Agent,Browser: 人間と agent が同一 UserSessionProcess を共有トークン発行パターンは セッショントークン を参照してください。
宣言境界(declaration boundary)
- MCP tool になるのは
<.dialup_action>/declare_action/1と<.dialup_region>/declare_region/1のみです。生のws-eventやws-hrefなどの ad-hoc WebSocket フックは エージェントへ 自動公開されません。 confirm: :humanの action は人間 UI のみで利用可能です。HTTP MCP から呼ぶと isError tool result が返ります。- エージェントが見える state は
agent_state/1による allowlist 投影であり、assigns 全体の dump ではありません。
ライセンス
MIT