mcpField
G
Gopher & Gemini MCP Server

Gopher & Gemini MCP Server

ローカル
github.com/cameronryeブラウザ自動化167 週間前に更新

Gopher & Gemini MCP Server について

Gopher & Gemini MCP Serverは、AIアシスタントがGopherとGeminiのリソースを読み取り専用で閲覧できるようにします。構造化出力に加え、SSRF対策、TLS、TOFU証明書チェック、読み取り量の制限を備えています。

主な機能

  1. 01GopherとGeminiの取得
  2. 02リソースの一括取得
  3. 03Gemtextの解析
  4. 04TOFU証明書の管理
  5. 05SSRF対策とホスト制限
  6. 06構造化JSON出力

活用シーン

  1. 01Gopherのメニューやテキストの閲覧
  2. 02Geminiカプセルやgemlogの読解
  3. 03Geminiの信頼ピンの確認と更新
  4. 04並列数を制限した複数リソースの取得

MCP 対応

GopherとGeminiの取得・一括取得ツールに加え、Geminiの信頼情報とクライアント証明書を管理するツールを提供します。ポリシーリソースと2つのナビゲーション用プロンプトも公開します。

設定

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

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

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

パッケージ
pypigopher-mcp0.10.2
ocighcr.io/cameronrye/gopher-mcp:0.10.2

ツール

8

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

パラメーター型必須説明
urlstringはい完全な gopher:// URL。パスの先頭文字はアイテムタイプです(1=メニュー、0=テキストファイル、7=検索)。メニュー項目の `next_url` に従って移動します。タイプ 7 の検索サーバーに問い合わせるには、検索セレクター自体の URL(gopher://gopher.floodgap.com/7/v2/vs)を指定し、検索語はパスセグメントに追加せず `search` に渡します。URL にクエリ文字列を記述しても(gopher://gopher.floodgap.com/7/v2/vs?python)動作しますが、`search` なら #、+、非 ASCII 文字を正しく処理できます。例:gopher://gopher.floodgap.com/1/
offsetintegerいいえ結果が切り詰められたリソースの読み取り開始位置です。前の結果の `next_offset` を渡します。これは Gopher メニューではメニュー項目数、ページ本文では文字数を数え、次のウィンドウを取得します。先頭から読む場合は 0(デフォルト)のままにします。`truncated: true` と `next_offset` を含む結果は続きがあることを示します。部分ページを全体として提示せず、その位置から読み進め、`next_offset` が null になったら停止してください。通常、続きの取得コストは低く、リソースは一度だけダウンロードされ、後続のウィンドウはそこから表示されます。ただし例外が 2 つあります。キャッシュが無効の場合、各ウィンドウで再ダウンロードされます。また、クエリ付きの GEMINI URL は常に再ダウンロードされます。status-10/11 プロンプトへの回答は URL 経由で送られ、保持されないためです。必要な内容がある場合に読み進め、機械的には続けないでください。
searchanyいいえタイプ 7(インデックス検索)セレクターの検索語です。例:Veronica-2。検索語はパーセントエンコードされ、クエリ文字列として送信されるため、ユーザーの語句をそのまま渡してください。#、+、&、非 ASCII 文字を含むクエリを URL に手動で記述すると、切り詰められたり文字化けしたりし、サーバーは要求されていない検索に応答します。`url` にすでにクエリがある場合は置き換えます。タイプ 7 以外のすべてのアイテムタイプでは設定しないでください。RFC 1436 でクエリフィールドが定義されているのはタイプ 7 のみのため、他のタイプに送信した検索語は破棄されます。
refreshbooleanいいえこの URL のキャッシュ済みコピーを使わず、サーバーから再取得します。ユーザーが現在の状態を知りたい場合(「もう一度確認して」「投稿された?」「古いようだ」など)、または以前の結果が `cached: true` で返され、`cache_age_seconds` が大きすぎて誠実に回答できない場合は true に設定してください。`offset` で取得したウィンドウも `cached: true` を返します。最初のウィンドウを取得した際のダウンロードから描画され、再取得されたものではないためです。`cache_age_seconds` は他のキャッシュ結果と同様に、そのバイト列の経過時間を示します。判断は経過時間だけに基づいてください。巡回の途中で更新すると、要求されたウィンドウだけでなく、そのリソースのすべてのウィンドウが破棄されます。そのため、既読のウィンドウは次に読む際に再ダウンロードされます。まず巡回を完了し、その後に再読込することをおすすめします。通常の閲覧やリンクの追跡では false のままにしてください。Gopher と Gemini は小規模な愛好家のホストで提供されていることが多く、キャッシュによって重複リクエストの負荷を抑えられます。どちらの場合も、返されたレスポンスは後続の読み取り用に保存されます。

詳細

Gopher & Gemini MCP サーバー

CI Documentation PyPI version Python 3.11+ License: MIT Code style: ruff Checked with mypy Downloads

AI アシスタントが Gopher プロトコルと Gemini プロトコルの両方のリソースを安全かつ効率的に閲覧し、操作できるようにする、最新のクロスプラットフォーム対応 Model Context Protocol (MCP) サーバーです。

概要

Gopher & Gemini MCP サーバーは、古典的なインターネットプロトコルと現代の代替プロトコルを AI アシスタントと結び付け、Claude のような LLM が、Gopherspace と Geminispace の両方で栄える独自のコンテンツやコミュニティを探索できるようにします。FastMCP と現代的な Python の手法を用いて構築されており、こうした特徴的なインターネットプロトコルへの、安全で効率的なゲートウェイを提供します。

主な利点:

  • 代替インターネットコンテンツを発見 - Gopher と Gemini の両プロトコル上にある独自のリソースにアクセスできます
  • 安全な探索 - 組み込みのセキュリティ対策、TLS 暗号化、コンテンツフィルタリングを備えています
  • 最新の実装 - FastMCP フレームワークと async/await パターンを使用しています
  • 開発者に使いやすい - 包括的なテスト、型ヒント、ドキュメントを備えています
  • 高度なセキュリティ - Gemini 用の TOFU 証明書検証とクライアント証明書に対応しています

