@uemcp/server 0.3.0 → 1.0.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 +75 -0
- package/dist/index.mjs +1630 -269
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,18 @@ The MCP tool surface currently exposes:
|
|
|
32
32
|
- `set_config_value`
|
|
33
33
|
- `list_config_section`
|
|
34
34
|
- `run_clang_tidy`
|
|
35
|
+
- `list_actors`
|
|
36
|
+
- `get_actor_details`
|
|
37
|
+
- `get_current_level`
|
|
38
|
+
- `spawn_actor`
|
|
39
|
+
- `destroy_actor`
|
|
40
|
+
- `set_actor_transform`
|
|
41
|
+
- `set_actor_property`
|
|
42
|
+
- `select_actors`
|
|
43
|
+
- `undo`
|
|
44
|
+
- `redo`
|
|
45
|
+
- `open_level`
|
|
46
|
+
- `get_open_level_status`
|
|
35
47
|
|
|
36
48
|
## Project Identity
|
|
37
49
|
|
|
@@ -137,6 +149,69 @@ This is intended to let an MCP client resolve the canonical Unreal project ident
|
|
|
137
149
|
- Spawns `clang-tidy` (server-configured via `CLANG_TIDY_PATH`, else `PATH`) over the translation units and returns structured findings (file, line, column, check, severity, message)
|
|
138
150
|
- Report only — does not apply fixes
|
|
139
151
|
|
|
152
|
+
### `list_actors` / `get_actor_details` / `get_current_level`
|
|
153
|
+
|
|
154
|
+
- The read half of the level surface. All three answer about actors instantiated in the **editor world** — not the PIE world, and not assets on disk (`find_assets` remains the tool for those)
|
|
155
|
+
- Every actor is identified by `actorPath` — `AActor::GetPathName()`, e.g. `/Game/Maps/Test.Test:PersistentLevel.StaticMeshActor_3` — carried alongside the display `label`. The path is the only stable, unique, resolvable identifier; labels are neither unique nor resolvable, so never address an actor by one. The same `actorPath` round-trips through `get_editor_state`, `list_actors`, and `select_actors`
|
|
156
|
+
- `list_actors` filters by class and by name substring, and pages with `nextCursor` / `totalCount` / `truncated`. Pages are trimmed by encoded bytes as well as by entry count, because actor paths vary wildly in length — `totalCount` stays authoritative when a page truncates
|
|
157
|
+
- `get_actor_details` reports the actor's class, transform, folder, tags, components, and reflected properties, paging components and properties independently. Property values are `ExportText`, the same format `get_cdo_defaults` emits and `set_actor_property` accepts
|
|
158
|
+
- `get_current_level` reports the loaded map's package name plus its streaming levels and World Partition state
|
|
159
|
+
|
|
160
|
+
### `spawn_actor` / `destroy_actor` / `set_actor_transform` / `set_actor_property` / `select_actors`
|
|
161
|
+
|
|
162
|
+
- The mutating half. **Every one of these runs inside a single `FScopedTransaction` on the game thread, so one Ctrl+Z in the editor reverses the whole call** — a batch of 64 spawns undoes as one step, not 64. The response reports the `transactionTitle` the batch collapsed into
|
|
163
|
+
- All five take **arrays** and return per-item results (`{ index, ok, actorPath?, error? }`) with `successCount` / `failureCount`. One bad entry fails its own item and neither aborts the batch nor rolls back the transaction. The batch bound is 64 (`BRIDGE_LEVEL_EDIT_MAX_BATCH`), sized so the request also stays under the 64 KiB payload cap
|
|
164
|
+
- `dryRun: true` validates everything the real call validates — class resolves, actor exists, property path resolves and the value coerces — and reports what *would* change with `state: 'validated'`, opening no transaction and touching no world state
|
|
165
|
+
- Mutations `Modify()` the objects they touch and dirty their packages, so `save_dirty_packages` subsequently reports and persists them
|
|
166
|
+
- `set_actor_transform` treats each of `location` / `rotation` / `scale` as optional: an omitted component keeps its current value, so a move need not restate the rest. An entry naming none of the three is rejected rather than silently doing nothing
|
|
167
|
+
- `set_actor_property` addresses a property on the actor (`bHidden`) or on one of its components (`StaticMeshComponent.RelativeScale3D`), and writes via `ImportText_InContainer` — the exact inverse of the reader, so a value read from `get_actor_details` or `get_cdo_defaults` can be edited and written back with no format translation. `EditConst` and non-editable properties are rejected with an explicit error rather than silently no-op'ing
|
|
168
|
+
- `select_actors` sets the editor selection; an empty `actorPaths` clears it, and `addToSelection` adds rather than replaces. It reports the resulting `selectedActorCount`
|
|
169
|
+
- In a non-editor plugin build every one of these returns `state: 'unsupported'` rather than failing
|
|
170
|
+
|
|
171
|
+
### `undo` / `redo`
|
|
172
|
+
|
|
173
|
+
- Act on the editor's own transaction buffer — the same buffer Ctrl+Z and Ctrl+Y drive — and report the `transactionTitle` they acted on, or `state: 'nothing_to_undo'` / `'nothing_to_redo'`
|
|
174
|
+
- The head of that buffer may be a **human's** edit rather than the agent's, so check the returned `transactionTitle` before assuming what was reversed
|
|
175
|
+
|
|
176
|
+
### `open_level` / `get_open_level_status`
|
|
177
|
+
|
|
178
|
+
- Loading a map tears down and rebuilds the world, which runs far past the plugin's 5 s game-thread budget, so `open_level` does not load inline: it validates the request, schedules the load onto the next editor tick, and returns `state: 'opening'` with an `openLevelJobId`. Poll it with `get_open_level_status` (or `wait_for_job`) until the state leaves `opening`
|
|
179
|
+
- `dryRun: true` checks that the level package exists and reports `state: 'validated'` with no job — nothing was scheduled, so nothing is pollable
|
|
180
|
+
- Unsaved changes in the current world are **discarded** unless `saveDirtyPackages: true`, which saves before the teardown. There is no user in front of a bridge request to answer the editor's usual "save these?" prompt
|
|
181
|
+
- **Not undoable.** Loading a map clears the transaction buffer, so `undo` cannot bring the previous level back
|
|
182
|
+
- Actor paths do not survive a load: re-run `list_actors` against the new world rather than reusing paths from the old one
|
|
183
|
+
- One load at a time per editor; a second request while one is running is refused with `open_level_in_progress` rather than queued
|
|
184
|
+
|
|
185
|
+
## MCP Resources
|
|
186
|
+
|
|
187
|
+
Resources carry context a client can attach without spending a tool call and a model turn. Every one of them is served from in-memory state or the local filesystem: **a resource read never opens a bridge socket and never waits on the editor**, so an unreachable or absent editor produces a structured payload rather than a timeout.
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
| URI | Contents |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| `uemcp://registry` | Every registered Unreal Editor: `projectId`, `projectName`, `projectPath`, `port`, `pid`, `registeredAt`, `lastSeenAt`, plus `totalCount` and `truncated` |
|
|
194
|
+
| `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 |
|
|
195
|
+
| `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` |
|
|
196
|
+
| `uemcp://workspace/project` | The Unreal project identity of the server's working directory, resolved from `Config/DefaultGame.ini` |
|
|
197
|
+
|
|
198
|
+
`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.
|
|
199
|
+
|
|
200
|
+
Registering or deregistering an editor over the HTTP API emits `notifications/resources/list_changed`. There is no `resources/subscribe`: the MCP SDK's `McpServer` does not implement it, so the list-changed notification is the only change signal.
|
|
201
|
+
|
|
202
|
+
## MCP Prompts
|
|
203
|
+
|
|
204
|
+
Prompts ship the workflows this server knows about as discoverable entry points for any MCP client, not just ones that can read the repo's `.agents/skills/` runbooks. They are compiled into the bundle, reference only tools this server registers, and assume no shell, no checkout, and no particular client.
|
|
205
|
+
|
|
206
|
+
| Prompt | Arguments | What it drives |
|
|
207
|
+
| --- | --- | --- |
|
|
208
|
+
| `diagnose_crash` | `projectId` | Registration check, `get_crash_callstack`, `get_editor_state`, then the output log around the crash |
|
|
209
|
+
| `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 |
|
|
210
|
+
| `smoke_test_bridge` | `projectId` | Green-path proof: registration, `ping_project`, `get_editor_state`, and a full log subscribe-emit-drain cycle |
|
|
211
|
+
| `add_gameplay_feature` | `projectId`, `className`, `parentClass`, `moduleName` (optional) | `create_cpp_class` → `wait_for_job` → implement → `trigger_live_compile` → `run_automation_tests` → `save_dirty_packages` |
|
|
212
|
+
|
|
213
|
+
`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.
|
|
214
|
+
|
|
140
215
|
## Configuration
|
|
141
216
|
|
|
142
217
|
- Engine toolchain (`build_target`, `generate_project_files`, `clean_build`):
|