ablative docs
↑↓ move↵ openesc close
Stack Notebook ablative.com.au

Connect an agent

Use the stdio or HTTP MCP server, the repository plugins or an installed bundle with the agent's own Lys proof.

Open .md

Before you begin

Have the store running and know its service configuration or socket. For a protected store, first complete Sign-in and permissions. The MCP adapter does not open a data directory or start a service.

Standard input and output

Register the command with the host. This registration reads a pass file before each tool call.

{
  "mcpServers": {
    "haematite": {
      "command": "haematite",
      "args": [
        "mcp",
        "--config",
        "/etc/haematite/service.json",
        "--pass-file",
        "/etc/haematite/agent-pass"
      ]
    }
  }
}

For an agent with its own key, use the Lys agent options instead.

haematite mcp --config /etc/haematite/service.json \
  --lys-agent https://lys.example.au ward-agent --key-file /etc/haematite/agent-key.pem

The permissions page also gives credential-file and run-pass forms. Only one pass source can be selected. Key and credential files require absolute paths. A relative --pass-file is accepted and resolved from the MCP process’s working directory. --socket can replace --config; --timeout-ms bounds store and issuer requests.

The process reads one JSON-RPC message per line. Standard output carries responses only; diagnostics go to standard error. End of standard input exits 0. A process exit of 0 does not mean every tool succeeded. Read the result’s isError and its named refusal.

HTTP

Put an explicit HTTP object in the service file.

{
  "mcp_http": {
    "listen": "127.0.0.1:7742",
    "allowed_origins": [
      "https://studio.example.au"
    ],
    "path": "/mcp",
    "resource_metadata_url": "https://studio.example.au/.well-known/oauth-protected-resource"
  }
}

Start the HTTP adapter separately from the store.

haematite mcp-http --config /etc/haematite/service.json

Keep the listener on loopback and put TLS at the proxy. The public MCP address is the proxy origin plus the configured path. The resource metadata URL belongs to the sign-in arrangement and must be served separately.

Each request needs one bearer pass for haematite. A present browser origin must be allowed. POST requests use JSON content type and accept both JSON and event streams.

POST /mcp HTTP/1.1
Host: studio.example.au
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer <pass supplied by the MCP client>

{"jsonrpc":"2.0","id":1,"method":"tools/list"}

A missing bearer is HTTP 401 mcp_sign_in_required. A refused pass gets a 401 sign-in challenge. A wrong origin is 403 mcp_http_origin_refused. An Accept header missing either response type is 406 mcp_http_accept_invalid. A request accepted by HTTP can still contain a tool refusal.

For a host that supports a header helper, use the mcp-pass command to obtain its Authorization object from a key, credential or run pass. Hand standard output directly to the host. Do not paste its result into a configuration file or terminal transcript.

Write one bearer header object for an MCP client header helper. The output contains a pass; give it directly to the client.

haematite mcp-pass --lys-agent <issuer origin> <agent id> ((--credential-file | --key-file) <absolute path> | --run-pass-env <variable>) [--timeout-ms N]
--timeout-ms N
Positive milliseconds for the Lys request, default 10000.

The repository plugins

The repository has a Claude Code marketplace at .claude-plugin/marketplace.json and a Codex marketplace at .agents/plugins/marketplace.json. Both point to plugins/haematite. Add that local marketplace through the host’s plugin interface and select haematite. No store data belongs in the plugin directory.

At main 84b9497a, the Claude Code manifest starts scripts/mcp.sh through CLAUDE_PLUGIN_ROOT. It requires the config_file user setting and passes the optional lys_issuer, lys_agent and key_file settings to the launcher. Set all three agent settings for key-based sign-in, or give the MCP registration another explicit pass source.

At that same commit, the Codex manifest reads ./.mcp.json, which starts ./scripts/mcp.sh in the plugin directory. It includes the haematite skill from ./skills/. The launcher selects the configured pass file first, then the agent’s key, credential or run-pass proof in that order.

HAEMATITE_CONFIG
The service configuration path. Defaults to HOME/.config/haematite/serve.json. Missing files are haematite_mcp_config_missing, exit 78.
HAEMATITE_BIN
The executable path or name. Defaults to haematite on PATH. Missing commands are haematite_mcp_binary_missing, exit 127.
HAEMATITE_PASS_FILE
Pass file, used first when set. The launcher passes it as --pass-file.
HAEMATITE_LYS_AGENT
Agent id. Requires HAEMATITE_LYS_ISSUER and a proof. A missing issuer is haematite_mcp_issuer_missing, exit 78.
HAEMATITE_LYS_ISSUER
Lys origin for the agent pass route. No API path.
HAEMATITE_KEY_FILE
Agent key path. First proof selected for HAEMATITE_LYS_AGENT.
HAEMATITE_CREDENTIAL_FILE
Grant credential path. Selected when there is no key path.
HAEMATITE_RUN_PASS_ENV
Name of the variable holding a run pass. Selected when neither file proof is set. LYS_AGENT_PASS is the usual seat variable.
HOME and PATH
Passed to the launcher for its default configuration location and executable lookup. LYS_AGENT_PASS is also forwarded by the repository MCP registration.

