mcpField
H
hwcontract

hwcontract

ローカル
github.com/MohibShaikh開発ツール01 週間前に更新

hwcontract について

hwcontractは、ハードウェアのキャプチャやシリアルログを契約に照らして判定し、測定根拠とともにPASS、MARGINAL、FAIL、MISSINGを返します。実機のタイミングやイベント動作を検証するファームウェア開発者やAgent向けで、CLI、MCP、CIに対応します。

主な機能

  1. 01タイミング、シリアル、イベントの契約判定
  2. 02WS2812とDShot600のライブキャプチャ
  3. 03証拠ハッシュと測定値に基づく判定
  4. 0428種類の契約テンプレートを同梱
  5. 05CLI、MCP、pytest、GitHub Actionに対応

活用シーン

  1. 01ロジックアナライザーのキャプチャでWS2812のタイミングを検証
  2. 02DShot600信号やシリアル起動ログを確認
  3. 03ピンイベントの順序やタイミングの不具合を検出
  4. 04キャプチャに基づくCIでファームウェア変更を判定

MCP 対応

契約の一覧表示、測定値・シリアルログ・イベントの判定、対応ハードウェア信号のキャプチャと検査を行うMCPツールを提供します。標準はstdioで、HTTP転送とBearer認証も選択できます。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"mohibshaikh-hwcontract": {
"command": "uvx",
"args": [
"hwcontract"
]
}
}
}
起動コマンドuvx hwcontract

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
pypihwcontract0.6.1

ツール

7

掲載時のツール一覧 · 1 週間前に更新

パラメーター型必須説明
observationsarrayはい
contract_pathstringはい

詳細

hwcontract

ファームウェアは仕様上は正しくても、実際の信号では誤動作することがあります。

コーディングエージェントは、WS2812 ドライバーや ESC のビットストリーム、ブートログを作成します。それらはレビューには通っても、実際のチップに信号が届いた途端に失敗します。hwcontract はそのギャップを埋めます。ハードウェアで実際に起きたことを記録し、対処に役立つ判定を返します。

  • PASS:仕様の範囲内
  • MARGINAL:仕様の範囲内ですが、限界値に近すぎます。ベンチでは動作しても、現場の低温の基板では動作しなくなる可能性があります。この判定は失敗扱いです。判定機は出荷を許可しません。
  • FAIL:仕様の範囲外です。測定値と、どれだけ外れているかを示します。
  • MISSING:必須の観測結果がありません。これも判定の失敗扱いです。

緑の判定によって、表に加えて次のことが得られます。

  • 中央値だけでなく、すべてのパルスを判定します。 キャプチャにはパルス分布全体が含まれます。中央値では見逃される波形の乱れも、ヒントに違反パルス数を示したうえで、MARGINAL または FAIL として検出されます。
  • キャプチャで確認できる精度を超える判定はしません。 サンプルから測定した幅の精度は、サンプル周期単位に限られます。FAIL と判定するには、その幅がサンプル周期を超えて仕様外である必要があります。PASS と判定するには、サンプル周期以上の余裕をもって仕様内である必要があります。それより差が小さい場合は MARGINAL となり、より高いサンプルレートでのキャプチャを促すヒントが表示されます。
  • すべての判定に証拠が付属します。 コントラクトのハッシュ、キャプチャのハッシュ、キャプチャのパラメーター、ツールのバージョン、タイムスタンプが含まれます。CI での緑のビルドを、それを生み出した正確なバイト列までたどれます。

手元にハードウェアがなくても、以下のデモでは実際に記録された信号を使って一連の処理を実行するため、何かを配線する前に結果を詳しく確認できます。

実際の WS2812B キャプチャ、DMA が原因で破損した SPI トレース、シリアルブートログを判定する hwcontract

実際のキャプチャを再生する

pip install hwcontract
python3 -m hwcontract.demo ws2812b

