コンテンツにスキップ

Slack MCP Agent — システムアーキテクチャ

v1.3 / 2026-04-20

MVP のシステム構成を定義する。
先行プロジェクト caster-division-ai の思想(Custom Tool パターン、pause/resume、監査ログ)を継承しつつ、
本プロジェクト固有の要件(SQLite / シングルプロセス / 4 月 MVP)に最適化する。


1. 設計原則

# 原則 説明
A-1 シングルプロセスで十分 MVP はローカル実行 × 1 人運用。Backend・MCP Server・Web を個別プロセスで起動し、make dev で一括管理
A-2 Custom Tool ファースト セキュリティが重要な HR データ操作は Custom Tool で Backend 内完結。MCP Server は Managed Agent 経由のツール提供に使用
A-3 状態は SQLite に集約 Backend と Web が同一 DB ファイルを参照。Backend が書き込み、Web は読み取り主体
A-4 Slack が主 UI、Web が副 UI ユーザー体験は Slack で完結。Web は管理・監視・設定用
A-5 マルチテナント対応を構造で準備 全テーブルに tenant_id。MVP はシングルテナントだがスキーマレベルで将来に備える
A-6 caster-division-ai の継承 Custom Tool パターン、承認フロー、監査ログ、DashShell レイアウトを踏襲

2. システム構成図

flowchart TD
    subgraph user ["User / PM"]
        Slack["Slack\n(Channel / Agent Container)"]
        WebUI["Web 管理画面"]
    end

    subgraph backend ["Backend (Python)"]
        Bolt["Slack Bolt App\n(Socket Mode)\n- app_mention\n- assistant_*\n- actions"]
        Pipeline["Pipeline\nL1: Msg → Task\nL2: Task → Prompt\nL3: Prompt → Steps\nL4: Steps → Exec"]
        CTE["Custom Tool Executor\n(freee API 直接)"]
        Bolt --> Pipeline --> CTE
    end

    subgraph web ["Web (Next.js)"]
        SC["Server Components → SQLite 読取\nServer Actions → SQLite 書込\n(settings, audit)"]
    end

    subgraph anthropic ["Anthropic Cloud"]
        MAS["Managed Agent Session\n- L4 実行ランタイム\n- MCP Tool 呼出\n- tool_confirmation"]
        MCP["Combined MCP Server\n(scripts/combined_mcp_server.py, port 8001)\nhr-freee 系モック + Google Sheets + Slack DM"]
        MAS --> MCP
    end

    FreeeAPI["freee API\n(OAuth 2.0)"]
    SQLite["SQLite DB\nagent.db"]

    Slack -->|"Socket Mode"| Bolt
    WebUI -->|"HTTP"| SC
    CTE --> FreeeAPI
    SC --> SQLite
    backend --> SQLite
    MCP -->|"MCP (streamable-http)"| FreeeAPI

2.1 プロセス構成

MVP は開発容易性を優先し、MCP Server を 1 プロセスに統合scripts/combined_mcp_server.py)して運用する。mcp-servers/hr-freee/mcp-servers/common/ のパッケージは単体起動・テスト用に残っているが、make dev では使用しない。

プロセス 言語 ポート 起動コマンド 役割
Backend Python 8000 (設定用、Socket Mode のため外部公開不要) python -m src.app (in backend/) Slack Bot + パイプライン + Custom Tool Executor
Combined MCP Server Python 8001 python scripts/combined_mcp_server.py HR モック + Google Sheets + Slack DM を単一プロセスで提供
Web Node.js 3100 pnpm dev (in web/) 管理画面 (Next.js)
ngrok - - ngrok http --domain=$NGROK_DOMAIN 8001 MCP Server を Anthropic Cloud に公開

補助(必要時のみ個別起動):

プロセス ポート 用途
mcp-servers/hr-freee/server.py MCP_HR_FREEE_PORT (既定 8001) hr-freee 単体のユニットテスト・動作確認
mcp-servers/common/server.py MCP_COMMON_PORT (既定 8002) common 単体のユニットテスト・動作確認

