unreal-engine-mcp-server 0.3.1 → 0.4.3
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/.env.production +1 -1
- package/.github/copilot-instructions.md +45 -0
- package/.github/workflows/publish-mcp.yml +1 -1
- package/README.md +22 -7
- package/dist/index.js +137 -46
- package/dist/prompts/index.d.ts +10 -3
- package/dist/prompts/index.js +186 -7
- package/dist/resources/actors.d.ts +19 -1
- package/dist/resources/actors.js +55 -64
- package/dist/resources/assets.d.ts +3 -2
- package/dist/resources/assets.js +117 -109
- package/dist/resources/levels.d.ts +21 -3
- package/dist/resources/levels.js +31 -56
- package/dist/tools/actors.d.ts +3 -14
- package/dist/tools/actors.js +246 -302
- package/dist/tools/animation.d.ts +57 -102
- package/dist/tools/animation.js +429 -450
- package/dist/tools/assets.d.ts +13 -2
- package/dist/tools/assets.js +58 -46
- package/dist/tools/audio.d.ts +22 -13
- package/dist/tools/audio.js +467 -121
- package/dist/tools/blueprint.d.ts +32 -13
- package/dist/tools/blueprint.js +699 -448
- package/dist/tools/build_environment_advanced.d.ts +0 -1
- package/dist/tools/build_environment_advanced.js +236 -87
- package/dist/tools/consolidated-tool-definitions.d.ts +232 -15
- package/dist/tools/consolidated-tool-definitions.js +124 -255
- package/dist/tools/consolidated-tool-handlers.js +749 -766
- package/dist/tools/debug.d.ts +72 -10
- package/dist/tools/debug.js +170 -36
- package/dist/tools/editor.d.ts +9 -2
- package/dist/tools/editor.js +30 -44
- package/dist/tools/foliage.d.ts +34 -15
- package/dist/tools/foliage.js +97 -107
- package/dist/tools/introspection.js +19 -21
- package/dist/tools/landscape.d.ts +1 -2
- package/dist/tools/landscape.js +311 -168
- package/dist/tools/level.d.ts +3 -28
- package/dist/tools/level.js +642 -192
- package/dist/tools/lighting.d.ts +14 -3
- package/dist/tools/lighting.js +236 -123
- package/dist/tools/materials.d.ts +25 -7
- package/dist/tools/materials.js +102 -79
- package/dist/tools/niagara.d.ts +10 -12
- package/dist/tools/niagara.js +74 -94
- package/dist/tools/performance.d.ts +12 -4
- package/dist/tools/performance.js +38 -79
- package/dist/tools/physics.d.ts +34 -10
- package/dist/tools/physics.js +364 -292
- package/dist/tools/rc.js +98 -24
- package/dist/tools/sequence.d.ts +1 -0
- package/dist/tools/sequence.js +146 -24
- package/dist/tools/ui.d.ts +31 -4
- package/dist/tools/ui.js +83 -66
- package/dist/tools/visual.d.ts +11 -0
- package/dist/tools/visual.js +245 -30
- package/dist/types/tool-types.d.ts +0 -6
- package/dist/types/tool-types.js +1 -8
- package/dist/unreal-bridge.d.ts +32 -2
- package/dist/unreal-bridge.js +621 -127
- package/dist/utils/elicitation.d.ts +57 -0
- package/dist/utils/elicitation.js +104 -0
- package/dist/utils/error-handler.d.ts +0 -33
- package/dist/utils/error-handler.js +4 -111
- package/dist/utils/http.d.ts +2 -22
- package/dist/utils/http.js +12 -75
- package/dist/utils/normalize.d.ts +4 -4
- package/dist/utils/normalize.js +15 -7
- package/dist/utils/python-output.d.ts +18 -0
- package/dist/utils/python-output.js +290 -0
- package/dist/utils/python.d.ts +2 -0
- package/dist/utils/python.js +4 -0
- package/dist/utils/response-validator.d.ts +6 -1
- package/dist/utils/response-validator.js +66 -13
- package/dist/utils/result-helpers.d.ts +27 -0
- package/dist/utils/result-helpers.js +147 -0
- package/dist/utils/safe-json.d.ts +0 -2
- package/dist/utils/safe-json.js +0 -43
- package/dist/utils/validation.d.ts +16 -0
- package/dist/utils/validation.js +70 -7
- package/mcp-config-example.json +2 -2
- package/package.json +11 -10
- package/server.json +37 -14
- package/src/index.ts +146 -50
- package/src/prompts/index.ts +211 -13
- package/src/resources/actors.ts +59 -44
- package/src/resources/assets.ts +123 -102
- package/src/resources/levels.ts +37 -47
- package/src/tools/actors.ts +269 -313
- package/src/tools/animation.ts +556 -539
- package/src/tools/assets.ts +59 -45
- package/src/tools/audio.ts +507 -113
- package/src/tools/blueprint.ts +778 -462
- package/src/tools/build_environment_advanced.ts +312 -106
- package/src/tools/consolidated-tool-definitions.ts +136 -267
- package/src/tools/consolidated-tool-handlers.ts +871 -795
- package/src/tools/debug.ts +179 -38
- package/src/tools/editor.ts +35 -37
- package/src/tools/foliage.ts +110 -104
- package/src/tools/introspection.ts +24 -22
- package/src/tools/landscape.ts +334 -181
- package/src/tools/level.ts +683 -182
- package/src/tools/lighting.ts +244 -123
- package/src/tools/materials.ts +114 -83
- package/src/tools/niagara.ts +87 -81
- package/src/tools/performance.ts +49 -88
- package/src/tools/physics.ts +393 -299
- package/src/tools/rc.ts +103 -25
- package/src/tools/sequence.ts +157 -30
- package/src/tools/ui.ts +101 -70
- package/src/tools/visual.ts +250 -29
- package/src/types/tool-types.ts +0 -9
- package/src/unreal-bridge.ts +658 -140
- package/src/utils/elicitation.ts +129 -0
- package/src/utils/error-handler.ts +4 -159
- package/src/utils/http.ts +16 -115
- package/src/utils/normalize.ts +20 -10
- package/src/utils/python-output.ts +351 -0
- package/src/utils/python.ts +3 -0
- package/src/utils/response-validator.ts +68 -17
- package/src/utils/result-helpers.ts +193 -0
- package/src/utils/safe-json.ts +0 -50
- package/src/utils/validation.ts +94 -7
- package/tests/run-unreal-tool-tests.mjs +720 -0
- package/tsconfig.json +2 -2
- package/dist/python-utils.d.ts +0 -29
- package/dist/python-utils.js +0 -54
- package/dist/tools/tool-definitions.d.ts +0 -4919
- package/dist/tools/tool-definitions.js +0 -1065
- package/dist/tools/tool-handlers.d.ts +0 -47
- package/dist/tools/tool-handlers.js +0 -863
- package/dist/types/index.d.ts +0 -323
- package/dist/types/index.js +0 -28
- package/dist/utils/cache-manager.d.ts +0 -64
- package/dist/utils/cache-manager.js +0 -176
- package/dist/utils/errors.d.ts +0 -133
- package/dist/utils/errors.js +0 -256
- package/src/python/editor_compat.py +0 -181
- package/src/python-utils.ts +0 -57
- package/src/tools/tool-definitions.ts +0 -1081
- package/src/tools/tool-handlers.ts +0 -973
- package/src/types/index.ts +0 -414
- package/src/utils/cache-manager.ts +0 -213
- package/src/utils/errors.ts +0 -312
package/.env.production
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Unreal MCP – AI Agent Guide
|
|
2
|
+
|
|
3
|
+
## Architecture essentials
|
|
4
|
+
- `src/index.ts` boots the Model Context Protocol server, registers output schemas, and connects to Unreal only when a tool/resource call requires it via `ensureConnectedOnDemand`.
|
|
5
|
+
- Consolidated tool routing lives in `src/tools/consolidated-tool-definitions.ts` and `src/tools/consolidated-tool-handlers.ts`, shrinking the surface to 13 multi-action tools that wrap the specialized classes in `src/tools/*`.
|
|
6
|
+
- The Unreal bridge (`src/unreal-bridge.ts`) owns HTTP/WS transport, command throttling, health checks, and graceful fallbacks so callers never talk to Remote Control directly.
|
|
7
|
+
- Health/metrics (connection status, latency, recent errors) are tracked in `index.ts` and exposed through the `ue://health` resource for quick diagnostics.
|
|
8
|
+
|
|
9
|
+
## Key directories
|
|
10
|
+
- `src/tools/` – domain-specific helpers (actors, landscapes, audio, niagara, etc.) that emit Python scripts or console commands.
|
|
11
|
+
- `src/resources/` – read-only listings (assets, actors, levels) with caching and path normalization utilities.
|
|
12
|
+
- `src/utils/` – shared helpers: validation/coercion, `escapePythonString`, result interpretation, AJV-powered response validation, stdout redirection.
|
|
13
|
+
- `docs/unreal-tool-test-cases.md` + `tests/run-unreal-tool-tests.mjs` – declarative test matrix consumed by the automated harness; reports land in `tests/reports/` with time-stamped JSON.
|
|
14
|
+
|
|
15
|
+
## Tool workflow expectations
|
|
16
|
+
- New tool actions must be declared in the consolidated definitions (input/output schema) and wired in the handler switch before delegating to the relevant class in `src/tools/*`.
|
|
17
|
+
- Always return plain JS objects with `success`, `message`, `error`, and optional `warnings` arrays; let `responseValidator.wrapResponse` handle MCP formatting.
|
|
18
|
+
- Shared helpers like `interpretStandardResult` and `cleanObject` keep payloads JSON-safe—use them instead of ad-hoc parsing.
|
|
19
|
+
- When validating inputs, reuse `ensureVector3`, `ensureRotation`, and other utilities from `src/utils/validation.ts` to keep error messaging consistent.
|
|
20
|
+
|
|
21
|
+
## Unreal bridge & Python usage
|
|
22
|
+
- Prefer `bridge.executePythonWithResult` for multi-line scripts; it captures stdout, parses the last `RESULT:` block, and falls back to the console `py` command when plugins are missing.
|
|
23
|
+
- Python snippets should print a single `RESULT:` JSON payload and sanitize inputs with `escapePythonString` or typed coercion helpers.
|
|
24
|
+
- `UnrealBridge.httpCall` enforces timeouts, queues commands, and blocks dangerous console strings (`buildpaths`, `rebuildnavigation`, etc.); avoid bypassing it with raw HTTP.
|
|
25
|
+
- Auto-reconnect is disabled by default—tool handlers should call `ensureConnectedOnDemand()` instead of assuming a live session.
|
|
26
|
+
|
|
27
|
+
## Response & validation conventions
|
|
28
|
+
- Every consolidated tool has an AJV schema; mismatches surface as `_validation` warnings in responses and appear in stderr logs.
|
|
29
|
+
- For Python-driven tools, use the `interpretResult`/`interpretStandardResult` helpers to turn raw bridge output into the normalized `{ success, message, error, warnings }` shape.
|
|
30
|
+
- Keep warning text short and user-facing—the test harness searches response strings for keywords to grade scenarios.
|
|
31
|
+
|
|
32
|
+
## Build & test workflow
|
|
33
|
+
- `npm run build` compiles TypeScript to `dist/`; `npm run lint` enforces the ESLint rules shipped in `.eslintrc.json`.
|
|
34
|
+
- `npm run test:tools` launches the compiled server via stdio, iterates the Markdown-defined cases, and writes a JSON run summary under `tests/reports/`.
|
|
35
|
+
- Override harness behavior with env vars like `UNREAL_MCP_SERVER_CMD`, `UNREAL_MCP_SERVER_ARGS`, `UNREAL_MCP_TEST_DOC`, or `UNREAL_MCP_FBX_FILE` when Unreal lives elsewhere.
|
|
36
|
+
|
|
37
|
+
## Unreal environment & configuration
|
|
38
|
+
- Ensure the project enables: Remote Control API, Remote Control Web Interface, Python Editor Script Plugin, Editor Scripting Utilities, Sequencer, and Level Sequence Editor before invoking automation.
|
|
39
|
+
- Default connection settings come from `UE_HOST`, `UE_RC_HTTP_PORT`, and `UE_RC_WS_PORT` (see README for the `DefaultEngine.ini` snippet that unlocks remote execution).
|
|
40
|
+
- Tools defensively return `UE_NOT_CONNECTED` or asset-not-found errors; keep that contract when extending behavior so clients can retry intelligently.
|
|
41
|
+
|
|
42
|
+
## Debugging & monitoring
|
|
43
|
+
- Logs are routed to stderr via `routeStdoutLogsToStderr()` to keep MCP stdout JSON-only—check the terminal for detailed stack traces or validation warnings.
|
|
44
|
+
- Use the `ue://health` and `ue://version` resources to confirm bridge connectivity, last health-check timestamp, and detected engine version.
|
|
45
|
+
- The command queue in `UnrealBridge` spaces out console/Python traffic; heavy operations may need explicit `__callTimeoutMs` in the payload to extend HTTP timeouts.
|
|
@@ -49,7 +49,7 @@ jobs:
|
|
|
49
49
|
- name: Update server.json version
|
|
50
50
|
run: |
|
|
51
51
|
VERSION=${{ steps.version.outputs.version }}
|
|
52
|
-
jq --arg v "$VERSION" '.version = $v | .packages[] |= if .
|
|
52
|
+
jq --arg v "$VERSION" '.version = $v | .packages[] |= if .registryType == "npm" then .version = $v else . end' server.json > tmp && mv tmp server.json
|
|
53
53
|
echo "Updated server.json:"
|
|
54
54
|
cat server.json
|
|
55
55
|
|
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/unreal-engine-mcp-server)
|
|
5
5
|
[](https://github.com/modelcontextprotocol/sdk)
|
|
6
6
|
[](https://www.unrealengine.com/)
|
|
7
|
+
[](https://registry.modelcontextprotocol.io/)
|
|
7
8
|
|
|
8
9
|
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal Engine via Remote Control API. Built with TypeScript and designed for game development automation.
|
|
9
10
|
|
|
@@ -24,11 +25,26 @@ A comprehensive Model Context Protocol (MCP) server that enables AI assistants t
|
|
|
24
25
|
### Prerequisites
|
|
25
26
|
- Node.js 18+
|
|
26
27
|
- Unreal Engine 5.0-5.6
|
|
27
|
-
- Required UE Plugins:
|
|
28
|
-
- Remote Control
|
|
29
|
-
- Web
|
|
30
|
-
- Python Script Plugin
|
|
31
|
-
- Editor Scripting Utilities
|
|
28
|
+
- Required UE Plugins (enable via **Edit ▸ Plugins**):
|
|
29
|
+
- **Remote Control API** – core Remote Control HTTP/WS endpoints
|
|
30
|
+
- **Remote Control Web Interface** – enables WebSocket bridge used by this server
|
|
31
|
+
- **Python Editor Script Plugin** – exposes Python runtime for automation
|
|
32
|
+
- **Editor Scripting Utilities** – unlocks Editor Actor/Asset subsystems used throughout the tools
|
|
33
|
+
- **Sequencer** *(built-in)* – keep enabled for cinematic tools
|
|
34
|
+
- **Level Sequence Editor** – required for `manage_sequence` operations
|
|
35
|
+
|
|
36
|
+
> 💡 After toggling any plugin, restart the editor to finalize activation. Keep `Editor Scripting Utilities` and `Python Editor Script Plugin` enabled prior to connecting, otherwise many subsystem-based tools (actor spawning, audio, foliage, UI widgets) will refuse to run for safety.
|
|
37
|
+
|
|
38
|
+
### Plugin feature map
|
|
39
|
+
|
|
40
|
+
| Plugin | Location | Used By | Notes |
|
|
41
|
+
|--------|----------|---------|-------|
|
|
42
|
+
| Remote Control API | Developer Tools ▸ Remote Control | All tools | Provides HTTP/WS endpoints consumed by the MCP bridge |
|
|
43
|
+
| Remote Control Web Interface | Developer Tools ▸ Remote Control | All tools | Enables persistent WebSocket session |
|
|
44
|
+
| Python Editor Script Plugin | Scripting | Landscapes, lighting, audio, physics, sequences, UI | Required for every Python execution path |
|
|
45
|
+
| Editor Scripting Utilities | Scripting | Actors, foliage, assets, landscapes, UI | Supplies Editor Actor/Asset subsystems in UE5.6 |
|
|
46
|
+
| Sequencer | Built-in | Sequencer tools | Ensure not disabled in project settings |
|
|
47
|
+
| Level Sequence Editor | Animation | Sequencer tools | Activate before calling `manage_sequence` operations |
|
|
32
48
|
|
|
33
49
|
### Installation
|
|
34
50
|
|
|
@@ -114,7 +130,7 @@ Then enable Python execution in: Edit > Project Settings > Plugins > Remote Cont
|
|
|
114
130
|
}
|
|
115
131
|
```
|
|
116
132
|
|
|
117
|
-
## Available Tools (13
|
|
133
|
+
## Available Tools (13)
|
|
118
134
|
|
|
119
135
|
| Tool | Description |
|
|
120
136
|
|------|-------------|
|
|
@@ -161,7 +177,6 @@ Blueprints, Materials, Textures, Static/Skeletal Meshes, Levels, Sounds, Particl
|
|
|
161
177
|
UE_HOST=127.0.0.1 # Unreal Engine host
|
|
162
178
|
UE_RC_HTTP_PORT=30010 # Remote Control HTTP port
|
|
163
179
|
UE_RC_WS_PORT=30020 # Remote Control WebSocket port
|
|
164
|
-
USE_CONSOLIDATED_TOOLS=true # Use 13 consolidated tools (false = 37 individual)
|
|
165
180
|
LOG_LEVEL=info # debug | info | warn | error
|
|
166
181
|
```
|
|
167
182
|
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import { BlueprintTools } from './tools/blueprint.js';
|
|
|
17
17
|
import { LevelTools } from './tools/level.js';
|
|
18
18
|
import { LightingTools } from './tools/lighting.js';
|
|
19
19
|
import { LandscapeTools } from './tools/landscape.js';
|
|
20
|
+
import { BuildEnvironmentAdvanced } from './tools/build_environment_advanced.js';
|
|
20
21
|
import { FoliageTools } from './tools/foliage.js';
|
|
21
22
|
import { DebugVisualizationTools } from './tools/debug.js';
|
|
22
23
|
import { PerformanceTools } from './tools/performance.js';
|
|
@@ -27,8 +28,6 @@ import { SequenceTools } from './tools/sequence.js';
|
|
|
27
28
|
import { IntrospectionTools } from './tools/introspection.js';
|
|
28
29
|
import { VisualTools } from './tools/visual.js';
|
|
29
30
|
import { EngineTools } from './tools/engine.js';
|
|
30
|
-
import { toolDefinitions } from './tools/tool-definitions.js';
|
|
31
|
-
import { handleToolCall } from './tools/tool-handlers.js';
|
|
32
31
|
import { consolidatedToolDefinitions } from './tools/consolidated-tool-definitions.js';
|
|
33
32
|
import { handleConsolidatedToolCall } from './tools/consolidated-tool-handlers.js';
|
|
34
33
|
import { prompts } from './prompts/index.js';
|
|
@@ -37,6 +36,7 @@ import { responseValidator } from './utils/response-validator.js';
|
|
|
37
36
|
import { ErrorHandler } from './utils/error-handler.js';
|
|
38
37
|
import { routeStdoutLogsToStderr } from './utils/stdio-redirect.js';
|
|
39
38
|
import { cleanObject } from './utils/safe-json.js';
|
|
39
|
+
import { createElicitationHelper } from './utils/elicitation.js';
|
|
40
40
|
const log = new Logger('UE-MCP');
|
|
41
41
|
// Ensure stdout remains JSON-only for MCP by routing logs to stderr unless opted out.
|
|
42
42
|
routeStdoutLogsToStderr();
|
|
@@ -56,14 +56,13 @@ let healthCheckTimer;
|
|
|
56
56
|
let lastHealthSuccessAt = 0;
|
|
57
57
|
// Configuration
|
|
58
58
|
const CONFIG = {
|
|
59
|
-
//
|
|
60
|
-
USE_CONSOLIDATED_TOOLS: process.env.USE_CONSOLIDATED_TOOLS !== 'false',
|
|
59
|
+
// Tooling: use consolidated tools only (13 tools)
|
|
61
60
|
// Connection retry settings
|
|
62
61
|
MAX_RETRY_ATTEMPTS: 3,
|
|
63
62
|
RETRY_DELAY_MS: 2000,
|
|
64
63
|
// Server info
|
|
65
64
|
SERVER_NAME: 'unreal-engine-mcp',
|
|
66
|
-
SERVER_VERSION: '0.3
|
|
65
|
+
SERVER_VERSION: '0.4.3',
|
|
67
66
|
// Monitoring
|
|
68
67
|
HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
|
|
69
68
|
};
|
|
@@ -123,7 +122,7 @@ export async function createServer() {
|
|
|
123
122
|
bridge.setAutoReconnectEnabled(false);
|
|
124
123
|
// Initialize response validation with schemas
|
|
125
124
|
log.debug('Initializing response validation...');
|
|
126
|
-
const toolDefs =
|
|
125
|
+
const toolDefs = consolidatedToolDefinitions;
|
|
127
126
|
toolDefs.forEach((tool) => {
|
|
128
127
|
if (tool.outputSchema) {
|
|
129
128
|
responseValidator.registerSchema(tool.name, tool.outputSchema);
|
|
@@ -196,6 +195,7 @@ export async function createServer() {
|
|
|
196
195
|
const lightingTools = new LightingTools(bridge);
|
|
197
196
|
const landscapeTools = new LandscapeTools(bridge);
|
|
198
197
|
const foliageTools = new FoliageTools(bridge);
|
|
198
|
+
const buildEnvAdvanced = new BuildEnvironmentAdvanced(bridge);
|
|
199
199
|
const debugTools = new DebugVisualizationTools(bridge);
|
|
200
200
|
const performanceTools = new PerformanceTools(bridge);
|
|
201
201
|
const audioTools = new AudioTools(bridge);
|
|
@@ -212,10 +212,34 @@ export async function createServer() {
|
|
|
212
212
|
capabilities: {
|
|
213
213
|
resources: {},
|
|
214
214
|
tools: {},
|
|
215
|
-
prompts: {
|
|
216
|
-
|
|
215
|
+
prompts: {
|
|
216
|
+
listChanged: false
|
|
217
|
+
},
|
|
218
|
+
logging: {},
|
|
219
|
+
elicitation: {}
|
|
217
220
|
}
|
|
218
221
|
});
|
|
222
|
+
// Optional elicitation helper – used only if client supports it.
|
|
223
|
+
const elicitation = createElicitationHelper(server, log);
|
|
224
|
+
const defaultElicitationTimeoutMs = elicitation.getDefaultTimeoutMs();
|
|
225
|
+
const createNotConnectedResponse = (toolName) => {
|
|
226
|
+
const payload = {
|
|
227
|
+
success: false,
|
|
228
|
+
error: 'UE_NOT_CONNECTED',
|
|
229
|
+
message: 'Unreal Engine is not connected (after 3 attempts). Please open UE and try again.',
|
|
230
|
+
retriable: false,
|
|
231
|
+
scope: `tool-call/${toolName}`
|
|
232
|
+
};
|
|
233
|
+
return responseValidator.wrapResponse(toolName, {
|
|
234
|
+
...payload,
|
|
235
|
+
content: [
|
|
236
|
+
{
|
|
237
|
+
type: 'text',
|
|
238
|
+
text: JSON.stringify(payload, null, 2)
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
});
|
|
242
|
+
};
|
|
219
243
|
// Handle resource listing
|
|
220
244
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
221
245
|
return {
|
|
@@ -394,29 +418,23 @@ export async function createServer() {
|
|
|
394
418
|
}
|
|
395
419
|
throw new Error(`Unknown resource: ${uri}`);
|
|
396
420
|
});
|
|
397
|
-
// Handle tool listing -
|
|
421
|
+
// Handle tool listing - consolidated tools only
|
|
398
422
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
399
|
-
log.info(
|
|
423
|
+
log.info('Serving consolidated tools');
|
|
400
424
|
return {
|
|
401
|
-
tools:
|
|
425
|
+
tools: consolidatedToolDefinitions
|
|
402
426
|
};
|
|
403
427
|
});
|
|
404
|
-
// Handle tool calls -
|
|
428
|
+
// Handle tool calls - consolidated tools only (13)
|
|
405
429
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
406
|
-
const { name
|
|
430
|
+
const { name } = request.params;
|
|
431
|
+
let args = request.params.arguments || {};
|
|
407
432
|
const startTime = Date.now();
|
|
408
433
|
// Ensure connection only when needed, with 3 attempts
|
|
409
434
|
const connected = await ensureConnectedOnDemand();
|
|
410
435
|
if (!connected) {
|
|
411
|
-
const notConnected = {
|
|
412
|
-
content: [{ type: 'text', text: 'Unreal Engine is not connected (after 3 attempts). Please open UE and try again.' }],
|
|
413
|
-
success: false,
|
|
414
|
-
error: 'UE_NOT_CONNECTED',
|
|
415
|
-
retriable: false,
|
|
416
|
-
scope: `tool-call/${name}`
|
|
417
|
-
};
|
|
418
436
|
trackPerformance(startTime, false);
|
|
419
|
-
return
|
|
437
|
+
return createNotConnectedResponse(name);
|
|
420
438
|
}
|
|
421
439
|
// Create tools object for handler
|
|
422
440
|
const tools = {
|
|
@@ -432,6 +450,7 @@ export async function createServer() {
|
|
|
432
450
|
lightingTools,
|
|
433
451
|
landscapeTools,
|
|
434
452
|
foliageTools,
|
|
453
|
+
buildEnvAdvanced,
|
|
435
454
|
debugTools,
|
|
436
455
|
performanceTools,
|
|
437
456
|
audioTools,
|
|
@@ -441,18 +460,76 @@ export async function createServer() {
|
|
|
441
460
|
introspectionTools,
|
|
442
461
|
visualTools,
|
|
443
462
|
engineTools,
|
|
463
|
+
// Elicitation (client-optional)
|
|
464
|
+
elicit: elicitation.elicit,
|
|
465
|
+
supportsElicitation: elicitation.supports,
|
|
466
|
+
elicitationTimeoutMs: defaultElicitationTimeoutMs,
|
|
467
|
+
// Resources for listing and info
|
|
468
|
+
assetResources,
|
|
469
|
+
actorResources,
|
|
470
|
+
levelResources,
|
|
444
471
|
bridge
|
|
445
472
|
};
|
|
446
|
-
//
|
|
473
|
+
// Execute consolidated tool handler
|
|
447
474
|
try {
|
|
448
475
|
log.debug(`Executing tool: ${name}`);
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
476
|
+
// Opportunistic generic elicitation for missing primitive required fields
|
|
477
|
+
try {
|
|
478
|
+
const toolDef = consolidatedToolDefinitions.find(t => t.name === name);
|
|
479
|
+
const inputSchema = toolDef?.inputSchema;
|
|
480
|
+
const elicitFn = tools.elicit;
|
|
481
|
+
if (inputSchema && typeof elicitFn === 'function') {
|
|
482
|
+
const props = inputSchema.properties || {};
|
|
483
|
+
const required = Array.isArray(inputSchema.required) ? inputSchema.required : [];
|
|
484
|
+
const missing = required.filter((k) => {
|
|
485
|
+
const v = args[k];
|
|
486
|
+
if (v === undefined || v === null)
|
|
487
|
+
return true;
|
|
488
|
+
if (typeof v === 'string' && v.trim() === '')
|
|
489
|
+
return true;
|
|
490
|
+
return false;
|
|
491
|
+
});
|
|
492
|
+
// Build a flat primitive-only schema subset per MCP Elicitation rules
|
|
493
|
+
const primitiveProps = {};
|
|
494
|
+
for (const k of missing) {
|
|
495
|
+
const p = props[k];
|
|
496
|
+
if (!p || typeof p !== 'object')
|
|
497
|
+
continue;
|
|
498
|
+
const t = (p.type || '').toString();
|
|
499
|
+
const isEnum = Array.isArray(p.enum);
|
|
500
|
+
if (t === 'string' || t === 'number' || t === 'integer' || t === 'boolean' || isEnum) {
|
|
501
|
+
primitiveProps[k] = {
|
|
502
|
+
type: t || (isEnum ? 'string' : undefined),
|
|
503
|
+
title: p.title,
|
|
504
|
+
description: p.description,
|
|
505
|
+
enum: p.enum,
|
|
506
|
+
enumNames: p.enumNames,
|
|
507
|
+
minimum: p.minimum,
|
|
508
|
+
maximum: p.maximum,
|
|
509
|
+
minLength: p.minLength,
|
|
510
|
+
maxLength: p.maxLength,
|
|
511
|
+
pattern: p.pattern,
|
|
512
|
+
format: p.format,
|
|
513
|
+
default: p.default
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (Object.keys(primitiveProps).length > 0) {
|
|
518
|
+
const elicitOptions = { fallback: async () => ({ ok: false, error: 'missing-params' }) };
|
|
519
|
+
if (typeof tools.elicitationTimeoutMs === 'number' && Number.isFinite(tools.elicitationTimeoutMs)) {
|
|
520
|
+
elicitOptions.timeoutMs = tools.elicitationTimeoutMs;
|
|
521
|
+
}
|
|
522
|
+
const elicitRes = await elicitFn(`Provide missing parameters for ${name}`, { type: 'object', properties: primitiveProps, required: Object.keys(primitiveProps) }, elicitOptions);
|
|
523
|
+
if (elicitRes && elicitRes.ok && elicitRes.value) {
|
|
524
|
+
args = { ...args, ...elicitRes.value };
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
452
528
|
}
|
|
453
|
-
|
|
454
|
-
|
|
529
|
+
catch (e) {
|
|
530
|
+
log.debug('Generic elicitation prefill skipped', { err: e?.message || String(e) });
|
|
455
531
|
}
|
|
532
|
+
let result = await handleConsolidatedToolCall(name, args, tools);
|
|
456
533
|
log.debug(`Tool ${name} returned result`);
|
|
457
534
|
// Clean the result to remove circular references
|
|
458
535
|
result = cleanObject(result);
|
|
@@ -483,13 +560,23 @@ export async function createServer() {
|
|
|
483
560
|
metrics.recentErrors.splice(0, metrics.recentErrors.length - 20);
|
|
484
561
|
}
|
|
485
562
|
catch { }
|
|
486
|
-
|
|
563
|
+
const sanitizedError = cleanObject(errorResponse);
|
|
564
|
+
let errorText = '';
|
|
565
|
+
try {
|
|
566
|
+
errorText = JSON.stringify(sanitizedError, null, 2);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
errorText = sanitizedError.message || errorResponse.message || `Failed to execute ${name}`;
|
|
570
|
+
}
|
|
571
|
+
const wrappedError = {
|
|
572
|
+
...sanitizedError,
|
|
573
|
+
isError: true,
|
|
487
574
|
content: [{
|
|
488
575
|
type: 'text',
|
|
489
|
-
text:
|
|
490
|
-
}]
|
|
491
|
-
...errorResponse
|
|
576
|
+
text: errorText
|
|
577
|
+
}]
|
|
492
578
|
};
|
|
579
|
+
return responseValidator.wrapResponse(name, wrappedError);
|
|
493
580
|
}
|
|
494
581
|
});
|
|
495
582
|
// Handle prompt listing
|
|
@@ -498,11 +585,21 @@ export async function createServer() {
|
|
|
498
585
|
prompts: prompts.map(p => ({
|
|
499
586
|
name: p.name,
|
|
500
587
|
description: p.description,
|
|
501
|
-
arguments: Object.entries(p.arguments || {}).map(([name, schema]) =>
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
588
|
+
arguments: Object.entries(p.arguments || {}).map(([name, schema]) => {
|
|
589
|
+
const meta = {};
|
|
590
|
+
if (schema.type)
|
|
591
|
+
meta.type = schema.type;
|
|
592
|
+
if (schema.enum)
|
|
593
|
+
meta.enum = schema.enum;
|
|
594
|
+
if (schema.default !== undefined)
|
|
595
|
+
meta.default = schema.default;
|
|
596
|
+
return {
|
|
597
|
+
name,
|
|
598
|
+
description: schema.description,
|
|
599
|
+
required: schema.required ?? false,
|
|
600
|
+
...(Object.keys(meta).length ? { _meta: meta } : {})
|
|
601
|
+
};
|
|
602
|
+
})
|
|
506
603
|
}))
|
|
507
604
|
};
|
|
508
605
|
});
|
|
@@ -512,17 +609,11 @@ export async function createServer() {
|
|
|
512
609
|
if (!prompt) {
|
|
513
610
|
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
514
611
|
}
|
|
515
|
-
|
|
612
|
+
const args = (request.params.arguments || {});
|
|
613
|
+
const messages = prompt.build(args);
|
|
516
614
|
return {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
role: 'user',
|
|
520
|
-
content: {
|
|
521
|
-
type: 'text',
|
|
522
|
-
text: `Set up three-point lighting with ${request.params.arguments?.intensity || 'medium'} intensity`
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
]
|
|
615
|
+
description: prompt.description,
|
|
616
|
+
messages
|
|
526
617
|
};
|
|
527
618
|
});
|
|
528
619
|
return { server, bridge };
|
package/dist/prompts/index.d.ts
CHANGED
|
@@ -2,13 +2,20 @@ export interface PromptArgument {
|
|
|
2
2
|
type: string;
|
|
3
3
|
description?: string;
|
|
4
4
|
enum?: string[];
|
|
5
|
-
default?:
|
|
5
|
+
default?: unknown;
|
|
6
6
|
required?: boolean;
|
|
7
7
|
}
|
|
8
|
-
export interface
|
|
8
|
+
export interface PromptTemplate {
|
|
9
9
|
name: string;
|
|
10
10
|
description: string;
|
|
11
11
|
arguments?: Record<string, PromptArgument>;
|
|
12
|
+
build: (args: Record<string, unknown>) => Array<{
|
|
13
|
+
role: 'user' | 'assistant';
|
|
14
|
+
content: {
|
|
15
|
+
type: 'text';
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
12
19
|
}
|
|
13
|
-
export declare const prompts:
|
|
20
|
+
export declare const prompts: PromptTemplate[];
|
|
14
21
|
//# sourceMappingURL=index.d.ts.map
|