DEV Community

Cover image for Give Your AI Agent a Scientist's Library. a Science MCP Server
Prosper Otemuyiwa for Valyu AI

Posted on Originally published at Medium

Give Your AI Agent a Scientist's Library. a Science MCP Server

Most "research agent" demos are searching abstracts and calling it literature review. The abstract tells you a Phase 3 melanoma immunotherapy trial hit its endpoint. It does not tell you the imaging protocol used to assess tumour response that lives in the methods section, or a supplementary table, or a figure caption.

This walks through wiring a hosted science MCP server into your AI agent, scoping it to actual scientific collections, and running a controlled before-and-after test where exactly one variable changes.

Valyu is a search API built for AI agents: one endpoint over biomedical literature, clinical trial registries, patents, financial filings and the open web, returning full text and structured metadata with resolvable identifiers rather than a list of links to go click.

TL;DR

  • The hosted MCP server is a plain HTTP endpoint. No local process, no Node, no mcp-remote.
  • The MCP tools take query and max_num_results and nothing else. No source filtering. If you need scoped retrieval, you need the REST API or an SDK — not the MCP client.
  • Scope with included_sources in the API, then check result.source on every hit.
  • include_abstracts=True widens PubMed to the whole abstract corpus; the default restricts to papers with available full text.
  • Every claim gets a resolvable identifier. The API returns result.doi — read it, don't prompt for it.

What you need

  • A Valyu API key from platform.valyu.ai — $10 in free credits, $20 if you sign up with a work email, no card
  • Claude Desktop, Cursor, or any MCP client
  • Python 3 and pip install valyu for the API examples

No Node.js. The hosted server is remote HTTP.

Connecting the MCP server

The endpoint is:

https://mcp.valyu.ai/mcp?valyuApiKey=YOUR_API_KEY
Enter fullscreen mode Exit fullscreen mode

Auth rides in the query string. You can cap spend per session by appending &maxPrice=50.

Claude Desktop / claude.ai — go to claude.ai/settings/connectors → Add custom connector → paste the URL.

Open in Cursor

What the server exposes

Eleven tools, not one :

Tool Arguments Use
valyu_search query, max_num_results, fast_mode Web search returning full page content
valyu_academic_search query, max_num_results Full text across arXiv, PubMed, bioRxiv, medRxiv
valyu_bio_search query, max_num_results PubMed, clinical trials, FDA labels, bioRxiv, medRxiv, ChEMBL, PubChem, DrugBank, Open Targets, NPI Registry, WHO ICD
valyu_patents query, max_num_results Patent documents — claims, abstracts, inventors, filings
valyu_contents urls Extract content from up to 10 URLs
valyu_datasources category Enumerate the 36+ datasets at runtime

Plus valyu_financial_search, valyu_sec_search (adds response_length), valyu_company_research (company, sections), valyu_economics_search and valyu_datasources_categories.

Scoping your sources

(API and SDK only — see the constraint above.)

Sources are addressed two ways through included_sources: presets (curated bundles) and dataset IDs (individual collections).

Presets: academic, finance, patent, health, genomics, chemistry, physics, legal, politics, transportation, pulse, cybersecurity, environment, automotive, compliance, medical.

Watch the preset boundaries — this bites people. academic covers literature and preprints only:

Dataset ID Preset Coverage
valyu/valyu-pubmed academic 37M+ open-access biomedical papers, monthly
valyu/valyu-arxiv academic Physics, CS, maths, quant finance, economics
valyu/valyu-biorxiv academic 250K+ life-sciences preprints
valyu/valyu-medrxiv academic 80K+ clinical/health preprints
valyu/valyu-chemrxiv academic 30K+ chemistry preprints
valyu/valyu-clinical-trials health 500K+ ClinicalTrials.gov studies, real-time
valyu/valyu-drug-labels health 150K+ FDA labels via DailyMed
valyu/valyu-patents patent 8M+ USPTO filings, full text and figures
valyu/valyu-patents-epo patent 4M+ European filings from 1978
valyu/valyu-chembl chemistry 2.5M+ bioactive compounds
valyu/valyu-pubchem chemistry 100M+ compounds
valyu/valyu-open-targets chemistry 60K+ drug targets

valyu_bio_search additionally reaches DrugBank, the NPI Registry and WHO ICD codes, which aren't broken out as dataset IDs in the datasources guide.

Clinical trials are not in academic. If you scope a trial question to the academic preset you will get papers about trials, not registry records. Use health, or name valyu/valyu-clinical-trials directly.

from valyu import Valyu

valyu = Valyu(api_key="YOUR_API_KEY")  # or set VALYU_API_KEY

response = valyu.search(
    "Phase 3 melanoma immunotherapy trials",
    search_type="proprietary",          # all | web | proprietary | news
    included_sources=["valyu/valyu-pubmed"],
    max_num_results=10,
)

