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

#AI
本記事について

GitHub Spec Kit と spec-workflow-mcp という2つの仕様駆動開発ツールを、自作のMCPサーバーで連携させた実践記録です。設計・検証から、実際に1つの機能を完成させるまでの手順と、つまずいた箇所をすべて公開します。

自作MCPサーバーは配布していません

本記事で作成した橋渡しMCPサーバー(bridge)およびレシピ・スキル一式は、個人の検証環境で動かしているもので、パッケージやリポジトリとしての配布は行っていません。 そのまま導入できる成果物ではなく、同等の仕組みを自分で構築するための設計資料としてお読みください。アーキテクチャ、bridge が公開するツールの一覧、実際のレシピ、検証中に遭遇した問題など、再現に必要な情報はすべて記事内に記載しています。

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 など)が導入されます。

1.2. spec-workflow-mcp:承認と進捗管理に特化

一方、spec-workflow-mcp はMCPサーバーとして動作し、リアルタイムのWebダッシュボードを備えています。

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.mdclarify
技術設計・実装計画 Spec Kit plan.mddata-model.md、API契約など
形式変換・同期 bridge Spec Kit の成果物を requirements / design へ変換
仕様・設計のレビューと承認 spec-workflow requirements.mddesign.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

しかも、単純なファイルコピーでは解決しません。節構成が違うからです。

つまり 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 リクエストを使います。

クライアントによって使えるかが変わります

Goose は MCP Sampling に対応済み(自動有効) ですが、Claude Code は未対応です(2026年8月時点)。そのため本記事では Goose を採用しています。Claude Code で使う場合は、後述する MCP Prompts 経由の半自動モードになります。

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
よくある間違い

winget install Pressly.Goose でインストールされるのは 別物(Go言語製のDBマイグレーションツール)です。AIエージェントの Goose は公式スクリプトからインストールします。

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
オプション名が変わっています

古い記事にある --ai goose は現行版では --integration goose に改名されています。また --script ps を省略すると対話プロンプトで停止することがあります。フォルダが既に存在する場合は --force を追加してください(マージ動作で既存ファイルは保持されます)。

5.5. Step 5: レシピとスキルの展開

ここからは実運用に向けた準備です。Goose の レシピ(パラメータ付きの再利用可能なワークフロー)と スキル.goose/skills/ から自動的に読み込まれる手順書)を使います。

セットアップスクリプトを1回実行するだけで、番号付きレシピ7本・規律スキル・運用ドキュメントが展開されます。

.\setup-project.ps1 -ProjectPath C:\project\sample-books
PowerShell 5.1 の文字化けに注意

日本語を含む .ps1 ファイルは UTF-8 BOM付きで保存してください。BOMなしだと Windows PowerShell 5.1 が CP932 として誤読し、「文字列に終端記号 " がありません」というパースエラーになります。私はこれで1度ハマりました。

展開されるレシピは以下の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 ファイルで、parametersextensionsprompt の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_dir001-reading-log と入力する」だけになります。長文の指示を打つ必要も、プロジェクトパスを毎回書く必要もありません。

6.2.2. 2. extensions を内蔵できる(これが最も効果的)

レシピには使用する拡張の定義そのものを書けます。レシピ実行時に自動的に有効化されるため、

という利点があります。実際、タスク生成と実装のレシピには 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 ... 定期実行の登録
validate は必ず通してください

宣言したパラメータを prompt 内で {{ 名前 }} として参照していないと、Unnecessary parameter definitions というエラーで実行できません。私はこれで1度弾かれました。編集したら必ず goose recipe validate を通す習慣をつけると事故が減ります。

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 への振り分け → 承認申請まで自動で進みます。振り分けのルールはこうです。

7.2. 1. 仕様ヒアリング:args を考えなくていい対話形式

Spec Kit の /speckit.specify は「機能の説明文」を引数で渡す方式ですが、何をどこまで書けばいいか分からないという問題があります。

そこで、エージェントが1問ずつ質問するヒアリング形式のレシピを作りました。

  1. 機能の名前と、一言でいうと何のための機能か
  2. 主なユーザーストーリー(「〜として、〜したい、なぜなら〜」形式)
  3. 制約・ルール(文字数上限、冪等性、エラー時の挙動など)
  4. 今回はやらないこと(スコープ外)

回答を「何を・なぜ」だけの説明文にまとめ、内容を確認してから 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 の分離が、生成後にも検証されている状態です。

