Skip to content

MCP server

gutenberg_kg.mcp_server

GutenbergKG MCP Server.

Serves the consolidated corpus bundle's DocKG over the fleet's shared server base, :mod:kg_utils.mcp, with GutenbergKG's illustration tools beside it.

Tools

stats, query, pack, get_node, neighbors The fleet's five canonical tools, under the same names as every other KG server, over bundles/gutenberg-all (books, verse and their sections). query and pack are DocKG's own hybrid dense + BM25 search. generate_image Direct text-to-image on a running image server (no VLM planning). corpus_imagine Query the corpus — diaries first, then the books — for context, then generate an illustration.

The image tools call an image server over HTTP -- the one GUTENKG_IMAGE_ENDPOINT names, or the first of image_gen.DEFAULT_IMAGE_ENDPOINTS that answers (make up starts one). They never load a model in-process: mflux is not a dependency of this package.

Run

::

gutenkg mcp # stdio transport (Claude Code / Cursor) gutenkg mcp --manifest # the tool list as JSON, without opening the graph

gutenkg-mcp is the same command. --repo is the GutenbergKG checkout; --db and --vectors default to bundles/gutenberg-all/.dockg/.

build_server(repo, db=None, vectors=None, model=None)

Build the GutenbergKG MCP server, without opening the bundle's graph.

Parameters:

Name Type Description Default
repo str | Path

The GutenbergKG checkout; its corpus/ is the corpus root.

required
db str | Path | None

Bundle graph; None for <repo>/bundles/gutenberg-all/.dockg/graph.sqlite.

None
vectors str | Path | None

Bundle vectors; None for the vectors.sqlite beside db.

None
model str | None

Embedding model; None for DocKG's default.

None

Returns:

Type Description
KGServer

The server; serve it with :func:kg_utils.mcp.run.

Source code in src/gutenberg_kg/mcp_server.py
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
def build_server(
    repo: str | Path,
    db: str | Path | None = None,
    vectors: str | Path | None = None,
    model: str | None = None,
) -> KGServer:
    """Build the GutenbergKG MCP server, without opening the bundle's graph.

    :param repo: The GutenbergKG checkout; its ``corpus/`` is the corpus root.
    :param db: Bundle graph; ``None`` for ``<repo>/bundles/gutenberg-all/.dockg/graph.sqlite``.
    :param vectors: Bundle vectors; ``None`` for the ``vectors.sqlite`` beside *db*.
    :param model: Embedding model; ``None`` for DocKG's default.
    :return: The server; serve it with :func:`kg_utils.mcp.run`.
    """
    from doc_kg import DocKG  # noqa: PLC0415
    from doc_kg.dockg import DEFAULT_MODEL  # noqa: PLC0415
    from doc_kg.mcp_server import pack, query  # noqa: PLC0415
    from kg_utils.mcp import make_server  # noqa: PLC0415

    from gutenberg_kg import __version__  # noqa: PLC0415

    root = Path(repo).resolve()
    db_path = Path(db) if db else root / DEFAULT_BUNDLE / ".dockg" / "graph.sqlite"
    vectors_path = Path(vectors) if vectors else db_path.parent / "vectors.sqlite"

    def open_kg() -> DocKG:
        return DocKG(root / "corpus", db_path, vectors_path, model=model or DEFAULT_MODEL)

    server = make_server(
        open_kg, name="gutenkg", kind="gutenberg", version=__version__, instructions=PREAMBLE
    )
    server.canonical("stats")(stats)
    server.canonical("query")(query)
    server.canonical("pack")(pack)
    for tool in EXTRA_TOOLS:
        server.tool()(tool)
    return server

corpus_imagine(query, book=None, extra_prompt=None, size='1536x1024', seed=None, steps=4) async

Search the corpus for a scene (diaries first, then the books), then illustrate it.

The matching passages are rewritten into an image prompt by the local VLM and generated on the running image server.

Parameters:

Name Type Description Default
query str

What to search for, e.g. "great fire of London".

required
book str | None

Optional book or author name substring to search within, e.g. "pepys".

None
extra_prompt str | None

Style or scene notes appended to the corpus text, e.g. "oil painting".

None
size str

Output size "WIDTHxHEIGHT" (default landscape 1536x1024).

'1536x1024'
seed int | None

Optional integer seed for reproducibility.

None
steps int

Inference steps — 4 (fast) to 25 (higher quality).

