unreal-engine-mcp-server 0.4.0 → 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.
Files changed (135) hide show
  1. package/.env.production +1 -1
  2. package/.github/copilot-instructions.md +45 -0
  3. package/.github/workflows/publish-mcp.yml +1 -1
  4. package/README.md +21 -5
  5. package/dist/index.js +124 -31
  6. package/dist/prompts/index.d.ts +10 -3
  7. package/dist/prompts/index.js +186 -7
  8. package/dist/resources/actors.d.ts +19 -1
  9. package/dist/resources/actors.js +55 -64
  10. package/dist/resources/assets.js +46 -62
  11. package/dist/resources/levels.d.ts +21 -3
  12. package/dist/resources/levels.js +29 -54
  13. package/dist/tools/actors.d.ts +3 -14
  14. package/dist/tools/actors.js +246 -302
  15. package/dist/tools/animation.d.ts +57 -102
  16. package/dist/tools/animation.js +429 -450
  17. package/dist/tools/assets.d.ts +13 -2
  18. package/dist/tools/assets.js +52 -44
  19. package/dist/tools/audio.d.ts +22 -13
  20. package/dist/tools/audio.js +467 -121
  21. package/dist/tools/blueprint.d.ts +32 -13
  22. package/dist/tools/blueprint.js +699 -448
  23. package/dist/tools/build_environment_advanced.d.ts +0 -1
  24. package/dist/tools/build_environment_advanced.js +190 -45
  25. package/dist/tools/consolidated-tool-definitions.js +78 -252
  26. package/dist/tools/consolidated-tool-handlers.js +506 -133
  27. package/dist/tools/debug.d.ts +72 -10
  28. package/dist/tools/debug.js +167 -31
  29. package/dist/tools/editor.d.ts +9 -2
  30. package/dist/tools/editor.js +30 -44
  31. package/dist/tools/foliage.d.ts +34 -15
  32. package/dist/tools/foliage.js +97 -107
  33. package/dist/tools/introspection.js +19 -21
  34. package/dist/tools/landscape.d.ts +1 -2
  35. package/dist/tools/landscape.js +311 -168
  36. package/dist/tools/level.d.ts +3 -28
  37. package/dist/tools/level.js +642 -192
  38. package/dist/tools/lighting.d.ts +14 -3
  39. package/dist/tools/lighting.js +236 -123
  40. package/dist/tools/materials.d.ts +25 -7
  41. package/dist/tools/materials.js +102 -79
  42. package/dist/tools/niagara.d.ts +10 -12
  43. package/dist/tools/niagara.js +74 -94
  44. package/dist/tools/performance.d.ts +12 -4
  45. package/dist/tools/performance.js +38 -79
  46. package/dist/tools/physics.d.ts +34 -10
  47. package/dist/tools/physics.js +364 -292
  48. package/dist/tools/rc.js +97 -23
  49. package/dist/tools/sequence.d.ts +1 -0
  50. package/dist/tools/sequence.js +125 -22
  51. package/dist/tools/ui.d.ts +31 -4
  52. package/dist/tools/ui.js +83 -66
  53. package/dist/tools/visual.d.ts +11 -0
  54. package/dist/tools/visual.js +245 -30
  55. package/dist/types/tool-types.d.ts +0 -6
  56. package/dist/types/tool-types.js +1 -8
  57. package/dist/unreal-bridge.d.ts +32 -2
  58. package/dist/unreal-bridge.js +621 -127
  59. package/dist/utils/elicitation.d.ts +57 -0
  60. package/dist/utils/elicitation.js +104 -0
  61. package/dist/utils/error-handler.d.ts +0 -33
  62. package/dist/utils/error-handler.js +4 -111
  63. package/dist/utils/http.d.ts +2 -22
  64. package/dist/utils/http.js +12 -75
  65. package/dist/utils/normalize.d.ts +4 -4
  66. package/dist/utils/normalize.js +15 -7
  67. package/dist/utils/python-output.d.ts +18 -0
  68. package/dist/utils/python-output.js +290 -0
  69. package/dist/utils/python.d.ts +2 -0
  70. package/dist/utils/python.js +4 -0
  71. package/dist/utils/response-validator.js +28 -2
  72. package/dist/utils/result-helpers.d.ts +27 -0
  73. package/dist/utils/result-helpers.js +147 -0
  74. package/dist/utils/safe-json.d.ts +0 -2
  75. package/dist/utils/safe-json.js +0 -43
  76. package/dist/utils/validation.d.ts +16 -0
  77. package/dist/utils/validation.js +70 -7
  78. package/mcp-config-example.json +2 -2
  79. package/package.json +10 -9
  80. package/server.json +37 -14
  81. package/src/index.ts +130 -33
  82. package/src/prompts/index.ts +211 -13
  83. package/src/resources/actors.ts +59 -44
  84. package/src/resources/assets.ts +48 -51
  85. package/src/resources/levels.ts +35 -45
  86. package/src/tools/actors.ts +269 -313
  87. package/src/tools/animation.ts +556 -539
  88. package/src/tools/assets.ts +53 -43
  89. package/src/tools/audio.ts +507 -113
  90. package/src/tools/blueprint.ts +778 -462
  91. package/src/tools/build_environment_advanced.ts +266 -64
  92. package/src/tools/consolidated-tool-definitions.ts +90 -264
  93. package/src/tools/consolidated-tool-handlers.ts +630 -121
  94. package/src/tools/debug.ts +176 -33
  95. package/src/tools/editor.ts +35 -37
  96. package/src/tools/foliage.ts +110 -104
  97. package/src/tools/introspection.ts +24 -22
  98. package/src/tools/landscape.ts +334 -181
  99. package/src/tools/level.ts +683 -182
  100. package/src/tools/lighting.ts +244 -123
  101. package/src/tools/materials.ts +114 -83
  102. package/src/tools/niagara.ts +87 -81
  103. package/src/tools/performance.ts +49 -88
  104. package/src/tools/physics.ts +393 -299
  105. package/src/tools/rc.ts +102 -24
  106. package/src/tools/sequence.ts +136 -28
  107. package/src/tools/ui.ts +101 -70
  108. package/src/tools/visual.ts +250 -29
  109. package/src/types/tool-types.ts +0 -9
  110. package/src/unreal-bridge.ts +658 -140
  111. package/src/utils/elicitation.ts +129 -0
  112. package/src/utils/error-handler.ts +4 -159
  113. package/src/utils/http.ts +16 -115
  114. package/src/utils/normalize.ts +20 -10
  115. package/src/utils/python-output.ts +351 -0
  116. package/src/utils/python.ts +3 -0
  117. package/src/utils/response-validator.ts +25 -2
  118. package/src/utils/result-helpers.ts +193 -0
  119. package/src/utils/safe-json.ts +0 -50
  120. package/src/utils/validation.ts +94 -7
  121. package/tests/run-unreal-tool-tests.mjs +720 -0
  122. package/tsconfig.json +2 -2
  123. package/dist/python-utils.d.ts +0 -29
  124. package/dist/python-utils.js +0 -54
  125. package/dist/types/index.d.ts +0 -323
  126. package/dist/types/index.js +0 -28
  127. package/dist/utils/cache-manager.d.ts +0 -64
  128. package/dist/utils/cache-manager.js +0 -176
  129. package/dist/utils/errors.d.ts +0 -133
  130. package/dist/utils/errors.js +0 -256
  131. package/src/python/editor_compat.py +0 -181
  132. package/src/python-utils.ts +0 -57
  133. package/src/types/index.ts +0 -414
  134. package/src/utils/cache-manager.ts +0 -213
  135. package/src/utils/errors.ts +0 -312
