Skip to content

Latest commit

 

History

History

README.md

@hayba/mcp — the Hayba MCP server

This is the core product. A Node/TypeScript Model Context Protocol server that exposes Hayba's tool surface to an agent host (Claude / GPT / any MCP client) over stdio, and bridges those tools to a running Unreal Engine 5 editor over a TCP seam.

Agent host ──stdio──▶ @hayba/mcp ──TCP──▶ UE5 C++ plugin
 (Claude/GPT)        (this package)       (unreal/HaybaMCPToolkit)

One protocol across two language boundaries. The TCP envelope is the single most important invariant in the repo — see ../../CONTEXT.md for the domain language and ../../docs/adr/ for the recorded decisions. (docs/ARCHITECTURE.md, referenced by the root README, is a planned long-form expansion of the CONTEXT.md seam section.)

What it is

  • MCP module — the interface is the set of MCP tools plus their Zod schemas. The server is started in src/index.ts: it registers catalog resources (pcgex://catalog/{category}), registers tools via registerTools, starts the local web dashboard, connects a StdioServerTransport, and background-probes the visual sidecar.
  • TCP client seamsrc/tcp-client.ts is the Node adapter that talks to the UE plugin's FHaybaMCPTcpServer. Both sides must agree on a length-prefixed JSON envelope { cmd, id, params, auth? }.
  • Schema registrysrc/tools/schema-registry.ts records the Zod shape of every tool at registration time so signatures are derived from the same schema used to validate inputs — never a hand-maintained dict that drifts.

Run it

Requires Node ≥ 22.5 (see engines in package.json; older Node crashes on the node:sqlite-adjacent native deps).

npm install                 # from the repo root (npm workspaces)
npm run build -w @hayba/mcp # builds the dashboard, then `tsc`
node mcp-tools/hayba-mcp/dist/index.js

Scripts (package.json):

Script Action
build build:dashboard then tsc
build:server tsc only (skip the dashboard)
dev tsc --watch
start node dist/index.js
test vitest run
typecheck tsc --noEmit

The package also exposes a hayba-mcp bin (./dist/index.js), so an MCP host can launch it directly once built.

npm test here (tsc --noEmit + vitest) is the authoritative local gate. Run it before pushing.

Configuration

All via environment variables (src/config.ts):

Var Default Purpose
UE_TCP_HOST 127.0.0.1 UE plugin host
UE_TCP_PORT 52342 UE plugin port (plugin auto-falls back 52343–52350)
DASHBOARD_PORT 52360 Local web dashboard (kept clear of the UE 52342–52350 walk)
HAYBA_CODE_MODE on (off to disable) Progressive tool discovery (see below)
HAYBA_NODE_CATALOG resolved Override PCGEx node_catalog.json path
HAYBA_PCGEX_DB resolved Override PCGEx pcgex_registry.db path
HAYBA_CRITIQUE_ENABLED / HAYBA_CRITIQUE_THRESHOLD on / 15.0 Terrain self-critique

Resource paths (node_catalog.json, pcgex_registry.db) are resolved by walking a fallback list — new plugin layout, workspace Resources/, legacy Hayba_PcgEx_MCP layout — so existing installs keep working.

Code Mode (the deep interface)

By default the server exposes only three meta-tools instead of the full ~100-tool catalog:

  • list_tool_categories — enumerates the handler domains and the command names within each (src/tools/code-mode/list-tool-categories.ts); disabled tools are filtered out.
  • get_tool_signature — derives a command's parameter schema from the Zod registry on demand, with a "did you mean" suggestion on a miss (src/tools/code-mode/get-tool-signature.ts).
  • python_run — executes a script through UE's PythonScriptPlugin over the TCP seam for anything the typed commands don't cover (src/tools/python/python-run.ts); filesystem/subprocess (Tier 3) access is gated behind allow_unsafe.

The full catalog is registered eagerly only when HAYBA_CODE_MODE=off (see the if (config.codeMode) return; guard in src/tools/index.ts). This is a deliberately deep module: a tiny interface hiding a large surface, so a multi-domain task doesn't pay the token cost of every schema up front.

The TCP client seam

src/tcp-client.ts (UETcpClient) connects to 127.0.0.1:52342 by default. Wire format:

  • Length-prefixed JSON. A 4-byte little-endian length header precedes the UTF-8 JSON body.
  • Request: { cmd, id, params } (TcpCommand).
  • Response: { id, ok, data?, error? } (TcpResponse), correlated back to the caller by id.
  • The client tolerates partial reads (it buffers until a full frame arrives) and rejects all pending requests on socket close. The matching adapter is the UE plugin's FHaybaMCPTcpServer; the plugin publishes its actual port to Saved/HaybaMCP/instances/<pid>.json for multi-editor setups.

Visual sidecar addon

addons/visual-embeddings is a Python FastAPI sidecar (CLIP / SpatialCLIP / OWL-ViT) used for spatial grounding and physics validation. src/index.ts background-probes it at startup (pingSidecar) and caches availability so visual tools and hayba_check_ue_status can branch on it without paying connect-timeout latency. It is optional and degraded-mode aware — the server runs without it. Setup: addons/visual-embeddings/README.md and ../../docs/getting-started.md (Tier 2).

Other addons:

  • addons/workflowsSKILL.md workflow guides (hayba-new-scene, hayba-refine-scene, hayba-debug-level, hayba-pcg-build) a Claude Code host surfaces for matching tasks (Tier 3).
  • dashboard/ — the Vite/React local web dashboard served by src/dashboard/server.ts.

How to add a tool

  1. Add a handler module under src/tools/ (or a domain subfolder) exporting a Zod schema and a handler of type ToolHandler.
  2. Register it in src/tools/index.ts via the wrapped server.tool(...). Registration also calls recordSchema(name, { shape, cost, returns }) so the schema registry knows the command — regardless of whether Code Mode eagerly registers it.
  3. If the command is dispatched into UE, ensure the UE plugin has a matching handler (see ../../unreal/HaybaMCPToolkit/README.md) and that both sides agree on the { cmd, id, params } envelope.
  4. Run npm test -w @hayba/mcp (tsc --noEmit + vitest).

Eagerly-registered tools are only exposed to the agent when Code Mode is off; under Code Mode they are reached through python_run / discovered via list_tool_categories. Recording the schema keeps get_tool_signature accurate either way.

Layout

src/
  index.ts            MCP server entrypoint (stdio + dashboard + sidecar probe)
  tcp-client.ts       UE TCP adapter (length-prefixed JSON envelope)
  config.ts           env-driven config + resource path resolution
  resources.ts        pcgex://catalog/{category} resources
  catalog.ts          PCGEx node catalog access
  tools/
    index.ts          registerTools — the single registration point
    schema-registry.ts Zod-shape registry feeding get_tool_signature
    code-mode/        list_tool_categories, get_tool_signature
    python/           python_run
    actor|scene|editor|visual|material|plumb/ … domain tool modules
  agents/ dashboard/ … supporting modules
addons/workflows          SKILL.md workflow guides
dashboard/                Vite/React local dashboard

The Python visual sidecar lives at ../visual-sidecar.

See also