2.2 通信経路

From To プロトコル 説明
Slack → Backend WebSocket (Socket Mode) Slack Events / Actions をリアルタイム受信
Backend → Slack HTTPS chat.postMessage, chat.update, views.open
Backend → Anthropic (L1-L3) HTTPS messages.create (Anthropic SDK)
Backend → Anthropic (L4) HTTPS beta.sessions.create/retrieve/events
Anthropic → MCP Server HTTP (streamable-http) Managed Agent がツール実行 (ngrok 経由、combined MCP の単一ポート)
Backend → freee API HTTPS Custom Tool Executor 内で直接呼出
Backend → SQLite ファイル I/O aiosqlite (WAL モード)
Web → SQLite ファイル I/O Server Components から直接読取

3. レイヤー構成

3.1 概要

flowchart TD
    subgraph presentation ["Presentation Layer"]
        P1["Slack (Block Kit cards / Agent Container)"]
        P2["Web (Next.js Server Components)"]
    end

    subgraph application ["Application Layer"]
        A1["Slack Event/Action Handlers"]
        A2["Pipeline Orchestrator"]
        A3["Web Server Actions"]
    end

    subgraph domain ["Domain Layer"]
        D1["L1-L4 Pipeline (LLM 呼出 + 状態遷移)"]
        D2["Custom Tool Executor (freee API)"]
        D3["Approval Gate (承認/却下/再生成)"]
    end

    subgraph infrastructure ["Infrastructure Layer"]
        I1["SQLite (aiosqlite / WAL)"]
        I2["Anthropic SDK (Messages + Managed Agents beta)"]
        I3["Slack SDK (slack-bolt)"]
        I4["freee API Client (OAuth 2.0)"]
        I5["MCP Servers (FastMCP / streamable-http)"]
    end

    presentation --> application --> domain --> infrastructure

3.2 各レイヤーの責務

レイヤー 責務 依存方向
Presentation Slack Block Kit JSON 構築、Web ページレンダリング → Application
Application ユーザーイベントの受信、パイプライン起動、状態更新の調整 → Domain
Domain ビジネスロジック(L1-L4、承認フロー、ツール実行) → Infrastructure
Infrastructure 外部サービス接続、永続化、SDK ラッパー (最下層)

4. 4 層パイプライン詳細

4.1 全体フロー

flowchart TD
    UserMsg["User Message"]

    subgraph L1 ["L1: Message → Task"]
        L1P["Claude messages.create (1-shot)\n→ Task JSON (title, desc, priority)\n→ DB: tasks INSERT\n→ Slack: Task Card 投稿"]
    end

    subgraph L2 ["L2: Task → Prompt"]
        L2P["Claude messages.create (1-shot)\n→ 実行方針テキスト\n→ DB: prompts INSERT\n→ Slack: Prompt Card (承認待ち)"]
    end

    Gate1{"人間が承認 or 却下"}
    L2Reject["却下 → 理由入力\n→ L2 再実行 (新バージョン)"]

    subgraph L3 ["L3: Prompt → Process"]
        L3P["Claude messages.create (1-shot)\n→ steps JSON (ツール呼出計画)\n→ DB: processes INSERT\n→ Slack: Process Card (承認待ち)"]
    end

    Gate2{"人間が承認 or 却下"}
    L3Reject["却下 → 理由入力\n→ L3 再実行 (新バージョン)"]

    subgraph L4 ["L4: Process → Execution"]
        L4P["Managed Agent Session\n→ Custom Tool (freee) / MCP (common)\n→ DB: executions INSERT/UPDATE\n→ Slack: Execution Card (進捗更新)\n→ 完了/失敗/中止"]
    end

    UserMsg --> L1
    L1 --> L2
    L2 --> Gate1
    Gate1 -->|"却下"| L2Reject --> L2
    Gate1 -->|"承認"| L3
    L3 --> Gate2
    Gate2 -->|"却下"| L3Reject --> L3
    Gate2 -->|"承認"| L4

