# legible-mcp — reference MCP server

The reference agent interface for The Legible Protocol. It exposes operations over `../legible-py`, which calls the Rust CLI `legible`. The transport uses Python stdlib, JSON-RPC 2.0 over stdio. Actual host integration must be tested; supporting this transport does not establish compatibility with every client.

## The invariant (why it exists this way)
The agent can **read, draft and prepare**; no signing tool is exposed by this MCP server. Preparation returns an unsigned record and a human signing command. This surface limitation does not prevent another tool in the host from reaching a key: key isolation and confirmation belong to the deployment. A chat confirmation is not cryptographic proof of human review.

## Tools
`read_state · draft · verify · check · discover · reputation · prepare_appoint · prepare_nod · search_drafts · import_draft_packet · current_view`
plus `save_draft · read_draft · export_draft_packet · prepare_from_draft`.
The optional contact tools are `prepare_contact · verify_contact ·
import_contact · export_own_contact`. They prepare unsigned content, verify
or import signed profiles from the participant's scoped `imports/`, and export
only the current profile after an explicit selection. They do not sign or
publish contacts. The participant DID is configured separately from login;
the profile proves key control, not civil identity or payment authority.
There are **no** `sign`/`seal`/`emit` tools. `draft` currently uses the stub provider, not
model inference. `reputation` returns local counts and configured source results;
those external operations are disabled in the scoped reference profile. A
`perspective: querying-node-private-lens` label does not demonstrate actual
CH interpretation. No official protocol score follows from these counts.

## Run

Replace the checkout and participant paths below with absolute paths. This setup
also works when the terminal or agent host starts in another directory:

```bash
LEGIBLE_CHECKOUT=/ABSOLUTE/PATH/TO/PROTOCOL
export LEGIBLE_BIN="$LEGIBLE_CHECKOUT/reference/legible-core/target/debug/legible"
export PYTHONPATH="$LEGIBLE_CHECKOUT/reference/legible-mcp:$LEGIBLE_CHECKOUT/reference/legible-py${PYTHONPATH:+:$PYTHONPATH}"
export LEGIBLE_REPO_ROOT=/ABSOLUTE/PRIVATE/PARTICIPANT/REPOSITORY
export LEGIBLE_PARTICIPANT=your-handle
export LEGIBLE_DRAFT_ORIGIN=urn:uuid:REPLACE_WITH_YOUR_OWN_PERSISTENT_UUID
python3 -m legible_mcp.server        # speaks MCP over stdin/stdout
```
If Cargo uses another target directory, set `LEGIBLE_BIN` to the actual executable.
Register the Python command with the agent host together with these environment
values; use the absolute path of the Python interpreter in that configuration.
The root is mandatory; requests for another repository fail closed. Verification
is restricted to that participant's record files and imports to its imports/
directory. Symlinks in stores are refused. Explicit formal authors must match
the configured participant DID. The boundary does not isolate an unrestricted
terminal agent or host OS administrator. Login does not prove key control.

Create the private participant root first. Choose and retain a distinct origin
URI per collection/participant (for example, generate a UUID once with
`python3 -c "import uuid; print(uuid.uuid4())"` and keep `urn:uuid:<that value>` in the host configuration).
It identifies an origin, not a person or a reachable server. Never reuse the
placeholder above or derive the origin from a common participant name. Saving
own drafts fails closed without an explicit URI; reading/importing preserves
the incoming origin and does not require a new one. Existing shared origins
must not be silently relabelled. A conflict remains a conflict, not an overwrite.

Draft saving/search needs no DID or key. When formal preparation is chosen,
separately set `LEGIBLE_AUTHOR_DID` to the declarant's explicit public DID;
the requested author must match. The server still neither signs nor shares.
`python3 -m legible.doctor desktop-mcp` (with `legible-py` on PYTHONPATH)
reports whether the origin URI is configured; it cannot prove uniqueness,
permissions or host integration. See `../../docs/open-legibility.md` for the
private-cache/selected-export/shared-collection distinction.

