PM Server — Architecture & Behavior

Claude Code 用プロジェクト管理 MCP サーバーの全体機能・データフロー・状態遷移マップ
バージョン 0.9.0 (2026-05-26 時点) · 37 MCP tools · 15 RO + 2 outbox-RW + 20 full-RW · 803+ tests · MIT
関連ドキュメント: User Guide (CLI & UX Tips) · Workflow Guide (作り方) · Cheatsheet (ja)

1. Overview

PM Server は Claude Code 用のプロジェクト管理 MCP (Model Context Protocol) サーバー。 タスク追跡・進捗可視化・ブロッカー検知・ADR 記録・横断ダッシュボード・workflow 状態マシン・ SQLite メモリ層 (FTS5 全文検索) を一体提供する。

言語Python 3.11+
主要フレームワークFastMCP, Pydantic v2, Click, Jinja2, tomlkit
データ形式YAML (human-readable, git-friendly) + SQLite per-project (.pm/memory.db) + global (~/.pm/memory.db) + outbox (~/.pm/desktop/desktop.db)
配布PyPI pm-server (Full RW) + .mcpb bundle (Lens RO / Desktop Outbox)
対象ホストClaude Code, Claude Desktop, Claude Cowork, Codex CLI
動作モードPM_LENS=0 Full RW / PM_LENS=1 Lens RO / PM_LENS=1 + PM_DESKTOP_WRITE=1 Outbox bridge (Phase 2)
ライセンスMIT

2. Module Architecture

18 個の Python モジュールが server.py (FastMCP インスタンス) を中心に連携する。 データ層 (models / storage / memory / outbox) が下位、機能層 (workflow / hooks / installer / discovery / velocity / dashboard / recall / rules) が中位、orchestration (server / __main__) が上位。 outbox.py は v0.8.0 で追加された Phase 2 Desktop Outbox SQLite store (ADR-019)。

__main__.py click CLI + hook subcommands server.py FastMCP + 37 @_tool() PM_LENS + PM_DESKTOP_WRITE gating workflow.py state machine hooks.py PostToolUse / Lens-aware installer.py Claude Code / Codex discovery.py depth-5 cap walk dashboard.py Jinja2 HTML/text velocity.py analytics + risks recall.py session context claudemd.py CLAUDE.md template rules.py codex AGENTS.md context.py CLI bootstrap storage.py YAML I/O memory.py SQLite + FTS5 (RO/RW) outbox.py append-only (ADR-019) models.py Pydantic v2 (17 models) utils.py — shared helpers CLI entry MCP server Data layer Feature Shared helpers

点線 (utils.py) は多数モジュールから利用される依存。discovery.py は深さ 5 cap + 除外 ディレクトリ + ~/.pm/ スキップで bounded walk を保証 (PMSERV-081)。 memory.py は PM_LENS=1 時に ?mode=ro&immutable=1 で SQLite を開き WAL sidecar の意図せぬ作成を防ぐ (PMSERV-080)。

3. MCP Tool Catalog

37 個の MCP ツールがカテゴリ別に提供される。配布モードによって登録される ツール集合が異なる: RO は Lens モード (PM_LENS=1) で利用可能、 OUTBOX は Lens + PM_DESKTOP_WRITE=1 で追加開放される Desktop Outbox 書込み tool、 RW は full pm-server (Claude Code / Codex 配布) のみ。

