Skip to main content
Glama

KAIRAN

Claude Code / Codex などの agent が生成した markdown / HTML を、tool call ひとつでブラウザに表示するローカル MCP サーバー。

3ペインUI(セッション / ファイル / ビュー)

リビジョン差分(unified / side-by-side)

ダークモード

差分表示

ダークモード

  • 何個の agent から接続されても、表示サーバーは 1 つ・port は 1 つ(初回 tool call で自動起動、全員がいなくなると自動停止)

  • セッションには表示名を付けられる(start_session で agent が付け、ブラウザからいつでも変更できる)。ID は日時ベース(0814-1345)で自動採番され、URL に出る

  • サイドバーの各セッションから改名・アーカイブ・完全削除ができる。ファイルは表示中のツールバーから削除できる(どちらも元に戻せない)

  • URL は http://localhost:5766/<セッションID>/<ファイル名>。全 URL が deep link

  • 同じ名前で再 publish すると新リビジョンとして積まれ、リビジョン間の差分(unified / side-by-side)が見られる

  • 3 ペイン UI(セッション / ファイル / ビュー)+ SSE live update。新着 publish への自動追従は「新着に追従」トグルで制御

  • agent が終了したセッションは自動で archive され、サイドバーの「archived」トグルで表示できる。kairan restart を挟んでも、生きている agent のセッションは active のまま残る

  • agent を閉じて --resume / --continue で開き直すと、同じセッションに戻る(Claude Code のセッション ID を鍵にしている。この ID を持たない agent では従来どおり毎回新しいセッションになる)。セッションができるのは最初に kairan を使った時点なので、agent を立ち上げただけでは何も増えない

  • markdown は GFM + shiki シンタックスハイライト + mermaid 図に対応。HTML は iframe でそのまま実行できる

  • publish された HTML は kairan と同一オリジンで動く。実行中の文書にそのままインラインコメントを付けられるようにするための設計で、引き換えに文書のスクリプトは kairan の API(セッション・ファイルの削除、レビュー送信、ローカルファイルを開く操作)を叩ける。信頼できない HTML を publish しないこと(markdown 側は本体画面の script-src 'self' で inline handler を禁止している)

  • タブの favicon がステータスを示す。あなたの対応待ち(未回答の質問・agent がレビュー送信を待っている)があれば赤バッジ、タブを開いている間に届いた未読の publish があれば青バッジ。タブタイトルにも対応待ちの件数が出る

  • 表示中のファイルを「Finder で表示」「エディタで開く」「ダウンロード」できる。Finder / エディタは path で publish されたファイルを localhost から見ているときだけ出る(cloudflare tunnel 等のリモート閲覧ではダウンロードのみ)

  • publish 時に macOS 通知センターへ通知(設定で off 可)。terminal-notifier が入っていれば通知クリックでそのファイルをブラウザで開けるbrew install terminal-notifier。無ければ osascript 通知にフォールバック、クリック遷移なし)

  • 人間 → agent のフィードバックにも対応。文書にインラインコメントを付けて GitHub PR レビューのように一括送信でき、agent からの選択肢つき質問はその文書の末尾に埋め込まれたフォームとして出る

    • agent は待たない。回答・レビューは Stop hook(kairan hook stop)がセッションへ注入する。hook を入れていない agent は次の kairan tool call か list_feedback で回収する

    • 質問は 1 文書に 1 セットだけ。同じ文書に publish し直すと前の未回答の質問は置き換わるので、答えないまま溜まらない

    • 選択範囲へのインラインコメントは markdown・HTML のどちらの表示でも使える(HTML は実行したまま。ソース表示ではファイル全体へのコメントのみ)

    • 本文の横に常時並ぶコメントカードは markdown 表示だけ。HTML では iframe が内側でスクロールして位置を追えないため、ハイライトの hover / クリックでカードを出す

セットアップ

bun install
bun link   # `kairan` コマンドをグローバルに登録

Claude Code

claude mcp add --scope user kairan -- kairan mcp

Codex CLI

# ~/.codex/config.toml
[mcp_servers.kairan]
command = "kairan"
args = ["mcp"]

Stop hook(回答をセッションへ届ける・Claude Code)

質問への回答とレビューは、agent が待つのではなく Stop hook が注入する~/.claude/settings.jsonStop に足す:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "if command -v kairan >/dev/null 2>&1; then kairan hook stop; else exit 0; fi",
            "asyncRewake": true,
            "timeout": 3900,
            "statusMessage": "kairan: 回答を待っています"
          }
        ]
      }
    ]
  }
}
  • asyncRewake: true が要る。hook はバックグラウンドで走り、回答が届いた時点でセッションを起こす(timeout は秒。hookWaitMs より長くしておく)

  • 止まっているデーモンを hook が起こすことはない(デーモンが動いていなければ即座に終わる)。デーモンが動いている場合は、質問もレビューも無いターンでも hook は次の回答を待って hookWaitMs(既定 60 分)までバックグラウンドに留まる。agent のターン自体はブロックしない

  • ターン終了時に Stop hook error occurred · ctrl+o to see と出るのは正常。Claude Code は「モデルを起こす」合図に exit code 2 を使うため、成功時もエラー表示になる

  • hook を入れない場合も動く(回答は次の kairan tool call か list_feedback で回収される)。ただし人が答えたことに気付くのは agent が次に kairan を触ったときになる

