Design Pattern MCP について
Design Pattern MCP は、設計パターンの選定と適用を支援する、AI コーディングエージェント向け MCP サーバーです。自然言語のシステム説明からパターンを検索し、実装を含む設計の生成や品質評価を行います。複数言語のバリアントを備えたカタログを利用します。
主な機能
- 01システム説明から要件を分析
- 02設計パターンの検索と選定
- 03参加者と実装手順の生成
- 048つの品質軸による設計評価
- 05非同期設計ジョブの送信と確認
- 06カタログの閲覧とパターン取得
活用シーン
- 01新規システムに適したパターンの選定
- 02コーディングエージェントへの判断根拠の提供
- 03既存設計の品質属性の評価
MCP 対応
MCP サーバーとして、パターン分析、設計生成・評価、カタログ検索、非同期ジョブのツールをクライアントに提供します。ワークフロープロンプトとバリアントのリソースも公開します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
docker run -i --rm -e DESIGN_PATTERN_GENERATOR_API_KEY -e DESIGN_PATTERN_GENERATOR_PROVIDER -e DESIGN_PATTERN_GENERATOR_MODEL -e DESIGN_PATTERN_GENERATOR_BASE_URL -e DESIGN_PATTERN_PATTERN_DIRECTORY -e DESIGN_PATTERN_EMBEDDER_BASE_URL -e DESIGN_PATTERN_EMBEDDER_API_KEY -e DESIGN_PATTERN_RERANKER_BASE_URL docker.io/olkowa/design-pattern-mcp:1.0.1DESIGN_PATTERN_GENERATOR_API_KEYシークレットAPI key for the generator LLM (LiteLLM provider).DESIGN_PATTERN_GENERATOR_PROVIDERLiteLLM provider prefix, e.g. openai, anthropic, openrouter.DESIGN_PATTERN_GENERATOR_MODELModel name, combined as <provider>/<model> via LiteLLM.DESIGN_PATTERN_GENERATOR_BASE_URLOpenAI-compatible base URL (LiteLLM api_base).DESIGN_PATTERN_PATTERN_DIRECTORYDirectory with the catalog records (*.json main records plus per-language files). The image bakes the 438-record catalogue at /app/pattern.DESIGN_PATTERN_EMBEDDER_BASE_URLRequired: the dense retrieval leg embeds the 438-record catalogue during startup warmup and the server refuses to start when the endpoint is unreachable. TEI embeddings endpoint: on the Docker host use http://host.docker.internal:8080 (add --add-host=host.docker.internal:host-gateway on Linux); on a shared Docker network use http://design-pattern-tei-embed:8080.DESIGN_PATTERN_EMBEDDER_API_KEYシークレットAPI key for the embedder. Keyless TEI accepts any non-empty value; use 'not-needed' there.DESIGN_PATTERN_RERANKER_BASE_URLRequired: retrieval runs a mandatory TEI cross-encoder rerank whenever the fused pool has more than one candidate. On the Docker host use http://host.docker.internal:8080; on a shared Docker network use http://design-pattern-tei-rerank:8080.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
design-pattern-mcp
AI コーディングエージェントにデザインパターン選定の知見を提供する MCP(Model Context Protocol)サーバーです。システムの説明を自由記述で受け取ると、問題を分析し、該当するデザインパターンを取得します(組み込みパターンは 486 個あり、それぞれ 13 の具体的な言語と汎用バインディングで実現されています)。さらに、主要パターン、補助パターン、参加者、相互作用、実装手順、言語に応じたバリエーションを含む具体的な設計を生成し、設計レベルの 8 つの品質軸(テスト容易性、拡張性、保守性、シンプルさ、パフォーマンス、信頼性、セキュリティ、スケーラビリティ)に基づいて評価します。パイプライン(分析 → 重み付け ∥ 検索 → 生成 → 評価 → 制限付き改善)と通信仕様は、docs/implementation-guide.md に記載されています。
目次
- ⚡ クイックスタート
- AI エージェント経由でインストール
- 🔌 エージェントを接続する
- 🧑🏫 AI エージェント向け SKILL
- 🧪 ツールを使う
- 🛠️ ツール一覧
- 💬 プロンプト
- 📖 パターンカタログ
- architecture-pattern-mcp と連携する
- インストール方法の選択肢
- 設定
- 構造化推論
- 長時間実行ツールとタイムアウト
- ベンチマークと検証
- トラブルシューティング
- ビルドと開発
- 公開
- systemd サービス(Linux)
- ライセンス
⚡ クイックスタート
# 1. Clone
git clone https://git.odvlh.xyz/olli/design-pattern-mcp.git && cd design-pattern-mcp
# 2. Add your API key
export MINIMAXAI_API_KEY=your_key_here
# 3. Build + start (three images: MCP server + TEI embed + TEI rerank)
make docker-build-all
make docker-up
# 4. Demo (analyze-only: fast. For the full design: make client-design)
make client-analyze
make client-async # full design via the background job trio
サーバーは http://localhost:8822/mcp で streamable-http として起動します(開発用 Compose ではホストの 8822 をコンテナーの 8050 に割り当てます)。make client-analyze / make client-design は http://localhost:8822/mcp を対象とします。CLIENT_URL で変更できます。
AI エージェント経由でインストール
AI コーディングエージェントに、ビルド済みイメージから Docker スタックをインストールさせる場合(ローカルでのビルドは不要)、非対話型エージェント向け手順書(コピーして貼り付けるコマンド、想定される出力、検証プローブ)の INSTALL.md を参照してください。
Clone this repo, then follow INSTALL.md Path A (compose stack, port 8822).
My generator API key is: sk-...
INSTALL.md で次のいずれかの方法を選択してください。
- Path A — Compose stack(
:8822):評価・開発向け。sudo は不要です。TEI サイドカーのコピーを個別に使用します。 - Path B — systemd stack(
:8812):永続的なホストサービスで、起動時に自動起動します。sudoが必要です。共有のpattern-tei-infraTEI スタックを使用します。
エージェントには、リポジトリのクローン、グループメンバーシップが設定された Docker ≥ v2.20、約 8 GB の空き容量、MINIMAXAI_API_KEY(または INSTALL.md#switching-the-generator-llm に記載された別のプロバイダーのキー)、および linux/amd64 が必要です(arm64 の場合は make docker-build-all を使用してローカルでビルドします)。開発用スタックと systemd スタックは、既定のホストポート(8822 と 8812)で同時に実行できます。インストール後は、以下のエージェントを接続するに進んでください。
🔌 エージェントを接続する
Claude Code
# Install (one-time)
uv pip install -e .
# Run as stdio subprocess — pass the API key via env
claude mcp add design-patterns \
-e GENERATOR_PROVIDER=minimax \
-e GENERATOR_MODEL=minimax/MiniMax-M2.7 \
-e GENERATOR_BASE_URL=https://api.minimax.io/v1 \
-e GENERATOR_API_KEY=your_key \
-- design-pattern-mcp --transport stdio
Stdio モードでは、TEI エンドポイントに到達できる必要があります(デフォルトは http://design-pattern-tei-embed:8080、リランカーの URL は config/config.json から取得します)。Compose スタックのサイドカーを実行するか、EMBEDDER_BASE_URL / RERANKER_BASE_URL に独自の値を設定してください。ネットワークを使わずにスモークテストを実行する場合は、--offline を指定して起動してください(TEI の代わりに、決定的な語彙ベースのみの検索を行います)。
OpenCode
OpenCode は HTTP トランスポートを使用します。まずサーバーを起動し、その後 opencode を設定します。
# Terminal 1: start the server
make docker-up
# or locally:
uv run design-pattern-mcp --transport streamable-http --port 8050
# Terminal 2: add to ~/.config/opencode/opencode.json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"design-patterns": {
"type": "remote",
"url": "http://localhost:8822/mcp"
}
}
}
注: ジェネレーターの API キーは opencode の環境変数ではなく、サーバーが設定ファイル(
config/config.json、{env:...}プレースホルダー)から読み込みます。
Codex CLI
# Install (one-time)
uv pip install -e .
~/.codex/config.toml に追加します。
[mcp_servers.design-patterns]
command = "design-pattern-mcp"
args = ["--transport", "stdio"]
[mcp_servers.design-patterns.env]
GENERATOR_PROVIDER = "minimax"
GENERATOR_MODEL = "minimax/MiniMax-M2.7"
GENERATOR_BASE_URL = "https://api.minimax.io/v1"
GENERATOR_API_KEY = "your_key"
または、CLI から設定します。
codex mcp add design-patterns \
-e GENERATOR_API_KEY=your_key \
-- design-pattern-mcp --transport stdio
🧑🏫 AI エージェント向け SKILL
skills/design-pattern-mcp/ の SKILL は Oh My Pi(OMP) 向けに作成されています。この環境では、このサーバーのツールに xd://mcp__design_pattern_local_* デバイスとしてアクセスし、すべての MCP リクエストに期限が設定されます(OMP_MCP_TIMEOUT_MS → サーバーごとの timeout → 30 秒)。SKILL では、期限に応じたエントリーポイントの選び方、結果の読み方、ワークフロー全体のレシピをエージェントに説明します。
skills/design-pattern-mcp/
├── SKILL.md # OMP constraints, entry-point decision guide, device quick reference
└── references/
├── tools.md # 9 tool signatures, arg shapes, status codes
└── workflows.md # workflow recipes, result interpretation, troubleshooting
OMP にインストールする — SKILL ディレクトリを OMP のユーザースキルのルートにコピーします。
cp -r skills/design-pattern-mcp ~/.omp/agent/skills/
SKILL では、エージェントに次のことを説明します。
- OMP の 30 秒の期限に適したエントリーポイント:デフォルトでは非同期ジョブの 3 つのツールを使用します。
select_design_patterns(パイプライン全体)には 2~10 分かかります - ジョブに適したパス:エージェント自身がコードを書く場合は
analyze_design(判断と根拠を返し、設計は生成しません)、呼び出し元が設計を求める場合はパイプライン全体を使用します job_idだけが永続的なハンドルであること。これを保存してください(SQLite ジョブストア、JOBS_DB)descriptionの記述方法(コンポーネント名、役割、制約)と、language、paradigm、focusを個別の構造化引数として指定する方法overview.score(0~100)、findings[]、パートごとの選択結果の解釈方法
references/ のツールスキーマはクライアントに依存しません。期限とデバイスに関する説明のみが OMP 固有です。
🧪 ツールを使う
すべてのパイプラインツールでは、主要な引数として自由記述の description を指定します。以下の例では、どの MCP クライアントや API コンシューマーでも使えるよう、正確なツール呼び出しの形式を示します。
最初の選択を設計する
Claude Code(または任意の MCP クライアント)で、自然言語の指示を貼り付けます。
We're building the ingestion path of an IoT platform: a Kafka consumer
service parses 10k events/sec, an enricher adds geolocation from Redis,
and a writer persists to InfluxDB and S3. Components: consumer, parser,
enricher, sink. Constraints: at-least-once delivery, backpressure, Python.
エージェントは内部で select_design_patterns を呼び出します。サーバーは DesignResult を返します。内容は overview(主要パターン、補助パターン、原則、スコア、推論)、applications[](参加者、インタラクション、実装手順、コードスケッチ、バインドされたバリアントを含む language_binding)、interactions[]、quality_attributes、test_strategy、risks[] です。
コードを自分で書くコーディングエージェントは、代わりに analyze_design を呼び出してください。 同じ ANALYZE フェーズ(§1~§8:ブリーフの修正 → 判断パート → パートごとの検索 → 品質の重み付け → 選択)を実行し、そこで停止します。修正済みのブリーフ、判断パート、パートごとの選択結果(主要パターン、補助パターン、順位付けされた代替案、拒否リスト、重み)を取得できますが、アプリケーションの生成と評価は行われません。選択結果が示すのは、パートごとに適用する「もの」であり、その「方法」ではありません。参加者、実装手順、
コードスケッチ、言語バインディングは、そのペイロードから generate_design が具体化します(またはパターンごとに get_design_pattern で参照できます)。実装には、パターン名だけでなくアプリケーションを使用してください。select_design_patterns は引き続き設計用のパスであり、完全な実行時間の大部分を占める GENERATE → EVALUATE まで続行します。どちらの方法でも、判断には同じ ANALYZE ステージが使われます。呼び出しごとに新しい LLM 分析が行われるため、どちらのパスも冪等ではありません。後で具体化されたアプリケーションが必要になった場合は、analyze_design のペイロードを generate_design に渡してください。
または、エージェントからツールを直接呼び出します。
Call select_design_patterns with:
description: "IoT ingestion path: Kafka consumer (10k events/sec) → JSON parse
→ Redis geo-enrich → InfluxDB + S3 sink. At-least-once, backpressure."
language: "python"
Call list_design_patterns() # all 486 patterns
Call list_design_patterns(category="workflow-messaging")
Call get_design_pattern(name="process-manager")
非同期ジョブパターン:submit_design_job + get_design_status
リクエストのタイムアウトが短いクライアント向けです。submit_design_job はすぐに job_id を返します。完了するまで get_design_status をポーリングしてください。
# Step 1: start the job
Call submit_design_job with:
description: "IoT ingestion path: Kafka consumer → parse → Redis enrich → InfluxDB + S3"
# Step 2: poll every 10-30 seconds
Call get_design_status with:
job_id: "<job_id from step 1>"
# → status is "pending" | "running" | "completed" | "failed" | "cancelled"
# When status is "completed", the full DesignResult is in result
# When status is "failed", the error is in error
Python の場合(MCP クライアント API 経由。examples/design_pattern_client_async.py を参照してください):
import asyncio
from fastmcp import Client
SERVER = "http://localhost:8822/mcp"
POLL_EVERY = 15 # seconds
async def main():
client = Client(SERVER)
async with client:
# Step 1: start the job
result = await client.call_tool(
"submit_design_job",
{"description": "IoT ingestion: Kafka → parse → enrich → sink"},
)
job_id = result.data["job_id"]
print(f"Job started: {job_id}")
# Step 2: poll until terminal state
while True:
await asyncio.sleep(POLL_EVERY)
status = await client.call_tool("get_design_status", {"job_id": job_id})
data = status.data
print(f" status={data['status']}")
if data["status"] in ("completed", "failed", "cancelled"):
break
print(data.get("result", data)) # full DesignResult when completed
asyncio.run(main())
実行可能な完全なサンプルは examples/design_pattern_client_async.py を参照してください。次のコマンドで実行できます。
make docker-up # Terminal 1
make client-async # Terminal 2
🛠️ ツールの概要
| ツール | 説明 |
|---|---|
select_design_patterns | 生成された設計を必要とする呼び出し元向けの設計パスです。完全なパイプライン(分析 → 重み付け ∥ 検索 → 生成 → 評価 → 改良、LOOP_MAX_TRIES 回の試行)を実行し、参加者、実装手順、バインドされたバリアント、品質属性、テスト戦略、リスクを含むアプリケーションを返します。実行時間が長い(2~10 分)。ハートビートで保護されています。 |
analyze_design | コードを自分で記述するコーディングエージェント向けの分析専用パスです。要件 → 概要、判断の要素、要素ごとの選択(主要、補助、代替、却下、重み付け)を返し、設計は生成しません。コーディングの前に、同じペイロードで generate_design を続けて呼び出すと、参加者、手順、言語バインディングを取得できます。実行時間が長い(約 1 分)。 |
generate_design | 以前の analyze_design のペイロードまたは明示的に指定したパターンからアプリケーションを生成します。実行時間が長い(約 1 分)。 |
evaluate_design | 既存の設計 JSON を 8 つの品質軸に照らして評価します。実行時間が長い(約 30 秒)。 |
submit_design_job | バックグラウンドの選択ジョブを開始し、すぐに job_id を返します(SQLite に保存され、サーバー再起動後も維持されます)。タイムアウトが短いクライアント向けです。 |
get_design_status | ジョブのステータスをポーリングします。completed の場合は完全な DesignResult を返します。 |
cancel_design | 実行中のジョブをキャンセルします(ベストエフォート。次のパイプラインステージの境界で有効になります)。 |
list_design_patterns | 名前、カテゴリ、説明とともにパターンを一覧表示します。category および/または language で絞り込めます。 |
get_design_pattern | 指定した名前の完全なパターン JSON を返します(ローダーで解決可能、エイリアス対応)。 |
説明、言語、パラダイム、フォーカスは構造化パラメーターです — 説明文に埋め込まず、個別のツール引数として渡してください。バリアントの選択はシグナルに基づきます。カタログ全体に共通する variant_id 列挙型はありません(バリアント ID はパターンと言語の組み合わせごとに異なり、カタログの編集に伴って変わります。有効な ID は design-patterns://variants リソースや、過去の実行における bound_variant で確認できます)。
💬 プロンプト
このサーバーでは、ユーザーが呼び出す 4 つのワークフロープロンプト(MCP クライアントのスラッシュコマンド)も公開しています。ツールとは異なり、LLM がプロンプトを自律的に呼び出すことはありません。ユーザーがプロンプトを選択し、引数を入力します。各プロンプトには検証済みのツールオーケストレーション手順が含まれており、パターン名をハードコードしていません(カテゴリの案内には、ライブカタログ自体の構成が引用されています)。
| プロンプト | 引数 | 内容 |
|---|---|---|
/select_design_patterns_workflow | description*、language、architecture_style、paradigm、focus | ガイド付きのエンドツーエンド選択ワークフロー |
/explore_pattern_catalog | category | design-patterns://variants リソースと判断プローブを使ったライブカタログの検索 |
/evaluate_my_design | description* | カタログによる選択結果を既存の設計と比較 |
/compare_pattern_candidates | decision*、candidates* | 1 つの判断について、指定した 2~3 個のパターンを並べて比較。候補ごとに選択を 1 回実行 |
* = 必須の引数
ツールのみをサポートするクライアント
ツールプロトコルのみをサポートするクライアント(ネイティブの prompts/list または prompts/get は非対応)でも、生成された list_prompts および get_prompt ツールを通じて 4 つすべてのワークフロープロンプトを利用できます。これらのツールは、ネイティブのプロンプト呼び出しと同じようにサーバーのミドルウェアチェーンを通ります。
📖 パターンカタログ
MCP ツール経由(推奨 — すべてのクライアントで動作します)
list_design_patterns() # all 486 patterns
list_design_patterns(category="concurrency-resilience") # filter by category
get_design_pattern(name="acceptor-connector") # full pattern JSON
有効な category の値(9つの判断グループ、カタログのセクション1~9):domain-modeling、construction-composition、behavior-selection、interaction-boundary、workflow-messaging、persistence-state、concurrency-resilience、security-verification、specialized-components。
MCP リソース経由
mcp_read_resource(uri="design-patterns://variants")
各パターンの言語ごとの実現形を、言語、次にパターンの順でまとめています。これは、language/paradigm/focus のヒントやバリアント選択に使う実現形の語彙です。
各パターンレコードに含まれる内容
ローダー(src/patterns/loader.py)は、14個のファイルから各レコードを結合します。内訳は、汎用エントリが1つと、具体的な言語ごとのエントリが1つずつ(13言語:C、C++、C#、Go、Java、JavaScript、Kotlin、Python、Ruby、Rust、Scala、Swift、TypeScript)です。各言語バインディングには、support(native/idiomatic/discouraged)、idiom_name、implementation_notes、省略可能な code_sketch、pitfalls が含まれます。また、discouraged のサポートでは alternative が必須です。言語バインディングでは variants[](言語ごとの実現形。カタログ全体で4,953件)と default_variant も宣言できます。各レコードにはさらに、related_patterns(ローダーで解決可能な正規名のみ)と related_patterns_notes(リンクごとの説明)があります。
エイリアス解決は組み込みです。ローダーは 993件の生エイリアスエントリ → 大文字・小文字を区別しない照合後に978件 → 966件のインデックス済みエイリアスマッピングに変換し、曖昧なエイリアス(別のレコード名そのものであるエイリアス、または2つのレコードが主張するエイリアス)を除外します。これらは ambiguous_aliases ヘルスフィールドに一覧表示されます。そのため、get_design_pattern(name="circuit-breaker") とその別名表記は同じレコードに解決されます。カタログの列挙型が docs/pattern-schema.json と一致しない場合、起動時に即座に失敗します(assert_enums_in_sync)。
architecture-pattern-mcp と連携する
このサーバーは「各ユニットをどのようにコーディングするか」(デザインパターン)に答えます。兄弟プロジェクトの architecture-pattern-mcp は「どのようなシステム構成にするか」(アーキテクチャ)に答えます。アーキテクチャパターンは、システム全体(コンポーネント、その関係、および API/データ/イベントの契約)を構成します(例:兄弟プロジェクトの40パターンのカタログから選ぶ event-driven、pipe-and-filter、microservices)。デザインパターンは、単一のコンポーネント内で繰り返し発生する実装上の問題(参加者、その相互作用、言語に依存するコード)を解決します(例:上記の486パターンのカタログから、各13言語のバインディングとともに選ぶ observer、process-manager、circuit-breaker)。
推奨される順序 — まずマクロ、次にミクロです。
1. Call the sibling's design_architecture with:
requirements: "ETL pipeline for IoT: Kafka → JSON → Redis geo-enrich → InfluxDB + S3"
domain: "data-processing"
# → components (Kafka source, parser filter, enricher, sinks),
# relationships, API contracts, data models, event contracts
# Keep design_architecture for callers that want the design: the sibling's LLM
# generates the architecture artifact (5–10 min) rather than only advising.
2. For each component with non-trivial internal logic, ask for the pattern decisions:
- coding agent writes the code itself → analyze_design with:
description: "<component name + responsibilities + constraints from step 1>"
language: "python"
# → decision parts + per-part selection with rationale; no generated applications
- caller wants the generated pattern design → select_design_patterns with:
description: "<component name + responsibilities + constraints from step 1>"
language: "python"
# → primary + supporting patterns, participants, implementation_steps,
# code_sketch with the bound language variant
どちらのパターン呼び出しも同じ ANALYZE ステージ(§1–§8)を実行します。analyze_design はそこで停止し、生成と評価には進みません。各呼び出しではそれぞれ独立した LLM 分析が実行されます(どちらのツールも冪等ではないため、同じ説明に対して2回呼び出しても異なる選択になる場合があります)。
IoT の例で具体的な受け渡しを示します。兄弟プロジェクトは pipe-and-filter アーキテクチャを出力します(Kafka ソース → JSON パーサーフィルター → 地理情報エンリッチャー → InfluxDB/S3 シンク。契約には少なくとも1回の配信とバックプレッシャーが含まれます)。地理情報エンリッチャーのキャッシュミス時の処理は、ここで扱います。エージェントがエンリッチャーを実装する際、analyze_design は根拠とともに circuit-breaker + retry の決定を返します。一方、呼び出し元が生成されたデザインを必要とする場合、select_design_patterns はアプリケーション(参加者、implementation_steps、Python の code_sketch)も生成します。これらの内部要素はアーキテクチャレベルのコンポーネント契約で参照されますが、その契約では実装されません。
analyze_design の経路では、エンリッチャーのコードを書く前に、返されたペイロードに対して generate_design を実行してください(または、Python バインディングと注意点について get_design_pattern("circuit-breaker") を実行してください)。
両サーバーを並行して実行してください(開発ホストのポートはアーキテクチャが :8060、デザインパターンが :8822、systemd は :8050/:8812 です)。両者は独立しているため、調査ではどちらを先に実行してもかまいませんが、先にアーキテクチャを決めることでコンポーネントの境界が安定し、このサーバーはその内部を補完できます。
インストール方法の選択肢
Docker(手動)
# Build the three images (dense vector cache generated and baked in)
make docker-build-all
# Run with your API key
MINIMAXAI_API_KEY=your_key make docker-up
最初に docker-build-all を実行すると、TEI 埋め込みサイドカーを介して FAISS の高密度キャッシュ(data/dense-cache/)が生成され、サーバーイメージに組み込まれます。make docker-build-nocache はこれをスキップします(初回起動時にコーパスを埋め込むため、約19分かかります)。
どちらの compose スタックも、そのキャッシュディレクトリを名前付き Docker ボリューム(design-pattern-mcp-dev-dense-cache/design-pattern-mcp-systemd-dense-cache、/app/data/dense-cache にマウント)で保持します。新しいボリュームはイメージに組み込まれたスナップショットから初期化され、それ以降の書き込みはすべてボリュームに保存されます。そのため、コンテナを再起動または再作成しても、カタログを再埋め込みせずにベクトルを読み込みます(約486件のドキュメントの埋め込みではなく、プローブ用の埋め込み1回で済みます)。docker compose down を実行しても、ボリュームは保持されます。
運用上の注意点は2つあります。イメージの再ビルド後、既存のボリュームは新しいスナップショットを隠し、フィンガープリントのガードによって再埋め込みがちょうど1回発生します。そのため、カタログまたはモデルに変更を加えて再ビルドした後は、まずボリュームを削除してください(docker volume rm design-pattern-mcp-dev-dense-cache)。その後、up -d で再初期化します。また、DENSE_CACHE_MODE=off はキャッシュを完全に無効化します。compose スタックはホストの値をコンテナに渡し、ホスト上で実行する場合はその値を直接読み込みます。詳細は高密度埋め込みキャッシュを参照してください。
ローカル開発(uv)
前提条件: Python 3.12 以降、uv
# Install
make install
# Configure — the shipped wire config with {env:...} placeholders
export GENERATOR_PROVIDER=minimax
export GENERATOR_MODEL=minimax/MiniMax-M2.7
export GENERATOR_BASE_URL=https://api.minimax.io/v1
export GENERATOR_API_KEY=your_key
# Dense cache (host runs): the image default /app/data/dense-cache does not exist
# on a workstation, so point it at the gitignored repo snapshot. `make embed-cache`
# fills it; otherwise the first start embeds once and writes it (later starts warm).
export DENSE_CACHE_PATH="$PWD/data/dense-cache"
# Run the server
uv run design-pattern-mcp --transport stdio # for Claude Code / Codex
uv run design-pattern-mcp --transport streamable-http --port 8050 # HTTP
uv run design-pattern-mcp --health # offline readiness check (config + catalog + index)
または、インストール済みのコンソールスクリプト(make install の実行後)を使用します:design-pattern-mcp。
高品質な検索には、TEI サイドカーが必要です。起動時に、密ベクトル検索(埋め込みサイドカー経由の Qwen3-Embedding-0.6B)と BM25 検索を統合します(service_healthy の順序指定により、サーバーがリクエストを受け付ける前に埋め込みモデルの起動が保証されます。インデックスのウォームアップに失敗すると、サーバーの起動は拒否されます)。再ランキングサイドカー(gte-reranker-modernbert-base)は、候補となるパターンを再ランキングします。サイドカーに接続できない場合は、--offline を指定して起動してください(ネットワークを使わない、決定的な語彙のみの検索です)。
TEI の起動: どちらのサイドカーイメージ(
docker/Dockerfile.tei-embed、docker/Dockerfile.tei-rerank)にも、WARMUP_TOKENS=0がデフォルトで設定された自前ビルドの TEI ルーター(v1.9.4 + 未マージのPR #884)が含まれています。これにより、合成ウォームアップ処理(実測値で埋め込みは約131秒、再ランキングは約78秒)を省略し、重みの読み込み後にコンテナーが準備完了になります。各サイドカーでは、TEI_WARMUP_TOKENS/TEI_RERANK_WARMUP_TOKENSで個別に上書きできます(正の値 N を指定すると N トークンでウォームアップします。MAX_BATCH_TOKENSを超える値は起動時に拒否されます)。
GPU での TEI: 同梱のサイドカーは CPU イメージ(cpu-1.9)です。TEI-CUDA 対応の NVIDIA GPU(コンピュート能力 ≥ 7.5)と NVIDIA Container Toolkit を備えたホストでは、make docker-build-tei-embed-gpu で CUDA ベース(ghcr.io/huggingface/text-embeddings-inference:cuda-1.9)の埋め込みサイドカーをビルドでき、make docker-up-gpu で docker/docker-compose.gpu.yml を使って開発スタックを起動できます(GPU を1基確保します)。このオーバーライドは埋め込みサイドカーのみを対象とし、再ランキング側は CPU のままです。GPU では、正の小さな WARMUP_TOKENS を指定すると、処理を省略せずに起動時にカーネルをウォームアップします。
稼働中の埋め込みモデル/再ランキングモデル: 埋め込みと再ランキングのエンドポイントが別のサーバーですでに稼働している場合は、EMBEDDER_BASE_URL(OpenAI 互換ルート、http://<host>:8080/v1)と RERANKER_BASE_URL(http://<host>:8080)を指定して MCP サーバーから接続し、サイドカーを省略できます。埋め込みモデルは LiteLLM 経由で EMBEDDER_MODEL(デフォルトは openai//data/embedding-model)と EMBEDDER_API_KEY を転送します。プロバイダーとモデルの構文については、LiteLLM の埋め込みドキュメントを参照してください。再ランキング側は LiteLLM クライアントではありません。{base_url}/rerank に直接 POST するため(src/patterns/safe_tei_rerank.py)、RERANKER_MODEL(デフォルトは Alibaba-NLP/gte-reranker-modernbert-base)には LiteLLM のモデル文字列ではなく、TEI モデル名を指定します。別のモデルを提供するリモート埋め込みモデルを使うと、組み込みの密ベクトルキャッシュは無効になります。ベクトルを提供中のモデルと一致させるには、make embed-cache で再生成してください。
設定
config.json
サーバーは、リポジトリを基準とする config/config.json を読み込みます(--config-path または CONFIG_PATH で上書きできます)。すべての値は {env:VAR:-default} 形式のプレースホルダーであり、読み込み時に展開されます。そのため、同梱ファイルを環境変数のリファレンスとしても利用できます。スキーマ(抜粋):
{
"logging_level": "{env:LOGGING_LEVEL:-INFO}",
"transport": "{env:TRANSPORT:-stdio}",
"host": "{env:HOST:-0.0.0.0}",
"port": "{env:PORT:-8050}",
"generator": {
"provider": "{env:GENERATOR_PROVIDER:-openai}",
"config": {
"model": "{env:GENERATOR_MODEL:-gpt-4o-mini}",
"base_url": "{env:GENERATOR_BASE_URL:-https://api.openai.com/v1}",
"api_key": "{env:GENERATOR_API_KEY:-}",
"temperature": "{env:GENERATOR_TEMPERATURE:-0.1}",
"timeout_seconds": "{env:GENERATOR_TIMEOUT_SECONDS:-120.0}",
"generation_batch_size": "{env:GENERATOR_GENERATION_BATCH_SIZE:-5}",
"generation_max_concurrency": "{env:GENERATION_MAX_CONCURRENCY:-2}",
"circuit_breaker_failure_threshold": "{env:GENERATOR_CIRCUIT_BREAKER_FAILURE_THRESHOLD:-3}",
"circuit_breaker_cooldown_seconds": "{env:GENERATOR_CIRCUIT_BREAKER_COOLDOWN_SECONDS:-60.0}"
}
},
"embedder": { "provider": "{env:EMBEDDER_PROVIDER:-tei}", "config": { "base_url": "{env:EMBEDDER_BASE_URL:-http://design-pattern-tei-embed:8080}" } },
"retrieval": { "bm25_weight": "{env:RETRIEVAL_BM25_WEIGHT:-0.3}", "dense_weight": "{env:RETRIEVAL_DENSE_WEIGHT:-0.7}", "max_concurrent_parts": "{env:RETRIEVAL_MAX_CONCURRENT_PARTS:-1}" },
"reranker": { "rerank_text": "{env:RERANK_TEXT:-full}", "rerank_top_n": "{env:RERANK_TOP_N:-10}" },
"reasoning": { "enabled": "{env:REASONING_ENABLED:-true}", "per_phase": { "analyze": { "enabled": "{env:REASONING_ANALYZE_ENABLED:-true}" }, "weights": { "enabled": "{env:REASONING_WEIGHTS_ENABLED:-true}" }, "generate": { "enabled": "{env:REASONING_GENERATE_ENABLED:-true}" }, "evaluate": { "enabled": "{env:REASONING_EVALUATE_ENABLED:-true}" } }, "max_injected_chars": "{env:REASONING_MAX_INJECTED_CHARS:-}" },
"validation": { "max_retries": "{env:VALIDATION_MAX_RETRIES:-1}", "retry_on_fail": "{env:VALIDATION_RETRY_ON_FAIL:-true}", "truncation_rerun": "{env:VALIDATION_TRUNCATION_RERUN:-repair}", "truncation_boost_max_completion_tokens": "{env:VALIDATION_TRUNCATION_BOOST_TOKENS:-65536}" },
"prompts": { "stable_prefix_first": "{env:PROMPT_STABLE_PREFIX:-false}" },
"loop": { "max_tries": "{env:LOOP_MAX_TRIES:-2}", "min_quality_score": "{env:LOOP_MIN_QUALITY_SCORE:-50}" },
"tasks": { "deadline_seconds": "{env:TASKS_DEADLINE_SECONDS:-1800.0}", "heartbeat_enabled": "{env:TASKS_HEARTBEAT_ENABLED:-true}", "heartbeat_interval_seconds": "{env:TASKS_HEARTBEAT_INTERVAL_SECONDS:-30}" },
"jobs": { "db_path": "{env:JOBS_DB:-~/.config/design-pattern-mcp/jobs.db}" }
}
生成用 LLM(LlamaIndex LiteLLM)
生成用 LLM には、LlamaIndex LiteLLM 統合を通じてアクセスします。すべてのプロバイダー設定で、LiteLLM のモデル構文である <provider>/<model> に従います。開発スタックとベンチマークハーネスでは、デフォルトで MiniMax を使用します。
| 設定/環境変数 | 開発スタックのデフォルト |
|---|---|
GENERATOR_PROVIDER | minimax |
GENERATOR_MODEL | minimax/MiniMax-M2.7 |
GENERATOR_BASE_URL | https://api.minimax.io/v1 |
GENERATOR_API_KEY | MINIMAXAI_API_KEY にフォールバックします(ホストの環境変数または .env) |
プロバイダー一覧と構文については、LiteLLM プロバイダーのドキュメントを参照してください。temperature、top_p、top_k、timeout_seconds、およびサーキットブレーカー関連の設定は、対応する LiteLLM/プロバイダーのパラメーターにマッピングされます。
密ベクトル埋め込みキャッシュ(再起動後も保持される検索)
デフォルトで有効です。 出荷時の設定では、dense_cache.mode=auto と
write=true が適用されます(Pydantic のデフォルト値と config/config.json の両方で設定されており、どちらの Compose スタックでも同じ値が明示的に設定されています)。また、サービングパスでは常にキャッシュが渡されます。そのため、コンテナスタックではキャッシュ設定は一切不要で、ホストで実行する場合に必要なエクスポートは 1 つだけです(DENSE_CACHE_PATH。Local Development ブロックを参照してください)。意図的に無効化するのは、DENSE_CACHE_MODE=off(Compose スタックまたはホストでの実行)、設定ファイルの mode=off、--offline/--health パス(決定論的エンベッダーを使用し、キャッシュなし)、およびベンチマーク用の Compose オーバーライド(コールドスタートの測定)の場合に限られます。
tests/unit/test_config.py::test_dense_cache_defaults と
::test_shipped_config_validates でデフォルト値を固定しています。
高密度検索レッグでは、カタログ内のすべてのレコードを対象とする 1 つの FAISS インデックスを使用します(486 レコードの場合、起動時に 486 個の
document_text 埋め込みを生成します)。この行列はマニフェストとともにディスクにスナップショットとして保存され、次回起動時に読み込まれます。そのため、再起動時に必要なのは全コーパスの処理ではなく、プローブ埋め込み 1 つだけです(CPU の TEI サイドカーでは約 19 分かかります)。
- パス:
DENSE_CACHE_PATH(イメージ内では/app/data/dense-cache)。 内容はmanifest.json(コミットマーカー)とvectors.npyです。使用可能なマニフェストがないディレクトリは通常のキャッシュミスとして扱われます。mode=requireではキャッシュミスが起動エラー(ERR_008)になり、mode=offではキャッシュの読み込みを完全にスキップします。 - 書き込みのタイミング:コールド状態での埋め込み直後(
DENSE_CACHE_WRITE=true)に書き込むため、その後の起動処理で失敗しても、スナップショットはディスク上に保存されています。ベクトルファイルはマニフェストより先に書き込まれるため、書き込み中にクラッシュすると、そのディレクトリは次回起動時にキャッシュミスとして扱われます。 - コンテナでは名前付きボリュームに保存されます
(
design-pattern-mcp-dev-dense-cache/design-pattern-mcp-systemd-dense-cache)。そのため、コンテナの再作成、docker compose down、イメージのアップグレード後もスナップショットが保持されます。新規ボリュームにはイメージに組み込まれたスナップショットがコピーされ、既存のボリュームにはその内容が保持されます。そのため、カタログまたはモデルを変更して再ビルドした後は、up -dの前にボリュームを削除してください(docker volume rm <name>)。削除しないとガード機能によって再埋め込み(約19分)が1回実行され、ボリュームが書き換えられます。真にコールド状態のキャッシュを使うには、キャッシュなしのイメージ(make docker-build-nocache)を使うか、mode=offを設定してください。DENSE_CACHE_MODE=offは、両方のcomposeスタックとホストでの実行に適用されます。 - 次の場合に自動で再構築されます:埋め込み関数が変更された場合(モデル文字列、テキスト指示、宣言された
model_repo/onnx_variant)、カタログが変更された場合(レコード数、名前の順序、またはいずれかのdocument_text――corpus_sha256)、vectors.npyが存在しない、形状が無効、またはノルムが不正な場合、あるいはプローブベクトルによって、設定が変わっていないのに異なるモデルが使われていることが判明した場合(TEIサイドカーが再デプロイされ、コサイン類似度がdense_cache.probe_min_cosineを下回る場合)。いずれの場合も、再埋め込みは1回だけ発生します。再構築したスナップショットは新しい識別情報とともに書き戻されるため、次回の再起動時には再びキャッシュがヒットします(たとえば、EMBED_MODEL_REPOがない旧リリースで作成されたキャッシュは、同じ理由で1回ミスします)。各判断は、使用したディレクトリとともにログに記録されます(composeスタックのJSONロガーフィールド):Dense cache hit(path、records、mode)、Dense cache miss(reason、path)、Dense cache written(path、records、dimension)、Dense cache disabled(mode、path)、Dense cache write failed(path、reason)。さらに、リトリーバーの要約としてDense vectors resolved(source=cache-hit/embedded/disabled、documents)も記録されます。 - ベストエフォートの書き込み:キャッシュディレクトリが読み取り専用または満杯の場合は
Dense cache write failedがログに記録され、実行は埋め込み直後のベクトルをそのまま使います。キャッシュが原因で起動に失敗することはありません。 - 単一の書き込みプロセス:ロック機構はありません。キャッシュディレクトリごとにサーバープロセスを1つだけ実行してください(開発用スタックとsystemdスタックには別々のボリュームが使われます。ベンチマークハーネスは専用の一時的な
data/dense-cacheを使い、mode=offでない限りコールド状態のキャッシュを拒否します)。 - ホストでの実行(
uv run python -m src.main)では、DENSE_CACHE_PATHに書き込み可能なディレクトリを指定する必要があります。標準設定の/app/data/dense-cacheはイメージ内にしか存在しません。export DENSE_CACHE_PATH=$PWD/data/dense-cacheを設定してmake embed-cacheのスナップショットを再利用するか、~/.config/design-pattern-mcp/dense-cacheを指定して初回起動時に書き込ませてください。
主な環境変数
| 変数 | デフォルト | 説明 |
|---|---|---|
GENERATOR_API_KEY | (必須) | Generator API キー(開発スタックでは MINIMAXAI_API_KEY にフォールバックします) |
GENERATOR_PROVIDER | openai | LiteLLM プロバイダーのプレフィックス(minimax、openai、anthropic、openrouter、…) |
GENERATOR_MODEL | gpt-4o-mini | モデル名。minimax/MiniMax-M2.7 は測定済みの開発スタックのデフォルトです |
GENERATOR_BASE_URL | https://api.openai.com/v1 | API ベース URL(LiteLLM の api_base) |
GENERATOR_TEMPERATURE | 0.1 | サンプリング温度 |
GENERATOR_TIMEOUT_SECONDS | 120.0 | モデルへの 1 回の HTTP リクエストに対するタイムアウトです |
GENERATOR_GENERATION_BATCH_SIZE | 5 | GENERATE バッチあたりのパターン数 |
GENERATION_MAX_CONCURRENCY | 2 | 同時に処理する GENERATE バッチ数 |
GENERATOR_CIRCUIT_BREAKER_FAILURE_THRESHOLD | 3 | サーキットを開く、連続して発生した対象プロバイダー障害の回数(タイムアウト、接続エラー、408/429/5xx、空のレスポンス)。認証/設定に関する 4xx はカウントされません |
GENERATOR_CIRCUIT_BREAKER_COOLDOWN_SECONDS | 60.0 | オープン状態からハーフオープンのプローブを 1 回実行するまでの時間 |
EMBEDDER_BASE_URL | http://design-pattern-tei-embed:8080 | TEI エンベッダーのエンドポイント |
EMBEDDER_BATCH_SIZE | 16 | 埋め込みのバッチサイズ |
DENSE_CACHE_PATH | /app/data/dense-cache (image) | Dense レッグのスナップショットディレクトリ(manifest.json + vectors.npy)。再起動時に再読み込みされるため、コーパスの埋め込み呼び出しはゼロです。エンベッダー、コーパス、プローブに変更があると再構築されます。ホストで実行する場合は、書き込み可能なパスで上書きしてください |
DENSE_CACHE_MODE | auto | auto(キャッシュミス時に再埋め込み)、require(キャッシュミス時は起動時に ERR_008 を発生)、off(キャッシュの読み書きを行いません) |
DENSE_CACHE_WRITE | true | 再埋め込みしたコーパスを保存します(ベストエフォート。書き込みに失敗すると Dense cache write failed をログに記録し、実行ではベクトルがそのまま使われます) |
RETRIEVAL_DENSE_WEIGHT | 0.7 | Dense レッグの融合重み(..._BM25_WEIGHT と合計して 1.0 ± 1e-3 である必要があります。設定検証の対象です。レッグの重みが 0.05 未満の場合は警告がログに記録されます) |
RETRIEVAL_BM25_WEIGHT | 0.3 | BM25 レッグの融合重み |
RETRIEVAL_MAX_CONCURRENT_PARTS | 1 | パートごとの再ランキングの同時実行数。上限を 2 にすると、CPU TEI 再ランキングサイドカーの 16,384 トークンバッファーが飽和し(HTTP 429)、処理が失敗します。サイドカーを実測していない場合は 1 のままにしてください |
RERANK_TEXT | full | 再ランキング入力テキスト。full(ドキュメントどおりのランキング)または capsule(3.6~4.3 倍高速ですが、ランキングが異なります。オプトイン) |
RERANK_TOP_N | 10 | 再ランキングの上位 N 件 |
REASONING_ENABLED | true | LLM の前段にある推論レイヤーのマスタースイッチ |
REASONING_{ANALYZE,WEIGHTS,GENERATE,EVALUATE}_ENABLED | true | フェーズごとの推論スイッチ。無効なフェーズでは空の推論コンテキストが使用されます(Generator 呼び出し、サブプロセス、キャッシュエントリーは発生しません)。有効なフェーズで障害が発生した場合は、プロンプト内のスキャフォールドにサイレントにフォールバックします |
REASONING_{...}_THOUGHTS | 2 / phase | 各フェーズのメイン呼び出し前に行う推論ステップ数 |
REASONING_STEP_TIMEOUT_SECONDS | 20 | 思考ごとのツール呼び出しタイムアウト |
REASONING_MAX_TOTAL_STEPS | 8 | フェーズごとの推論ステップ数の上限 |
REASONING_MAX_INJECTED_CHARS | 上限なし | 下流のプロンプトに挿入する、描画済み推論トレースの文字数上限。超過分は切り詰められ、(reasoning trace truncated at N chars) とマークされます(測定されたレイテンシー調整要因) |
REASONING_FAIL_FAST | false | 推論ツールに到達できない場合、サーバーの起動を失敗させます(デフォルトでは処理を継続し、ERROR をログに記録します) |
REASONING_QUIET_STDERR | true | 推論サブプロセスの stderr を抑制します。プロセス起動の失敗をデバッグするには false に設定してください |
VALIDATION_MAX_RETRIES | 1 | 構造化出力の修正回数(モデル出力の失敗のみが対象です。プロバイダーエラーは再スローされ、修正プロンプトは行われません) |
VALIDATION_TRUNCATION_RERUN | repair | 切り詰めへの対応:repair(デフォルト)、boost(切り詰めを優先し、ブースト上限で 1 回再実行します。同じ上限での修正は行いません)、off |
VALIDATION_TRUNCATION_BOOST_TOKENS | 65536 | ブースト再実行時の max_completion_tokens(MiniMax M2.x に推奨される上限) |
PROMPT_STABLE_PREFIX | false | 各操作のユーザープロンプトを並べ替え、静的なレスポンス契約ブロックを先頭に、実行ごとのペイロードを末尾に配置します(MiniMax のパッシブなプロンプトキャッシュ最適化。どちらの順序でも内容は同一です) |
LOOP_MAX_TRIES | 2 | 設計ループの GENERATE→EVALUATE 試行回数 |
LOOP_MIN_QUALITY_SCORE | 50.0 | 早期終了する品質スコアのしきい値 |
TASKS_DEADLINE_SECONDS | 1800.0 | 1 回のツール呼び出し/ジョブ全体の実行時間上限。期限に達すると ERR_014 で実行を終了します(パイプライン内部の llama-index Workflow タイムアウトはこの値の 0.9 倍です) |
TASKS_HEARTBEAT_INTERVAL_SECONDS | 30 | 進行状況通知の間隔(クライアントのアイドルタイムアウトより短くしてください) |
JOBS_DB | ~/.config/design-pattern-mcp/jobs.db | 非同期ジョブ一式で使用する SQLite のパス |
PATTERN_DIRECTORY | pattern/ | パターンファイルのディレクトリ |
LOGGING_LEVEL | INFO | DEBUG では、すべての推論とステップごとのツールレスポンスが記録されます |
TRANSPORT | stdio | stdio または streamable-http |
PORT | 8050 | HTTP バインドポート(コンテナーでは 8050、開発ホストでは 8822、systemd ホストでは 8812) |
CONFIG_PATH | config/config.json | 設定ファイルのパス |
CLI フラグ
| フラグ | 説明 |
|---|---|
--config-path | 設定ファイルへのパス(デフォルト:config/config.json) |
--transport {stdio,streamable-http} | トランスポートモードを上書きします |
--host | HTTP のバインド先ホストを上書きします(デフォルト:0.0.0.0) |
--port | HTTP ポートを上書きします(デフォルト:8050) |
--health | オフラインの準備状況チェック(設定+カタログ+インデックス)を実行して終了します |
--offline | 決定論的なオフライン埋め込み器/再ランキング器でサービスを提供します(ネットワーク接続なし、語彙情報のみの検索) |
構造化された推論
プロンプトを受けて実行される各 LLM フェーズの呼び出し(analyze / weights / generate / evaluate)の前に、サーバーは上限付きの ThoughtGenerator ループを実行します。各ステップは生成 LLM が作成し(1 ステップにつき 1 回の補完)、shannonthinking および/または code-reasoning の MCP サーバーに送信されます。これらは各ステップを検証、番号付けし、記録する構造化思考スクラッチパッドです。生成されたトレースは、<reasoning_context> ブロックとしてフェーズプロンプトに挿入されます。契約:各思考 = 1 回の LLM 補完 + 1 回の MCP ツール呼び出しで、REASONING_MAX_TOTAL_STEPS(デフォルト 8)を上限とします。各フェーズで使用するツールのリストはデフォルトで両方のスクラッチパッドです。起動時のヘルスチェックでは、有効なフェーズが使用するツールのみを正確に確認します。
主な特性:
- Docker に組み込み — イメージに両方の npm パッケージが含まれ、実行時には
nodeから直接呼び出します(ネットワーク接続も npx も不要です)。 - Docker 外では npx に自動フォールバック — 組み込みのエントリーポイントが見つからない場合は、
npx -y <pkg>にフォールバックします(初回の呼び出し時にダウンロードします)。 - 呼び出しごとにサイレントに縮退 — 有効なフェーズで spawn/タイムアウト/ツールのいずれかに障害が発生すると、WARNING をログに記録し、フェーズは縮退したプロンプト内思考スキャフォールドを使用して続行します。推論の停止によってツール呼び出しが失敗することはありません。
REASONING_<PHASE>_ENABLED=falseで無効にしたフェーズでは、空の推論コンテキストが生成されます。ジェネレーター呼び出し、サブプロセス、キャッシュエントリーはいずれも発生しません。 - 起動時は明確に通知し、障害時は継続 — ライフスパンのヘルスチェックで推論の状態を報告します。ツールの障害時に起動も失敗させるには、
REASONING_FAIL_FAST=trueを設定します。 - トレースのキャッシュ — analyze と generate のトレースは設計リクエストごとに一度だけ計算され、設計ループの試行間で再利用されます。weights と evaluate のトレースは実行ごとに計算されます。
REASONING_MAX_INJECTED_CHARS— 後続のプロンプトに挿入するレンダリング済みトレースには上限を設定できます(デフォルトでは上限なし)。これにより、後続のすべての呼び出しで入力トークンを削減できます。
レイテンシに関する注意:完全な実行では、推論ツールをシリアルなサブプロセスとして 8 回呼び出します(フェーズごとに 2 回)。推論はエンドツーエンドのレイテンシに大きく影響します。Stage-0 ベンチマーク(MiniMax-M2.7 ジェネレーター、18 シナリオ、NO_WARMUP=1)での測定結果:出荷時の設定(全フェーズ有効)では p50 288.4 s/p95 439.6 s、REASONING_WEIGHTS_ENABLED=false REASONING_EVALUATE_ENABLED=false の場合は p50 253.0 s/p95 413.9 s でした。どちらも品質ゲート(全体のヒット率がベースラインの Wilson 下限以上)を通過しました。オプトインには、文書化されているフェーズごとの切り替えを使用してください。
長時間実行ツールとタイムアウト
select_design_patterns は、呼び出しごとに数分かかる複数段階のパイプラインを実行します。これは処理内容に固有のものです。生成 LLM は、統合された検索候補、パーツごとの品質重み、およびそれ以前の各段階の出力全体を処理し、厳密に構造化された大規模な JSON ドキュメントをトークンごとに生成します。設計ループは LOOP_MAX_TRIES 回まで generate → evaluate を繰り返します。呼び出し元がパターンの判断だけを必要とする場合(コードはエージェント自身が作成する場合)は、analyze_design で §1–§8 のみを実行し、generate → evaluate のループを省略できます。
実行を支配する4つの上限 — 混同しないでください
| 上限 | デフォルト | 適用範囲 |
|---|---|---|
GENERATOR_TIMEOUT_SECONDS | 120 s | モデルへの1回のHTTPリクエスト |
VALIDATION_MAX_RETRIES | 1 | 修復時に再発行するのは厳密に1回だけで、モデル出力の失敗に限られます |
LOOP_MAX_TRIES | 2 | GENERATE→EVALUATE の設計ループの試行回数 |
TASKS_DEADLINE_SECONDS | 1800 s | 実行全体。期限切れになると実行中の処理をキャンセルし、ERR_014 で終了します |
ハートビートによる防御(デフォルトで適用)
長時間実行されるすべてのツールは、並列コルーチンから30秒ごとに progress 通知を送信します(TASKS_HEARTBEAT_INTERVAL_SECONDS)。受信したデータでアイドルタイマーがリセットされるクライアントは、パイプラインが完了するまで接続を維持できます。
TS-SDK クライアント(Claude Desktop、Cursor)は、進捗通知を受信してもタイムアウトをリセットしません。
非同期ジョブの3つのツール(タイムアウトに制約のあるクライアント向け)
submit_design_job(description, language?, ...) → job_id
get_design_status(job_id) → {status, result | error}
cancel_design(job_id) → best-effort, next stage boundary
submit_design_job はミリ秒単位で job_id を返します。パイプラインはバックグラウンドタスクで実行され、ストアには SQLite(JOBS_DB)を使用します。10〜30秒ごとにポーリングしてください。TS-SDK クライアントで機能する解決策はこれです。
直接HTTPクライアントでクライアント側のタイムアウトを回避する
examples/design_pattern_client.py は、クライアント側のアイドルタイムアウトがない直接利用できる Python HTTP クライアントです。1回のブロッキングリクエストで、所要時間にかかわらず完全な応答を受け取れます。
make docker-up # Terminal 1
make client-analyze # Terminal 2: analyze_design only
make client-design # Terminal 2: select_design_patterns, the long synchronous call
どちらのターゲットも、同じ直接クライアント(--tool analyze|design)を使用します。make client-design を実行すると、サーバーが長時間のリクエストを正しく完了することを確認できます。タイムアウトの問題はクライアント側だけにあります。MCP をラップするホストアプリケーション(Claude Desktop、Cursor、…)には独自のタイムアウトがあるため、そこで非同期ジョブの3つのツールを使用してください。
ベンチマークと検証
このリポジトリでは、選択品質とパイプラインのレイテンシを主張ではなく、測定可能な特性として扱います。
ベンチマークハーネス(tests/benchmark/、make benchmark-*):ホールドアウト分割を含む18シナリオのコーパスです。make benchmark-live NO_WARMUP=1 OUT=benchmarks/<arm> RUN_ID=<id> は、段階ごとの内訳(reasoning.、llm.、rerank、embed — 呼び出し回数、レイテンシ、文字数/秒、修復カウンター)とともに、パイプライン全体をプロセス内で実行します。make benchmark-analyze は同じ処理経路を実行し、ANALYZE の後で停止します(選択のヒット率/再現率/スコアを測定し、生成は行いません)。make benchmark-compare は analyze/live のペアを受け取り、ヒット率の Newcombe 信頼区間、選択指標の差分、ヒットの反転を報告します。e2e の差分が省略された生成処理によるものなので、p95 レイテンシのルールは適用外として示されます。make benchmark-selfcheck はすべての指標の計算式を手作業で検証します。make benchmark-compare A=<baseline> B=<candidate> は事前登録されたルール — 候補が勝つのは、ホールドアウトの p95 e2e が15%以上改善し、かつ候補の全体的な acceptable-primary ヒット率がベースラインの全体的な Wilson 下限以上の場合のみです — を適用し、候補が負けると終了コード1で終了します。e2e モードはデプロイ済みの compose スタックを測定するため、プロセス内モードとは比較できません。
参照アーム(すべて benchmarks/ にコミット済み、MiniMax-M2.7、18シナリオ):
| アーム | 構成 | e2e p50 | e2e p95 |
|---|---|---|---|
base-fresh | 提供時のデフォルト(すべてのフェーズで reasoning が有効) | 288.4 s | 439.6 s |
cfg-reasoning | + weights/evaluate の reasoning を無効化(オプトイン環境変数) | 253.0 s | 413.9 s |
code-levers | + L5 truncation-boost、L6 trace cap、L7 stable-prefix(すべてデフォルトでは無効、環境変数で制御) | 223.6 s | 379.2 s |
analyze-20261025 | analyze のみのアーム(generate/evaluate なし)、live-20261025 と同じ構成/環境変数 | 161.5 s | 378.1 s |
live-20261025 | パイプライン全体、NO_WARMUP=1、提供時のデフォルト | 273.0 s | 474.6 s |
analyze と live のペア(2026-10-25、ベースライン live-20261025、候補 analyze-20261025)では、全体の acceptable-primary ヒット数は12/18 対 12/18(Newcombe Δ +0.000 [−0.286, +0.286])でした。ホールドアウトでは4/6対5/6(Δ −0.167 [−0.558, +0.295])、recall@5 は0.378対0.367でした。シナリオはそれぞれの方向に2件ずつ反転しているため(合計4件)、合計数は同じでも該当するシナリオは異なります。すべての分割で、信頼区間はゼロを含みます。この結果に基づいてデフォルトは変更されませんでした。
コードのレバーはデフォルトではなく、オプトインの環境設定として提供されています。累積効果の主張は、事前登録したゲートを base-fresh に対して通過しました(ホールドアウトの p95 は 410.9 → 297.5 s = 27.6 % ≥ 15 %、全体では 13/18 ≥ 下限)。一方、cfg-reasoning に対する帰属比較では候補の損失(+12.5 % < 15 %)が記録されたため、デフォルト設定は動作に影響しない状態を維持しています。オプトインするには:
export VALIDATION_TRUNCATION_RERUN=boost
export REASONING_MAX_INJECTED_CHARS=4000
export PROMPT_STABLE_PREFIX=true
検証レイヤー(このリポジトリの差別化要素):
| レイヤー | 内容 | 場所 |
|---|---|---|
| L1 | 単体テスト(418 件を収集、テスト関数 362 件) | tests/unit/ |
| L2 | Hypothesis プロパティオラクル(test_config_properties.py に 15 件、検証テストは合計 115 件を収集) | tests/verification/ |
| L3 | 仕様ガーデン(FG-* ミュータント) | verify/、make verify-fizz-garden |
| L4 | FizzBee の網羅的モデル(8 件の仕様:ジョブランナー/プロトコル、パイプライン制御、設計ループ、推論リトライ、TEI フォールバック、検索融合、密ベクトルキャッシュ) | verify/fizz/、make verify-fizz |
| — | ミューテーション・ラチェット(mutmut ベースライン。新たな生存ミュータントがあると make test-mutations は失敗し、夜間 CI では参考情報として扱われます) | verify/mutmut-baseline.json、make test-mutations |
| — | 台帳(ゲート判定)+相互整合性チェック(自然言語ドキュメントのトリアージに関する参考情報) | make verify-ledger、make verify-cross-consistency |
すべての .fizz アサーションは、少なくとも 1 つの仕様ガーデン・ミュータントをキルするか、# spec-explains: による根拠を記載する必要があります。意思決定モジュールを変更する場合は、同じ PR で対応する .fizz モデルも更新します。規範となるパイプライン仕様は docs/implementation-guide.md(§番号付き。ドイツ語版は docs/selektions-process-de.md)です。ツール結果のワイヤー契約は docs/result-schema.json、カタログレコードの契約は docs/pattern-schema.json です。
トラブルシューティング
サーバーは起動するが、ツールが表示されない
- エージェントの MCP 接続を確認します:Claude Code
/mcp、OpenCodeopencode mcp list、Codexcodex mcp list - サーバープロセスが起動し、カタログが読み込まれたことを確認します:
uv run design-pattern-mcp --healthはOKと JSON 形式の準備状況レポート(レコード、言語ファイル、エイリアス)を出力します - TEI 埋め込みモデルが正常であることを確認します:
docker compose -f docker/docker-compose.yml ps(ヘルスチェックでは埋め込みモデルの起動期間として 120 s を設定)
「Connection refused」または起動失敗
インデックスのウォームアップ時に埋め込みモデルへ接続できない場合、サーバーの起動を拒否します(最初のリクエストを失敗させるのではなく、フェイルファストで動作します)。
docker compose -f docker/docker-compose.yml logs design-pattern-tei-embed
Docker を使わずにローカル実行する場合は、EMBEDDER_BASE_URL / RERANKER_BASE_URL を接続可能なエンドポイントに設定するか、--offline を指定して起動してください。
LLM プロバイダーのエラー(502 / 401)
GENERATOR_API_KEY(または開発スタックではMINIMAXAI_API_KEY)が設定されており、有効期限が切れていないことを確認しますGENERATOR_BASE_URLがプロバイダーのエンドポイントと一致していることを確認します- トランスポートエラーが 3 回連続すると、サーキットブレーカーが 60 s の間開きます(
..._CIRCUIT_BREAKER_*の各設定)。認証/設定に関する 4xx エラーはカウントされません
ERR_012 / ERR_014
ERR_012= キャンセル(cancel_design経由、または次のステージ境界でクライアントが切断)ERR_014= 実行全体の期限超過(TASKS_DEADLINE_SECONDS)。正当に長時間かかる実行では、明示的に値を引き上げてください。実行中のプロバイダーコルーチンはキャンセルされ、部分的な結果は破棄されます
リランカーの 429 エラー
全文のリランク要求 1 件だけで、CPU 上の TEI サイドカーの 16,384 トークン推論バッファーはすでに飽和します(MAX_BATCH_TOKENS=8192 はリクエストのパッキングと相互作用します。2026-09-25 に測定)。retrieve_many の cap-2 レッグは HTTP 429「Model is overloaded」で完全に失敗します。RETRIEVAL_MAX_CONCURRENT_PARTS=1(提供時のデフォルト)を維持してください。サーバー側の不変条件により、dense_top_k + bm25_top_k もサイドカーの MAX_CLIENT_BATCH_SIZE(48)以下に保たれます。
パターン JSON ファイルが読み込まれない場合
- 各パターンの 14 個のファイル(
pattern/<name>.generic.jsonと 13 個の<language>.json)からレコードを結合します。汎用ファイルは必須です ./scripts/verify-patterns.shでカタログを検証します(両方のスキーマに加え、ajv では表現できないバリアントチェックを実行します。マップの整合性と孤立要素は起動時のゲートで検証されます)- スキーマとの列挙値の不一致があると起動に失敗します(
assert_enums_in_sync)。これは意図した動作です。ガードではなくデータを修正してください
ビルドと開発
主な make ターゲット:
| ターゲット | 説明 |
|---|---|
make install | 依存関係を .venv に同期します |
make install-mcps | 推論用 MCP サーバーをグローバルにインストールします(ローカル開発用。Docker には組み込み済みです) |
make check-lint | ruff |
make check-static-typing | src/ に対して mypy --strict を実行します(60 ファイル) |
make check-deadcode / make check-depcheck | vulture / deptry |
make check-all | 4 つのチェックゲートをすべて実行します |
make test-unit | 単体テスト(tests/unit/) |
make test-oracles | 実行可能なオラクル:L1 カナリア、L2 プロパティオラクル、仕様ガーデンのリプレイ(test_fizz_garden)。test_fizz_traces/test_fizz_conformance は計画中で、まだ追加されていません |
make test-mutations | verify/mutmut-baseline.json に対する mutmut ラチェット |
make verify-fizz | FizzBee モデルの網羅的なチェック(L4) |
make verify-ledger / make verify-cross-consistency | 台帳と NL ドキュメントの整合性ゲート |
make benchmark-live / make benchmark-analyze / make benchmark-compare / make benchmark-selfcheck | ベンチマークハーネス(上記を参照) |
make embed-cache | data/dense-cache/ を強制的に再生成します |
make docker-build-all | 3 つすべてのイメージをビルドします(高密度キャッシュを組み込み済み) |
make docker-up / make docker-down | 開発スタックを起動/停止します |
make client-analyze / make client-design / make client-async | http://localhost:8822/mcp に接続するデモクライアント |
make docker-publish | サーバーイメージを Docker Hub と GHCR にプッシュします(git タグも作成) |
開発ワークフロー:
make install # first-time setup
uv run pre-commit install # First-time setup: commit hook
make check-all # before pushing
make test-unit # behavioral oracle
uv run mypy --strict # src/ is strict; must exit 0
make docker-up # after image-affecting changes
基本ルール(AGENTS.md より):src/ は mypy-strict に準拠させます。スキーマを変更する場合は、同じコミットで docs/implementation-guide.md、スキーマ、コードも変更します。パイプラインの制御フローを変更する場合は、同じ PR で対応する FizzBee モデルも更新します。
公開
3 つのイメージはすべて、2 つのレジストリに同時に公開されます。
| イメージ | Docker Hub | GHCR | タグ |
|---|---|---|---|
| MCP サーバー | olkowa/design-pattern-mcp | ghcr.io/olk/design-pattern-mcp | $(DOCKER_TAG)、latest |
| TEI エンベッダー | olkowa/pattern-tei-embed | ghcr.io/olk/pattern-tei-embed | $(DOCKER_TAG)、latest |
| TEI リランカー | olkowa/pattern-tei-rerank | ghcr.io/olk/pattern-tei-rerank | $(DOCKER_TAG)、latest |
3 つのイメージはすべて、同じ $(DOCKER_TAG)(pyproject.toml のバージョン)を共有します。TEI イメージは兄弟プロジェクトの architecture-pattern-mcp サーバーと共有されており、変更頻度は低いです。blob の重複排除により、変更のない TEI イメージの再タグ付けは低コストで行えます。また、サイドカーに変更がなければ TEI のプッシュを省略できます。
帯域幅に関する注意: TEI エンベッダーイメージは約 5 GB です(ONNX fp32 の重みを組み込み済み)。各レジストリへの初回プッシュでは、約 5 GB をアップロードします。2 回目以降のプッシュは増分方式で行われ、変更されたレイヤーのみが転送されます。
前提条件
Docker Hub — ローカルで認証済みです(docker login)。
GitHub Container Registry — write:packages スコープを持つ classic PAT が必要です。2FA は問題ありません。PAT は 2FA を回避します。ログイン後、トークンは破棄されますが、ログアウトするまでは認証情報が ~/.docker/config.json に保持されます。
公開(初回設定とセッションごと)
# 1. Login to GHCR (interactive — paste token at the password prompt)
docker login ghcr.io -u olk
# 2. Build and push all three images (MCP + TEI embedder + TEI reranker)
# The umbrella target builds the MCP image, tags it, pushes it, creates the git tag,
# then builds and pushes each TEI image in sequence.
make docker-publish-all
# 3. Logout from GHCR immediately after publishing
# (removes the ghcr.io credential from ~/.docker/config.json)
docker logout ghcr.io
次回以降の公開では、手順 1~3 を繰り返します。PAT の有効期限が切れている場合は、上記のリンクから新しいものを生成してください。make docker-publish は MCP サーバーイメージのみをプッシュします(v$(DOCKER_TAG) の git タグと、mcp-publisher による MCP Registry エントリーも対象です)。make docker-publish-tei は 2 つの TEI サイドカーのみをプッシュします。
初回のプッシュ — パッケージを公開に設定(GHCR のみ)
GHCR のパッケージは、デフォルトで非公開です。初回の make docker-publish-all の実行後、3 つすべてのパッケージを公開に切り替えてください。
| パッケージ | 設定 URL |
|---|---|
| MCP サーバー | https://github.com/users/olk/packages/container/design-pattern-mcp/settings |
| TEI 埋め込みモデル | https://github.com/users/olk/packages/container/pattern-tei-embed/settings |
| TEI 再ランキングモデル | https://github.com/users/olk/packages/container/pattern-tei-rerank/settings |
それぞれを公開に設定して保存します。
一部失敗時の復旧
プッシュの途中で失敗した場合(例:GHCR の認証が設定されていなかった場合)、Docker Hub にはすでにレイヤーがアップロードされています。認証を修正した後、make docker-publish-all を再実行しても問題ありません。各レジストリは、アップロード済みのレイヤーをキャッシュヒットとして報告し、残りのプッシュを完了します。対象を絞って再試行する場合は、make docker-publish-tei で TEI サイドカーをプッシュできます。
systemd サービス(Linux)
サーバーは、systemd ベースの任意の Linux ホストで systemd サービスとして実行できます。起動時に Docker Compose スタックが自動的に開始されます。エージェント向けのインストール手順全体については、INSTALL.md(Path B)を参照してください。systemd/ には以下が含まれます。
| ファイル | 用途 |
|---|---|
systemd/design-pattern-mcp.service | systemd ユニット |
systemd/docker-compose.yml | 本番用 Compose バリアント(build: なし、絶対パス、コンテナーポート 8050) |
systemd/README.md | インストール、確認、トラブルシューティングの全手順 |
本番用 Compose は開発用ファイルのデプロイ用バリアントです。ビルド済みイメージのみを使用し、絶対パスを使うため、/etc/design-pattern-mcp/ にデプロイできます。TEI サイドカーは共有の pattern-tei-infra スタックで稼働します。このスタックはその Docker ネットワークに接続し、DNS 名でサイドカーにアクセスします。共有 TEI インフラが active (running) になってから、サービスを開始してください。
# Fast path (see INSTALL.md Path B for the full verified sequence):
VERSION=$(grep -m 1 '^version' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')
docker pull "olkowa/design-pattern-mcp:${VERSION}"
docker tag "olkowa/design-pattern-mcp:${VERSION}" design-pattern-mcp:latest
sudo install -d /etc/design-pattern-mcp/config
sudo install -m 644 systemd/docker-compose.yml /etc/design-pattern-mcp/
sudo install -m 644 config/config.json /etc/design-pattern-mcp/config/
sudo install -m 644 systemd/design-pattern-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now design-pattern-mcp
日常的な操作:systemctl start|stop|restart design-pattern-mcp、ログの確認:journalctl -u design-pattern-mcp -f。詳細については、systemd/README.md を参照してください。
ライセンス
MIT ライセンス。Copyright (c) 2026 Oliver Kowalke。ソースファイル内のライセンスヘッダーを参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。