khive-mcp 0.6.0

khive MCP server library — served via the kkernel binary
Documentation
# khive-mcp Design

Function-specific technical reference docs (coordinator dispatch contract)
live in [`docs/api/`](api/); save-sink rationale lives in
[`docs/save-sink.md`](save-sink.md). This document covers ADR-driven design
decisions and rationale.

## ADR Compliance

### Request DSL — Single `request` Tool (ADR-016)

- The MCP server exposes exactly one tool named `request`. All verbs are dispatched
  through it using the function-call DSL or JSON form.
- The DSL supports three execution modes: Single, Parallel (batch), and Chain
  (`|`-separated with `$prev` substitution).
- `run_parsed` in `server.rs` is intentionally kept as a single match expression
  over the three execution modes. Splitting the branches would scatter the
  contract invariants (summary shape, aborted semantics, `$prev` substitution
  ordering) across files, making them harder to review as a unit.
- Response envelope shape: `{"results": [...], "summary": {"total": N, "succeeded": K, "failed": M, "aborted": A}}`.
- Per-op failures do not abort siblings in Parallel mode; they do abort remaining
  ops in Chain mode (reported as `{"ok": false, "aborted": true}`).
- Invalid DSL (parse/lex failure) returns an RPC-level `invalid_params` error.
  Per-verb validation failure returns a per-op `{ok: false, error: "..."}` entry.

### Pack Standard — Vocabulary, Visibility, and Schema Plans (ADR-017)

- Subhandler verbs are operator-only and are blocked at the MCP wire boundary.
  Exception: `help=true` is short-circuited in `VerbRegistry::dispatch` before
  reaching the pack, so introspection passes through.
- Pack-auxiliary schema plans are applied at server startup (before any handler
  runs) so that pack tables are present. Errors are logged but not propagated
  to avoid a single pack's schema failure aborting the whole server boot.
- `register_embedders` is called on every pack after the registry is built so
  custom embedding providers are available before the first `remember`/`recall`.

### Dynamic Pack Loading (ADR-027)

- `builtin_pack_names()` is sourced from `PackRegistry::discovered_names()` so
  the list always reflects whichever pack crates are linked into the binary.
- Pack registration fails fast on unknown names or unsatisfied dependencies —
  a misconfigured `KHIVE_PACKS` is a boot error, not a silent degradation.
- `pack.rs` force-references one public symbol per pack crate so the linker
  includes their `inventory::submit!` constructors in the final binary.

### Multi-Engine Retrieval — Edge Endpoint Rules and Embedder Registration (ADR-031)

- After the registry is built, `install_edge_rules` aggregates pack-declared
  edge endpoint rules into the runtime so `validate_edge_relation_endpoints`
  can consult the combined ruleset.
- `call_register_embedders` is invoked after registry construction, before any
  verb dispatch, to wire custom embedding providers from each pack.

### Authorization Gate and Audit Persistence (ADR-018)

- The authorization gate from `runtime.config().gate` is threaded into the
  registry. Gate decisions are hard-enforcing — a `Deny` result blocks pack
  dispatch and returns `PermissionDenied`.
- The `EventStore` is wired into the registry via `builder.with_event_store` for
  audit persistence of all dispatched operations.

### Write-Key Conflict Detection (ADR-038)

- Before parallel/single dispatch, operations targeting the same write key in
  the same batch are detected and receive per-op error entries.
- Non-conflicting ops in the same batch execute normally.
- `results.length == summary.total` is preserved (the response envelope contract
  is never violated by conflict detection).

### Presentation Transforms (ADR-045)

- Presentation transforms are applied per-op AFTER dispatch, at the response
  envelope boundary. Chain `$prev` substitution uses canonical (verbose) handler
  output — the transform runs only on the final result, not on the intermediate
  value passed to the next op.
- `AlwaysVerbose` verbs (as declared by the verb registry policy) override the
  caller's requested presentation mode.