機能

  • 2 つのプロトコルに対応:包括的なプロトコル対応を実現する gopher_fetch および gemini_fetch ツール
  • 包括的な Gopher 対応:標準の RFC 1436 項目タイプすべてに対応します。メニュー(1)とインデックス検索サーバー(7)は構造化メニューとして、テキスト(0)、HTML(h)、情報(i)、エラー(3)の各行はテキストとして扱います。14 種類のバイナリタイプはメタデータのみを扱い、3 種類の対話型タイプ(2、8、T)は接続を開かずに拒否します。独自のカテゴリを持たない唯一の標準タイプは +(冗長サーバー)です。これは表示対象のコンテンツではなく、直前の項目の代替ホストを示すため、以下の不明なタイプの処理経路に従います。不明なタイプはベストエフォートでテキストとして読み取ります。また、hURL URL:<target> セレクターがあれば、サーバーが実際に指定した宛先をたどります
  • Gemini の完全な実装:ネイティブな gemtext 解析、TLS セキュリティ、ステータスコード処理
  • 高度なセキュリティ:専用の検査・復旧ツールを備えた TOFU 証明書検証、スコープを限定したクライアント証明書、安全な TLS 接続
  • 安全性を最優先:組み込みのタイムアウト、サイズ制限、入力のサニタイズ、SSRF 保護、ホストごとのレート制限、ホスト許可リスト
  • LLM 向けに最適化:AI での利用を想定して設計された構造化 JSON レスポンスを返します
  • クロスプラットフォーム対応:Windows、macOS、Linux でシームレスに動作します
  • 最新の開発環境:完全な型チェック、リンティング、テスト、CI/CD パイプライン
  • 高性能:インテリジェントなキャッシュを備えた async/await パターン。キャッシュ済みの結果はその旨を示し、リクエストごとの refresh でキャッシュを迂回できます
  • 続きから読み取り可能:メニューやページが表示上限で切り詰められた場合、どこで停止したかを示すため、部分的な表示のままにならず、offset で残りを読み取れます
対応プロトコル

Gopher(RFC 1436、および一般的に使われているデファクトのアイテムタイプを含む)とGeminiです。どちらも読み取り専用です。対応範囲は以上です。隣接するプロトコルは、単に未実装なのではなく、意図的に対象外としています。

  • Titan と Misfin は書き込みプロトコルで、アップロードやメール送信に使われます。LLM が操作するツールから、誰かのカプセルへの公開や、その人になりすましたメール送信ができるべきではありません。どちらかを追加すると、ここでのあらゆる保護策(robots、レート制限、許可リスト)が、ツールの機能のごく一部しか保護しなくなります。
  • Spartan と Nex は、それぞれ独自のパーサーと障害モードを持つ別個のプロトコルで、両プロトコルが対応するホストに比べて、ごく一部のホストでしか使われていません。到達範囲がわずかに広がるだけで、セキュリティに関わる対応範囲は倍増します。
  • Gopher+ は実装されていません。通常のメニューに現れるため、アイテムタイプ : は認識されますが、Gopher+ の属性リクエストやメタデータリクエストが送信されることはありません。

これらのいずれかが必要な場合は、このサーバーにフラグを追加するより、適切に対応する MCP サーバーを利用するほうがよいでしょう。

ドキュメント

完全なドキュメントは cameronrye.github.io/gopher-mcp でご覧いただけます。

クイックスタート

前提条件
インストール
オプション 1:uvx でインストール不要(推奨)

クローンもチェックアウトも不要です。uv が公開済みパッケージを必要に応じて取得し、実行します。

uvx gopher-mcp
オプション 2:PyPI からインストール
# Install from PyPI
pip install gopher-mcp

# Or with uv
uv add gopher-mcp
オプション 3:開発用インストール
# Clone the repository
git clone https://github.com/cameronrye/gopher-mcp.git
cd gopher-mcp

# Set up development environment
./scripts/dev-setup.sh  # Unix/macOS
# or
scripts\dev-setup.bat   # Windows

# Run the server
uv run task serve
オプション 4:Docker

タグ付きリリースでは、スリムな非 root イメージが ghcr.io/cameronrye/gopher-mcp に公開されます。イメージにはリリースバージョンのタグが付与され、安定版(プレリリースではない)タグの場合は :latest も付与されます。

# The default CMD serves streamable-http on 0.0.0.0:8000
docker run --rm -p 8000:8000 \
  -v gopher-mcp-state:/home/app/.local/share/gopher-mcp \
  ghcr.io/cameronrye/gopher-mcp:latest

# Or run over stdio, e.g. for an MCP client
docker run --rm -i --no-healthcheck \
  -v gopher-mcp-state:/home/app/.local/share/gopher-mcp \
  ghcr.io/cameronrye/gopher-mcp:latest --transport stdio

変更を加えたツリーを実行する場合に備えて、リポジトリには公開イメージのビルド元となる Dockerfile が含まれています:docker build -t gopher-mcp .

ボリュームをマウントしてください。そうしないと、Gemini の信頼性は意味をなしません。 マウントしないと、TOFU ピンとクライアント証明書の秘密鍵がコンテナとともに失われるため、起動するたびに初回使用時の盲目的な信頼が再び有効になります。ピンは Gemini カプセルを認証する唯一の手段です。また、秘密鍵を復元できないため、作成した ID もすべて失われます。

必ずこの正確なパスにマウントしてください。 サーバーが書き込みを行う場所は /home/app/.local/share/gopher-mcp(tofu.json と certs/)です。イメージ内で実行ユーザーが所有し、モードが 700 に設定されてあらかじめ作成されているディレクトリはここだけです。そのため、名前付きボリュームが root 所有ではなく書き込み可能な状態で作成されます。それ以外の場所にマウントすると、空のディレクトリが永続化されます。~/.gemini は正しいパスではありません。gopher-mcp に独自のディレクトリができる前に証明書を固定したインストール向けの、読み取り専用のアップグレード経路にすぎません。また、保存ファイルがすでに存在する場合にのみ使用されますが、新しいイメージではそのようなファイルは存在しません。

ヘルスチェック。 HTTP トランスポートは GET /health を提供し、{"status": "ok", "version": "..."} だけを返します。設定、許可リスト、保存先のパスは返しません。SDK の設計上、認可を迂回するため、プローブとして使用できます。イメージの HEALTHCHECK はデフォルトの CMD に合わせて、ハードコードされたポート 8000 をポーリングします。そのため、--port とともにヘルスチェックもオーバーライドしてください。また、stdio で実行する場合は --no-healthcheck を渡してください。stdio コンテナは HTTP を提供しないため、正常に動作していても異常と報告されてしまいます。

