Files
chemenu/instructions/mcp-read-server.md
T
torbenandClaude Opus 5.5 0d3499ab04
CI / verify (push) Successful in 5m26s
CI / pwsh (push) Successful in 2m7s
Release / release (push) Successful in 35s
feat: the test suite no longer ships, and dist upgrade deletes what a release stops shipping (#113)
dist export leaves out tools/chemenu/tests/, tools/pytest.ini and tools/.coveragerc by exact
path - the suite tests the origin repository, and 257 of its tests failed in a fresh export.
dist upgrade now deletes a no-longer-shipped file that is unchanged since install, with any
directory that leaves empty, and blocks one changed since install like any local change
(--take-release deletes it, --keep-local keeps it). --prune is accepted and ignored.

Files changed:
- CHANGES.md
- EVALS.md
- VERSION
- instructions/mcp-read-server.md
- instructions/upgrade-instance.md
- tools/CONTRACT.md
- tools/README.md
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/tests/test_dist_cmd.py
- tools/chemenu/tests/test_dist_upgrade.py
- tools/requirements-mcp.txt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SnAJ7Z3CpVD3PRbN73QtU2
2026-10-03 20:12:32 +02:00

6.7 KiB

type, name, description
type name description
types/instruction.md mcp-read-server Run and keep current the MCP read server that serves this wiki to a second consumer

Running the MCP read server

Chemenu has a second consumer. search, types, describe_type, lint and status are served over MCP to callers that are not this terminal - the CLI and the server are two adapters over one core (chemenu.api.Corpus), not a CLI with a network interface bolted on. A sixth tool, submit, is opt-in: a checkout that creates .wikitool-upload.json also offers a quarantined write path for documents pushed from outside - see instructions/ingest-queue.md for reviewing what lands there.

This document is about operating it: how to start it, what has to be true of the checkout it serves, and how that checkout stays current. What it exposes and why is in tools/CONTRACT.md and in the module's own docstring (tools/chemenu/mcp/server.py).

Deployment is deliberately not here. Which cluster, which ingress host, where the credential lives - that is private infrastructure and this is a public repository. What is here is everything an operator needs that is true of the software rather than of one installation.

Contents

When to run

  • Standing something up for a consumer that is not a terminal on this machine.
  • Diagnosing an answer that looks stale, or one that disagrees with the CLI.
  • Before pointing a new consumer at an existing server.

Steps

  1. Install the server's dependency. It is deliberately not in requirements.txt: an instance that only uses the CLI should not be made to install pydantic, starlette, uvicorn and cryptography to do it.

    tools/.venv/bin/python -m pip install -r tools/requirements-mcp.txt
    
  2. Decide which checkout it serves. The root resolves by precedence - an explicit --root, then CHEMENU_ROOT, then the checkout the package lives in. A deployment points at its corpus with one variable and no code, set in the environment the server process starts in (its service unit or container spec):

    Variable Value
    CHEMENU_ROOT The checkout it serves, for instance /srv/chemenu
  3. Take tracing out of the served tree. The server refuses to start otherwise, and the refusal is the point: telemetry defaults to on and writes under reports/telemetry/ inside the repo, which step 5's sync is entitled to wipe. Set one of the two in the same environment:

    Variable Value
    WIKI_TRACE 0 - tracing off
    WIKI_TRACE_DIR A directory outside the corpus, for instance /var/log/chemenu
  4. Start it on the transport that matches what is in front of it.

    tools/.venv/bin/python -m chemenu.mcp                                # stdio
    tools/.venv/bin/python -m chemenu.mcp --transport streamable-http    # deployed
    

    stdio is for developing and testing without a network - one process per consumer, started locally. streamable-http is what a deployed instance speaks, and the only one the authentication middleware can sit in front of, because that middleware is an HTTP reverse proxy. sse is reachable through the SDK and deliberately not offered: it is the superseded remote transport, and building on it now only moves the migration later.

  5. Keep the checkout current by polling, and keep it clean.

    git -C <checkout> fetch --quiet origin
    git -C <checkout> reset --hard --quiet origin/main
    

    The second only after the first succeeded, every few minutes, from a timer beside the server. Polling rather than a webhook on purpose: it needs no inbound endpoint and no signature checking, which is a smaller surface than the thing it would optimize. A webhook is a later optimization, not a starting point.

    reset --hard is load-bearing, not a convenience. The corpus cache reuses a parse while the commit is unchanged and refuses to cache a dirty tree at all, so a checkout that has drifted answers correctly but reparses on every request - and every answer it gives is stamped "commit": null, because a dirty tree corresponds to no revision.

    Never add git clean to this sync. reset --hard leaves every gitignored path alone by design, which is exactly what keeps mcp-upload/ (the submit tool's own quarantine) and reports/telemetry/ intact across a sync - a git clean -xd bolted on "to tidy up" would delete a submission nobody has reviewed yet, silently, on the next poll.

Decision points

  • An answer looks stale? Read commit in the response. If it names an old revision, the sync is not running. If it is null, the served tree has uncommitted changes - something is writing into the corpus that should not be.
  • The server disagrees with wikitool on the same query? That is a defect, not a configuration difference: the two go through the same functions and a golden test in the origin repository holds their output together. Check first that both are pointed at the same root - CHEMENU_ROOT is easy to set for one and not the other.
  • Asked to expose a write tool? Five of the six tools have none, structurally: the server imports nothing under chemenu.commands, so new, touch, xref, cite, publish and migrate are unreachable from it rather than filtered out of a list. The one exception is submit (opt-in via .wikitool-upload.json): it may write, but only into mcp-upload/, a quarantine no other command reads - a positive list enforced in code (chemenu.upload._write_atomic_within), not an absence. The commands that move a submission out of that quarantine (upload accept/upload reject) still have the absence property: they live under chemenu.commands and stay unreachable from the server. Reviewing what submit receives is instructions/ingest-queue.md, not this file.
  • Asked to rate-limit inside the server? Rate limiting belongs in the middleware in front of the process, next to authentication. Not the Iteration Budget Gate: that exists to stop an agent session from iterating unnoticed over the wiki's state, which is why retrieval is exempt from it, and using it as a rate limiter would dilute it into one.

Scope

Not for setting up an instance (setup-instance.md) or a fresh clone (bootstrap.md). Not for the authentication or rate-limiting middleware, which is infrastructure configuration rather than part of this repository. Not a write path: see the decision point above.