4.2 L1-L3: Messages API 呼出

System Prompt 入力 出力 max_tokens
L1 prompts/l1_system.md <user_input> ラップされたユーザーメッセージ Task JSON (title, description, priority, taskType) 2048
L2 prompts/l2_system.md Task JSON + ツールカタログ 実行方針テキスト (自然言語) 8192
L3 prompts/l3_system.md 承認済み Prompt + ツールスキーマ (JSON) Process steps JSON (steps[]) 8192

共通パターン: - 全て anthropic.AsyncAnthropic.messages.create (1-shot) - モデル: claude-sonnet-4-6 (環境変数 CLAUDE_MODEL で上書き可) - System Prompt はファイルから読み込み

4.3 L4: Managed Agent Session

Anthropic の Managed Agents API (beta) を使用。Agent のランタイムは Anthropic Cloud で実行され、ツール呼出は MCP Server 経由。

sequenceDiagram
    participant B as Backend
    participant A as Anthropic Cloud
    participant M as MCP Server

    B->>A: sessions.create(agent_id, env_id)
    B->>A: events.send(user.message, steps)
    A->>M: MCP tool call
    M-->>A: tool result
    B->>A: sessions.retrieve (polling)
    A-->>B: status: processing / idle

    Note over B,A: tool_confirmation が来た場合
    B->>A: events.send(allow_once)

    B->>A: sessions.events.list (完了確認)
    Note over B: summary 抽出
パラメータ 説明
ポーリング間隔 3 秒 POLL_INTERVAL_SECONDS
最大ポーリング回数 120 回 MAX_POLL_ATTEMPTS (= 360 秒タイムアウト)
tool_confirmation allow_once で自動承認 MVP では全ツール自動承認

4.4 Custom Tool Executor

freee API を扱う HR 3 ツール (list_employees, list_attendance, calculate_overtime) は Custom Tool Executor で Backend 内実行する。L4 ポーリングループが custom_tool_use イベントを検知し、Backend 内の Router → freee Adapter → freee API の順に処理する。

flowchart TD
    MAS["Managed Agent Session"] -->|"custom_tool_use event"| Backend["Backend (polling で検知)"]
    Backend --> CTE["Custom Tool Executor"]
    CTE --> T1["list_employees\n→ freee GET /api/1/employees"]
    CTE --> T2["list_attendance\n→ freee GET /api/1/employees/{id}/work_records"]
    CTE --> T3["calculate_overtime\n→ list_attendance 結果を集計 (Backend 内計算)"]
    T1 --> Result["events.send(tool_result)"]
    T2 --> Result
    T3 --> Result
    Result -->|"Managed Agent Session 続行"| MAS

セキュリティ上の利点: - OAuth トークンが Anthropic Cloud に渡らない - ツール実行ログが Backend 内で完結 - Policy Engine (Post-MVP) で制御可能


5. 承認フローアーキテクチャ

5.1 承認ゲート

stateDiagram-v2
    [*] --> 生成中
    生成中 --> 承認待ち
    承認待ち --> 承認済み : 承認
    承認待ち --> 却下 : 却下
    却下 --> 生成中 : 理由入力 → 再生成
    承認済み --> [*]

Prompt と Process の 2 箇所に同一パターンの承認ゲートを配置。

5.2 却下 → 再生成

  1. ユーザーが [却下] をクリック
  2. views.open で理由入力モーダルを表示
  3. 理由を DB に保存 (rejection_reason, rejected_by)
  4. 前バージョンの内容 + 却下理由を含めて L2/L3 を再実行
  5. 新バージョン (version + 1) を DB に INSERT
  6. Slack カードを新バージョンの承認待ちに更新

5.3 実行中止

  1. 実行中の Execution カードに [中止] ボタンを表示
  2. クリック → Managed Agent Session を停止 (sessions.cancel)
  3. Execution ステータスを cancelled に更新
  4. cancelled_by, cancelled_at を記録
  5. Task ステータスを cancelled に更新

