Skip to main content
Glama
Romumrn

ViromeChat MCP server

by Romumrn

ViromeChat MCP サーバー

データセットへのアクセス、外部 API 呼び出し、ビジネスロジックのすべてを担う FastMCP サーバーです。Viromech@t のために動作します。クライアント(別リポジトリ viromechat 内の FastAPI バックエンド / React フロントエンド)は、データフレーム、S3 認証情報、カラム名に直接触れることは一切ありません。MCP / HTTP 経由で、このサーバーが現在公開しているツールやリソースを読み取るという形で、汎用的に通信するだけです。

このリポジトリは、そのサーバーを単体で管理する場所です。アプリのリポジトリには依存しません。両者の間の唯一の契約は、以下に文書化された MCP ツール/リソース一式であり、バックエンドは MCP_SERVER_URL 環境変数を介してこれを利用します。


実行方法

前提条件: 分類学データセット(data/TAXONOMY.csv、約 327 MB)は Git LFS で保存されています。クローンする前に各マシンで一度 git lfs install を実行するか、クローン後に git lfs pull を実行して、ファイルを具体化してください。

ローカル(Python)

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

どちらの方法でも、0.0.0.0:8000 で HTTP サーバーが起動し、MCP エンドポイントは /mcp(http://localhost:8000/mcp — バックエンドが MCP_SERVER_URL に指定する場所)になります。起動時には:

  1. data/TAXONOMY.csv 全体をメモリにロードし、df_taxo として保持します。

  2. 下記の 2 つの MCP リソースを支える、2 つのカラム説明ファイル(data/v@_columns_description.csv と data/TAXONOMY_columns_description.json)を読み込みます。

  3. インメモリ DuckDB 接続を開き、httpfs と spatial 拡張をインストールして、S3 Parquet データセット上に host ビューを登録します。Parquet ファイルがメモリにロードされることはありません。query_host_sql の呼び出しはすべて DuckDB によって S3 にプッシュダウンされます(カラム/行グループのプルーニングが行われます)。

テスト

pip install pytest
pytest

ヘルパーテストは純粋関数(_ok / _fail、figure / table のビルダー、SQL ガード)を検証するもので、実際の S3 接続は必要ありません。


Related MCP server: Alma Atlas

クライアントの組み込み

任意の 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_resources())、artifact["type"] でディスパッチする必要があります。ツール名やカラムの知識をハードコードしてはいけません。これが 2 つのリポジトリの分離を保っています。既存のアーティファクトタイプを再利用するツールをここに追加しても、クライアント側の変更は不要です。


リソース

リソースは静的で一度だけ読み込まれる知識であり、ツールのように LLM が「呼び出す」ものではありません。クライアントは会話ごとにリソースを一度読み取り、その内容をシステムプロンプトに組み込みます。

URI

内容

ソース

resource://datasets/host/schema

JSON マップ {column_name: {description, Type}}(host テーブルの全カラム分)

data/v@_columns_description.csv

resource://datasets/taxonomy/schema

df_taxo の完全な JSON スキーマ(名前、説明、カラム、主キー、行定義)

data/TAXONOMY_columns_description.json

新しいリソース(例: 3 つ目のデータセット)を追加しても、クライアント側の変更は不要です。クライアントは 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 は空になります。server_mcp.py の先頭にある 2 つのヘルパー _ok(content, artifacts) / _fail(content) がこの形を構築します。手書きの dict ではなく、常にこれらを使ってください。

アーティファクトタイプ

type

生成元

形状

クライアント側での利用用途

url

wikipedia_search

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

くSivesources」パネル内の Wikipedia リンク

pubmed

pubmed_search

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

PubMed リンク + 幻覚ガード用の PMID ホワイトリスト

ncbi_taxonomy

ncbi_taxonomy_search

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

for "Sources" リンクパネルのNCBI Taxonomy リンク

table

query_host_sql, query_dataframe

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

"Sources" で実行済み SQL/コードとして追跡。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

Wikipedia 上のページを検索します。タイトルが完全一致しない場合は、最も近い全文検索結果にフォールバックし、それはコンテンツ内に「fuzzy match」ノートとして記されます。url アーティファクトを返します。

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

PubMed(NCBI E-utilities の esearch + efetch、db=pubmed)を検索し、各ヒットの title、authors、journal、year、abstract、DOI、PMID を返します。実際に見つかったすべての PMID を含む pubmed アーティファクトを返します。これは、クライアントの PMID 幻覚防止の唯一の情報源です。

ncbi_taxonomy_search(name: str) -> dict