package/.env.production CHANGED
@@ -19,7 +19,7 @@ LOG_LEVEL=info
19
19
 
20
20
  # Server Settings
21
21
  SERVER_NAME=unreal-engine-mcp
22
- SERVER_VERSION=0.4.0
22
+ SERVER_VERSION=0.4.3
23
23
 
24
24
  # Connection Settings
25
25
  MAX_RETRY_ATTEMPTS=3
@@ -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 .registry_type == "npm" then .version = $v else . end' server.json > tmp && mv tmp server.json
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
  [![NPM Package](https://img.shields.io/npm/v/unreal-engine-mcp-server)](https://www.npmjs.com/package/unreal-engine-mcp-server)
5
5
  [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-TypeScript-blue)](https://github.com/modelcontextprotocol/sdk)
6
6
  [![Unreal Engine](https://img.shields.io/badge/Unreal%20Engine-5.0--5.6-orange)](https://www.unrealengine.com/)
7
+ [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-Published-green)](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 Remote Control
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
 
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ import { responseValidator } from './utils/response-validator.js';
36
36
  import { ErrorHandler } from './utils/error-handler.js';
37
37
  import { routeStdoutLogsToStderr } from './utils/stdio-redirect.js';
38
38
  import { cleanObject } from './utils/safe-json.js';
39
+ import { createElicitationHelper } from './utils/elicitation.js';
39
40
  const log = new Logger('UE-MCP');
40
41
  // Ensure stdout remains JSON-only for MCP by routing logs to stderr unless opted out.
41
42
  routeStdoutLogsToStderr();
@@ -61,7 +62,7 @@ const CONFIG = {
61
62
  RETRY_DELAY_MS: 2000,
62
63
  // Server info
63
64
  SERVER_NAME: 'unreal-engine-mcp',
64
- SERVER_VERSION: '0.4.0',
65
+ SERVER_VERSION: '0.4.3',
65
66
  // Monitoring
66
67
  HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
67
68
  };
@@ -211,10 +212,34 @@ export async function createServer() {
211
212
  capabilities: {
212
213
  resources: {},
213
214
  tools: {},
214
- prompts: {},
215
- logging: {}
215
+ prompts: {
216
+ listChanged: false
217
+ },
218
+ logging: {},
219
+ elicitation: {}
216
220
  }
217
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
+ };
218
243
  // Handle resource listing
219
244
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
220
245
  return {
@@ -402,20 +427,14 @@ export async function createServer() {
402
427
  });
403
428
  // Handle tool calls - consolidated tools only (13)
404
429
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
405
- const { name, arguments: args } = request.params;
430
+ const { name } = request.params;
431
+ let args = request.params.arguments || {};
406
432
  const startTime = Date.now();
407
433
  // Ensure connection only when needed, with 3 attempts
408
434
  const connected = await ensureConnectedOnDemand();
409
435
  if (!connected) {
410
- const notConnected = {
411
- content: [{ type: 'text', text: 'Unreal Engine is not connected (after 3 attempts). Please open UE and try again.' }],
412
- success: false,
413
- error: 'UE_NOT_CONNECTED',
414
- retriable: false,
415
- scope: `tool-call/${name}`
416
- };
417
436
  trackPerformance(startTime, false);
418
- return notConnected;
437
+ return createNotConnectedResponse(name);
419
438
  }
420
439
  // Create tools object for handler
421
440
  const tools = {
@@ -441,6 +460,10 @@ 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,
444
467
  // Resources for listing and info
445
468
  assetResources,
446
469
  actorResources,
@@ -450,6 +473,62 @@ export async function createServer() {
450
473
  // Execute consolidated tool handler
451
474
  try {
452
475
  log.debug(`Executing tool: ${name}`);
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
+ }
528
+ }
529
+ catch (e) {
530
+ log.debug('Generic elicitation prefill skipped', { err: e?.message || String(e) });
531
+ }
453
532
  let result = await handleConsolidatedToolCall(name, args, tools);
454
533
  log.debug(`Tool ${name} returned result`);
455
534
  // Clean the result to remove circular references
@@ -481,13 +560,23 @@ export async function createServer() {
481
560
  metrics.recentErrors.splice(0, metrics.recentErrors.length - 20);
482
561
  }
483
562
  catch { }
484
- return {
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,
485
574
  content: [{
486
575
  type: 'text',
487
- text: errorResponse.message || `Failed to execute ${name}`
488
- }],
489
- ...errorResponse
576
+ text: errorText
577
+ }]
490
578
  };
579
+ return responseValidator.wrapResponse(name, wrappedError);
491
580
  }
492
581
  });
