はじめに
Claude CodeからSnowflake公式ドキュメントを検索・取得できるMCPサーバーを作成した。APIキーや認証は不要で、docs.snowflake.comの内部構造を利用して動作する。
本記事では、このMCPサーバーの技術的な仕組みについて記載する。
MCPサーバーの概要
提供するツールは2つ。
| ツール名 | 機能 |
|---|---|
search_snowflake_docs |
キーワードでドキュメントを全文検索 |
get_doc_content |
指定URLのページ本文をMarkdown形式で取得 |
Claude Codeへの登録は以下のコマンドで行う。
claude mcp add snowflake-docs -- uv run --directory /path/to/snowflake-docs-mcp-server python server.py
Next.jsの内部データ取得の仕組みを利用
このMCPサーバーの実装の肝は、docs.snowflake.comがNext.jsで構築されている点にある。Next.jsにはgetServerSidePropsで取得したデータをJSONとして返す内部エンドポイントが存在し、これを利用している。
処理フロー
1. docs.snowflake.com のHTMLを取得
2. HTMLから Next.js の buildId を正規表現で抽出
3. _next/data/{buildId}/{lang}/search.json?q=... にリクエスト
4. 返却されたJSONから検索結果を抽出
buildIdの取得
Next.jsアプリケーションはビルド時に一意のbuildIdを生成する。この値はページのHTMLソース内に埋め込まれている。
async def _get_build_id() -> str:
async with httpx.AsyncClient(timeout=10.0, follow_redirects=True) as client:
resp = await client.get("https://docs.snowflake.com")
resp.raise_for_status()
m = re.search(r'"buildId":"([^"]+)"', resp.text)
if not m:
raise ValueError("Could not extract buildId from docs.snowflake.com")
return m.group(1)
取得されるbuildIdは例えば 4Vr4FcaZt5TNi0rcHXIGK のようなランダムな文字列である。サイトが再デプロイされるたびにこの値は変わる。
検索APIへのリクエスト
buildIdを使って以下のURLにリクエストを送る。
https://docs.snowflake.com/_next/data/{buildId}/en/search.json?q=Dynamic+Tables
レスポンスはJSON形式で、pageProps.searchResults.resultsに検索結果の配列が含まれる。各結果にはtitle、href、description、categoryなどのフィールドがある。
ドキュメント本文の取得
get_doc_contentツールでは、指定URLのHTMLページをBeautifulSoupでパースし、本文をMarkdown風に整形して返す。
処理の流れは以下の通り。
- HTMLを取得し、
<article>またはrole="main"の要素を抽出 nav、footer、aside、sidebar、toc等の不要な要素を除去- 見出し、段落、コードブロック、リスト、テーブルをMarkdown形式に変換
max_lengthを超えた場合は切り捨て
セキュリティのため、docs.snowflake.com以外のURLは拒否する設計としている。
制限事項
- docs.snowflake.comの構造変更(Next.jsのbuildId体系の変更等)により動作しなくなる可能性がある
- 公式APIではなく、サイト内部の非公開の挙動に依存しているため、予告なく動作しなくなるリスクがある
- 日本語ドキュメント(
language: "ja")の検索精度は英語に比べて低い場合がある
まとめ
Snowflakeドキュメント検索MCPサーバーは、docs.snowflake.comのNext.js内部エンドポイントを利用して検索・取得を行っている。公式APIに依存しないためAPIキーが不要だが、サイト構造の変更には弱いというトレードオフがある。