Koskamo

gutenberg-mcp-server

An MCP server that provides tools to search the Project Gutenberg catalog, fetch complete book metadata, and retrieve plain-text content with chunking capabilities. It supports filtering by title, author, topic, language, and copyright status, enabling agents to access over 78,000 public-domain ebooks.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

gutenberg-mcp-server logo, by cyanheadsCommunity listingStreamable HTTPNo sign-inEndpoint checked 2026-11-06Published by cyanheads
MCP endpoint (Streamable HTTP)
https://gutenberg.caseyjhand.com/mcp
Vendor documentation

What its tools do

4 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

4 of 4 have a description · 4 of 4 declare behavior hints · 4 of 4 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Search the Project Gutenberg catalog by title, author, or topic
  • Fetch complete metadata for a specific book by ID
  • Retrieve plain-text content of books with offset/limit chunking
  • Browse popular books filtered by language or subject

Source and maintenance

Community listingRegistry 0.3.2
Maintenance checked

Protocol and capabilities

Server
gutenberg-mcp-server 0.3.2
MCP protocol negotiated in check
2025-11-25
Tools
4 advertised
Resources
1 advertised
Resource templates
1 advertised
Prompts
0 advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  },
  "logging": {},
  "prompts": {
    "listChanged": true
  },
  "resources": {
    "subscribe": true,
    "listChanged": true
  },
  "extensions": {
    "io.modelcontextprotocol/skills": {
      "directoryRead": true
    }
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Project Gutenberg MCP server. No API key required. Typical workflow: gutenberg_search_books → gutenberg_get_book → gutenberg_get_text; search rows already carry is_readable, so gutenberg_get_book is needed only for formats, translators, or source notes. Use gutenberg_browse_popular for discovery. gutenberg_get_text supports offset/limit chunking for long works — novels routinely exceed 500KB — and find to jump to a known passage. Check is_readable to select eligible Text records with UTF-8 plain text, or HTML without audio formats; it does not guarantee the mirror file exists. has_plain_text reports UTF-8 plain-text availability only. The local catalog mirror ignores case and accents; live matching may require accents, and passage find remains accent-sensitive. Popularity uses downloads in the last 30 days at the source snapshot. Outside code spans and fences, a backslash before punctuation in content[] is Markdown encoding; structuredContent carries every string as written. The gutenberg-guide skill (skill://gutenberg-guide/SKILL.md) carries the gates for readability, empty searches, front matter, chapters, paging, and ranking editions.

Tools

4 advertised

gutenberg_search_books — Search Gutenberg Books
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-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.

gutenberg_search_books input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "ids": {
      "type": "array",
      "items": {
        "type": "integer",
        "maximum": 9007199254740991,
        "exclusiveMinimum": 0
      },
      "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."
    },
    "page": {
      "type": "integer",
      "default": 1,
      "maximum": 9007199254740991,
      "description": "Page number for paginated results (1-indexed). Each page returns up to 32 books. Use totalCount to determine total pages.",
      "exclusiveMinimum": 0
    },
    "sort": {
      "enum": [
        "popular",
        "ascending",
        "descending"
      ],
      "type": "string",
      "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."
    },
    "query": {
      "type": "string",
      "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."
    },
    "topic": {
      "type": "string",
      "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."
    },
    "copyright": {
      "type": "array",
      "items": {
        "enum": [
          "true",
          "false",
          "null"
        ],
        "type": "string"
      },
      "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."
    },
    "languages": {
      "type": "array",
      "items": {
        "type": "string",
        "maxLength": 3,
        "minLength": 2
      },
      "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."
    },
    "mime_type": {
      "type": "string",
      "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."
    },
    "translator": {
      "type": "string",
      "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."
    },
    "author_year_end": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": -9007199254740991,
      "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."
    },
    "author_year_start": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": -9007199254740991,
      "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."
    }
  },
  "additionalProperties": false
}
gutenberg_search_books output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "books",
        "totalCount",
        "page",
        "hasMore",
        "effectiveCriteria"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "page": {
      "type": "number",
      "description": "Current page number."
    },
    "books": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "id",
          "title",
          "authors",
          "languages",
          "subjects",
          "download_count",
          "has_plain_text",
          "is_readable"
        ],
        "properties": {
          "id": {
            "type": "number",
            "description": "Gutenberg ID — pass to gutenberg_get_book for the full record or gutenberg_get_text to read the book."
          },
          "title": {
            "type": "string",
            "description": "Book title."
          },
          "authors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "birth_year",
                "death_year"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Author name in \"Last, First\" format."
                },
                "birth_year": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Author birth year, or null if unknown."
                },
                "death_year": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Author death year, or null if unknown or still living."
                }
              },
              "description": "Author entry.",
              "additionalProperties": false
            },
            "description": "Author(s) of the work."
          },
          "subjects": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Library of Congress subject headings."
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Catalog language codes for this edition."
          },
          "is_readable": {
            "type": "boolean",
            "description": "Eligible Text record with UTF-8 plain text, or HTML without audio formats. Does not guarantee the mirror file exists."
          },
          "download_count": {
            "type": "number",
            "description": "Downloads in the last 30 days at the source snapshot."
          },
          "has_plain_text": {
            "type": "boolean",
            "description": "True for Text records with UTF-8 plain text; HTML-only editions can still have is_readable=true."
          }
        },
        "description": "Book entry.",
        "additionalProperties": false
      },
      "description": "Matching books, ordered by the sort parameter."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "translator_requires_mirror",
                "query_requires_mirror",
                "page_out_of_range",
                "catalog_unavailable"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `translator_requires_mirror`: A translator filter was supplied while the local mirror could not answer. `query_requires_mirror`: More than four query words were supplied while the local mirror could not answer. `page_out_of_range`: The requested page number is past the last page of results for this query. `catalog_unavailable`: The Project Gutenberg catalog did not answer within the time this server allows for one search. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "notice": {
      "type": "string",
      "description": "Actionable guidance for an empty selection, or a caveat that the live catalog checked author_year_start and author_year_end separately. The caveat can accompany results."
    },
    "hasMore": {
      "type": "boolean",
      "description": "True if there are additional pages of results."
    },
    "totalCount": {
      "type": "number",
      "description": "Total number of books matching the query across all pages."
    },
    "effectiveCriteria": {
      "type": "object",
      "required": [
        "sort",
        "page"
      ],
      "properties": {
        "ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "maximum": 9007199254740991,
            "exclusiveMinimum": 0
          },
          "description": "Restrict to these Gutenberg IDs, intersected with other filters. An empty list selects nothing; omit for unrestricted IDs."
        },
        "page": {
          "type": "integer",
          "default": 1,
          "maximum": 9007199254740991,
          "description": "One-indexed page of at most 32 matches. Pages beyond the result range are errors.",
          "exclusiveMinimum": 0
        },
        "sort": {
          "enum": [
            "popular",
            "ascending",
            "descending"
          ],
          "type": "string",
          "default": "popular",
          "description": "Popular orders by source-snapshot downloads in the last 30 days, with ID tie-breaks locally; ascending and descending order by Gutenberg ID."
        },
        "query": {
          "type": "string",
          "description": "Every nonblank word must occur in the title or an author name as a literal substring. The local mirror ignores case and accents; live matching is upstream-defined. More than four words require the local mirror. Whitespace separates words."
        },
        "topic": {
          "type": "string",
          "description": "Phrase in a subject or bookshelf, ignoring case and, on the local mirror, accents; blank means unset."
        },
        "copyright": {
          "type": "array",
          "items": {
            "enum": [
              "true",
              "false",
              "null"
            ],
            "type": "string"
          },
          "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."
        },
        "languages": {
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 3,
            "minLength": 2
          },
          "description": "Any listed two- or three-letter catalog language code; empty means all languages."
        },
        "mime_type": {
          "type": "string",
          "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."
        },
        "translator": {
          "type": "string",
          "description": "Every nonblank word must occur in a translator name as a literal substring, ignoring case and accents. Requires the local mirror. Whitespace separates words."
        },
        "author_year_end": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "An author has a known birth or death year on or before this year (CE positive, BCE negative). With author_year_start, one author must meet both bounds."
        },
        "author_year_start": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "An author has a known birth or death year on or after this year (CE positive, BCE negative). With author_year_end, one author must meet both bounds."
        }
      },
      "description": "Normalized search criteria, including sort and page defaults.",
      "additionalProperties": false
    }
  },
  "additionalProperties": false
}
gutenberg_search_books annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
gutenberg_get_book — Get Gutenberg Book
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-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.