493
582
  // Handle prompt listing
@@ -496,11 +585,21 @@ export async function createServer() {
496
585
  prompts: prompts.map(p => ({
497
586
  name: p.name,
498
587
  description: p.description,
499
- arguments: Object.entries(p.arguments || {}).map(([name, schema]) => ({
500
- name,
501
- description: schema.description,
502
- required: schema.required || false
503
- }))
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
+ })
504
603
  }))
505
604
  };
506
605
  });
@@ -510,17 +609,11 @@ export async function createServer() {
510
609
  if (!prompt) {
511
610
  throw new Error(`Unknown prompt: ${request.params.name}`);
512
611
  }
513
- // Return a template for the lighting setup
612
+ const args = (request.params.arguments || {});
613
+ const messages = prompt.build(args);
514
614
  return {
515
- messages: [
516
- {
517
- role: 'user',
518
- content: {
519
- type: 'text',
520
- text: `Set up three-point lighting with ${request.params.arguments?.intensity || 'medium'} intensity`
521
- }
522
- }
523
- ]
615
+ description: prompt.description,
616
+ messages
524
617
  };
525
618
  });
526
619
  return { server, bridge };
@@ -2,13 +2,20 @@ export interface PromptArgument {
2
2
  type: string;
3
3
  description?: string;
4
4
  enum?: string[];
5
- default?: any;
5
+ default?: unknown;
6
6
  required?: boolean;
7
7
  }
