Claude Media
Claude CodeにAsyncAPIの仕様書を書かせる — validateで検証するスキル

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.yaml

validateの終了コードは--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/orders

WebSocketのサーバーがクライアントへ状態を押し出すなら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-write

singleFile=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のバージョンによって止まることがあるので、出力の存在確認までを手順に含めると安全です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/skills
  2. asyncapi.com/docs/tools/cli/usage
  3. asyncapi.com/docs/tools/cli/installation
  4. asyncapi.com/docs/tools/generator
  5. asyncapi.com/docs/reference/specification/v3.0.0
この記事を共有:XはてブLinkedIn