RO (Lens で利用可)
OUTBOX-RW (Desktop bridge only)
Full-RW (mutator / subprocess)
カテゴリツール用途モード
Project setup pm_init新規プロジェクトの .pm/ 初期化RW
pm_discoverサブツリー walk で .pm/ project を検出 (depth=5 cap)RW
pm_list~/.pm/registry.yaml の登録 project 一覧RO
pm_cleanup古いログ・記憶・summary の clean-upRW
pm_update_claudemdCLAUDE.md の pm-server セクションを最新化RW
pm_update_rulesCodex AGENTS.md を最新化RW
Status & navigation pm_statusフェーズ進捗・タスク数・blocker・diagnosticsRO
pm_next優先度・依存・phase で次タスク推薦RO
pm_dashboardHTML/テキスト ダッシュボード生成RO
pm_velocity4 週ベロシティ + 完了率RO
pm_risks遅延予測・blocker・期限超過のリスク一覧RO
Tasks & issues pm_tasksフィルタ付きタスク一覧RO
pm_add_task新規タスク登録 (ID 自動採番)RW
pm_update_taskstatus/priority/blocked_by 等の更新RW
pm_blockersblocked タスクと blocker 一覧RO
pm_add_issuedefect/enhancement issue を親 task 配下に追加 (defect で親 auto-revert)RW
Memory & session pm_rememberobservation/insight/lesson を SQLite 保存RW
pm_recalllast session + recent memories + FTS5 検索RO
pm_memory_searchtags / type / task_id / cross_project 検索RO
pm_memory_statstype 別カウント・最古/最新タイムスタンプRO
pm_memory_cleanup古い memory の dry-run / actual 削除RW
pm_logdaily log エントリ追記 (progress/decision/blocker/milestone)RW
pm_session_summarysession summary の save/get/listRW
Cross-host Outbox
(Phase 2, ADR-019)
pm_outbox_rememberDesktop → 共有 outbox に memory/lesson を append (host 共有 bridge)OUTBOX
pm_outbox_logDesktop → outbox に daily log entry を append (category 保持)OUTBOX
pm_outbox_pendingoutbox の pending/merged/rejected 一覧 (paginated)RW
pm_outbox_mergepending entries を target project の memory.db / daily log へ promoteRW
pm_outbox_rejectpending entries を reason 付きで reject (append-only 保持)RW
Decisions (ADR) pm_add_decisionArchitecture Decision Record の登録RW
Knowledge records pm_recordresearch/spec/tradeoff/risk_analysis を構造化記録RW
pm_knowledgelist/get/update/summary (update は RW)RW
pm_knowledge_queryRO 専用クエリ (Lens 用に切り出し PMSERV-079)RO
Workflow engine pm_workflow_startdevelopment/discovery/super-research 起動RW
pm_workflow_statusactive workflow の step 詳細・gate・artifactsRO
pm_workflow_advance次 step へ遷移 (gate user_approval 必要)RW
pm_workflow_abandonactive workflow を放棄RW
pm_workflow_listactive/completed/paused/abandoned 一覧RO
pm_workflow_templatesbuiltin + custom テンプレート一覧RO

15 RO + 2 OUTBOX-RW + 20 Full-RW = 37 ツール (2026-05-26, v0.9.0)。Lens 配布では RO_ALLOWLIST 外の ツールが FastMCP に未登録 = 構造的不変条件 (ADR-015/017/018)。OUTBOX 2 ツールは PM_DESKTOP_WRITE=1 が追加で立った時のみ登録され、main .pm/memory.db は touch しない (ADR-019)。

4. Lens Mode (Read-Only) Flow

PM_LENS=1 環境変数が立った pm-server プロセスは、起動時に RO_ALLOWLIST 外のツールを FastMCP に登録しない。これは Claude Desktop / Cowork のような passive ビューア host で「書き込み事故」を 構造的に防ぐ防衛機構。

Claude Desktop / Cowork .mcpb manifest を読込 manifest.json server.type = uv server.mcp_config.env.PM_LENS = "1" uv runtime PyPI から pm-server fetch pm-server プロセス env: PM_LENS=1 server.py:87 PM_LENS_ENABLED = True @_tool() decorator (L112-) name in RO_ALLOWLIST? → 登録 FastMCP に登録 (15 tools) pm_status, pm_next, pm_tasks, pm_blockers, pm_recall, pm_memory_search, pm_dashboard, pm_workflow_status … FastMCP 未登録 (22 tools) pm_add_task, pm_update_task, pm_log, pm_remember, pm_init, pm_cleanup, pm_workflow_start, pm_outbox_pending … MCP クライアントは RO 15 tools のみ確認 (+OUTBOX 2 if PM_DESKTOP_WRITE=1)
構造的強制 vs ポリシー宣言: manifest の server.mcp_config.env.PM_LENS=1 は配布意図のシグナル (host が読まなければ無効) だが、RO_ALLOWLIST による FastMCP 未登録は code レベルの構造的不変条件。 1 箇所のロジック漏れがあっても他方で防御するレイヤード設計 (ADR-015/017/018)。