初回実行時に、sigrok から 24 個の LED を接続した NeoPixel の記録済みキャプチャ(約 109 KiB)をダウンロードし、SHA256 を検証してから、$XDG_CACHE_HOME/hwcontract または ~/.cache/hwcontract にキャッシュします。初回はネットワーク接続が必要ですが、以降は --offline を使用できます。同じ固定済みキャプチャのコピーには、--cache-dir DIR または --capture PATH を使用してください。sigrok のインストールも、ハードウェアも不要です。キャプチャの由来とライセンス。

このキャプチャを 2 つのコントラクトに照らして判定します。同じ信号に対する 2 つの判定です(出力の抜粋。各判定では証拠のハッシュも表示されます)。

measured on the real WS2812B signal (300000 samples @24MHz):
  T0H 333 ns   T1H 833 ns   T1L 417 ns   T0L 917 ns   RESET 992250 ns

=== generic WS2812 contract -> FAIL ===
  T0H     350   333  PASS
  T0L     800   917  MARGINAL  within one sample (41.7ns) of max; 1842 of 4275
                               pulses within one sample of the window
  T1H     700   833  MARGINAL  within one sample (41.7ns) of max
  T1L     600   417  MARGINAL  33ns past min, within one sample (41.7ns);
                               capture at a higher samplerate to resolve
  RESET 50000 992250 PASS

=== matching WS2812B contract -> PASS ===
  (all five edges PASS)

同じハードウェアでも、コントラクトが 2 つあれば判定は異なります。汎用コントラクトは失敗し、チップ固有のコントラクトは合格します。24MHz では各サンプルが 41.7ns に相当し、すべての逸脱がそれより小さいため、汎用コントラクトの項目は FAIL ではなく MARGINAL となります。キャプチャから分かるのは信号が WS2812 の限界値に近すぎるということであり、確実に限界を超えているということではありません。WS2812B は WS2812 とは異なります。実際の信号を測定して仕様に照らし、実際のチップに合ったコントラクトを選んでください。

時系列エンジンで DMA のバグを検出する

python3 -m hwcontract.demo spi

100MHz で生成した 100 個の SPI フレームを CS/SCK/MOSI の生波形として用意し、ピンのエッジに変換して、同梱の spi-frame コントラクトに照らして判定します。フレーム 77 には、チップセレクトがクロックの開始後にアサートされる、シミュレーション上の DMA 順序エラーがあります。フレーム 42 では、サンプリングエッジの 10ns 前に MOSI が安定します。どちらも正確なタイムスタンプとともに結果が返されます。インポーターのテストとして、人工的に作成した B/E jsontrace の往復処理も実行します。これらの注釈は実際の sigrok SPI デコーダーの出力ではありません。

cs-precedes-first-clock  800  1  FAIL  trigger at 1540310ns: no gpio.cs.falling
                                          in [1530310ns, 1540310ns] (first of 1)
mosi-setup               800  1  FAIL  forbidden spi.mosi.* at 843300ns is 10ns
                                          before spi.sck.rising at 843310ns

これはオフラインで動作する、決定論的なピンタイミングのデモです。SPI ペイロードのバイトはデコードせず、ループバックテストも実行しません。python3 -m hwcontract.demo serial を実行すれば、合成した正常なブートログと、パニックを起こすブートログをオフラインで確認できます。python3 demo/spi_dma_temporal.py などのリポジトリ内コマンドは、互換性を維持するためのラッパーです。期待される PASS/FAIL の結果が得られた場合、すべてのデモは終了コード 0 で終了します。本番環境の確認には、判定 CLI または MCP の判定を使用してください。

