Back to Directory/Developer Tools

io.github.cyanheads/worms-marine-taxa-mcp-server

Resolve WoRMS marine species names; fetch classifications, synonyms, distributions, traits, and IDs.

Developer ToolsTypeScriptv0.1.2

@cyanheads/worms-marine-taxa-mcp-server

Resolve WoRMS marine species names; fetch classifications, synonyms, distributions, traits, and IDs via MCP. STDIO or Streamable HTTP.

8 Tools

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


Overview

Marine taxonomy from the World Register of Marine Species (WoRMS). Resolve scientific or common names to AphiaIDs, follow accepted names, and pull classifications, synonyms, distributions, traits, and external identifiers from the keyless WoRMS REST API.

Tools

ToolDescription
worms_resolve_taxonResolve scientific names (exact or fuzzy) or one common name to AphiaIDs, with accepted-name links
worms_get_taxonFetch full Aphia records by AphiaID, optionally with common names or sources
worms_get_classificationFetch the root-to-tip classification, optionally with direct children
worms_get_synonymsList synonyms of the accepted taxon behind an AphiaID
worms_get_distributionsFetch recorded distributions, filtered by status, establishment means, invasiveness, or occurrence
worms_get_traitsFetch attribute trees (body size, functional group, conservation listings) with sources and inheritance
worms_crosswalk_idMap an AphiaID to nine external identifier systems, or reverse an external ID
worms_list_referenceList identifier systems, statuses, environment flags, distribution filter values, ranks, and trait definitions

Capability reference

worms_resolve_taxon tool

  • names (1–500 scientific names, 50 per upstream request) or one vernacular, never both. fuzzy: true opts into TAXAMATCH matching; marineOnly and extantOnly default to true for names. Vernacular search matches a whole recorded common name, or with vernacularMatch: "contains" every recorded name containing the text (grouper finds no exact match but many by contains); it applies neither filter
  • Each result keeps its inputIndex (duplicates included), every match with status, validAphiaId, and validName, and a guidance line on a miss; continue with nextInputOffset and nextMatchOffset
  • Vernacular results page with the upstream offset (1-based, steps of 50) and the local resultOffset

worms_get_taxon tool

  • Up to 500 aphiaIds, 50 fetched per request; continue with nextInputOffset. Unknown IDs stay in position as found: false
  • includeVernaculars: true (one ID only) adds common names, paged with vernacularOffset; a failed lookup returns vernacularError beside the record
  • includeSources: true (one ID only, not with includeVernaculars) adds the taxon's sources, such as its original description and basis of record, with reference, page, url, link, fulltext, and doi verbatim. WoRMS returns the whole list at once, so the tool pages it with sourceOffset; a failed lookup returns sourcesError beside the record

worms_get_classification tool

  • One aphiaId; ranks is the root-to-tip lineage, with found: false when WoRMS has none
  • includeChildren: true adds one 50-row page of direct children (offset, marineOnly, extantOnly); a failed children lookup returns childrenError beside the lineage
  • An AphiaID that is not the accepted name keeps its own recorded lineage, and a notice names the accepted AphiaID to call instead; a failed status lookup returns statusError beside the lineage

worms_get_synonyms tool

  • One aphiaId, accepted or not: synonyms are listed for its accepted taxon (validAphiaId), 50 per upstream offset
  • found and guidance separate a missing taxon from a name with no known accepted ID

worms_get_distributions tool

  • One aphiaId; optional recordStatus (valid, doubtful, inaccurate), establishmentMeans, invasiveness, and occurrence filters combine with AND. recordStatus is exact; the others are case-insensitive whole values, with observed values in worms_list_reference topic distribution_terms
  • Reports unfilteredCount and filteredCount; rows carry the locality, its MarineRegions locationId, and status fields in source order
  • WoRMS attaches records to individual name usages, so by default the tool reads that AphiaID's own records. includeSynonyms: true combines the accepted taxon's records with those of its synonyms, 10 synonyms per call, each row tagged sourceAphiaId; synonymUnion reports the window, the nextSynonymOffset to continue from, and any failedSynonyms
  • A notice flags an AphiaID that is not the accepted name and names the accepted one; without includeSynonyms, a failed status lookup returns statusError beside the rows

worms_get_traits tool

  • One aphiaId; includeInherited (default true) adds attributes recorded on higher taxa. On an accepted taxon WoRMS also returns attributes recorded on its synonyms whatever includeInherited is set to; inheritedFrom names the source taxon in both cases
  • A notice flags an AphiaID that is not the accepted name and names the accepted one; a failed status lookup returns statusError beside the attributes
  • Every node keeps nodeIndex, parentIndex, and depth from the full tree, so units and qualifiers stay attached to their values across pages; values and references are verbatim
  • measurementType (case-insensitive whole value, such as Body size) keeps each matching node with its subtree and ancestors, indices unchanged; totalCount counts that set and unfilteredCount the whole tree