gutenberg_get_book input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "id"
  ],
  "properties": {
    "id": {
      "type": "integer",
      "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.",
      "exclusiveMinimum": 0
    }
  },
  "additionalProperties": false
}
gutenberg_get_book output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "id",
        "title",
        "authors",
        "translators",
        "editors",
        "subjects",
        "bookshelves",
        "languages",
        "copyright",
        "media_type",
        "download_count",
        "source_notes",
        "is_readable",
        "summary",
        "summaries",
        "formats",
        "has_plain_text"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "id": {
      "type": "number",
      "description": "Gutenberg ID."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "not_found",
                "catalog_unavailable"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `not_found`: No book exists with the given ID. `catalog_unavailable`: The Project Gutenberg catalog did not answer within the time this server allows for one lookup. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "title": {
      "type": "string",
      "description": "Book title."
    },
    "authors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "birth_year",
          "death_year"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Author name in \"Last, First\" format."
          },
          "birth_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Birth year, or null if unknown."
          },
          "death_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Death year, or null if unknown or still living."
          }
        },
        "description": "Author entry.",
        "additionalProperties": false
      },
      "description": "Primary author(s)."
    },
    "editors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "birth_year",
          "death_year"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Editor name."
          },
          "birth_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Birth year, or null if unknown."
          },
          "death_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Death year, or null if unknown."
          }
        },
        "description": "Editor entry.",
        "additionalProperties": false
      },
      "description": "Editors, if any."
    },
    "formats": {
      "type": "object",
      "description": "Map of MIME type to download URL. Key types: \"text/plain; charset=utf-8\" (preferred for gutenberg_get_text), \"text/html\", \"application/epub+zip\", \"image/jpeg\" (cover). Not every format is present for every book.",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string"
      }
    },
    "summary": {
      "type": [
        "string",
        "null"
      ],
      "description": "First entry of summaries, not necessarily primary, or null when the source has none."
    },
    "subjects": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Library of Congress subject headings."
    },
    "copyright": {
      "type": [
        "boolean",
        "null"
      ],
      "description": "Copyright status: false = public domain in the USA, true = under copyright, null = unknown."
    },
    "languages": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Catalog language codes for this edition."
    },
    "summaries": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Every source-provided summary: sorted on the RDF mirror, upstream order on live fallback. Empty when there are none."
    },
    "media_type": {
      "type": "string",
      "description": "Source media type, such as \"Text\" or \"Sound\" (audio). Use is_readable to check reader support."
    },
    "bookshelves": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Project Gutenberg bookshelf categories (e.g., \"Best Books Ever Listings\", \"Category: Classics of Literature\")."
    },
    "is_readable": {
      "type": "boolean",
      "description": "Eligible Text record with UTF-8 plain text, or HTML without audio formats. Does not guarantee the mirror file exists."
    },
    "translators": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "birth_year",
          "death_year"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Translator name."
          },
          "birth_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Birth year, or null if unknown."
          },
          "death_year": {
            "type": [
              "number",
              "null"
            ],
            "description": "Death year, or null if unknown."
          }
        },
        "description": "Translator entry.",
        "additionalProperties": false
      },
      "description": "Translators, if this is a translated work."
    },
    "source_notes": {
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "description": "Source-provided RDF bibliographic notes in document order, including explicit edition references. [] means inspected with no notes; null means unavailable from this source."
    },
    "download_count": {
      "type": "number",
      "description": "Downloads in the last 30 days at the source snapshot, used for popularity ranking."
    },
    "has_plain_text": {
      "type": "boolean",
      "description": "True if media_type is \"Text\" and \"text/plain; charset=utf-8\" is present. HTML-only editions can still have is_readable=true."
    }
  },
  "additionalProperties": false
}
gutenberg_get_book annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
gutenberg_get_text — Get Gutenberg Book Text
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-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.

