Skip to content

7. The orchestrator

The orchestrator is a Deno/TypeScript service that sits above the kernel and handles two responsibilities the kernel cannot embed directly:

  1. IO component dispatch — invoke external systems on the kernel’s behalf when a program executes. The built-in components run scripts in worker containers spawned by the runtime-substrate addon (chapter 11).
  2. MCP server — expose Eigenius operations as Model Context Protocol tools so external LLM agents can query the knowledge graph and run programs.

The full architectural rationale is in D6 — Execution architecture. This chapter is the operational view.

7.1. When you need it

OperationOrchestrator required?
eigenius load, validate, compile, inspectNo
eigenius query against in-process or remote kernelNo (queries are read-only)
eigenius program-validateNo (static checks run in-process)
eigenius run against a program with no IO componentsNo
eigenius run against a program that calls RunRuntimeScript or any other IO componentYes
Serving the notebook SPA (EIGENIUS_NOTEBOOK_STATIC)Yes (the orchestrator serves it under /notebooks)
MCP-driven workflows (LLM agent calling Eigenius operations)Yes

If your workload is purely structural — load, query, type-check — you can skip the orchestrator entirely. As soon as a program dispatches an IO component, the kernel needs the orchestrator endpoint.

7.2. Starting the orchestrator

The orchestrator is a Deno program in orchestration/. Three ways to start it:

Direct:

Terminal window
cd orchestration
deno run --allow-net --allow-env --allow-sys=hostname src/main.ts

Via just:

Terminal window
just orchestrator

Via Docker Compose: docker compose up brings both services up; see chapter 5.

7.3. Configuration via environment variables

Read at startup; no config file:

VariableDefaultEffect
EIGENIUS_KERNEL_ENDPOINThttp://localhost:50051gRPC endpoint for the kernel (the orchestrator calls back into the kernel for read-access and query-access host imports)
EIGENIUS_ORCHESTRATOR_PORT8080HTTP port the orchestrator binds to
EIGENIUS_NOTEBOOK_STATICnoneDirectory of a built notebook bundle; when set, the SPA is served under /notebooks

When the kernel starts, it expects the orchestrator’s endpoint via --orchestrator <url> or EIGENIUS_ORCHESTRATOR_ENDPOINT. The two endpoints (kernel and orchestrator) are independent and known to each other through these flags.

7.4. Built-in components

When the runtime-substrate addon loads, main.ts registers two components:

ComponentIRIDoes
RunRuntimeScripturn:eigenius:program:components:RunRuntimeScriptRuns the runtime:RuntimeScript passed as the component argument in its pinned environment, with the input resource as the script’s input
CallRuntimeMethodurn:eigenius:program:components:CallRuntimeMethodCalls a typed entry point on a long-lived runtime worker (D26 §5.3.1)

Without the addon neither is registered; startup logs runtime substrate disabled, and a dispatch to either fails with No handler registered for component: <iri>.

Source: run_runtime_script.ts, call_runtime_method.ts.

In ESL, the script travels in the trailing config block:

program ex:fit : ex:Table -> core:Resource {
RunRuntimeScript(input) {
runtime:language = "r";
runtime:source = "...";
}
}

The bare name RunRuntimeScript resolves to the component IRI at compile time.

7.5. The component registry

orchestration/src/components/registry.ts maintains a map from component IRI to handler function. On startup, main.ts registers the two built-in handlers when the substrate addon loads. The registry is fixed at startup — there is no install-at-runtime path.

There is no /dispatch route. The request handler has four branches — /health, /mcp, the notebook static route when EIGENIUS_NOTEBOOK_STATIC is set, and the Connect service paths — and everything else returns 404. Component dispatch is the ComponentExecutor Connect service; the orchestrator registers three services in total (ComponentExecutor, NotebookService, and a passthrough of the kernel’s own EigeniusKernel surface). The kernel calls ComponentExecutor, the handler runs, and the CBOR-encoded response goes back over the same call.

7.6. The MCP server

The orchestrator exposes a curated subset of the kernel surface as Model Context Protocol tools so LLM agents (Claude Code, Claude Desktop, Cursor, IDE agents) can drive Eigenius as part of their reasoning.