bridge 側の対応:design の変換ソースを増やす

plan 一式が揃うと、design.md の変換材料が plan.md だけでは足りなくなります。そこで bridge の変換処理では、design を生成する際に data-model.mdcontracts/openapi.yaml も変換ソースとして読み込むようにしました。データモデルとAPI契約が design.md に反映され、変換品質が明確に上がります。

なお、レシピ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_FAILEDRATING_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.mduser-templates/ があればそちらを優先)の形式に従って tasks.md を日本語で生成します。その結果、19件の原子的なタスクに分割されました。

各タスクには次の情報が含まれます。

この「対象ファイル」と「要件参照」が、レシピ6の実装で効果を発揮します。タスクごとに変更可能な範囲が明示されているため、1タスクずつの直列実行でも破綻せずに済むわけです。

レシピの prompt には、ここでも過去の失敗を踏まえた対策を組み込んであります。

prompt の指示 何を防いでいるか
タスクは1〜3ファイル程度の原子的な単位に分割する 巨大タスク化による実装の暴走
チェックボックス形式・タスク番号はテンプレートの形式を崩さない ダッシュボードが進捗をパースできなくなる
HTMLコメント(<!-- -->)は使わない MDX検証エラーで承認申請が失敗する
approvalsfilePath のみ渡す(content は渡さない) 承認システムのエラー

最後の項目は、公式ドキュメントにも明記されている注意点です。承認リクエストにはファイルパスだけを渡し、ダッシュボードがファイルを直接読み込む仕様になっています。

生成後は承認申請まで自動で行われ、承認IDと pending 状態が報告されます。ここでもう一度ブラウザでの承認が必要です。1機能あたり合計3回(requirements / design / tasks)の承認ゲートを通ることになります。

トレードオフ:tasks を spec-workflow 側で作る意味

tasks.md を spec-workflow 側で生成すると、.specify/ 側に tasks.md が存在しなくなり、Spec Kit の /speckit.analyze(成果物間の整合性・カバレッジ分析)が本来の力を発揮できません。それでもこの方式を選ぶのは、実装進捗の正を spec-workflow に一本化するためです。ダッシュボードで進捗を可視化し、実装ログを残すのであれば、tasks は spec-workflow 側に置くべきだと判断しました。

7.6. 6. 実装:1タスクずつ最後まで順次実装する

tasks.md(19タスク)が承認されたら、実装レシピを Run します。このレシピは並列実装を明示的に禁止し、1タスクごとに以下を完了させてから次に進みます。

  1. 実装(そのタスクの範囲のファイルのみ)
  2. テスト実行
  3. log-implementation で記録
  4. tasks.md のチェックボックスを [x] に更新
  5. 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 が分離され、テストも単体・契約・統合・性能・アーキテクチャテストに分かれていました。開発原則が実装まで貫通していることが確認できます。

正直に書いておくこと

ブラウザでの手動受け入れ確認(実ブラウザでの操作性、キーボード操作、Chrome/Edge/Firefox/Safari での確認)は未実施です。エージェントは手順書を tests/e2e/reading-log.manual.md に整備した上で、「未実施のまま合格扱いにはしていない」と明示的に報告してきました。自動化できない検証を安易に合格扱いにしないのは、規律が機能している良い兆候だと思います。

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つを守れるかどうかが成否を分けます。

規律1:仕様の正は specs/、実装進捗の正は spec-workflow

.spec-workflow/ 配下の生成文書は絶対に直接編集しないでください。修正は必ず specs/ 側で行い、再インポートによって反映します。直接編集しても check_drift が stale として検出しますが、そもそも直接編集が生じない運用にすべきです。

規律2:承認は approval_status の approved だけが真

チャット上での「承認したよ」は無効です。spec-workflow-mcp 自身もツールの応答で明示的に警告する("VERBAL APPROVAL NOT ACCEPTED")ため、エージェントもこの規則に従います。

規律3:差し戻しは必ず上流に戻す

ダッシュボードのフィードバックは、生成物ではなく specs/spec.mdplan.md に反映します。エラーコード一覧の追加なら plan.md、要件の変更なら spec.md、という判断が必要です。

これらを 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 ディレクトリの続きから作業させてください。実装レシピがチェックボックスを更新しないまま中断されると、同じタスクを再実行します(基本的には問題ありませんが、差分の確認を推奨します)。