Skip to main content
Glama

Web Speed

Web Speedは、AIエージェントにおける信号対雑音比(Signal-to-Noise)の問題を解決します。現代のWebは人間の目向けに最適化されていますが(乱雑なHTML、複雑なレイアウト、JSを多用したインターフェース)、Web Speedはその混沌を、高スループットのエージェント群向けに設計された、決定論的でトークン効率の高い構造マップに変換します。

AIは含まれていません。 anthropicもopenaiも、LLMへの依存も一切ありません。すべての解釈は呼び出し側のエージェント内で行われます。


なぜ存在するのか

問題

Web Speedの解決策

生のHTMLは15万文字以上のスクリプト、スタイル、SVGのノイズを含む

構造以外のすべてを削除 → 最大97%のトークン削減

LLMは生のDOMで要素IDを幻覚したり、インタラクションポイントを見逃したりする

固定された構造マップを返す — そこにあるものはそこにあり、捏造は一切なし

カスタムスクレーパーはサイトごとに壊れる

決定論的プロトコル — Web上のすべてのサイトで同じJSON形式

エージェントがページを1往復ずつ再発見しなければならない

site_mapが1回の呼び出しでドメイン全体をクロール


Related MCP server: Delta-MCP

ツール

ツール

説明

interpret_page

完全な構造マップ:見出し、ナビゲーション、コンテンツリンク、フォーム、テーブル、テキスト、メタデータ

submit_form

フォームを送信(GETまたはPOST)し、結果ページのマップを取得

site_map

ルートURLからクロールし、全ページの結合マップを返す

inspect_element

CSSセレクタに一致するノードの詳細な構造データ

page_type

即時のページ分類 — login、listing、article、form、navigation、other

invalidate_cache

キャッシュされたマップを破棄し、次回の呼び出しで最新を取得


インストール

Mac / Linux

cd web-interpreter
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Windows

cd web-interpreter
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt

実行

MCPインスペクターを使用したローカル開発の場合:

mcp dev server.py

stdio経由で直接実行する場合(MCPクライアントが起動する方法):

python server.py

Claude / Coworkへの登録

~/Library/Application Support/Claude/claude_desktop_config.json(Mac)またはWindows上の同等のファイルに追加します:

{
  "mcpServers": {
    "web-speed": {
      "command": "/absolute/path/to/web-interpreter/venv/bin/python",
      "args": ["/absolute/path/to/web-interpreter/server.py"]
    }
  }
}

その後、Claude Desktop / Coworkを終了して再起動します。6つのツールがweb-speed MCPサーバーの下に表示されます。


出力スキーマ

interpret_page

{
  "url": "https://example.com/",
  "fetched_at": "2025-01-01T12:00:00Z",
  "page_type": "other",
  "title": "Example Domain",
  "description": "",
  "headings": [
    { "level": 1, "text": "Example Domain" }
  ],
  "navigation": [
    { "label": "Home", "url": "https://example.com/", "location": "header" }
  ],
  "content_links": {
    "total": 47,
    "truncated": false,
    "items": [
      { "label": "More information...", "url": "https://www.iana.org/domains/example" }
    ]
  },
  "forms": [
    {
      "id": "search",
      "action": "https://example.com/search",
      "method": "GET",
      "fields": [
        {
          "name": "q",
          "type": "text",
          "label": "Search",
          "placeholder": "Search...",
          "required": false,
          "value": ""
        },
        {
          "name": "_csrf",
          "type": "hidden",
          "label": "",
          "placeholder": "",
          "required": false,
          "value": "abc123"
        }
      ]
    }
  ],
  "tables": [
    {
      "id": "results",
      "headers": ["Name", "Price", "Stock"],
      "rows": [["Widget A", "$9.99", "In stock"]]
    }
  ],
  "text_blocks": [
    { "tag": "p", "text": "This domain is for use in illustrative examples." }
  ],
  "metadata": {
    "lang": "en",
    "canonical": "",
    "open_graph": { "title": "", "description": "", "image": "" }
  }
}

主要フィールド:

  • navigation — セマンティックなnav/header/footer要素内のリンク(サイトのクローム、メニュー)。最大60件。

  • content_links — ページ本文内のリンク(記事、検索結果、リスト)。60件で切り捨てられた場合でも実際の数を知るために常にtotalが含まれます。

  • forms — すべてのフィールドを含むすべてのフォーム。CSRFトークンは非表示フィールドのvalueにそのまま保持されます。

  • page_type — 構造から推論:パスワードフィールド → login、多数のアイテム/リンク → listing、段落を含む<article> → article、フォーム → form、リンクが主 → navigation。