利用できる機能

  • 28種類のコントラクトテンプレートを同梱。WS2812 系 LED、DShot、サーボ/I2C/センサー/ステッパーのタイミング、シリアルのブートログ、SPI のピン順序などを含みます。これらは仕様ライブラリであり、そのまま使える 28 種類のキャプチャ統合ではありません。テンプレートが実際の部品とそのデータシートに適合していることを確認してください。
  • 動作するキャプチャ経路:WS2812 系のパルス幅バケット、check_dshot による DShot600、シリアルログに対応します。DShot150/300/1200 には、レートに応じたアダプターのしきい値が必要です。サーボ、I2C、HC-SR04 などのタイミングテンプレートには、コントラクトで指定された測定値を生成するカスタム観測アダプターが必要です。
  • 正規化されたイベントに対する時間的アサーション。生のピン遷移は spi-frame に入力します。デコーダーの注釈には、デコーダーが実際に使用するソースとイベントタイプに合致するコントラクトが必要です。
  • 既存のアダプターが必要な観測結果を出力する場合、新しい仕様は YAML のみで作成できます。新しいプロトコルには、アダプターコードと正常/異常が確認されたキャプチャも必要になる場合があります。
  • エージェントから呼び出せるMCP サーバー、または手動で実行できる通常の CLI コマンドを利用できます。
  • すべての判定に証拠を付与:コントラクトハッシュ、キャプチャハッシュ、キャプチャパラメーター、ツールのバージョン、タイムスタンプを記録します。ビルドが成功した場合も、その根拠を正確なバイト列までたどれます。
  • デフォルトで実用的:タイミングエッジはすべてナノ秒単位で測定し、シリアルコントラクトは Python の正規表現です。判定結果には測定値と差分が含まれるため、エージェントは修正すべき箇所を正確に把握できます。

インストール

pip install hwcontract              # judge + logic-analyzer adapter
pip install "hwcontract[serial]"    # + live serial capture (pyserial)
pip install "hwcontract[untrusted]" # + google-re2 (ReDoS-immune, for untrusted contracts)
pip install "hwcontract[all]"       # everything

ライブのロジックアナライザーキャプチャ(check_ws2812/check_dshot)にも、PATH 上の sigrok-cli が必要です。判定専用ツール(judge_contract、judge_serial)には追加のものは必要ありません。

hwcontract --help
hwcontract --version
hwcontract --list-contracts         # names, kinds, capture requirements
hwcontract --list-contracts --json  # also includes absolute installed paths

これらのコマンドはすぐに終了します。引数なしで hwcontract を実行すると、MCP stdio サーバーが起動し、stdin からの JSON-RPC リクエストを待ち受けます。

エージェントに接続する

クライアントごとに設定を 1 つ追加すれば、以降はその設定を使えます。インストール後、hwcontract コマンドは PATH 上にあります。

Claude Code

claude mcp add hwcontract -- hwcontract

Codex CLI: ~/.codex/config.toml

[mcp_servers.hwcontract]
command = "hwcontract"

opencode/Cursor/Gemini/その他の stdio MCP クライアント

{ "mcpServers": { "hwcontract": { "command": "hwcontract" } } }

デフォルトのトランスポートは stdio(ローカル、認証対象なし)です。ChatGPT コネクターなどのリモート専用クライアントでは、hwcontract --http 8791 を実行し、ベアラー認証用に HWCONTRACT_TOKEN を設定したうえで、トンネル経由で公開してください。

ステートレス版の MCP 2026-07-28 に対応しています。リクエストごとの _meta、server/discover を使用し、ハンドシェイクはありません。最初に initialize を送るクライアントには、従来の形式で応答します。各リクエストが個別に仕様の世代を選ぶため、設定は不要です。

クライアントが hwcontract を見つけられない場合(PATH の問題)

GUI アプリや一部のエージェントはシェルの PATH を引き継がないため、hwcontract のみを指定すると「command not found」になることがあります。確実な対処法は 2 つあります。

  • 絶対パスを使用する:which hwcontract を実行し、表示されたフルパスを command に指定します。
  • または Python 経由で実行する(スクリプトの PATH 検索は不要):command: "python3"、args: ["-m", "hwcontract.server"] を指定します。インストール後は、どのディレクトリからでも動作します。

