Claude CodeにAsyncAPIの仕様書を書かせる — validateで検証するスキル
KafkaやWebSocketのproducerコードからAsyncAPI 3.0の仕様書を起こし、asyncapi validateで検証してHTMLまで出すスキルの作り方と、検証が見逃す向きの間違いをまとめます。
KafkaやWebSocketのイベント駆動APIは、REST向けのOpenAPIでは書き表せません。AsyncAPIは、チャネル(トピックやパス)、メッセージ、ペイロードのスキーマを1つのYAMLにまとめる仕様です。producerコードから仕様書を起こす作業はClaude Codeに任せやすい一方、出来上がった仕様書が正しいかどうかは別の話です。
この記事では、asyncapi validateを通すところまでを1つのスキルにまとめます。あわせて、検証が通っても間違いが残る場所を、手元で実行した結果で示します。
AsyncAPI 3.0の仕様書で最初に決めること
AsyncAPI 3.0の仕様書は、channelsとoperationsの2つが骨格です。チャネルは「メッセージが流れる場所」で、addressにトピック名やパスを書きます。オペレーションは「そのアプリケーションがチャネルで何をするか」で、actionにsendかreceiveを指定します。
ここで間違えやすいのが向きです。仕様では、sendは「アプリケーションがチャネルへメッセージを送る」、receiveは「アプリケーションがチャネルから受け取る」を指します。仕様書はアプリケーション自身の視点で書きます。仕様自体も、receiveの記述からsendだけを入れ替えて逆側の仕様書を作ることは勧めていません。
つまり、1つの仕様書が描くのは1つのアプリケーションです。注文サービスのproducerを描くならsend、同じトピックを読む請求サービスを描くなら、別の仕様書でreceiveにします。この単位を先にスキルへ書いておけば、1枚の仕様書に両方向が混ざる事態を避けられます。
validateが通すもの、落とすもの
仕様書を検証するasyncapi validateは、npmの@asyncapi/cliに入っています。インストールはNode.js 16以上とnpmがあれば、グローバルに入れる1行で済みます。
npm install -g @asyncapi/cli
asyncapi validate asyncapi.yamlvalidateの終了コードは--fail-severityで決まります。既定はerrorで、それ以上の重大度の診断があると失敗の終了コードを返します。選べる値はerror・warn・info・hintです。
CLI 5.0.7で、Kafkaのproducerを描いた最小の仕様書を試しました。以降の結果はすべてこのバージョンです。
手元の検証結果(AsyncAPI CLI 5.0.7)
正しい仕様書
エラー0件・警告0件で、情報レベルの指摘が1件だけ出ます。
asyncapi-latest-versionで、3.1.0への更新を勧める内容です。終了コードは0でした。$refの綴りミス
invalid-refのエラーが出て、終了コードは1です。指摘にはoperations.publishOrderCreated.channel.$refと、壊れた参照先の位置が含まれます。actionにpublishを指定
asyncapi-document-resolvedのエラーで、許される値はsendとreceiveだと示されます。AsyncAPI 2系の語彙を持ち込むと、ここで止まります。sendをreceiveに変更
エラーも警告も出ず、
--fail-severity warnでも終了コードは0でした。向きの取り違えは、検証では見つかりません。
最後の1つが、この記事でいちばん伝えたい点です。validateが確かめるのは仕様書の形式と参照の整合で、コードと一致しているかどうかは見ません。向きが逆でも、トピック名が1文字違っていても、仕様書として正しければ通ります。
情報レベルの指摘をCIで失敗扱いにしたいなら、--fail-severity infoを付けます。実際に付けると、先ほどの正しい仕様書でも終了コードが1に変わりました。バージョンの更新提案まで失敗にしてしまうので、通常は既定のerrorのままか、warnまでが現実的です。
producerコードから仕様書を起こす
題材は、注文作成イベントをKafkaへ送るTypeScriptのコードです。
// src/producers/order.ts
type OrderCreated = {
orderId: string;
total: number;
couponCode?: string;
};
export async function publishOrderCreated(o: OrderCreated) {
await producer.send({
topic: "orders.created",
messages: [{ key: o.orderId, value: JSON.stringify(o) }],
});
}このコードから読み取れる事実は3つです。トピックがorders.createdであること、送る側であること、ペイロードが3つのフィールドでcouponCodeだけ省略可能であること。仕様書の完成形は、たとえば次のようになります。
asyncapi: 3.0.0
info:
title: Order Service Events
version: 1.0.0
servers:
local:
host: localhost:9092
protocol: kafka
channels:
orderCreated:
address: orders.created
messages:
orderCreated:
$ref: '#/components/messages/OrderCreated'
operations:
publishOrderCreated:
action: send
channel:
$ref: '#/channels/orderCreated'
components:
messages:
OrderCreated:
payload:
type: object
required: [orderId, total]
properties:
orderId: { type: string }
total: { type: integer }
couponCode: { type: string }型の?はrequiredから外す、という対応は機械的に決まります。決まらないのが、numberをintegerにするかnumberのままにするかです。コードには金額が整数かどうかが書かれていないので、ここは推測で埋めさせず、確認を促す指示をスキルに入れておきます。
仕様書づくりをスキルにする
スキルは、プロジェクトなら.claude/skills/<スキル名>/SKILL.md、全プロジェクトで使うなら~/.claude/skills/<スキル名>/SKILL.mdに置きます。フロントマターで使うのは4つです。descriptionはClaudeが使いどころを判断する説明、argument-hintは入力補完に出す引数の目安、allowed-toolsはそのターンだけ確認なしで使えるツールです。
---
name: asyncapi-spec
description: producerコードからAsyncAPI 3.0の仕様書を起こし、validateを通す。KafkaやWebSocketの仕様書を書くときに使う
argument-hint: [producerのパス]
allowed-tools: Bash(npx -y @asyncapi/cli@5.0.7 validate *)
---
$ARGUMENTS のコードを読み、asyncapi.yaml を書く。
1. 1つの仕様書には1つのアプリケーションだけを書く。
このコードが送るなら action は send、受けるなら receive にする。
2. channels の address には、コードにあるトピック名かパスをそのまま書く。
3. ペイロードは型定義から起こす。省略可能なフィールドは required に入れない。
コードから決められない点(number が整数かなど)は推測せず、
description に「要確認」と書いて最後に一覧で報告する。
4. 書き終えたら validate を実行し、エラーが0件になるまで直す。
5. 最後に、各 operation の action と channel の address を表にして、
コードの該当行と並べて見せる。手順5が、前節の「向きの取り違え」への対策です。検証コマンドでは見つからないので、人が目で確かめられる形に変換させます。コードのproducer.sendの行と、仕様書のaction: send・addressが並んだ表なら、レビューは数秒で済みます。
allowed-toolsが省くのは、スキルを呼んだターンの承認だけです。次のメッセージを送ると効力は切れます。グローバルに入れたCLIを呼ぶならBash(asyncapi validate *)にします。ここでバージョンを固定したnpxにしているのは、チームで同じ診断結果を得るためです。
呼び出しは、スキル名に引数を続けるだけです。
/asyncapi-spec src/producers/order.tsスキル本体は500行以内に収め、長い参照資料は別ファイルに分けるのが望ましいとされています。AsyncAPIのbindingsのような詳細は、reference.mdとして横に置き、必要になったときだけ読ませます。
WebSocketの場合は何が変わるか
仕様書の骨格は同じで、変わるのはserversのprotocolと、チャネルのaddressの意味です。仕様では、addressはトピック名、ルーティングキー、イベント種別、パスのどれにもなり得ると説明されています。Kafkaではトピック名、WebSocketではパスを書くことになります。
servers:
local:
host: localhost:8080
protocol: ws
channels:
orderUpdates:
address: /ws/ordersWebSocketのサーバーがクライアントへ状態を押し出すならsend、クライアントからの購読要求を受けるならreceiveです。1本の接続で双方向にやり取りするため、チャネルを1つにしてsendとreceiveのオペレーションを2つ置くか、チャネルを分けるかは設計判断になります。コードにその判断は書かれていないので、スキルには「迷ったら分けて書き、理由を報告する」と加えておくと、あとから読み直しやすくなります。
接続の挙動を実際に確かめたいときは、Claude Code Monitorツールの使い方のように、WebSocketのログを監視する手段を併用できます。
HTMLドキュメントを出す
検証を通した仕様書は、ジェネレーターでHTMLにできます。ジェネレーターはAsyncAPI文書とテンプレートを入力にして、ドキュメントのほか、クライアントのコードや雛形も出力できる仕組みです。HTMLなら@asyncapi/html-templateを使います。
asyncapi generate fromTemplate asyncapi.yaml @asyncapi/html-template \
--param singleFile=true \
--output ./docs --force-writesingleFile=trueで1枚のHTMLになり、--force-writeは出力先が空でなくても書き込む指定です。スキルの手順6として、「validateが通ったら上のコマンドでdocsを更新する」と足せば、仕様書の更新とドキュメントの再生成が1回の呼び出しで済みます。
手元の環境では、この生成が失敗しました。Node.js 20.18.0でnpx経由のCLI 5.0.7を実行すると、require() of ES Moduleのエラーで止まり、docsディレクトリは空のままでした。validateは同じ環境で問題なく動いたので、失敗したのはテンプレート側の依存関係です。
同じエラーが出たら、まずNode.jsのバージョンを疑ってください。別の手段として、インストール手順にはDockerイメージの使い方があります。そのイメージにはChromiumが同梱され、@asyncapi/html-templateでのPDF生成もそのまま動くと書かれています。
落とし穴
スキルに「生成できた」と報告させない
生成が止まったのに「HTMLを作成しました」と報告されると、空のディレクトリが成果物として残ります。手順に「出力先のファイルが存在することを確認してから報告する」と書き、生成コマンドの終了コードも見させます。
仕様書の古びを防ぐ
producerコードが変わっても、仕様書は自動では変わりません。validateは仕様書の形式しか見ないため、古い仕様書も通ってしまいます。
現実的な対策は、仕様書の差分をレビューに載せることです。コードを変えたプルリクエストにasyncapi.yamlの差分がなければ、追従漏れを疑えます。CLIにはasyncapi diff OLD NEWというサブコマンドもあり、2つの仕様書を比べられます。
OpenAPIの側で同じことをするなら、Claude CodeでOpenAPIをSpectralでlintするの設定が参考になります。編集のたびに検査を走らせるPostToolUse hookの考え方は、AsyncAPIのvalidateにもそのまま使えます。
CIでは、診断の出力形式を--diagnostics-format github-actionsのように指定すると、プルリクエスト上で指摘が見えます。指定できる形式にはjunitやsarifもあります。
Kafka側の仕様とのつなぎ方
Kafkaを使う開発では、スキーマレジストリでペイロードを管理していることがあります。その場合は、AsyncAPIのペイロードを手書きせず、レジストリのスキーマを参照させるほうが二重管理になりません。レジストリを調べる手段はKafka Schema Registry MCPサーバーの記事にまとめています。
トピックやコンシューマーグループの実状をClaude Codeに調べさせたい場合は、Aiven MCPのようにKafkaを扱えるMCPサーバーが選択肢になります。仕様書は「こうあるべき」を書き、MCPは「いま動いている状態」を見る道具なので、両者を突き合わせれば、仕様書とブローカーの食い違いも見つけられます。
まとめ
AsyncAPIの仕様書づくりをClaude Codeに任せるときの要点は、検証コマンドが保証する範囲を知っておくことです。asyncapi validateは形式と参照を守ってくれますが、sendとreceiveの向きやトピック名の正しさは守ってくれません。
スキルには、1つの仕様書に1つのアプリケーションだけを書く決まりと、向きとアドレスをコードの行と並べて見せる表を入れておく。これで、検証の外側にある間違いを人が数秒で拾えます。HTMLの生成は、Node.jsのバージョンによって止まることがあるので、出力の存在確認までを手順に含めると安全です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。