page_type

軽量 — 分類のみを返します。ページがキャッシュされている場合は即時です。

{
  "url": "https://example.com/login",
  "fetched_at": "2025-01-01T12:00:00Z",
  "page_type": "login",
  "title": "Sign In"
}

submit_form

interpret_pageと同じ出力形式で、送信後にサーバーが到達したページのマップを返します。

{
  "url": "https://example.com/login",
  "method": "POST",
  "fields": {
    "email": "user@example.com",
    "password": "hunter2",
    "_csrf": "abc123"
  }
}

CSRFトークンはfieldsにそのまま入ります。前回のinterpret_page呼び出しのforms配列内の非表示フィールドから取得してください。


inspect_element

CSSセレクタに一致するノードの詳細な構造データ。最大25要素。

{
  "url": "https://example.com/shop",
  "selector": ".product-card",
  "matched": 48,
  "truncated": true,
  "elements": [
    {
      "tag": "div",
      "id": "product-42",
      "classes": ["product-card", "featured"],
      "text": "Widget Pro $49.99 Add to cart",
      "attributes": { "id": "product-42" },
      "links": [{ "label": "Add to cart", "url": "https://example.com/cart/add/42" }],
      "fields": [],
      "children": [
        { "tag": "h3", "text": "Widget Pro" },
        { "tag": "span", "text": "$49.99" },
        { "tag": "a", "text": "Add to cart", "href": "https://example.com/cart/add/42" }
      ]
    }
  ]
}

セレクタの例: #login-form, .product-card, table.results tbody tr, nav a, [data-testid="price"]


site_map

{
  "root_url": "https://example.com",
  "crawled_at": "2025-01-01T12:00:00Z",
  "total_pages": 8,
  "pages": [
    {
      "url": "https://example.com",
      "title": "Home",
      "page_type": "navigation",
      "depth": 0,
      "links_to": ["https://example.com/about", "https://example.com/contact"]
    }
  ],
  "all_forms": [
    {
      "found_on": "https://example.com/contact",
      "id": "contact",
      "action": "https://example.com/contact/submit",
      "method": "POST",
      "fields": [
        { "name": "email", "type": "email", "label": "Your email", "placeholder": "", "required": true, "value": "" },
        { "name": "message", "type": "textarea", "label": "Message", "placeholder": "", "required": true, "value": "" }
      ]
    }
  ],
  "all_navigation": [
    { "label": "About", "url": "https://example.com/about" },
    { "label": "Contact", "url": "https://example.com/contact" }
  ]
}

invalidate_cache

{ "url": "https://example.com", "invalidated": true }

エラー

ツールは例外を発生させません。失敗時:

{
  "error": true,
  "code": "FETCH_FAILED | PARSE_FAILED | TIMEOUT | NOT_HTML",
  "message": "human-readable explanation",
  "url": "https://example.com/broken"
}

エージェントによる出力の利用方法

サイトのナビゲート: サイトのクローム(メニュー、ヘッダー、フッター)にはnavigationを、ページ本文のリンクにはcontent_linksを読み取ります。content_links.totalはリストが切り捨てられていても存在する数を示します。目的に一致するリンクを選択し、interpret_pageを呼び出します。

フォームの送信: formsを読み取ります。各フィールドにはname(送信するもの)、type(期待されるデータ型)、label/placeholder(用途)、required、valueがあります。非表示フィールド(type: "hidden")はCSRFトークンを保持しているため、そのvalueをそのまま渡します。フラットなname → value辞書を作成し、submit_formを呼び出します。

実行前の分類: ロジックを分岐させる必要がある場合(例:ログインページかダッシュボードか?)、フルでinterpret_pageを呼び出すコストをかけずに、まずpage_typeを呼び出します。

コンポーネントの詳細確認: マップ内にテーブルを見つけたが個別の行が必要な場合や、商品リストがあるが各カードのリンクと価格が必要な場合は、CSSセレクタを指定してinspect_elementを呼び出し、ページ全体を再読み込みせずに特定のノードの詳細構造を取得します。

マルチステップワークフローの事前計画: 開始前にsite_mapを呼び出します。全ページのタイトル、タイプ、階層、発リンク、およびサイト全体の全フォームを取得できるため、ワークフロー全体(ログインフォームの場所、データ入力ページの場所、送信エンドポイントの場所)を1往復もせずに計画できます。

