-
Notifications
You must be signed in to change notification settings - Fork 169
Development
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
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
- 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.
-
The request is forwarded as
{ action, ...params }to the record's internal tool. There is no TypeScript action layer; onlymanage_toolsruns inside the Node.js process. - 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.
- A domain handler does the work. The reply is projected onto the capability's declared output schema and returned with a receipt.
🟦 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).
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 buildPoint 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-projectWarning
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.
| 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.
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
-
Write the record in
src/tools/catalog/capabilities/records/<tool>/, usually throughbuildCoreRecord(). 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"). -
If it belongs to a family, add it to
records/folds/<tool>.folds.tsas a new selector value instead of a separate capability. -
Regenerate:
npm run registry:generate, thenregistry:checkandmanifest:check. -
Implement the handler in C++ under
Private/Domains/<Domain>/, reachable from its tool's registration inPrivate/Core/Subsystem/. Use theFoundationhelpers andSafetywrappers. -
Test it: a case under
tests/mcp-tools/sotest:paramspasses, 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.
| 🎮 Unreal side | 🟦 TypeScript side |
|---|---|
|
|
- Keep pull requests small and focused, with a Conventional Commits title (
feat: …,fix: …,docs: …). - Add a line to
CHANGELOG.mdunder[Unreleased]that describes the change from a user's point of view. Plugin changes also go inplugins/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.
| 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 |
📖 This wiki covers the 0.6 line (dev branch, npm @beta) · ✏️ Something wrong or missing? Open an issue or start a discussion
🏠 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