Files
chemenu/instructions/mcp-read-server.md
T
torben 828521861d
CI / verify (push) Successful in 53s
Release / release (push) Successful in 36s
stack: MCP submit-Tool mit Upload Review Gate und Quarantäne-Schreibpfad (schliesst #32)
Files changed:
- .gitignore
- AGENTS.md
- CHANGES.md
- INSTALL-MCP.md
- README.md
- VERSION
- docs/why-gates-are-code.md
- instructions/gates.md
- instructions/ingest-queue.md
- instructions/mcp-read-server.md
- instructions/wiki-ingest/SKILL.md
- raw/CONTRACT.md
- tools/CONTRACT.md
- tools/chemenu/cli.py
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/upload_cmd.py
- tools/chemenu/config.py
- tools/chemenu/mcp/server.py
- tools/chemenu/tests/test_doctor.py
- tools/chemenu/tests/test_mcp_server.py
- tools/chemenu/tests/test_upload.py
- tools/chemenu/tests/test_upload_cmd.py
- tools/chemenu/upload.py
2026-09-11 09:51:37 +02:00

6.5 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/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:

    export CHEMENU_ROOT=/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. Either is fine:

    export WIKI_TRACE=0                      # off
    export WIKI_TRACE_DIR=/var/log/chemenu   # or elsewhere, outside the corpus
    
  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 "$CHEMENU_ROOT" fetch --quiet origin && \
      git -C "$CHEMENU_ROOT" reset --hard --quiet origin/main
    

    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 holds their output together (tools/chemenu/tests/test_mcp_server.py). 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.