ViromeChat MCP server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ViromeChat MCP serverSearch PubMed for recent papers on bat coronaviruses"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ViromeChat MCP server
A FastMCP server that owns all dataset access, external API calls,
and business logic for Viromech@t. The client (the
FastAPI backend / React front, in the separate viromechat repo) never touches a dataframe, an S3
credential, or a column name directly — it only talks to this server over MCP/HTTP, generically,
by reading whatever tools and resources it currently publishes.
This repo is the standalone home of that server. It has no dependency on the app repo; the only
contract between them is the set of MCP tools/resources documented below, consumed by the backend
via its MCP_SERVER_URL env var.
Running it
Prerequisites: the taxonomy dataset (data/TAXONOMY.csv, ~327 MB) is stored via
Git LFS. Run git lfs install once per machine before cloning, or
git lfs pull after cloning, to materialize it.
Local (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.pyDocker
cp .env.example .env # fill in your S3 credentials
docker compose up --buildEither way it starts an HTTP server on 0.0.0.0:8000, MCP endpoint at /mcp
(http://localhost:8000/mcp — this is what the backend points MCP_SERVER_URL at). On startup it:
Loads
data/TAXONOMY.csvfully into memory asdf_taxo.Loads the two column-description files (
data/v@_columns_description.csvanddata/TAXONOMY_columns_description.json) that back the two MCP resources below.Opens an in-memory DuckDB connection, installs the
httpfsandspatialextensions, and registers ahostview over the S3 Parquet dataset — the Parquet file is never loaded into memory; everyquery_host_sqlcall is pushed down to S3 by DuckDB (column/row-group pruning).
Tests
pip install pytest
pytestThe helper tests exercise the pure functions (_ok/_fail, figure/table builders, SQL guards) and
need no live S3 connection.
Related MCP server: OpenCode LLM Wiki MCP Server
Integrating a client
Any MCP client can consume this server. The Viromech@t backend does it with a 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"})The client should discover tools and resources dynamically (list_tools() / list_resources())
and dispatch on artifact["type"] — never hard-code tool names or column knowledge. That is
what keeps the two repos decoupled: adding a tool here that reuses an existing artifact type needs
no client change.
Resources
Resources are static, read-once knowledge — not something the LLM "calls" like a tool. The client reads them once per conversation and folds their content into the system prompt.
URI | Content | Source |
| JSON map |
|
| Full JSON schema (name, description, columns, primary key, row definition) of |
|
Adding a new resource (e.g. a third dataset) requires no client-side change: the client discovers
resources via list_resources() and reads each one generically.
The response contract
Every tool returns exactly this shape, regardless of what it does:
{
"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
}On failure, content holds the error message (with retry guidance where possible) and
artifacts is empty. The two helpers _ok(content, artifacts) / _fail(content) at the top of
server_mcp.py build this shape — always use them instead of hand-rolling a dict.
Artifact types
| Emitted by | Shape | Consumed by the client as |
|
|
| Wikipedia link in the "Sources" panel |
|
|
| PubMed links + PMID whitelist for the hallucination guard |
|
|
| NCBI Taxonomy link in the "Sources" panel |
|
|
| Tracked as executed SQL/code in "Sources"; |
|
|
| Rendered plotly chart |
The client dispatches purely on artifact["type"] — never on the tool's name. Adding a
tool that reuses an existing artifact type (e.g. another "table"-returning tool) requires no
client change at all.
Tools
wikipedia_search(search_term: str, wikipedia_limit: int = 4000) -> dict
Looks up a page on Wikipedia; falls back to the closest full-text search match if there's no exact
title match (flagged as a "fuzzy match" note in the content). Returns a url artifact.
pubmed_search(query: str, max_results: int = 5) -> dict
Searches PubMed (NCBI E-utilities esearch + efetch, db=pubmed) and returns title, authors,
journal, year, abstract, DOI, and PMID for each hit. Returns a pubmed artifact with every real
PMID found — this is the sole source of truth for the client's PMID hallucination guard.
ncbi_taxonomy_search(name: str) -> dict
Resolves any organism name — acronym, common name, or scientific name — against the NCBI
Taxonomy database (E-utilities, db=taxonomy). Returns, for every match: scientific name, rank
(species/genus/family/…), division, full lineage, and known synonyms/acronyms. This is the
authoritative way to turn HIV into Human immunodeficiency virus 1 / genus Lentivirus, or to
check whether a name is a genus or a family, without depending on Wikipedia's phrasing. Returns an
ncbi_taxonomy artifact for the top match.
Implementation note: NCBI's
efetchXML nests one<Taxon>per ancestor rank inside each result's<LineageEx>. The parser only iteratesroot.findall("Taxon")(direct children) — using.//Taxonwould also pick up every ancestor as if it were a separate match.
query_host_sql(sql: str, preview_rows: int = 50) -> dict
Runs a read-only SELECT against the host view (the S3 Parquet dataset), returning a table
artifact. This is the required first step before query_dataframe, create_visualization, or
create_map can use df_host — those tools operate on the result of the last query_host_sql
call (ctx.last_host_result), never on the full dataset.
Guardrails enforced before execution:
Only a single
SELECTstatement —INSERT/UPDATE/DELETE/DDL/PRAGMA/...are rejected by_FORBIDDEN_SQL_KEYWORDS.Bare
SELECT *is rejected outright.hosthas ~65 columns including a heavygeometryblob; pulling every column for every matching row over S3 is what caused multi-minute timeouts before this guard existed. Callers must project only the columns they need.Coordinates live in a native
GEOMETRYpoint column, not plainlat/lon— extract them withST_X(geometry) AS lon, ST_Y(geometry) AS lat(thespatialextension is loaded at startup).
query_dataframe(code: str, preview_rows: int = 50) -> dict
Executes pandas code with df_taxo, df_host (= ctx.last_host_result, or a clear error if
query_host_sql hasn't been called yet), pd, and np in scope. Must assign a DataFrame to
result. Returns a table artifact.
create_visualization(code: str) -> dict
Same execution environment as query_dataframe, plus px/go. Must assign a Plotly figure to
fig. Rejects empty figures (0 data points) with a guidance message rather than silently returning
a blank chart. Returns a plotly artifact.
create_map(code: str) -> dict
Same as create_visualization, but enforces px.scatter_mapbox(...) (never scatter_map) and
that the preceding query_host_sql call already extracted lon/lat from geometry. Returns a
plotly artifact.
Mandatory sample identifier: the resulting figure is rejected unless primary_id (the
BioSample accession) appears in hover_data — every plotted point must be traceable back to its
exact sample. Enforced in code (_check_hover_has_column(fig, "primary_id")), not just requested
in the docstring — a map missing it is a hard _fail(...).
Extending the server
To add a new tool:
Write it as a plain function decorated with
@mcp.tool, returning_ok(content, artifacts)or_fail(content)— never a hand-built dict.If it produces something the client should render specially (a link, a table, a figure), reuse an existing artifact
typefrom the table above whenever the shape fits — this means zero client changes. Only invent a newtype(and wire it into the client's dispatch loop) if the shape is genuinely new.Put every usage rule, caveat, and example in the tool's docstring. It is sent verbatim to the LLM as the tool's description — this is the only place dataset-specific guidance should live.
If the tool needs a UI-configurable default (like
preview_rowsorwikipedia_limit), just name the parameter that; the client applies the matching expert setting to any tool whose JSON schema declares a parameter with that name.
Configuration
server_mcp.py reads .env (see .env.example) at import time, via
load_env_file() from mcp_config.py:
Variable | Required | Default | Meaning |
| yes | — | S3-compatible endpoint hostname |
| yes | — | S3 access key |
| yes | — | S3 secret key |
| yes | — | S3 bucket name |
| yes |
| Object key of the Parquet dataset inside the bucket |
| no |
| S3 region |
| no |
| DuckDB |
| no |
| Local path to the taxonomy CSV |
Non-secret settings live in mcp_config.py.
This server cannot be installed
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 gradedqualityCmaintenanceEnables AI agents to discover and execute tools via a secure MCP server with JWT authentication, RBAC, rate limiting, and audit logging.1MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with a persistent knowledge graph backend using MCP tools for reading, searching, and analyzing wiki pages with vector search and graph algorithms.4
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to query live schema, lineage, and query-context across data warehouses, dbt projects, orchestration systems, and BI tools via MCP tools.Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to access a unified catalog of tools from various APIs (OpenAPI, GraphQL, MCP, Google Discovery) through the MCP protocol.MIT
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.
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/Romumrn/viromeatlas_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server