
Researcher profiles, works, affiliations, funding, and peer reviews from the ORCID registry.
Search and retrieve researcher profiles, works, affiliations, funding, and peer review records from the ORCID registry via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://orcid.caseyjhand.com/mcp
Researcher identity data from the ORCID registry. Search and disambiguate authors, build a researcher dossier from profile, works, affiliations, funding, and peer review records, and chain external identifiers to Crossref, PubMed, or arXiv from any MCP client. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
orcid_search_researchers | Search the ORCID registry using structured field params (name, affiliation, keyword, ROR ID, DOI, PMID, grant number) |
orcid_get_profile | Fetch a researcher's public profile — name, other names, biography, keywords, researcher URLs, external identifiers |
orcid_get_works | Retrieve works (publications, datasets, software, preprints) for a researcher, paginated |
orcid_get_work_detail | Fetch full detail records — abstracts, contributors, citations — for 1–100 works by put-code |
orcid_get_affiliations | Fetch affiliation records: employment, education, memberships, and more |
orcid_get_funding | Fetch funding records: grants, contracts, awards, and salary awards |
orcid_get_peer_reviews | Fetch peer review activity: convening organizations, reviewer role, review type |
orcid_get_research_resources | List research resources — compute allocations, equipment access, lab facilities |
orcid_resolve_researcher | Disambiguate an ambiguous author name to a ranked list of verified ORCID iD candidates |
| Resource | Description |
|---|---|
orcid://researcher/{orcid_id}/profile | Researcher profile (person section) — name, other names, bio, keywords, external IDs |
orcid://researcher/{orcid_id}/works | Works list for a researcher — the first 25 plus the total count |
All resource data is also reachable via tools. Use resources when injecting stable researcher context into a prompt; use tools when filtering or processing results is needed.
orcid_search_researchers toolgiven_name, family_name, affiliation, keyword, ror_id, doi, pmid, grant_number — AND together automatically; query adds raw Solr syntax — sent as written when it is the only field, otherwise ANDed as one parenthesized group so a top-level OR keeps its alternatives (an exclusion-only query such as -keyword:x is ANDed ungrouped, since ORCID matches nothing for a group of only exclusions)given_name phrase-matches, except that a value of only initials (J., J. A.) matches given names starting with each letterdoi and pmid map to doi-self / pmid-self field queries — finds researchers who linked that specific work to their ORCID record. URL and label forms (https://doi.org/…, doi:…, https://pubmed.ncbi.nlm.nih.gov/…/, PMID:…) are accepted and reduced to the bare identifierror_id takes a ROR ID bare (00f54p054) or as a ror.org URL (https://ror.org/…, ror.org/…, a www. host, a trailing slash), in either letter case, and is reduced to the https://ror.org/<lowercase id> form ORCID indexes; a value that is not a ROR ID, or whose check digits do not match, is rejected before any search runsgrant_number phrase-matches the grant numbers on researchers' funding items, case-insensitively, over the parts between separators such as hyphens and slashes — 5F31MH010500 also matches 5F31MH010500-03, but a number cut mid-part matches nothing — pair with orcid_get_funding to inspect each matchrows: 1–1000 (default 20); start: 0–10,000 offset pagination (the ORCID Public API's ceiling for unauthenticated requests)rows can come in below the requested count; nextStart continues from the first result left outorcid_resolve_researcher for ambiguous names needing ranked disambiguationorcid_get_profile tool0000-0001-2345-6789) or an orcid.org URI (https://orcid.org/…, orcid.org/…, a www. host, a trailing slash); a lowercase x check digit is canonicalized to X. Every tool that takes orcid_id accepts the same forms; the orcid://researcher/{orcid_id}/… resources take the bare iDnotice names every section with no public data (e.g. "no public email addresses or countries"), since an empty section can be private rather than emptyorcid_get_works toollimit max 1000); page with offset and the returned nextOffset — workCount reports the total availablereturnedCount can come in below limit; truncated and nextOffset carry the continuation either wayinclude_external_ids to false to drop DOI/PMID/arXiv/ISBN identifier lists for a lighter payloadputCode to orcid_get_work_detail for abstracts and contributor listssources — the researcher, or member organizations such as Crossref or a university system — with selfAsserted marking what the researcher asserted; an empty list does not mean no publicationsorcid_get_work_detail toolput_codes: 1–100 per call (from orcid_get_works), resolved in a single round-trip; a repeated put-code is fetched oncecontributorCount and contributorsTruncated marking the cut; a citation over 8,192 bytes is left out and flagged citationOmitted, so a large-collaboration paper fits beside other recordsorcid_get_works lists for the work group; sources names that sourceerrors entries — the rest of the batch still resolvesdeferredPutCodes with a notice, ready to pass as put_codes in the next callRateLimited error carrying ORCID's retryAfter when it sent oneorcid_get_affiliations tooltypes filters which sections to return: employment, education, invited-positions, distinctions, memberships, qualifications, services, or all — default is employment + education; an explicit empty list is rejectedsources names who added it — the researcher, or a member organization such as a university research information system; an empty result does not mean no affiliationorcid_get_funding toolfundingCount; when the group's versions record distinct award periods, such as renewals under one grant number, periods lists eachsources names which; most researchers with real grants have no entries here, and absence does not imply no fundingorcid_get_peer_reviews toolreviewer, editor, chair, etc.), review type, completion date, an ISSN-keyed group identifier, and the convening organization ORCID records per recordsources names which; coverage varies widely by researcherorcid_get_research_resources toolsources that deposited itorcid_resolve_researcher toolrows) with transparent disambiguation signals: name match type (exact/partial/other-name/none), institution overlap flag, and anchor type (doi/pmid/none)José Baselga and Jose Baselga classify the same candidate identicallydoi or pmid is provided (bare or as a URL), uses doi-self or pmid-self as an anchor — researchers who have linked that work to their ORCID record are near-deterministic matchesJennifer Doudna), a byline with initials (J. Doudna, Jennifer A. Doudna), or Family, Given (Doudna, Jennifer, read as Jennifer Doudna); an initial counts toward a partial match when the candidate's first given name starts with itorcid://researcher/{orcid_id}/profile resourceapplication/json{orcid_id} is the bare iD, with an X or x check digit — a URI segment cannot carry the https://orcid.org/ formdata.reason and a recovery hint, like the tools: invalid_orcid_id for a checksum-invalid iD (rejected locally, before any upstream call), profile_not_found for an iD ORCID does not know or a record with no public nameorcid_get_profile tool when the response needs to flow into conditional logicorcid://researcher/{orcid_id}/works resourceworkCount (the total available) as application/json{orcid_id} is the bare iD, with an X or x check digitinvalid_orcid_id (checksum, before any upstream call) or profile_not_found (unknown iD, data.orcidId included), each with a recovery hintorcid_get_works tool to page the full list or filter resultsBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
ORCID-specific:
https://pub.orcid.org/v3.0) — no API key required for public read endpointsexpanded-search as the primary search backend — returns ORCID iD, name, and institution data inline, eliminating N+1 profile fetches/activities call for affiliation queries, filtered client-side — eliminates up to 7 parallel upstream calls vs. per-section fetching<i>, <sup>, <h4>) is stripped to plain text; the deposited citation is relayed verbatimAgent-friendly output:
orcid_resolve_researcher returns raw disambiguation signals (name match type, institution overlap, anchor type) instead of a synthetic confidence scoresources: the person or member organization that added it (a university system, funder, Crossref, Web of Science) and whether the researcher asserted it (selfAsserted); grouped works and funding list every source in the grouporcid_search_researchers reports numFound and a truncated flag against the ORCID Public API's 10,000-offset ceiling, and cuts a page to a 64,000-byte response budget with nextStart to continue; orcid_get_works reports workCount and truncated against its own page size and a 64,000-byte response budgetorcid_get_work_detail returns per-put-code errors alongside successfully resolved works instead of failing the whole batch, and names any put-codes the response budget deferredorcid_get_works, orcid_get_affiliations, orcid_get_funding, orcid_get_peer_reviews, and orcid_get_research_resources return a notice when a result is empty, explaining that this may mean neither the researcher nor a member organization added entries, or that visibility settings hide them, rather than confirmed absence; orcid_get_profile names each section with no public datastructuredContent carries every value verbatimA public instance is available at https://orcid.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"orcid-mcp-server": {
"type": "streamable-http",
"url": "https://orcid.caseyjhand.com/mcp"
}
}
}
Add the following to your MCP client configuration file. No API key is required — the ORCID Public API is open for public read access.
{
"mcpServers": {
"orcid-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/orcid-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"orcid-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/orcid-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"orcid-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/orcid-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/orcid-mcp-server.git
cd orcid-mcp-server
bun install
cp .env.example .env
# edit .env if needed — no required vars
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
ORCID_API_BASE_URL | Override the ORCID API base URL. Useful for pointing at the sandbox (https://pub.sandbox.orcid.org/v3.0/). | https://pub.orcid.org/v3.0 |
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path | /mcp |
MCP_SESSION_MODE | HTTP session mode: auto, stateful, or stateless. This server declares stateless in createApp() — no tool asks the caller for input mid-handler — and a set value overrides it. | stateless |
MCP_PUBLIC_URL | Public origin for TLS-terminating reverse-proxy deployments | none |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.) | info |
MCP_GC_PRESSURE_INTERVAL_MS | Opt-in Bun-only forced-GC pressure loop (ms). Try 60000 if heap growth is observed under sustained HTTP load. | 0 (disabled) |
LOGS_DIR | Directory for log files (Node.js only) | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1 | in-memory |
OTEL_ENABLED | Enable OpenTelemetry | false |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t orcid-mcp-server .
docker run --rm -p 3010:3010 orcid-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/orcid-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and resources, inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Nine tools across search, disambiguation, profile, works, work detail, affiliations, funding, peer reviews, and research resources. |
src/mcp-server/resources | Resource definitions (*.resource.ts). Profile and works resources. |
src/services/orcid | ORCID Public API v3.0 service layer — search, record section fetchers, retry/backoff. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagecreateApp() arraysIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.