なぜ hook 経由なのか: MCP の tool call は Claude Code のハーネス側で壁時計 270 秒で打ち切られる(progress 通知では延びない)。人間の応答を tool call の中で待つ設計は、答えるのに数分かかる質問では成立しない。

Related MCP server: feedback-mcp-server

tool

start_session

このプロセスのセッションを開始し、人間向けの表示名を付ける。最初の publish の前に一度呼ぶと、サイドバーでどの agent のセッションか見分けられる。以後 session を省略した tool call はここで始めたセッションに載る。

引数

説明

label

サイドバーに出る表示名。一意である必要はなく、ブラウザからいつでも変更できる

id

セッション ID(URL セグメント)を固定したいときだけ渡す。省略時は日時ベース(0814-1345)で自動採番

戻り値: { sessionId, label, url }

publish

markdown / HTML をブラウザに表示する。path(ファイルパス)か content(文字列)のどちらかを渡す。

引数

説明

path

表示するファイルのパス(content と排他)

content

本文の直接渡し(name 必須)

name

セッション内のファイル ID(URL セグメント)。省略時は path の basename。同名で再 publish = 上書き = 新リビジョン

format

markdown / html。省略時は拡張子から推定

session

publish 先のセッション ID(別プロセスから同じセッションを継続するときに使う)。省略時はこのプロセスのセッション

title

ファイルリストに表示するタイトル

questions

この文書について聞きたいこと(最大 8 問)。文書の末尾にフォームとして出る。各問は選択肢 + 自由記述を持つ。渡さなければ今ある質問に触らない / [] を渡すと取り下げ

open

true で強制オープン / false でオープン抑制

戻り値: { url, sessionId, fileId, revision, pendingFeedback, askId }pendingFeedback は未受領フィードバック件数、askId は文書に出ている質問セットの ID)

questionsブロックしない。同じ内容で publish し直しても同じフォームが残り(回答途中の入力も消えない)、内容を変えると前の未回答の質問は取り下げられて新しいものに置き換わる。答えないまま何個も並ぶことはない。

path で publish したファイルは元の絶対パスが記録され、ブラウザの「Finder で表示」「エディタで開く」から開ける(content で publish し直すと記録は消える)。パス自体は API の応答にも list_files にも出ない。

list_files

自セッション(または session で指定したセッション ID)の publish 済みファイル一覧。

request_review

人間にブラウザでのレビューを依頼する。ブロックしない。人間側はコメントを下書きとして溜め、総評とともに「送信」した時点でまとめて返る(GitHub PR レビューと同じモデル。コメント 0 件 + 総評空の「コメントなしで返す」も可)。送信されたレビューは Stop hook がセッションへ注入する。

各コメントの commentId・対象ファイル・引用文(選択範囲)・本文と、総評・スレッド返信・未回収の質問回答が届く。

wait_seconds を明示的に渡したときだけ、その秒数だけブロックして待つ(Stop hook を持たない agent 向け)。

reply_comment

request_review / list_feedback が返した commentId へのスレッド返信。resolve: true でコメントを解決済みにできる(人間側から再オープン可)。

list_feedback

ブロックせずに、送信済み・未受領のフィードバック(レビュー・質問回答)を回収する。agent が待っていない間に送信されたぶんの回収用。各項目は一度だけ返る。

CLI

kairan status    # デーモンの稼働確認
kairan restart   # デーモンの再起動(コード・設定変更の反映用)
kairan stop      # デーモンの停止(通常は不要: 全接続が消えると自動停止する)
kairan daemon    # デーモンをフォアグラウンド起動(通常は自動起動されるため不要)
kairan relink    # 過去のセッションを agent のセッションに繋ぎ直す(下記)
kairan hook stop # Claude Code の Stop hook 本体(settings.json から呼ばれる。手で叩くものではない)