gutenberg_get_text input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "id"
  ],
  "properties": {
    "id": {
      "type": "integer",
      "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.",
      "exclusiveMinimum": 0
    },
    "find": {
      "type": "string",
      "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."
    },
    "limit": {
      "type": "integer",
      "default": 20000,
      "maximum": 9007199254740991,
      "minimum": 1,
      "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."
    },
    "offset": {
      "type": "integer",
      "default": 0,
      "maximum": 9007199254740991,
      "minimum": 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."
    }
  },
  "additionalProperties": false
}
gutenberg_get_text output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "id",
        "title",
        "text",
        "offset",
        "length",
        "requestedLimit",
        "appliedLimit",
        "totalChars",
        "remainingChars",
        "hasMore",
        "provenance",
        "sourceFormat"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "id": {
      "type": "number",
      "description": "Gutenberg book ID."
    },
    "text": {
      "type": "string",
      "description": "The requested chunk of literary text, stripped of Gutenberg license boilerplate. Encoding: UTF-8. Line endings: normalized to LF."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "text_file_not_found",
                "text_fetch_rejected",
                "invalid_text_response",
                "not_found",
                "audio_book",
                "no_text_format",
                "offset_out_of_range",
                "passage_not_found",
                "text_fetch_failed",
                "catalog_unavailable"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `text_file_not_found`: The catalog entry exists but its mirror text file returned HTTP 404. `text_fetch_rejected`: The mirror permanently rejected this text request. `invalid_text_response`: The mirror returned an empty document, HTML on a plain-text path, or recognizable server failure document. `not_found`: No book exists with the given ID. `audio_book`: The book is an audio recording (media_type \"Sound\"), not a text book — no literary plain text is available. `no_text_format`: The book has no UTF-8 plain-text or HTML edition available to read (for example an ASCII-only or PDF/TeX-only entry). `offset_out_of_range`: The offset is greater than or equal to totalChars (past the end of the book). `passage_not_found`: find has no occurrence at or after offset. `text_fetch_failed`: The Gutenberg mirror had a transient failure or the download did not finish within the remaining upstream allowance. `catalog_unavailable`: The catalog lookup that precedes reading a book did not answer within the time this server allows. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "match": {
      "type": "object",
      "required": [
        "offset",
        "length",
        "laterMatches"
      ],
      "properties": {
        "length": {
          "type": "number",
          "description": "Characters the match spans in the stored text. Can differ from the length of find, since a whitespace run matches a run of any length."
        },
        "offset": {
          "type": "number",
          "description": "Start of the match, in the same coordinates as offset and totalChars."
        },
        "laterMatches": {
          "type": "number",
          "description": "Non-overlapping occurrences after this one. While above 0, repeat the same find with offset = match.offset + match.length for the next."
        }
      },
      "description": "Where find matched. Absent on a read without find.",
      "additionalProperties": false
    },
    "title": {
      "type": "string",
      "description": "Book title, from the catalog record."
    },
    "length": {
      "type": "number",
      "description": "Number of characters in this chunk."
    },
    "offset": {
      "type": "number",
      "description": "Character offset where this chunk begins: the match start when find matched."
    },
    "hasMore": {
      "type": "boolean",
      "description": "True if there is more text after this chunk. When true, call again with offset = offset + length and no find."
    },
    "provenance": {
      "type": "string",
      "description": "One-line source note with Project Gutenberg ID, title, and license URL."
    },
    "totalChars": {
      "type": "number",
      "description": "Total characters in the stripped literary text. Use with offset and length to determine progress and plan subsequent calls."
    },
    "appliedLimit": {
      "type": "number",
      "description": "Character limit applied to this chunk, at most 50,000."
    },
    "sourceFormat": {
      "enum": [
        "text/plain; charset=utf-8",
        "text/plain; charset=us-ascii",
        "text/html"
      ],
      "type": "string",
      "description": "The format that was fetched. \"text/html\" indicates HTML-to-text conversion was applied because no plain-text format was available."
    },
    "remainingChars": {
      "type": "number",
      "description": "Characters remaining after this chunk (totalChars - offset - length). 0 means this chunk includes the end of the book."
    },
    "requestedLimit": {
      "type": "number",
      "description": "Positive integral character limit requested by the caller, before capping."
    }
  },
  "additionalProperties": false
}
gutenberg_get_text annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}
gutenberg_browse_popular — Browse Popular Gutenberg Books
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Browse Project Gutenberg books ordered by downloads in the last 30 days at the source snapshot. Returns up to 32 titles with IDs, authors, languages, download counts, and is_readable. Filter by language, topic, MIME prefix or copyright status. Empty matches succeed with criteria and a notice; truncated results include executable search continuation. Use this for discovery — "what are the most popular classics in French?".

