Skip to content

feat: .nitpicker アーカイブ照会 MCP サーバーの新規実装 #21

@YusukeHirao

Description

@YusukeHirao

背景

現状、.nitpicker ファイルの中身を確認するには analyzereport で Google Sheets に出力する必要がある。しかし、現場では「404 のページ一覧がほしい」「jQuery 使ってる?」「alt 未設定の画像は何件?」といった軽い確認を頻繁に行いたい。

MCP サーバーを作り、Claude Desktop 等の AI アシスタント経由で .nitpicker ファイルの中身を自然言語で問い合わせられるようにする。

利用者像

  • ウェブディレクター: 納品前チェック、定期監査、SEO/メタデータの品質確認、リンク切れ検出、クライアント報告用データ
  • フロントエンドエンジニア: 技術スタック調査(jQuery/React検出)、パフォーマンス(圧縮・CDN・画像最適化)、マークアップ品質、アクセシビリティ
  • クライアント企業のウェブ担当者: サイトの健全性確認、リンク切れ、OGP設定漏れ、上司への報告用サマリ

ニーズ一覧

A. サイト全体の概況

  • 総ページ数(内部/外部内訳)、ステータスコード分布(200/301/404/500別)
  • メタデータ設定率(title/description/canonical/OGP/twitter:card/lang の充足率)
  • リソース総数、リダイレクトページ数、スキップページ数
  • クロール設定の確認

B. ページ一覧(リッチフィルタ)

  • ステータスコード指定(404一覧、5xx一覧など)
  • メタデータ欠損フィルタ(title未設定、description未設定、canonical未設定、og:image未設定、twitter:card未設定、lang未設定)
  • SEO フラグ(noindex/nofollow付きページ)
  • URL/タイトルのパターン検索
  • ディレクトリ指定(/blog/ 配下のページ数)
  • ソート(URL順、ステータス順、サイズ順)
  • ページネーション

C. ページ詳細

  • URL指定でメタデータ全項目表示
  • 発リンク(anchors)一覧
  • 被リンク(referrers)一覧
  • リダイレクト元の一覧

D. HTML スナップショット

  • URL指定で保存済みHTMLを取得(サイズ制限付き)

E. リンク分析

  • 内部リンク切れ一覧(リンク元ページ+リンクテキスト付き)
  • 外部リンク切れ一覧
  • 孤立ページ(被リンク0の内部ページ)
  • 被リンク数上位ページ
  • 外部サイトへの発リンク一覧

F. リソース分析

  • content-type別の件数・合計サイズ集計
  • 外部リソースのドメイン別集計
  • URLパターン検索(jQuery/GTM/Analytics等のライブラリ検出)
  • 圧縮なしの大きいリソース
  • 壊れたリソース(4xx/5xx)
  • CDN配信状況

G. 画像分析

  • alt属性未設定の画像一覧
  • 表示サイズと実サイズの乖離が大きい画像(過大画像)
  • width/height未指定の画像(CLS原因)
  • lazy loading未設定の大きい画像
  • 特定ページの画像一覧

H. 分析結果(analyze実行済みの場合)

  • validator別/severity別/rule別の件数集計
  • 違反が多いページ上位
  • 特定ルール・特定validatorでフィルタした violation 一覧
  • analyze未実行時は「データなし」を返す

I. セキュリティ・ポリシー(レスポンスヘッダー経由)

  • CSP/X-Frame-Options未設定ページ
  • 外部JSスクリプト一覧(サードパーティ棚卸し)

パッケージ構成

@nitpicker/query — アーカイブ照会API

役割: .nitpicker ファイルのライフサイクル管理(open/close)と、上記ニーズ A〜I に対応するクエリ関数群を提供する。MCP サーバーに限らず、プログラムから直接使える汎用 API。

主な責務:

  • アーカイブのオープン/クローズ/自動クリーンアップ(シングルアーカイブモデル)
  • ニーズ A〜I それぞれに対応するクエリ関数(1関数1ファイル)
  • SQLite への直接クエリによる高速なフィルタリング・集計(後述)

依存: @nitpicker/crawler

パフォーマンス設計(重要)

Nitpicker は closed beta で 10,000ページ・500,000レコード・10MB超 のアーカイブを扱ってきた。この規模では「全件取得→アプリ層でフィルタ」は使えない。

