kitewright について
Kitewright は、AI エージェント向けの軽量なブラウザー自動化 MCP サーバーです。ナビゲート、スクリーンショット、抽出、PDF 出力の各ツールを単一の小さなバイナリで提供します。Node.js と Playwright を使う構成に比べ、起動の速さとアイドル時のメモリ消費の少なさを特徴としています。
主な機能
- 01ナビゲート、スクリーンショット、抽出、PDF のツール
- 02Node.js ランタイムを必要としない単一バイナリ
- 03ブラウザーとコンテキストのプリウォームプール
- 04要素が表示され安定するまでの自動待機
- 05Streamable HTTP と stdio のトランスポート
活用シーン
- 01LLM クライアントによるページ内容の取得とスクリーンショット
- 02Node.js 環境のないサーバーへのブラウザー自動化の導入
- 03ログイン状態を保ったままの連続操作
MCP 対応
ストリーミング HTTP または stdio で MCP サーバーとして動作し、Claude Code や Cursor などの MCP クライアントにブラウザー操作とスクリーンショットのツールを提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
24掲載時のツール一覧 · 2 週間前に更新
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| timeout_ms | integer|null | いいえ | タイムアウト(ミリ秒、デフォルト 10000、最大 30000) |
| should_exist | boolean | いいえ | 存在(true、デフォルト)または不在(false)をアサートします |
| condition_text | string|null | いいえ | 本文に表示される(または表示されない)テキスト |
| condition_selector | string|null | いいえ | 一致する(または一致しない)セレクター。CSS / text= / role= |
詳細
Kitewright
AI エージェント向けのブラウザー自動化を、小さな単一バイナリで実現します。 LLM クライアントに navigate / screenshot / extract を提供する MCP サーバー(Streamable HTTP + stdio)です。Node.js + Playwright のスタックを抱える必要はありません。

