Skip to content

MCP Server Reference

BioMCP exposes one execution tool (biomcp) and a current resource inventory centered on the help guide. This page documents the stable MCP contract and executes lightweight checks against the source tree.

Runtime Surface

BioMCP exposes two MCP entrypoints:

  • stdio: biomcp serve
  • remote Streamable HTTP: biomcp serve-http

The canonical remote endpoint is /mcp. Lightweight probe routes are /health, /readyz, and /.

from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
assert "StreamableHttpService" in shell
assert '.nest_service("/mcp", service)' in shell
assert '.route("/health", get(health_handler))' in shell
assert '.route("/readyz", get(health_handler))' in shell
assert '.route("/", get(index_handler))' in shell

Capability Advertisement

The server must advertise both tools and resources.

Capability Required
tools enabled
resources enabled
from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
assert "enable_tools()" in shell
assert "enable_resources()" in shell

Tool Description Contract

The runtime biomcp description is generated from src/cli/list_reference.md, but the build step emits an MCP-safe read-only subset. That sanitized description keeps the catalog-only study download --list form, but it must not advertise study download <study_id> or the combined CLI syntax study download [--list] [<study_id>]. CLI-only packaging or mutating commands such as skill install, ema sync, update, and uninstall must not appear in the MCP tool description. CLI-only cache commands such as cache path, cache stats, cache clean, and cache clear reveal workstation-local paths and filesystem context, so they also stay out of the MCP tool description.

from pathlib import Path

repo_root = Path.cwd()
build = (repo_root / "build.rs").read_text()
tests = (repo_root / "tests/test_mcp_contract.py").read_text()

assert "MCP_SHELL_INTRO" in build
assert "read-only biomedical MCP tool" in build
assert "BLOCKED_MCP_DESCRIPTION_TERMS" in build
assert "`skill install`" in build
assert "`ema sync`" in build
assert "`update [--check]`" in build
assert "`uninstall`" in build
assert "study download --list" in build
assert "study download [--list] [<study_id>]" in build
assert 'assert "study download --list" in description' in tests
assert 'assert "study download [--list] [<study_id>]" not in description' in tests
assert 'test_cache_stats_is_rejected_in_mcp_mode' in tests
assert 'test_cache_clean_is_rejected_in_mcp_mode' in tests
assert 'test_cache_clear_is_rejected_in_mcp_mode' in tests

Tool Response Content

The biomcp tool keeps non-chart calls text-only. In MCP mode, charted study commands return two success content blocks in order:

  • text with the normal markdown/table output
  • image with mimeType = "image/svg+xml" and base64-encoded SVG data

MCP chart calls do not write files. If the caller supplies --output or -o, the tool returns a tool error instructing the caller to consume the inline image instead.

Alias fallback is the main exception to the usual CLI stderr contract: failed get gene / get drug alias suggestions are returned to MCP as structured JSON text content with _meta.alias_resolution and _meta.next_commands so agents can apply their own retry policy without parsing markdown.

from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
cli = (repo_root / "src/cli/mod.rs").read_text()

assert "crate::cli::execute_mcp(args)" in shell
assert "CallToolResult::success" in shell
assert 'Content::image(encoded, "image/svg+xml")' in shell
assert "MCP chart responses do not support --output/-o" in cli
assert 'annotations(title = "BioMCP", read_only_hint = true)' in shell

Read-only Allowlist

The MCP biomcp tool accepts read-only CLI commands, including discover and the exact study download --list catalog lookup. Mutating commands remain blocked. Cache-family commands such as cache path, cache stats, cache clean, and cache clear are also rejected because they reveal workstation-local paths and filesystem context. In particular, study download <study_id> is rejected because installation performs network and filesystem writes into the local study directory; operators should run study installs directly via the CLI, outside MCP.

from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
tests = (repo_root / "tests/test_mcp_contract.py").read_text()
assert '"discover" => true' in shell or '| "discover" => true' in shell
assert '"study" => {' in shell
assert '"download" => args.len() == 4 && args[3] == "--list"' in shell
assert "discover/skill" in shell or "discover/skill)." in shell
assert 'assert "study download --list" in description' in tests
assert 'test_mutating_study_download_is_rejected_in_mcp_mode' in tests
assert '"BioMCP allows read-only commands only" in result.content[0].text' in tests
assert 'test_cache_path_is_rejected_in_mcp_mode' in tests
assert '"CLI-only over MCP" in result.content[0].text' in tests
assert '"workstation-local filesystem paths" in result.content[0].text' in tests

Resource Catalog

Current builds always publish the help resource and one markdown resource per embedded skill use-case:

URI Name Notes
biomcp://help BioMCP Overview Always listed
biomcp://skill/<slug> Pattern: ... Listed when the matching embedded worked example exists
from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
use_cases_dir = repo_root / "skills" / "use-cases"
assert "RESOURCE_HELP_URI" in shell
assert 'RawResource::new(RESOURCE_HELP_URI, "BioMCP Overview")' in shell
assert "list_use_case_refs()" in shell
assert use_cases_dir.exists()
assert list(use_cases_dir.glob("*.md"))

Resource Read Mapping

  • biomcp://help maps to show_overview().
  • biomcp://skill/<slug> maps to show_use_case(<slug>) when an embedded worked example exists.
  • All successful reads return text/markdown.
from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
assert "show_overview()" in shell
assert 'if let Some(slug) = uri.strip_prefix("biomcp://skill/")' in shell
assert "show_use_case(slug)" in shell
assert 'with_mime_type("text/markdown")' in shell

Unknown URI Behavior

Unknown resource URIs must return an MCP resource-not-found error and include a helpful message.

from pathlib import Path

repo_root = Path.cwd()
shell = (repo_root / "src/mcp/shell.rs").read_text()
assert "resource_not_found" in shell
assert "Unknown resource:" in shell

Companion Runtime Tests

Protocol-level checks are implemented in Python integration tests:

  • tests/conftest.py
  • tests/test_mcp_contract.py
  • tests/test_mcp_http_surface.py
  • tests/test_mcp_http_transport.py

These tests validate both transport modes:

  • biomcp serve stdio initialize/resource behavior,
  • stdio charted-study text + image/svg+xml responses and MCP --output rejection,
  • Streamable HTTP initialize/tools/list/tools/call,
  • Streamable HTTP charted-study text + image/svg+xml responses,
  • GET /, GET /health, and GET /readyz,
  • invalid URI error semantics.
from pathlib import Path

repo_root = Path.cwd()
assert (repo_root / "tests/conftest.py").exists()
assert (repo_root / "tests/test_mcp_contract.py").exists()
assert (repo_root / "tests/test_mcp_http_surface.py").exists()
assert (repo_root / "tests/test_mcp_http_transport.py").exists()