Skip to main content
Glama
Romumrn

ViromeChat MCP server

by Romumrn

MCP-сервер ViromeChat

Сервер FastMCP, который владеет всем доступом к наборам данных, внешними вызовами API и бизнес-логикой для Viromech@t. Клиент (бэкенд FastAPI / React-фронтенд, в отдельном репозитории viromechat) никогда не обращается напрямую ни к dataframe, ни к учётным данным S3, ни к именам колонок — он только общается с этим сервером по MCP/HTTP, обобщённо, читая те инструменты и ресурсы, которые сервер публикует в данный момент.

Этот репозиторий — автономный дом этого сервера. У него нет зависимости от репозитория приложения; единственный контракт между ними — набор MCP-инструментов и ресурсов, описанный ниже, который бэкенд использует через переменную окружения MCP_SERVER_URL.


Запуск

Предварительные требования: набор данных таксономии (data/TAXONOMY.csv, ~327 МБ) хранится через Git LFS. Выполните git lfs install один раз на каждой машине перед клонированием или git lfs pull после клонирования, чтобы материализовать его.

Локально

git lfs pull                      # fetch data/TAXONOMY.csv
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env              # fill in your S3 credentials
python server_mcp.py

Docker

cp .env.example .env              # fill in your S3 credentials
docker compose up --build