Two transports are wired:

  • HTTP — http://localhost:8080/mcp on the orchestrator’s existing port, mounted alongside the notebook SPA and Connect RPC. This is the right path for docker compose up workflows — the agent client connects directly without a host-side subprocess.
  • stdio — cd orchestration && deno task mcp runs a standalone process that talks MCP over stdin/stdout against an already-running kernel. Useful when you want a kernel on the host (e.g., eigenius serve --port 50051) without bringing up the orchestrator container.

Source: orchestration/src/mcp/server.ts (tools), orchestration/src/mcp/http.ts (HTTP transport), orchestration/src/mcp_main.ts (stdio entry point).

7.6.1. The tool surface (14 tools)

Each tool wraps one or two kernel RPCs and surfaces the response as JSON. Categories follow the platform’s natural division:

Explore (read-only chain navigation):

  • eigenius_query — execute an EigenQL query; rows decoded from Eigon-CBOR to JSON
  • eigenius_inspect — resolve a single resource by IRI
  • eigenius_list_branches — branches with head LayerId + commit time
  • eigenius_list_tags — immutable tag refs
  • eigenius_list_institutions — D14 institutions with their QueryClasses + comorphisms + runtime kind
  • eigenius_get_schema — JSON Schema for an ontology class, derived from the chain
  • eigenius_layer_topology — graph (nodes + edges) of classes / properties / institutions per layer

Mutate (commit-shaped operations):

  • eigenius_load — load Eigon-JSON; supports policy: "reject" | "cascadeTombstone", branch, explicitTombstones (D41)
  • eigenius_validate_program — type-check a program without executing
  • eigenius_run_program — execute program + input (both inline as Eigon-JSON)
  • eigenius_run_program_by_iri — execute a program already loaded in the chain

Observe (state and tasks):

  • eigenius_health — kernel version, layer count, resource count, D21 resume-sweep state
  • eigenius_list_tasks — D21 tasks (running, suspended, completed, failed)
  • eigenius_get_task_status — detail for one task UUID

Out of scope (deliberately). Branch / tag mutation, merge submission, chain consolidation, GC, task cancellation. These are stateful multi-turn flows or destructive operations — they belong in the notebook UI or operator CLI, not in a single-turn agent invocation. Use the CLI or the notebook workspace rail for those.

Bring the stack up:

Terminal window
docker compose up -d
# kernel on :50051, orchestrator on :8080 (notebook + /mcp + RPC)

Then register the MCP server with your client of choice. For Claude Code (CLI or VS Code extension):

Terminal window
claude mcp add --transport http eigenius http://localhost:8080/mcp
claude mcp list # verify it shows ✓ Connected

For Claude Desktop, edit claude_desktop_config.json (%APPDATA%\Claude\ on Windows, ~/Library/Application Support/Claude/ on macOS, ~/.config/Claude/ on Linux). Claude Desktop’s native HTTP transport support is uneven across builds — use the mcp-remote stdio bridge to be safe (requires Node.js on the host):

{
"mcpServers": {
"eigenius": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:8080/mcp"]
}
}
}

Some builds accept { "type": "http", "url": "http://localhost:8080/mcp" } directly — try that first if you’d rather skip the bridge. If the app reports “MCP failed to connect,” fall back to the mcp-remote form above.

Restart the app. The 14 eigenius_* tools appear in the tool list (look for the hammer icon in the input area).

7.6.3. Setup — stdio path (kernel-on-host development)

When you want a kernel running on your host (faster iteration, no rebuild for kernel changes) but don’t want to bring up the orchestrator container, use stdio. Start the kernel directly:

Terminal window
eigenius serve --port 50051 --db /var/lib/eigenius/db

Then configure your client to launch the MCP stdio entry point on demand. Claude Desktop config:

{
"mcpServers": {
"eigenius": {
"command": "deno",
"args": ["task", "mcp"],
"cwd": "/absolute/path/to/eigenius/orchestration",
"env": { "EIGENIUS_KERNEL_ENDPOINT": "http://localhost:50051" }
}
}
}

deno task mcp is defined in orchestration/deno.json and runs src/mcp_main.ts. The orchestrator container does not need to be running for this path.

7.6.4. The agent guide

docs/method/eigenius.md teaches a coding agent how to use the platform correctly: the mental model, the three surface languages, the MCP tool selection table, minimal Eigon-JSON / ESL / EigenQL shapes, common workflows, and the pitfalls that trip agents up (mandatory is_a, synthesized IRI row keys in query results, persistent-backend requirements, D41 multi-layer outcomes, …).