for result in response.results:
    print(result.title)
    print(result.source)          # check this
    print(result.doi)
    print(result.content)
Enter fullscreen mode Exit fullscreen mode

Then check the results. Every SearchResult carries a source field. After each search, confirm each result came from a collection you declared. If something arrives from elsewhere, treat the output as unscoped and rerun tighter. Filtering narrows the search; it is not a guarantee of exclusion.

Note excluded_sources accepts dataset IDs and domains but not presets.

The before-and-after test

Here's the part worth running, with one honest caveat up front.

By default (include_abstracts=False), PubMed search is restricted to papers that have available full text. Setting include_abstracts=True expands the search to PubMed's complete abstract corpus and returns document-level abstracts.

So this is not a clean single-variable A/B. Two things change at once: the corpus gets bigger, and the returned granularity drops to abstract level. It's still the sharpest comparison the API gives you, but describe it accurately — you are comparing full-text-only retrieval against broad abstract-level retrieval, not "the same search with and without full text."

1. Pick a question the abstract can't answer

You want a detail that lives in the methods, a figure caption, or a supplement:

What imaging protocol did the trial use for tumour response assessment?

2. Run it abstract-only

abstract_run = valyu.search(
    "Phase 3 melanoma immunotherapy tumour response assessment imaging protocol",
    search_type="proprietary",
    included_sources=["valyu/valyu-pubmed"],
    include_abstracts=True,      # widen to the full PubMed abstract corpus
    max_num_results=10,
)
Enter fullscreen mode Exit fullscreen mode

This has to run in Python, not through the MCP client. No MCP tool accepts include_abstracts — see the section above. Save the output verbatim. This is your baseline.

3. Run it with full text

fulltext_run = valyu.search(
    "Phase 3 melanoma immunotherapy tumour response assessment imaging protocol",
    search_type="proprietary",
    included_sources=["valyu/valyu-pubmed"],
    include_abstracts=False,     # default — papers with available full text only
    max_num_results=10,
)
Enter fullscreen mode Exit fullscreen mode

Identical query, identical source, identical result count. Save that too.

4. Compare

Put them side by side. Does the abstract-only answer contain the imaging protocol? Does the full-text one?

If full text surfaces evidence abstract-only missed, you have a controlled result — for this query, this index, and this date.

Things not to do with it:

  • Don't generalise to "abstract-only retrieval is unreliable." You tested one query against one index.
  • One run is a demonstration, not a benchmark.
  • PubMed full text is open access only. If your topic is dominated by paywalled journals, the full-text run has less to work with and the comparison says more about OA coverage than about retrieval depth.
  • Don't publish until you've independently opened the paper and confirmed the detail is where you say it is.

Recording the run

Four things, or nobody can reproduce it: the exact call parameters, the full verbatim output, the source list, and the correction.

{
  "query": "<identical query string used in both runs>",
  "shared_params": {
    "search_type": "proprietary",
    "included_sources": ["valyu/valyu-pubmed"],
    "max_num_results": 10
  },
  "run_abstract_only": {
    "include_abstracts": true,
    "output": "<full output, verbatim>",
    "sources_returned": ["<result.source values>"]
  },
  "run_full_text": {
    "include_abstracts": false,
    "output": "<full output, verbatim>",
    "sources_returned": ["<result.source values>"]
  },
  "run_date": "<YYYY-MM-DD>",
  "correction": {
    "missed_by_abstract": "<what was missing>",
    "found_in_full_text": "<what full-text surfaced>",
    "source_doi": "<result.doi>",
    "location": "<methods / figure caption / supplement>"
  }
}
Enter fullscreen mode Exit fullscreen mode

Record the date — PubMed syncs monthly and trials update in real time, so the same call will drift.

Citation rules that scientists actually use

Every claim links to a resolvable identifier. This is the line between a science agent and a chatbot with a search tool.

Source type Identifier Resolves at
Journal articles DOI https://doi.org/<doi>
Preprints (bioRxiv, medRxiv, ChemRxiv) DOI https://doi.org/<doi>
Clinical trials NCT number https://clinicaltrials.gov/study/<nct>
US patents USPTO patent number USPTO patent search portal

You don't have to parse these out of prose — SearchResult exposes doi, citation, authors, publication_date, citation_count and source as structured fields. Read them directly rather than asking the model to extract them.

Do not demand a DOI for everything. Trials and patents have their own registries, and a prompt that insists on DOIs produces fabricated ones.

For every factual claim, cite a resolvable identifier: a DOI for journal
articles, an NCT number for clinical trials, or a patent number for patents.
If the result has no identifier, give the URL and state that the claim is
unverified. Never construct an identifier that was not returned.
Enter fullscreen mode Exit fullscreen mode

Combining literature, trials and patents

Three scoped searches, correct preset for each:

from valyu import Valyu

valyu = Valyu(api_key="YOUR_API_KEY")

