.codex-plugin — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited .codex-plugin (MCP Server) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<div align="center"> <h1>@cyanheads/openlibrary-mcp-server</h1> <p><b>Search books and authors, fetch editions, browse subjects, and resolve cover images from Open Library via MCP. STDIO or Streamable HTTP.</b> <div>10 Tools • 2 Catalog Resources • 1 Agent Skill</div> </p> </div>
<div align="center">
</div>
<div align="center">
Public Hosted Server: https://openlibrary.caseyjhand.com/mcp
</div>
Open Library's catalog of 20M+ books, editions, authors, and subjects, plus full-text search inside Internet Archive's scanned books. Search and browse, drill from a work into its editions or an author into their works, and construct cover image URLs.
| Tool | Description |
|---|---|
openlibrary_search_books | Catalog search with field filters (title, author, subject, publisher, ISBN, language), a provider-selected edition, pagination, and optional live reading availability |
openlibrary_get_work | Fetch a work by Open Library Work ID (OL…W) — title, description, subjects, cover IDs, and author IDs |
openlibrary_get_editions | List editions of a work — publishers, languages, formats, ISBNs, and print run details |
openlibrary_get_edition | Resolve up to 50 distinct identifiers per call by ISBN-10, ISBN-13, OCLC, LCCN, or Open Library Edition ID (OL…M), reporting misses and unprocessed identifiers |
openlibrary_search_authors | Search authors by name — returns Author IDs, birth/death dates, top works, and subject associations |
openlibrary_get_author | Fetch author detail by Open Library Author ID (OL…A) — bio, dates, photo IDs, and linked identifiers from Wikidata, VIAF, ISNI, Goodreads, and LibraryThing |
openlibrary_get_author_works | List works by an author — titles, cover IDs, and Work OLIDs for drilling into editions or details |
openlibrary_get_subject | Browse works by subject tag — returns matching works with edition counts and cover IDs plus the total work count |
openlibrary_search_inside | Full-text search inside the scanned text of Internet Archive books — returns matching items with snippets |
openlibrary_get_cover_url | Resolve a cover image URL for a book or author photo in S/M/L size — returns a direct HTTPS URL embeddable in markdown |
| Resource | Description |
|---|---|
openlibrary://works/{work_id} | Work detail by Open Library Work ID — title, description, subjects, cover IDs, and author IDs as injectable context |
openlibrary://authors/{author_id} | Author detail by Open Library Author ID — name, bio, dates, photo IDs, and linked external identifiers as injectable context |
Both resources mirror data also available via openlibrary_get_work and openlibrary_get_author — useful for clients that don't surface MCP resources.
openlibrary-guide covers edition selection, bibliography resolution, passage identification, and incomplete metadata. It is included in npm, Docker, and desktop bundles and served through MCP skill discovery and skill:// resources.
Paginated tools return at most 100 rows per call. Larger positive integer limits are clamped with a notice showing the applied limit and next offset. OPENLIBRARY_CLAMP_LIMITS=false restores schema rejection above 100. Text and structured output carry the same complete domain data, including arrays and numeric precision.
openlibrary_search_books <sub>tool</sub>title:, author:, subject:, publisher:, isbn:, language:) or dedicated filter parameters; 1–100 results per page (default 10), offset paginationsort: relevance (default), new, old, rating, editionslanguage accepts a 3-letter MARC code or a translatable 2-letter ISO code; an untranslatable 2-letter code fails as unknown_language_code rather than being silently droppedinclude_availability adds live Internet Archive borrow/read status (~200ms latency), off by default; flags Open Library leaves out are omitted rather than reported as false, and availability: null means none was returned for the workmatching_edition and matching_edition_count preserve the Search API's selected edition and matching count. The selected edition is provider-ranked; it does not prove that every unfielded search term matches that editioninvalid_search_query reports upstream HTTP 422 validation with recovery guidance; empty queries with specific filters remain supportedopenlibrary_get_work <sub>tool</sub>/works/-prefixed; anything else — an ISBN, an edition or author OLID — fails input validation before any request, naming openlibrary_get_edition (id_type: "isbn") as the route from an ISBN to its workopenlibrary_get_author or openlibrary_search_books)work_id reports the canonical ID, and an enrichment notice names both IDs when they differnot_found when the Work ID doesn't exist or its redirect chain reaches no workopenlibrary_get_editions <sub>tool</sub>/works/-prefixed; 1–100 per page (default 10), offset pagination, with the requested offset echoed in the outputwork_id validation as openlibrary_get_work — an ISBN resolves to its work through openlibrary_get_edition (id_type: "isbn")physical_format and edition_name when recordedwork_id, with an enrichment notice naming both IDs; a live work still costs one requestnot_found when the Work ID doesn't exist or its redirect chain reaches no workopenlibrary_get_edition <sub>tool</sub>unprocessed for resubmission. Every identifier shares one id_type: isbn (10 or 13 digits, an ISBN-10 may end in an X check digit), oclc (numeric), lccn (unchecked), or olid (OL…M). With OPENLIBRARY_CLAMP_LIMITS=false, the input array is limited to 50 entrieseditions (request order); the rest land in unresolved with invalid_identifier (malformed, never sent upstream) or not_found (well-formed, no record) — the call fails only when nothing resolvesupstream_unavailable (retryable) when Open Library's batch lookup itself fails — an HTTP error status other than a rate limit, or an HTML page — so an upstream outage never reads as a missing editionsource: "work" recovered from the parent work when the edition itself lists none; if those lookups fail, the batch still returns, and an enrichment notice names the editions whose authors are missing or shown by IDlanguages, page count, cover IDs, parent work ID, and an Internet Archive ebook_url when one exists. The singular language remains the first cataloged language; physical_format and edition_name preserve the catalog's wording when presentunprocessed identifier in its data and recovery hintopenlibrary_search_authors <sub>tool</sub>openlibrary_get_author <sub>tool</sub>/authors/ prefix is strippednot_found when the Author ID doesn't exist, names a record that is not an author (a work or edition OLID), or its redirect chain reaches no authoropenlibrary_get_author_works <sub>tool</sub>offset echoed in the outputnot_found when the Author ID doesn't exist, names a record that is not an author (a work or edition OLID), or its redirect chain reaches no authoropenlibrary_get_subject <sub>tool</sub>offset echoed in the outputinvalid_subject before any requestopenlibrary_search_inside <sub>tool</sub>ia_identifier, not Open Library work IDs — match it against ia_identifiers from openlibrary_search_books to reach the catalogue recordupstream_unavailable (retryable) when the index answers without a result set, so a failed search never reads as "no book contains this"openlibrary_get_cover_url <sub>tool</sub>id (numeric), isbn (10 or 13 digits, an ISBN-10 may end in an X check digit), or olid (OL…M for target: "book", OL…A for target: "author"); size is S/M/L (default M).., and control characters fail as invalid_identifier, and an author lookup by isbn fails as invalid_targetopenlibrary://works/{work_id} <sub>resource</sub>openlibrary_get_work, as injectable application/json context for a conversation about a specific bookwork_id comes from openlibrary_search_books or openlibrary_get_author_works; a merged ID resolves to the canonical work, whose ID the returned work_id carries, and an ID that is not a work (an edition or author OLID) is not foundopenlibrary://authors/{author_id} <sub>resource</sub>openlibrary_get_author, as injectable application/json context for a conversation about a specific authorauthor_id comes from openlibrary_search_authors; a merged ID resolves to the canonical author, whose ID the returned author_id carries, and an ID that is not an author (a work or edition OLID) is not foundBuilt 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.
Open Library-specific:
User-Agent header (OPENLIBRARY_USER_AGENT) identifying the server per Open Library's bot-blocking conventionAgent-friendly output:
openlibrary_get_author, openlibrary_get_author_works, openlibrary_get_work, and openlibrary_get_editions follow merge redirects and surface the canonical ID via an enrichment notice when a requested ID was mergedopenlibrary_get_edition returns resolved editions alongside typed unresolved reasons instead of failing the whole batchAdd the hosted server to Claude Code — no API key required:
claude mcp add --transport http openlibrary-mcp-server https://openlibrary.caseyjhand.com/mcpOr run it locally:
claude mcp add openlibrary-mcp-server -- npx -y @cyanheads/openlibrary-mcp-serverFor other clients, add this to the MCP config file:
{
"mcpServers": {
"openlibrary-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/openlibrary-mcp-server"]
}
}
}For Docker or a source build, see Running the server.
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path where the MCP server is mounted | /mcp |
MCP_SESSION_MODE | HTTP session posture: stateless, stateful, or auto. Overrides the stateless declared in src/index.ts. | stateless |
MCP_PUBLIC_URL | Public origin override 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). Recommended starting point if heap growth is observed: 60000. | 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 |
OPENLIBRARY_USER_AGENT | User-Agent sent with all Open Library API requests. Include a contact email per community convention. | openlibrary-mcp-server [email protected] |
OPENLIBRARY_CLAMP_LIMITS | Clamp page limits to 100 and process the first 50 distinct edition identifiers, returning the remainder. false restores strict input bounds of 100 rows and 50 raw identifiers. | true |
OTEL_ENABLED | Enable OpenTelemetry | false |
See .env.example for the full list of optional overrides.
Requires Bun v1.4.0 or higher (or Node.js ≥ 24.0.0).
git clone https://github.com/cyanheads/openlibrary-mcp-server.git
cd openlibrary-mcp-server && bun install
cp .env.example .env # optional overrides; no required variables # One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against specdocker run --rm -p 3010:3010 ghcr.io/cyanheads/openlibrary-mcp-server:latestThe image defaults to HTTP transport, stateless session mode, and logs to /var/log/openlibrary-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/openlibrary-mcp-server:latest"]. To build locally, run docker build -t openlibrary-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. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts) — ten tools across Search, Books, Authors, Subjects, and Covers. |
src/mcp-server/resources | Resource definitions (*.resource.ts) — Work and Author. |
src/services/open-library | Open Library service layer — API client and domain types. |
skills/ | Published agent guidance, also served through MCP skill discovery. |
tests/ | Unit and integration tests mirroring the src/ structure. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for logging, ctx.state for storagecreateApp() arraysIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testThis project is licensed under the Apache 2.0 License. See the LICENSE file for details.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.