An agent without a proof is haematite_mcp_agent_proof_missing, exit 78. With no pass source at all, the script prints haematite_mcp_no_pass and starts MCP without one. A protected store then refuses tool calls as pass_missing; an unprotected private store can answer. The warning is not a successful sign-in.

These plugin details were reread at main 84b9497a. Host installation remains a separate check.

An installed bundle

At main 84b9497a, installation writes the binary, configured workers, serve.json, the skill and both plugin manifests into the bundle. It also writes .mcp.json and both marketplace files, using the bundle as their local plugin source. Without agent options, the Codex registration names absolute binary and configuration paths and the Claude Code registration uses CLAUDE_PLUGIN_ROOT.

Without agent options, those generated MCP registrations select no pass source and the installer prints bundle_no_pass. With --lys-issuer, --lys-agent and --key-file together, the preview installer adds scripts/mcp.sh and configures both registrations with that agent. It records the canonical key-file path; it does not copy the key. Moving the bundle requires a new installation because generated paths are absolute. See Install bundles.

The installed binary used to verify these pages produced the older Claude Code bundle layout. The Codex bundle and marketplace additions are preview source after tag v0.20.1; no tagged release carrying those additions was verified. The run-pass, header-helper and schema-recording commands are present in the v0.20.1 source, despite omissions in top-level help.

Prove the connection

List the tools, then describe the store.

{"jsonrpc":"2.0","id":1,"method":"tools/list"}
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "describe_store",
    "arguments": {
      "selection": {
        "branch": "main"
      },
      "page": {
        "rows": 100,
        "bytes": 65536
      },
      "after": {}
    }
  }
}

All eleven tools were called against the scratch store. The read tools returned captured versions. Writes returned receipts. review_branch takes an object for after, including an empty object for its first page. The tool reference lists every required field.

Repository manifest inspection and launcher execution do not prove installation into a particular Claude Code or Codex session. Verify the host’s tool list and one successful store read after installing.

A hosted assistant seat

From 0.21.0, haematite assistant run --config runs the store’s assistant seat: a contained Agent Client Protocol host for one person and one store. It is separate from the MCP adapter. The seat connects to the door’s assistant.socket, and Studio’s panel reaches it through the door (Use the assistant). Lys starts the seat; that start is not yet in the installed Lys.

The seat JSON requires harness, door_socket, serve_config, key_file, issuer and store. The three file or socket paths must be absolute; issuer and store must be nonempty. The harness requires an id, program and an args array. Loading checks program only for a non-empty value. Executable path resolution and the executable-file check happen when the host spawns the harness. Its optional cwd must be absolute; optional session must be nonempty and no longer than content_bytes.

The source catalogue recognises claude-code, codex, gemini-cli, opencode and pi. The current host refuses pi as pi_mcp_unavailable because that adapter drops MCP servers. A program is an absolute executable path or a bare name resolved only in absolute PATH entries. Arguments are passed without a shell. This catalogue is not proof that a particular harness implements the negotiated protocol.

Every harness bound is a positive platform-sized integer. The write and stream queues must also fit the channel capacity. Defaults are prompt_queue 32, content_bytes 65536, acp_frame_bytes 262144, pending_permissions 1024, write_queue 2048, stream_queue 128, issuer_response_bytes 65536, session_list 50 and title_chars 120. Unknown seat or harness fields are refused. Invalid limits name the harness field.

The seat rejects inherited environment names containing key, token, secret, password, credential or access-key markers, except the explicit LYS_AGENT_PASS launch proof. seat_unreadable, seat_invalid, seat_paths_must_be_absolute, seat_store_and_issuer_required and credential_environment_refused identify declaration failures. Fix the launch declaration at its owner rather than adding credentials to the harness environment.

The current host requires LYS_AGENT and its LYS_AGENT_PASS launch proof. It exchanges that proof for a store pass, then presents it to its separate owner-only assistant socket. Permission requests become Lys drafts. A matching person decides the exact draft before the host sends a one-time choice to the harness. A malformed request, failed hold or cancellation receives no approval. This behaviour is read from the 0.21.0 source. It has not yet been run with a seat Lys started.

The preview host records a private .revoke-owed file beside the seat file before acquiring a pass. Closing asks Lys to revoke the pass. If revocation fails, the file remains and the next launch must settle it before proceeding. This file contains the launch proof and must retain its owner-only mode. This lifecycle is read from the 0.21.0 source and has not yet been run with a seat Lys started.