1 行でインストールできます。
claude mcp add kitewright -- npx -y @kitewright/mcp
理由 — 主張ではなく実測値
同じマシン、同じ Chromium headless-shell ビルド、同じページでの @playwright/mcp 0.0.78 との直接比較(詳細な方法):
| @playwright/mcp | Kitewright | |
|---|---|---|
| コールドスタートから待ち受け開始まで | 354 ms | 75 ms |
| サーバーの RSS(アイドル時) | 102–125 MB | 7.6 MB |
| サーバーの RSS(処理後) | 93 MB | 10.9 MB |
| 配布形態 | 18 MB のパッケージ + Node.js ランタイム | 6.9 MB の静的バイナリ |
| 初回の navigate(ブラウザー起動を含む) | 2623 ms | 822 ms(セッションの事前ウォームアップ) |
| ウォーム時の navigate レイテンシー | 80–116 ms | 99–105 ms(同等) |
| アイドル時の動作 | ブラウザーを起動したまま維持 | アイドル TTL 経過後にブラウザーを破棄し、次のセッションで再度事前ウォームアップ |
ブラウザー本体(Chromium)のコストは、どの言語でも同じです。どちらも同じブラウザーと CDP で通信するため、ウォーム時のレイテンシーは同等です。差が出るのは、その周辺すべて、つまり起動、配布、アイドル時のフットプリント、ライフサイクル管理です。率直に言えば、playwright-mcp は現在約 25 個のツールを提供していますが、こちらは 21 個です。この差を埋めることが今後のロードマップです。
信頼性 — 操作可能になるまでの自動待機
click / type / fill_form / select_option / hover は、無条件に実行されることはありません。操作の前に、エンジンは最大 5 秒の操作ごとの予算内で、100 ms 間隔で対象要素をポーリングします。対象要素が存在し、表示され(display:none / visibility:hidden / サイズ 0 ではない)、有効で(disabled / aria-disabled がなく)、クリック位置が別の要素に覆われておらず、連続する 2 フレーム間で幾何学的に安定していることを確認します。安定した要素は最初のポーリングで処理されるため、問題がなければ待機は意識されません。一方、操作できない場合は、サイレントな誤クリックや一般的なタイムアウトではなく、原因に応じたエラー(not found / not visible / disabled / covered / unstable)が返されます。一時的な CDP エラーは 2 回再試行されます。インタラクションツールには timeout_ms を渡して、操作ごとの 5 秒の予算を上書きできます(たとえば、要素がすでに存在するはずの場合に、短いタイムアウトを指定して速やかに失敗させることができます)。
パフォーマンス
外部サイトへのレイテンシーはネットワークが支配的です。DNS + TLS + TTFB(約 400~600 ms)は、どのツールでも短縮できない下限であり、Kitewright もそれを実現できるとは主張しません。Kitewright が対処するのは、ネットワーク周辺の制御可能なコストです。
- 事前ウォームアップ + ウォームコンテキストプール。 MCP セッションが初期化されると、サーバーはブラウザーを起動し、バックグラウンドで事前作成済みの空のブラウザーコンテキストを小規模なプール(
MCP_CONTEXT_POOL、デフォルトは 2)に補充します。新しいセッションには準備済みのコンテキストとページが渡されるため、初回の navigate ではブラウザー起動とコンテキスト作成のコストがどちらもかかりません。localhost で実測した初回の navigate は、コールド時の約 709 ms に対して、事前ウォームアップ時は約 31 msです(Apple Silicon、詳細)。アイドル時にブラウザーが破棄されるとプールも空になり、次の要求時に遅延補充されるため、アイドル時のフットプリントは約 8 MB のベースラインに保たれます。 - ライトモード。
browser_navigate {lite:true}(およびextract/extract_markdownのデフォルト設定)では、読み込み前に画像、メディア、フォントと、広告・分析用ホストへのアクセスをブロックします。データ量の大部分をスキップすることで、重いページの DOM 準備完了までの時間を 30~70%短縮します。スクリーンショットと PDF では、リソースをブロックしません。 - 共有ディスクキャッシュ + 接続の事前ウォームアップ。 安定した
--disk-cache-dir(KITE_CACHE_DIR)を使うと、実行をまたいだアセットの再取得でキャッシュを利用できます。KITE_PREWARM_URLを設定すると、事前ウォームアップ中に既知のオリジンへの DNS + TLS 接続を確立します。
率直に言えば、短縮できるのはブラウザーの起動、ページのデータ量、セッションの開始、接続の確立です。リモートオリジンへのネットワーク往復時間ではありません。
アーキテクチャ
crates/
├── engine/ kitewright-engine — CDP core (chromiumoxide): lazy launch, idle reaper,
│ per-session browser contexts, capped text extraction + AX snapshots.
│ Shared by all frontends.
└── server/ kitewright — rmcp Streamable HTTP server exposing the engine as MCP tools.
bindings/
└── node/ @kitewright/node — napi-rs bindings exposing a Puppeteer-compatible
(experimental) API over the same engine (built separately; kept out
of the core cargo workspace). See "@kitewright/node" below.
各 MCP セッションは、それぞれ独自の Chromium ブラウザーコンテキスト内に、永続的なページを 1 つ持ちます(エージェント間で Cookie は分離されます)。一度ログインすれば、あとはクリックを続けるだけです。セッションが終了するとページとコンテキストは閉じられます。ブラウザー自体はアイドル TTL 経過後に終了し、次の呼び出し時に透過的に再起動されます。
インストール
最も手軽なのは、@playwright/mcp と同様に npx から直接実行する方法です。
claude mcp add kitewright -- npx -y @kitewright/mcp
npx @kitewright/mcp は、お使いのプラットフォーム用のビルド済み kite バイナリ(esbuild 形式のプラットフォームごとのオプション依存関係)を解決し、stdio MCP サーバーを起動します。Rust ツールチェーンもビルドも不要です。詳細は npm/kitewright-mcp をご覧ください。
生の kite バイナリを使いたい場合はこちらです(上記の npx が最も簡単な方法です。以下の方法では Rust ツールチェーンが必要です)。
# From the public repo — builds + installs the `kite` binary:
cargo install --git https://github.com/kitewright/kitewright kitewright # → ~/.cargo/bin/kite
# From a local checkout:
cargo install --path crates/server
# Docker (headless Chromium bundled in the image):
docker run --rm -p 8090:8090 kitewright # build locally: docker build -t kitewright .
crates.io または Homebrew ではまだ公開されていないため、
cargo binstall/brewはまだ設定されていません。npx @kitewright/mcp(ツールチェーン不要)または上記のcargo install --gitを使用してください。
ブラウザーを入手する
Kitewright は CDP 経由で Chromium を操作しますが、Chromium は同梱していません。システムに Chrome/Chromium があればそれを使用し、BROWSER_EXECUTABLE の設定を尊重します。どちらも見つからない場合は、kite install でダウンロードしたブラウザーを使用します。
kite install # fetch the latest stable chrome-headless-shell into the kite cache
kite install は、お使いのプラットフォーム向けの最新の Chrome-for-Testing chrome-headless-shell ビルドを $KITE_CACHE_DIR(または OS のキャッシュディレクトリ)にダウンロードし、エンジンが自動的に使用します。BROWSER_EXECUTABLE は不要です。ビルドがすでに存在する場合、再実行しても何も行われません。Docker イメージには Chromium がすでに含まれています。
実行と接続
引数なしで kite を実行すると、Streamable HTTP 経由で MCP を提供します(ネットワーク対応、デフォルト、認証と複数セッションをサポート)。kite --stdio を実行すると、ローカルクライアント向けに stdio 経由で単一セッションを提供します。
kite
# → kitewright listening on http://0.0.0.0:8090/mcp
HTTP トランスポート — Claude Code から接続します。
claude mcp add kite --transport http http://localhost:8090/mcp
stdio トランスポート — MCP クライアントの設定(Claude Desktop、Cursor など):
{
"mcpServers": {
"kite": {
"command": "kite",
"args": ["--stdio"]
}
}
}
設定
| 環境変数 | デフォルト | 意味 |
|---|---|---|
MCP_HTTP_BIND | 0.0.0.0:8090 | リッスンアドレス |
MCP_AUTH_TOKEN | 未設定 | 設定すると、/mcp には Authorization: Bearer <token> が必要になります(それ以外の場合は 401)。未設定の場合はアクセスが開放され、起動時に警告が表示されます |
MCP_RATE_LIMIT_PER_MINUTE | 300 | クライアント IP ごとのリクエスト上限(固定の 60 秒ウィンドウ)。超過すると 429 |
BROWSER_EXECUTABLE | 自動検出 | chrome / chromium / chrome-headless-shell のパス。未設定の場合、システムの Chrome / Chromium を検出し、なければキャッシュディレクトリ内の kite install 管理ビルドを使用します |
BROWSER_NO_SANDBOX | 未設定 | 任意の値を設定すると、--no-sandbox を渡します(コンテナ向け) |
KITE_HEADLESS | 未設定 | Kite はデフォルトで画面表示ありのブラウザーを起動するため、自動操作を確認できます。任意の値を設定するとヘッドレスで実行します。サーバー、CI、ディスプレイのないコンテナでは、画面表示ありの Chrome は起動に失敗するため必須です |
KITE_IDLE_TIMEOUT_SECS | 1800 | メモリを解放するため、ヘッドレスブラウザーを終了させるまでのアイドル秒数。デフォルトの 30 分なら、通常の一時停止中もセッションを維持できます(画面表示ありのブラウザーは終了しません)。終了した場合も、Cookie の自動復元によって認証済みセッションが維持されます。メモリに制約のある複数セッションサーバーでは値を小さくしてください |
KITE_ALLOW_SECRET_FILES | 未設定 | 任意の値を設定すると、browser_fill_secret がディスク上の file:/path シークレットを読み取れるようになります。任意のホストファイルの読み取りや持ち出しを防ぐため、KITE_SECRET_DIR(読み取り可能なディレクトリを制限するもの)も必須です。env: シークレットにはオプトインは不要です |
KITE_VIEWPORT | 1440x900 | デフォルトのビューポート/ウィンドウサイズ(WIDTHxHEIGHT)。Chromium 自体のデフォルトは狭い 800x600 です。実行時は browser_resize ツールで変更できます |
MCP_CONTEXT_POOL | 2 | 事前にウォームアップした空のブラウザーコンテキストを保持する数。これにより、新しいセッションはすぐに使用できるコンテキストとページを取得できます(コンテキスト作成の待ち時間はゼロ)。0 にすると無効になります。プールはバックグラウンドで補充され、アイドル時のブラウザー終了に伴って空になります(プロセスを存続させることはありません) |
KITE_CACHE_DIR | <tmp>/kitewright-cache | ディスク上で共有する HTTP キャッシュ(--disk-cache-dir)。起動後も維持されるため、繰り返し取得するアセットはキャッシュが使われます。注:セッションごとに分離されたコンテキスト(Cookie の分離)では一時キャッシュが使われます。この設定はブラウザーのデフォルトコンテキストに適用されます |
KITE_PREWARM_URL | 未設定 | 設定すると、最初の実際のページ遷移の前に、使い捨てページでこのオリジンへ移動し、DNS、TLS、接続を確立します。未設定の場合は何もしません |
BROWSER_PREWARM | 未設定 | 任意の値を設定すると、サーバー起動時にブラウザーを起動してウォームアップします(未設定の場合、MCP セッションの初期化時にウォームアップを実行します) |
RUST_LOG | info | ログフィルター |
セキュリティ
Kitewright は実際のブラウザーを操作するため、エンドポイントは特権的なものとして扱ってください。デフォルト設定はローカルでの使用には安全ですが、外部に公開する前に強化してください。
- デフォルトではループバック(
127.0.0.1:8090)にバインドします。MCP_HTTP_BINDを明示的に設定しない限り、ネットワーク経由では到達できません。公開する場合は、MCP_AUTH_TOKENを設定してください。 設定しないと、/mcpエンドポイントは認証されません(起動時に警告として記録されます)。認証では定数時間比較を使用し、リクエストはレート制限され、クロスオリジンのブラウザーリクエストは拒否されます(DNS リバインディング対策)。 - ブラウザーツールには SSRF が本質的に伴います。 呼び出し元は、内部アドレスやクラウドメタデータのアドレス(
169.254.169.254、RFC-1918、localhost)に移動できます。ローカル/開発自動化では それが目的 ですが、公開インスタンスではリスクになります。Kitewright はこれらをブロックしません(デフォルトでブロックするとローカル自動化に支障をきたすためです)。公開環境では、機密性の高い内部エンドポイントに到達できない認証およびネットワークポリシーの背後に配置してください。 - ローカルファイルへのアクセスはデフォルトで無効です。
file://への移動にはKITE_ALLOW_FILE_URLS=1が必要です。browser_fill_secretによるファイルの読み取りには、KITE_ALLOW_SECRET_FILES=1とKITE_SECRET_DIRによる制限が必要です(読み取り時に正規化され、指定ディレクトリ配下にとどまる必要があります)。 - 依存関係は CI でスキャンされます(Trivy +
cargo audit)。配布されるkiteバイナリには、既知の脆弱性を持つクレートは含まれていません。(サーバーでは使用されない)kite-pdfクレートに限定される 2 件の DoS アドバイザリについては、.cargo/audit.tomlを参照してください。
ツール(v0.4)
すべてのツールは、セッションの永続ページに対して動作します。
読み取り
browser_navigate {url, lite?}— タイトル、最終 URL、表示テキスト(上限あり)を返します。lite:trueを指定すると ライトモード が有効になり、CDP のNetwork.setBlockedURLsを使って画像/メディア/フォントと、一般的な広告/アナリティクスホスト(doubleclick、google-analytics、GTM、facebook pixel など)をブロックします。これにより、負荷の高いページで DOM の準備が速くなります(負荷の高いサイトでは 30~70%高速化します。テキストのみで、スクリーンショットの前には使用しないでください)。変更するまでセッション中は設定が維持されます。extract/extract_markdownは、ピクセルが関係しないため、移動時にデフォルトでライトモードを使用します。screenshot/pdfはリソースをブロックしませんbrowser_extract {url?, selector, attribute?}— セレクターに一致する要素から、テキスト/属性を取得しますbrowser_extract_markdown {url?}— メインコンテンツを Markdown として取得します(「readability」モード:見出し/リンク/リスト/コード/テーブルを含み、ナビゲーション/スクリプト/スタイルは除去されます。上限は約 2 万文字です)browser_screenshot {url?, full_page?}— 現在のページの PNG を取得します(urlを指定すると先に移動します)browser_pdf {url?, format?, landscape?, print_background?, display_header_footer?, header_template?, footer_template?, margin_top?, margin_bottom?, margin_left?, margin_right?, scale?, prefer_css_page_size?}— CDP のPage.printToPDFで PDF に出力します。実行時のヘッダー/フッター(法的文言、ページ番号)や CSS 単位の余白("35px"/"20mm")を含む、puppeteer の全オプションに対応しています。JSON エンベロープ{format, bytes, base64}を返します(MCP にはネイティブの PDF 型がありません。ファイルを取得するにはbase64をデコードしてください)browser_set_content {html, wait_until?}— CDP のPage.setDocumentContentを介し、puppeteer のpage.setContentを使って、現在のページに生の HTML 文字列を読み込みます。wait_untilはload(デフォルト)/domcontentloaded/networkidle0です。HTML から PDF をレンダリングする場合は、browser_pdfと組み合わせることで、サーバーへの往復なしに処理できます。大きなドキュメントにも対応しますbrowser_snapshot {diff?}— アクセシビリティツリーのスナップショット(ロール、名前、状態)を、約 1 万 5 千文字を上限として取得します。diff:trueを指定すると、このセッションで前回のスナップショットから変更された部分のみを返します(初回呼び出しは基準となります)
デバッグ
browser_console {clear?}— 前回の呼び出し以降にページで取得されたコンソールメッセージ(log/warn/error/info)を返します。clear:trueを指定するとバッファを空にしますbrowser_network {clear?, filter?}— ページで取得されたネットワークリクエスト(method、url、status、resourceType)を返します。filterは URL に部分一致する文字列です
操作
browser_click {selector, timeout_ms?}— 最初に一致した要素までスクロールしてクリックしますbrowser_type {selector, text, clear?, press_enter?, timeout_ms?}— 要素にフォーカスして文字を入力しますbrowser_fill_form {fields: [{selector, value}], timeout_ms?}— 1 回の呼び出しで複数の入力欄に値を入力します(フィールドごとの成功/エラー概要を返します)browser_fill_secret {selector, secret_ref, press_enter?, timeout_ms?}— 平文がツール呼び出しに含まれることなくシークレット(パスワード)を入力します。secret_refはenv:NAME(サーバー環境変数)またはfile:/path(KITE_ALLOW_SECRET_FILESによるオプトインと、必須のKITE_SECRET_DIRによる制限が必要)です。サーバー側で解決してから入力しますbrowser_select_option {selector, value?, label?, timeout_ms?}— 値または表示ラベルで<option>を選択します(changeを発火します)browser_hover {selector, timeout_ms?}— 要素の中央にマウスを移動します(CSS の:hoverメニューを表示します)browser_press_key {key}— フォーカス中の要素に Enter/Tab/Escape/ArrowDown/…を送信しますbrowser_navigate_back {}— 履歴を戻り、新しいタイトルと URL を返しますbrowser_handle_dialog {accept, prompt_text?}— 次の JS ダイアログを自動で承認/閉じるよう、あらかじめ設定しますbrowser_wait_for {selector?, text?, timeout_ms?}— セレクターに一致するかテキストが表示されるまでポーリングします(デフォルト 10 秒、最大 30 秒)
状態
browser_save_state {}— Cookie、localStorage、URL を JSON 文字列として取得し、保存できるようにしますbrowser_restore_state {state}— Cookie をすぐに設定し、localStorage はそのオリジンへの移動時または移動後に適用します(「一度ログインすれば、セッションをまたいで再利用できます」)
アサーション
browser_assert {condition_selector?, condition_text?, should_exist?, timeout_ms?}— 構造化された{passed, checked, found, elapsed_ms}を返します(条件が失敗してもエラーになりません。should_exist:falseは存在しないことを検証します)
セレクター構文
click / type / fill_form / select_option / hover / wait_for / extract / assert は、3 種類のセレクター形式を受け付けます(同じ形式は内部の要素ハンドル API でも使用されます。query / query_all は、Wave-3 の Puppeteer スタイルの npm ファサードの基盤となる .click() / .type_str() / .text() / .attribute() / .bounding_box() を備えた ElementRef ハンドルを返します)。
- CSS(デフォルト)— 例:
#login、button.primary、input[name="email"] text=<visible text>— テキストをトリミングした後に、その文字列を含む最初の表示要素role=<role>[name="<accessible name>"]— ARIA ロールとアクセシブル名に一致する要素。例:role=button[name="Submit"]
ロールの解決には、実用的な JS ヒューリスティック(暗黙的なロールを持つ要素のマップ、および aria-label / aria-labelledby / 関連付けられた <label> / テキスト / value / placeholder / title / alt から取得するアクセシブル名)を使用します。これは完全な ARIA 計算済み名前の実装ではありませんが、エージェントが対象とする一般的なインタラクティブロールをカバーします。通常の CSS による extract はすべての一致を返しますが、text= / role= による extract は解決された単一の要素を返します。
@kitewright/node(Puppeteer 互換、実験的)
bindings/node は napi-rs のネイティブアドオンで、kitewright-engine の上に Puppeteer 形式のファサードを提供します。ブラウザーのライフサイクル、CDP、待機のヒューリスティックは共有 Rust コアでネイティブに実行されるため、JS レイヤーは薄くなっています。一般的な HTML→PDF のワークフロー(たとえば、Puppeteer をレンダリングのみに使う請求書/レポートサービス)を対象としています。このワークフローでは、インポートを 1 行変更するだけで移行できます。
// import puppeteer from 'puppeteer'
import puppeteer from '@kitewright/node'
const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] })
const context = await browser.createBrowserContext() // per-invoice isolation
const page = await context.newPage()
await page.setContent(html, { waitUntil: 'networkidle0', timeout: 0 })
await page.evaluate(() => document.fonts.ready) // promise-returning
const pdf = await page.pdf({ // → Node Buffer
format: 'a4', printBackground: true, displayHeaderFooter: true,
footerTemplate: legalFooterHtml, // legal text + page numbers
margin: { top: '20px', bottom: '35px' },
})
await page.close(); await context.close(); await browser.close()
各 Page は、独自の Chromium ブラウザーコンテキスト内で、1 つの永続的なエンジンセッションになります。これは Puppeteer が保証する createBrowserContext() ごとの分離とまったく同じです。page.setContent と page.pdf(footerTemplate) を使うと、ページ番号に応じて繰り返し表示されるフッター付きの有効な複数ページ PDF を生成できます(請求書サービスの実際のフローを再現する bindings/node/test/invoice.e2e.mjs でエンドツーエンドに検証済みで、test/out/invoice.pdf に出力されます)。
互換性マトリックス
| 対応 | 非対応(明確なエラーがスローされます) |
|---|---|
puppeteer.launch({ headless, args, executablePath }) | リクエストインターセプト(setRequestInterception) |
browser.newPage() / browser.createBrowserContext() / browser.close() | トレース(page.tracing) |
context.newPage() / context.close() | デバイス/ビューポートのエミュレーション(setViewport、emulate) |
page.setContent(html, { waitUntil })(load / domcontentloaded / networkidle0) | page.screenshot(MCP の browser_screenshot ツールを使用してください) |
page.evaluate(fn | string)(Promise を返す本体を含む) | page.waitForSelector、addScriptTag、exposeFunction |
page.pdf(options) — 全オプション(format、landscape、printBackground、displayHeaderFooter、header/footerTemplate、margin、scale、preferCssPageSize)→ Buffer | puppeteer.connect(リモートブラウザー) |
page.goto(url) / page.close() | --no-sandbox は有効です。それ以外の Chromium の args は無視されます。headless:false は無視されます(常にヘッドレスです) |
注:waitUntil: 'networkidle0' は、読み込み完了後に短時間の安定待ちを行う形で近似されます(インラインの setContent コンテンツでは、インターセプトは行われません)。非対応のメソッドは何もせずに終了するのではなくエラーをスローするため、呼び出し側は不足点をすぐに把握できます。
ビルド(ローカル、このプラットフォーム)
アドオンはコアの cargo ワークスペースとは別にビルドされます([workspace] exclude に記載されているため、cargo clippy --workspace や Rust のテストが napi ツールチェーンに触れることはありません)。
cd bindings/node
npm install
npx napi build --release --js binding.js --dts binding.d.ts # emits kitewright-node.node
BROWSER_EXECUTABLE=/path/to/chrome node --test test/invoice.e2e.mjs
kite-pdf — HTML/Typst → PDF
kite-pdf は、同じエンジン(クレート crates/pdf、バイナリ kite-pdf)を基盤とする、ドキュメント → PDF に特化したレンダリングサービスおよび CLI です。バックエンドは 2 種類あり、ビルド時には Cargo の機能で、実行時にはリクエストごとに選択します。
- Chromium — 共通の
kitewright-engine(ヘッドレス Chromium、ヘッダー/フッターのテンプレート、余白、横向き、背景、スケール、CSS のページサイズなど、Page.printToPDFの全オプション)を使って、html/urlを PDF に変換します。 - Typst — Typst の
templateと JSON のdataから PDF を生成します。ブラウザーは起動しません。コンパイラーとフォントはバイナリに組み込まれており、レンダリングは CPU のみで実行され、言語に依存せず、再現可能です。
1 つのクレート、3 種類のビルド形態(バイナリ名は同じ)
| ビルド | 機能 | バックエンド | おおよそのサイズ | 対象 |
|---|---|---|---|---|
kite-pdf(デフォルト) | chromium + typst | HTML と Typst | 約 43 MB(macOS arm64、release+LTO) | 両方が必要な場合。1 つのバイナリですべてをレンダリングできます。 |
kite-pdf-chromium | --no-default-features --features chromium | HTML のみ | 最小のバイナリ(Typst/フォントなし)+ 実行時ブラウザー | HTML/URL のみをレンダリングする場合。Typst コンパイラーと同梱フォントを省けます。 |
kite-pdf-lite | --no-default-features --features typst | Typst のみ | 約 39 MB、ブラウザーなし | テンプレートを管理し、ブラウザー不要の distroless サービスを利用したい場合。 |
cargo build --release -p kite-pdf # both backends
cargo build --release -p kite-pdf --no-default-features --features chromium
cargo build --release -p kite-pdf --no-default-features --features typst
HTTP API
JSON 本文を指定して POST /render を呼び出すと、application/pdf のバイト列(200)または JSON { "error": "..." }(クライアントエラー 400/サーバーエラー 500)が返されます。GET /healthz は、バイナリに組み込まれたバックエンドを返します。バインドアドレス:KITE_PDF_BIND(デフォルトは 0.0.0.0:8091)。
{
"engine": "chromium" | "typst", // optional; else inferred (html/url→chromium, template→typst)
"html": "<!doctype html>...", // chromium
"url": "https://...", // chromium
"template": "= Invoice ...", // typst source
"data": { "number": "INV-1" }, // JSON, exposed to the template as sys.inputs.data
"format": "A4" | "Letter" | "Legal" | "A3",
"landscape": false,
"print_background": false,
"display_header_footer": false,
"header_template": "<div>...</div>",
"footer_template": "<div>... <span class=\"pageNumber\"></span> ...</div>",
"margin": { "top": "20px", "bottom": "40px", "left": "15px", "right": "15px" }
}
実行中のバイナリにコンパイルされていないバックエンドをリクエストすると、明確な 400 エラーが返されます(例:"typst backend not compiled in this build — use the full or -lite build")。Typst テンプレートでは、次のように注入されたデータを読み取ります。
#let data = json(bytes(sys.inputs.data))
= Invoice #data.number
# Chromium: render an HTML string
curl -sX POST localhost:8091/render \
-H 'content-type: application/json' \
-d '{"html":"<h1>Hello</h1>"}' -o hello.pdf
# Typst: data-driven invoice, no browser touched
curl -sX POST localhost:8091/render \
-H 'content-type: application/json' \
-d '{"template":"#let d=json(bytes(sys.inputs.data))\n= Invoice #d.number","data":{"number":"INV-7"}}' \
-o invoice.pdf
注: レンダリングサービスには、デフォルトで認証機能がありません。信頼できるネットワーク上、またはゲートウェイの背後で実行してください。(
kiteサーバーのHttpGuardと同様の Bearer 認証 + レート制限は、今後の対応事項です。)
CLI
# Chromium: HTML file → PDF, with a footer template + margins
kite-pdf render --html-file invoice.html --footer-file footer.html \
--margin-top 20px --margin-bottom 40px -o invoice.pdf
# Typst: template + data → PDF (no browser)
kite-pdf render --template invoice.typ --data invoice.json -o invoice.pdf
# Run the HTTP service (also the default with no arguments)
kite-pdf serve
Docker
# Full service (slim Debian + Chromium; both backends). Build from the repo root:
docker build -f crates/pdf/Dockerfile -t kite-pdf .
# Browser-free, distroless, Typst-only:
docker build -f crates/pdf/Dockerfile.lite -t kite-pdf-lite .
docker run -p 8091:8091 kite-pdf
誠実な比較
- Gotenbergとの比較: kite-pdfは、セルフホスト可能なHTML→PDF変換手段のなかで最も軽量です。単一の小さなバイナリで、ブラウザーのライフサイクルを遅延管理し、アイドル時には終了します。オフィス文書の変換(LibreOfficeを使ったDOCX/XLSX/ODT)や、必要な機能が揃ったAPIが必要な場合はGotenbergが優れています。kite-pdfは意図的にオフィス形式には対応していません。
- react-pdf/クライアント側PDFライブラリとの比較: Typstの経路はブラウザー不要で、言語を問いません。NodeランタイムもReactもヘッドレスChromeも不要で、あらゆる言語からHTTP経由でテンプレートとJSONを渡すだけです。その代わりに、Reactのコンポーネントモデルを手放すことで、はるかに小さく、高速で、再現性の高い組版パイプラインを実現します。
- 対応していない点: オフィス文書(DOCX/XLSX)の変換には対応しておらず、Chromiumバックエンドは実行時にブラウザーを必要とします(Typst/
-liteバックエンドでは不要です)。
ロードマップ
-
snapshot— アクセシビリティツリーのスナップショット(トークン予算に基づく) -
click/type/press_key -
wait_for(セレクター/テキストのポーリング) -
fill_form/select_option/hover/navigate_back/handle_dialog - ストレージ状態(
save_state/restore_state)— セッションをまたいでログイン状態を再利用 - CSSに加えて、ロール/テキストセレクター(
text=、role=…[name="…"]) -
assert— エージェント主導の機能テスト向けの構造化された成功/失敗判定プリミティブ - Markdown(readability)抽出モード
-
pdf— 現在のページをPDFに印刷(Page.printToPDF) -
kite-pdf— スタンドアロンのHTML/Typst→PDFサービス+CLI(デュアルバックエンド、機能フラグで制御する3種類のビルド形態) - 原因別エラーに対応した操作可能性の自動待機(表示/有効/遮られていない/安定)
- デバッグ用の
console/networkキャプチャ -
snapshot {diff}— 前回のスナップショット以降に変更された箇所のみを取得 - 要素ハンドルのプリミティブ(
query/query_all→ElementRef)— npmファサードの基盤 - 呼び出しごとにページを作成する代わりに、MCPセッションごとのブラウザーコンテキストを使用(Cookieを分離)
- Bearerトークン認証+レート制限
-
bindings/node:napi-rsによるPuppeteer互換ファサード(@kitewright/node、実験的)— launch/newPage/createBrowserContext/setContent/evaluate/pdf/goto/close。HTML→PDFのフローをエンドツーエンドで実証済み -
bindings/node:プラットフォーム別のビルド済みバイナリ+npm公開 - Pythonバインディング(PyO3)
- README向けにplaywright-mcpとのベンチマーク(コールドスタート、RSS、イメージサイズ)
- ビルド済みリリースバイナリ(macOS/Linux/Windows)+
cargo binstall+Homebrew tap
対象外
Kitewrightは、QAテストフレームワークではなく、軽量なエージェント用ツールとして設計されています。以下には対応しません。
- 複数のブラウザーエンジンへの対応。 設計上、CDP/Chromiumのみです。FirefoxやWebKitには対応しません。単一のエンジンと単一のプロトコルを使うことで、バイナリを小さく保ち、ライフサイクルをシンプルにしています。
- テストランナー、フィクスチャ、トレースビューアー、動画キャプチャの同梱。 これらはQAフレームワーク(Playwright Test、Cypress)の役割です。Kitewrightはエージェント向けのプリミティブ(
snapshot、assert、ストレージ状態)を提供し、ランナーはエージェント自身、または薄いスクリプトが担います。 - 任意のJavaScriptを実行する
evalツールの公開。 エージェントまたはモデルが作成したJavaScriptを、復元済みCookieを含む実行中のセッションに対して実行できるようにするのは、セキュリティ上の重大なリスクです。セレクターの解決とヘルパーの実行には、精査済みの固定JavaScriptのみを使用します。
ライセンス
MIT
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。