Spec Kit × spec-workflow-mcp を繋ぐMCPサーバーを自作する ― 仕様駆動開発に「承認ゲート」を組み込む

AIコーディングエージェントに「いきなり実装させる」時代は終わりつつあります。仕様を書き、計画を立て、タスクに分解してから実装させる 仕様駆動開発(Spec-Driven Development, SDD) が、実務でのAI活用における現実解になってきました。
しかし、SDDツールを実際に使い始めると、多くの人が同じ壁にぶつかります。「仕様はよく書けた。でも、誰がそれをレビューして承認するのか?」 という問題です。
本記事では、仕様作成に強い GitHub Spec Kit と、承認フローと進捗管理に強い spec-workflow-mcp を、自作のMCPサーバーで橋渡しした実践記録を公開します。最終的に、対話形式で要件を聞き取り、ブラウザで仕様を承認し、19件のタスクを自動で実装するところまで到達しました。
1. なぜ2つのツールを併用するのか
まず、それぞれのツールが何を得意としているかを整理します。
1.1. GitHub Spec Kit:仕様を「書かせる」ことに特化
Spec Kit は GitHub が公開しているSDDツールキットです。specify init でプロジェクトを初期化すると、エージェント用のコマンド群(/speckit.specify、/speckit.plan など)が導入されます。
- 強み: 仕様書のテンプレートと生成プロセスが非常に良く設計されています。特に spec.md には「何を・なぜ」だけを書き、技術選択は plan.md に分離する という規律が徹底されており、これだけでも導入価値があります。
- 強み:
/speckit.clarifyによる曖昧点の対話的な解消、/speckit.analyzeによる成果物間の整合性チェックなど、品質を上げる仕組みが揃っています。 - 弱み: レビューと承認の概念がありません。 生成された spec.md を人間が承認したかどうかを、Spec Kit は一切管理しません。
/speckit.implementを実行すればエージェントは即座に実装を始めます。 - 弱み: 進捗の可視化もありません。tasks.md はただのMarkdownファイルです。
1.2. spec-workflow-mcp:承認と進捗管理に特化
一方、spec-workflow-mcp はMCPサーバーとして動作し、リアルタイムのWebダッシュボードを備えています。
- 強み: 承認ゲートが強制されます。 ドキュメントの承認をブラウザで行うまで、エージェントは次のフェーズに進めません。「口頭承認は受け付けない(VERBAL APPROVAL NOT ACCEPTED)」という徹底ぶりです。
- 強み: ダッシュボードでタスクの進捗、承認待ち、差し戻しコメントをリアルタイムに確認できます。11言語に対応しており、日本語UIも用意されています。
- 弱み: 仕様を書かせるプロセス自体は、Spec Kit ほど作り込まれていません。特に「技術選択を仕様から分離する」といった規律の強制力は弱めです。
1.3. つまり、補完関係にある
| 観点 | Spec Kit | spec-workflow-mcp |
|---|---|---|
| 仕様生成の質 | ◎ テンプレートと規律が優秀 | ○ |
| 曖昧点の解消 | ◎ clarify コマンド | △ |
| 承認ゲート | ✕ 概念がない | ◎ ブラウザ承認必須 |
| 進捗の可視化 | ✕ | ◎ リアルタイムダッシュボード |
| 差し戻しフロー | ✕ | ◎ コメント付きで差し戻せる |
| 実装ログ | ✕ | ◎ log-implementation で記録 |
「Spec Kit で仕様を書き、spec-workflow で承認・管理する」 ── これが基本的な役割分担です。
1.4. 担当範囲の境界:plan.md の完成後に引き継ぐ
ひとことで表すと、Spec Kit は「何を、なぜ、どう作るか」を固めるところまで、spec-workflow は、その内容を人間が承認し、実装タスクと進捗を管理するところからを担当します。両者の境界で、bridge が Spec Kit の成果物を spec-workflow の形式へ変換します。
flowchart LR
subgraph SK["Spec Kit:上流工程"]
A["開発原則<br/>constitution.md"] --> B["要件定義<br/>spec.md"]
B --> C["技術計画<br/>plan.md など"]
end
C --> D["bridge:変換・同期"]
subgraph SW["spec-workflow:承認・実装管理工程"]
E["仕様・設計の承認<br/>requirements.md / design.md"] --> F["タスク生成・承認<br/>tasks.md"]
F --> G["実装進捗・ログ管理"]
end
D --> E
E -.->|"差し戻し時は上流を修正"| B
| 工程 | 主担当 | 主な成果物・役割 |
|---|---|---|
| 開発原則の策定 | Spec Kit | constitution.md |
| 要件のヒアリング・明確化 | Spec Kit | spec.md、clarify |
| 技術設計・実装計画 | Spec Kit | plan.md、data-model.md、API契約など |
| 形式変換・同期 | bridge | Spec Kit の成果物を requirements / design へ変換 |
| 仕様・設計のレビューと承認 | spec-workflow | requirements.md、design.md、承認記録 |
| タスク分解・タスク承認 | spec-workflow | tasks.md |
| 実装中の進捗・ログ管理 | spec-workflow | チェックボックス更新、log-implementation |
つまり、通常の流れでは plan.md までを Spec Kit で作成し、その後 bridge を介して spec-workflow に引き継ぎます。 差し戻しが発生した場合だけ Spec Kit 側の spec.md または plan.md に戻り、修正後に再同期します。
ところが、この役割分担を実現する上で大きな問題があります。
2. 課題:両者は互いを知らない
Spec Kit と spec-workflow-mcp の間に、公式の連携機能は存在しません。両者は互いの存在を認識せず、それぞれのディレクトリに独立してファイルを生成します。
graph LR
subgraph SK["Spec Kit"]
A1["specs/001-xxx/spec.md"]
A2["specs/001-xxx/plan.md"]
A3[".specify/memory/constitution.md"]
end
subgraph SW["spec-workflow"]
B1[".spec-workflow/specs/xxx/requirements.md"]
B2[".spec-workflow/specs/xxx/design.md"]
B3[".spec-workflow/steering/tech.md"]
end
A1 -.->|"連携なし"| B1
A2 -.->|"連携なし"| B2
A3 -.->|"連携なし"| B3
しかも、単純なファイルコピーでは解決しません。節構成が違うからです。
- Spec Kit の
spec.mdには「ユーザーストーリー・機能要件・受け入れ基準」を記述し、技術選択は含めません。 - spec-workflow の
requirements.mdは、design.mdと対になることを前提とした構成です。 - Spec Kit の
constitution.mdは1ファイルですが、spec-workflow の steering は tech.md / structure.md / product.md の3ファイルで構成されます。
つまり cat で流し込むのではなく、内容の振り分けと節構成の組み替えが必要になります。これはLLMの仕事です。
2.1. 3つのアプローチを比較する
| 方式 | 変換の質 | 決定論性 | 手間 |
|---|---|---|---|
| ① シェル/Nodeスクリプト + git hook | 低(機械コピーのみ) | 高 | 小 |
| ② エージェントに変換用コマンドを持たせる | 高(LLMが変換) | 低 | 小 |
| ③ 橋渡し専用のMCPサーバーを自作 | 高 | 中〜高 | 中 |
①は節構成を組み替えられないため、うまく機能しません。②は現実的ですが、コマンド定義をプロジェクトごとにコピーする必要があり、規律が守られるかどうかも利用者に委ねられます。
そこで本記事では③を選びました。決定論的な処理(ファイル収集・ハッシュ計算・パス解決・書き込み・ドリフト検出)はサーバー側のコードで行い、意味的な変換だけをLLMに任せるという役割分担です。
3. アーキテクチャ:MCPサーバーがMCPクライアントにもなる
自作した橋渡しサーバー(以下 bridge)の構成が、今回の技術的な肝です。
graph TD
Host["ホストLLM<br/>(Goose / Claude Code)"]
Bridge["bridge MCPサーバー<br/>(自作)"]
SWM["spec-workflow-mcp"]
Dash["ダッシュボード<br/>localhost:5000"]
Host -->|"ツール呼び出し"| Bridge
Bridge -->|"sampling/createMessage<br/>(変換を依頼)"| Host
Bridge -->|"内蔵MCPクライアント<br/>approvals ツール"| SWM
SWM --> Dash
Dash -->|"人間が承認"| SWM
ポイントは2つあります。
3.1. ポイント1:プロキシパターン
bridge は、内部で spec-workflow-mcp を子プロセスとして起動し、自らがMCPクライアントになって approvals ツールを呼びます。これにより、「文書を書き込む → 承認申請する」という一連の流れを、ホストからのツール呼び出し1回に畳み込めます。
MCPサーバーがMCPクライアントを兼ねる構成は、実際に動作することを確認済みです。承認レコードは .spec-workflow/approvals/ に正しく永続化され、ダッシュボードにも即座に反映されました。
3.2. ポイント2:MCP Sampling で「完全自動」にする
もう1つが MCP Sampling です。これは「サーバーがクライアント(ホスト)のLLMに推論を依頼できる」というMCPの機能で、sampling/createMessage リクエストを使います。
Sampling が使えると、bridge 自身が「この spec.md と plan.md を、このテンプレートの節構成に合わせて変換してください」とホストのLLMに依頼できます。つまりツール1回の呼び出しで、変換から承認申請まで完結します。
| クライアント | tools | prompts | sampling(完全自動) |
|---|---|---|---|
| Goose | ✅ | ✅ /prompt name key=value |
✅ 自動有効 |
| Claude Code | ✅ | ✅ /mcp__bridge__<prompt> |
❌ 未対応(prompts で代替) |
4. 検証で判明した「ハマりどころ」5選
実装に先立ってコードを書いて検証したところ、Web上の情報と食い違う点がいくつも見つかりました。同様の仕組みを構築する方に向けて、その内容を共有します。
4.1. 1. spec-workflow-mcp v2 でツール構成が刷新されている
多くの記事に書かれている create-spec-doc / create-steering-doc / get-template-context / request-approval / get-approval-status は、現行のv2系には存在しません。
現行の5ツールはこちらです。
| ツール | 役割 |
|---|---|
spec-workflow-guide |
ワークフロー手順の取得 |
steering-guide |
steering文書の手順取得 |
spec-status |
specの状態確認 |
approvals |
承認の request / status / delete |
log-implementation |
実装ログの記録 |
文書の作成はエージェントが直接ファイルに書き込む方式に変わり、ツールを経由するのは承認だけになりました。橋渡しをする側にとってはむしろ好都合で、書き込みという決定論的な処理をサーバー側に置けます。
4.2. 2. HTMLコメントが承認リクエストを壊す
これは検証中に実際に遭遇した問題です。生成した文書に「この文書は自動生成です」というマーカーをHTMLコメントで入れたところ、承認リクエストが拒否されました。
message: Markdown file has MDX compatibility errors that must be fixed before approval
rules[1]: mdx-compile-error
Line 1:2 [mdx-compile-error] Unexpected character `!` (U+0021) before name...
ダッシュボードは文書を MDX としてコンパイルするため、<!-- --> は構文エラーになります。正解はMDX形式のコメントです。
{/* GENERATED FROM specs/001-reading-log/spec.md
source-hash: e7718b06104e95dd
DO NOT EDIT DIRECTLY */}
4.3. 3. 仕様の置き場所は specs/ であって .specify/specs/ ではない
現行の Spec Kit は、機能仕様をプロジェクト直下の specs/ に置きます。.specify/ の下はテンプレートとスクリプト、そして memory/constitution.md です。ここを間違えると spec.md not found で延々と悩むことになります。
4.4. 4. projectPath は起動時引数より呼び出し時引数が優先される
これは嬉しい発見でした。spec-workflow-mcp を起動する際にプロジェクトパスを引数で渡しますが、ツール呼び出し時の projectPath 引数のほうが優先されます(パス引数なしでの起動も可能)。
検証コードで、パスAで起動したサーバーに projectPath=B で承認申請したところ、承認レコードは正しくB側にのみ作成されました。
つまり 拡張を1つ登録すれば、全プロジェクトで使い回せます。 プロジェクトを切り替えるたびに設定を書き換える必要はありません。
4.5. 5. Sampling は60秒でタイムアウトする
MCP SDK のリクエストの既定タイムアウトは60秒です。日本語の requirements.md を全文生成すると容易に超えてしまい、MCP error -32001: Request timed out が発生します。
対策は2つ。サーバー側で createMessage にタイムアウトを明示(600秒)し、さらに 1文書ずつ処理する only パラメータを追加して1回の呼び出しを短くしました。
5. 🛠️ 構築実践:環境構築からプロジェクト展開まで
ここからは実際の構築手順を説明します。検証環境: Windows 11 / Node.js 24 / Goose Desktop + CLI 1.46 / specify 0.16.4 / spec-workflow-mcp v2.2.5
5.1. Step 1: 前提ツールのインストール
node --version
Goose CLI をインストールします(PowerShell)。
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/aaif-goose/goose/main/download_cli.ps1" -OutFile "download_cli.ps1"
powershell -ExecutionPolicy Bypass -File .\download_cli.ps1
Spec Kit の CLI もインストールします(uv が必要です)。
uv tool install specify-cli --from git+https://github.com/github/spec-kit.git
CLI と Desktop は設定ファイル(config.yaml)を共有するため、どちらかで設定すれば両方に反映されます。
5.2. Step 2: bridge サーバーの配置
bridge 本体は Node.js の単一ファイルです。MCP SDK と spec-workflow-mcp を依存関係として使用します。
mkdir speckit-spec-workflow-bridge && cd speckit-spec-workflow-bridge
npm init -y
npm install @modelcontextprotocol/sdk @pimzino/spec-workflow-mcp
bridge が公開するツールとプロンプトは以下の通りです。
| 名前 | 種別 | 役割 |
|---|---|---|
check_drift |
tool | ソースと生成物のハッシュを比較し、同期状態を判定 |
list_specs |
tool | spec一覧と取り込み状況(連番除去の名前対応込み) |
get_transform_context |
tool | 変換元文書+変換先テンプレート+ハッシュを一括取得 |
push_document |
tool | 生成ヘッダー付きで書き込み+承認申請 |
approval_status |
tool | 承認状態の確認 |
import_spec_auto |
tool | Sampling による完全自動インポート |
sync-constitution |
prompt | 開発原則→steering の振り分け手順 |
import-spec |
prompt | spec/plan→requirements/design の変換手順 |
サーバー側の instructions(エージェントの振る舞いを制御する肝)
MCPサーバーは初期化時に instructions をクライアントへ渡せます。Goose はこれをエージェントのコンテキストに組み込むため、ここに運用規律を書いておけば、自律動作時にも従わせることができます。
const server = new McpServer(
{ name: "speckit-spec-workflow-bridge", version: "0.1.0" },
{
instructions: [
"Bridge between GitHub Spec Kit (.specify/) and spec-workflow-mcp (.spec-workflow/).",
"Typical flow: list_specs -> get_transform_context -> transform (you, the LLM) -> push_document -> poll approval_status until approved.",
"On sampling-capable hosts, import_spec_auto does the whole flow in one call.",
"Never proceed to implementation or task generation while approval_status reports pending/BLOCKED.",
"Generated documents are derived from .specify/ sources - never edit .spec-workflow/ copies directly; fix the source and re-import.",
].join("\n"),
}
);
5.3. Step 3: Goose への登録
Goose Desktop の Extensions → Add custom extension で登録します。
| フィールド | 入力値 |
|---|---|
| Extension Name | speckit-bridge |
| Type | STDIO |
| Command | node C:/path/to/speckit-spec-workflow-bridge/server.mjs |
| Timeout | 600(LLM生成が長引くため延長推奨) |
設定ファイル(~/.config/goose/config.yaml)に直接書いても同じです。
extensions:
speckit-bridge:
enabled: true
type: stdio
cmd: node
args: ["C:/path/to/speckit-spec-workflow-bridge/server.mjs"]
timeout: 600
5.4. Step 4: プロジェクトの初期化
specify init C:/project/sample-books --integration goose --script ps
5.5. Step 5: レシピとスキルの展開
ここからは実運用に向けた準備です。Goose の レシピ(パラメータ付きの再利用可能なワークフロー)と スキル(.goose/skills/ から自動的に読み込まれる手順書)を使います。
セットアップスクリプトを1回実行するだけで、番号付きレシピ7本・規律スキル・運用ドキュメントが展開されます。
.\setup-project.ps1 -ProjectPath C:\project\sample-books
展開されるレシピは以下の7本です。番号順に実行するだけで、一連の開発ワークフローを進められます。
| レシピ | 役割 |
|---|---|
| 0. 開発原則(初回のみ) | constitution作成+steering同期+承認申請 |
| 1. 仕様ヒアリング | 対話で要件聴取→specify→clarify |
| 2. 技術計画 | plan.md作成 |
| 3. Spec同期 | 変換+承認申請(bridge使用) |
| 4. ドリフト検査(随時) | 同期状態の点検(読み取り専用) |
| 5. タスク生成 | tasks.md作成+承認申請 |
| 6. 実装(1つずつ順次) | 1タスクずつ最後まで順次実装 |
6. Goose レシピで「手順」を資産にする
ここが本記事で最も実用的な部分かもしれません。bridge を作っただけでは、まだ自動化は完成していませんでした。
最初は Goose のチャットに長文の指示を毎回打ち込んで運用していました。しかし、これには3つの問題がありました。
- 毎回同じ長文を打つのが面倒で、次第に省略するようになる
- 省略した結果、検証で得た対策(分割実行など)を書き忘れ、同じ失敗を繰り返す
- 「承認前に実装に進まない」といった規律も、指示に含めるのを忘れると守られない
これを解決するのが Goose の レシピ です。
6.1. レシピの3要素
レシピは YAML ファイルで、parameters・extensions・prompt の3つが本質です。実際に使っている同期レシピの全文を見てください。
spec-sync.yaml(3. Spec同期)の全文
version: 1.0.0
title: 3. Spec同期
description: Spec Kitのspecをspec-workflowへ変換し、承認フローに載せて結果を日本語で報告する
parameters:
- key: spec_dir
input_type: string
requirement: required
description: specs/ 配下のディレクトリ名(例 002-manage-todo-api)
- key: project_path
input_type: string
requirement: optional
default: C:/project/sample-books
description: プロジェクトルートの絶対パス
extensions:
- type: stdio
name: speckit-bridge
cmd: node
args:
- C:/path/to/speckit-spec-workflow-bridge/server.mjs
timeout: 600
prompt: |
spec-sync スキルの規律に従って、次を順に実行してください。
1. list_specs を projectPath="{{ project_path }}" で実行し、
"{{ spec_dir }}" の hasSpec / hasPlan を確認する。
hasPlan が false ならインポートを中止し、先に speckit.plan の実行が
必要であることを報告して終了する。
2. import_spec_auto を projectPath="{{ project_path }}"
specDir="{{ spec_dir }}" only="requirements" で実行する。
3. 成功したら同じ引数で only="design" を実行する。
4. 発行された各 approvalId を approval_status で確認する。
5. 結果(書き込んだファイル、承認ID、承認状態、ダッシュボードURL)を
日本語で報告する。承認が pending の間は実装や tasks 生成に進まないこと。
6.2. なぜレシピ化がここまで効くのか
6.2.1. 1. パラメータが入力フォームになる
requirement: required にしたパラメータは、Desktop の Recipe Library から Run したときに入力フォームとして表示されます。optional + default にしたものは省略可能です。
つまり利用者は「Run を押して spec_dir に 001-reading-log と入力する」だけになります。長文の指示を打つ必要も、プロジェクトパスを毎回書く必要もありません。
6.2.2. 2. extensions を内蔵できる(これが最も効果的)
レシピには使用する拡張の定義そのものを書けます。レシピ実行時に自動的に有効化されるため、
- Desktop 側で拡張が ON になっているかどうかに依存しない
- タイムアウト値(
timeout: 600)もレシピ側で保証される - 別マシンでも、レシピを渡すだけで同じ環境が再現される
という利点があります。実際、タスク生成と実装のレシピには spec-workflow-mcp の定義を内蔵したため、手動での拡張登録作業そのものが不要になりました。
extensions:
- type: stdio
name: spec-workflow
cmd: npx
args: ["-y", "@pimzino/spec-workflow-mcp@latest"]
timeout: 300
パス引数を書いていない点にも注目してください。前述の通り projectPath は呼び出し時引数が優先されるため、この定義のまま全プロジェクトで使い回せます。
6.2.3. 3. 過去の失敗対策が手順として固定される
これが実務上、最も価値のある点です。同期レシピの prompt には、検証を通じて得た対策がすべて組み込まれています。
| prompt に書かれた手順 | 何を防いでいるか |
|---|---|
hasPlan が false なら中止 |
plan.md 不在のまま同期して design が TBD だらけになる事故 |
only="requirements" → only="design" の分割実行 |
60秒タイムアウト(MCP error -32001) |
approval_status で確認してから報告 |
承認状態を確認せず「完了しました」と報告する事故 |
| 「承認が pending の間は進まないこと」 | 承認前の実装着手 |
一度定義しておけば、以後はどのプロジェクトでも同じ品質で実行できます。
6.2.4. 4. 番号を付けて順序を可視化する
レシピのタイトルの先頭に番号を付け、Library に「0. 開発原則」〜「6. 実装」が番号順に並ぶようにしました。さらに、各レシピの prompt 末尾で「次は『2. 技術計画』レシピです」と案内させることで、レシピ同士の実行順序が明確になります。
初めて使う人でも、Library を上から順に実行すれば開発を進められます。
6.3. レシピとスキルの使い分け
Goose には スキル(.goose/skills/<name>/SKILL.md)という仕組みもあり、こちらはセッション開始時に自動で読み込まれます。両者は役割が違います。
| レシピ | スキル | |
|---|---|---|
| 書くもの | 手順(何を、どの順で) | 規律(どんな時も守ること) |
| 起動方法 | Library から明示的に Run | セッション開始時に自動読み込み |
| 使いどころ | 定型ワークフロー | 逸脱の禁止、トラブル対処 |
スキルに規律を書いておくと、レシピを使わず、チャットで「001-reading-log を同期して」と指示した場合でも、同じ規律が適用されます。また、レシピ側は「spec-sync スキルの規律に従って」の一文で済むため、prompt を短く保てます。
SKILL.md に書いている規律(抜粋)
---
name: spec-sync
description: Spec Kit(specs/)とspec-workflow(.spec-workflow/)の同期・承認ワークフロー。
仕様のインポート、承認確認、差し戻し対応、ドリフト検査を行うときに必ずこのスキルに従う
---
## 規律(違反しないこと)
- **承認が pending / BLOCKED の間は、実装にも tasks 生成にも進まない**。
口頭・チャットでの「承認したよ」は無効。approval_status の approved のみを信じる
- **`.spec-workflow/` 配下の生成文書を直接編集しない**。修正は必ず `specs/` 側
(spec.md / plan.md)で行い、`import_spec_auto` で再インポートする
- bridge / spec-workflow のツールを呼ぶときは**必ず projectPath を明示**する
## トラブルシューティング
- `MCP error -32001`(タイムアウト)→ only= の分割実行を徹底
- 承認リクエストが「MDX compatibility errors」で失敗 → 文書内の `<!-- -->` を
`{/* */}` に置換する
6.4. ループ処理もレシピで表現できる
実装レシピ(6番)では、「1タスクずつ、最後まで連続して処理する」というループを prompt で表現しています。単なる手順の羅列ではなく、制御構造をプロンプトとして記述するという発想です。
## 実装ループ(必ず1つずつ順番に)
開始タスクから、tasks.md に書かれた順序で1タスクずつ処理する。
各タスクについて次のA〜Eを完了させてから、次のタスクに着手すること。
A. タスクの内容を実装する(対象ファイルはそのタスクの範囲に限定)
B. テストが定義されているタスクはテストを実行し、結果を確認する
C. log-implementation ツールで実装内容を記録する
D. tasks.md の該当タスクのチェックボックスを [x] に更新する
E. 「タスク<番号>: 完了(変更ファイル / テスト結果 / 残り<n>件)」を1〜2行で報告する
### 厳守事項
- **複数タスクを並行して実装しない。**
- **前のタスクのD(チェック更新)が終わるまで、次のタスクに着手しない。**
- 上限件数に達するか、未完了タスクが無くなるまでループを続ける。
1タスクごとにユーザーの許可を求めて止まる必要はない。
### 中断条件(該当したら即座にループを止めて報告する)
- requirements.md / design.md と矛盾する内容を実装しようとしている
- テストが失敗し、そのタスクの範囲内では修正できない
- タスクの記述が曖昧で、解釈によって実装が大きく変わる
「D が終わるまで次に着手しない」という一文が実質的な直列化の要になっており、これにより、19タスクを一度の実行で処理しても破綻しませんでした。start_task(開始位置)と max_tasks(1回の上限)をパラメータ化してあるため、長時間の実行を避けたいときは件数を絞ることもできます。
6.5. 導入前後の比較
| 操作 | レシピ導入前 | レシピ導入後 |
|---|---|---|
| 同期の実行 | 長文のチャット指示を毎回入力 | Run → spec_dir を入力するだけ |
| 拡張の準備 | 手動登録+タイムアウト設定 | レシピが自動で有効化 |
| 規律の遵守 | 毎回指示に含める(記載漏れの恐れ) | スキルが自動適用 |
| 手順の共有 | 口頭やドキュメントで伝達 | YAMLファイルまたはディープリンクを渡す |
| ドリフト検出 | 思い出したら手動 | スケジューラで定期実行 |
6.6. レシピ管理に使うコマンド
| コマンド | 用途 |
|---|---|
goose recipe validate <file> |
構文チェック(編集後は必ず) |
goose recipe open <file> |
Desktop で開いて Library に保存 |
goose recipe deeplink <file> |
チーム共有用の goose://recipe?... リンク生成 |
goose schedule add --cron ... |
定期実行の登録 |
6.7. 新規プロジェクトへの展開も1コマンドに
レシピ本体にはプロジェクトの絶対パスが埋まっているため、そのままでは他プロジェクトで使えません。そこでテンプレートにプレースホルダ(__PROJECT_PATH__)を置き、展開時に実パスへ置換するスクリプトを用意しました。
.\setup-project.ps1 -ProjectPath C:\project\new-app
これだけで、レシピ7本・スキル・運用ドキュメントが新規プロジェクトに配置され、パスも自動的に書き換わります。新しいプロジェクトへの導入コストを、ほぼゼロにできました。
7. 実践:レシピ0〜6で1機能を作り切る
実際に「読書記録API」を題材に、最初から最後まで回してみました。
graph TD
R0["0:開発原則"] --> AP1{"承認"}
AP1 --> R1["1:仕様ヒアリング"]
R1 --> R2["2:技術計画"]
R2 --> R3["3:Spec同期"]
R3 --> AP2{"承認"}
AP2 -->|"差し戻し"| FIX["specs/ 側を修正"]
FIX --> R3
AP2 -->|"承認"| R5["5:タスク生成"]
R5 --> AP3{"承認"}
AP3 --> R6["6:実装(順次)"]
7.1. 0. 開発原則:ここで「日本語で書く」を決める
最初に作るのが constitution(開発原則)です。ここが言語を統一する上での要点になります。
「全成果物を日本語で書く」ことを原則そのものとして明記すると、constitution は以降のすべてのコマンドに読み込まれるため、この1箇所で下流の spec / plan / tasks がすべて日本語になります。
レシピには言語原則を自動で先頭に追加する仕込みを入れてあるため、入力するのはプロジェクト固有の原則だけです。
TypeScript strict必須。全機能にテスト必須、カバレッジ80%以上。
src/features/<機能名>/ の機能別モジュール構成。シンプル優先、新規依存はPRで正当化
実行すると、constitution の生成 → tech.md / structure.md への振り分け → 承認申請まで自動で進みます。振り分けのルールはこうです。
- 技術スタック / テスト / 品質 / 非機能 → tech.md
- ディレクトリ構成 / モジュール境界 / 命名規約 → structure.md
- 製品価値に関する記述 → 転記せず、差分として報告のみ(steering の product.md には constitution 側に対応物がないため)
7.2. 1. 仕様ヒアリング:args を考えなくていい対話形式
Spec Kit の /speckit.specify は「機能の説明文」を引数で渡す方式ですが、何をどこまで書けばいいか分からないという問題があります。
そこで、エージェントが1問ずつ質問するヒアリング形式のレシピを作りました。
- 機能の名前と、一言でいうと何のための機能か
- 主なユーザーストーリー(「〜として、〜したい、なぜなら〜」形式)
- 制約・ルール(文字数上限、冪等性、エラー時の挙動など)
- 今回はやらないこと(スコープ外)
回答を「何を・なぜ」だけの説明文にまとめ、内容を確認してから specify を実行します。回答に技術選択が含まれていた場合は説明文から除外し、「技術選択は plan で扱います」と伝えるようにしてあるため、Spec Kit が重視する spec/plan の分離をヒアリング段階から確実に守れます。
7.3. 2. 技術計画:技術を語るのはここだけ
Spec Kit の設計思想で最も重要なのが、spec.md には技術を書かず、plan.md で初めて技術を扱うという分離です。レシピ1のヒアリングで技術に関する内容を除外したのは、この段階で扱うためです。
レシピを Run すると tech_choices の入力を求められます。今回の入力はこちらです。
Node.js 24 + Fastify 5 + SQLite(better-sqlite3)。入力検証はzodで統一。
エラーは機械可読コード(RATING_OUT_OF_RANGE等)
レシピは実行前に対象となるspecを確認し(通常は最新の連番ディレクトリ)、すでに plan.md がある場合は上書きしてよいかも確認します。生成された成果物は plan.md だけではありません。
| 生成物 | 内容 |
|---|---|
plan.md |
実装計画本体 |
research.md |
技術選定の調査結果 |
data-model.md |
データモデル定義 |
contracts/openapi.yaml |
OpenAPI 3.1 によるREST API契約 |
quickstart.md |
セットアップ手順 |
注目したいのが Constitution Check です。Spec Kit は plan の生成後に、開発原則(constitution)と設計が矛盾していないかを自動検査します。今回は「設計後もすべてPASS」という結果でした。レシピ0で定めた原則が、ここで自動的に検証されます。
さらに、レシピには独自の検査を1つ追加してあります。
生成後、plan.md に要件の再定義(spec.md の繰り返し)が混ざっていないか確認し、あれば技術計画のみに整理する。
エージェントは実際に「plan.md に User Story、機能要件、受け入れシナリオなどの再定義は含まれておらず、技術計画のみに整理されています」と報告してきました。spec/plan の分離が、生成後にも検証されている状態です。
なお、レシピ2を飛ばすと、レシピ3の同期処理が hasPlan: false を検出して中止します。design.md が「TBD」だらけになる問題を、手順によって防いでいるわけです。
7.4. 3. Spec同期と差し戻し:ここが本命
同期レシピを Run すると、hasPlanチェック → requirements/design の分割インポート → 承認申請 → 承認IDの報告まで、自動的に実行されます。
ダッシュボードには、日本語の requirements.md / design.md が承認待ちとして並びます。ここで 意図的に差し戻しを一度試すことをおすすめします。今回は design.md に対して次のようにコメントしました。
エラーコードの一覧表を追加してください
続いて、エージェントに次のように指示します。
ダッシュボードの差し戻しコメントを確認し、specs/ 側に反映してから再同期して
すると、生成物である design.md には直接手を加えず、Spec Kit 側の plan.md にエラーコード一覧表(VALIDATION_FAILED、RATING_OUT_OF_RANGE など15種類、HTTPステータスと発生条件付き)が追加されました。その内容から再変換が行われ、新しい承認リクエストが作成されました。
これが併用構成の最大の価値です。 仕様の正は常に specs/ 側にあり、レビューは spec-workflow 側で行い、修正は必ず正である specs/ 側に反映する、というループが成立します。
7.5. 5. タスク生成:承認済みの設計から19件の原子的タスクへ
requirements.md と design.md の両方が承認されたら、ようやくタスク分解に進めます。
このレシピが最初にやるのは実装ではなく検問です。spec-status で承認状態を確認し、未承認なら「先にダッシュボードで承認してください」と報告して中止します。ここを飛ばせないようにしておくのが、承認ゲートを形骸化させないコツです。
検問を通過すると、requirements.md と design.md を読み、.spec-workflow/templates/tasks-template.md(user-templates/ があればそちらを優先)の形式に従って tasks.md を日本語で生成します。その結果、19件の原子的なタスクに分割されました。
各タスクには次の情報が含まれます。
- 対象ファイル ── そのタスクで触ってよい範囲
- 要件参照 ── どの要件を満たすためのタスクか
- 再利用元 ── 既存のどのコードを使うか
- 実装プロンプト ── 実装時にエージェントへ渡す具体的な指示
この「対象ファイル」と「要件参照」が、レシピ6の実装で効果を発揮します。タスクごとに変更可能な範囲が明示されているため、1タスクずつの直列実行でも破綻せずに済むわけです。
レシピの prompt には、ここでも過去の失敗を踏まえた対策を組み込んであります。
| prompt の指示 | 何を防いでいるか |
|---|---|
| タスクは1〜3ファイル程度の原子的な単位に分割する | 巨大タスク化による実装の暴走 |
| チェックボックス形式・タスク番号はテンプレートの形式を崩さない | ダッシュボードが進捗をパースできなくなる |
HTMLコメント(<!-- -->)は使わない |
MDX検証エラーで承認申請が失敗する |
approvals は filePath のみ渡す(content は渡さない) |
承認システムのエラー |
最後の項目は、公式ドキュメントにも明記されている注意点です。承認リクエストにはファイルパスだけを渡し、ダッシュボードがファイルを直接読み込む仕様になっています。
生成後は承認申請まで自動で行われ、承認IDと pending 状態が報告されます。ここでもう一度ブラウザでの承認が必要です。1機能あたり合計3回(requirements / design / tasks)の承認ゲートを通ることになります。
7.6. 6. 実装:1タスクずつ最後まで順次実装する
tasks.md(19タスク)が承認されたら、実装レシピを Run します。このレシピは並列実装を明示的に禁止し、1タスクごとに以下を完了させてから次に進みます。
- 実装(そのタスクの範囲のファイルのみ)
- テスト実行
log-implementationで記録- tasks.md のチェックボックスを
[x]に更新 - 1行で進捗報告
前のタスクのチェック更新が終わるまで次に着手しないという制約が、実質的な直列化の要になっています。並列にすると、途中で仕様との矛盾に気づいても引き返せなくなるためです。
中断条件も明示してあります。「requirements/design と矛盾する実装になる」「そのタスクの範囲内では修正できないテスト失敗がある」「タスクの記述が曖昧である」のいずれかに該当したら、後続のタスクには進まず、ループを停止して報告します。
8. 実際の成果
読書記録API(reading-log)を最後まで実装した結果です。
| 検証項目 | 結果 |
|---|---|
| 完了タスク | 19件 / 19件 |
npm run typecheck |
成功 |
npm test |
37件成功、失敗0件 |
npm run build |
成功 |
8.1. カバレッジ
| 指標 | 結果 | 基準 |
|---|---|---|
| 行 | 92.25% | 80%以上 |
| 分岐 | 89.96% | 80%以上 |
| 関数 | 94.95% | 80%以上 |
8.2. 性能テスト(10,000件)
| 処理 | p95 | 回帰閾値 |
|---|---|---|
| 無条件一覧 | 0.41ms | 500ms未満 |
| 複合検索 | 6.17ms | 500ms未満 |
生成された構成は、constitution の原則どおり src/features/reading-records/ に types / errors / schemas / validation / repository / service / routes が分離され、テストも単体・契約・統合・性能・アーキテクチャテストに分かれていました。開発原則が実装まで貫通していることが確認できます。
9. メリット・デメリット
実際に運用して感じたメリットとデメリットを、率直に整理します。
9.1. メリット
| 項目 | 内容 |
|---|---|
| 暴走の抑止 | 承認されるまで実装に進めない。「良さそうだから実装しちゃえ」が構造的に不可能になる |
| 仕様の一意性 | 仕様の正が specs/ に固定され、生成物との乖離は check_drift が検出する |
| レビューのしやすさ | ブラウザで差分を確認し、日本語でコメントを付けて差し戻せる。Markdownをgit diffで追うより格段に扱いやすい |
| 規律の自動適用 | Goose のスキルに規律を書いておけば、毎回指示しなくてもエージェントが守る |
| 手順の再現性 | レシピ化により「Run してパラメータを入れるだけ」になる。属人性が消える |
| 進捗の可視化 | 19タスクの消化状況がリアルタイムに見える |
9.2. デメリット
| 項目 | 内容 |
|---|---|
| 初期構築のコスト | bridge の実装、レシピ7本、スキル整備。ここまでで丸1日かかりました |
| 二重管理の宿命 | 仕様は specs/、実装進捗は .spec-workflow/。どちらが正かを常に意識する必要があります |
/speckit.analyze が本来の力を出せない |
tasks を spec-workflow 側で生成するため、.specify/ 側に tasks.md がありません。使うなら検査用の複製が必要です |
| 変換品質がモデル依存 | Sampling による変換の品質は、Goose に設定しているモデルの性能に左右されます |
| 承認が人間のボトルネックになる | 承認を必須とする製品仕様上、1人で開発する場合は自分自身がボトルネックになります |
| LLMプロバイダの障害に弱い | 長いループの途中でAPIエラーが出ると中断します。実際に server_error で1度止まりました |
9.3. 向いているケース・向いていないケース
向いているのは、レビュアーと実装者が別のチーム開発、規制産業など仕様承認の記録が必要なプロジェクト、「AIに大きめの機能を任せたいが、いきなり実装させるのは不安」というケースです。
向いていないのは、プロトタイピングや使い捨てスクリプトです。承認ゲートが単なる足枷になります。
10. 運用で守るべき3つの規律
構成そのものより、この3つを守れるかどうかが成否を分けます。
これらを Goose の スキル(.goose/skills/spec-sync/SKILL.md)に記述しておけば、セッション開始時に自動的に適用されます。人間が毎回指示する必要はありません。
11. つまずきポイント一覧
実際に遭遇したエラーと対処をまとめます。
| 症状 | 原因と対処 |
|---|---|
No such option: --ai |
旧オプション。--integration goose を使う |
| init が「既存フォルダ」エラー | --force を付ける(マージ動作、既存ファイルは保持) |
| init が無反応で止まる | スクリプト種別の対話プロンプト待ち。--script ps を付ける |
.ps1 が文字化けパースエラー |
UTF-8 BOM付きで保存する(PowerShell 5.1 対策) |
spec.md not found |
仕様は specs/ 直下。.specify/specs/ ではない |
MCP error -32001(タイムアウト) |
SDK既定の60秒超過。サーバー側でタイムアウト明示+1文書ずつの分割実行 |
| 承認申請が MDX エラーで失敗 | <!-- --> を {/* */} に置換 |
| design.md が TBD だらけ | plan.md 不在。技術計画を先に実行する |
レシピが Unnecessary parameter definitions |
宣言したパラメータを prompt 内で {{ 名前 }} として参照していない |
| speckit レシピがスクリプトを見つけられない | Desktop セッションの作業ディレクトリがプロジェクト直下になっていない |
12. 今後の課題
12.1. ドリフト検査の定期実行
Goose にはスケジューラがあるため、同期状態の点検を自動化できます。
goose schedule add --schedule-id drift-check --cron "0 0 9 * * 1-5" --recipe-source C:/project/sample-books/.goose/recipes/drift-check.yaml
「specs/ を直したのに再インポートを忘れ、古い仕様が承認済みのまま残る」という、この構成で最も起きやすい事故を機械的に防げます。
12.2. /speckit.analyze との併用
前述の通り、tasks を spec-workflow 側で生成すると、analyze が本来の力を発揮できません。analyze の直前に spec-workflow の tasks.md を specs/NNN-xxx/tasks.md へ読み取り専用の写しとしてコピーするという回避策を検討中です。実装進捗の正を変更しない検査用の複製なので、規律とは矛盾しません。
12.3. 逆方向同期は「しない」という判断
ダッシュボードで steering や requirements を直接編集しても、その内容は Spec Kit 側には反映されません。これは意図した設計です。双方向同期はコンフリクトの温床になるため、「生成物である」ことをヘッダーで明示し、編集が必要になったら必ず上流を修正する、という一方向の同期を貫くほうが事故を減らせます。
13. おわりに
本記事では、Spec Kit と spec-workflow-mcp という互いを知らない2つのツールを、自作MCPサーバーで橋渡しした実践記録を紹介しました。
技術的な要点は 「変換はLLM、検知と転送は機械、承認は人間」 という役割分担です。MCPサーバーがMCPクライアントを兼ねるプロキシパターンと、Goose の Sampling 対応がこれを可能にしました。
実務的な要点は、手順をレシピに、規律をスキルに落とし込んだことです。bridge を作った時点では、まだ「長文の指示を毎回入力する」運用でした。しかし、レシピ化によって「Run してパラメータを1つ入力するだけ」になり、検証で得た対策(分割実行、事前チェック、承認確認)が手順として固定されました。属人性がなくなり、新規プロジェクトへの展開も1コマンドで済むようになったことは、bridge 本体と同じか、それ以上に価値のある変化でした。
運用面での要点は、仕様の正を1箇所に固定し、生成物には決して手を入れないという規律です。この規律にはツールで強制できる部分もありますが、最終的には人間の運用判断に委ねられます。Goose のスキルに規律を書き込んでおくことで、エージェントには自動的に従わせることができました。
AIエージェントに大きな機能を任せられるようになった今、「どう作らせるか」より「どう承認するか」のほうが重要な設計課題になりつつあります。本記事がその一助になれば幸いです。
14. FAQ
- Claude Code でも使えますか?
- 使えますが、MCP Sampling には未対応(2026年8月時点)のため、完全自動モード(
import_spec_auto)は動きません。代わりに MCP Prompts 経由の半自動モード(/mcp__bridge__import-spec)を使います。変換はエージェント自身が自分のターンで行うため、タイムアウトのリスクはむしろ低くなります。1つのサーバーで、クライアントの能力に応じて自動化レベルが切り替わる構成です。 - なぜ Spec Kit 側のコマンドをフォークせずに済むのですか?
- Spec Kit のコマンド定義は、インストール時に各エージェントのディレクトリ(Goose なら
.goose/recipes/)へ書き出されます。本記事の構成では、これらを内部から呼ぶ薄いラッパーレシピを別ファイル名で作っているため、Spec Kit 本体には一切手を入れていません。specify initを再実行しても自作レシピは無傷です(ファイル名が違うため)。 - 承認は1人開発でも意味がありますか?
- あります。むしろ1人開発のほうが「AIが出した仕様をよく読まずに実装させてしまう」リスクが高いためです。ブラウザで承認ボタンを押すという明確な操作が、少なくとも一度は仕様を目視するきっかけになります。ただし、プロトタイピングでは足枷になるため、使い分けが必要です。
- ダッシュボードは複数プロジェクトで共用できますか?
- できます。ダッシュボードはマルチプロジェクト型で、MCPサーバーのツールが呼び出されたプロジェクトは自動的に一覧に表示されます。また
projectPathは起動時引数より呼び出し時引数が優先されるため、拡張もパス引数なしで1回登録すれば、全プロジェクトで使い回せます。 - 変換の日本語品質が低い場合はどうすればいいですか?
- 効果の高い順に3つあります。①constitution の言語原則を具体化する(「です・ます調」「用語集」まで書く)②
.spec-workflow/user-templates/に日本語版テンプレートを置く(変換先の見出しが日本語になると本文も安定します)③Goose のモデルを変更する。特に②は効果が大きいです。 - タスクの実装を並列化すれば速くなりませんか?
- 速くはなりますが、推奨しません。並列にすると、途中で仕様との矛盾やテスト失敗に気づいても引き返せなくなります。本記事の実装レシピは「前のタスクのチェックボックス更新が終わるまで次に着手しない」という制約で直列化しており、中断条件に該当したら残りを飛ばさず停止して報告します。19タスクを一度に流しても、この制約のおかげで破綻しませんでした。
- レシピとスキルはどう使い分けるべきですか?
- レシピは「手順」、スキルは「規律」です。レシピは Library から明示的に Run する定型ワークフローで、スキルはセッション開始時に自動的に読み込まれる、守るべきルールです。スキルに規律を書いておくと、レシピを使わずチャットで指示した場合にも同じ規律が適用されます。また、レシピ側は「〇〇スキルの規律に従って」の一文で済むため、prompt を短く保てます。
- レシピの
extensionsに拡張を内蔵するメリットは? - 実行時に自動で有効化されるため、Desktop 側で拡張が ON になっているか、タイムアウトが適切に設定されているかに依存しなくなります。本記事の構成では、タスク生成レシピに spec-workflow-mcp の定義を内蔵したことで、手動での拡張登録作業そのものが不要になりました。別マシンにレシピを渡すだけで同じ環境が再現されるのも利点です。
- レシピを編集したら
Unnecessary parameter definitionsと言われました。 - 宣言したパラメータを
prompt内で{{ 名前 }}として参照していないことが原因です。参照を追加するか、不要なパラメータ宣言を削除してください。編集後は必ずgoose recipe validate <file>を実行する習慣をつけると、この種のエラーを実行前に解消できます。 - LLMプロバイダのエラーで中断した場合、やり直しは安全ですか?
- レシピによって異なります。技術計画・同期・タスク生成・ドリフト検査は同じファイルを上書きするだけなので、何度実行しても問題ありません。一方、仕様ヒアリングの再実行には注意が必要です。新しい連番ディレクトリ(
002-...)が作られ、仕様が重複します。中断した場合は再実行せず、既存の spec ディレクトリの続きから作業させてください。実装レシピがチェックボックスを更新しないまま中断されると、同じタスクを再実行します(基本的には問題ありませんが、差分の確認を推奨します)。