5. Workflow Engine

3 つの builtin テンプレートでステートマシン化された開発プロセスを提供。各 step が tool_hint/skill_hint/agent_hint でガイダンスを返し、 gate: user_approval step では明示的承認待ち、loop: true step では 品質スコアによるループバックを許容する。

5.1 development (9 steps)

1. decision ADR 2. tasks pm_add_task 3. spec 4. plan 5. check user_approval 6. implement 7. test 8. quality user_approval 9. issues optional

5.2 super-research (6 steps, depth_check ループ付き)

1. scope pm_remember 2. parallel_research 3 agents 3. depth_check 6-dim, <3.0 ↺ 4. fact_check 5. cross_check 6. synthesis (KR 必須) user_approval score < 3.0 → loop back (iteration++)

5.3 discovery (調査・ブレスト)

scope → research → analysis → synthesis → proposal の 5 step。完了時に chain_to: development で次の開発 workflow 起動を提案する。

6. Task & Issue Lifecycle

タスクは todo → in_progress → review → done の主経路、別途 blocked (任意状態から推移可) を持つ。 issue (子 task) を severity=defect で起票すると親 task が自動的に done → review に巻き戻る。

todo in_progress review done pm_add_issue(severity=defect) → parent_reverted: done → review blocked blocked ↔ in_progress / review 注: user_approval gate (workflow side) と task status 巻き戻し (pm_add_issue defect) は連動して WF-025 step 8 quality の再判定対象を systematic に再構成する (CLAUDE.md ルール準拠)。

6.1 Issue severity ごとの挙動

severity親 task の自動状態変更warning使用シナリオ
defect (default)done → review (auto-revert)parent_reverted品質レビュー中に欠陥発見
enhancement変更なし将来改善・親に紐付く提案

7. Data Layout

YAML (human-readable, git-friendly) + SQLite (高速検索) のハイブリッド。プロジェクトローカル .pm/ と global ~/.pm/ が分離。

Per-project (<repo>/.pm/)

.pm/ ├── project.yaml # メタデータ + phases + pm_schema ├── tasks.yaml # 全タスク (parent_id で issue 親子) ├── decisions.yaml # ADR-NNN ├── workflows.yaml # WF-NNN 状態マシン ├── knowledge.yaml # KR-NNN (research/spec/etc) ├── memory.db # SQLite + FTS5 (per-project) ├── daily/ │ └── YYYY-MM-DD.yaml # pm_log エントリ └── registry.yaml # 内部メタ (project 自身の参照)

Global (~/.pm/)

~/.pm/ ├── registry.yaml # 登録 project 一覧 (path + name) ├── memory.db # cross-project SQLite (sync 先) └── <cache files> # dashboard cache 等 # discover_projects は ~/.pm/ を skip # (depth-5 cap + excluded dirs と二重防御)

Lens モード時の RO 担保

レイヤ挙動 (PM_LENS=1)
YAML filesread-only open (load_* のみ呼ばれ save_* は到達不能 — RO_ALLOWLIST 外)
per-project SQLite?mode=ro&immutable=1 で接続 — WAL -shm sidecar 不生成 (PMSERV-080)
global SQLiteMemoryStore(readonly=True)global_db_path=None 強制 — sync 経路無効化
memory.db 不在:memory: SQLite に fallback (search は空、エラーなし)

8. Distribution Channels

同一コードベースが 3 つの配布モードを持つ。PyPI は Full RW、.mcpb は構造的 RO (Lens)、 .mcpb + PM_DESKTOP_WRITE=1 は Lens に Outbox 書込みだけを足した bridge モード。