- Error envelopes are never transformed — only successful `result` fields.
- Known presentation mode strings: `"agent"` (default, token-efficient),
  `"verbose"` (full canonical shape), `"human"` (same as verbose at runtime).

### Daemon — Warm Pack Registry (ADR-049)

- The `daemon.rs` module provides the client side: `forward_or_spawn` connects
  to a warm daemon, auto-spawns it on first use, and maps responses to MCP
  error types. Ordinary fallback paths return `None` so the caller dispatches
  locally. `KHIVE_DAEMON_STRICT=1` (#947) turns a recordable fallback into a
  caller-visible per-op error instead of completing it locally; `KHIVE_NO_DAEMON`
  and the `request` tool's `save_to` bypass remain intentional local paths,
  unaffected by strict mode.
- The daemon is bound to `~/.khive/khived.sock`. Namespace mismatches trigger
  local-dispatch fallback.
- `warm_all` is called in a background task after the daemon socket is bound so
  ANN indexes and pack in-memory state are warmed before serving requests.
- `DaemonDispatch` is implemented on `KhiveMcpServer` so the runtime daemon
  server can call back into the MCP server's local dispatch path.

### Fail-Fast Pack Validation (ADR-014)

- Pack registration is fail-fast: unknown names or unsatisfied dependencies
  abort construction and return the original runtime so callers can recover.
  The `PackRegError` type carries the runtime for this reason.

### Result depth guard — stack-overflow defense (`server.rs`)

Handler results (e.g. `traverse`/`context`) are not bounded by the DSL
parser's syntax-tree guard, so a pathologically deep result could overflow
the stack via recursive `Value::clone`, JSON serialization, or the agent-mode
presentation transform — all of which recurse natively over `Value`.
`result_within_depth_limit` MUST be called on the raw value coming straight
out of coordinator/registry dispatch, before any of those operations touch
it; it delegates to `khive_request::value_nesting_within_limit`, which walks
an explicit worklist instead of native recursion, so the check itself cannot
overflow the stack on the same input it is screening.

`chain_aggregation_depth_reject` is the second, defense-in-depth check
applied to a dispatched op's full result envelope in chain mode. By the time
it runs, `dispatch_op`'s own `chain_ok_envelope_or_depth_error` has already
screened the same `result` field, so this should never trip in practice —
but if a future refactor bypasses that earlier guard, the rejected envelope
must still be discarded iteratively rather than dropped natively, or the
recursive `Drop` this branch exists to prevent happens anyway on the way out
of scope.

### Bench embedder — feature hashing (`bench_embedder.rs`)

`FeatureHashProvider` is a bench-only deterministic embedder, never compiled
into release/publish builds, registered under the `all-minilm-l6-v2` name so
it overrides the real lattice provider for that slot without changing
`compute_config_id`. `hash_embed` tokenises → maps each token to a (dimension,
sign) pair via FNV-1a → accumulates → L2-normalises. Lexically similar texts
share tokens and therefore accumulate signal in the same dimensions with the
same sign, producing correlated vectors — this lets a benchmark exercise the
vector/ANN/fusion legs rather than treating embeddings as pure noise (as the
previous whole-text FNV avalanche did).

## Consistency Notes

- The `schemars` description on `RequestParams.ops` references "ADR-016" inline;
  this is user-facing schema documentation and has been left as-is to avoid
  breaking schema consumers. The description text is surfaced in MCP client
  tool discovery.
- The `get_info` server instructions string and the `request` tool description
  both contain references visible to MCP clients. These are runtime-generated
  strings, not compile-time doc comments, and serve as discoverability hints
  for agent consumers of the MCP server.
- `adr-dsl-packs H3` referenced in `server.rs` is an internal tracking label
  for a specific UX improvement (field hints in `$prev` substitution errors);
  it maps to the DSL hardening work associated with the pack DSL design.