unreal-engine-mcp-server 0.2.1

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 (155) hide show
  1. package/.dockerignore +57 -0
  2. package/.env.production +25 -0
  3. package/.eslintrc.json +54 -0
  4. package/.github/workflows/publish-mcp.yml +75 -0
  5. package/Dockerfile +54 -0
  6. package/LICENSE +21 -0
  7. package/Public/icon.png +0 -0
  8. package/README.md +209 -0
  9. package/claude_desktop_config_example.json +13 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.js +7 -0
  12. package/dist/index.d.ts +31 -0
  13. package/dist/index.js +484 -0
  14. package/dist/prompts/index.d.ts +14 -0
  15. package/dist/prompts/index.js +38 -0
  16. package/dist/python-utils.d.ts +29 -0
  17. package/dist/python-utils.js +54 -0
  18. package/dist/resources/actors.d.ts +13 -0
  19. package/dist/resources/actors.js +83 -0
  20. package/dist/resources/assets.d.ts +23 -0
  21. package/dist/resources/assets.js +245 -0
  22. package/dist/resources/levels.d.ts +17 -0
  23. package/dist/resources/levels.js +94 -0
  24. package/dist/tools/actors.d.ts +51 -0
  25. package/dist/tools/actors.js +459 -0
  26. package/dist/tools/animation.d.ts +196 -0
  27. package/dist/tools/animation.js +579 -0
  28. package/dist/tools/assets.d.ts +21 -0
  29. package/dist/tools/assets.js +304 -0
  30. package/dist/tools/audio.d.ts +170 -0
  31. package/dist/tools/audio.js +416 -0
  32. package/dist/tools/blueprint.d.ts +144 -0
  33. package/dist/tools/blueprint.js +652 -0
  34. package/dist/tools/build_environment_advanced.d.ts +66 -0
  35. package/dist/tools/build_environment_advanced.js +484 -0
  36. package/dist/tools/consolidated-tool-definitions.d.ts +2598 -0
  37. package/dist/tools/consolidated-tool-definitions.js +607 -0
  38. package/dist/tools/consolidated-tool-handlers.d.ts +2 -0
  39. package/dist/tools/consolidated-tool-handlers.js +1050 -0
  40. package/dist/tools/debug.d.ts +185 -0
  41. package/dist/tools/debug.js +265 -0
  42. package/dist/tools/editor.d.ts +88 -0
  43. package/dist/tools/editor.js +365 -0
  44. package/dist/tools/engine.d.ts +30 -0
  45. package/dist/tools/engine.js +36 -0
  46. package/dist/tools/foliage.d.ts +155 -0
  47. package/dist/tools/foliage.js +525 -0
  48. package/dist/tools/introspection.d.ts +98 -0
  49. package/dist/tools/introspection.js +683 -0
  50. package/dist/tools/landscape.d.ts +158 -0
  51. package/dist/tools/landscape.js +375 -0
  52. package/dist/tools/level.d.ts +110 -0
  53. package/dist/tools/level.js +362 -0
  54. package/dist/tools/lighting.d.ts +159 -0
  55. package/dist/tools/lighting.js +1179 -0
  56. package/dist/tools/materials.d.ts +34 -0
  57. package/dist/tools/materials.js +146 -0
  58. package/dist/tools/niagara.d.ts +145 -0
  59. package/dist/tools/niagara.js +289 -0
  60. package/dist/tools/performance.d.ts +163 -0
  61. package/dist/tools/performance.js +412 -0
  62. package/dist/tools/physics.d.ts +189 -0
  63. package/dist/tools/physics.js +784 -0
  64. package/dist/tools/rc.d.ts +110 -0
  65. package/dist/tools/rc.js +363 -0
  66. package/dist/tools/sequence.d.ts +112 -0
  67. package/dist/tools/sequence.js +675 -0
  68. package/dist/tools/tool-definitions.d.ts +4919 -0
  69. package/dist/tools/tool-definitions.js +891 -0
  70. package/dist/tools/tool-handlers.d.ts +47 -0
  71. package/dist/tools/tool-handlers.js +830 -0
  72. package/dist/tools/ui.d.ts +171 -0
  73. package/dist/tools/ui.js +337 -0
  74. package/dist/tools/visual.d.ts +29 -0
  75. package/dist/tools/visual.js +67 -0
  76. package/dist/types/env.d.ts +10 -0
  77. package/dist/types/env.js +18 -0
  78. package/dist/types/index.d.ts +323 -0
  79. package/dist/types/index.js +28 -0
  80. package/dist/types/tool-types.d.ts +274 -0
  81. package/dist/types/tool-types.js +13 -0
  82. package/dist/unreal-bridge.d.ts +126 -0
  83. package/dist/unreal-bridge.js +992 -0
  84. package/dist/utils/cache-manager.d.ts +64 -0
  85. package/dist/utils/cache-manager.js +176 -0
  86. package/dist/utils/error-handler.d.ts +66 -0
  87. package/dist/utils/error-handler.js +243 -0
  88. package/dist/utils/errors.d.ts +133 -0
  89. package/dist/utils/errors.js +256 -0
  90. package/dist/utils/http.d.ts +26 -0
  91. package/dist/utils/http.js +135 -0
  92. package/dist/utils/logger.d.ts +12 -0
  93. package/dist/utils/logger.js +32 -0
  94. package/dist/utils/normalize.d.ts +17 -0
  95. package/dist/utils/normalize.js +49 -0
  96. package/dist/utils/response-validator.d.ts +34 -0
  97. package/dist/utils/response-validator.js +121 -0
  98. package/dist/utils/safe-json.d.ts +4 -0
  99. package/dist/utils/safe-json.js +97 -0
  100. package/dist/utils/stdio-redirect.d.ts +2 -0
  101. package/dist/utils/stdio-redirect.js +20 -0
  102. package/dist/utils/validation.d.ts +50 -0
  103. package/dist/utils/validation.js +173 -0
  104. package/mcp-config-example.json +14 -0
  105. package/package.json +63 -0
  106. package/server.json +60 -0
  107. package/src/cli.ts +7 -0
  108. package/src/index.ts +543 -0
  109. package/src/prompts/index.ts +51 -0
  110. package/src/python/editor_compat.py +181 -0
  111. package/src/python-utils.ts +57 -0
  112. package/src/resources/actors.ts +92 -0
  113. package/src/resources/assets.ts +251 -0
  114. package/src/resources/levels.ts +83 -0
  115. package/src/tools/actors.ts +480 -0
  116. package/src/tools/animation.ts +713 -0
  117. package/src/tools/assets.ts +305 -0
  118. package/src/tools/audio.ts +548 -0
  119. package/src/tools/blueprint.ts +736 -0
  120. package/src/tools/build_environment_advanced.ts +526 -0
  121. package/src/tools/consolidated-tool-definitions.ts +619 -0
  122. package/src/tools/consolidated-tool-handlers.ts +1093 -0
  123. package/src/tools/debug.ts +368 -0
  124. package/src/tools/editor.ts +360 -0
  125. package/src/tools/engine.ts +32 -0
  126. package/src/tools/foliage.ts +652 -0
  127. package/src/tools/introspection.ts +778 -0
  128. package/src/tools/landscape.ts +523 -0
  129. package/src/tools/level.ts +410 -0
  130. package/src/tools/lighting.ts +1316 -0
  131. package/src/tools/materials.ts +148 -0
  132. package/src/tools/niagara.ts +312 -0
  133. package/src/tools/performance.ts +549 -0
  134. package/src/tools/physics.ts +924 -0
  135. package/src/tools/rc.ts +437 -0
  136. package/src/tools/sequence.ts +791 -0
  137. package/src/tools/tool-definitions.ts +907 -0
  138. package/src/tools/tool-handlers.ts +941 -0
  139. package/src/tools/ui.ts +499 -0
  140. package/src/tools/visual.ts +60 -0
  141. package/src/types/env.ts +27 -0
  142. package/src/types/index.ts +414 -0
  143. package/src/types/tool-types.ts +343 -0
  144. package/src/unreal-bridge.ts +1118 -0
  145. package/src/utils/cache-manager.ts +213 -0
  146. package/src/utils/error-handler.ts +320 -0
  147. package/src/utils/errors.ts +312 -0
  148. package/src/utils/http.ts +184 -0
  149. package/src/utils/logger.ts +30 -0
  150. package/src/utils/normalize.ts +54 -0
  151. package/src/utils/response-validator.ts +145 -0
  152. package/src/utils/safe-json.ts +112 -0
  153. package/src/utils/stdio-redirect.ts +18 -0
  154. package/src/utils/validation.ts +212 -0
  155. package/tsconfig.json +33 -0
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Enhanced error types for better error handling and recovery
3
+ */
4
+ export var ErrorCode;
5
+ (function (ErrorCode) {
6
+ // Connection errors
7
+ ErrorCode["CONNECTION_FAILED"] = "CONNECTION_FAILED";
8
+ ErrorCode["CONNECTION_TIMEOUT"] = "CONNECTION_TIMEOUT";
9
+ ErrorCode["CONNECTION_REFUSED"] = "CONNECTION_REFUSED";
10
+ // API errors
11
+ ErrorCode["API_ERROR"] = "API_ERROR";
12
+ ErrorCode["INVALID_RESPONSE"] = "INVALID_RESPONSE";
13
+ ErrorCode["RATE_LIMITED"] = "RATE_LIMITED";
14
+ // Validation errors
15
+ ErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
16
+ ErrorCode["INVALID_PARAMETERS"] = "INVALID_PARAMETERS";
17
+ ErrorCode["MISSING_REQUIRED_FIELD"] = "MISSING_REQUIRED_FIELD";
18
+ // Resource errors
19
+ ErrorCode["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND";
20
+ ErrorCode["RESOURCE_LOCKED"] = "RESOURCE_LOCKED";
21
+ ErrorCode["RESOURCE_UNAVAILABLE"] = "RESOURCE_UNAVAILABLE";
22
+ // Permission errors
23
+ ErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
24
+ ErrorCode["FORBIDDEN"] = "FORBIDDEN";
25
+ // System errors
26
+ ErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
27
+ ErrorCode["CIRCUIT_BREAKER_OPEN"] = "CIRCUIT_BREAKER_OPEN";
28
+ ErrorCode["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE";
29
+ })(ErrorCode || (ErrorCode = {}));
30
+ /**
31
+ * Base application error with metadata
32
+ */
33
+ export class AppError extends Error {
34
+ metadata;
35
+ constructor(message, metadata = {}) {
36
+ super(message);
37
+ this.name = this.constructor.name;
38
+ this.metadata = {
39
+ code: metadata.code || ErrorCode.INTERNAL_ERROR,
40
+ retriable: metadata.retriable || false,
41
+ timestamp: new Date(),
42
+ ...metadata
43
+ };
44
+ Error.captureStackTrace(this, this.constructor);
45
+ }
46
+ toJSON() {
47
+ return {
48
+ name: this.name,
49
+ message: this.message,
50
+ ...this.metadata,
51
+ stack: this.stack
52
+ };
53
+ }
54
+ }
55
+ /**
56
+ * Connection-related errors
57
+ */
58
+ export class ConnectionError extends AppError {
59
+ constructor(message, metadata = {}) {
60
+ super(message, {
61
+ code: ErrorCode.CONNECTION_FAILED,
62
+ retriable: true,
63
+ statusCode: 503,
64
+ ...metadata
65
+ });
66
+ }
67
+ }
68
+ /**
69
+ * API-related errors
70
+ */
71
+ export class ApiError extends AppError {
72
+ constructor(message, statusCode, metadata = {}) {
73
+ super(message, {
74
+ code: ErrorCode.API_ERROR,
75
+ statusCode,
76
+ retriable: statusCode >= 500 || statusCode === 429,
77
+ ...metadata
78
+ });
79
+ }
80
+ }
81
+ /**
82
+ * Validation errors
83
+ */
84
+ export class ValidationError extends AppError {
85
+ constructor(message, metadata = {}) {
86
+ super(message, {
87
+ code: ErrorCode.VALIDATION_ERROR,
88
+ statusCode: 400,
89
+ retriable: false,
90
+ ...metadata
91
+ });
92
+ }
93
+ }
94
+ /**
95
+ * Resource errors
96
+ */
97
+ export class ResourceError extends AppError {
98
+ constructor(message, code, metadata = {}) {
99
+ super(message, {
100
+ code,
101
+ statusCode: code === ErrorCode.RESOURCE_NOT_FOUND ? 404 : 409,
102
+ retriable: code === ErrorCode.RESOURCE_LOCKED,
103
+ ...metadata
104
+ });
105
+ }
106
+ }
107
+ /**
108
+ * Circuit Breaker implementation for fault tolerance
109
+ */
110
+ export var CircuitState;
111
+ (function (CircuitState) {
112
+ CircuitState["CLOSED"] = "CLOSED";
113
+ CircuitState["OPEN"] = "OPEN";
114
+ CircuitState["HALF_OPEN"] = "HALF_OPEN";
115
+ })(CircuitState || (CircuitState = {}));
116
+ export class CircuitBreaker {
117
+ state = CircuitState.CLOSED;
118
+ failures = 0;
119
+ successCount = 0;
120
+ lastFailureTime;
121
+ options;
122
+ constructor(options = {}) {
123
+ this.options = {
124
+ threshold: options.threshold || 5,
125
+ timeout: options.timeout || 60000, // 1 minute
126
+ resetTimeout: options.resetTimeout || 30000, // 30 seconds
127
+ onStateChange: options.onStateChange
128
+ };
129
+ }
130
+ /**
131
+ * Execute function with circuit breaker protection
132
+ */
133
+ async execute(fn) {
134
+ // Check circuit state
135
+ if (this.state === CircuitState.OPEN) {
136
+ if (this.shouldAttemptReset()) {
137
+ this.transitionTo(CircuitState.HALF_OPEN);
138
+ }
139
+ else {
140
+ throw new AppError('Circuit breaker is open', {
141
+ code: ErrorCode.CIRCUIT_BREAKER_OPEN,
142
+ retriable: true,
143
+ context: {
144
+ failures: this.failures,
145
+ lastFailure: this.lastFailureTime
146
+ }
147
+ });
148
+ }
149
+ }
150
+ try {
151
+ const result = await fn();
152
+ this.onSuccess();
153
+ return result;
154
+ }
155
+ catch (error) {
156
+ this.onFailure();
157
+ throw error;
158
+ }
159
+ }
160
+ onSuccess() {
161
+ this.failures = 0;
162
+ if (this.state === CircuitState.HALF_OPEN) {
163
+ this.successCount++;
164
+ if (this.successCount >= 3) {
165
+ this.transitionTo(CircuitState.CLOSED);
166
+ }
167
+ }
168
+ }
169
+ onFailure() {
170
+ this.failures++;
171
+ this.lastFailureTime = new Date();
172
+ this.successCount = 0;
173
+ if (this.failures >= this.options.threshold) {
174
+ this.transitionTo(CircuitState.OPEN);
175
+ }
176
+ }
177
+ shouldAttemptReset() {
178
+ return (this.lastFailureTime !== undefined &&
179
+ Date.now() - this.lastFailureTime.getTime() >= this.options.resetTimeout);
180
+ }
181
+ transitionTo(newState) {
182
+ const oldState = this.state;
183
+ this.state = newState;
184
+ if (newState === CircuitState.CLOSED) {
185
+ this.failures = 0;
186
+ this.successCount = 0;
187
+ }
188
+ if (this.options.onStateChange && oldState !== newState) {
189
+ this.options.onStateChange(oldState, newState);
190
+ }
191
+ }
192
+ getState() {
193
+ return this.state;
194
+ }
195
+ getMetrics() {
196
+ return {
197
+ state: this.state,
198
+ failures: this.failures,
199
+ successCount: this.successCount,
200
+ lastFailureTime: this.lastFailureTime
201
+ };
202
+ }
203
+ }
204
+ /**
205
+ * Error recovery strategies
206
+ */
207
+ export class ErrorRecovery {
208
+ static circuitBreakers = new Map();
209
+ /**
210
+ * Get or create circuit breaker for a service
211
+ */
212
+ static getCircuitBreaker(service, options) {
213
+ if (!this.circuitBreakers.has(service)) {
214
+ this.circuitBreakers.set(service, new CircuitBreaker(options));
215
+ }
216
+ const breaker = this.circuitBreakers.get(service);
217
+ if (!breaker) {
218
+ throw new Error(`Circuit breaker for service ${service} could not be created`);
219
+ }
220
+ return breaker;
221
+ }
222
+ /**
223
+ * Wrap function with error recovery
224
+ */
225
+ static async withRecovery(fn, options) {
226
+ const breaker = this.getCircuitBreaker(options.service);
227
+ try {
228
+ return await breaker.execute(fn);
229
+ }
230
+ catch (error) {
231
+ if (options.onError) {
232
+ options.onError(error);
233
+ }
234
+ // Try fallback if available
235
+ if (options.fallback) {
236
+ return await options.fallback();
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+ /**
242
+ * Check if error is retriable
243
+ */
244
+ static isRetriable(error) {
245
+ if (error instanceof AppError) {
246
+ return error.metadata.retriable;
247
+ }
248
+ // Check for network errors
249
+ const message = error.message.toLowerCase();
250
+ return (message.includes('timeout') ||
251
+ message.includes('network') ||
252
+ message.includes('econnrefused') ||
253
+ message.includes('econnreset'));
254
+ }
255
+ }
256
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,26 @@
1
+ import { AxiosInstance, AxiosRequestConfig } from 'axios';
2
+ interface RetryConfig {
3
+ maxRetries: number;
4
+ initialDelay: number;
5
+ maxDelay: number;
6
+ backoffMultiplier: number;
7
+ retryableStatuses: number[];
8
+ retryableErrors: string[];
9
+ }
10
+ /**
11
+ * Enhanced HTTP client factory with connection pooling and retry logic
12
+ */
13
+ export declare function createHttpClient(baseURL: string): AxiosInstance;
14
+ /**
15
+ * Execute request with retry logic for resilience
16
+ */
17
+ export declare function requestWithRetry<T = any>(client: AxiosInstance, config: AxiosRequestConfig, retryConfig?: Partial<RetryConfig>): Promise<T>;
18
+ /**
19
+ * Batch multiple requests for efficiency
20
+ */
21
+ export declare function batchRequests<T = any>(client: AxiosInstance, requests: AxiosRequestConfig[], options?: {
22
+ concurrency?: number;
23
+ throwOnError?: boolean;
24
+ }): Promise<(T | Error)[]>;
25
+ export {};
26
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1,135 @@
1
+ import axios from 'axios';
2
+ import http from 'http';
3
+ import https from 'https';
4
+ // Connection pooling configuration for better performance
5
+ const httpAgent = new http.Agent({
6
+ keepAlive: true,
7
+ keepAliveMsecs: 30000,
8
+ maxSockets: 10,
9
+ maxFreeSockets: 5,
10
+ timeout: 30000
11
+ });
12
+ const httpsAgent = new https.Agent({
13
+ keepAlive: true,
14
+ keepAliveMsecs: 30000,
15
+ maxSockets: 10,
16
+ maxFreeSockets: 5,
17
+ timeout: 30000
18
+ });
19
+ const defaultRetryConfig = {
20
+ maxRetries: 3,
21
+ initialDelay: 1000,
22
+ maxDelay: 10000,
23
+ backoffMultiplier: 2,
24
+ retryableStatuses: [408, 429, 500, 502, 503, 504],
25
+ retryableErrors: ['ECONNABORTED', 'ETIMEDOUT', 'ECONNRESET', 'ENOTFOUND']
26
+ };
27
+ /**
28
+ * Calculate exponential backoff delay with jitter
29
+ */
30
+ function calculateBackoff(attempt, config) {
31
+ const delay = Math.min(config.initialDelay * Math.pow(config.backoffMultiplier, attempt - 1), config.maxDelay);
32
+ // Add jitter to prevent thundering herd
33
+ return delay + Math.random() * 1000;
34
+ }
35
+ /**
36
+ * Enhanced HTTP client factory with connection pooling and retry logic
37
+ */
38
+ export function createHttpClient(baseURL) {
39
+ const client = axios.create({
40
+ baseURL,
41
+ headers: {
42
+ 'Content-Type': 'application/json',
43
+ 'Accept': 'application/json'
44
+ },
45
+ timeout: 15000,
46
+ httpAgent,
47
+ httpsAgent,
48
+ // Ensure proper handling of request body transformation
49
+ transformRequest: [(data, headers) => {
50
+ // Remove Content-Length if it's set incorrectly
51
+ delete headers['Content-Length'];
52
+ delete headers['content-length'];
53
+ // Properly stringify JSON data
54
+ if (data && typeof data === 'object') {
55
+ const jsonStr = JSON.stringify(data);
56
+ // Let axios set Content-Length automatically
57
+ return jsonStr;
58
+ }
59
+ return data;
60
+ }],
61
+ // Optimize response handling
62
+ maxContentLength: 50 * 1024 * 1024, // 50MB
63
+ maxBodyLength: 50 * 1024 * 1024,
64
+ decompress: true
65
+ });
66
+ // Add request interceptor for timing
67
+ client.interceptors.request.use((config) => {
68
+ // Add metadata for performance tracking
69
+ config.metadata = { startTime: Date.now() };
70
+ return config;
71
+ }, (error) => {
72
+ return Promise.reject(error);
73
+ });
74
+ // Add response interceptor for timing and logging
75
+ client.interceptors.response.use((response) => {
76
+ const duration = Date.now() - (response.config.metadata?.startTime || 0);
77
+ if (duration > 5000) {
78
+ console.warn(`[HTTP] Slow request: ${response.config.url} took ${duration}ms`);
79
+ }
80
+ return response;
81
+ }, (error) => {
82
+ return Promise.reject(error);
83
+ });
84
+ return client;
85
+ }
86
+ /**
87
+ * Execute request with retry logic for resilience
88
+ */
89
+ export async function requestWithRetry(client, config, retryConfig = {}) {
90
+ const retry = { ...defaultRetryConfig, ...retryConfig };
91
+ let lastError = null;
92
+ for (let attempt = 1; attempt <= retry.maxRetries; attempt++) {
93
+ try {
94
+ const response = await client.request(config);
95
+ // Check if we should retry based on status
96
+ if (retry.retryableStatuses.includes(response.status)) {
97
+ throw new Error(`Retryable status: ${response.status}`);
98
+ }
99
+ return response.data;
100
+ }
101
+ catch (error) {
102
+ lastError = error;
103
+ const axiosError = error;
104
+ // Check if error is retryable
105
+ const isRetryable = retry.retryableErrors.includes(axiosError.code || '') ||
106
+ (axiosError.response && retry.retryableStatuses.includes(axiosError.response.status));
107
+ if (!isRetryable || attempt === retry.maxRetries) {
108
+ throw error;
109
+ }
110
+ // Calculate delay and wait
111
+ const delay = calculateBackoff(attempt, retry);
112
+ console.error(`[HTTP] Retry attempt ${attempt}/${retry.maxRetries} after ${Math.round(delay)}ms`);
113
+ await new Promise(resolve => setTimeout(resolve, delay));
114
+ }
115
+ }
116
+ throw lastError || new Error('Request failed after retries');
117
+ }
118
+ /**
119
+ * Batch multiple requests for efficiency
120
+ */
121
+ export async function batchRequests(client, requests, options = {}) {
122
+ const { concurrency = 5, throwOnError = false } = options;
123
+ const results = [];
124
+ // Process requests in batches
125
+ for (let i = 0; i < requests.length; i += concurrency) {
126
+ const batch = requests.slice(i, i + concurrency);
127
+ const batchPromises = batch.map(req => client.request(req)
128
+ .then(res => res.data)
129
+ .catch(err => throwOnError ? Promise.reject(err) : err));
130
+ const batchResults = await Promise.all(batchPromises);
131
+ results.push(...batchResults);
132
+ }
133
+ return results;
134
+ }
135
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1,12 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export declare class Logger {
3
+ private scope;
4
+ private level;
5
+ constructor(scope: string, level?: LogLevel);
6
+ private shouldLog;
7
+ debug(...args: any[]): void;
8
+ info(...args: any[]): void;
9
+ warn(...args: any[]): void;
10
+ error(...args: any[]): void;
11
+ }
12
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1,32 @@
1
+ export class Logger {
2
+ scope;
3
+ level;
4
+ constructor(scope, level = 'info') {
5
+ this.scope = scope;
6
+ const envLevel = (process.env.LOG_LEVEL || process.env.LOGLEVEL || level).toString().toLowerCase();
7
+ this.level = ['debug', 'info', 'warn', 'error'].includes(envLevel)
8
+ ? envLevel
9
+ : 'info';
10
+ }
11
+ shouldLog(level) {
12
+ const order = ['debug', 'info', 'warn', 'error'];
13
+ return order.indexOf(level) >= order.indexOf(this.level);
14
+ }
15
+ debug(...args) {
16
+ if (this.shouldLog('debug'))
17
+ console.error(`[${this.scope}]`, ...args);
18
+ }
19
+ info(...args) {
20
+ if (this.shouldLog('info'))
21
+ console.error(`[${this.scope}]`, ...args);
22
+ }
23
+ warn(...args) {
24
+ if (this.shouldLog('warn'))
25
+ console.error(`[${this.scope}]`, ...args);
26
+ }
27
+ error(...args) {
28
+ if (this.shouldLog('error'))
29
+ console.error(`[${this.scope}]`, ...args);
30
+ }
31
+ }
32
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1,17 @@
1
+ export type Vec3Array = [number, number, number];
2
+ export type Rot3Array = [number, number, number];
3
+ export interface Vec3Obj {
4
+ x: number;
5
+ y: number;
6
+ z: number;
7
+ }
8
+ export interface Rot3Obj {
9
+ pitch: number;
10
+ yaw: number;
11
+ roll: number;
12
+ }
13
+ export declare function toVec3Object(input: any): Vec3Obj | null;
14
+ export declare function toVec3Array(input: any): Vec3Array | null;
15
+ export declare function toRotObject(input: any): Rot3Obj | null;
16
+ export declare function toRotArray(input: any): Rot3Array | null;
17
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1,49 @@
1
+ export function toVec3Object(input) {
2
+ try {
3
+ if (Array.isArray(input) && input.length === 3) {
4
+ const [x, y, z] = input;
5
+ if ([x, y, z].every(v => typeof v === 'number' && isFinite(v))) {
6
+ return { x, y, z };
7
+ }
8
+ }
9
+ if (input && typeof input === 'object') {
10
+ const x = Number(input.x ?? input.X);
11
+ const y = Number(input.y ?? input.Y);
12
+ const z = Number(input.z ?? input.Z);
13
+ if ([x, y, z].every(v => typeof v === 'number' && !isNaN(v) && isFinite(v))) {
14
+ return { x, y, z };
15
+ }
16
+ }
17
+ }
18
+ catch { }
19
+ return null;
20
+ }
21
+ export function toVec3Array(input) {
22
+ const obj = toVec3Object(input);
23
+ return obj ? [obj.x, obj.y, obj.z] : null;
24
+ }
25
+ export function toRotObject(input) {
26
+ try {
27
+ if (Array.isArray(input) && input.length === 3) {
28
+ const [pitch, yaw, roll] = input;
29
+ if ([pitch, yaw, roll].every(v => typeof v === 'number' && isFinite(v))) {
30
+ return { pitch, yaw, roll };
31
+ }
32
+ }
33
+ if (input && typeof input === 'object') {
34
+ const pitch = Number(input.pitch ?? input.Pitch);
35
+ const yaw = Number(input.yaw ?? input.Yaw);
36
+ const roll = Number(input.roll ?? input.Roll);
37
+ if ([pitch, yaw, roll].every(v => typeof v === 'number' && !isNaN(v) && isFinite(v))) {
38
+ return { pitch, yaw, roll };
39
+ }
40
+ }
41
+ }
42
+ catch { }
43
+ return null;
44
+ }
45
+ export function toRotArray(input) {
46
+ const obj = toRotObject(input);
47
+ return obj ? [obj.pitch, obj.yaw, obj.roll] : null;
48
+ }
49
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Response Validator for MCP Tool Outputs
3
+ * Validates tool responses against their defined output schemas
4
+ */
5
+ export declare class ResponseValidator {
6
+ private ajv;
7
+ private validators;
8
+ constructor();
9
+ /**
10
+ * Register a tool's output schema for validation
11
+ */
12
+ registerSchema(toolName: string, outputSchema: any): void;
13
+ /**
14
+ * Validate a tool's response against its schema
15
+ */
16
+ validateResponse(toolName: string, response: any): {
17
+ valid: boolean;
18
+ errors?: string[];
19
+ structuredContent?: any;
20
+ };
21
+ /**
22
+ * Wrap a tool response with validation
23
+ */
24
+ wrapResponse(toolName: string, response: any): any;
25
+ /**
26
+ * Get validation statistics
27
+ */
28
+ getStats(): {
29
+ totalSchemas: number;
30
+ tools: string[];
31
+ };
32
+ }
33
+ export declare const responseValidator: ResponseValidator;
34
+ //# sourceMappingURL=response-validator.d.ts.map
@@ -0,0 +1,121 @@
1
+ import Ajv from 'ajv';
2
+ import { Logger } from './logger.js';
3
+ import { cleanObject } from './safe-json.js';
4
+ const log = new Logger('ResponseValidator');
5
+ /**
6
+ * Response Validator for MCP Tool Outputs
7
+ * Validates tool responses against their defined output schemas
8
+ */
9
+ export class ResponseValidator {
10
+ ajv;
11
+ validators = new Map();
12
+ constructor() {
13
+ this.ajv = new Ajv({
14
+ allErrors: true,
15
+ verbose: true,
16
+ strict: false // Allow additional properties for flexibility
17
+ });
18
+ }
19
+ /**
20
+ * Register a tool's output schema for validation
21
+ */
22
+ registerSchema(toolName, outputSchema) {
23
+ if (!outputSchema) {
24
+ log.warn(`No output schema defined for tool: ${toolName}`);
25
+ return;
26
+ }
27
+ try {
28
+ const validator = this.ajv.compile(outputSchema);
29
+ this.validators.set(toolName, validator);
30
+ log.info(`Registered output schema for tool: ${toolName}`);
31
+ }
32
+ catch (error) {
33
+ log.error(`Failed to compile output schema for ${toolName}:`, error);
34
+ }
35
+ }
36
+ /**
37
+ * Validate a tool's response against its schema
38
+ */
39
+ validateResponse(toolName, response) {
40
+ const validator = this.validators.get(toolName);
41
+ if (!validator) {
42
+ log.debug(`No validator found for tool: ${toolName}`);
43
+ return { valid: true }; // Pass through if no schema defined
44
+ }
45
+ // Extract structured content from response
46
+ let structuredContent = response;
47
+ // If response has MCP format with content array
48
+ if (response.content && Array.isArray(response.content)) {
49
+ // Try to extract structured data from text content
50
+ const textContent = response.content.find((c) => c.type === 'text');
51
+ if (textContent?.text) {
52
+ try {
53
+ // Check if text is JSON
54
+ structuredContent = JSON.parse(textContent.text);
55
+ }
56
+ catch {
57
+ // Not JSON, use the full response
58
+ structuredContent = response;
59
+ }
60
+ }
61
+ }
62
+ const valid = validator(structuredContent);
63
+ if (!valid) {
64
+ const errors = validator.errors?.map((err) => `${err.instancePath || 'root'}: ${err.message}`);
65
+ log.warn(`Response validation failed for ${toolName}:`, errors);
66
+ return {
67
+ valid: false,
68
+ errors,
69
+ structuredContent
70
+ };
71
+ }
72
+ return {
73
+ valid: true,
74
+ structuredContent
75
+ };
76
+ }
77
+ /**
78
+ * Wrap a tool response with validation
79
+ */
80
+ wrapResponse(toolName, response) {
81
+ // Ensure response is safe to serialize first
82
+ try {
83
+ // The response should already be cleaned, but double-check
84
+ if (response && typeof response === 'object') {
85
+ // Make sure we can serialize it
86
+ JSON.stringify(response);
87
+ }
88
+ }
89
+ catch (error) {
90
+ log.error(`Response for ${toolName} contains circular references, cleaning...`);
91
+ response = cleanObject(response);
92
+ }
93
+ const validation = this.validateResponse(toolName, response);
94
+ // Add validation metadata
95
+ if (!validation.valid) {
96
+ log.warn(`Tool ${toolName} response validation failed:`, validation.errors);
97
+ // Add warning to response but don't fail
98
+ if (response && typeof response === 'object') {
99
+ response._validation = {
100
+ valid: false,
101
+ errors: validation.errors
102
+ };
103
+ }
104
+ }
105
+ // Don't add structuredContent to the response - it's for internal validation only
106
+ // Adding it can cause circular references
107
+ return response;
108
+ }
109
+ /**
110
+ * Get validation statistics
111
+ */
112
+ getStats() {
113
+ return {
114
+ totalSchemas: this.validators.size,
115
+ tools: Array.from(this.validators.keys())
116
+ };
117
+ }
118
+ }
119
+ // Singleton instance
120
+ export const responseValidator = new ResponseValidator();
121
+ //# sourceMappingURL=response-validator.js.map
@@ -0,0 +1,4 @@
1
+ export declare function safeStringify(obj: any, space?: number): string;
2
+ export declare function sanitizeResponse(response: any): any;
3
+ export declare function cleanObject(obj: any, maxDepth?: number): any;
4
+ //# sourceMappingURL=safe-json.d.ts.map