PyPI pm-server.mcpb bundle (Lens).mcpb + PM_DESKTOP_WRITE
対象 hostClaude Code (CLI) / Codex CLIClaude Cowork / その他 viewerClaude Desktop (Phase 2 bridge)
インストールpip install pm-serverpm-server install.mcpb ファイルを host に drag & drop.mcpb + manifest が PM_DESKTOP_WRITE=1 を auto-propagate (PMSERV-100)
登録 toolsFull RW (37 tools)Lens RO (15 tools)RO 15 + OUTBOX-RW 2 = 17 tools
環境変数未設定PM_LENS=1PM_LENS=1, PM_DESKTOP_WRITE=1
SQLite モードRW main + outbox?mode=ro&immutable=1 main / outbox 不触main は RO 不変、outbox ~/.pm/desktop/desktop.db は append-only RW
同一バイナリ?はい — 配布物は 1 種類、env で role gating
バージョン同期pyproject.toml が source of truth、manifest.jsonbuild_mcpb.py validator で lockstep (CI 強制)
共有 surface の安全性: 両者は ~/.pm/ と project 内 .pm/memory.db を共有しうるが、 Lens 側は memory.py:98-117?mode=ro&immutable=1 で RO 接続するため、 Claude Code 側が WAL を書いていても -shm sidecar の意図せぬ生成は発生しない (PMSERV-080 / FINDING-J)。

installer.py の PM_LENS / PM_DESKTOP_WRITE 認識

install_claude_code()--env PM_LENS=1 (+ PM_DESKTOP_WRITE=1) を claude mcp add に注入、install_codex() は TOML inline_table env = { PM_LENS = "1" } を section に書き込む。 InstallResult.lens_mode / InstallResult.desktop_write で caller が role を判別可能。 Phase 2 (PMSERV-100) で .mcpb manifest が PM_DESKTOP_WRITE を auto-propagate するため、 Desktop ユーザーは bundle drag & drop だけで outbox bridge が成立する。

9. Desktop Outbox (Phase 2 — ADR-019)

Claude Desktop (Lens) は本来 RO だが、PM_DESKTOP_WRITE=1 時に限り ~/.pm/desktop/desktop.db という専用 SQLite へ append-only で書き込める。 main の per-project .pm/memory.db は依然不可触 (Lens 不変条件保持)。 書込みは「outbox」として一旦溜まり、Claude Code 側で pm_outbox_pendingpm_outbox_merge / pm_outbox_reject でレビューして promote する。

9.1 Cross-host data flow

Claude Desktop PM_LENS=1 + PM_DESKTOP_WRITE=1 pm_outbox_remember pm_outbox_log ~/.pm/desktop/desktop.db append-only SQLite (PRAGMA user_version=1) desktop_outbox 行 • host_id / source_session • type: memory | lesson | log | artifact • content / tags / source_project • status: pending → merged | rejected • merged_to_id / merged_to_path / merged_at trg_outbox_append_only immutable columns への UPDATE は BEFORE で ABORT Claude Code Full RW (PM_LENS unset) pm_outbox_pending pm_outbox_merge pm_outbox_reject target <project>/.pm/memory.db MemoryStore.save() (memory/lesson) add_daily_log() (log → daily/YYYY-MM-DD.yaml) append poll pending promote on merge main <project>/.pm/memory.db は Desktop 側から決して触られない (Lens 不変条件保持)

9.2 状態遷移と invariant

段階tool結果不変条件
capturepm_outbox_remember / pm_outbox_logdesktop.db に INSERT (status=pending)main memory.db 不変
reviewpm_outbox_pendingfilter (project/type/status) + paginateRO クエリのみ
promotepm_outbox_mergemain project の memory / daily log に追記 → status=mergedappend-only trigger により past 行は immutable
discardpm_outbox_rejectstatus=rejected + reason 保存履歴は失われない (audit trail)
なぜ別 DB か: Desktop は信頼境界が薄いため (LLM が直接書込み)、main project の memory.db に そのまま書くと「LLM の hallucination が project の正史を汚染する」リスクがある。 Outbox を hop 1 段挟むことで、Code 側の human-in-the-loop review が必須化される。 append-only SQL trigger は「過去行を後から書き換える攻撃」を DB レイヤで構造的に防ぐ (= Lens RO 構造的不変条件と同じ防御哲学を写像)。

9.3 関連 ADR / 実装

ADR-019 (Phase 2 設計) / ADR-020 (ADR-017 amendment) / ADR-021 (ADR-018 amendment) · PMSERV-094..104 (実装), PMSERV-100 (manifest 自動伝播), PMSERV-105 (v0.8.0 release) · outbox.py + tests 30 cases + lens invariant 4 amendment tests (test_lens_mode.py) · storage 設計: ~/.pm/desktop/desktop.db (cross-project shared, project ローカルではない)