### Export a selected draft locally

Read or search the draft first. After the participant chooses that exact version,
call `export_draft_packet` with its `origin`, `id`, `bytes_sha256`, a new simple
`package` label (1–64 ASCII letters, digits, hyphens or underscores, starting
with a letter or digit) and `selected: true`. The flag represents authorization;
it cannot prove that a person actually consented. No authorization follows from
reading or preparing a draft.

The result contains `path`, `manifest`, `signed: false` and `shared: false`.
Only the Markdown and existing transport manifest are written, under
`<LEGIBLE_REPO_ROOT>/exports/drafts/<package>/`. Wrong digests, other roots,
path labels, store symlinks and existing destinations are refused. Received
drafts may be re-exported from the authorized store with their original origin,
author and bytes. Evidence, contact files, CH and credentials are not bundled.

Review those two files, then separately authorize any copy into a shared
collection or submission channel. To import at another installation, put only
the selected packet under its `imports/` and call `import_draft_packet`.
Resending identical bytes preserves identity. This is local stdio support,
not a new tool in the restricted LAN pilot and not a Git/PR integration.

### Partial search and conflicts

`search_drafts` keeps its `drafts` list and adds `complete`, `issues`,
`checked_entries`, `attribution` and a notice. Always present incomplete results
as partial, including affected paths/hashes. Healthy exact reads and exports
work independently of corruption elsewhere. Participant/symlink boundaries
remain enforced; no failed file is automatically moved, deleted or repaired.

Read/import/save failures carry JSON text with `error` and `diagnostic` when
available, under the ordinary MCP `isError` response. This uses the same text
transport in all supported editions, not newer structured-output features.
`line-endings-only` is reported only after comparison with a known original.
It is a byte-format difference, not automatic creation of a cognitive contribution.
Preserve the original; do not normalize or replace exchanged bytes silently.

Origin/author remain declared, not authenticated. `selected: true` does not prove
human consent; export omits evidence files but not sensitive text already inside
the chosen draft. The LAN probe returns exit 2 for partial search, without
printing draft contents or credentials.

## Optional LAN pilot

See [local-pilot.md](../../docs/local-pilot.md) for the participant-run HTTPS
server, browser interface, per-participant credentials and network probe. HTTP
negotiates 2025-03-26, 2025-06-18 and 2025-11-25, uses JSON responses and no SSE.
Local Bearer credentials are not the MCP OAuth profile. Native Gemini-to-LAN
integration and third-party client compatibility are not established.
The LAN catalogue excludes external discovery, signing, sharing and evidence
reads. The browser's selected sharing preserves exact draft bytes, not evidence.

## Tests
```bash
python3 tests/test_mcp.py            # includes conformance demo (f): the agent prepares, never signs
python3 tests/test_pilot.py          # TLS requests, isolation, draft bridge and replay
```

## Key policy (part of the adoption guide)
The baseline suite accepts plain `ssh-ed25519` in SSHSIG v1 with namespace `legible`.
FIDO `ed25519-sk`, SSH certificates, Secure Enclave and TPM are not interchangeable
with that suite; no hardware-backed adapter has been validated here. Keep the real
key outside the agent's reach. An OpenSSH agent can request confirmation on use:
  ```bash
  ssh-add -c ~/.ssh/legible_sovereign   # every signature asks for confirmation
  ```

Confirm-on-use depends on the agent and confirmation UI actually configured; it
does not itself prove review of the payload. The Python POC can invoke OpenSSH on
a raw key path. There is no implemented automatic hardware-backed default.

The server negotiates the requested edition among `2024-11-05`, `2025-03-26`,
`2025-06-18`, `2025-11-25`; otherwise it offers `2025-11-25` and the client must
decide whether it supports it. The local suite covers this negotiation and the
stdio/tool circuit, not every feature of those editions or a real mobile host.