8
- export interface Prompt {
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: Prompt[];
20
+ export declare const prompts: PromptTemplate[];
14
21
  //# sourceMappingURL=index.d.ts.map
@@ -1,37 +1,216 @@
1
+ function clampChoice(value, choices, fallback) {
2
+ if (typeof value === 'string') {
3
+ const normalized = value.toLowerCase();
4
+ if (choices.includes(normalized)) {
5
+ return normalized;
6
+ }
7
+ }
8
+ return fallback;
9
+ }
10
+ function coerceNumber(value, fallback, min, max) {
11
+ const num = typeof value === 'number' ? value : Number(value);
12
+ if (!Number.isFinite(num)) {
13
+ return fallback;
14
+ }
15
+ if (min !== undefined && num < min) {
16
+ return min;
17
+ }
18
+ if (max !== undefined && num > max) {
19
+ return max;
20
+ }
21
+ return num;
22
+ }
23
+ function formatVector(value) {
24
+ if (!value || typeof value !== 'object') {
25
+ return null;
26
+ }
27
+ const vector = value;
28
+ const x = typeof vector.x === 'number' ? vector.x : Number(vector.x);
29
+ const y = typeof vector.y === 'number' ? vector.y : Number(vector.y);
30
+ const z = typeof vector.z === 'number' ? vector.z : Number(vector.z);
31
+ if ([x, y, z].some((component) => !Number.isFinite(component))) {
32
+ return null;
33
+ }
34
+ return `${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}`;
35
+ }
1
36
  export const prompts = [
2
37
  {
3
38
  name: 'setup_three_point_lighting',
4
- description: 'Set up a basic three-point lighting rig around the current camera focus',
39
+ description: 'Author a cinematic three-point lighting rig aligned to the active camera focus.',
5
40
  arguments: {
6
41
  intensity: {
7
42
  type: 'string',
8
43
  enum: ['low', 'medium', 'high'],
9
44
  default: 'medium',
10
- description: 'Light intensity level'
45
+ description: 'Overall lighting mood. Low = dramatic contrast, high = bright key light.'
11
46
  }
47
+ },
48
+ build: (args) => {
49
+ const intensity = clampChoice(args.intensity, ['low', 'medium', 'high'], 'medium');
50
+ const moodHints = {
51
+ low: 'gentle key with strong contrast and subtle rim highlights',
52
+ medium: 'balanced key/fill ratio for natural coverage',
53
+ high: 'bright key with energetic fill and crisp rim separation'
54
+ };
55
+ const text = `Configure a three-point lighting rig around the current cinematic focus.
56
+
57
+ Tasks:
58
+ - Position a key light roughly 45° off-axis at eye level. Target the subject center and tune intensity for ${intensity} output (${moodHints[intensity]}).
59
+ - Add a fill light on the opposite side with wider spread and softened shadows to control contrast.
60
+ - Place a rim/back light to outline silhouettes and separate the subject from the background.
61
+ - Ensure all lights use physically plausible color temperature, enable shadow casting where helpful, and adjust attenuation to avoid spill.
62
+ - Once balanced, report the final intensity values, color temperatures, and any blockers encountered.`;
63
+ return [{
64
+ role: 'user',
65
+ content: { type: 'text', text }
66
+ }];
12
67
  }
13
68
  },
14
69
  {
15
70
  name: 'create_fps_controller',
16
- description: 'Create a first-person shooter character controller',
71
+ description: 'Spin up a first-person controller blueprint with input mappings, collision, and starter movement.',
17
72
  arguments: {
18
73
  spawnLocation: {
19
- type: 'object',
20
- description: 'Location to spawn the controller',
74
+ type: 'vector',
75
+ description: 'Optional XYZ spawn position for the player pawn.',
21
76
  required: false
22
77
  }
78
+ },
79
+ build: (args) => {
80
+ const spawnVector = formatVector(args.spawnLocation);
81
+ const spawnLine = spawnVector ? `Spawn the pawn at world coordinates (${spawnVector}).` : 'Spawn the pawn at a safe default player start or the origin.';
82
+ const text = `Build a First Person Character blueprint with:
83
+ - Camera + arms mesh, basic WASD input, jump, crouch, and sprint bindings using Enhanced Input.
84
+ - Proper collision capsule sizing for a 180cm tall human.
85
+ - Momentum-preserving air control with configurable acceleration and friction.
86
+ - A configurable base turn rate with mouse sensitivity scaling.
87
+ - Serialized defaults for walking speed (600 uu/s) and sprint speed (900 uu/s).
88
+ - Expose key movement settings as editable defaults.
89
+ - ${spawnLine}
90
+
91
+ Finish by compiling, saving, and summarizing the created blueprint path plus the mapped input actions.`;
92
+ return [{
93
+ role: 'user',
94
+ content: { type: 'text', text }
95
+ }];
23
96
  }
24
97
  },
25
98
  {
26
99
  name: 'setup_post_processing',
27
- description: 'Configure post-processing volume with cinematic settings',
100
+ description: 'Author a post-process volume tuned to a named cinematic grade.',
28
101
  arguments: {
29
102
  style: {
30
103
  type: 'string',
31
104
  enum: ['cinematic', 'realistic', 'stylized', 'noir'],
32
105
  default: 'cinematic',
33
- description: 'Visual style preset'
106
+ description: 'Look preset to emphasize color grading and tone-mapping style.'
107
+ }
108
+ },
109
+ build: (args) => {
110
+ const style = clampChoice(args.style, ['cinematic', 'realistic', 'stylized', 'noir'], 'cinematic');
111
+ const styleNotes = {
112
+ cinematic: 'filmic tonemapper, gentle bloom, warm highlights, cool shadows, slight vignette',
113
+ realistic: 'minimal grading, accurate white balance, restrained bloom, detail-preserving sharpening',
114
+ stylized: 'bold saturation shifts, custom color LUT, exaggerated contrast, selective bloom',
115
+ noir: 'monochrome conversion, strong contrast curve, subtle film grain, heavy vignette'
116
+ };
117
+ const text = `Create a global post-process volume with priority over level defaults.
118
+ - Apply the "${style}" look: ${styleNotes[style]}.
119
+ - Configure tone mapping, exposure, bloom, chromatic aberration, and LUTs as required.
120
+ - Ensure the volume is unbound unless level-specific constraints apply.
121
+ - Provide sanity checks for HDR output and keep auto-exposure transitions smooth.
122
+ - Summarize all modified settings with their final numeric values or asset references.`;
123
+ return [{
124
+ role: 'user',
125
+ content: { type: 'text', text }
126
+ }];
127
+ }
128
+ },
129
+ {
130
+ name: 'setup_dynamic_day_night_cycle',
131
+ description: 'Create or update a Blueprint to drive a dynamic day/night cycle with optional weather hooks.',
132
+ arguments: {
133
+ startTime: {
134
+ type: 'string',
135
+ enum: ['dawn', 'noon', 'dusk', 'midnight'],
136
+ default: 'dawn',
137
+ description: 'Initial lighting state for the cycle.'
138
+ },
139
+ transitionMinutes: {
140
+ type: 'number',
141
+ default: 5,
142
+ description: 'Game-time minutes to blend between major lighting states.'
143
+ },
144
+ enableWeather: {
145
+ type: 'boolean',
146
+ default: false,
147
+ description: 'Whether to expose hooks for weather-driven sky adjustments.'
148
+ }
149
+ },
150
+ build: (args) => {
151
+ const startTime = clampChoice(args.startTime, ['dawn', 'noon', 'dusk', 'midnight'], 'dawn');
152
+ const transitionMinutes = coerceNumber(args.transitionMinutes, 5, 1, 60);
153
+ const enableWeather = Boolean(args.enableWeather);
154
+ const weatherLine = enableWeather
155
+ ? '- Expose interfaces for cloud opacity, precipitation-driven skylight updates, and lightning flashes.'
156
+ : '- Weather hooks are disabled; keep the blueprint lean';
157
+ const text = `Implement a Blueprint-based day/night cycle manager.
158
+ - Start the sequence at ${startTime} lighting.
159
+ - Advance sun rotation, skylight captures, fog, and sky atmosphere continuously with ${transitionMinutes} minute blends between key states.
160
+ - Sync directional light intensity/color with real-world sun elevation and inject moonlight at night.
161
+ - ${weatherLine}.
162
+ - Provide editor controls for time-of-day multiplier and manual overrides.
163
+ - Document the generated blueprint path and exposed parameters.`;
164
+ return [{
165
+ role: 'user',
166
+ content: { type: 'text', text }
167
+ }];
168
+ }
169
+ },
170
+ {
171
+ name: 'design_cinematic_camera_move',
172
+ description: 'Author a sequencer shot with a polished camera move and easing markers.',
173
+ arguments: {
174
+ durationSeconds: {
175
+ type: 'number',
176
+ default: 6,
177
+ description: 'Shot duration in seconds.'
178
+ },
179
+ moveStyle: {
180
+ type: 'string',
181
+ enum: ['push_in', 'orbit', 'tracking', 'crane'],
182
+ default: 'push_in',
183
+ description: 'Camera move archetype to emphasize.'
184
+ },
185
+ focusTarget: {
186
+ type: 'string',
187
+ description: 'Optional actor or component name to keep in focus.',
188
+ required: false
34
189
  }
190
+ },
191
+ build: (args) => {
192
+ const duration = coerceNumber(args.durationSeconds, 6, 2, 30);
193
+ const moveStyle = clampChoice(args.moveStyle, ['push_in', 'orbit', 'tracking', 'crane'], 'push_in');
194
+ const focusLine = typeof args.focusTarget === 'string' && args.focusTarget.trim().length > 0
195
+ ? `Lock focus distance on "${args.focusTarget}" and animate depth of field pulls if necessary.`
196
+ : 'Pick the most prominent subject in frame and maintain crisp focus throughout the move.';
197
+ const moveHints = {
198
+ push_in: 'Ease-in push toward the subject with gentle camera roll stabilization.',
199
+ orbit: '360° orbit with consistent parallax and a tracked look-at target.',
200
+ tracking: 'Match the subject velocity along a spline with smoothed acceleration.',
201
+ crane: 'Combine vertical rise with lateral drift for a reveal shot.'
202
+ };
203
+ const text = `In Sequencer, author a ${duration.toFixed(1)} second cinematic shot.
204
+ - Movement style: ${moveStyle} (${moveHints[moveStyle]}).
205
+ - Key auto-exposure, camera focal length, and focal distance for a premium look.
206
+ - Add ease-in/ease-out tangents at shot boundaries to avoid abrupt starts/stops.
207
+ - ${focusLine}
208
+ - Annotate the timeline with intent markers (intro beat, climax, resolve).
209
+ - Render a preview range and summarize the created assets.`;
210
+ return [{
211
+ role: 'user',
212
+ content: { type: 'text', text }
213
+ }];
35
214
  }
36
215
  }
37
216
  ];
@@ -7,7 +7,25 @@ export declare class ActorResources {
7
7
  private getFromCache;
8
8
  private setCache;
9
9
  listActors(): Promise<any>;
10
- getActorByName(actorName: string): Promise<any>;
10
+ getActorByName(actorName: string): Promise<{
11
+ success: true;
12
+ name: string;
13
+ path: string | undefined;
14
+ class: string | undefined;
15
+ error?: undefined;
16
+ } | {
17
+ success: false;
18
+ error: string;
19
+ name?: undefined;
20
+ path?: undefined;
21
+ class?: undefined;
22
+ } | {
23
+ error: string;
24
+ success?: undefined;
25
+ name?: undefined;
26
+ path?: undefined;
27
+ class?: undefined;
28
+ }>;
11
29
  getActorTransform(actorPath: string): Promise<any>;
12
30
  }
13
31
  //# sourceMappingURL=actors.d.ts.map