gutenberg_browse_popular input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "limit": {
      "type": "integer",
      "default": 20,
      "maximum": 9007199254740991,
      "minimum": 1,
      "description": "Requested number of books. Default 20; requests above 32 are capped locally at 32."
    },
    "topic": {
      "type": "string",
      "description": "Filter by a subject or bookshelf keyword (case-insensitive phrase match). Example: \"science fiction\", \"adventure\", \"detective\". Applies on top of the language filter."
    },
    "copyright": {
      "type": "array",
      "items": {
        "enum": [
          "true",
          "false",
          "null"
        ],
        "type": "string"
      },
      "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."
    },
    "languages": {
      "type": "array",
      "items": {
        "type": "string",
        "maxLength": 3,
        "minLength": 2
      },
      "description": "Restrict to books in these catalog language codes, exactly as results report them (\"en\", not \"eng\"). Example: [\"en\"] for English only, [\"de\", \"fr\"] for German or French, [\"ang\"] for Old English. Omit for all languages."
    },
    "mime_type": {
      "type": "string",
      "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."
    }
  },
  "additionalProperties": false
}
gutenberg_browse_popular output schema
{
  "type": "object",
  "anyOf": [
    {
      "not": {
        "required": [
          "error"
        ]
      },
      "required": [
        "requestedLimit",
        "appliedLimit",
        "books",
        "totalInCatalog",
        "effectiveCriteria",
        "truncated",
        "shown",
        "cap"
      ]
    },
    {
      "required": [
        "error"
      ]
    }
  ],
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "cap": {
      "type": "number",
      "description": "The limit that was applied."
    },
    "books": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "id",
          "title",
          "authors",
          "languages",
          "download_count",
          "has_plain_text",
          "is_readable"
        ],
        "properties": {
          "id": {
            "type": "number",
            "description": "Gutenberg ID."
          },
          "title": {
            "type": "string",
            "description": "Book title."
          },
          "authors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "birth_year",
                "death_year"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "Author name in \"Last, First\" format."
                },
                "birth_year": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Birth year, or null if unknown."
                },
                "death_year": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Death year, or null if unknown or still living."
                }
              },
              "description": "Author entry.",
              "additionalProperties": false
            },
            "description": "Author(s)."
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Language codes."
          },
          "is_readable": {
            "type": "boolean",
            "description": "Eligible Text record with UTF-8 plain text, or HTML without audio formats. Does not guarantee the mirror file exists."
          },
          "download_count": {
            "type": "number",
            "description": "Downloads in the last 30 days at the source snapshot — the popularity ranking basis."
          },
          "has_plain_text": {
            "type": "boolean",
            "description": "True for Text records with UTF-8 plain text; HTML-only editions can still have is_readable=true."
          }
        },
        "description": "Book entry.",
        "additionalProperties": false
      },
      "description": "Top books by downloads in the last 30 days at the source snapshot, most popular first."
    },
    "error": {
      "type": "object",
      "required": [
        "code",
        "message"
      ],
      "properties": {
        "code": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "JSON-RPC error code for this failure."
        },
        "data": {
          "type": "object",
          "properties": {
            "reason": {
              "type": "string",
              "examples": [
                "catalog_unavailable"
              ],
              "description": "Machine-readable failure mode. Declared by this tool: `catalog_unavailable`: The Project Gutenberg catalog did not answer within the time this server allows for one browse. Other values are possible when a failure originates below the handler."
            },
            "recovery": {
              "type": "object",
              "required": [
                "hint"
              ],
              "properties": {
                "hint": {
                  "type": "string"
                }
              },
              "description": "Actionable next step for the caller.",
              "additionalProperties": {}
            },
            "retryable": {
              "type": "boolean",
              "description": "Whether retrying may succeed."
            }
          },
          "additionalProperties": {}
        },
        "message": {
          "type": "string",
          "description": "Human-readable description of what went wrong."
        }
      },
      "description": "Present when the call failed. Absent on success.",
      "additionalProperties": {}
    },
    "shown": {
      "type": "number",
      "description": "Number of books returned in this response."
    },
    "notice": {
      "type": "string",
      "description": "Guidance for empty or capped results."
    },
    "truncated": {
      "type": "boolean",
      "description": "True when the catalog held more matches than were returned."
    },
    "appliedLimit": {
      "type": "number",
      "description": "Book limit applied locally, at most 32."
    },
    "continuation": {
      "type": "object",
      "required": [
        "tool",
        "arguments"
      ],
      "properties": {
        "tool": {
          "type": "string",
          "const": "gutenberg_search_books",
          "description": "Tool to call for the remaining matching books."
        },
        "arguments": {
          "type": "object",
          "required": [
            "sort",
            "page"
          ],
          "properties": {
            "ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "maximum": 9007199254740991,
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these Gutenberg IDs, intersected with other filters. An empty list selects nothing; omit for unrestricted IDs."
            },
            "page": {
              "type": "integer",
              "default": 1,
              "maximum": 9007199254740991,
              "description": "One-indexed page of at most 32 matches. Pages beyond the result range are errors.",
              "exclusiveMinimum": 0
            },
            "sort": {
              "enum": [
                "popular",
                "ascending",
                "descending"
              ],
              "type": "string",
              "default": "popular",
              "description": "Popular orders by source-snapshot downloads in the last 30 days, with ID tie-breaks locally; ascending and descending order by Gutenberg ID."
            },
            "query": {
              "type": "string",
              "description": "Every nonblank word must occur in the title or an author name as a literal substring. The local mirror ignores case and accents; live matching is upstream-defined. More than four words require the local mirror. Whitespace separates words."
            },
            "topic": {
              "type": "string",
              "description": "Phrase in a subject or bookshelf, ignoring case and, on the local mirror, accents; blank means unset."
            },
            "copyright": {
              "type": "array",
              "items": {
                "enum": [
                  "true",
                  "false",
                  "null"
                ],
                "type": "string"
              },
              "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."
            },
            "languages": {
              "type": "array",
              "items": {
                "type": "string",
                "maxLength": 3,
                "minLength": 2
              },
              "description": "Any listed two- or three-letter catalog language code; empty means all languages."
            },
            "mime_type": {
              "type": "string",
              "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."
            },
            "translator": {
              "type": "string",
              "description": "Every nonblank word must occur in a translator name as a literal substring, ignoring case and accents. Requires the local mirror. Whitespace separates words."
            },
            "author_year_end": {
              "type": "integer",
              "maximum": 9007199254740991,
              "minimum": -9007199254740991,
              "description": "An author has a known birth or death year on or before this year (CE positive, BCE negative). With author_year_start, one author must meet both bounds."
            },
            "author_year_start": {
              "type": "integer",
              "maximum": 9007199254740991,
              "minimum": -9007199254740991,
              "description": "An author has a known birth or death year on or after this year (CE positive, BCE negative). With author_year_end, one author must meet both bounds."
            }
          },
          "description": "Executable search arguments preserving all filters. Page 1 may repeat already shown books when the browse cap cuts a page.",
          "additionalProperties": false
        }
      },
      "description": "Present only when more matching books exist.",
      "additionalProperties": false
    },
    "requestedLimit": {
      "type": "number",
      "description": "Positive integral book limit requested before capping."
    },
    "totalInCatalog": {
      "type": "number",
      "description": "Total books matching the filter in the full catalog (useful for context — \"top 20 of 60,000\")."
    },
    "effectiveCriteria": {
      "type": "object",
      "required": [
        "sort",
        "page"
      ],
      "properties": {
        "ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "maximum": 9007199254740991,
            "exclusiveMinimum": 0
          },
          "description": "Restrict to these Gutenberg IDs, intersected with other filters. An empty list selects nothing; omit for unrestricted IDs."
        },
        "page": {
          "type": "integer",
          "default": 1,
          "maximum": 9007199254740991,
          "description": "One-indexed page of at most 32 matches. Pages beyond the result range are errors.",
          "exclusiveMinimum": 0
        },
        "sort": {
          "enum": [
            "popular",
            "ascending",
            "descending"
          ],
          "type": "string",
          "default": "popular",
          "description": "Popular orders by source-snapshot downloads in the last 30 days, with ID tie-breaks locally; ascending and descending order by Gutenberg ID."
        },
        "query": {
          "type": "string",
          "description": "Every nonblank word must occur in the title or an author name as a literal substring. The local mirror ignores case and accents; live matching is upstream-defined. More than four words require the local mirror. Whitespace separates words."
        },
        "topic": {
          "type": "string",
          "description": "Phrase in a subject or bookshelf, ignoring case and, on the local mirror, accents; blank means unset."
        },
        "copyright": {
          "type": "array",
          "items": {
            "enum": [
              "true",
              "false",
              "null"
            ],
            "type": "string"
          },
          "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."
        },
        "languages": {
          "type": "array",
          "items": {
            "type": "string",
            "maxLength": 3,
            "minLength": 2
          },
          "description": "Any listed two- or three-letter catalog language code; empty means all languages."
        },
        "mime_type": {
          "type": "string",
          "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."
        },
        "translator": {
          "type": "string",
          "description": "Every nonblank word must occur in a translator name as a literal substring, ignoring case and accents. Requires the local mirror. Whitespace separates words."
        },
        "author_year_end": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "An author has a known birth or death year on or before this year (CE positive, BCE negative). With author_year_start, one author must meet both bounds."
        },
        "author_year_start": {
          "type": "integer",
          "maximum": 9007199254740991,
          "minimum": -9007199254740991,
          "description": "An author has a known birth or death year on or after this year (CE positive, BCE negative). With author_year_end, one author must meet both bounds."
        }
      },
      "description": "Normalized browse criteria, including popular sort and first page.",
      "additionalProperties": false
    },
    "truncationCeiling": {
      "type": "number",
      "description": "Downloads in the last 30 days at the source snapshot for the least-popular book shown; omitted books have at most this count."
    }
  },
  "additionalProperties": false
}
gutenberg_browse_popular annotations
{
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true
}