page_typeはシグナルであり、保証ではない: 分類はヒューリスティックです。JavaScriptでレンダリングされるSPAで空のHTMLシェルを返すものは、多くの場合otherになります。パスワードフィールドはJavaScriptが実行されるまでHTML内に存在しないためです。page_typeは高速なフィルターとして扱い、実際のformsやheadingsで検証してください。


共有レジストリの同期

デフォルトでは、OSSサーバーが構築するすべての新しいページマップは、api.getwebspeed.ioのWeb Speed共有レジストリに非同期で提供されます。これはクラウドソーシングによるフライホイールです。URLを取得したすべてのエージェントがそれをグローバルキャッシュに追加するため、他の場所の次のエージェントは即座に応答を得られます。

これはオプトアウト方式です。 貢献者が増えるほど全員のエージェントが高速に動作するため、デフォルトでオンになっています。

共有されるもの

構造的なページデータのみ:

  • ページタイプ、タイトル、説明

  • 見出し、ナビゲーションリンク、コンテンツリンク

  • フォームフィールド名、型、ラベル(値は含まれません)

  • テーブル、テキストブロック

  • Open Graphメタデータ

共有されないもの: クッキー、セッション・トークン、フォームの値、JSレンダリングされたマップ(セッション固有のログイン状態が含まれる可能性があるため)。

同期の無効化

サーバー起動前に環境変数を設定します:

WEB_SPEED_REGISTRY_SYNC=false python server.py

またはMCPクライアント設定で:

{
  "mcpServers": {
    "web-speed": {
      "command": "/path/to/venv/bin/python",
      "args": ["/path/to/server.py"],
      "env": {
        "WEB_SPEED_REGISTRY_SYNC": "false"
      }
    }
  }
}

セルフホスト型レジストリへの指定

独自のホストインスタンスを実行している場合は、同期先をそこに指定します:

WEB_SPEED_REGISTRY_URL=https://your-instance.example.com python server.py

同期の動作

  • ファイア・アンド・フォーゲット:貢献はバックグラウンドで送信されます。エージェントのリクエストは、pingの成功に関係なくフルスピードで完了します。

  • キャッシュミス時のみ:ローカルの24時間ディスクキャッシュに既にあるマップは再送信されません。

  • 失敗はサイレント:ネットワークエラー、タイムアウト、サーバー拒否はDEBUGレベルでのみログ記録され、エージェントには表示されません。


アーキテクチャ

URL  ──▶  fetcher.py         (httpx: 10s timeout, 5 redirects, Chrome UA
                ▼             OR Playwright headless Chromium for js=true)
          cleaner.py         (BeautifulSoup/lxml: strip noise, split nav vs content
                ▼             links, filter layout tables, deduplicate text blocks,
          structured map      infer page_type, detect auth_gated)
                ▼
          cache.py           (24h TTL, MD5 keyed JSON files in ./cache/)
                ▼
          registry_sync.py   (fire-and-forget POST to api.getwebspeed.io/v1/contribute)
                ▼
          server.py          (FastMCP: 8 tools over stdio)

AIなし。解釈なし。エージェントが脳です。


既知の制限

  • JSレンダリングされるSPA: JavaScript(React、Vue、Angular)を介してコンテンツを読み込むページは、プリレンダリングされたHTMLシェルのみを返します。JSによって注入されるパスワードフィールド、検索結果、ナビゲーションは欠落します。表示されているものに対してinspect_elementを使用し、SPAを多用するターゲットにはブラウザ自動化ツールと組み合わせてください。

  • page_typeのヒューリスティクス: 分類は構造的で高速ですが、完璧ではありません。内部リンクが多いマーケティングページはlistingと判定される可能性があり、メールフィールドはあるがパスワードフィールドがないページはloginとは判定されません。

  • キャッシュはローカルディスク: ./cache/ディレクトリはローカルです。マルチプロセスや分散デプロイメントでは、キャッシュエントリはインスタンス間で共有されません。共有キャッシュが必要な場合は、cache.pyをRedisやMemcachedバックエンドに置き換えてください。

  • レート制限は未実装: Web Speedはアウトバウンドリクエストをスロットリングしません。大量のエージェント群を運用する場合は、サーバーの前にレート制限プロキシ(Cloudflare、nginxなど)を配置してください。

Related MCP Connectors

Related MCP Servers