arxiv-agent-mcp
arXiv Research-Concept Companion
AI/ML-агент-компаньон для учебы (задание KSE Agentic Lab). Он читает заметку-сводку по концепции из вашего хранилища Obsidian, находит связанные статьи arXiv, оценивает каждую по тематической релевантности и цитатному влиянию с поправкой на возраст, находит устоявшиеся работы, на которые опирается прошедший отбор кандидат, и записывает результаты обратно в хранилище.
Существующий MCP-сервер (часть A): Obsidian Local REST API MCP.
Кастомный MCP-сервер (часть B):
custom_server/— FastMCP-приложение, 3 инструмента поверх публичных API arXiv и OpenAlex (без авторизации).Агент:
agent/— PydanticAIAgent(на базе OpenRouter), содержащий оба MCP-подключения как наборы инструментов и оркестрируемый машиной состояний LangGraph.
Требования
Python 3.12+,
uv.Ключ OpenRouter API.
Obsidian с установленным и запущенным community-плагином Local REST API и MCP-сервером, который умеет с ним работать (любая реализация Obsidian Local REST API MCP — команда запуска настраивается, см. ниже).
Related MCP server: arxiv-mcp
Установка
uv sync
cp .env.example .envЗаполните .env:
Переменная | Значение |
| Ключ OpenRouter — используется агентом и функцией |
| Слаг модели, например |
| Учётные данные плагина Local REST API. |
| Аргументы argv через пробел для запуска вашего Obsidian MCP-сервера, например |
| Минимальный балл релевантности (0–1), чтобы пройти фильтр. По умолчанию |
| Минимум цитирований в год для прохождения проверки импакта. По умолчанию |
| Статьи моложе этого возраста освобождаются от проверки импакта. По умолчанию |
Запуск
Два независимых процесса, использующих один uv-проект:
# process 1 — the custom MCP server (arXiv + OpenAlex)
uv run python -m custom_server.server
# process 2 — the agent (connects to both MCP servers), driven by a free-text prompt
uv run python -m agent.graph "Find papers related to my 'Transformers Concept Note'"agent/graph.py сам запускает custom_server/server.py как stdio-подпроцесс, поэтому для процесса 2 не нужно, чтобы процесс 1 уже работал, — две команды выше просто демонстрируют, что каждый из них запускается независимо.
Промпт не является буквальным названием заметки — первый шаг агента (parse_prompt) использует вызов LLM, чтобы определить, к какой заметке Obsidian относится промпт. Если определить её невозможно, выполнение немедленно останавливается и печатается «Недостаточно информации: в промпте не указана ни заметка, ни страница Obsidian» — без обращения к Obsidian. Если найденная заметка не даёт достаточно ключевых слов по концепции (меньше min_keywords, по умолчанию 2), выполнение останавливается после её чтения и печатает похожее сообщение «недостаточно информации» вместо поиска в arXiv.
Автономный режим / режим воспроизведения
Кастомный сервер вызывает три живых сетевых API (arXiv, OpenAlex, OpenRouter). Установка CUSTOM_SERVER_OFFLINE=1 заставляет его отдавать результаты инструментов из записанных фикстур в custom_server/fixtures/ — сетевой доступ и OPENROUTER_API_KEY не требуются. Полезно для демонстрации/защиты без устойчивой сети или для быстрой итерации.
CUSTOM_SERVER_OFFLINE=1 uv run python -m custom_server.serverЧто охвачено: search_arxiv_papers (один записанный фид поиска, отдаваемый для любого запроса — см. ограничение ниже), а также search_arxiv_papers / find_foundational_citations для двух записанных работ: GPT-3 (2005.14165) и ResNet (1512.03385).
Известные ограничения:
search_arxiv_papersв автономном режиме не зависит от запроса: он всегда возвращает одну и ту же записанную ленту, независимо от текста запроса.score_paper_relevanceиfind_foundational_citationsраспознают только две указанные выше статьи. Незаписанныйarxiv_idвызываетPaperNotFoundError(ту же ошибку, которую породило бы реальное отсутствие записи в OpenAlex); незаписанное название статьи, переданное вscore_paper_relevance, вызываетFixtureNotFoundError— это отличимо, а не тихий неверный ответ.
Чтобы перегенерировать или расширить фикстуры: uv run python -m custom_server.fixtures.record заново получает сохранённые ответы arXiv/OpenAlex (оба API — публичные и не требуют аутентификации) и перезаписывает JSON/XML-файлы в custom_server/fixtures/. Чтобы добавить новую статью, добавьте в record.py два её httpx.get-вызова и соответствующую запись в relevance_scores.json (написанную вручную – это не реальный выход OpenRouter: записывать его сырой ответ chat-completion не стоит из-за хрупкости формата; структурированные поля {relevance, novelty, rationale} воспроизводятся напрямую через PydanticAI FunctionModel).
Тесты
uv run pytest custom_server/tests agent/testsВсе сетевые вызовы (arXiv, OpenAlex, OpenRouter) мокаются; никакого живого трафика во время тестов нет.
Контракты инструментов (часть C)
search_arxiv_papers (кастомный)
Назначение | Основной источник данных: поиск статей-кандидатов по теме arXiv. |
Описание для модели | «Осуществляет поиск по arXiv по теме, при необходимости с ограничением по категориям и минимальной дате подачи. Используйте этот инструмент в поисках кандидатов, прежде чем оценивать каждого индивидуально через |
Входные данные |
|
Выходные данные |
|
Ошибки |
|
Побочные эффекты | Нет — только чтение: HTTP GET к |
Пример |
|
score_paper_relevance (кастомный)
| | |
| Назначение | Оценочный инструмент: судит о тематическом соответствии одного кандидата и о том, преодолевает ли его цитационная история скорректированный по возрасту порог. |
| Описание для модели | «Оцени насколько будет статья релевантна и нована с точки потребности для концептуального файла, и проверь, проходит ли её цитатный уровень минимальный порог (цитирований в год, исключая статьи моложе одного года). Используй для каждого кандидата из search_arxiv_papers, чтобы решить, стоит ли включать её в список для чтения. Выдаёт исключение case. такое исключение если у статьи нет записи в OpenAlex или если не удалась, underlying call к модели оценки релевантности.» |
| Входные данные | concept_summary: str, paper: {arxiv_id, title, abstract} |
| Выходные данные | {relevance: float, novelty: float, citation_count: int, publication_year: int, citations_per_year: float, impact_pass: bool, rationale: str} |
| Ошибки | PaperNotFoundError (из custom_server.openalex) если в OpenAlex нет записи для arXiv DOI статьи — в отличие от найденной, но не цитируемой статьи, где допустимо citation_count: 0. UnexpectedModelBehavior, если структурированный результат вызова OpenRouter не прошёл валидацию схемы после повторных попыток. |
| Побочное воздействие | Только чтение: один OpenAlex GET и один вызов reusable OpenRouter chat-completion. |
| add Example | score_paper_relevance(concept_summary="attention mechanisms in NLP", paper={...}) → {relevance: 0.92, novelty: 0.6, citation_count: 84331, impact_pass: True, ...} |
cpp |4 |
### `find_foundational_citations` (кастомный)
| | |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Назначение** | Анализ графа цитирований: для заданной статьи ранжирует её собственные ссылки по количеству цитирований, чтобы выявить устоявшиеся работы, на которых она построена. Отличается от `search_arxiv_papers` — он анализирует список ссылок конкретной статьи, а не поиск по ключевым словам. |
| **Описание для модели** | «Если передан arXiv ID одной статьи, вернуть её наиболее цитируемые ссылки — устоявшиеся предшествующие работы, на которых она строится. Использовать после выбора статьи для чтения, чтобы выявить стоящую за ней базовую литературу. Статья без записанных ссылок возвращает пустой список — это нормальный результат, а не ошибка." |
| **Вход** | `arxiv_id: str`, `max_results: int = 3` (1–3) |
| **Выход** | `list[{openalex_id, title, cited_by_count, publication_year}]`, отсортированный по `cited_by_count` по убыванию, первые `max_results` |
| **Условия ошибки** | `ValueError`, если `max_results` вне диапазона `[1, 3]`. `PaperNotFoundError`, если в OpenAlex нет записи для этого arXiv ID. Статья с нулевым числом ссылок возвращает `[]` — это корректный результат, а не ошибка. |
| **Побочные эффекты** | Только чтение: один запрос статьи OpenAlex и один или более пакетных запросов works в OpenAlex (пакетами по 50 идентификаторов на запрос). |
| **Пример** | `find_foundational_citations(arxiv_id="2005.14165", max_results=3)` → три наиболее цитируемые работы, на которые ссылается GPT-3. |
### Obsidian Local REST API MCP (существующий, часть A)
Используется через вызовы инструментов на естественном языке агента PydanticAI (не как фиксированная функция-обёртка) для двух операций в потоке:
| | |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Разрешение ссылки** | Перед любым вызовом Obsidian `parse_prompt` просит агента PydanticAI (обычные рассуждения LLM, а не вызов MCP) определить название заметки, подразумеваемое свободным текстовым запросом пользователя. Если ничего не удаётся определить, поток останавливается со статусом "insufficient information" и Obsidian не вызывается. |
| **Чтение** | Агент получает задание прочитать заметку с названием `note_title` (из `parse_prompt`) и вернуть её содержимое в виде обычного текста — эти данные попадают в `concept_text`, вход для извлечения ключевых слов и оценки релевантности. |
| **Запись** | Агенту предписывается создать/перезаписать заметку с названием `"{note_title} — Related Papers"` с markdown-содержимым, сформированным в `compose_note_content` — наблюдаемый эффект, замыкающий цикл между обоими MCP-серверами. |
| **Условия ошибки** | Остановленный плагин, неверный API-ключ или отсутствующая заметка проявляются как различимый сбой вызова инструмента со стороны MCP-сервера, а не молчаливый пустой результат. |
## Обоснование решений
* **Почему Obsidian:** заданию нужен существующий MCP-сервер, из которого агент и читает, и пишет. Собственные концепт-заметки студента — естественный вход «что я уже знаю», а запись отобранных работ обратно наглядно замыкает цикл в хранилище.
* **Почему arXiv + OpenAlex вместо сайта за логином:** изначально рассматривавшиеся расписание KSE и материалы Moodle требуют личного входа, что противоречит правилу задания о публичных API. arXiv и OpenAlex открыты, не требуют аутентификации и напрямую поддерживают область «релевантность + влияние».
* **Почему релевантность оценивает LLM, а не эмбеддинги:** у OpenRouter нет эндпотинга эмбеддингов (проверено по живому списку моделей), поэтому приложение `score_paper_relevance` использует вызов PydanticAI со структурированным выводом его на структуру: вместо векторной близости — переиспользуя те учётос данные для модели, которые уже всё равно нужны проекту.
* **Почему `find_foundational_citations` — не «искать ещё раз с OpenAlex»:** берёт список ссылок конкретной статьи и ранжирует его по влиянию цитиований — тот же вид контролируемого сравнения показателей, что и примеры из самой задачи; ответственная и обработка у него отдельная, отличная от поиска по ключевым словам в `search_arxiv_papers`.
* **Фильтрация — это обычный Python, а не 4-й инструмент:** фильтр по порогу релевантности вместе с `impact_pass` в `agent_candidates_node` из `агент/graph.py` — детерминированная постобработка уже оценённых данных, а не новая предметная логика; инструмент был бы просто обёрткой вокруг `if`.
* **Компромиссы / ограничения:** режим офлайна/воспроизведения кастомного сервера (см. «Offline / replay mode» выше) покрывает две записанные статьи и независящий от запроса поиск arXiv — а не универсальную запись/воспроизведение произвольных запросов. Собственные вызовы Obsidian и OpenRouter в `agent/` этим режимом не затрагиваются и по-прежнему требуют живого доступа. Пороги оценки влияния/релевантности — значения `.env`, а не параметры, изменяемые во время выполнения без перезапуска.
## Отложено (отмечено, не снято)
* Открытие жёстко заданных порогов как более богатой конфигурации времени выполнения, чем `.env`.
## Демо / чек-лист защиты
* [ ] `uv run python -m custom_server.server` запускается автономно; `list_tools` сырого MCP-клиента показывает все 3 инструмента.
* [ ] `uv run pytest custom_server/tests agent/tests` — все тесты зелёные, сеть мокирована.
* [ ] `CUSTOM_SERVER_OFFLINE=1 uv run python -m custom_server.server` запускается и обслуживает все 3 вызова инструмента без живого сетевого доступа и API-ключей (см. «Offline / replay mode»).
* [ ] Завести в демо-хранилище заметку с кратким описанием концепта (например, "attention mechanisms"), названную, например, "Transformers Concept Note".
* [ ] `uv run python -m agent.graph "Find papers related to my 'Transformers
Concept Note'"` — полный живой прогон: определяет ссылку на заметку, читает
заметку, ищет в arxiv, оценивает кандидатов, фильтрует, находит
ключевые цитирования, записывает `"<note> — Related Papers"` в обратною сторону —
в хранилище.
* [ ] Показать обе MCP-связи, формирующие итоговый результат: (противопоставя) внешней заметкой-записью цитируются данные из arXiv/OpenAlex (кастомный сервер) и содержимое исходной концепт-заметки (Obsidian).
* [ ] Демонстрация «недостаточно информации»: запустить с запросом, который не упоминает заметку (например, `"What's a transformer?"`) — показать, что агент останавливается и печатает
"Not enough information..." без вызова Obsidian. Затем привести близко
записи с почти пустым содержимым — показать, что он останавливается после читания
записи, до обращения к arXiv.
* [ ] Демонстрация сбоя, Obsidian: остановить затычку Local REST API plugin (или использовать неверный
`OBSIDIAN_API_KEY` / несуществующий разметкий заголовок) — показать инициализацию
уагента выдаст различимую ошибку, а не молчаливый пустой результат.
* [ ] Демонстрация сбоя, кастомный сервер: вызвать `search_arxiv_papers` с
недопустимой категорией, или `find_foundational_citations` для arXiv ID,
отсутствующего в OpenAlex — показать `ValueError` / `PaperNotFoundError`
соответственно, в отличие от корректного пустого результата.Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceThis MCP server enables users to search for scientific papers on arXiv and retrieve detailed metadata for specific papers. It provides tools to perform search queries and fetch in-depth information using paper IDs.3Apache 2.0
- FlicenseAqualityDmaintenanceA streamlined MCP server that connects AI assistants to arXiv's vast collection of academic papers, enabling search, retrieval, and analysis of research papers.71
- FlicenseNot gradedqualityDmaintenanceAn advanced scholarly research MCP server that enables AI assistants to discover, fetch, process, and manage academic papers across multiple sources like arXiv, PubMed, and Semantic Scholar, with capabilities for summarization, citation analysis, and concept relationship extraction.1
- FlicenseNot gradedqualityCmaintenanceAn MCP server that enables AI assistants to search arXiv papers, retrieve metadata, and access PDFs.
Related MCP Connectors
Academic research MCP server for paper search, citation checks, graphs, and deep research.
Driflyte MCP server which lets AI assistants query topic-specific knowledge from web and GitHub.
An MCP server for deep research or task groups
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/tbaraniuk/arxiv-agent-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server