コントラクトのパス: MCP ツールでは、ws2812b、boot、spi-frame などの短い同梱名、またはフルファイル名(ws2812b.contract.yaml)を指定できます。これらの名前は pytest プラグインと Action で共通です。DShot600 の名前は dshot、ミニ LED の名前は ws2812b_mini です。プロジェクト内に同名のファイルがある場合は、そちらが優先されます。相対パスは HWCONTRACT_ROOT(デフォルト:サーバーの作業ディレクトリ)を基準に解決されます。任意の絶対パスとシンボリックリンクは、このルート内にとどまる必要があります。list_contracts が返すインストール済みの正確なパスのみ、ルート外の同梱コントラクトを参照できます。ただし、これによってほかのパッケージファイルへのアクセスが許可されるわけではありません。下位レベルの judge および temporal モジュールの CLI では、引き続きファイルパスを指定します。

ツール

ツールハードウェア?機能
list_contractsいいえ28 個すべてのテンプレート、種類、インストール先のパス、キャプチャ要件を一覧表示します。
judge_contractいいえ指定された観測結果をタイミングコントラクトに照らして判定します。リプレイ/テスト用です。
judge_serialいいえ指定されたログ文字列を、シリアルコントラクトの expect/forbid に照らして判定します。
judge_eventsいいえデコードされたイベントを時間的アサーション(when/require/within、forbid/while/before)に照らして判定します。
check_ws2812はいWS2812 の信号線をリアルタイムでキャプチャし、同時に判定します。1 回の呼び出しで実行します。
check_dshotはいDShot600 ESC 信号に対して同じ処理を行います。
capture_ws2812はいキャプチャのみを行い、観測結果を返します(判定は行いません)。
check_serialはいシリアルポートを N 秒間読み取り、ログを判定します。

イベントコントラクトは、レイテンシ分布と最初の失敗時刻を記録しながら、すべての発生箇所について関係性をチェックします。付属の spi-frame コントラクトは、gpio.cs.falling/rising、spi.sck.rising、spi.mosi.* の生のピンエッジを必要とします。sigrok SPI の Data アノテーションでは、これらのイベントを取得できません。実際のキャプチャを判定するには、複数チャンネルの CSV からエッジを取得する完全な手順に従ってください。セマンティックデコーダーのチェックには、インポートされたデコーダーの各行に一致する別のコントラクトを使用します。

コントラクトの種類に合った判定ツールを使用してください。タイミング、シリアル、イベントの各種類があります。種類が異なる呼び出しはエラーになります。1,000,000 文字を超えるシリアルログは、暗黙に切り詰められるのではなく拒否されます。CSV 入力には有効なデジタルサンプルが含まれている必要があります。不正な形式の行や空のキャプチャは、デバイスが合格した証拠ではなくエラーになります。

MCP よりも通常の pytest を使いたい場合は、pytest-hwcontract が判定結果をテストに変換するプラグインです。FAIL、MARGINAL、または MISSING のエッジがあると、判定表をメッセージに含めてテストが失敗し、JUnit も出力されます。

CI でゲートする

このリポジトリには GitHub Action が付属しているため、リポジトリにチェックインされたキャプチャは、すべての PR で判定されます。

- uses: MohibShaikh/hwcontract@action-v0
  with:
    timing: "ws2812b=captures/strip.csv"     # contract=capture-glob, bundled names work
    serial: "boot=logs/boot.log"
    samplerate: 24000000                     # for CSV captures (0/1 per line)
    junit: hwcontract-junit.xml              # shows in the tests tab

FAIL、MARGINAL、または MISSING のエッジがあるとステップが失敗し、失敗した行に注釈が付き、JUnit が出力されます。タイミング CSV は WS2812 ファミリーのしきい値で分類されるため、この Action が判定するのは WS2812 ファミリーのキャプチャのみです。DShot やその他のタイミングプロトコルの場合は、pytest プラグインの capture_csv にしきい値を渡してください。この Action は、このリポジトリへのプッシュごとに、正常なキャプチャと意図的に壊したキャプチャを 1 つずつ使って自己テストを実行します。

変更されていない記録済みキャプチャをリプレイすると、記録されたバイト列に対して判定ツールとコントラクトをチェックできます。新しくビルドしたファームウェアのテストにはなりません。ファームウェアの変更をゲートするには、そのビルドを書き込み、新しいキャプチャ/ログを取得して判定してください。ファームウェアのリビジョン、キャプチャパラメーター、判定の証拠はまとめて保持してください。