It is a reference document, not an auto-loaded agent skill — point the agent at the path, or paste the section you need. Two companions sit beside it: reasoning.md, the method for capturing reasoning as a typed chain of graded, witnessed propositions, and grounding.md, the retrieval-and-citation method its anchoring step uses.

7.6.5. Smoke-test with the MCP Inspector

Before wiring into a client, confirm the server works against the MCP Inspector:

Terminal window
npx @modelcontextprotocol/inspector --url http://localhost:8080/mcp

Opens a local web UI where you can list tools and invoke them interactively. If eigenius_health returns { healthy: true, version: "...", ... }, the orchestrator → kernel path is good.

7.6.6. Debugging

If a client says “MCP server failed to connect,” walk the chain bottom-up:

  1. Kernel reachable? — curl http://localhost:50051/ (gRPC will reject the bare GET but the connection should establish; “connection refused” means the kernel isn’t running).
  2. Orchestrator reachable? — curl http://localhost:8080/health should return {"healthy":true,...}.
  3. MCP endpoint live? — curl -X POST http://localhost:8080/mcp -H "Accept: application/json, text/event-stream" -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}' should return a JSON-RPC response with serverInfo.name === "eigenius".
  4. Client wiring — claude mcp list (Claude Code) or check ~/Library/Logs/Claude/mcp.log / %APPDATA%\Claude\Logs\mcp.log (Claude Desktop) for handshake errors.

The orchestrator logs each MCP request to stderr; pipe it through jq when running against a real client to see what the agent is calling.

7.6.7. Architecture and scope notes

  • Mode: stateless + JSON-response. WebStandardStreamableHTTPServerTransport from the SDK with sessionIdGenerator: undefined and enableJsonResponse: true. Each POST is one tool invocation, one JSON-RPC response. No SSE streaming, no session bookkeeping.
  • Per-request lifecycle: the SDK requires a fresh McpServer + Transport pair per request in stateless mode; the orchestrator’s HTTP handler builds them on demand and tears them down when the response resolves. The underlying KernelClient (Connect RPC over gRPC-Web) is long-lived.
  • CORS: not configured. Native MCP clients don’t need it; if you want a browser-based agent to call MCP from a different origin, add CORS headers to the /mcp branch in orchestration/src/server/mod.ts.
  • Authentication: none. The current model assumes the orchestrator port is reachable only to trusted callers (local docker stack, dev environment). Production deployment should front the orchestrator with a proxy that enforces auth before forwarding to /mcp.

7.7. WASM IO components (removed)

WASM extensibility was removed on 2026-07-08. There is no orchestration/src/wasm/ directory, no WASM addon, no capability install, and wasmtime is not a dependency of the workspace. The examples/wasm-* directories were emptied in the same change.

The live extension paths are the built-in TypeScript components (§7.9) and the runtime substrate, whose native addon the orchestrator loads to spawn sibling worker containers (chapter 11).

7.8. Observability

The orchestrator logs each component dispatch to stdout, one line per record: human-readable when stdout is a TTY, the kernel’s JSON record shape otherwise (EIGENIUS_LOG_FORMAT=pretty|json overrides):

2026-10-17T12:00:00.000Z INFO orchestrator.component.dispatch RunRuntimeScript completed output_bytes=412 partial_invocation_bytes=188 latency_ms=1240

Pipe stdout to a file or your log aggregation tool to capture the full record.

The kernel records its own trace of each component dispatch for incremental re-evaluation; that trace lives in the kernel’s trace store, not in the orchestrator. See D6b — Reasoning trace schema.

7.9. Adding a TypeScript-side component

For non-WASM components implemented in TypeScript:

  1. Write a handler in orchestration/src/components/<name>.ts matching the ComponentHandler shape from registry.ts.
  2. Register it in main.ts alongside the built-ins.
  3. Declare the component in your ontology (a Component resource with input_type, output_type, and the chosen IRI).
  4. Reference it from your ESL programs by short name or by IRI.

This path is suitable for components that are best written in TypeScript (e.g., wrapping a TypeScript-only library). For anything that needs a full language ecosystem, or that should run in an isolated container, use the runtime substrate instead (chapter 11).

7.10. Stopping the orchestrator

Plain Ctrl-C for the foreground process, or docker compose down for the containerized setup. There is no persistent state on the orchestrator side — it is stateless between restarts. All persistent state lives in the kernel.


Next: 8. Worked demos →