5.4 再実行

  1. 失敗した Execution カードに [再実行] ボタンを表示
  2. クリック → 同じ Process steps で新しい L4 Session を開始
  3. 新しい Execution レコードを INSERT
  4. Task ステータスを running に戻す

6. 認証アーキテクチャ

6.1 Slack 認証

項目 方式
アプリ配信 Socket Mode (WebSocket)
トークン SLACK_BOT_TOKEN (xoxb-), SLACK_APP_TOKEN (xapp-)
署名検証 SLACK_SIGNING_SECRET (Bolt が自動検証)
ユーザー識別 Slack user ID (event.user / body.user.id)

6.2 freee OAuth 2.0

sequenceDiagram
    participant W as Web 管理画面
    participant B as Backend/API
    participant F as freee

    W->>B: [接続] クリック
    B->>F: authorize URL 生成
    B-->>W: リダイレクト
    W->>F: freee で認可
    F-->>B: callback + auth code
    B->>F: token exchange
    F-->>B: access + refresh token
    Note over B: DB 保存 (暗号化)
    B-->>W: 接続完了リダイレクト
項目 方式
トークン保存 integrations テーブル (credentials JSON, 暗号化)
リフレッシュ 401 応答時に自動リフレッシュ → リトライ
スコープ employees:read, work_records:read
トークン管理 Backend プロセス内のみ。Anthropic Cloud には渡さない

6.3 Web 管理画面認証

フェーズ 方式 詳細
MVP HTTP Basic 認証 Next.js Middleware で全ルートに適用。ADMIN_USER / ADMIN_PASSWORD 環境変数
Post-MVP Firebase Auth + Google SSO @cast-er.com ドメイン制限。Caster 既存と同構成

6.4 Anthropic API

項目 方式
認証 API Key (ANTHROPIC_API_KEY)
SDK anthropic.AsyncAnthropic (Backend 内)
Managed Agent agent_id + environment_id で Session を作成

7. 永続化アーキテクチャ

7.1 SQLite 設計方針

項目 方式 理由
WAL モード 有効 読み書き並行性。Web の読取と Backend の書込が同時可能
ULID 全テーブルの PK 時系列ソート可能な分散 ID
JSON カラム steps, results, config, credentials, metadata, details 柔軟なスキーマ拡張
FK 制約 有効 (PRAGMA foreign_keys = ON) データ整合性
マイグレーション バージョン管理 (SCHEMA_VERSION + _MIGRATIONS dict) インクリメンタル DDL

7.2 テーブル一覧

テーブル 主要カラム 用途
tenants id, slug, name, agent_id, vault_id テナント管理
tasks id, tenant_id, title, status, channel, thread_ts タスク (L1 出力)
prompts id, task_id, version, content, status, rejection_reason 実行方針 (L2 出力)
processes id, task_id, version, steps (JSON), status, rejection_reason ステップ計画 (L3 出力)
executions id, task_id, process_id, session_id, status, results (JSON) 実行記録 (L4)
slack_messages id, task_id, card_type, channel, message_ts Slack カード ↔ DB マッピング
tool_permissions id, tenant_id, tool_name, permission, constraints (JSON) ツール権限 (Post-MVP)
audit_logs id, tenant_id, timestamp, actor_type, action, details (JSON) 監査ログ
settings id, tenant_id, config (JSON) テナント設定
integrations id, tenant_id, provider, status, credentials (JSON) 外部連携

7.3 Backend ↔ Web の DB 共有

flowchart TD
    BackendPy["Backend (Python, aiosqlite)\nINSERT/UPDATE\n(tasks, prompts, processes,\nexecutions, audit_logs)"]
    WebNode["Web (Node.js, better-sqlite3)\nSELECT (全テーブル)\nUPDATE (settings)\nINSERT (audit_logs)"]
    DB["agent.db (WAL)"]

    BackendPy --> DB
    WebNode --> DB
  • Backend: 主要な書き込み担当 (パイプライン進行)
  • Web: 読み取り主体 + 設定変更・監査ログ書込
  • WAL モードにより同時アクセスが安全