注: デフォルトの CMD は 0.0.0.0 にバインドするため、コンテナはすぐにアクセス可能です。ループバック以外の --host を指定すると、FastMCP の DNS リバインディングに対する Host/Origin チェックも無効になります。これは、そのようなホストを指定して SDK を構築した場合の動作と同じです。そうしないと、localhost 以外から接続したすべてのクライアントに 421 Misdirected Request が返されます。デプロイ環境で応答するホスト名を --allowed-host NAME で指定すると、チェックを有効にしたままにできます(複数指定可能。ポートを付けない名前は任意のポートに一致します)。HTTP トランスポートには認証も TLS もありません。コンテナを公開する場合は、信頼できるリバースプロキシの背後に置くか、--transport stdio を使用してください。

MCP クライアントの統合

以下のすべてのクライアントは、サーバーを stdio 経由で実行します。ポートも TLS もリスニングソケットも必要ありません。エントリはどこでも同じ 3 つのフィールドで、異なるのはファイルとトップレベルキーだけです。

クライアントエントリの追加先トップレベルキー
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json (macOS), %APPDATA%\Claude\claude_desktop_config.json (Windows), ~/.config/Claude/claude_desktop_config.json (Linux)mcpServers
Claude Codeclaude mcp add --scope user gopher -- uvx gopher-mcp、または --scope project の場合はリポジトリルートの .mcp.jsonmcpServers
Cursor~/.cursor/mcp.json、または 1 つのプロジェクト用の .cursor/mcp.jsonmcpServers
VS Codeワークスペース内の .vscode/mcp.json、または MCP: Open User Configurationservers
Zedsettings.json(zed: open settings)context_servers
Windsurf~/.codeium/windsurf/mcp_config.jsonmcpServers
{
  "mcpServers": {
    "gopher": {
      "command": "uvx",
      "args": ["gopher-mcp"]
    }
  }
}

エントリはこれだけです。Configuration のすべての設定には動作するデフォルト値があるため、実際に変更したい場合にのみ "env" ブロックを追加してください。uvx ではなく pip でインストールした場合は、代わりに "command": "gopher-mcp" と "args": [] を使用してください。

インストールガイドには、mcpServers キーを使用しない 2 つを含め、各クライアント用の正確な JSON が記載されています。

GUI クライアントでサーバーの起動に失敗したと表示された場合、原因はほとんどの場合 PATH です。GUI から起動したアプリケーションはシェルの PATH を引き継がないため、uvx が見つからないことがあります。絶対パス(which uvx)を "command" として使用し、ウィンドウを再読み込みするのではなく、アプリケーションを完全に終了してから再起動してください。

代替方法:ローカルチェックアウトから実行する
{
  "mcpServers": {
    "gopher": {
      "command": "uv",
      "args": ["--directory", "/path/to/gopher-mcp", "run", "task", "serve"]
    }
  }
}

Windows では、バックスラッシュをエスケープした絶対パス(C:\\path\\to\\gopher-mcp)を使用してください。

## 使用方法

サーバーは、8 つの MCP ツールを登録します。

ツール目的
gopher_fetchGopher リソースを 1 つ取得します
gemini_fetchGemini リソースを 1 つ取得します
gopher_batch_fetch複数の Gopher URL を一度に取得します(同時実行数を制限、最大 50 件)
gemini_batch_fetch複数の Gemini URL を一度に取得します(同時実行数を制限、最大 50 件)
gemini_trust_listGemini TOFU 信頼ストアを確認します(読み取り専用)
gemini_trust_updateホスト 1 台分の証明書を削除または再登録します(破壊的操作)
gemini_client_cert_list保存済みの Gemini クライアント ID を確認します(読み取り専用)
gemini_client_cert_updateクライアント ID を 1 つ作成または削除します(破壊的操作)

4 つの取得ツールには、読み取り専用およびオープンワールドの注釈が付いています。4 つの証明書ツールはネットワークに接続せず、各ペアは読み取り用と書き込み用に分かれているため、クライアントは書き込み用ツールの実行を独自に制御できます。

これらに加えて、サーバーは 1 つのリソース gopher-mcp://policy を公開します。このリソースには、このプロセスが実際に使用している取得ポリシーが表示されます。拒否の判断に用いられる許可リスト、上限、robots 設定が含まれ、2 つのストアパスは <configured>/<default> に置き換えられます。ポリシーを編集するツールは意図的に用意していません。取得したページに説得されて許可リストを広げると、その後のすべての取得に影響するためです。2 つのプロンプト、カプセルまたは Gopher ホールを探索する と ジェムログまたはフログを要約する は、ナビゲーションと安全性のルールをまとめた、ワンクリックで始められる手段です。

gopher_fetch ツール

URL を指定して Gopher メニュー、テキストファイル、またはメタデータを取得します。包括的なエラー処理とセキュリティ対策を備えています。

