MCP Research Friend Server
公式研究ツール(Sqliteバックアップのドキュメントストレージを含む)
ドキュメント
Research Friend
AIアシスタントがWeb上の情報を検索し、ローカルのリサーチストックを管理するためのフレンドリーなヘルパーです。
Research Friendは、AIツールにWebページの取得やインターネット検索の機能を提供するMCPサーバーです。内部では実際のWebブラウザを使用するため、JavaScriptに大きく依存する最新のWebサイトでも動作します。また、ドキュメントの保存、テキスト抽出、ライブラリ全体の検索を行うためのローカル「ストック」機能も備えています。
すべての機能を活用するには、プロンプト(一般的)とサンプリング(やや稀)をサポートするMCPクライアントが必要です。Research Friendは、両方をサポートするChabeauと並行して開発を進めています。
機能
- 実際のブラウザによるWebページの取得(JavaScriptを多用するサイトを含む)
- PDFの取得とテキストコンテンツの抽出
- DuckDuckGoまたはGoogleによるWeb検索
- ドキュメントのローカルストック管理(検索、一覧表示、抽出)
はじめに
コンピュータにNode.jsバージョン20以降がインストールされている必要があります。
1. 依存関係のインストール
このフォルダでターミナルを開き、以下を実行します。
npm install
2. ブラウザサポートのインストール
Research FriendはPlaywrightを使用してWebブラウザを制御します。依存関係のインストール後、ブラウザをインストールする必要があります。
npx playwright install chromium
これにより、Playwrightが使用するChromiumのコピーがダウンロードされます。既存のブラウザとは別にインストールされます。
3. サーバーの起動
node src/index.js
サーバーは標準入出力(stdio)を介して通信します。これはMCPクライアントが接続する方法です。
MCPクライアントへの追加
Research Friendの追加方法は、使用するMCPクライアントによって異なります。以下は設定の一般的な例です。
[[mcp_servers]]
id = "research-friend"
command = "node"
args = ["/path/to/mcp-research-friend/src"]
transport = "stdio"
/path/to/mcp-research-friend は、コンピュータ上のこのフォルダへの実際のパスに置き換えてください。
ツール
Webツール
friendly_web_fetch
Webページを取得し、そのコンテンツを返します。デフォルトでは、リンクを保持したMarkdown形式で返します(LLMに最適)。 Readabilityを使用してメインコンテンツを抽出します(ナビゲーションや広告などを除去)。PDF、ページネーション、コンテンツ内検索には、代わりに friendly_web_extract を使用してください。
パラメータ:
url(必須) - 取得するWebアドレスoutputFormat- 出力形式:markdown(デフォルト)、text、またはhtmlwaitMs- コンテンツの表示が遅い場合に、ページ読み込み後に追加で待機する時間timeoutMs- タイムアウトまでの待機時間(デフォルト: 15秒)maxChars- 返すコンテンツの最大量(デフォルト: 40,000文字)includeHtml-trueに設定すると、コンテンツと共に生のHTMLも返しますheadless-falseに設定すると、ブラウザウィンドウを表示します(デバッグに便利)
戻り値:
url- リクエストされたURLfinalUrl- リダイレクト後のURLtitle- ページタイトルcontent- 抽出されたコンテンツ(リクエストされた形式)html- 生のHTML(includeHtmlがtrueの場合のみ)meta- ページのメタデータ(説明、作成者、公開日時など)fetchedAt- ページが取得された日時のISOタイムスタンプtruncated- コンテンツがmaxCharsに収まるように切り詰められたかどうか
friendly_search
Webを検索し、結果のリストを返します。
パラメータ:
query(必須) - 検索キーワードengine- 使用する検索エンジン(duckduckgoまたはgoogle)maxResults- 返す結果の数(デフォルト: 10、最大: 50)timeoutMs- タイムアウトまでの待機時間(デフォルト: 15秒)headless-falseに設定すると、ブラウザウィンドウを表示します
戻り値:
query- 使用された検索クエリengine- 使用された検索エンジンresults- 結果の配列。各結果にはtitle、url、snippetが含まれますsearchedAt- 検索が実行された日時のISOタイムスタンプfallback_result_html- ページの生のHTML(結果が見つからなかった場合のみ)debug_info- 検索試行に関する診断情報
CAPTCHA処理:
ヘッドレスモードで実行中にCAPTCHAが検出されると、ツールは自動的に可視ブラウザウィンドウで再試行します。これにより、手動でCAPTCHAを解決する機会が得られます。debug_info.retried フィールドは、このフォールバックが使用されたかどうかを示します。
friendly_web_extract
URLからコンテンツを抽出します。URLがPDFかWebページかを自動検出し、それぞれ適切に処理します。
パラメータ:
url(必須) - 取得するURL(PDFまたはWebページ)maxChars- 返すテキストの最大量(デフォルト: 40,000文字)offset- 開始文字位置(デフォルト: 0)。大きなコンテンツをページ分割するために使用します。search- フレーズを検索し、完全なコンテンツの代わりに周辺コンテキスト付きの一致を返しますcontextChars- 各検索一致の前後のコンテキスト文字数(デフォルト: 200)waitMs- 動的コンテンツのためのページ読み込み後の追加待機時間(Webページのみ)timeoutMs- タイムアウトまでの待機時間(デフォルト: 15秒、Webページのみ)headless-falseに設定すると、ブラウザウィンドウを表示します(Webページのみ)
戻り値(通常モード):
url- リクエストされたURLcontentType-pdfまたはhtmltitle- ページ/ドキュメントのタイトルauthor- PDFの作成者(PDFのみ、利用可能な場合)creationDate- PDFの作成日時(PDFのみ、利用可能な場合)pageCount- ページ数(PDFのみ)totalChars- 総文字数(offsetと共にページ分割に使用)offset- 使用されたオフセットcontent- 抽出されたテキストコンテンツfetchedAt- ISOタイムスタンプtruncated- このチャンクの後にさらにコンテンツが残っているかどうか
戻り値(検索モード):
url、contentType、title、totalChars、fetchedAt- 上記と同じsearch- 使用された検索フレーズmatchCount- 見つかった一致の数matches- 一致の配列。各一致にはposition、context、prefix、suffixが含まれます
friendly_web_ask
URL(PDFまたはWebページ)を取得し、LLMにそれに関する質問に回答させます。コンテンツタイプを自動検出します。ドキュメントは別のコンテキストで処理されるため、メインの会話をコンパクトに保てます。
パラメータ:
url(必須) - 取得するURL(PDFまたはWebページ)ask(必須) - LLMへの質問または指示(要約、情報抽出、質問への回答など)askMaxInputTokens- LLM呼び出しあたりの最大入力トークン数(デフォルト: 150,000)askMaxOutputTokens- LLM呼び出しあたりの最大出力トークン数(デフォルト: 4,096)askTimeout- タイムアウト(ミリ秒)(デフォルト: 300,000 = 5分)askSplitAndSynthesize- 大きなドキュメントの場合: チャンクに分割し、それぞれを処理して結果を統合します(デフォルト: false)。警告: 多くのトークンを消費します。waitMs- 動的コンテンツのためのページ読み込み後の追加待機時間(Webページのみ)timeoutMs- タイムアウトまでの待機時間(デフォルト: 15秒、Webページのみ)headless-falseに設定すると、ブラウザウィンドウを表示します(Webページのみ)
戻り値:
url- リクエストされたURLcontentType-pdfまたはhtmltitle- ページ/ドキュメントのタイトルtotalChars- ドキュメントの総文字数ask- 与えられた指示answer- LLMの応答model- 応答を生成したモデルchunksProcessed- 処理されたチャンク数(小さなドキュメントでは1、askSplitAndSynthesize使用時はそれ以上)fetchedAt- ISOタイムスタンプ
Askモードは、MCPサンプリングを使用してLLMに任意の指示でドキュメントを処理させます。これは以下の場合に役立ちます。
- コンテキストを圧迫する大きなドキュメント
- メインの会話のトークンコストを抑える
askSplitAndSynthesize が有効な場合、askMaxInputTokens を超えるドキュメントは自動的にオーバーラップするチャンクに分割されます。各チャンクは個別に処理され、結果は単一の一貫した回答に統合されます。最終的な応答は、ドキュメントの言語に関係なく、リクエストと同じ言語で提供されます。
ドキュメントストック
ストックは、ローカルで検索可能なドキュメントのライブラリです。PDF、HTMLファイル、プレーンテキスト(Markdown/TXT)をサポートします。ドキュメントを追加すると、Research Friendは元のファイルを保存し、テキストを抽出し(PDF/HTMLの場合)、メタデータをローカルデータベースに保存します。検索には、高速でフレーズ認識可能なマッチングのために、内部でripgrepを使用します。
ストックの場所
ストックは ~/.research-friend/ の下にあります。
inbox/- 処理するファイルをここにドロップしますstore/- 整理されたドキュメントストレージと抽出されたテキストstash.db- メタデータデータベース
サポートされているファイルタイプ
- PDF:
.pdf(テキスト抽出) - HTML:
.html、.htm(テキスト抽出) - Markdown:
.md、.markdown(プレーンテキストとして保存) - テキスト:
.txt(プレーンテキストとして保存)
ストックツール
stash_open_inbox
ドラッグアンドドロップを容易にするために、ファイルマネージャーでストックの受信箱フォルダを開きます。
戻り値:
opened- フォルダを開くリクエストが送信されたかどうかinboxPath- 受信箱への絶対パスcommand- 使用されたOSコマンドargs- 使用されたコマンド引数
stash_process_inbox
inbox/ 内のファイルを処理し、トピックに分類し、テキストを抽出し、結果を保存します。
長いドキュメントの場合、分類はサンプリングされたセクション(開始/中間/終了といくつかのランダムなチャンク)を使用して、トピックの精度を向上させます。
戻り値:
processed- 正常に処理されたファイル名の配列errors- 発生したエラーdocuments- 作成されたドキュメントレコードの配列
reindex_stash
ストックされたドキュメントのサマリーを再生成し、トピックを再割り当てし、ストアのメタデータを更新します。ids が省略されるか空の場合、すべてのドキュメントが再インデックスされます。
パラメータ:
ids- 再インデックスするドキュメントID(オプション)
戻り値:
reindexed- 再インデックスされたドキュメントIDerrors- 発生したエラーdocuments- 更新されたドキュメントレコードの配列
stash_list
ストック内のドキュメントを一覧表示します。
パラメータ:
topic- トピックでフィルタリング(オプション)limit- 最大結果数(デフォルト: 50)offset- ページネーションオフセット(デフォルト: 0)
戻り値:
type-allまたはtopictotalDocuments- 総ドキュメント数(typeがallの場合のみ)count- ページネーション後に返された結果offset- 使用されたページネーションオフセットlimit- 使用されたページネーション制限topics- 既知のトピックとドキュメント数のサマリーdocuments- メタデータ付きのドキュメントリスト(トピックを一覧表示する場合、isPrimaryを含む)
stash_search
ストック全体でファイル名とコンテンツを検索します。すべての検索語が存在する必要があります(AND論理)。ファイル名の一致が最初にリストされます。正確なフレーズには引用符を使用します。
パラメータ:
query(必須) - 検索語。フレーズには引用符を使用します:"sparkling wine"topic- トピックでフィルタリング(オプション)ids- 特定のドキュメントIDでフィルタリング(オプション)limit- 返す最大ドキュメント数(デフォルト: 20)offset- ページネーションオフセット(デフォルト: 0)maxMatchesPerDoc- ドキュメントあたりの最大一致数(デフォルト: 50)context- 各一致の前後のコンテキスト行数(デフォルト: 1、最大: 5)。一致と見なされるために語がどれだけ近くに出現する必要があるか、および返される周辺テキストの量の両方を制御します。 戻り値:totalMatches- ページネーション前の一致ドキュメント総数count- ページネーション後に返された結果results- ドキュメントの配列。各要素は以下を含む:id,filename,fileType,summary,charCount,createdAtmatchType-filename,content, またはfilename+contentmatches- 各一致箇所の{ line, context }の配列
line の値を stash_extract と共に使用して、一致箇所に直接ジャンプします。
stash_extract
スタッシュされたドキュメントからコンテンツを抽出して読み取ります。stash_search の結果の行番号を使用して、一致箇所に直接ジャンプします。
パラメータ:
id(必須) -stash_list/stash_searchからのドキュメントIDmaxChars- 返すテキストの最大量 (デフォルト: 40,000文字)offset- 開始文字位置 (lineとは排他的)line- 開始行番号 (offsetとは排他的)
戻り値:
id,filename,fileType,summary- ドキュメントのメタデータtotalChars- ドキュメントの総文字数offset- 文字オフセット (参照にlineを使用した場合に含まれる)line- 行番号 (lineパラメータが使用された場合のみ)content- 抽出されたテキストコンテンツtruncated- このチャンクの後にさらにコンテンツが残っているかどうか
stash_ask
スタッシュされたドキュメントについてLLMに質問させます。ドキュメントは別のコンテキストで処理されるため、メインの会話をコンパクトに保てます。
パラメータ:
id(必須) -stash_list/stash_searchからのドキュメントIDask(必須) - LLMへの質問または指示askMaxInputTokens- LLM呼び出しあたりの最大入力トークン数 (デフォルト: 150,000)askMaxOutputTokens- LLM呼び出しあたりの最大出力トークン数 (デフォルト: 4,096)askTimeout- タイムアウト (ミリ秒) (デフォルト: 300,000 = 5分)askSplitAndSynthesize- 大きなドキュメントの場合: チャンクに分割し、それぞれを処理してから結果を統合します (デフォルト: false)
戻り値:
id,filename,fileType,summary- ドキュメントのメタデータtotalChars- ドキュメントの総文字数ask- 与えられた指示answer- LLMの応答model- 応答を生成したモデルchunksProcessed- 処理されたチャンク数
典型的な流れ
~/.research-friend/inbox/にファイルをドロップするstash_process_inboxを実行するstash_listを使用してトピックをブラウズするstash_searchを使用して関連ドキュメントを見つけるstash_extractを使用して特定のドキュメントを読むか、stash_askを使用してそれについて質問する
トラブルシューティング
「ブラウザが予期せず閉じられました」などのエラー
ブラウザを再インストールしてみてください:
npx playwright install chromium --force
Linuxでは、システム依存関係も必要になる場合があります:
npx playwright install-deps chromium
サーバーが起動しない
Node.js 20以降を使用していることを確認してください:
node --version
バージョンが古い場合は、nodejs.org にアクセスして新しいバージョンをダウンロードしてください。
ライセンス
MIT