gutenberg-mcp-server

v0.3.2 pre-1.0

Search, browse, and read 78,000+ public-domain books from Project Gutenberg with full plain-text retrieval and offset/limit chunking via MCP. STDIO or Streamable HTTP.

gutenberg.caseyjhand.com/mcp
claude mcp add --transport http gutenberg-mcp-server https://gutenberg.caseyjhand.com/mcp
codex mcp add gutenberg-mcp-server --url https://gutenberg.caseyjhand.com/mcp
{
  "mcpServers": {
    "gutenberg-mcp-server": {
      "url": "https://gutenberg.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http gutenberg-mcp-server https://gutenberg.caseyjhand.com/mcp
{
  "mcpServers": {
    "gutenberg-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://gutenberg.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "gutenberg-mcp-server": {
      "type": "http",
      "url": "https://gutenberg.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://gutenberg.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

4

gutenberg_search_books

open-world

Search the Project Gutenberg catalog of 78,000+ books. Every query word matches a title or author substring; filter by topic, language, translator, author lifespan, IDs, MIME prefix or copyright status. More than four query words, and the translator filter, require the local mirror. Empty matches succeed with criteria and a notice. Default popularity uses downloads in the last 30 days at the source snapshot. Returns IDs, titles, authors, languages, subjects, counts, and is_readable — use gutenberg_get_book for full metadata.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gutenberg_search_books",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "mime_type": {
      "description": "Literal prefix of a format MIME key, such as \"text/plain\" or \"application/pdf\". Type and subtype casing is normalized; parameter suffixes retain literal case. Blank means unset.",
      "type": "string"
    },
    "copyright": {
      "description": "Any selected source copyright status: \"true\" copyrighted, \"false\" public domain in the USA, \"null\" unknown. Empty means all statuses; this is not worldwide legal clearance.",
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "true",
          "false",
          "null"
        ]
      }
    },
    "query": {
      "description": "Every nonblank whitespace-separated word must match title or author name as a literal substring. The local mirror ignores case and accents; live fallback may require accents and rejects more than four words. Example: \"dickens expectations\" matches Great Expectations.",
      "type": "string"
    },
    "topic": {
      "description": "Phrase to match against subjects and bookshelves, ignoring case and, on the local mirror, accents. Example: \"detective\" matches the \"Detective Fiction\" bookshelf and \"Detective and mystery stories\" subject. Separate from query — topic searches categorization metadata, not title/author.",
      "type": "string"
    },
    "translator": {
      "description": "Every nonblank whitespace-separated word must match a translator name as a literal substring, ignoring case and accents; words can land on different translators of one book. Requires the local mirror. Example: \"maude\" finds the Aylmer and Louise Maude translations. Result rows omit translators; gutenberg_get_book reports them.",
      "type": "string"
    },
    "languages": {
      "description": "Filter to books in any of these catalog language codes, exactly as results report them (\"en\", not \"eng\"). Example: [\"en\"] for English, [\"fr\", \"de\"] for French or German, [\"enm\"] for Middle English.",
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 2,
        "maxLength": 3
      }
    },
    "author_year_start": {
      "description": "Include only books with an author who has a known birth or death year on or after this year (positive = CE, negative = BCE). With author_year_end, one author must meet both bounds.",
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "author_year_end": {
      "description": "Include only books with an author who has a known birth or death year on or before this year. With author_year_start, one author must meet both bounds. Example: author_year_start=1800 with author_year_end=1899 returns books with 19th-century authors.",
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    },
    "sort": {
      "default": "popular",
      "description": "Result ordering. \"popular\" (default) sorts by downloads in the last 30 days at the source snapshot, descending. \"ascending\" and \"descending\" sort by Gutenberg ID number.",
      "type": "string",
      "enum": [
        "popular",
        "ascending",
        "descending"
      ]
    },
    "ids": {
      "description": "Narrow results to these Gutenberg IDs, intersected with other filters. An empty list selects nothing; omit for unrestricted IDs. Use gutenberg_get_book for full single-ID metadata.",
      "type": "array",
      "items": {
        "type": "integer",
        "exclusiveMinimum": 0,
        "maximum": 9007199254740991
      }
    },
    "page": {
      "default": 1,
      "description": "Page number for paginated results (1-indexed). Each page returns up to 32 books. Use totalCount to determine total pages.",
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "sort",
    "page"
  ],
  "additionalProperties": false
}
view source ↗

gutenberg_get_book

open-world

Fetch complete metadata for a Project Gutenberg book by ID — title, authors (with birth/death years), translators, editors, source bibliographic notes, subjects, bookshelves, languages, copyright status, and the full formats map with download URLs (plain text, HTML, EPUB, cover image, etc.). Use is_readable before gutenberg_get_text; source_notes may identify specific editions.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gutenberg_get_book",
    "arguments": {
      "id": "<id>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Project Gutenberg book ID. Visible in Gutenberg URLs (e.g., gutenberg.org/ebooks/1342) and returned by gutenberg_search_books and gutenberg_browse_popular. Example: 1342 for Pride and Prejudice, 2600 for War and Peace."
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}
view source ↗

gutenberg_get_text

open-world

Retrieve the plain-text content of a Project Gutenberg book, stripped of the standard license header and footer. Front matter such as producer credits, the title page, contents, and prefaces stays in the text. For long works — novels routinely run 500KB–2MB — use offset and limit to read in chunks rather than fetching the whole book at once, or pass find to jump to a known passage without paging. The response reports totalChars and remainingChars for reliable pagination. Prefers UTF-8 plain text; falls back to an HTML edition converted to text; refuses audio books (media_type "Sound") with a clear error.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gutenberg_get_text",
    "arguments": {
      "id": "<id>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "maximum": 9007199254740991,
      "description": "Project Gutenberg book ID. Use gutenberg_search_books or gutenberg_get_book to find IDs. Example: 1342 for Pride and Prejudice, 2600 for War and Peace, 84 for Frankenstein."
    },
    "offset": {
      "default": 0,
      "description": "Character offset into the stripped literary text at which to start reading. 0 returns the beginning of the work. To read subsequent chunks, use offset = prior_offset + prior_length (the length field from the previous response — NOT offset + limit, because the actual returned length may be slightly less than limit due to paragraph-boundary trimming). With find, the search starts here and the returned chunk starts at the match.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "limit": {
      "default": 20000,
      "description": "Requested maximum characters. Default 20,000; requests above 50,000 are capped locally at 50,000. Paragraph-boundary trimming may return less than the applied limit — use length for continuation. Trimming never cuts before a find match ends.",
      "type": "integer",
      "minimum": 1,
      "maximum": 9007199254740991
    },
    "find": {
      "description": "A passage to locate instead of paging to it: the response starts at the first occurrence at or after offset and adds match, and an absent passage fails with passage_not_found rather than returning other text. Matching ignores letter case and whitespace around find. Any run of spaces, tabs, or line breaks matches any other such run, so a hard-wrapped line still matches. Straight and curly quotes match each other. Accents count: \"cafe\" does not match \"café\". Everything else is literal, with no wildcards or patterns, so an italic word stored as _word_ needs its underscores. A blank find is an ordinary read.",
      "type": "string"
    }
  },
  "required": [
    "id",
    "offset",
    "limit"
  ],
  "additionalProperties": false
}
view source ↗

Extensions

1

io.modelcontextprotocol/skills

{
  "directoryRead": true
}