8. MCP Server アーキテクチャ

8.1 MVP 構成

MCP Server は Managed Agent がツールを呼び出す際のインターフェース。Backend が直接呼び出すのではなく、Anthropic Cloud 経由で使用される。MVP では HR 系モックと出力系ツールを 1 プロセス(combined) に統合して運用する。

サーバー ポート ツール 備考
Combined MCP Server (scripts/combined_mcp_server.py) 8001 list_employees, list_attendance, calculate_overtime(モック), write_rows_to_google_sheet, send_slack_dm make dev で起動する本線。Google Sheets / Slack DM は GOOGLE_SERVICE_ACCOUNT_JSON / SLACK_BOT_TOKEN 設定時のみ実 API。HR 系は常にモックデータを返す(本番実行は Custom Tool Executor で行う)
mcp-servers/hr-freee/ MCP_HR_FREEE_PORT HR モックツール 単体テスト・動作確認用。make dev では未使用
mcp-servers/common/ MCP_COMMON_PORT write_rows_to_google_sheet, send_slack_dm 単体テスト・動作確認用。make dev では未使用

8.2 ツール実行パスの使い分け (MVP)

MVP では freee HR ツールを Custom Tool Executor、出力系ツールを MCP Server で実行する。

ツール 実行パス 理由
list_employees Custom Tool Executor (Backend 内) freee OAuth トークン保護・個人情報
list_attendance Custom Tool Executor (Backend 内) freee OAuth トークン保護・個人情報
calculate_overtime Custom Tool Executor (Backend 内) freee OAuth トークン保護・個人情報
write_rows_to_google_sheet MCP Server common (Agent 経由) 書込系・低リスク
send_slack_dm MCP Server common (Agent 経由) 書込系・低リスク
判断基準 Custom Tool Executor MCP Server
認証情報の扱い Backend プロセス内で保持。Anthropic Cloud に渡さない MCP Server プロセス内で保持
監査 Backend ログ + audit_logs テーブル Agent イベントログ
適用範囲 freee API (HR データ) Google Sheets, Slack DM

8.3 ネットワーク要件

  • MCP Server は localhost で起動
  • Anthropic Cloud からのアクセスには ngrok トンネルが必要
  • setup-managed-agent.py で Agent 定義に MCP Server URL を登録

9. 非同期処理アーキテクチャ

9.1 イベント駆動フロー

Slack Bolt は同期ハンドラ内で ack() を呼び、重い処理は asyncio.new_event_loop() で非同期実行。

def handle_approve_prompt(ack, body, client):
    ack()  # 3 秒以内に応答

    async def async_handler():
        # L3 実行 (10 秒程度)
        # DB 更新
        # Slack カード更新

    loop = asyncio.new_event_loop()
    loop.run_until_complete(async_handler())

9.2 L4 ポーリング

L4 は Managed Agent Session のステータスを 3 秒間隔でポーリング。

flowchart TD
    Start["Backend"] --> Create["sessions.create()"]
    Create --> Send["events.send(user.message)"]
    Send --> Loop{"loop\n(max 120 回 = 360 秒)"}
    Loop --> Retrieve["sessions.retrieve() → status"]
    Retrieve --> CheckStatus{"status?"}

    CheckStatus -->|"processing"| EventsList["sessions.events.list()"]
    EventsList --> ToolConf{"tool_confirmation?"}
    ToolConf -->|"Yes"| AllowOnce["events.send(allow_once)"] --> Loop
    ToolConf -->|"No"| CustomTool{"custom_tool_use?"}
    CustomTool -->|"Yes"| RunTool["handle_custom_tool()\n→ events.send(tool_result)"] --> Loop
    CustomTool -->|"No"| Loop

    CheckStatus -->|"idle"| Summary["break → summary 抽出"]
    CheckStatus -->|"error"| ErrorHandle["break → エラー処理"]