連携の仕組み

  observers (capture)                  judge (this repo)
  ─────────────────────                ─────────────────
  logic analyzer  ─ pulse widths ─┐
  serial port     ─ log text ─────┼─►  contract × observation  ─►  PASS/MARGINAL/FAIL/MISSING
  sigrok jsontrace ─ events ──────┘         (judge.py / temporal.py)
  • judge.py。 タイミングとシリアルを対象とする純粋な判定ツールで、コントラクトの検証にも対応します。ハードウェア不要、フレームワーク不要、キャッシュ済みです。
  • temporal.py。 イベント間の時間的アサーションを扱います。セレクター、符号付きウィンドウ、レイテンシ分布、最初の失敗時刻に対応します。
  • jsontrace.py。 sigrok-cli の Google Trace Event JSON を正規化されたイベントにインポートします。
  • sigrok_adapter.py。 ロジックアナライザーのキャプチャを、WS2812 と DShot のパルス幅分布に変換します。
  • serial_adapter.py。 シリアルログをキャプチャするか、保存済みログをリプレイします。
  • server.py。 MCP サーバーです。stdio と HTTP JSON-RPC に対応し、stdlib のみを使用します。
  • *.contract.yaml。 「正しい」状態を定義します。人が編集でき、回帰テストとしても機能します。

コントラクト形式

タイミング(ws2812.contract.yaml、dshot.contract.yaml):パルス幅を ns 単位で指定します

contract: ws2812
headroom_pct: 20         # in-spec but within 20% of a rail => "marginal"
edges:
  - {name: T0H, min: 200, typ: 350, max: 500}   # '0' bit high time

シリアル(boot.contract.yaml)では、Python の正規表現を使用します。

contract: boot
kind: serial
expect: ["IMU init OK", "boot v\\d+"]
forbid: ["panic", "Guru Meditation", "\\bnan\\b"]

イベント(spi-frame.contract.yaml)では、デコードされたイベント間の関係をアサートします。生のピンエッジまたは sigrok アノテーションに対する SVA スタイルの時間的チェックです。

contract: spi-frame
kind: events
assertions:
  - {name: cs-precedes-first-clock, when: spi.sck.rising,
     require: gpio.cs.falling, within: [-10us, 0ns]}
  - {name: mosi-setup, when: spi.sck.rising,
     forbid: spi.mosi.*, before: 20ns}

新しいコントラクトには、それに対応する観測結果が必要です。既存のキャプチャ経路で出力されない場合は、アダプターを追加してください。YAML だけではプロトコルのサポートは追加されません。

キルスイッチ

ハードウェアにアクセスするすべてのツール(キャプチャ)を即座に無効にし、純粋な判定ツールは引き続き使用できるようにします。

export HWCONTRACT_SAFE=1

セキュリティ

呼び出し元はプロンプトインジェクションを受ける可能性のある LLM であるため、すべてのツール引数を信頼できないものとして扱います。任意のコントラクトパスはサーバーの作業ディレクトリ内に制限され、HWCONTRACT_ROOT で上書きできます。バンドルされているコントラクトは、完全一致するものを許可リストに登録しています。driver/channel/port は文字セットを検証し、samples/seconds/samplerate は範囲を制限します。sigrok-cli はタイムアウト付きで実行され、YAML は safe_load で読み込みます。認証を追加せずに、このサーバーをネットワーク上に公開しないでください。

セルフテスト:ハードウェア不要、どこからでも実行可能

hwcontract --selftest                       # full MCP round-trip
python3 -m hwcontract.judge --demo           # tiny synthetic self-test, includes MISSING RESET
python3 -m hwcontract.sigrok_adapter --demo
python3 -m hwcontract.serial_adapter --demo
pytest                                      # the tests/ suite; pip install -e .[dev]

CI はすべての push と PR でテストスイートを実行し、PyPI の公開ジョブはその完了を待ちます。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。