📘
arXivを検索するMCPサーバを開発してみた
MCPについて、記事を読んだり、利用はしていたものの、自分で作ったことがなかったので論文検索用MCPサーバをサンプル実装してみました。
こちらの記事にインスパイアされました:
実装したものはこちら:
やりたいこと
- arXiv.orgの論文を簡単に検索できるMCPサーバーを作成する
- 特定のカテゴリ(AI、機械学習など)から最新の論文を取得する
- 検索結果を整形して、タイトル、著者、要約、リンクなどを分かりやすく表示する
- Claude Desktopから利用する
技術選定
主に以下の技術を使用しました:
- Deno: 高速で安全なTypeScriptランタイム
- TypeScript: 型安全なコードの作成
- FastMCP: MCPサーバーの実装をシンプルにするライブラリ (使ってみたかった)
実装の流れ
1. プロジェクト初期設定
まず基本的なディレクトリ構造を設定し、必要な依存関係を定義しました:
// deno.json
{
"tasks": {
"dev": "deno run --watch --allow-read --allow-env --allow-net src/main.ts",
"test": "deno test --allow-read --allow-env --allow-net",
"compile": "deno compile --allow-read --allow-env --allow-net --output=./bin/arxiv-search-mcp src/main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@1",
"fastmcp": "npm:fastmcp@^1.21.0",
"zod": "npm:zod@^3.24.2",
"fast-xml-parser": "npm:fast-xml-parser@4.3.4"
}
}
2. arXiv APIの統合
arXiv.orgはAPIを提供しており、このAPIを使用して論文の検索と取得を行います。実装では、以下の主要な要素を定義しました:
- XMLレスポンスのパース用インターフェース
- arXivエントリーのインターフェース
- 検索パラメータのスキーマ(Zodを使用)
// 型定義
export interface XMLFeedEntry {
title: string;
author: { name: string }[] | { name: string };
summary: string;
id: string;
}
interface XMLResponse {
feed: {
entry: XMLFeedEntry[];
};
}
export interface ArxivEntry {
title: string;
authors: string;
summary: string;
link: string;
}
// パラメータスキーマ
export const SearchArxivParamsZod = z.object({
category: z.string().describe("arXiv category (e.g., cs.LG, astro-ph)"),
max_results: z.number().min(1).max(100).default(5).describe(
"Number of papers to fetch (1-100)",
),
});
3. 検索機能の実装
検索機能の中核部分は search_arxiv_execute
関数です。この関数は:
- 指定されたカテゴリと最大結果数に基づいてAPIリクエストを構築
- arXiv APIにリクエストを送信
- XMLレスポンスをパース
- 結果を整形して返す
export async function search_arxiv_execute(args: z.infer<typeof SearchArxivParamsZod>) {
try {
// APIリクエスト構築
const query = new URLSearchParams({
search_query: `cat:${args.category}`,
sortBy: "submittedDate",
sortOrder: "descending",
max_results: args.max_results.toString(),
});
const response = await fetch(`${ARXIV_API_BASE}${query}`, {
headers: {
"User-Agent": USER_AGENT,
"Accept": "application/xml",
},
});
// レスポンス処理
const xmlText = await response.text();
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "@_",
});
const xmlDoc = parser.parse(xmlText) as XMLResponse;
// エントリーパース
const entries = Array.isArray(xmlDoc.feed.entry)
? xmlDoc.feed.entry.map(parseArxivEntry)
: xmlDoc.feed.entry
? [parseArxivEntry(xmlDoc.feed.entry)]
: [];
// 結果フォーマット
const formattedPapers = entries.map((paper: ArxivEntry) =>
`Title: ${paper.title}\nAuthors: ${paper.authors}\nSummary: ${paper.summary}\nLink: ${paper.link}`
);
return formattedPapers.join("\n\n---\n\n");
} catch (error: unknown) {
console.error("Error in search_arxiv:", error);
const errorMessage = error instanceof Error
? error.message
: "An unknown error occurred";
return `Error during search: ${errorMessage}`;
}
}
4. MCPサーバーの構築
FastMCPライブラリを使用して、MCPサーバーをセットアップし、検索ツールを登録しました:
const server = new FastMCP({
name: "arXiv-Search",
version: "1.0.0",
});
server.addTool({
name: "search_arxiv",
description: "Search latest papers from a specific arXiv category",
parameters: SearchArxivParamsZod,
execute: search_arxiv_execute,
});
server.start({
transportType: "stdio",
});
使用方法
arXiv Search MCPサーバーは、以下のように使用できます:
- リポジトリをクローンして依存関係をインストール
-
deno task compile
コマンドでバイナリをビルド - Claude Desktopの設定ファイルにMCPサーバーを追加
{
"mcpServers": {
"arxiv-search-mcp": {
"command": "/path/to/dir/arxiv-search-mcp/bin/arxiv-search-mcp"
}
}
}
Claudeとの対話では、以下のようにして論文を検索できます:
cs.AIカテゴリの最新論文を5件教えてください
すると、Claudeは内部でsearch_arxiv
ツールを呼び出し、フォーマットされた論文情報を表示します:
Title: [論文タイトル]
Authors: [著者リスト]
Summary: [論文要約]
Link: [論文へのリンク]
---
...
ひとまず使われていそうです!
まとめ
これまで利用はしていたものの、実際に手を動かしてサーバを実装してみることで改めてMCPの可能性と柔軟性、そして外部APIとの統合の重要性を実感しました。あとFastMCP良いっすね。
引き続きやっていきます。
Discussion