方針: ArchiveAccessor に getKnex() メソッドを1つだけ追加し、@nitpicker/query はこの Knex インスタンスを使って SQL レベルでフィルタリング・集計・ページネーションを行う

  • ArchiveAccessor の既存 API で十分な操作(getHtmlOfPage 等)はそちらを使う
  • 複雑なクエリ(JOIN/GROUP BY/HAVING/WHERE の組み合わせ)は Knex インスタンス経由で直接 SQL 実行
  • DB は WAL モードのため、既存の読み書き処理と競合しない

SQL レベルのクエリが必要なケース:

  • ステータスコード別集計(GROUP BY status
  • メタデータ欠損フィルタ(WHERE title IS NULL + ページネーション)
  • title/description 重複検出(GROUP BY title HAVING COUNT(*) > 1
  • canonical ≠ url の不一致検出(WHERE canonical != url
  • リソースのドメイン別集計
  • 画像テーブルの各種フィルタ(500,000レコードの全件取得は不可)
  • レスポンスヘッダー JSON 内の特定キー検索

@nitpicker/mcp-server — MCP サーバー

役割: @nitpicker/query を MCP ツールとして公開する。AI アシスタントが自然言語の質問をツール呼び出しに変換して利用する。

主な責務:

  • @modelcontextprotocol/sdk で MCP サーバーを構築(stdio トランスポート)
  • @nitpicker/query のクエリ関数を MCP ツールとしてラップ
  • 各ツールの description に LLM 向けのガイダンス(「こういう質問にはこのパラメータ」)を記載
  • bin エントリポイント(nitpicker-mcp)を提供

依存: @nitpicker/query, @modelcontextprotocol/sdk, zod

MCP ツール一覧(14個)

# ツール名 対応ニーズ 概要
1 open_archive .nitpicker ファイルを開く
2 close_archive アーカイブを閉じる
3 get_summary A サイト概要(ページ数、ステータス分布、メタデータ充足率)
4 list_pages B ページ一覧(ステータス/メタデータ欠損/noindex/URL/ディレクトリ等のリッチフィルタ)
5 get_page_detail C URL指定のページ詳細(メタデータ+発リンク+被リンク)
6 get_page_html D URL指定のHTMLスナップショット
7 list_links E リンク分析(リンク切れ/孤立ページ/外部リンク/被リンク上位)
8 list_resources F, I リソース一覧・集計(ライブラリ検出、圧縮・CDN確認、ドメイン別集計)
9 list_images G 画像一覧(alt欠損/過大/width未指定/lazy未設定)
10 get_violations H 分析結果(validator/severity/rule別フィルタ・集計)
11 find_duplicates B 重複検出(同じ title / description を持つページの組を返す)
12 find_mismatches B, I フィールド間の不一致検出(canonical≠URL、og:title≠title、og:description≠description 等)
13 get_resource_referrers F 特定リソースの参照元ページ一覧(「jQuery を使っているのはどのページ?」「GTMが全ページに入っているか?」)
14 check_headers I レスポンスヘッダーの検査(CSP/X-Frame-Options等の特定ヘッダーが未設定のページ一覧)

ツール追加の理由

  • find_duplicates: 重複検出は全ページをグルーピングする操作であり、list_pages の単純フィルタでは対応できない。LLM が全ページを取得して自力で比較するにはデータ量が多すぎる。
  • find_mismatches: canonical と URL の比較、og:title と title の比較など、2つのフィールド間の不一致を検出するクエリは list_pages では表現できない。SEO チェック・OGP チェックの頻出パターン。
  • get_resource_referrers: 「このライブラリはどのページで使われている?」「Analytics タグは全ページに入っているか?」という問いには、リソース→ページの逆引きが必要。list_resources はリソース一覧を返すだけで参照元ページ情報を含まない。
  • check_headers: レスポンスヘッダーは JSON カラムに格納されており、特定ヘッダーの有無を判定するには JSON パースが必要。list_pages のフィルタとして実装するには責務が広くなりすぎる。

crawler パッケージへの変更

最小限の変更のみ:

  1. ArchiveAccessor に getKnex() メソッドを追加: 内部の Database が保持する Knex インスタンスを返す。@nitpicker/query がこれを使って独自クエリを実行する。
  2. DB_Image 型定義を types.ts に追加: images テーブルの DB スキーマは database.ts に定義されているが、対応する型が未定義。
  3. ArchiveAccessor の既存メソッド群には変更なし(肥大化を回避)。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions