Skip to content

Development

ChiR24 edited this page Sep 30, 2026 · 6 revisions

Contribute · Development: records in, everything generated

For contributors: how the code is organized, how to build and test it, and how a capability is added. The repository's AGENTS.md files hold the detailed rules for each area; this page is the map.

On this page · How a call flows · Layout · Build and run · Tests · Adding a capability · Rules · Contributing

How a call flows

sequenceDiagram
    autonumber
    participant C as MCP client
    participant G as Gateway
    participant P as Plugin
    participant H as Domain handler
    C->>G: execute
    G->>G: validate against the record
    G->>P: { action, ...params }
    P->>P: re-check security, then queue
    P->>H: run on the game thread
    H-->>P: result
    P-->>G: result
    G-->>C: data + receipt
Loading
  1. The gateway validates the call against the capability's generated record: parameters, options, scope and consent. It runs in TypeScript on the stdio route and in C++ inside the plugin on the native route.
  2. The request is forwarded as { action, ...params } to the record's internal tool. There is no TypeScript action layer; only manage_tools runs inside the Node.js process.
  3. The plugin re-checks scope, consent, paths, console policy and quota, then queues the request. The game thread drains the queue, up to 16 requests per tick.
  4. A domain handler does the work. The reply is projected onto the capability's declared output schema and returned with a receipt.

Repository layout

🟦 TypeScript server, in src/:

Folder Holds
server/ SDK server, stdio lifecycle, tools/list and tools/call
server/gateway/ search, describe and execute routing; idempotency ledger
server/mcp-primitives/ Prompts, completions, progress
tools/catalog/capabilities/records/ Capability records, the contract source of truth
tools/handlers/foundation/ The validated boundary that sends requests to the plugin
automation/ WebSocket client: handshake, token, request tracking
resources/, handlers/ MCP resources: providers and registration

🎮 Unreal plugin, in plugins/McpAutomationBridge/Source/McpAutomationBridge/Private/:

Folder Holds
Core/ Request queue, game-thread drain, handler table, security gate
Domains/ The editor work, one folder per domain
MCP/ Native MCP server and gateway. MCP/Generated/ is generated.
Foundation/ Shared helpers: reflection, paths, auth predicates, responses
Safety/ Wrappers for hazardous editor operations: save, load, delete
Transport/ WebSocket listener, TLS, token auth, rate limits

The optional Fab adapter lives in Source/McpAutomationBridgeFab/. Also at the root: scripts/ (generators, plugin packaging and sync, smoke test), tests/ (unit, integration, search-ranking eval) and docs/ (protocol, security, gateway guide, generated action reference).

Build and run

git clone https://github.com/ChiR24/Unreal_mcp.git
cd Unreal_mcp
npm install
npm run build         # clean + tsc to dist/
npm run dev           # run from source with ts-node, no build

Point a client at node <repo>/dist/cli.js (🔌 Connecting Clients). For the plugin, sync it into a test project, then build that project's editor target:

node scripts/sync-mcp-plugin.js --project "C:/Path/To/TestProject/Plugins" --clean-project

Warning

Live Coding patches don't survive an editor restart and don't update generated parameter tables. Any change to a capability's schema needs a full build.

Tests and checks

Command Editor? Checks
npm run lint — ESLint. CI runs it with --max-warnings=0.
npm run type-check — TypeScript, sources and tests
npm run test:unit — Vitest unit and contract tests, including source-contract tests that read the C++
npm run test:smoke — Starts the built server in mock mode; exactly one tool, unreal, must be listed
npm run test:params — Every declared parameter is covered by a test case, and no case sends an undeclared one
npm run eval:check — Search-ranking corpus: plain requests must find the intended capability
npm run registry:check · manifest:check · headers:check — Generated files match their sources
npm test ✅ Integration suite against a live editor (tests/integration.mjs)
node tests/mcp-tools/<area>/<tool>.test.mjs ✅ One internal tool's integration cases

CI runs, in order: lint › type-check › unit tests › registry:check › manifest:check › headers:check › test:params › eval:check. A separate job runs npm audit, and a Node 20.19 / 26 matrix adds build and test:smoke.

Note

The integration suite isn't in CI, because CI has no engine. Run it before submitting anything that changes editor behavior. Its cases use an expectation grammar such as success, error or success|not found: the first token is the intent, the rest are acceptable alternatives.

Adding or changing a capability

The capability records are the single source of truth. Everything a client or the plugin sees is generated from them:

flowchart LR
    rec["Capability records<br/>src/tools/catalog/capabilities/records"] -- "npm run registry:generate" --> gen{{"generator"}}
    gen --> ts["TypeScript tool definitions"]
    gen --> man["Gateway manifest"]
    gen --> nat["Plugin registry and schema shards"]
    gen --> doc["Action Reference doc"]
    classDef src fill:#1f6feb,stroke:#58a6ff,color:#ffffff
    classDef tool fill:#30363d,stroke:#8b949e,color:#f0f6fc
    classDef out fill:#238636,stroke:#3fb950,color:#ffffff
    class rec src
    class gen tool
    class ts,man,nat,doc out
Loading
  1. Write the record in src/tools/catalog/capabilities/records/<tool>/, usually through buildCoreRecord(). Declare exactly the parameters the C++ handler reads and the fields it returns (the gateway refuses anything undeclared), plus effect, scope, consent, cost, and 3 to 6 search topics phrased the way people ask ("spawn actor", "place actor").
  2. If it belongs to a family, add it to records/folds/<tool>.folds.ts as a new selector value instead of a separate capability.
  3. Regenerate: npm run registry:generate, then registry:check and manifest:check.
  4. Implement the handler in C++ under Private/Domains/<Domain>/, reachable from its tool's registration in Private/Core/Subsystem/. Use the Foundation helpers and Safety wrappers.
  5. Test it: a case under tests/mcp-tools/ so test:params passes, a search phrasing in the eval corpus if it's hard to find, and a live run in the editor.

Caution

Never hand-edit a *.generated.* file or anything in MCP/Generated/, and never change the asserted record count just to make a mismatch go away. The rules for records, folding and search ranking are in src/tools/catalog/AGENTS.md.

Rules the checks enforce

🎮 Unreal side 🟦 TypeScript side
  • Save and load only through McpSafeAssetSave, McpSafeLevelSave and McpSafeLoadMap. A direct UPackage::SavePackage() fails a source-contract test.
  • Blueprint components must be owned by SCS nodes (SCS->CreateNode(), then SCS->AddNode()).
  • No ANY_PACKAGE. Guard version-specific engine APIs so the plugin builds on every minor from 5.0 to 5.8.
  • At most 250 lines of code per plugin file and 25 source files per folder. Split into a subfolder before adding to a full one.
  • Security checks belong in the plugin. The TypeScript side may fail fast, but the plugin is the authority.
  • Strict types: no as any, no @ts-ignore.
  • No console.log at runtime: stdout carries the JSON-RPC stream. Log through Logger, which writes to stderr.
  • Send requests to the editor only through executeAutomationRequest(), never over a raw socket.
  • Sort with the byte-order helpers in src/utils/serialization/ordering.ts, not localeCompare, so generated files are identical on every machine.

Contributing

  • Keep pull requests small and focused, with a Conventional Commits title (feat: …, fix: …, docs: …).
  • Add a line to CHANGELOG.md under [Unreleased] that describes the change from a user's point of view. Plugin changes also go in plugins/McpAutomationBridge/CHANGELOG.md.
  • Run the checks above before pushing, plus a live test for anything that touches the editor.
  • Questions and ideas: Discussions. Bugs and feature requests: Issues.

Deeper documentation in the repository

Document Covers
docs/gateway-client-guide.md The gateway contract, with the source file behind each claim
docs/protocol.md Transports, protocol negotiation, cancellation, version sources
docs/security-and-receipts.md Scopes, consent, path gating, idempotency ledger, refusal codes
docs/mcp-primitives.md Resources, prompts, completions, progress
docs/testing-guide.md Test suites and how to add cases
AGENTS.md (root and per folder) Area-by-area rules for contributors and coding agents

🏠 Home

Get started
🚀 Quick Start
📦 Installation
🔌 Connecting Clients

Use it
🧭 Using the Gateway
🧰 Tools Reference
📚 Resources and Prompts

Set it up
⚙️ Configuration
🔐 Security

Help
🩺 Troubleshooting
💬 FAQ
⬆️ Upgrading from 0.5.x

Contribute
🛠️ Development


Covers the 0.6 line · Releases · Discussions

Clone this wiki locally