@uemcp/server 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -4
- package/dist/index.mjs +20163 -16076
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,12 @@ The MCP tool surface currently exposes:
|
|
|
125
125
|
- `create_sound_cue`
|
|
126
126
|
- `create_metasound`
|
|
127
127
|
- `set_metasound_graph`
|
|
128
|
+
- `find_symbol`
|
|
129
|
+
- `export_blueprint_cpp`
|
|
130
|
+
- `add_reflected_member`
|
|
131
|
+
- `scaffold_class_template`
|
|
132
|
+
- `get_live_compile_details`
|
|
133
|
+
- `run_editor_python`
|
|
128
134
|
|
|
129
135
|
## Project Identity
|
|
130
136
|
|
|
@@ -141,6 +147,13 @@ If `DefaultGame.ini` cannot be read, if the section is missing, or if the `Proje
|
|
|
141
147
|
|
|
142
148
|
## MCP Tools
|
|
143
149
|
|
|
150
|
+
### `doctor`
|
|
151
|
+
|
|
152
|
+
- Runs every setup check in one pass and returns `checks` with a `pass` / `warn` / `fail` status and, for anything that is not passing, the `fix` that clears it
|
|
153
|
+
- Covers engine installs under `UE_INSTALLS_DIR` and whether UnrealBuildTool resolves in each, the MCPBridge descriptor beside the project or under an engine, the ProjectID in `Config/DefaultGame.ini`, the registry file, a live bridge ping per registered editor, plugin/server version skew — read from what the ping answered, not from what the registration recorded — and whether an MCP client config names this server
|
|
154
|
+
- Runs with no registered project and no engine installed — a setup that is broken everywhere is exactly when it is called, so no check is allowed to be a precondition for the rest
|
|
155
|
+
- `projectId` narrows the bridge and version checks to one editor, and fails when nothing is registered under it; `projectPath` names the project to resolve, falling back to a registration and then to the server working directory
|
|
156
|
+
|
|
144
157
|
### `get_project_id`
|
|
145
158
|
|
|
146
159
|
- Accepts a project path from the MCP caller
|
|
@@ -182,9 +195,11 @@ This is intended to let an MCP client resolve the canonical Unreal project ident
|
|
|
182
195
|
### `wait_for_job`
|
|
183
196
|
|
|
184
197
|
- Blocks server-side until an async job reaches a terminal state, then returns the payload the matching `get_*_status` tool would have returned under `status`
|
|
185
|
-
- Replaces a client-side poll loop for `kind` `compile` (`trigger_live_compile` / `trigger_hot_reload`), `create_class`, `test_run`, `pie`, and `source_control` (`checkout_assets` / `refresh_source_control_status`); `kind` is required because job ids are bare GUIDs
|
|
198
|
+
- Replaces a client-side poll loop for `kind` `compile` (`trigger_live_compile` / `trigger_hot_reload`), `create_class`, `test_run`, `pie`, `package` (`cook_content` / `package_project` / `run_editor_commandlet`), and `source_control` (`checkout_assets` / `refresh_source_control_status`); `kind` is required because job ids are bare GUIDs
|
|
186
199
|
- `outcome` is `completed`, `timed_out` (last observed status still returned), or `unknown_job` (unknown/expired/cancelled id, returned on the first poll)
|
|
187
200
|
- Per-kind default budgets (compile 120s, create_class 60s, pie 30s, source_control 60s, test_run 300s), clamped to 600s; polls back off from 250ms (1s for test runs) and a transient bridge read timeout or dropped socket is retried rather than failing the wait
|
|
201
|
+
- `package` is a server-side child process, so waiting on one needs no registered editor — every other kind does
|
|
202
|
+
- Emits `notifications/progress` on each poll when the request carried a `_meta.progressToken`, and nothing when it did not
|
|
188
203
|
- For `test_run`, `status.results` is the first page — page the rest with `get_test_run_status` and `nextCursor`
|
|
189
204
|
|
|
190
205
|
### `build_target` / `generate_project_files` / `clean_build`
|
|
@@ -603,18 +618,67 @@ This is intended to let an MCP client resolve the canonical Unreal project ident
|
|
|
603
618
|
- `restartProperties` names the settings the engine reads once at startup. They are written correctly and still inert until the editor restarts, which is worth saying rather than leaving the caller to wonder
|
|
604
619
|
- `set_default_gamemode` writes the project default through `GameMapsSettings`, or a level override through that level's world settings. The pawn, controller, state and HUD classes are not settings — they live on the game mode class — so they are written onto its defaults and the Blueprint is recompiled. A **C++ game mode refuses them**: its class defaults are rebuilt from its constructor on every start, so the write would live only until the editor closed
|
|
605
620
|
|
|
621
|
+
### `find_symbol`
|
|
622
|
+
|
|
623
|
+
- Answers "where is this type declared" across the engine and the project without a running editor: it scans headers for `UCLASS` / `UINTERFACE` / `USTRUCT` / `UENUM` / `UFUNCTION` / `UPROPERTY` and reports the header path, the owning module, and the `#include` path to write
|
|
624
|
+
- The include path is derived from the nearest `Public/` or `Classes/` directory, which is what UBT puts on the include path — a header under `Private/` is reported without one, because there is no include that reaches it from another module
|
|
625
|
+
- The module name comes from the nearest `*.Build.cs` walking up from the header, so it is the module a caller has to add to `PrivateDependencyModuleNames`, not a guess from the directory name
|
|
626
|
+
- Matching is exact first, then prefix, then substring, so a full type name never loses to a longer one that happens to contain it. `kind` narrows to one symbol kind, and results page with `maxResults` / `totalCount` / `truncated`
|
|
627
|
+
- The index is cached on disk per root set (hashed engine + project roots) beside the registry file, and rebuilt incrementally: a file whose size and mtime are unchanged reuses its cached records, so a rescan after a build touches only what the build touched
|
|
628
|
+
- `Binaries`, `Intermediate`, `Saved`, `DerivedDataCache`, `ThirdParty` and VCS directories are skipped, and the walk stops at `maxFiles` (default 80,000) rather than running unbounded over an engine source tree
|
|
629
|
+
- Cost is real and worth planning around: a cold UE 5.8 engine scan reads about 45,000 headers and takes a couple of minutes, after which a warm call re-stats them and answers in seconds. Call it once per engine rather than per question
|
|
630
|
+
|
|
631
|
+
### `export_blueprint_cpp`
|
|
632
|
+
|
|
633
|
+
- Turns a `get_blueprint_graph` payload into something readable: pseudo-code for each graph, and a C++ header skeleton for the class
|
|
634
|
+
- Pure server-side transform — no plugin change of its own. The editor still owns the graph; this only renders what it hands back
|
|
635
|
+
- The walk follows exec pins from each entry node, so `K2Node_IfThenElse` becomes `if`/`else`, `K2Node_ExecutionSequence` becomes sequential statements, and the For / While / For Each macro instances become the matching loop. A node reached twice emits a `goto` and sets `truncated` rather than looping forever
|
|
636
|
+
- Roots are sorted by graph title then node id, so the same graph renders byte-identical on every call — the output is diffable
|
|
637
|
+
- The skeleton maps Blueprint variables to `UPROPERTY` (including `ReplicatedUsing` for a variable with a RepNotify) and Blueprint functions to `UFUNCTION`, with out parameters as references. It is a starting point for a hand port, **not** a compilable nativization
|
|
638
|
+
|
|
639
|
+
### `add_reflected_member` / `scaffold_class_template`
|
|
640
|
+
|
|
641
|
+
- The authoring half of the C++ surface: `add_reflected_member` inserts a `UPROPERTY` or `UFUNCTION` into an existing class, `scaffold_class_template` writes a new pair of files for one of the shapes UE has no simple template for
|
|
642
|
+
- Members are inserted directly after the class's own `GENERATED_BODY()` under a fresh `public:`. That is this tool's policy, not a UHT rule — UHT reflects `private:` and `protected:` members too, and `meta = (AllowPrivateAccess = "true")` only governs whether Blueprints can reach a private `UPROPERTY`. Opening a section makes the insertion point deterministic instead of inheriting whatever section the cursor happened to land in, at the cost of every generated member being public; move it afterwards if it belongs elsewhere. A member that is already declared is reported rather than duplicated
|
|
643
|
+
- **Specifiers are validated before anything is written.** Rejected combinations include more than one Edit or Visible specifier, `BlueprintReadOnly` with `BlueprintReadWrite`, `Replicated` with `ReplicatedUsing`, a bare `ReplicatedUsing` with no handler, `BlueprintPure` with `BlueprintCallable`, `BlueprintImplementableEvent` with `BlueprintNativeEvent`, more than one RPC target, an RPC with no reliability specifier, and an RPC with a non-void return. An unknown specifier is rejected too — a typo UHT would catch minutes later at build time fails here in milliseconds
|
|
644
|
+
- A function also gets a `.cpp` stub (header `Public/X.h` → source `Private/X.cpp`), except for `BlueprintImplementableEvent`, which has no native body by definition. A `BlueprintNativeEvent` gets its stub named `Name_Implementation`, which is the symbol UHT's dispatcher calls and the one that fails to link if it is missing
|
|
645
|
+
- `scaffold_class_template` covers `interface` (the `UInterface` + `IInterface` pair, the shape that is got wrong most often) and the `game_instance_subsystem` / `world_subsystem` / `editor_subsystem` trio. It refuses to overwrite existing files and returns the module dependencies the new files need
|
|
646
|
+
- Both are filesystem-only and need no editor. Run project-file generation and a build — or `trigger_live_compile` for a body-only change — afterwards
|
|
647
|
+
|
|
648
|
+
### `get_live_compile_details`
|
|
649
|
+
|
|
650
|
+
- Reports what Live Coding actually did with the last patch: the modules it patched, and, when it refused, why
|
|
651
|
+
- The restart case is the one worth naming. A change to a reflected type's layout — a new `UPROPERTY`, a reordered member — patches into the running editor, but the editor is then running against types its loaded assets no longer match, and the engine says so only as a warning on the completion line. That warning comes back as `restartRequired` with the engine's own wording, instead of leaving a caller to wonder why their change behaved oddly
|
|
652
|
+
- The two halves come from two places, because neither alone is enough: `ILiveCodingModule` reports counts only, the editor's `LogLiveCoding` carries just the completion line, and which modules were patched is printed by the separate `LiveCodingConsole` process into its own log. That log is read on demand, filtered to the compile's own time window, and to modules this editor has actually loaded — one console serves every editor in its process group
|
|
653
|
+
- Bounded: at most 64 entries per kind and 512 characters per entry, with `truncated` set when either cap is hit
|
|
654
|
+
- Windows-only in practice — Live Coding ships only where the engine supports it. Elsewhere the compile job carries no details rather than failing
|
|
655
|
+
|
|
656
|
+
### `run_editor_python`
|
|
657
|
+
|
|
658
|
+
- Executes Python inside the running editor through `IPythonScriptPlugin` and returns the log output, the error lines, and the evaluated result. The `py` console command exec path cannot return any of that
|
|
659
|
+
- A failed run reports its traceback in `errors` and leaves `result` unset, so `result` always means "the value the script produced" rather than sometimes meaning "why it did not"
|
|
660
|
+
- **Off by default.** It hands a caller the editor's entire Python API with no sandbox around it, so it stays disabled until a human enables `Allow run_editor_python` under Project Settings → Plugins → MCP Bridge (`MCPBridgeSettings.bAllowEditorPython`). With the setting off the tool returns `state: 'disabled'` naming that setting rather than failing opaquely
|
|
661
|
+
- An editor whose Python Script Plugin is absent or disabled returns `state: 'unsupported'` with the reason, which is a different answer from a script that ran and failed
|
|
662
|
+
- The whole script runs inside one `FScopedTransaction`, so a successful run is a single editor undo; a failed run cancels the transaction rather than leaving half its effects behind
|
|
663
|
+
- `executionMode` is `script` (default: a whole block of Python, or a `.py` path with arguments), `statement` (exactly one statement) or `evaluate` (one expression, whose value comes back in `result`). `script` is the default because the engine reports a traceback line by line only in that mode; the two statement modes put it in `result` instead. Commands are capped at 16,384 characters, and output and error lines at 200 lines each, with `truncated` set
|
|
664
|
+
|
|
606
665
|
## MCP Resources
|
|
607
666
|
|
|
608
|
-
Resources carry context a client can attach without spending a tool call and a model turn.
|
|
667
|
+
Resources carry context a client can attach without spending a tool call and a model turn. The `registry`, `engines` and `workspace` resources are served from in-memory state or the local filesystem: **those reads never open a bridge socket and never wait on the editor**, so an unreachable or absent editor produces a structured payload rather than a timeout. The `project` resources are the exception — they are the editor-backed reads, and they report `found: false` with a `reason` instead of throwing when the editor is gone.
|
|
609
668
|
|
|
610
|
-
All resources use the `uemcp://` URI scheme, are `application/json`, and are capped at 64 KB (`MAX_RESOURCE_PAYLOAD_BYTES`). A read that hits the cap drops entries from the end of the list and sets `truncated: true` while `totalCount` stays authoritative — the same convention the paged tools use. The authority segment (`registry`, `engines`, `workspace`) is the type discriminator.
|
|
669
|
+
All resources use the `uemcp://` URI scheme, are `application/json`, and are capped at 64 KB (`MAX_RESOURCE_PAYLOAD_BYTES`). A read that hits the cap drops entries from the end of the list and sets `truncated: true` while `totalCount` stays authoritative — the same convention the paged tools use. The authority segment (`registry`, `engines`, `workspace`, `project`) is the type discriminator.
|
|
611
670
|
|
|
612
671
|
| URI | Contents |
|
|
613
672
|
| --- | --- |
|
|
614
673
|
| `uemcp://registry` | Every registered Unreal Editor: `projectId`, `projectName`, `projectPath`, `port`, `pid`, `registeredAt`, `lastSeenAt`, plus `totalCount` and `truncated` |
|
|
615
674
|
| `uemcp://registry/{projectId}` | One registration, addressed by canonical ProjectID. Enumerable — the template's `list` callback returns the current registry, and `projectId` offers completions from it |
|
|
616
675
|
| `uemcp://engines` | Unreal installs found under `UE_INSTALLS_DIR`, ascending by version, each with `engineVersion`, `engineRoot`, and `unrealBuildToolResolved`. Source builds and GUID engine associations are excluded, matching `resolveEngine` |
|
|
617
|
-
| `uemcp://workspace/project` | The Unreal project identity of the server's working directory,
|
|
676
|
+
| `uemcp://workspace/project` | The Unreal project identity of the server's working directory, plus a `context` block: modules with their `Build.cs` dependencies, enabled plugins, target platforms, engine association, and the default game mode, game instance and startup map |
|
|
677
|
+
| `uemcp://project/{projectId}/asset/{+assetPath}` | Asset metadata for one content path — the payload `get_asset_metadata` returns |
|
|
678
|
+
| `uemcp://project/{projectId}/class/{className}` | UClass reflection for one class — the payload `get_class_reflection` returns |
|
|
679
|
+
| `uemcp://project/{projectId}/blueprint/{+assetPath}/graph` | A Blueprint's `EventGraph` nodes and connections — the payload `get_blueprint_graph` returns. Use the tool for any other graph or to page past the node cap |
|
|
680
|
+
|
|
681
|
+
The asset and blueprint templates use reserved expansion (`{+var}`) so an Unreal content path keeps its slashes. Both forms of the path resolve: `uemcp://project/<id>/asset/Game/Meshes/SM_Rock` and `uemcp://project/<id>/asset//Game/Meshes/SM_Rock` reach the same asset, because the template segment already follows a `/`.
|
|
618
682
|
|
|
619
683
|
`uemcp://registry/{projectId}` reports `found: false` with a `reason` for an unknown or malformed ProjectID, and `uemcp://engines` / `uemcp://workspace/project` report a `reason` when `UE_INSTALLS_DIR` is unset or the directory is not an Unreal project — none of them fail the read.
|
|
620
684
|
|
|
@@ -630,15 +694,37 @@ Prompts ship the workflows this server knows about as discoverable entry points
|
|
|
630
694
|
| `diagnose_bridge_disconnect` | `projectPath` (optional) | Which leg of the bridge is broken: registry state, `get_project_id` cross-check, `ping_project`, plugin-side log evidence |
|
|
631
695
|
| `smoke_test_bridge` | `projectId` | Green-path proof: registration, `ping_project`, `get_editor_state`, and a full log subscribe-emit-drain cycle |
|
|
632
696
|
| `add_gameplay_feature` | `projectId`, `className`, `parentClass`, `moduleName` (optional) | `create_cpp_class` → `wait_for_job` → implement → `trigger_live_compile` → `run_automation_tests` → `save_dirty_packages` |
|
|
697
|
+
| `fix_blueprint_compile_errors` | `projectId`, `blueprintPath` (optional) | Read the graph before editing it: `get_blueprint_compile_errors` → `get_blueprint_summary`/`get_blueprint_graph` → targeted authoring edits → `compile_blueprint` until it settles |
|
|
698
|
+
| `profile_hitch` | `projectId`, `tracePath` (optional) | `start_insights_trace` → reproduce → `stop_insights_trace` → `analyze_trace`, then confirm the suspect scope against `get_stat_snapshot` and the project |
|
|
699
|
+
| `setup_gas_character` | `projectId`, `characterName`, `packagePath` (optional) | Bottom-up GAS stack: tags → attribute set → cost/cooldown effects → ability → cue → character Blueprint |
|
|
700
|
+
| `port_blueprint_to_cpp` | `projectId`, `blueprintPath`, `moduleName` (optional) | `get_blueprint_summary`/`find_blueprint_references` → `create_cpp_class` → implement with matching member names → build → reparent → tests |
|
|
633
701
|
|
|
634
702
|
`diagnose_bridge_disconnect` and `smoke_test_bridge` are distilled from the same-named skills in `.agents/skills/`. Those files stay separate on purpose: they are maintainer runbooks that reference `just` recipes, `registry.json` paths, and pino module loggers, and the published package ships `dist` only, so they cannot be read at runtime. Each skill carries a pointer to its prompt counterpart so the two do not silently diverge.
|
|
635
703
|
|
|
704
|
+
## Tool annotations, toolsets and search mode
|
|
705
|
+
|
|
706
|
+
Every tool declares all four MCP annotation hints — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` — so a client can auto-approve a query and prompt on a delete. `openWorldHint` is true only where the tool reaches past the project directory and the running editor: a target device, or a source file the caller names.
|
|
707
|
+
|
|
708
|
+
The gateway groups the tools into toolsets: `core`, `build`, `test`, `level`, `asset`, `blueprint`, `anim`, `ai`, `gas`, `ui`, `sequence`, `fx`, `audio`, `input`. Two environment variables shape what a client sees:
|
|
709
|
+
|
|
710
|
+
- `UEMCP_TOOLSETS` — a comma-separated selection, or `all`. Unset means every toolset, which is what a client that has never heard of this setting keeps getting. `core` is always included, because `enable_toolset` lives there and it is the way back to the rest of the list. `UEMCP_TOOLSETS=core` fits inside the ~40-tool ceiling clients like Cursor impose; `enable_toolset` loads a further group at runtime and emits `notifications/tools/list_changed`.
|
|
711
|
+
- `UEMCP_TOOL_MODE` — `direct` (the default) advertises the tool list. `search` advertises exactly three meta-tools — `search_tools`, `get_tool_schema`, `call_tool` — for a client that cannot hold a large tool list at all. `search_tools` ranks the catalog with BM25 over names, titles, descriptions and toolset keywords; `call_tool` validates against the same schema a direct call uses, so an invalid payload fails identically. The prompts name tools directly; in search mode each one opens with a line saying to run those names through `call_tool`.
|
|
712
|
+
|
|
713
|
+
A plugin that reports its supported request types (see the plugin's capability list) also shapes the tool list: a tool whose bridge request no registered plugin serves is hidden, and calling one against a plugin that cannot serve it returns a clear unsupported error before a socket is opened. A plugin build that predates capability reporting leaves every tool visible.
|
|
714
|
+
|
|
715
|
+
## Progress notifications
|
|
716
|
+
|
|
717
|
+
`wait_for_job` sends `notifications/progress` as a job advances whenever the request carried a `_meta.progressToken`, and sends nothing when it did not. `progress` is the milliseconds waited against a `total` of the wait budget, so it rises monotonically and a client can render a bar. `kind` covers `build`, `compile`, `create_class`, `import`, `package` (cook, package and commandlet jobs), `pie`, `render`, `source_control` and `test_run`; `package` is server-side and needs no registered editor.
|
|
718
|
+
|
|
636
719
|
## Configuration
|
|
637
720
|
|
|
638
721
|
- Engine toolchain (`build_target`, `generate_project_files`, `clean_build`):
|
|
639
722
|
- `UE_INSTALLS_DIR` — base directory holding installed engines (`UE_<version>`)
|
|
640
723
|
- `run_clang_tidy`:
|
|
641
724
|
- `CLANG_TIDY_PATH` — optional clang-tidy binary override
|
|
725
|
+
- Tool surface:
|
|
726
|
+
- `UEMCP_TOOLSETS`
|
|
727
|
+
- `UEMCP_TOOL_MODE`
|
|
642
728
|
- Local HTTP registry API:
|
|
643
729
|
- `MCP_SERVER_HTTP_HOST`
|
|
644
730
|
- `MCP_SERVER_HTTP_PORT`
|