unreal-engine-mcp-server 0.4.5 → 0.4.6

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 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.5
22
+ SERVER_VERSION=0.4.6
23
23
 
24
24
  # Connection Settings
25
25
  MAX_RETRY_ATTEMPTS=3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.4.6] - 2025-10-04
6
+ ### Fixed
7
+ - Fixed duplicate response output issue where tool responses were being displayed twice in MCP content
8
+ - Response validator now emits concise summaries in text content instead of duplicating full JSON payloads
9
+ - Structured content is preserved for validation and tests while user-facing output is streamlined
10
+
5
11
  ## [0.4.5] - 2025-10-03
6
12
  ### Added
7
13
  - Expose `UE_PROJECT_PATH` environment variable across runtime config, Smithery manifest, and client example configs. This allows tools that need an absolute .uproject path (e.g., engine_start) to work without additional manual configuration.
package/README.md CHANGED
@@ -5,7 +5,7 @@
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
7
  [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-Published-green)](https://registry.modelcontextprotocol.io/)
8
- [![smithery badge](https://smithery.ai/badge/@ChiR24/unreal_mcp_server)](https://smithery.ai/server/@ChiR24/unreal_mcp_server)
8
+ [![smithery badge](https://smithery.ai/badge/@ChiR24/unreal_mcp)](https://smithery.ai/server/@ChiR24/unreal_mcp)
9
9
 
10
10
  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.
11
11
 
package/dist/index.js CHANGED
@@ -63,7 +63,7 @@ const CONFIG = {
63
63
  RETRY_DELAY_MS: 2000,
64
64
  // Server info
65
65
  SERVER_NAME: 'unreal-engine-mcp',
66
- SERVER_VERSION: '0.4.5',
66
+ SERVER_VERSION: '0.4.6',
67
67
  // Monitoring
68
68
  HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
69
69
  };
@@ -2,6 +2,66 @@ import Ajv from 'ajv';
2
2
  import { Logger } from './logger.js';
3
3
  import { cleanObject } from './safe-json.js';
4
4
  const log = new Logger('ResponseValidator');
5
+ function isRecord(value) {
6
+ return !!value && typeof value === 'object' && !Array.isArray(value);
7
+ }
8
+ function normalizeText(text) {
9
+ return text.replace(/\s+/g, ' ').trim();
10
+ }
11
+ function buildSummaryText(toolName, payload) {
12
+ if (typeof payload === 'string') {
13
+ const normalized = payload.trim();
14
+ return normalized || `${toolName} responded`;
15
+ }
16
+ if (typeof payload === 'number' || typeof payload === 'bigint' || typeof payload === 'boolean') {
17
+ return `${toolName} responded: ${payload}`;
18
+ }
19
+ if (!isRecord(payload)) {
20
+ return `${toolName} responded`;
21
+ }
22
+ const parts = [];
23
+ const message = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
24
+ const error = typeof payload.error === 'string' ? normalizeText(payload.error) : '';
25
+ const success = typeof payload.success === 'boolean' ? (payload.success ? 'success' : 'failed') : '';
26
+ const path = typeof payload.path === 'string' ? payload.path : '';
27
+ const name = typeof payload.name === 'string' ? payload.name : '';
28
+ const warningCount = Array.isArray(payload.warnings) ? payload.warnings.length : 0;
29
+ if (message)
30
+ parts.push(message);
31
+ if (error && (!message || !message.includes(error)))
32
+ parts.push(`error: ${error}`);
33
+ if (success)
34
+ parts.push(success);
35
+ if (path)
36
+ parts.push(`path: ${path}`);
37
+ if (name)
38
+ parts.push(`name: ${name}`);
39
+ if (warningCount > 0)
40
+ parts.push(`warnings: ${warningCount}`);
41
+ const summary = isRecord(payload.summary) ? payload.summary : undefined;
42
+ if (summary) {
43
+ const summaryParts = [];
44
+ for (const [key, value] of Object.entries(summary)) {
45
+ if (typeof value === 'number' || typeof value === 'string') {
46
+ summaryParts.push(`${key}: ${value}`);
47
+ }
48
+ if (summaryParts.length >= 3)
49
+ break;
50
+ }
51
+ if (summaryParts.length) {
52
+ parts.push(`summary(${summaryParts.join(', ')})`);
53
+ }
54
+ }
55
+ if (parts.length === 0) {
56
+ const keys = Object.keys(payload).slice(0, 3);
57
+ if (keys.length) {
58
+ return `${toolName} responded (${keys.join(', ')})`;
59
+ }
60
+ }
61
+ return parts.length > 0
62
+ ? parts.join(' | ')
63
+ : `${toolName} responded`;
64
+ }
5
65
  /**
6
66
  * Response Validator for MCP Tool Outputs
7
67
  * Validates tool responses against their defined output schemas
@@ -127,15 +187,10 @@ export class ResponseValidator {
127
187
  return response;
128
188
  }
129
189
  // Otherwise, wrap structured result into MCP content
130
- let text;
131
- try {
132
- // Pretty-print small objects for readability
133
- text = typeof response === 'string'
134
- ? response
135
- : JSON.stringify(response ?? { success: true }, null, 2);
136
- }
137
- catch (_e) {
138
- text = String(response);
190
+ const summarySource = structuredPayload !== undefined ? structuredPayload : response;
191
+ let text = buildSummaryText(toolName, summarySource);
192
+ if (!text || !text.trim()) {
193
+ text = buildSummaryText(toolName, response);
139
194
  }
140
195
  const wrapped = {
141
196
  content: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unreal-engine-mcp-server",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "mcpName": "io.github.ChiR24/unreal-engine-mcp",
5
5
  "description": "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.",
6
6
  "type": "module",
package/server.json CHANGED
@@ -2,13 +2,13 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-09-16/server.schema.json",
3
3
  "name": "io.github.ChiR24/unreal-engine-mcp",
4
4
  "description": "MCP server for Unreal Engine 5 with 13 tools for game development automation.",
5
- "version": "0.4.5",
5
+ "version": "0.4.6",
6
6
  "packages": [
7
7
  {
8
8
  "registryType": "npm",
9
9
  "registryBaseUrl": "https://registry.npmjs.org",
10
10
  "identifier": "unreal-engine-mcp-server",
11
- "version": "0.4.5",
11
+ "version": "0.4.6",
12
12
  "transport": {
13
13
  "type": "stdio"
14
14
  },
package/src/index.ts CHANGED
@@ -88,7 +88,7 @@ const CONFIG = {
88
88
  RETRY_DELAY_MS: 2000,
89
89
  // Server info
90
90
  SERVER_NAME: 'unreal-engine-mcp',
91
- SERVER_VERSION: '0.4.5',
91
+ SERVER_VERSION: '0.4.6',
92
92
  // Monitoring
93
93
  HEALTH_CHECK_INTERVAL_MS: 30000 // 30 seconds
94
94
  };
@@ -4,6 +4,69 @@ import { cleanObject } from './safe-json.js';
4
4
 
5
5
  const log = new Logger('ResponseValidator');
6
6
 
7
+ function isRecord(value: unknown): value is Record<string, unknown> {
8
+ return !!value && typeof value === 'object' && !Array.isArray(value);
9
+ }
10
+
11
+ function normalizeText(text: string): string {
12
+ return text.replace(/\s+/g, ' ').trim();
13
+ }
14
+
15
+ function buildSummaryText(toolName: string, payload: unknown): string {
16
+ if (typeof payload === 'string') {
17
+ const normalized = payload.trim();
18
+ return normalized || `${toolName} responded`;
19
+ }
20
+
21
+ if (typeof payload === 'number' || typeof payload === 'bigint' || typeof payload === 'boolean') {
22
+ return `${toolName} responded: ${payload}`;
23
+ }
24
+
25
+ if (!isRecord(payload)) {
26
+ return `${toolName} responded`;
27
+ }
28
+
29
+ const parts: string[] = [];
30
+ const message = typeof payload.message === 'string' ? normalizeText(payload.message) : '';
31
+ const error = typeof payload.error === 'string' ? normalizeText(payload.error) : '';
32
+ const success = typeof payload.success === 'boolean' ? (payload.success ? 'success' : 'failed') : '';
33
+ const path = typeof payload.path === 'string' ? payload.path : '';
34
+ const name = typeof payload.name === 'string' ? payload.name : '';
35
+ const warningCount = Array.isArray(payload.warnings) ? payload.warnings.length : 0;
36
+
37
+ if (message) parts.push(message);
38
+ if (error && (!message || !message.includes(error))) parts.push(`error: ${error}`);
39
+ if (success) parts.push(success);
40
+ if (path) parts.push(`path: ${path}`);
41
+ if (name) parts.push(`name: ${name}`);
42
+ if (warningCount > 0) parts.push(`warnings: ${warningCount}`);
43
+
44
+ const summary = isRecord(payload.summary) ? payload.summary : undefined;
45
+ if (summary) {
46
+ const summaryParts: string[] = [];
47
+ for (const [key, value] of Object.entries(summary)) {
48
+ if (typeof value === 'number' || typeof value === 'string') {
49
+ summaryParts.push(`${key}: ${value}`);
50
+ }
51
+ if (summaryParts.length >= 3) break;
52
+ }
53
+ if (summaryParts.length) {
54
+ parts.push(`summary(${summaryParts.join(', ')})`);
55
+ }
56
+ }
57
+
58
+ if (parts.length === 0) {
59
+ const keys = Object.keys(payload).slice(0, 3);
60
+ if (keys.length) {
61
+ return `${toolName} responded (${keys.join(', ')})`;
62
+ }
63
+ }
64
+
65
+ return parts.length > 0
66
+ ? parts.join(' | ')
67
+ : `${toolName} responded`;
68
+ }
69
+
7
70
  /**
8
71
  * Response Validator for MCP Tool Outputs
9
72
  * Validates tool responses against their defined output schemas
@@ -148,14 +211,10 @@ export class ResponseValidator {
148
211
  }
149
212
 
150
213
  // Otherwise, wrap structured result into MCP content
151
- let text: string;
152
- try {
153
- // Pretty-print small objects for readability
154
- text = typeof response === 'string'
155
- ? response
156
- : JSON.stringify(response ?? { success: true }, null, 2);
157
- } catch (_e) {
158
- text = String(response);
214
+ const summarySource = structuredPayload !== undefined ? structuredPayload : response;
215
+ let text = buildSummaryText(toolName, summarySource);
216
+ if (!text || !text.trim()) {
217
+ text = buildSummaryText(toolName, response);
159
218
  }
160
219
 
161
220
  const wrapped = {