{
  "markdown": "# Biolab MCP Server\n\n[![Python Version](https://img.shields.io/badge/python-3.11%2B-blue)](https://python.org)\n[![Go Version](https://img.shields.io/badge/go-1.23%2B-00ADD8)](https://golang.org)\n[![MCP](https://img.shields.io/badge/MCP-1.28.1-purple)](https://modelcontextprotocol.io)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![PyPI](https://img.shields.io/pypi/v/biolab-mcp)](https://pypi.org/project/biolab-mcp/)\n[![Docker](https://img.shields.io/badge/docker-ghcr.io%2Fsrikarjy%2Fbiolab--mcp-blue)](https://github.com/srikarjy/biolab-mcp-server/pkgs/container/biolab-mcp)\n\n> *\"AI agents querying biological databases leave no audit trail. Six months later, nobody can answer: what exact query returned this result, when, and was that paper peer-reviewed at the time? Biolab solves that.\"*\n\nA **dual-implementation** (Python + Go) [MCP](https://modelcontextprotocol.io) server that sits between AI agents and biological/scientific databases (PubMed, Europe PMC, ClinicalTrials.gov, bioRxiv/medRxiv). Every query is intercepted, logged with full retrieval context, and returns a `retrieval_id` that calling systems store alongside their reasoning traces — creating an end-to-end auditable chain from conclusion back to raw source.\n\n**New to MCP?** It's a small, open standard (built by Anthropic) that lets an AI assistant — Claude, ChatGPT, Cursor, etc. — call out to external tools during a conversation. Add Biolab as an MCP server and any of those assistants gains four new abilities: searching PubMed, Europe PMC, ClinicalTrials.gov, and bioRxiv/medRxiv, with every single result permanently logged so it can be checked later.\n\n## Use It Now — No Install\n\nA hosted instance is running at `https://srikarjy025-biolab-mcp.hf.space/mcp`. Point your client at it and you're done — nothing to install, nothing to run locally, nothing to sign up for.\n\n**Claude Code:**\n```bash\nclaude mcp add --transport http biolab https://srikarjy025-biolab-mcp.hf.space/mcp\n```\n\n**Claude Desktop / Cursor** — add this to your MCP config file:\n```json\n{\n  \"mcpServers\": {\n    \"biolab\": {\n      \"url\": \"https://srikarjy025-biolab-mcp.hf.space/mcp\"\n    }\n  }\n}\n```\n(Add `\"headers\": {\"Authorization\": \"Bearer <your key>\"}` alongside `\"url\"` once you have a key — see the rate-limit note below.)\n\nThat's it — `search_pubmed`, `search_europepmc`, `search_clinicaltrials`, `search_biorxiv`, and `get_retrieval` are now available as tools your assistant can call. Every retrieval is written to a hash-chained audit trail you can inspect later (see [Audit Trail Schema](#audit-trail-schema-v2) below).\n\nAlso listed on the [official MCP Registry](https://registry.modelcontextprotocol.io) and [Smithery](https://smithery.ai/servers/srikarjy025/biolab-mcp) if you'd rather discover/install it from there.\n\n**A note on rate limits.** The hosted server is shared and stays open — no signup required for casual use — but callers with no API key share one small, low-throughput budget (1 req/s to PubMed) so no single anonymous user can starve everyone else. If you're doing more than a handful of queries, ask for a key (below) and you get your own isolated, higher budget instead.\n\n**Getting a key:**\n```\nAuthorization: Bearer <your key>\n```\nAdd that header in your client's MCP config (Claude Code: `claude mcp add --transport http biolab <url> --header \"Authorization: Bearer <key>\"`). Keys are issued with `biolab keys create <label>` — see [Managing API Keys](#managing-api-keys) below if you're running your own instance; otherwise ask the maintainer for one.\n\nWant to run your own copy instead (local dev, your own storage, self-hosting)? Keep reading.\n\n## The Problem\n\nA drug discovery team uses an AI agent to research gene targets. The agent queries PubMed 200 times over three days and surfaces a paper claiming gene X is upregulated in pancreatic cancer. A scientist makes a decision based on that. Six months later, during FDA submission:\n\n- What exact query returned that paper?\n- What date was it retrieved?\n- Was it peer-reviewed at retrieval time, or a preprint published later?\n- Did the agent summarize it accurately, or hallucinate details?\n\nWithout Biolab, nobody can answer any of those questions. The retrieval is invisible.\n\n## What Biolab Does\n\nBiolab is an **interception and logging layer**, not a retrieval layer. It doesn't interpret evidence, rank it, or summarize it — it records what happened, verbatim, so an agent's claim can always be traced back to an unforgeable original.\n\n```\nYour AI Agent\n    ↓  MCP tool call (e.g. search_pubmed)\nBiolab MCP Server\n    ↓  HTTP\nSource API (PubMed, Europe PMC, ClinicalTrials.gov, bioRxiv/medRxiv)\n    ↓  paper\nBiolab writes a hash-chained retrieval record to the audit database\n    ↓  paper + retrieval_id\nBack to your agent\n```\n\nThe agent gets the paper it asked for. Biolab gets a permanent, queryable, tamper-evident record of exactly what happened.\n\n## Sources Supported\n\n| Source | MCP Tool | CLI Command | Notes |\n|--------|----------|-------------|-------|\n| **PubMed** | `search_pubmed` | `biolab search` | E-utilities, full XML stored |\n| **Europe PMC** | `search_europepmc` | `biolab search-europepmc` | Free, indexes bioRxiv/medRxiv |\n| **ClinicalTrials.gov** | `search_clinicaltrials` | `biolab search-clinicaltrials` | API v2, condition-based search |\n| **bioRxiv/medRxiv** | `search_biorxiv` | `biolab search-biorxiv` | Date-range pagination (API limit) |\n\nAll sources share a **single audit database** (SQLite locally, or [Turso](https://turso.tech) — a hosted, SQLite-compatible database — in production) with one source-agnostic schema.\n\n## Build It Yourself\n\nYou don't need to know Python or Go to get this running locally — just follow these steps in order. All commands are run in a terminal.\n\n### Prerequisites\n\n- **Python 3.11 or newer** — check with `python3 --version`. Get it from [python.org](https://www.python.org/downloads/) if you don't have it.\n- **Git** — to download (clone) the code. Check with `git --version`.\n\nThat's genuinely it for the Python path — no database server to install, no API keys required (PubMed works anonymously, just at a lower rate limit).\n\n### 1. Get the code\n\n```bash\ngit clone https://github.com/srikarjy/biolab-mcp-server.git\ncd biolab-mcp-server\n```\n\n### 2. Install it\n\n```bash\npython3 -m venv .venv          # creates an isolated Python environment\nsource .venv/bin/activate      # on Windows: .venv\\Scripts\\activate\npip install -e \".[dev]\"        # installs the package + test tools\n```\n\n### 3. Try it\n\n```bash\nbiolab demo --query \"BRCA1 pancreatic cancer\"\n```\n\nThis searches PubMed for real, stores every result in a local `biolab.db` file (created automatically, no setup needed), and prints back the `retrieval_id` for each paper — the same ID an AI agent would get back over MCP.\n\n### 4. Run the test suite (optional, confirms everything works)\n\n```bash\npytest tests/ -v\n```\n\nMost tests hit the real PubMed/Europe PMC/ClinicalTrials.gov APIs on purpose (no mocking) — that's a deliberate project rule, not a bug, so a slow test run is normal.\n\n### 5. Run it as an MCP server (what an AI agent actually connects to)\n\n```bash\npython -m biolab.server\n```\n\nThis starts an HTTP server on `http://localhost:8000/mcp` — point Claude Desktop, Claude Code, or Cursor at that URL exactly like in [Use It Now](#use-it-now--no-install), just with `localhost:8000` instead of the hosted URL.\n\n### 6. Build the Docker image (optional)\n\nIf you'd rather not install Python locally at all:\n\n```bash\ndocker build -f space/Dockerfile -t biolab-mcp .\ndocker run -p 8000:8000 biolab-mcp\n```\n\n(Storage defaults to an ephemeral file inside the container unless you set `TURSO_DATABASE_URL`/`TURSO_AUTH_TOKEN` — see [Environment Variables](#environment-variables) below.)\n\n### Prefer a pre-built release?\n\n```bash\npipx install biolab-mcp      # or: pip install biolab-mcp\n```\n\n```bash\n# Or the Go binary, no Python required at all:\ncurl -L https://github.com/srikarjy/biolab-mcp-server/releases/latest/download/biolab_darwin_arm64.tar.gz | tar xz\n./biolab search \"BRCA1 pancreatic cancer\" --max 3\n```\n\n## Usage\n\n### CLI (Scientist-Friendly)\n```bash\n# Search PubMed\nbiolab search \"BRCA1 pancreatic cancer\" --max 5\n\n# Search Europe PMC\nbiolab search-europepmc \"BRCA1 pancreatic cancer\" --max 5\n\n# Search ClinicalTrials.gov\nbiolab search-clinicaltrials \"pancreatic cancer\" --max 5\n\n# List bioRxiv preprints (no free-text search - API limitation)\nbiolab search-biorxiv neuroscience --max 10\nbiolab search-biorxiv all --server medrxiv --max 10\n\n# Retrieve full audit record\nbiolab get <retrieval_id>\n\n# List recent retrievals\nbiolab list --source pubmed --limit 10\n\n# Export for analysis\nbiolab export evidence.jsonl --source clinicaltrials\n\n# Run demo\nbiolab demo --query \"BRCA1 pancreatic cancer\"\n```\n\n### MCP Tools (Agent-Friendly)\n```json\n// Search any source\n{\"name\": \"search_pubmed\", \"arguments\": {\"query\": \"BRCA1 pancreatic cancer\", \"agent_id\": \"aletheia:advocate\", \"max_results\": 5}}\n{\"name\": \"search_europepmc\", \"arguments\": {\"query\": \"BRCA1 pancreatic cancer\", \"agent_id\": \"aletheia:advocate\", \"max_results\": 5}}\n{\"name\": \"search_clinicaltrials\", \"arguments\": {\"query\": \"pancreatic cancer\", \"agent_id\": \"aletheia:advocate\", \"max_results\": 5}}\n{\"name\": \"search_biorxiv\", \"arguments\": {\"category\": \"neuroscience\", \"agent_id\": \"aletheia:advocate\", \"max_results\": 5, \"server\": \"biorxiv\"}}\n\n// Retrieve full audit record (works for ALL sources)\n{\"name\": \"get_retrieval\", \"arguments\": {\"retrieval_id\": \"uuid-from-search\"}}\n```\n\n### Python API\n```python\nfrom biolab.pubmed_client import search_and_fetch\nfrom biolab.retrieval_log import write_retrieval, get_retrieval\nfrom biolab.db import connect\n\nconn = connect(\"biolab.db\")\npapers = search_and_fetch(\"BRCA1 pancreatic cancer\", 3)\nfor p in papers:\n    record = write_retrieval(conn, query=\"...\", pmid=p.pmid, ...)\n    print(record.retrieval_id)\n```\n\n## Managing API Keys\n\nThe server stays open to unauthenticated callers by design — but they all share one small, low-throughput rate-limit budget (see [Use It Now](#use-it-now--no-install)). Issuing someone a key gives them their own isolated, higher budget instead. This doesn't gate *access* — it's purely a fairness mechanism so one caller can't starve everyone else's share of PubMed's real rate limit.\n\n```bash\n# Issue a key — the raw key is shown once, save it immediately\nbiolab keys create alice\n\n# List issued keys (never shows the raw key — only a hash is stored)\nbiolab keys list\n\n# Revoke all of a label's active keys\nbiolab keys revoke alice\n```\n\nThe caller sends the key back as `Authorization: Bearer <key>`. A missing header still works (anonymous tier); a header with an invalid or revoked key is rejected with `401`, not silently downgraded — a typo'd key should fail loudly, not quietly run at a lower tier.\n\n## Environment Variables\n\nAll optional — the server runs with sensible defaults if you set none of these.\n\n| Variable | Purpose | Default |\n|----------|---------|---------|\n| `BIOLAB_DB_PATH` | Local SQLite file path (ignored if `TURSO_DATABASE_URL` is set) | `biolab.db` |\n| `TURSO_DATABASE_URL` | Remote [Turso](https://turso.tech) database URL — use this for real persistence in production | unset (uses local file) |\n| `TURSO_AUTH_TOKEN` | Auth token for the Turso database above | unset |\n| `BIOLAB_HOST` | Host the MCP server binds to | `0.0.0.0` |\n| `BIOLAB_PORT` | Port the MCP server listens on | `8000` |\n| `NCBI_API_KEY` | Raises the PubMed rate limit from 3 req/s to 10 req/s | unset (works fine without one) |\n\n## Audit Trail Schema (v2)\n\n```sql\nCREATE TABLE retrievals (\n    retrieval_id     TEXT PRIMARY KEY,  -- UUID\n    source           TEXT NOT NULL,     -- \"pubmed\", \"europepmc\", \"clinicaltrials\", \"biorxiv\"\n    external_id      TEXT NOT NULL,     -- PMID, NCT ID, DOI, etc.\n    query_text       TEXT NOT NULL,     -- exact query sent to source\n    retrieved_at     TEXT NOT NULL,     -- ISO 8601 UTC\n    agent_id         TEXT NOT NULL,     -- e.g. \"aletheia:advocate\"\n    source_metadata  TEXT NOT NULL,     -- JSON: source-specific fields\n    raw_response     TEXT NOT NULL,     -- verbatim XML/JSON from source\n    snapshot         TEXT NOT NULL,     -- JSON: structured fields (title, abstract, authors, journal, DOI, pub types, MeSH/conditions)\n    response_hash    TEXT NOT NULL,     -- SHA-256(prev_hash + raw_response + retrieval_id + retrieved_at)\n    prev_hash        TEXT NOT NULL      -- response_hash of the previous row — makes this a hash chain\n);\n```\n\n**Key properties:**\n- One row per paper retrieval (not per query)\n- Raw response stored verbatim — parsing bugs are recoverable\n- **Hash-chained**, not just hashed: each row's hash covers the previous row's hash too, so deleting or editing any row — even in the database directly — breaks the chain for every row after it. Call `retrieval_log.verify_chain(conn)` to check the whole log; it returns exactly which row broke, if any.\n- Background write queue serializes all writes through one path, so the chain stays consistent even under concurrent agent calls\n\n## Architecture\n\n```\nbiolab/\n├── cli.py                  # Typer CLI (search, get, list, export, demo)\n├── server.py                # FastMCP server, streamable-http transport\n├── db.py                    # Connection + schema (local SQLite or remote Turso)\n├── models.py                 # RetrievalRecord dataclass\n├── retrieval_log.py          # Only writer + background queue + hash chain\n├── pubmed_client.py           # PubMed E-utilities wrapper + rate limiter\n├── europepmc_client.py        # Europe PMC adapter\n├── clinicaltrials_client.py   # ClinicalTrials.gov adapter\n├── biorxiv_client.py          # bioRxiv/medRxiv adapter\n└── migrations/                # Schema migration scripts\n\nspace/                      # Files pushed to the hosted Hugging Face Space\n├── Dockerfile                # Python-server-specific image (see repo-root Dockerfile for the Go one)\n└── README.md                  # Space config (title, hosting metadata)\n```\n\n**Design principles:**\n- Python + Go implementations (same interface, different runtimes)\n- MCP tools, not REST API — zero integration overhead for agents\n- Database, not log files — structured queries across time\n- Hard-fail, never degrade — paper without `retrieval_id` is worse than error\n- Live-API tests, no mocks — real XML/JSON shape catches real bugs\n- Single-writer queue, not row-level locking — simplest thing that keeps the hash chain consistent under concurrency\n\n## Development\n\n```bash\n# Python\npip install -e \".[dev]\"\npytest tests/ -v\n\n# Go\ncd go-biolab\ngo test ./...\ngo build -o biolab ./cmd/cli\ngo build -o biolab-server ./cmd/server\n```\n\n## Deployment\n\n| Target | Method |\n|--------|--------|\n| **Hosted (no install)** | https://srikarjy025-biolab-mcp.hf.space/mcp — Hugging Face Space, Docker SDK, backed by Turso |\n| **Local** | `pipx install biolab-mcp` or download binary |\n| **CI/CD** | GitHub Actions → PyPI (Trusted Publishing/OIDC) + GHCR + GitHub Releases |\n| **Containers** | `docker pull ghcr.io/srikarjy/biolab-mcp:latest`, or build `space/Dockerfile` yourself |\n| **Linux packages** | `.deb`, `.rpm`, `.apk` via goreleaser |\n| **Discovery** | [MCP Registry](https://registry.modelcontextprotocol.io) · [Smithery](https://smithery.ai/servers/srikarjy025/biolab-mcp) |\n\n**Running cost: $0/month.** The Space runs on Hugging Face's free `cpu-basic` tier (this workload waits on network calls, not compute, so it never needed more). Turso's free tier is currently at 0% of its storage/read/write quotas, and has overages *disabled* — if usage ever did hit a limit, requests get rejected, not silently billed. There's no realistic query volume (short of literally millions/month) that would introduce a cost.\n\n## Roadmap\n\n- [ ] Evidence drift detection (retraction monitoring via response hashes)\n- [ ] Provenance graph (cross-source linking by DOI)\n- [ ] Nextflow/Snakemake plugins\n- [ ] Rate limiting + caching (audit-safe)\n- [ ] Auth + multi-tenant support\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n\n## Author\n\n**Srikar Jy** — [srikarjy025@gmail.com](mailto:srikarjy025@gmail.com)\n",
  "bytes": 16157,
  "sha": "291258ccd82beed102621c313871c5c7eb03090065bf01d10562f4f1be2814ef",
  "repo_slug": "srikarjy/biolab-mcp-server",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_srikarjy_biolab_mcp_server_97d7688e/readme"
}