あらゆる生物名、すなわち略称・通用名・学名を、NCBI Taxonomy データベース(E-utilities、db=taxonomy)に対して解決します。一致する各項目について、学名、ランク(species / genus / family / …)、division、完全な系統、既知の別名・省略形を返します。これは、Wikipedia の表現に頼らずに、HIV を Human immunodeficiency Virus 1 や属 Lentivirus に変換したり、名前が属なのか, Family な のを確認したりするための、信頼できる方法です。最上位一致に対応する ncbi_taxonomy アーティファクトを返します。

実装上の注記: NCBI の efetch XML は、各結果の <LineageEx> 内に、それぞれの祖先ランクごとに 1 つの <Taxon> をネストします。パーサーは root.findall("Taxon")(直接の子のみ)を走査します。.//Taxon を使うと、すべての祖先も別の一致として拾われるためです。

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

host ビュー(S3 Parquet データセット)に対して読み取り専用の SELECT を実行し、table アーティファクトを返します。これは、query_dataframe、create_visualization、create_map が df_host を使用できるようにするための必須の最初のステップです。これらのツールは、最後の query_host_sql 呼び出しの結果(ctx.last_host_result)に対して動作し、データセット全体に対してではありません。

実行前に実施されるガードレール:

  • 単一の SELECT 文のみ許可されます。INSERT / UPDATE / DELETE / DDL / PRAGMA /... は _FORBIDDEN_SQL_KEYWORDS で拒否されます。

  • 裸の SELECT * は完全に拒否されます。 host には重い geometry ブロブを含む約 65 カラムがあります。S3 から一致するすべての行の全カラムを取得すると、このガードが存在する前は数分間のタイムアウトが発生しました。呼び出し側は必要なカラムだけに投影しなければなりません。

  • 座標は素の lat / lon ではなく、ネイティブの GEOMETRY ポイントカラムにあります。ST_X(geometry) AS lon, ST_Y(geometry) AS lat で抽出してください(spatial 拡張は起動時にロードされます)。

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

df_taxo、df_host(ctx.last_host_result と等価。まだ query_host_sql が呼び出されていない場合は明確なエラー)、pd、np をスコープに含む pandas コードを実行します。result に DataFrame を代入する必要があります。table アーティファクトを返します。

create_visualization(code: str) -> dict

query_dataframe と同じ実行環境に、加えて px / go があります。Plotly figure を fig に代入する必要があります。空の figure(データポイント 0)は、黙って空のチャートを返すのではなく、ガイダンスメッセージとともに拒否されます。plotly アーティファクトを返します。

create_map(code: str) -> dict

create_visualization と同じですが、px.scatter_mapbox(...) を強制し(scatter_map は不可)、直前の query_host_sql 呼び出しが既に geometry から lon / lat を抽出していることを要求します。plotly アーティファクトを返します。

必須のサンプル識別子: 生成された figure は、primary_id(BioSample アクセッション番号)が hover_data に現れない限り拒否されます。プロットされるすべての点は、正確なサンプルまで遡れる必要があります。これは単に docstring に書かれているだけでなく、コード上でも(_check_hover_has_column(fig, "primary_id"))強制されます。これを含まない map は、確実に失敗する _fail(...) になります。


サーバーの拡張

新しいツールを追加するには:

  1. @mcp.tool で装飾された普通の関数として書き、_ok(content, artifacts) または _fail(content) を返します — 決して手書きの dict ではありません。

  2. クライアントが特別に描画すべきもの(リンク、テーブル、figure)を生成する場合、形が合うときは既存のアーティファクト type を再利用します。これはクライアントの変更ゼロを意味します。形が本当に新しい場合のみ、新しい type を発明します(そしてクライアントのディスパッチループに接続します)。

  3. すべての使用ルール、注意点、例をツールの docstring に書いてください。それがそのままツールの説明として LLM に送信されます。データセット固有のガイダンスはここにだけ置くべきです。

  4. ツールに UI 設定可能なデフォルト(preview_rows や wikipedia_limit のような)が必要な場合は、そのパラメータをそのまま名付けてください。クライアントは、JSON スキーマがその名前のパラメータを宣言している任意のツールに対して、対応するエキスパート設定を適用します。


設定

server_mcp.py は、インポート時に mcp_config.py の load_env_file() を介して .env(.env.example を参照します)を読み取ります。

変数

必須

デフォルト

意味

ENDPOINT

はい

—

S3 互換エンドポイントのホスト名

ACCESS_KEY

はい

—

S3 アクセスキー

SECRET_KEY

はい

—

S3 シークレットキー

BUCKET

はい

—

S3 バケット名

VIRAL_HOST_DATASET

はい

*.parquet

バケット内の Parquet データセットのキー

REGION

いいえ

fr

S3 リージョン

S3_URL_STYLE

いいえ

path

DuckDB の s3_url_style 設定

TAXO_DB_PATH

いいえ

data/TAXONOMY.csv

分類学データセットのローカルパス

非秘密の設定は mcp_config.py にあります。

Related MCP Connectors

Related MCP Servers