4
Source code in src/gutenberg_kg/mcp_server.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
async def corpus_imagine(
    query: str,
    book: str | None = None,
    extra_prompt: str | None = None,
    size: str = "1536x1024",
    seed: int | None = None,
    steps: int = 4,
) -> Image:
    """
    Search the corpus for a scene (diaries first, then the books), then illustrate it.

    The matching passages are rewritten into an image prompt by the local VLM
    and generated on the running image server.

    :param query: What to search for, e.g. "great fire of London".
    :param book: Optional book or author name substring to search within, e.g. "pepys".
    :param extra_prompt: Style or scene notes appended to the corpus text, e.g. "oil painting".
    :param size: Output size "WIDTHxHEIGHT" (default landscape 1536x1024).
    :param seed: Optional integer seed for reproducibility.
    :param steps: Inference steps — 4 (fast) to 25 (higher quality).
    """
    from gutenberg_kg import image_gen  # noqa: PLC0415
    from gutenberg_kg.cli.cmd_imagine import _query_corpus  # noqa: PLC0415
    from gutenberg_kg.image_gen import vlm_rewrite  # noqa: PLC0415

    corpus_text = _query_corpus(query, book)
    log = _logger()
    log.info("corpus_imagine", query=query, book=book, corpus_chars=len(corpus_text))

    vlm_input = corpus_text
    if extra_prompt:
        vlm_input = f"{corpus_text}\n\nAdditional style/scene notes: {extra_prompt}"
    prompt, vlm_error = vlm_rewrite(vlm_input)
    if vlm_error:
        log.warning("vlm_rewrite_failed", error=vlm_error)

    pil = await _run_sync(
        image_gen.generate_via_server,
        prompt,
        server_url=_image_endpoint(),
        size=size,
        seed=seed,
        steps=steps,
    )
    return _as_mcp_image(pil)

generate_image(prompt, size='1536x1024', seed=None, steps=4) async

Generate an image from a text prompt on the running image server, with no VLM planning.

Parameters:

Name Type Description Default
prompt str

Text description of the image to generate.

required
size str

Output size "WIDTHxHEIGHT" (default landscape 1536x1024).

'1536x1024'
seed int | None

Optional integer seed for reproducible outputs.

None
steps int

Inference steps — 4 (fast) to 25 (higher quality).

4
Source code in src/gutenberg_kg/mcp_server.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
async def generate_image(
    prompt: str,
    size: str = "1536x1024",
    seed: int | None = None,
    steps: int = 4,
) -> Image:
    """
    Generate an image from a text prompt on the running image server, with no VLM planning.

    :param prompt: Text description of the image to generate.
    :param size: Output size "WIDTHxHEIGHT" (default landscape 1536x1024).
    :param seed: Optional integer seed for reproducible outputs.
    :param steps: Inference steps — 4 (fast) to 25 (higher quality).
    """
    from gutenberg_kg import image_gen  # noqa: PLC0415

    _logger().info("generate_image", prompt=prompt[:80], size=size, seed=seed)
    pil = await _run_sync(
        image_gen.generate_via_server,
        prompt,
        server_url=_image_endpoint(),
        size=size,
        seed=seed,
        steps=steps,
    )
    return _as_mcp_image(pil)

main(argv=None)

Entry point for gutenkg-mcp: the same command as gutenkg mcp.

Parameters:

Name Type Description Default
argv list[str] | None

Arguments; None for sys.argv[1:].

None
Source code in src/gutenberg_kg/mcp_server.py
271
272
273
274
275
276
277
278
def main(argv: list[str] | None = None) -> None:
    """Entry point for ``gutenkg-mcp``: the same command as ``gutenkg mcp``.

    :param argv: Arguments; ``None`` for ``sys.argv[1:]``.
    """
    from gutenberg_kg.cli.cmd_mcp import mcp_command  # noqa: PLC0415

    mcp_command.main(args=argv, prog_name="gutenkg-mcp")

stats(*, ctx)

Node and edge counts for the corpus bundle, in total and by kind and relation, plus its vector count.

Source code in src/gutenberg_kg/mcp_server.py
73
74
75
76
77
def stats(*, ctx: Context) -> dict[str, Any]:
    """
    Node and edge counts for the corpus bundle, in total and by kind and relation, plus its vector count.
    """
    return {**kg_from(ctx).stats(), "kind": "gutenberg"}