パラメーター:

  • url(文字列、必須):Gopher の完全な URL(例:gopher://gopher.floodgap.com/1/)
  • search(文字列、任意):タイプ 7(インデックス検索)セレクターに指定する語句です。パーセントエンコードされてクエリ文字列として送信されるため、ユーザーの入力をそのまま渡してください。#、+、&、または非 ASCII 文字を含むクエリを URL に直接記述すると、末尾が切り捨てられたり、内容が壊れたりします。RFC 1436 でクエリフィールドが定義されているのはタイプ 7 のみです。ほかのすべてのアイテムタイプでは設定しないでください
  • refresh(ブール値、任意、デフォルト false):キャッシュ済みのコピーを使わず、サーバーから再取得します
  • offset(整数、任意、デフォルト 0):切り詰められた結果の続きを取得します。前回の結果の next_offset を渡してください。メニューの場合、この値はメニュー項目数を表します

レスポンスの種類:

  • MenuResult(kind: "menu"):Gopher メニュー(タイプ 1)および検索結果(タイプ 7)用
    • タイプ、タイトル、セレクター、ホスト、ポートを含む構造化されたメニュー項目です。各項目には、たどるための next_url があります。next_url が空の場合、その行は表示専用の情報行です
  • TextResult(kind: "text"):テキストファイル(タイプ 0)用
    • メタデータとともにテキストコンテンツを返します
  • BinaryResult(kind: "binary"):バイナリーアイテムタイプ(4、5、6、9、g、I、d、s、;、p、P、:、M、<)のメタデータのみを返します
    • バイナリーコンテンツをダウンロードせずに、bytes と mime_type を提供します
  • ErrorResult(kind: "error"):エラーおよび取得できないコンテンツ用
    • error.code と error.message を返します。取得は行われていません。対話型タイプ(2 CSO、8 telnet、T tn3270)には取得可能な本文がなく、接続を開くことなく NOT_FETCHABLE を返します
gemini_fetch ツール

完全な TLS セキュリティ、TOFU 証明書検証、ネイティブの gemtext 解析を使用して Gemini コンテンツを取得します。

パラメーター:

  • url(文字列、必須):Gemini URL 全体(例:gemini://geminiprotocol.net/)
  • input(文字列、任意):Gemini の入力プロンプト(ステータス 10/11)への回答テキストです。クエリ文字列にパーセントエンコードされます
  • refresh(ブール値、任意、デフォルトは false):キャッシュされたコピーを使わず、サーバーから再取得します
  • offset(整数、任意、デフォルトは 0):切り詰められた結果の続きを取得します。ページ本文では文字数を数える 前の結果の next_offset を渡します

レスポンスの種類: kind ごとに 1 つ、計 7 種類です。

  • GeminiGemtextResult(kind: "gemtext"):gemtext コンテンツ(text/gemini)用です
    • document.lines と document.links に解析済みドキュメントが含まれます。url フィールドはすでに解決済みです。各行は独自の type、content、およびマーカーだけでは表せない情報(text、level、alt_text、language)を持ちます。行ごとのネストされたオブジェクトはなく、ペイロードにドキュメント全体の raw_content もありません
  • GeminiSuccessResult(kind: "success"):その他のテキストコンテンツタイプ用です
    • MIME タイプ情報とともに、デコード済みテキストが content に含まれます
  • GeminiBinaryResult(kind: "binary"):バイナリーコンテンツ用です
    • メタデータのみで、size と検出された mime_type が含まれます。バイト列は含まれません。1 MB の本文を base64 にすると約 350k トークンになりますが、モデルではいずれにせよ表示できません
  • GeminiInputResult(kind: "input"):入力リクエスト(ステータス 1x)用です
    • カプセルの prompt が含まれ、ステータス 11 では sensitive: true が付きます。手動でクエリ文字列を組み立てず、input= を指定して再度呼び出すことで回答してください
  • GeminiRedirectResult(kind: "redirect"):リダイレクト(ステータス 3x。31 は恒久的)用です
    • new_url が遷移先です。リダイレクトは自動で 追跡されないため、結果には cross_host(遷移先がリクエスト先とは異なる当事者に属するかどうか)と scheme(gemini 以外の場合は Geminispace の外に出るため、このツールでは取得できません)も含まれます。連続して追跡するのは最大 5 回までとし、すでに確認した URL に到達したら停止してください
  • GeminiErrorResult(kind: "error"):エラー(ステータス 40-59)および通信経路のこちら側で発生した失敗(SSRF や許可リストによる拒否、robots によるブロック、証明書の不一致、タイムアウト)用です
    • error.code と error.message が含まれます。message はこのサーバーが記述したものです。カプセル独自の信頼できない META テキストは error.meta に分けて保持されるため、悪意のある 51 <instruction> がこのサーバーの案内として読まれることはありません。ステータスに対処法が定義されている場合(4x の一時的なステータス全体を含む)、その対処法は error.next_step に含まれます
  • GeminiCertificateResult(kind: "certificate"):証明書ステータス(60-69)用です
    • 証明書要件の情報と、このサーバーが記述した next_step が含まれます(message はカプセル独自のテキストです)。ホスト/ポート/パスのスコープに対する証明書がすでに存在する場合は自動的に添付され、取得処理で証明書が新たに作成されることはありません。そのため、変更せずに再試行すると、再びステータス 60 が返されます。gemini_client_cert_update はそのスコープ用の証明書を発行しますが、そのカプセルに対して永続的な ID を保持することにユーザーが同意した後に限られます

GeminiErrorResult は、gopher_fetch が返すものと同じ ErrorResult モデルの別名であり、別個の型ではありません。カプセルが実際に応答した場合、その error オブジェクトに追加の status、temporary、meta キーが含まれるだけです。

Gemini の結果ではコンテンツの長さを size と表記しますが、Gopher の結果では同じ値を bytes と表記します。概念は 1 つですが、通信上の名前は 2 つあり、どちらかを変更すると既存のすべての利用側に影響するため、分けたままにしています。

キャッシュされた結果とrefresh

成功した本文は、プロトコルごとに数分間キャッシュされます。キャッシュから取得した結果にはその旨が示されるため、再生された結果をリソースの現在の状態と取り違えることはありません。

  • cached — 結果がローカルキャッシュから再生された場合はtrue
  • cached_at — そのコピーを実際に取得した日時(ISO-8601 UTC タイムスタンプ、2026-09-02T12:00:00+00:00)
  • cache_age_seconds — 返された時点でのコピーの経過秒数

これらは、実際にキャッシュされる種類(Gopherのmenu、text、binary、Geminiのgemtext、success、binary)にのみ表示されます。エラー、リダイレクト、入力プロンプト、証明書プロンプトはキャッシュされません。

ユーザーが現在の状態を求めている場合は、refresh: trueを渡してください。その呼び出しに限りキャッシュをスキップし、新しいレスポンスは引き続き保存されます。バッチ用の2つのツールを含め、4つすべてのフェッチツールで指定できます。

切り詰められた結果とoffset

メニューとページ本文は、モデルに渡される前に上限が適用されます(*_MAX_RENDERED_CHARS、GOPHER_MAX_MENU_ITEMS)。ただし、上限に達してもそこで終わりではありません。結果が切り詰められると、truncated: trueが設定され、続きの取得位置が示されます。

  • next_offset — 切り詰められた部分が始まる位置。続きがない場合はnull
  • total_items(Gopherのmenu)/total_chars(text、success、gemtext)— リソース全体のサイズ。レンダリング上限を超えるディレクトリの場合、そのケースでは総数を数えないため、total_itemsはnull

前回のnext_offsetをoffsetに設定して同じツールを再度呼び出し、next_offsetがnullになるまで続けてください。単位はメニューではアイテム数、本文では文字数です。bytesとsizeはバイト数であり、オフセットには使われません。gemtextではウィンドウの末尾が最後の完全な行になるため、連続するウィンドウは正確につながり、リンクの途中で切れた部分が完全なリンクとして解析されることはありません。

どちらのバッチツールもoffsetを受け取りません。異なるURLのリスト全体に対して、1つのオフセットを意味のある形で指定できないためです。切り詰められたバッチ項目の続きは、next_offsetに対応している単一URL用ツールで取得してください。

Gemini 信頼ストアツール

Gemini には認証局がありません。ホストで最初に確認された証明書がピン留めされ、以降のすべての接続で同じ証明書を提示する必要があります。ホストが証明書を再発行すると(自己署名証明書では一般的で、通常は有効期限切れの際に発生します)、取得に失敗し、CERTIFICATE_CHANGED が返されます。ディスク上の信頼ストアを手作業で編集しなくても済むよう、次の 2 つのツールが用意されています。信頼ストアは $XDG_DATA_HOME/gopher-mcp/tofu.json にあり、これが設定されていない場合は ~/.local/share/gopher-mcp/、macOS では ~/Library/Application Support/gopher-mcp/、Windows では %LOCALAPPDATA%\gopher-mcp\ が使われます。また、GEMINI_TOFU_STORAGE_PATH で保存先を変更できます。すでに ~/.gemini/tofu.json があるインストール環境では、今後もその場所をそのまま使い続けます。ピンを移動すると失われるか、さらに悪い場合には、ピン留め済みのホストが未ピン留めに見えてしまうためです。詳細なルールについては、Gemini の状態の保存場所をご覧ください。

  • gemini_trust_list(読み取り専用)は、ピン留めされている内容を報告します。任意で 1 つの host を指定できます。フィンガープリント、ポート、初回および最終確認日時、有効期限(ISO-8601 UTC 形式)に加え、事前に計算された expired も報告します。有効期間が終了している場合、フィンガープリント変更の原因として通常の再発行が考えられます。
  • gemini_trust_update(破壊的操作)は、指定した host のピンを削除(action: "remove")または置き換え(action: "pin")します。ワイルドカードは使えません。

フィンガープリントの変更は、アクティブな中間者攻撃が発生している場合にもまったく同じように見え、クライアント側では両者を区別できません。そのため、ユーザーが新しい証明書が想定どおりであることを確認した後に限り、ピンを変更します。確認は運用担当者または別のデバイスと照合して行い、取得したページの主張を根拠にすることはありません。これを徹底するため、action: "remove" には現在ピン留めされているフィンガープリント(gemini_trust_list が報告する値)が必要です。一致しない場合は FINGERPRINT_MISMATCH が返され、何も変更されません。MCP エリシテーションに対応しているクライアントでは、変更を実行する前にユーザーに確認します。拒否した場合は、ピンをそのままにして USER_DECLINED が返されます。この機能に対応していないクライアントには確認を求めず、これまでどおり動作します。

Gemini クライアント ID ツール

クライアント証明書は、Gemini の証明書に関するもう一方の要素で、方向も逆です。これはカプセルがこちらに提示する証明書ではなく、このサーバーがカプセルに提示する ID です。アカウント機能のあるカプセルは、ステータス 60(証明書が必要) で証明書を要求します。取得処理では、要求されたスコープをすでにカバーする証明書があれば添付しますが、新たに作成することはありません。そのため、ステータス 60 への対応には明示的な呼び出しが必要です。

  • gemini_client_cert_list(読み取り専用)は、ID が設定されているスコープを報告します。それぞれ、すぐに使えるスコープ URL、フィンガープリント、有効期間、期限切れかどうかを示します。秘密鍵やその保存場所は一切報告しません。
  • gemini_client_cert_update(破壊的操作)は、指定した gemini:// URL のスコープに ID を作成するか、その URL をカバーする ID を削除します。信頼ストアツールと同様に、エリシテーションに対応しているクライアントでは最初に確認を求めます。ID の作成は訪問をまたいで紐付け可能になることについての判断であり、削除すると再作成できない秘密鍵が失われるためです。

証明書は、その URL のパスと、それより下位のすべてのパスをカバーします。そのため、gemini://host/app/page.gmi は 1 つのページを、gemini://host/app/ はセクションを、gemini://host/ はカプセル全体をカバーします。証明書が存在する間、そのスコープ内のすべてのリクエストに証明書が添付されます。これによってカプセルは訪問を紐付けられるため、判断するのはユーザーであり、証明書を要求するページや META 文字列への反応として決めるものではありません。秘密鍵は復元できないため、スコープをすでにカバーしている証明書がある場合、新たな証明書の作成は拒否されます。また、action: "remove" には削除するフィンガープリントが必要です。これは信頼ツールでピン留め済みのフィンガープリントを指定する場合と同じです。

試してみる URL の例
Gopher プロトコル
# Classic Gopher menu
gopher://gopher.floodgap.com/1/

# Gopher news and information
gopher://gopher.floodgap.com/1/gopher

# Search example (type 7)
gopher://gopher.floodgap.com/7/v2/vs

# Text file example
gopher://gopher.floodgap.com/0/gopher/welcome
Gemini プロトコル
# Gemini protocol homepage
gemini://geminiprotocol.net/

# Gemini software directory
gemini://geminiprotocol.net/software/

# Example personal capsule
gemini://skyjake.fi/

# A large, browsable aggregator capsule
gemini://kennedy.gemi.dev/

Geminispace には、このツールで操作できる検索エンジンがありません。Kennedy と tlgs.one は閲覧する価値がありますが、どちらも robots.txt で Disallow: /search を指定しているため、どちらの検索 URL も BLOCKED_BY_ROBOTS になります。これは変更すべき設定ではなく、停止を意味します。運営者は、自動クライアントに対してこれらのパスを利用しないよう求めています。

AI との対話例

設定が完了したら、Claude に次のように依頼できます。

Gopher の探索:

  • "gopher.floodgap.com のメイン Gopher メニューを閲覧して"
  • "Veronica-2 検索サーバーで 'python' を検索して"
  • "Floodgap の Gopher サーバーのウェルカムテキストを表示して"
  • "Gopher コミュニティのディレクトリには何がありますか?"

Gemini の探索:

  • "Gemini プロトコルのホームページを取得して"
  • "geminiprotocol.net のソフトウェアディレクトリを表示して"
  • "gemlog の最新投稿を閲覧して"
  • "Gopher プロトコルと Gemini プロトコルの違いは何ですか?"

開発

タスクランナー

すべての開発コマンドは [tool.taskipy.tasks](pyproject.toml)内のタスクとして定義されており、各コマンドの定義はここに一元化されています。同期が必要な別の一覧はありません。すべてのプラットフォームで同じ方法で実行できます。

uv run task dev-setup     # install dependencies and pre-commit hooks
uv run task quality       # lint + typecheck + test
uv run task ci            # what CI runs: check + test-cov
uv run task help          # list every task (alias for `task --list`)

Unix と macOS では、make <command> が同じタスク一覧を呼び出す薄いラッパーになっており、引数なしの make は help を実行します。正式な一覧は uv run task help で確認できます。各タスクと、分かりにくいタスクの実行理由については、CONTRIBUTING.md で説明しています。

プロジェクト構成
gopher-mcp/
├── src/gopher_mcp/          # Main package
│   ├── __init__.py          # Package initialization
│   ├── __main__.py          # CLI entry point (--transport/--host/--port/--allowed-host)
│   ├── server.py            # FastMCP server + the eight MCP tool definitions
│   ├── client_base.py       # Shared fetch scaffolding for both clients
│   ├── gopher_client.py     # Gopher protocol client
│   ├── gopher_transport.py  # Low-level Gopher transport
│   ├── gopher_parse.py      # Gopher URL and menu parsing
│   ├── gemini_client.py     # Gemini protocol client
│   ├── gemini_tls.py        # Gemini TLS connection handling
│   ├── gemini_parse.py      # Gemini URL and response parsing
│   ├── gemtext.py           # Gemtext document parsing
│   ├── mime.py              # MIME type detection and filtering
│   ├── tofu.py              # Trust-on-First-Use certificate store
│   ├── client_certs.py      # Gemini client certificate storage
│   ├── identity.py          # Trust/identity decision and wording helpers
│   ├── ssrf.py              # SSRF protection / address filtering
│   ├── ratelimit.py         # Per-host rate limiting
│   ├── robots.py            # robots.txt fetching and policy gate
│   ├── cache.py             # Shared TTL + LRU response cache
│   ├── config.py            # Pydantic settings models
│   ├── models.py            # Pydantic data models
│   ├── helpers.py           # Shared URL/IO/sanitization helpers
│   └── utils.py             # Backward-compatible facade re-exporting the above
├── tests/                   # Comprehensive test suite
├── docs/                    # MkDocs documentation
├── scripts/                 # Development scripts
├── config/                  # Example configuration (example.env)
├── .github/workflows/       # CI/CD pipelines
├── Dockerfile               # Slim, non-root container image
├── Makefile                 # Unix/macOS shortcut onto the taskipy tasks
├── server.json              # MCP registry manifest
└── pyproject.toml           # Modern Python project config
開発ワークフロー
  1. セットアップ:uv run task dev-setup - 依存関係と pre-commit フックをインストールします
  2. コーディング:IDE のすべての機能(型ヒント、リンティング)を利用して変更を加えます
  3. 品質チェック:uv run task quality - すべての品質チェック(リンティング+型チェック+テスト)を実行します
  4. テスト:uv run task test-cov - カバレッジレポート付きでテストを実行します
  5. コミット:pre-commit フックにより、コードの品質が自動的に保たれます
テスト
# Run all tests
uv run task test

# Run with coverage
uv run task test-cov

# Run specific test types
uv run task test-unit
uv run task test-integration

# Run one file
uv run pytest tests/test_server.py

設定

サーバーは、両方のプロトコルについて環境変数で設定できます。

Gopher の設定
変数説明デフォルト例
GOPHER_MAX_RESPONSE_SIZE最大レスポンスサイズ(バイト)1048576 (1MB)2097152
GOPHER_TIMEOUT_SECONDSリクエストのタイムアウト時間(秒)3060
GOPHER_CACHE_ENABLEDレスポンスのキャッシュを有効にするtruefalse
GOPHER_CACHE_TTL_SECONDSキャッシュの TTL(秒);0 = 無効300600
GOPHER_MAX_CACHE_ENTRIESキャッシュする最大エントリー数(LRU)10002000
GOPHER_ALLOWED_HOSTS許可するホスト(リスト)未設定(すべて)example.com,test.com
GOPHER_ALLOWED_PORTS許可するポート(リスト)未設定(任意)70
GOPHER_ALLOW_LOCAL_HOSTSループバック/プライベートホストを許可するfalsetrue
GOPHER_REQUESTS_PER_MINUTEホストごとのリクエスト上限(0 = 無効)6030
GOPHER_MAX_CONCURRENT_REQUESTS同時フェッチ数(0 = 無効)52
GOPHER_RESPECT_ROBOTS_TXT/robots.txt に従うtruefalse
Gemini の設定
変数説明デフォルト例
GEMINI_MAX_RESPONSE_SIZE最大レスポンスサイズ(バイト)1048576 (1MB)2097152
GEMINI_TIMEOUT_SECONDSフェッチ全体の通信時間予算3060
GEMINI_CACHE_ENABLEDレスポンスキャッシュを有効化truefalse
GEMINI_CACHE_TTL_SECONDSキャッシュ TTL(秒)。0 = 無効300600
GEMINI_MAX_CACHE_ENTRIESキャッシュエントリの最大数(LRU)10002000
GEMINI_ALLOWED_HOSTS許可するホスト(リスト)未設定(すべて)example.org,test.org
GEMINI_ALLOWED_PORTS許可するポート(リスト)未設定(任意)1965
GEMINI_ALLOW_LOCAL_HOSTSループバック/プライベートホストを許可falsetrue
GEMINI_TOFU_ENABLEDTOFU 証明書検証を有効化truefalse
GEMINI_CLIENT_CERTS_ENABLEDクライアント証明書を保存して添付truefalse
GEMINI_REQUESTS_PER_MINUTEホストごとのリクエスト上限(0 = 無効)6030
GEMINI_MAX_CONCURRENT_REQUESTS同時フェッチ数(0 = 無効)52
GEMINI_RESPECT_ROBOTS_TXT/robots.txt を遵守truefalse

SSRF 対策: デフォルトでは、どちらのツールも、ループバック、リンクローカル(クラウドメタデータ 169.254.169.254 を含む)、またはプライベート/RFC1918 アドレスに解決される接続先を拒否します。 ローカルホストへの接続が意図的に必要な場合(例:localhost 上のサーバーのテスト)に限り、GOPHER_ALLOW_LOCAL_HOSTS/GEMINI_ALLOW_LOCAL_HOSTS を true に設定してください。

タイムアウト。 *_TIMEOUT_SECONDS はフェッチごとの全体の期限であり、フェーズごとのタイムアウトではありません。Gemini では、DNS、接続、TLS ハンドシェイク、トラストストアへの書き込み、送信、読み取りのすべてが同じ予算を消費します。また、robots チェックが有効な場合は /robots.txt の確認にも予算が使われます。そのため、ホストの応答が遅くても、各ステップで設定値全体を使い切ることはありません。

リスト値の変数。 *_ALLOWED_HOSTS、*_ALLOWED_PORTS、GEMINI_DENIED_MIME_TYPES は、カンマ区切り形式(a,b)または JSON 配列(["a", "b"])を受け付けます。各エントリの前後の空白は削除されます。「制限なし」を意味させるには、変数を未設定(または空)にします。値が設定されているにもかかわらずエントリがない場合(" , "、または両方のシェル変数が空の "$A,$B" など)は、起動エラーになります。空の許可リストは未設定と区別できず、意図した制限が黙って適用されなくなるためです。同じ理由で、許可リスト内の 1~65535 の範囲外のポートも起動エラーになります。そのポートに一致することはあり得ないため、代わりに実行時にすべてのフェッチが拒否されてしまいます。

キャッシュ。 *_CACHE_TTL_SECONDS=0 は、書き込んだ瞬間に期限切れとなるエントリを保存するのではなく、キャッシュを無効にします。

上記の表では、最も一般的な設定を取り上げています。追加オプションには、robots ポリシーのキャッシュと再試行(*_ROBOTS_CACHE_TTL_SECONDS、*_ROBOTS_HONOR_AI_TOKENS、*_ROBOTS_FAILURE_BACKOFF_SECONDS)、レンダリング出力の上限(*_MAX_RENDERED_CHARS、GOPHER_MAX_MENU_ITEMS)、Gemini の TOFU/証明書ストレージパスと有効期限ポリシー(GEMINI_TOFU_STORAGE_PATH、GEMINI_TOFU_REJECT_EXPIRED、GEMINI_CLIENT_CERTS_STORAGE_PATH)、MIME フィルタリング(GEMINI_DENIED_MIME_TYPES)、および GOPHER_MCP_ プレフィックスのサーバー/ログ設定があります。すべての変数、その型、範囲、デフォルトについては、完全版の設定ガイドを参照するか、編集してすぐに使えるひな形として config/example.env をご覧ください。

設定例
# Gopher settings
export GOPHER_MAX_RESPONSE_SIZE=2097152
export GOPHER_TIMEOUT_SECONDS=60
export GOPHER_CACHE_ENABLED=true
export GOPHER_ALLOWED_HOSTS="gopher.floodgap.com,gopher.quux.org"

# Gemini settings
export GEMINI_MAX_RESPONSE_SIZE=2097152
export GEMINI_TIMEOUT_SECONDS=60
export GEMINI_TOFU_ENABLED=true
export GEMINI_CLIENT_CERTS_ENABLED=true
export GEMINI_ALLOWED_HOSTS="geminiprotocol.net,skyjake.fi"

# Run with custom config
uv run task serve

ネットワーク上のマナー

Gopherspace と Geminispace は、主に個人が運用する小規模なマシンによって提供されています。このサーバーは、そうした環境にゲストとして受け入れてもらえるように作られています。

このツールが行うこと、行わないこと

gopher-mcp は、誰かがアシスタントにリソースを求めたときに取得し、その会話に内容を返します。取得した内容を使ってトレーニングしたり、アーカイブしたり、再ホストしたり、検索可能にしたりはしません。また、自らリンクをたどることもありません。取得する URL はすべて、呼び出し元が指定したものです。

どちらのプロトコルにもユーザーエージェントのフィールドがないため、通信上でこのクライアントを特定できず、サーバー側が名前で認識したりブロックしたりすることはできません。これは Gopher と Gemini の仕様によるものであり、ここで選択したものではありません。一方、以下で robots の確認を有効にした場合は、gopher-mcp トークンに対して記述されたルールを順守します。そのため、運用者がこのサーバーだけを対象にしたい場合に、その手段を提供できます。

レート制限

どちらのクライアントも、同じホストへのリクエスト間隔を空け、同時に実行する取得数を制限します。以前のバージョンとは異なり、これらはデフォルトで有効です。ホストごとに毎秒 1 リクエスト(*_REQUESTS_PER_MINUTE=60)、同時に 5 件まで取得します(*_MAX_CONCURRENT_REQUESTS=5)。無効にするには、どちらかを 0 に設定します。Gemini サーバーが 44 SLOW_DOWN と応答した場合は、これらの設定にかかわらず、常にその指示に従います。

ロボットの除外(robots.txt)

サーバーはホストのルートから /robots.txt を取得し、何かを取得する前にそのルールに従います。これはデフォルトで有効です。これらのサーバーは大部分が趣味で運用されているため、明示されたポリシーを無視することは、LLM が無人で操作するツールのデフォルトとして適切ではありません。ポリシーはホストごとに 24 時間キャッシュされるため(*_ROBOTS_CACHE_TTL_SECONDS)、追加の往復通信が発生するのはホストごとに一度だけで、取得ごとではありません。

GOPHER_RESPECT_ROBOTS_TXT=false / GEMINI_RESPECT_ROBOTS_TXT=false を設定すると無効になりますが、この上書きは自分が運用するホストに対して使用してください。他者のサーバーにある包括的な Disallow: / は、設定ミスではなく意図的な判断です。エラーメッセージもそのように記述されています。メッセージを読むのはあなたではなくモデルだからです。ブロックされた取得では BLOCKED_BY_ROBOTS が返され、再試行したりパスの別表記を試したりせず、停止してユーザーに伝えるよう指示されます。ポリシーをまったく読み取れずに取得が拒否された場合は、代わりに ROBOTS_UNAVAILABLE が返されます。これは一時的な状態であり、取得を禁止されたわけではないため、別のコードになっています。

適用される規約はプロトコルによって異なります。

  • Gemini は公式の関連仕様に従い、仮想エージェント archiver、indexer、researcher、webproxy を定義しています。このサーバーは gopher-mcp、webproxy、indexer、* に一致します。archiver や researcher は名乗りません。ここでは内容を保持せず、researcher は取得した内容を表に出さずに動作するツール向けに定義されているためです。
  • Gopher は、Veronica-2 が gopher://gopher.floodgap.com/0/v2/help/indexer で説明している慣例に従います(gopher:// の href は PyPI のサニタイザーで削除されるため、リンクではなく URL をそのまま記載しています)。このサーバーは gopher-mcp と * に一致します。Floodgap のインデクサーに属する veronica は名乗りません。

どちらも RFC 9309 ではなく、1994 年の元の robots.txt の文法を使用しています。そのため、認識されるのは #、User-agent:、Disallow: のみで、それ以外のフィールドはすべて無視されます。これには Allow: も含まれます。これは重要です。RFC 9309 のパーサーは Allow: 行にも従いますが、これらのネットワークの作成者はその行が無視されることを想定しています。その結果、意図よりも_許容的_になってしまいます。

デフォルトでは、サーバーは ClaudeBot、GPTBot、CCBot などの AI クローラートークンを指定したルールにも従います(*_ROBOTS_HONOR_AI_TOKENS)。これらはどちらのプロトコルの慣例にも含まれませんが、運用者がそのルールを記述した意図は「LLM ツールを使わせない」ということであり、ここではその要求に従います。 ポリシーをまったく取得できない場合は、リクエストごとに新たな接続タイムアウトが発生するのを避けるため、再度プローブするまでホストを短時間待機させます(デフォルトでは *_ROBOTS_FAILURE_BACKOFF_SECONDS が 60s)。すぐに再プローブするには 0 に設定し、停止中のホストから頻繁に取得する場合は値を大きくしてください。Gemini カプセルが 44 SLOW_DOWN を返す場合は例外です。独自の再試行期間が指定されているため、その期間を使用します。

既知の制限が 2 つあります。どちらも取り繕わずに記載しています。

  • Gopher はフェイルオープンです。 このプロトコルにはステータスコードがないため、セレクターの欠落、エラードキュメント、空のファイルを通信上で区別できません。RFC 9309 §2.3.1.4 に従うと、到達不能なポリシーはすべてを拒否することになり、Gopherspace の大部分がブロックされてしまいます。そのため、パーサーは寛容に動作し、User-agent: グループを含まないコンテンツにはルールを適用しません。ステータスコードがある Gemini はフェイルクローズで、51 NOT FOUND を「ポリシーなし」として扱います。フェイルクローズが対象とするのは 4x ステータスだけではない点に注意してください。到達不能なカプセル(接続拒否、TLS エラー、タイムアウト、不正な応答)にも取得可能なポリシーがないため、アクセスを拒否します。ただし、カプセルが何かを禁止したわけではないため、別の ROBOTS_UNAVAILABLE コードを使用します。メッセージには根本原因が示されます。また、robots チェックを無効にしても、そのようなカプセルにアクセスできるようにはなりません。
  • Gopher のパスルールはベストエフォートです。 Gopher URI では、アイテムタイプがパスの先頭文字になります。そのため、gopher://host/1/archive の URI パスは /1/archive ですが、通信上のセレクターは /archive です。ルールは両方の表記に対してテストされますが、作成者の意図どおりに動作することが保証されるのは Disallow: / の形式だけです。

ディレクトリごと、またはユーザーごとの robots.txt はありません。どちらのプロトコルの慣習にも RFC 9309 §2.3 にも、そのような定義はありません。共有ホストでは、ルートにパスプレフィックスを使った単一のファイルを置くのが確立された方法です。

貢献

貢献を歓迎します。詳しくは、貢献ガイドラインをご覧ください。

簡単な貢献手順
  1. GitHub でリポジトリをフォークします
  2. フォークをクローンします:git clone https://github.com/your-username/gopher-mcp.git
  3. 開発環境をセットアップします:uv run task dev-setup
  4. 機能ブランチを作成します:git checkout -b feature/amazing-feature
  5. テストを追加して変更を加えます
  6. 品質を確認します:uv run task quality
  7. 変更をコミットします:git commit -m 'Add amazing feature'
  8. フォークにプッシュします:git push origin feature/amazing-feature
  9. 明確な説明を添えてプルリクエストを送信します
開発基準
  • すべての関数とメソッドに型ヒントを付けること
  • 包括的なテスト(CI によりカバレッジ 95% 以上を必須とします)
  • すべての公開 API にドキュメントを用意すること
  • すべてのネットワーク操作でセキュリティを考慮すること
  • クロスプラットフォーム互換性(Windows、macOS、Linux)

ライセンス

このプロジェクトは MIT ライセンスの下でライセンスされています。詳しくは、LICENSE ファイルをご覧ください。

謝辞

  • Anthropic による**Model Context Protocol** — この統合を可能にする基盤です
  • MCP Python SDK — 同梱の FastMCP(mcp.server.fastmcp)を基盤としてこのサーバーを構築しており、mcp>=1.28.1,<2 に固定しています
  • Gopher プロトコルコミュニティ — 初期のインターネットの精神を守り続けています

関連プロジェクト

サポート

どの項目を選ぶ場合も、バージョンを記載してください。gopher-mcp --version で確認できます。このコマンドは、パッケージをインポートするチェックアウトがない uvx と Docker のインストール環境でも動作します。


❤️ を込めて Cameron Rye が作成しました。

役に立ったと思ったら、このプロジェクトにスターを付けてください!

コメント

MCP は初めてですか?

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

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