Prompts

0 advertised

No prompt definitions available.

Resources

1 advertised

gutenberg-guide

Advertised by the server during the last successful anonymous check.

Gates and workflows for the gutenberg_* tools over Project Gutenberg. Covers deciding whether a book can be read, why a title, accent, or translator search comes back empty, finding a novel's opening or a chapter inside a book, jumping to a known passage or paging long works, ranking reading lists by downloads, and comparing editions and translations. Use when finding, reading, quoting, or recommending books from Project Gutenberg.

gutenberg-guide URI
skill://gutenberg-guide/SKILL.md

MIME type: text/markdown

io.modelcontextprotocol/skills

Advertised by the server during the last successful anonymous check.

io.modelcontextprotocol/skills URI template
skill://{+path}

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (1 of 1 answered).

  1. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
725 ms
95th percentile latency
725 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 725 ms · metadata changed · 4 tool(s), 1 resource(s), 1 resource template(s), 0 prompt(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http gutenberg-mcp-server https://gutenberg.caseyjhand.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add gutenberg-mcp-server --url https://gutenberg.caseyjhand.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable gutenberg-mcp-server in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "gutenberg-mcp-server": {
      "url": "https://gutenberg.caseyjhand.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "gutenberg-mcp-server": {
      "type": "streamableHttp",
      "url": "https://gutenberg.caseyjhand.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start gutenberg-mcp-server and inspect its tools.
VS Code configuration
{
  "servers": {
    "gutenberg-mcp-server": {
      "type": "http",
      "url": "https://gutenberg.caseyjhand.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http gutenberg-mcp-server https://gutenberg.caseyjhand.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://gutenberg.caseyjhand.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "gutenberg-mcp-server": {
      "serverUrl": "https://gutenberg.caseyjhand.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like gutenberg-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.