# `Quiddity.Mcp`
[🔗](https://github.com/d-led/ex.quiddity/blob/main/lib/quiddity/mcp.ex#L1)

A Model Context Protocol server over stdio: a STEP part, for an assistant.

One JSON-RPC message per line in, one out — the stdio transport, which is what
a client launches as a process. No sessions, no server-sent events, no batches:
every request is self-contained, which is what lets a client treat it as a
pipe.

## What the tools are for

quiddity answers "what is this part?" and this server exposes that, plus the
geometry underneath it. The questions worth asking are different enough to be
separate tools:

  * **what is it?** — `quiddity_part`, `quiddity_features`, `quiddity_face`;
  * **what is here?** — `quiddity_probe` and `quiddity_sense`: a ray, a point,
    and a camera frame rendered against the exact B-rep;
  * **how do two parts differ?** — `quiddity_correspond` matches the features
    of one part against another's by name and size.

`quiddity_export_mjcf` is the one tool that writes: it puts the part on disk as
a MuJoCo model.

## Parts are opened once

Calls name a path; the server keeps the parsed part, so a second question about
the same file does not re-read it. The cache is this process's — restarting the
server costs a re-read, and nothing on disk depends on it.

A tool that cannot answer answers: a refusal is text saying what was wrong and
what to try, never a dropped connection.

# `state`

```elixir
@type state() :: %{parts: %{optional(String.t()) =&gt; Quiddity.Part.t()}}
```

What the server remembers between messages: the parts it has read.

# `handle`

```elixir
@spec handle(String.t(), state()) :: {String.t() | nil, state()}
```

One message: the reply (or `nil` for a notification) and the state after it.

`nil` for a blank line, and for a line that is not JSON — a transport that
crashes on garbage is a transport nobody can debug.

# `protocol_versions`

```elixir
@spec protocol_versions() :: [String.t()]
```

The protocol revisions this server speaks, newest first.

# `serve`

```elixir
@spec serve() :: :ok
```

Reads JSON-RPC messages from stdin and writes answers to stdout until stdin
closes.

Notifications are answered with nothing, which is what the transport says: a
`notifications/initialized` gets no reply.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