# Literature — academic preset
lit = valyu.search(
    "PD-1 inhibitor combination therapy melanoma",
    search_type="proprietary",
    included_sources=["academic"],
)

# Clinical trials — registry records live in health, NOT academic
trials = valyu.search(
    "PD-1 inhibitor melanoma Phase 3",
    search_type="proprietary",
    included_sources=["valyu/valyu-clinical-trials"],
)

# Patents — USPTO full text and figures
patents = valyu.search(
    "PD-1 antibody immunotherapy",
    search_type="proprietary",
    included_sources=["valyu/valyu-patents"],
)
Enter fullscreen mode Exit fullscreen mode

DOIs for the literature, NCT numbers for the trials, patent numbers for the patents.

Valyu's DeepResearch (POST /v1/deepresearch/tasks) spans the same catalogue asynchronously. It can reach across domains in one task, but verify the returned sources match your intended scope before treating the output as complete.

Six checks to keep the agent honest

Source provenance — read result.source on every result. If it isn't a collection you declared, the run is unscoped.

Identifier resolution — verify the cited identifier actually resolves before presenting the claim. DOI at doi.org, NCT at clinicaltrials.gov, patent through USPTO. Doesn't resolve → unverified.

Full-text availability — PubMed full text is open access only, and include_abstracts=True means you got abstracts instead of full text. Have the agent state which mode it ran in. An agent reasoning over an abstract as though it read the paper is the failure this whole post is about.

Preprint status — bioRxiv, medRxiv and ChemRxiv are not peer-reviewed. Label them as preprints, with server name and DOI, so the reader can judge evidence level.

Citation entailment — when the agent says a source supports a statement, confirm the passage is actually in the returned content. If it cites a figure, confirm the figure came back.

Missing assets — figures, tables and supplements are not retrievable from every source. valyu/valyu-patents is the one dataset documented as carrying full text and figures; don't assume that generalises to the preprint servers. If an asset isn't there, the agent says so rather than substituting.

Access and copyright limits

Retrieval is not a redistribution licence. The Valyu Acceptable Use Policy applies across all APIs, datasets, models and indexes. You must not:

  • Reproduce or redistribute copyrighted content
  • Access content behind paywalls or access controls
  • Store or display publisher content in ways licensing doesn't allow
  • Extract, store or manipulate full-text articles from licensed sources
  • Rebuild, replicate or simulate any Valyu corpus, dataset, index or scoring
  • Join outputs to reassemble source materials
  • Scrape or bulk download via high-volume search queries

The contents endpoint is your responsibility. Per the AUP: "You — not Valyu — are the party responsible for ensuring that your use of the Contents endpoint in connection with any given URL is lawful and authorised." Before submitting a URL, review the target's terms and acceptable use policy, and confirm automated extraction isn't prohibited by robots.txt, X-Robots-Tag headers or <meta name="robots"> directives.

In practice: the agent reads and reasons over retrieved content in-session and does not store it for redistribution. And retrieved research is not a substitute for professional medical advice, or for a human reading the primary source.

Checklist

  • [ ] MCP endpoint connected and tools listed in the client
  • [ ] Reproducibility runs done via API/SDK, not the MCP client
  • [ ] included_sources set, with the right preset for the source type
  • [ ] Both runs use identical query, sources and max_num_results
  • [ ] Only include_abstracts differs between the two runs, and the writeup says the corpus widened too
  • [ ] Full output saved verbatim, plus run date
  • [ ] result.source checked on every result
  • [ ] Every claim linked to a returned doi, NCT number or patent number
  • [ ] Preprints labelled as preprints
  • [ ] Conclusion scoped to this query and index only
  • [ ] No stored or redistributed licensed full text
  • [ ] No URL sent to /v1/contents without checking terms and robots directives
  • [ ] Retrieved research not presented as medical advice

FAQ

How do I run the same question through both abstract-only and full-content workflows?
In Python, not through MCP — no MCP tool exposes include_abstracts. Issue the identical query twice against valyu/valyu-pubmed, once with include_abstracts=True and once with the default False, keeping search_type, included_sources and max_num_results fixed. Save both outputs verbatim.

Can I restrict my Claude Desktop agent to just PubMed?
No. MCP search tools accept only query and max_num_results. Your scope control is which tool the agent picks. For real source pinning, call the API directly.

What makes a good test question?
One where the decisive detail sits outside the abstract — imaging protocols, assay conditions, eligibility subtleties. "What imaging protocol did the Phase 3 melanoma immunotherapy trial use for tumour response assessment?" works because that lives in methods or a supplement.

Why did my clinical trial search return papers instead of registry records?
You almost certainly scoped to the academic preset. Clinical trials live in health — use included_sources=["valyu/valyu-clinical-trials"].

How should the agent cite results without DOIs?
NCT number for trials, patent number for patents. If none exists, the URL plus an explicit note that the claim is unverified. Read result.doi rather than having the model extract it.

Top comments (0)