: on_progress コールバックは run_l4_session() のシグネチャに存在するが、MVP 実装ではポーリングループから呼び出されていない。Slack の Execution カードは L4 開始時と完了・失敗・中止の遷移タイミングでのみ更新される。ツール実行ごとのリアルタイム進捗更新は Post-MVP(docs/post-mvp-roadmap.md 参照)。

9.3 中止処理

  • ユーザーが [中止] → Backend が sessions.cancel() を呼出
  • 次回ポーリングで cancelled ステータスを検知
  • ポーリングループ終了 → DB + Slack 更新

10. エラー境界

境界 エラー種別 対処
Slack → Backend Socket Mode 切断 自動再接続 (Bolt 標準)
Backend → Anthropic (L1-L3) API エラー / タイムアウト リトライ (最大 3 回) → 失敗カード表示
Backend → Anthropic (L4) Session エラー Execution ステータス failed → [再実行] ボタン
Backend → freee API 401 トークンリフレッシュ → リトライ
Backend → freee API 429 Retry-After に従いリトライ
Backend → freee API 5xx 最大 3 回 (exponential backoff)
Backend → SQLite ロックエラー WAL + BUSY_TIMEOUT で軽減
Web → SQLite 接続エラー エラーページ表示
MCP Server ツール実行エラー エラーテキストを Agent に返却 → Agent が判断

11. セキュリティ境界

flowchart TD
    subgraph trust1 ["信頼境界 1: Backend プロセス"]
        FreeeToken["freee OAuth Token\n(暗号化保存)"]
        SlackToken["Slack Bot Token\nAnthropic API Key\n(環境変数)"]
        Note1["認証情報は Backend 外に出ない\nLLM API に送信されるのは業務データのみ"]
    end

    subgraph trust2 ["信頼境界 2: Anthropic Cloud"]
        ManagedAgent["Managed Agent Session"]
        MCPExec["MCP Server 経由のツール実行"]
        DataPolicy["データ保持なしポリシー (API)"]
    end

    subgraph trust3 ["信頼境界 3: MCP Server プロセス"]
        Localhost["localhost 起動、ngrok 経由でのみ外部公開"]
        ToolExec["ツール実行は MCP Server プロセス内で完結"]
    end

    trust1 --> trust2
    trust2 --> trust3

LLM 入力のセキュリティ

  • ユーザー入力は <user_input> タグで隔離 (Prompt Injection 軽減)
  • HR 個人データ (氏名、勤怠) の LLM API 送信は許容
  • マイナンバー等の重要個人情報は Post-MVP で対策

12. Post-MVP 拡張ポイント

領域 MVP Post-MVP
テナント シングル (tenant_id はスキーマに存在) Multi-tenant Router + Tenant Config
認証 Basic 認証 Firebase Auth + Google SSO
DB SQLite PostgreSQL (or Firestore)
ツール Custom Tool + MCP (3+2 ツール) Shared Schema Layer + ベンダーアダプタ
デプロイ Railway 単一コンテナ (nginx + Backend + MCP + Web) Cloud Run / ECS (サービス分割)
監視 ログ出力のみ Datadog / Cloud Logging
Skills L4 システムプロンプト固定 Managed Agents Skills (動的ローディング)
Policy Engine 全ツール自動承認 tool_permissions テーブルで制御
Vault 環境変数 + DB 暗号化 Managed Agents Vault (テナント別)

変更履歴

日付 バージョン 変更内容
2026-04-18 v1.0 初版作成
2026-04-19 v1.1 freee ツール実行パスを Custom Tool Executor に一本化。MCP Server hr-freee を開発テスト用と明記 (issue #11)
2026-04-20 v1.2 デプロイを「ローカル」から「Railway 単一コンテナ」に更新 (issue #31)
2026-04-20 v1.3 実装実態に同期: MCP を combined 構成に修正、Web ポートを 3100 に訂正、L2/L3 max_tokens を 8192 に訂正、on_progress が未配線である旨を明記、Custom Tool Executor のポーリング分岐を追加