Skip to main content
Glama
Romumrn

ViromeChat MCP server

by Romumrn

ViromeChat MCP server

Viromech@t를 위해 모든 데이터셋 액세스, 외부 API 호출, 비즈니스 로직을 담당하는 FastMCP 서버입니다. 클라이언트(별도 viromechat 저장소에 있는 FastAPI 백엔드 / React 프런트)는 데이터프레임, S3 자격 증명, 또는 컬럼 이름을 직접 건드리지 않습니다. 그 대신 MCP/HTTP를 통해 이 서버와만 통신하며, 현재 서버가 게시하는 도구와 리소스를 읽어서 제네릭하게 동작합니다.

이 저장소는 그 서버가 독립적으로 존재하는 저장소입니다. 앱 저장소에 대한 어떤 의존성도 없습니다. 둘 사이의 유일한 계약은 아래에 문서화된 MCP 도구/리소스 세트이며, 백엔드는 MCP_SERVER_URL 환경 변수를 통해 이를 소비합니다.


실행하기

전제 조건: taxonomy 데이터셋(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.csvdf_taxo로 메모리에 완전히 로드합니다.

  2. 아래 두 MCP 리소스를 뒷받침하는 두 컬럼 설명 파일(data/v@_columns_description.csvdata/TAXONOMY_columns_description.json)을 로드합니다.

  3. 인메모리 DuckDB 연결을 열고 httpfsspatial 확장을 설치한 다음, S3 Parquet 데이터셋 위에 host 뷰를 등록합니다 — Parquet 파일은 절대 메모리에 로드되지 않습니다. 모든 query_host_sql 호출은 DuckDB가 S3로 푸시다운합니다(컬럼/행그룹 프루닝).

테스트

pip install pytest
pytest

헬퍼 테스트는 순수 함수(_ok/_fail, figure/table 빌더, 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_resources()) 그리고 artifact["type"]에 따라 dispatch해야 합니다 — 도구 이름이나 컬럼 지식을 절대 하드코딩하면 안 됩니다. 덕분에 두 저장소가 분리됩니다: 기존 artifact 타입을 재사용하는 도구를 여기에 추가해도 클라이언트 변경은 필요 없습니다.


리소스

펼리는 대상 등 정적이고 한 번 읽는 지식입니다 — 도구처럼 LLM이 "호출"하는 것이 아닙니다. 클라이언트는 대화당 한 번 읽어 그 내용을 시스템 프롬프트에 포함합니다.

URI

콘텐츠

소스

resource://datasets/host/schema

host 테이블의 모든 컬럼에 대한 JSON 맵 {column_name: {description, Type}}

data/v@_columns_description.csv

resource://datasets/taxonomy/schema

df_taxo의 전체 JSON 스키마(name, description, columns, primary key, row definition)

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는 비어 있습니다. server_mcp.py 상단에 있는 _ok(content, artifacts) / _fail(content) 두 헬퍼가 이 형태를 만들므로, 이들을 항상 사용하고 자체 dict를 수기 생성하지 마세요.

아티팩트 타입

type

생성 도구

형태

클라이언트가 사용하는 방식

url

wikipedia_search

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

"Sources" 패널의 Wikipedia 링크

pubmed

pubmed_search

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

PubMed 링크 + PMID 안티셉션 가드용 PMID 화이트리스트

ncbi_taxonomy

ncbi_taxonomy_search

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

"Sources" 패널의 NCBI Taxonomy 링크

table

query_host_sql, query_dataframe

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

"Sources"에서 실행된 SQL/코드로 추적됨; rowspreview_rows까지로 제한됨

plotly

create_visualization, create_map

{"type": "plotly", "figure": {...}} (fig.to_json()에서 나와 다시 dict로 파싱된 것)

렌더링된 Plotly 차트

클라이언트는 artifact["type"] 만으로 완전히 dispatch합니다 — 도구 이름이 아니라. 기존 artifact 타입을 재사용하는 도구(예: 또 다른 "table" 반환 도구)를 추가해도 클라이언트 변경이 전혀 없어집니다.


도구

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

Wikipedia에서 페이지를 찾습니다. 정확한 제목 일치가 없으면 가장 근접한 전문(full-text) 검색 결과로 대체합니다(콘텐츠에 "퍼지 매치" 노트로 표시됨). url 아티팩트를 반환합니다.

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

PubMed(NCBI E-utilities의 esearch + efetch, db=pubmed)를 검색하여 각 결과에 대해 제목, 저자, 저널, 연도, 초록, DOI, PMID를 반환합니다. 실제 확인된 모든 PMID를 담은 pubmed 아티팩트를 반환하며, 이는 클라이언트의 PMID 환각조사에 유일한 판단 기준이 됩니다.

ncbi_taxonomy_search(name: str) -> dict

어떤생물학적 이름(약어, 일반명, 학명)이든 NCBI Taxonomy 데이터베이스(E-utilities, db=taxonomy)에 대비해 식별합니다. 모든 일치 항목에 대해 계명, 순위(species/genus/family/...), division(분류군), 전체 lineage(계통), 알려진 동의어/약어를 반환합니다. HIVHuman 개복면역결핍바이러스 1 / 속 Lentivirus로 바꾸거나, 어떤 이름이 속인지 과인지 확인하는 정식 방법입니다. Wikipedia의 표현에 좌우되지 않습니다. 가장 높은 일치 항목에 대해 ncbi_taxonomy 아티팩트를 반환합니다.

구현 사항: NCBI의 efetch XML은 각 결과의 <LineageEx> 내부에 상위 계급(rank)마다 하나씩 <Taxon>을 중첩시킵니다. 파서는 root.findall("Taxon")(직접 자손만)만 반복합니다 — .//Taxon을 쓰면 모든 조상도 별도 일치 결과로 잡히므로 주의하세요.

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

S3 Parquet 데이터셋인 host 뷰에 대해 읽기 전용 SELECT를 실행하고 table 아티팩트를 반환합니다. 이는 query_dataframe, create_visualization, 또는 create_mapdf_host를 사용할 수 있기 위한 필수 전 단계입니다 — 이들 도구는 그 이전에 호출된 query_host_sql 결과(ctx.last_host_result)에 대해서만 동작하며, 절대 전체 데이터셋을 대상으로 하지 않습니다.

실행 전에 적용되는 안전장치:

  • 오직 단일 SELECT 문만 허용됩니다. INSERT/UPDATE/DELETE/DDL/PRAGMA/..._FORBIDDEN_SQL_KEYWORDS에 의해 거부됩니다.

  • 베어 SELECT *는 완전히 거부되어 버립니다. host에는 큰 geometry blob을 포함한 ~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/grp가 추가됩니다. 반드시 fig 변수에 Plotly figure를 할당해야 합니다. 빈 figure(0개의 점)는 안내 메시지와 함께 거부하고, 빈 차트를 조용히 반환하지 않습니다. plotly 아티팩트를 반환합니다.

create_map(code: str) -> dict

create_visualization과 같지만 px.scatter_mapbox(...)을 강제합니다(scatter_map는 아님). 그리고 그 앞 query_host_sql 호출이 이미 geometry에서 lon/lat을 추출해 놓은 상태여야 합니다. plotly 아티팩트를 반환합니다.

필수 샘플 식별자: hover_dataprimary_id(BioSample accession)가 나타나지 않으면 결과 figure는 거부됩니다. 그려진 모든 점은 정확한 샘플까지 거슬러 갈 수 있어야 합니다. 이것은 docstring에 요청한 사항일 뿐 아니라 코드에서도 강제됩니다 (_check_hover_has_column(fig, "primary_id")). 이 식별자가 없는 지도는 무조건 _fail(...)입니다.


서버 확장

새 도구를 추가하려면:

  1. @mcp.tool 데코레이션된 순수 함수로 작성하고, 수제 dict가 아니라 _ok(content, artifacts) 또는 _fail(content)을 반환합니다.

  2. 클라이언트가 특별히 렌더링해야 할 것을 만들어낸다면(링크, 표, figure) 기존 artifact type 중에 해당 형태를 지원하는 것을 재사용하세요. 그러면 클라이언트 변경이 전혀 필요 없습니다. 정말 새로운 형태일 때만 새 type을 만들고 그에 맞게 클라이언트 디스패치 루프도 수정해야 합니다.

  3. 모든 사용 규칙, 주의사항, 예제는 도구의 docstring에 넣어라. 이 docstring은 그대로 LLM에게 도구의 설명으로 전달됩니다. 데이터셋별 안내가 들어갈 유일한 곳이 바로 여기입니다.

  4. 도구가 UI로 설정 가능한 기본값(예: preview_row 또는 wikipedia_limit)이 필요하면 해당 매개변수를 그 이름으로 지정하세요. 클라이언트는 그 이름이 JSON 파라미터에 선언된 어떤 도구든 그에 맞는 전문가 설정을 적용할 수 있으므로.


환경설정

server_mcp.py는 import 시 mcp_config.pyload_env_file()를 통해 .env( .env.example 참고)를 읽습니다:

Variable

필수

기본값

의미

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

taxonomy 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