復帰の鍵(agent のセッション ID)を持たない古いセッションに、Claude Code の履歴から鍵を埋め直す。鍵が無いセッションは --resume で戻れず、開き直すたびに新しいセッションができてしまうため、その復旧用。

  • ~/.claude/projects/*/*.jsonlCLAUDE_CONFIG_DIR を設定していればそちら)から、kairan の tool 呼び出しの結果だけを拾って対応付ける

  • 併せて畳まれていて中身が空のセッションを削除する(残したくない場合は --keep-empty

  • --dry-run で何をするかだけ表示できる

  • 適用時はデーモンを止めてから DB をバックアップし、終わったら元の状態に戻す。稼働中のセッションからは鍵を奪わない

コード変更の反映

  • デーモン側(Web UI・API・レンダリング・通知など大半のロジック): kairan restart で反映される。稼働中の agent は自動で接続し直すため、セッションは active のまま残る

  • stdio ランチャー側(tool 定義・入力解決): ランチャープロセスは agent が起動・保持しているため kairan 側からは再起動できない。agent の MCP 再接続で反映される(Claude Code は /mcp → Reconnect、または新しいセッションを開始)

設定

~/.kairan/config.json(すべて任意)と環境変数で上書きできる。優先度: 環境変数 > config.json > デフォルト。

キー

環境変数

デフォルト

説明

port

KAIRAN_PORT

5766

デーモンの listen port

host

KAIRAN_HOST

127.0.0.1

bind アドレス(127.0.0.1 / localhost / ::1 のみ。認証なしのため loopback 限定)

dataDir

KAIRAN_DATA_DIR

~/.kairan

SQLite / lock の置き場所

autoOpen

KAIRAN_AUTO_OPEN

session-first

session-first(セッション初回のみ自動オープン)/ always / never

reopenWhenNoTab

KAIRAN_REOPEN_WHEN_NO_TAB

true

publish 時にそのセッションを見ているタブが無ければ開き直す

notifications

KAIRAN_NOTIFICATIONS

true

macOS 通知センターへの通知

notifyOn

KAIRAN_NOTIFY_ON

all

all(上書きも通知)/ new-file(新規ファイルのみ)

openCommand

KAIRAN_OPEN_COMMAND

open

ブラウザを開くコマンド

editorUrl

KAIRAN_EDITOR_URL

vscode://file{path}

「エディタで開く」の URL テンプレート。{path} が publish 元の絶対パスに置換される(Cursor なら cursor://file{path})。空文字にするとボタンを出さない

followDefault

KAIRAN_FOLLOW_DEFAULT

true

UI「新着に追従」トグルの初期値

reuseTab

KAIRAN_REUSE_TAB

true

自動オープン・通知クリック時に既存の kairan タブを再利用する(Chrome 系 / Safari。初回に macOS の自動化許可が必要。false で常に新規タブ)

shutdownGraceMs

KAIRAN_SHUTDOWN_GRACE_MS

5000

全接続 0 になってから自動停止するまでの猶予

archiveGraceMs

KAIRAN_ARCHIVE_GRACE_MS

10000

デーモン起動後、生きている agent が接続し直すのを待つ時間。これを過ぎても接続の無い active セッションは archive する

feedbackWaitMs

KAIRAN_FEEDBACK_WAIT_MS

240000(4 分)

ブロックして待つ API(/api/feedback/wait)で待ち時間の指定が無かったときの既定。MCP tool call はハーネス側で 270 秒で切られるため、それより短くしてある

hookWaitMs

KAIRAN_HOOK_WAIT_MS

3600000(60 分)

Stop hook が回答を待つ時間。settings.json の hook timeout(秒)より短くしておく

設定ファイルのパス自体は KAIRAN_CONFIG_PATH で変更できる。

公開する場合の注意

kairan 自体は認証を持たない/api/* の POST に入っているのは cross-origin を弾く CSRF 対策であって認証ではなく、Origin ヘッダの無いリクエスト(MCP ランチャー・curl)は意図的に通す。cloudflare tunnel 等で外から届くようにする場合、tunnel 側で認証をかけること(到達できる相手はセッションの一覧取得も完全削除もできる)。

publish された HTML も同一オリジンで動く(実行中の文書にコメントを付けるための設計)。つまり文書のスクリプトは kairan の API を叩けるので、信頼できない HTML を publish しない。ローカルでの利便性を優先した割り切りで、agent が生成した文書を自分で見る用途を想定している。

アーキテクチャ

agent (Claude Code / Codex)
  │ stdio MCP
  ▼
kairan mcp(agent ごとに 1 プロセス。port は使わない)
  │ HTTP(初回 tool call 時にデーモンを自動 spawn・生存申告の SSE を維持)
  ▼
kairan daemon(全体で 1 つ・port 1 つ)── SQLite (~/.kairan/kairan.db)
  │ HTTP + SSE
  ▼
browser(3ペイン UI)
  • stdio プロセス = 1 セッション。プロセス終了(= agent 終了)で TCP が切れ、デーモンがセッションを archive する

  • デーモンは「active セッション 0 かつ 閲覧タブ 0」になると自動停止する。データは SQLite に永続化されているため、次回起動時も過去セッションを閲覧できる

  • 設計判断の経緯: .local/docs/adr/0001-stdio-launcher-shared-daemon.md

開発

bun test            # テスト
bun run typecheck   # tsc
bun run lint        # biome
bun run dev         # デーモンをフォアグラウンド起動

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that lets AI publish markdown documents to a clean, themed web UI. One tool call creates a shareable doc.
    8
    4
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    A lightweight MCP server that enables AI assistants to collect interactive user feedback via a browser window with full Markdown rendering and syntax highlighting.
    1
    22 npm
    3
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A local MCP server that provides shared, real-time context across multiple AI agents via WebSocket and MCP resource notifications, enabling collaborative workspaces, memory, tasks, and messaging.
    8 npm
    1
    MIT