The MCP server
jevlin mcp serves the machine paths as Model Context Protocol tools over stdio, for a host that prefers MCP to a shell skill. It runs only while a host holds it open, and setup does not register it.
A host starts jevlin mcp and holds it open: newline-delimited JSON-RPC 2.0 on stdin and stdout, and it ends when the host closes its stdin. stdout carries nothing but protocol messages.
The four tools
| tool | runs | arguments |
|---|---|---|
search | jevlin search --stdin | The stdin request |
fetch | jevlin fetch --stdin | Reading a page in full |
decide | jevlin decide --stdin, where the router offers decisions | Decisions |
decide_models | jevlin decide --models | None: {} or absent |
How a call is answered
| what | value |
|---|---|
| A tool's arguments | The --stdin request for that command; version may be left out. Each tool's input schema has exactly the request's fields. |
| The answer | The envelope --stdin prints, as the result's one text item and, from protocol revision 2025-06-18 on, as structuredContent too. |
| A refused request, or a call before setup or without a key | A tool result with isError set and that envelope's code and action. The server keeps running. |
| Protocol revisions | 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05. |
| Order | One request at a time, in the order they arrive. |
| The trace | harness is mcp:<name>, the client's own name (mcp when the name is not a plain token), with one session per host process. JEVLIN_HARNESS, when the host's environment sets it, takes precedence. |
-log | One line per message on stderr, naming the method, the tool and the outcome code, never what a request carried. |
-config file | As for every command; add it after mcp for an installation other than the machine's own. |
The tools answer a field in another case, or a VERSION key, as the commands do: unknown_field, and the router is never called. The decide tool hands jevlin decide the arguments as the caller wrote them, with "version": 1 in front when they carry none, so state and questions reach the router byte for byte.
A decide call, and the start of its result (the text item carries the same envelope, on one line):
{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"decide","arguments":{
"model":"vendor/model","state":{"page":"…"},"questions":{"kind":{"type":"choice","instructions":"…"}}}}}
{"jsonrpc":"2.0","id":7,"result":{"content":[{"type":"text","text":"{\"version\":1,\"command\":\"decide\",…}"}],
"structuredContent":{"version":1,"command":"decide","ok":true,…,"result":{"answers":{
"kind":{"type":"choice","choice":"docs","probabilities":{"blog":0.09,"docs":0.91},"confidence":0.88}},…}}}}
Registering it
Setup does not register the server. To add it by hand, with jevlin on the host's PATH (or its full path in its place):
| host | how |
|---|---|
| Claude Code | claude mcp add jevlin -- jevlin mcp |
Codex, in ~/.codex/config.toml | [mcp_servers.jevlin], then command = "jevlin" and args = ["mcp"] |
Cursor, in ~/.cursor/mcp.json | {"mcpServers": {"jevlin": {"command": "jevlin", "args": ["mcp"]}}} |
Each host's page shows the same lines under MCP instead of the skill: Claude Code, Codex, Cursor.