В любом случае запускается HTTP-сервер на 0.0.0.0:8000, endpoint MCP на /mcp (http://localhost:8000/mcp — именно это бэкенд указывает через MCP_SERVER_URL). При запуске он:

  1. Полностью загружает data/TAXONOMY.csv в память как df_taxo.

  2. Загружает два файла описания колонок (data/v@_columns_description.csv и data/TAXONOMY_columns_description.json), которые лежат в основе двух ресурсов MCP, описанных ниже.

  3. Открывает in-memory-соединение DuckDB, устанавливает расширения httpfs и spatial и регистрирует представление host поверх S3-набора данных Parquet — Parquet-файл никогда не загружается в память; каждый запрос query_host_printer выполняется DuckDB прямо в S3 (отсечение по колонкам и группам строк).

Тесты

pip install pytest
pytest

Вспомогательные тесты покрывают чистые функции (_ok/_fail, построители figure/таблиц, SQL-защиты) и не требуют живого подключения к S3.


Related MCP server: OpenCode LLM Wiki MCP Server

Подключение клиента

Любой MCP-клиент может работать с этим сервером. Бэкенд Viromech@t делает это с помощью fastmcp.Client:

from fastmcp import Client

async with Client("http://localhost:8000/mcp") as mcp:
    tools = await mcp.list_tools()
    result = await mcp.call_tool("wikipedia_search", {"search_term": "Lentivirus"})

Клиент должен динамически находить инструменты и ресурсы (list_tools() / list_reseources()) и выполнять диспетчеризацию по artifact["type"]никогда не захардкоживать имена инструментов или знание о колонках. Именно это держит два репозитория развязанными: добавление инструмента, который переиспользует существующий тип артефакта, не требует изменения клиента.


Ресурсы

Ресурсы — это статическое знание, которое читается один раз, а не то, что LLM «вызывает» как инструмент. Клиент читает их один раз за диалог и встраивает их содержимое в системный промпт.

URI

Содержимое

Источник

resource://datasets/host/schema

JSON-карта {column_name: {description, Type}} для каждой колонки таблицы host

data/v@_columns_description.csv

resource://datasets/taxonomy/schema

Полная JSON-схема (имя, описание, колонки, первичный ключ, определение строки) для df_taxo

data/TAXONOMY_columns_description.json

Добавление нового ресурса (например, третьего набора данных) не требует изменений на клиенте: клиент обнаруживает ресурсы через list_resources() и читает каждый из них универсально.


Контракт ответа

Каждый инструмент возвращает ровно такую структуру, независимо от того, что он делает:

{
  "success": true,           // or false
  "content": "human-readable text — this is what the LLM reads back as the tool result",
  "artifacts": [ ... ]        // structured extras the client can render; [] if none
}

При ошибке content содержит сообщение об ошибке (где возможно — с подсказкой, как повторить), а artifacts пуст. Две вспомогательные функции _ok(content, artifacts) / _fail(content) в начале server_mcp.py формируют эту структуру — всегда используйте их вместо ручной сборки dict.

Типы артефактов

type

Источник производит

Структура

Использование клиентом

url

wikipedia_search

{"type": "url", "url": "..."}

Ссылка на Википедию в панели «Sources»

pubmed

pubmed_search

{"type": "pubmed", "pmids": [123, 456]}

Ссылки на PubMed + белый список PMID для защиты от галлюцинаций

ncbi_taxonomy

ncbi_taxonomy_search

{"type": "ncbi_taxonomy", "url": "...", "tax_id": "..."}

Ссылка на NCBI Taxonomy в панели «Sources»

table

query_host_sql, query_dataframe

{"type": "table", "rows": [...], "columns": [...], "total_rows": N}

Отслеживается как выполненный SQL/код в «Sources»; rows ограничен preview_rows

plotly

create_visualization, create_map

{"type": "plotly", "figure": {...}} (из fig.to_json(), разобранного обратно в dict)

Отрисованный график plotly

Клиент выполняет диспетчеризацию только по artifact["type"] — никогда по имени инструмента. Добавление инструмента, который переиспользует существующий тип артефакта (например, ещё одного инструмента, возвращающего "table"), вообще не требует изменений в клиенте.


Инструменты

wikipedia_search(search_term: str, wikipedia_limit: int = 4000) -> dict

Находит страницу в Википедии; при отсутствии точного совпадения по названию возвращает ближайший результат полнотекстового поиска (в содержимом это помечено как «нечёткое совпадение»). Возвращает артефакт url.

pubmed_search(query: str, max_results: int = 5) -> dict

Выполняет поиск по PubMed (NCBI E-utilities esearch + efetch, db=pubmed) и возвращает для каждого результата название, авторов, журнал, год, аннотацию, DOI и PMID. Возвращает артефакт pubmed со всеми реально найденными PMID — это единственный источник правды для защиты клиента от галлюцинаций по PMID.

ncbi_taxonomy_search(name: str) -> dict

Сопоставляет любое название организма — аббревиатуру, бытовое называние или научное название — с базой данных NCBI Taxonomy (E-utilities, db=taxonomy). Для каждого совпадения возвращает: научное название, ранг (вид/род/семейство/…), отдел, полную родословную и известные синонимы/аббревиатуры. Это авторитетный способ превратить HIV в Human immunodeficiency virus 1 / род Lentivirus или проверить, является ли название родом или семейством, не полагаясь на формулировку Википедии. Возвращает артефакт ncbi_taxonomy для лучшего совпадения.

Примечание к реализации: XML NCBI efetch содержит по одному <Taxon> на каждый ранг-предок внутри элемента <LineageEx> каждого результата. Парсер перебирает только root.findall("Taxon") (прямые потомки) — использование .//Taxon захватило бы каждого предка как отдельное совпадение.

query_host_sql(sql: str, preview_rows: int = 50) -> dict

Выполняет read-only запрос SELECT к представлению host (набор S3 Parquet) и возвращает артефакт table. Это обязательный первый шаг перед тем, как query_dataframe, create_visualization или create_map смогут использовать df_host, — эти инструменты работают с результатом последнего вызова query_host_sql (ctx.last_host_result), а не с полным набором данных.

Защиты, действующие перед выполнением:

  • Разрешается только одна операция SELECTINSERT/UPDATE/DELETE/DDL/PRAGMA/... отклоняются _FORBIDDEN_SQL_KEYWORDS.

  • Голый SELECT * отклоняется сразу. В host около ~65 колонок, включая тяжёлое бинарное поле geometry; выбор всех колонок для всех подходящих строк через S3 — причина многотаимных таймаутов до ввода этой защиты. Вызывающий должен проецировать только те колонки, которые ему нужны.

  • Координаты находятся в нативной колонке точек типа GEOMETRY, а не в обычных lat/lon — извлекайте их как ST_X(geometry) AS lon, ST_Y(geometry) AS lat (расширение spatial загружается при старте).

query_dataframe(code: str, preview_rows: int = 50) -> dict

Выполняет pandas-код, получая в области видимости df_taxo, df_host (= ctx.last_host_result, либо точную ошибку, если query_host_sql ещё не вызывался), pd и np. Код должен присвоить DataFrame переменной result. Возвращает артефакт table.

create_visualization(code: str) -> dict

Та же среда выполнения, что и у query_dataframe, плюс px/go. Код должен присвоить Plotly figure переменной fig. Пустые фигуры (0 точек данных) отклоняются с подсказкой, а не молча превращаются в пустой график. Возвращает артефакт plotly.

create_map(code: str) -> dict

То же, что и create_visualization, но требует px.scatter_mapbox(...) (никогда не scatter_map) и того, что предыдущий вызов query_host_sql уже извлёк lon/lat из geometry. Возвращает артефакт plotly.

Обязательный идентификатор образца: результирующая фигура отклоняется, если в hover_data не встречается primary_id (номер BioSample) — каждая нанесённая на график точка должна прослеживаться до конкретного образца. Это проверяется в коде (_check_hover_has_column(fig, "primary_id")), а не просто упоминается в docstring — карта без него получает жёсткий _fail(...).


Расширение сервера

Чтобы добавить новый инструмент:

  1. Напишите его как обычную функцию с декоратором @mcp.tool, возвращающую _ok(content, artifacts) или _fail(content) — никогда не вручную собранный dict.

  2. Если инструмент создаёт нечто, что клиент должен отрисовать особым образом (ссылку, таблицу, фигуру), используйте существующий type артефакта из таблицы выше, когда форма подходит, — это ноль изменений в клиенте. Вводите новый type (и подключайте его в диспетчерский цикл клиента), только если форма действительно новая.

  3. Все правила использования, предостережения и примеры пишете в docstring инструмента. Она передаётся LLM дословно как описание инструмента — это единственное место, где должна находиться специфичная для данных информация.

  4. Если инструменту нужна настройка по умолчанию, принимаемая в интерфейсе (как preview_rows или wikipedia_limit), дайте параметру именно то имя — клиент применит подходящую экспертную настройку любому инструменту, в JSON-схеме которого заявлен параметр с таким именем.


Конфигурация

server_mcp.py читает .env (см. .env.example) при импорте через load_env_file() из mcp_config.py:

Переменную

Обязательно

По умолчанию

Описание

ENDPOINT

да

Хост S3-совместимого endpoint

ACCESS_KEY

да

Ключ доступа S3

SECRET_KEY

да

Секретный ключ S3

BUCKET

да

Имя бакета S3

VIRTUAL_DATASET

да

*.parquet

Ключ объекта Parquet-набора внутри бакета

REGION

нет

fr

Регион S3

S3_URL_STYLE

нет

path

Настройка DuckDB s3_url_style

TAXO_DB_PATH

нет

data/TAXONOMY.csv

Локальный путь к CSV-файлу таксономии

Несекретные параметры находятся в mcp_config.py.

F
license - not found
Not graded
quality - not tested
C
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

View all related MCP servers

Related MCP Connectors

  • Free public MCP for AI agents — 193 tools, 44 workflows. No API key.

  • Real-time Amazon, WIPO & PACER data for AI agents — 19 tools via the MCP protocol.

  • OCR, transcription, file extraction, and image generation for AI agents via MCP.

View all MCP Connectors

Latest Blog Posts

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/Romumrn/viromeatlas_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server