worms_crosswalk_id tool

  • Exactly one of aphiaId (forward) or externalId with type (reverse). Forward reads one type or all nine: algaebase, bold, dyntaxa, fishbase, iucn, lsid, ncbi, tsn, gisd
  • Forward returns ids per type, typeTotals (0 means none recorded), and failedTypes for systems that errored; reverse returns found and the full record. IDs stay opaque strings, leading zeros included

worms_list_reference tool

  • topic: external_id_types, statuses, environment_flags, and distribution_terms are served offline; ranks and trait_types are read live
  • statuses covers the 28 statuses the WoRMS manual and REST API document, each with a one-line meaning and a group (accepted, unaccepted, uncertain, technical). A record's status can be the specific reason itself, such as junior subjective synonym, so test the group rather than status === "unaccepted"; the guide is non-exhaustive
  • distribution_terms lists the establishmentMeans, invasiveness, and occurrence values observed in WoRMS distributions, for the whole-value filters of worms_get_distributions; the lists are non-exhaustive
  • Pages with resultOffset; trait definitions keep their tree indices across pages

Features

Built 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.

WoRMS-specific:

  • Exact and TAXAMATCH fuzzy name matching with marine and extant filters; every match and record carries its accepted-name links
  • One shared request queue: one upstream request at a time, 500 ms between starts, a 45-second tool deadline, and an 8 MiB response ceiling
  • Per-call retrieval with no cache or bulk harvesting, as the WoRMS webservice guidance asks
  • Every response carries WoRMS attribution (doi:10.14284/170); credit the WoRMS Editorial Board and keep per-record citations when reusing results (terms)

Agent-friendly output:

  • Byte-bounded pages: reads return whole items within a 24,000-byte response and name the next offset (nextResultOffset, nextInputOffset, upstream page.nextOffset); a full 50-row upstream page reports hasMore: null rather than guessing
  • Unknown stays unknown: null and absent fields pass through, a null environment flag means not recorded, and empty evidence never claims biological absence
  • Partial failure: optional lookups (vernacularError, sourcesError, childrenError, statusError), synonym-union failedSynonyms, and crosswalk failedTypes keep the data that did arrive, with typed reasons and recovery hints

Getting started

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "worms-marine-taxa-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/worms-marine-taxa-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "worms-marine-taxa-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/worms-marine-taxa-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "worms-marine-taxa-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/worms-marine-taxa-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

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key or account: the WoRMS REST API is public.

Installation

  1. Clone the repository:
git clone https://github.com/cyanheads/worms-marine-taxa-mcp-server.git
  1. Navigate into the directory:
cd worms-marine-taxa-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env

Configuration

The server has no variables of its own; these are the common framework settings.

VariableDescriptionDefault
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOGS_DIRDirectory for log files (Node.js only).<app-root>/logs
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1.in-memory
OTEL_ENABLEDEnable OpenTelemetry.false

See .env.example for the common framework overrides.

Running the server

Local development

  • Build and run the production version:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:http
    # or
    bun run start:stdio
    
  • Run checks and tests:

    bun run devcheck  # Lints, formats, type-checks, and more
    bun run test      # Runs the test suite
    

Project structure

DirectoryPurpose
src/mcp-server/tools/definitionsTool definitions (*.tool.ts). Eight read-only tools.
src/services/wormsWoRMS service: paced transport, normalization, domain schemas, response budgeting, and formatting.
docs/Design contract (design.md) and the generated directory tree.
tests/Service, tool-contract, and JSON-RPC integration tests.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches; optional lookups report classified failures beside the primary data instead of discarding it
  • Register new tools in the createApp() arrays in src/index.ts
  • Internal imports are relative ESM paths with .js extensions
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Installation

Source-derived launch command. Check the maintainer’s required arguments and credentials before running:

bash
npx -y @cyanheads/worms-marine-taxa-mcp-server

Set up in your AI client

Merge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.

json
{
  "mcpServers": {
    "io-github-cyanheads-worms-marine-taxa-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "@cyanheads/worms-marine-taxa-mcp-server"
      ]
    }
  }
}

Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.

Claude Desktop setup reference

Package

@cyanheads/worms-marine-taxa-mcp-servernpm

Compatible MCP Clients

io.github.cyanheads/worms-marine-taxa-mcp-server works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.

  • Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.
  • Cursor~/.cursor/mcp.jsonRestart Cursor for changes to take effect.
  • VS Code.vscode/mcp.jsonReload VS Code window for changes to take effect.
  • Windsurf~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect.
  • Claude Code.mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.

Learn More