@midscene/shared 1.7.4 → 1.7.5-beta-20260420031652.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
6
  import express from "express";
7
+ import { getErrorMessage } from "./error-formatter.mjs";
7
8
  function _define_property(obj, key, value) {
8
9
  if (key in obj) Object.defineProperty(obj, key, {
9
10
  value: value,
@@ -45,7 +46,7 @@ class BaseMCPServer {
45
46
  try {
46
47
  await this.toolsManager.initTools();
47
48
  } catch (error) {
48
- const message = error instanceof Error ? error.message : String(error);
49
+ const message = getErrorMessage(error);
49
50
  console.error(`Failed to initialize tools: ${message}`);
50
51
  console.error('Tools will be initialized on first use');
51
52
  }
@@ -71,7 +72,7 @@ class BaseMCPServer {
71
72
  try {
72
73
  await this.mcpServer.connect(transport);
73
74
  } catch (error) {
74
- const message = error instanceof Error ? error.message : String(error);
75
+ const message = getErrorMessage(error);
75
76
  console.error(`Failed to connect MCP stdio transport: ${message}`);
76
77
  throw new Error(`Failed to initialize MCP stdio transport: ${message}`);
77
78
  }
@@ -142,7 +143,7 @@ class BaseMCPServer {
142
143
  });
143
144
  }
144
145
  } catch (error) {
145
- const message = error instanceof Error ? error.message : String(error);
146
+ const message = getErrorMessage(error);
146
147
  const duration = Date.now() - startTime;
147
148
  console.error(`[${new Date().toISOString()}] [${requestId}] MCP request error after ${duration}ms: ${message}`);
148
149
  if (!res.headersSent) res.status(500).json({
@@ -170,7 +171,7 @@ class BaseMCPServer {
170
171
  for (const session of sessions.values())try {
171
172
  await session.transport.close();
172
173
  } catch (error) {
173
- const message = error instanceof Error ? error.message : String(error);
174
+ const message = getErrorMessage(error);
174
175
  console.error(`Failed to close session ${session.transport.sessionId}: ${message}`);
175
176
  }
176
177
  sessions.clear();
@@ -205,7 +206,7 @@ class BaseMCPServer {
205
206
  try {
206
207
  await this.mcpServer.connect(transport);
207
208
  } catch (error) {
208
- const message = error instanceof Error ? error.message : String(error);
209
+ const message = getErrorMessage(error);
209
210
  console.error(`[${new Date().toISOString()}] Failed to connect MCP transport: ${message}`);
210
211
  if (transport.sessionId) sessions.delete(transport.sessionId);
211
212
  throw new Error(`Failed to initialize MCP session: ${message}`);
@@ -224,7 +225,7 @@ class BaseMCPServer {
224
225
  sessions.delete(sid);
225
226
  console.log(`[${new Date().toISOString()}] Session ${sid} cleaned up due to inactivity (remaining: ${sessions.size})`);
226
227
  } catch (error) {
227
- const message = error instanceof Error ? error.message : String(error);
228
+ const message = getErrorMessage(error);
228
229
  console.error(`[${new Date().toISOString()}] Failed to close session ${sid} during cleanup: ${message}`);
229
230
  sessions.delete(sid);
230
231
  }
@@ -237,7 +238,7 @@ class BaseMCPServer {
237
238
  for (const session of sessions.values())try {
238
239
  session.transport.close();
239
240
  } catch (error) {
240
- const message = error instanceof Error ? error.message : String(error);
241
+ const message = getErrorMessage(error);
241
242
  console.error(`Error closing session during shutdown: ${message}`);
242
243
  }
243
244
  sessions.clear();
@@ -250,7 +251,7 @@ class BaseMCPServer {
250
251
  this.performCleanup().finally(()=>process.exit(1));
251
252
  }, 5000);
252
253
  } catch (error) {
253
- const message = error instanceof Error ? error.message : String(error);
254
+ const message = getErrorMessage(error);
254
255
  console.error(`Error closing HTTP server: ${message}`);
255
256
  this.performCleanup().finally(()=>process.exit(1));
256
257
  }
@@ -0,0 +1,19 @@
1
+ function getErrorMessage(error) {
2
+ if (error instanceof Error) return error.message;
3
+ if (null == error) return String(error);
4
+ if ('object' != typeof error) return String(error);
5
+ const candidate = extractStringMessage(error);
6
+ if (candidate) return candidate;
7
+ try {
8
+ return JSON.stringify(error);
9
+ } catch {
10
+ return Object.prototype.toString.call(error);
11
+ }
12
+ }
13
+ function extractStringMessage(error) {
14
+ const anyError = error;
15
+ if ('string' == typeof anyError.message && anyError.message) return anyError.message;
16
+ if (anyError.error && 'string' == typeof anyError.error.message && anyError.error.message) return anyError.error.message;
17
+ if (anyError.cause && 'string' == typeof anyError.cause.message && anyError.cause.message) return anyError.cause.message;
18
+ }
19
+ export { getErrorMessage };
@@ -1,5 +1,6 @@
1
1
  export * from "./base-server.mjs";
2
2
  export * from "./base-tools.mjs";
3
+ export * from "./error-formatter.mjs";
3
4
  export * from "./tool-generator.mjs";
4
5
  export * from "./types.mjs";
5
6
  export * from "./inject-report-html-plugin.mjs";
@@ -1,9 +1,7 @@
1
1
  import { parseBase64 } from "@midscene/shared/img";
2
2
  import { z } from "zod";
3
3
  import { getZodDescription, getZodTypeName, isMidsceneLocatorField, unwrapZodField } from "../zod-schema-utils.mjs";
4
- function getErrorMessage(error) {
5
- return error instanceof Error ? error.message : String(error);
6
- }
4
+ import { getErrorMessage } from "./error-formatter.mjs";
7
5
  function describeActionForMCP(action) {
8
6
  const actionDesc = action.description || `Execute ${action.name} action`;
9
7
  if (!action.paramSchema) return `${action.name} action, ${actionDesc}`;
@@ -44,6 +44,7 @@ const stdio_js_namespaceObject = require("@modelcontextprotocol/sdk/server/stdio
44
44
  const streamableHttp_js_namespaceObject = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
45
45
  const external_express_namespaceObject = require("express");
46
46
  var external_express_default = /*#__PURE__*/ __webpack_require__.n(external_express_namespaceObject);
47
+ const external_error_formatter_js_namespaceObject = require("./error-formatter.js");
47
48
  function _define_property(obj, key, value) {
48
49
  if (key in obj) Object.defineProperty(obj, key, {
49
50
  value: value,
@@ -85,7 +86,7 @@ class BaseMCPServer {
85
86
  try {
86
87
  await this.toolsManager.initTools();
87
88
  } catch (error) {
88
- const message = error instanceof Error ? error.message : String(error);
89
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
89
90
  console.error(`Failed to initialize tools: ${message}`);
90
91
  console.error('Tools will be initialized on first use');
91
92
  }
@@ -111,7 +112,7 @@ class BaseMCPServer {
111
112
  try {
112
113
  await this.mcpServer.connect(transport);
113
114
  } catch (error) {
114
- const message = error instanceof Error ? error.message : String(error);
115
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
115
116
  console.error(`Failed to connect MCP stdio transport: ${message}`);
116
117
  throw new Error(`Failed to initialize MCP stdio transport: ${message}`);
117
118
  }
@@ -182,7 +183,7 @@ class BaseMCPServer {
182
183
  });
183
184
  }
184
185
  } catch (error) {
185
- const message = error instanceof Error ? error.message : String(error);
186
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
186
187
  const duration = Date.now() - startTime;
187
188
  console.error(`[${new Date().toISOString()}] [${requestId}] MCP request error after ${duration}ms: ${message}`);
188
189
  if (!res.headersSent) res.status(500).json({
@@ -210,7 +211,7 @@ class BaseMCPServer {
210
211
  for (const session of sessions.values())try {
211
212
  await session.transport.close();
212
213
  } catch (error) {
213
- const message = error instanceof Error ? error.message : String(error);
214
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
214
215
  console.error(`Failed to close session ${session.transport.sessionId}: ${message}`);
215
216
  }
216
217
  sessions.clear();
@@ -245,7 +246,7 @@ class BaseMCPServer {
245
246
  try {
246
247
  await this.mcpServer.connect(transport);
247
248
  } catch (error) {
248
- const message = error instanceof Error ? error.message : String(error);
249
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
249
250
  console.error(`[${new Date().toISOString()}] Failed to connect MCP transport: ${message}`);
250
251
  if (transport.sessionId) sessions.delete(transport.sessionId);
251
252
  throw new Error(`Failed to initialize MCP session: ${message}`);
@@ -264,7 +265,7 @@ class BaseMCPServer {
264
265
  sessions.delete(sid);
265
266
  console.log(`[${new Date().toISOString()}] Session ${sid} cleaned up due to inactivity (remaining: ${sessions.size})`);
266
267
  } catch (error) {
267
- const message = error instanceof Error ? error.message : String(error);
268
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
268
269
  console.error(`[${new Date().toISOString()}] Failed to close session ${sid} during cleanup: ${message}`);
269
270
  sessions.delete(sid);
270
271
  }
@@ -277,7 +278,7 @@ class BaseMCPServer {
277
278
  for (const session of sessions.values())try {
278
279
  session.transport.close();
279
280
  } catch (error) {
280
- const message = error instanceof Error ? error.message : String(error);
281
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
281
282
  console.error(`Error closing session during shutdown: ${message}`);
282
283
  }
283
284
  sessions.clear();
@@ -290,7 +291,7 @@ class BaseMCPServer {
290
291
  this.performCleanup().finally(()=>process.exit(1));
291
292
  }, 5000);
292
293
  } catch (error) {
293
- const message = error instanceof Error ? error.message : String(error);
294
+ const message = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
294
295
  console.error(`Error closing HTTP server: ${message}`);
295
296
  this.performCleanup().finally(()=>process.exit(1));
296
297
  }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ getErrorMessage: ()=>getErrorMessage
28
+ });
29
+ function getErrorMessage(error) {
30
+ if (error instanceof Error) return error.message;
31
+ if (null == error) return String(error);
32
+ if ('object' != typeof error) return String(error);
33
+ const candidate = extractStringMessage(error);
34
+ if (candidate) return candidate;
35
+ try {
36
+ return JSON.stringify(error);
37
+ } catch {
38
+ return Object.prototype.toString.call(error);
39
+ }
40
+ }
41
+ function extractStringMessage(error) {
42
+ const anyError = error;
43
+ if ('string' == typeof anyError.message && anyError.message) return anyError.message;
44
+ if (anyError.error && 'string' == typeof anyError.error.message && anyError.error.message) return anyError.error.message;
45
+ if (anyError.cause && 'string' == typeof anyError.cause.message && anyError.cause.message) return anyError.cause.message;
46
+ }
47
+ exports.getErrorMessage = __webpack_exports__.getErrorMessage;
48
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
49
+ "getErrorMessage"
50
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
51
+ Object.defineProperty(exports, '__esModule', {
52
+ value: true
53
+ });
@@ -9,6 +9,9 @@ var __webpack_modules__ = {
9
9
  "./chrome-path" (module) {
10
10
  module.exports = require("./chrome-path.js");
11
11
  },
12
+ "./error-formatter" (module) {
13
+ module.exports = require("./error-formatter.js");
14
+ },
12
15
  "./inject-report-html-plugin" (module) {
13
16
  module.exports = require("./inject-report-html-plugin.js");
14
17
  },
@@ -73,25 +76,29 @@ var __webpack_exports__ = {};
73
76
  var __rspack_reexport = {};
74
77
  for(const __rspack_import_key in _base_tools__rspack_import_1)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_base_tools__rspack_import_1[__rspack_import_key];
75
78
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
76
- var _tool_generator__rspack_import_2 = __webpack_require__("./tool-generator");
79
+ var _error_formatter__rspack_import_2 = __webpack_require__("./error-formatter");
80
+ var __rspack_reexport = {};
81
+ for(const __rspack_import_key in _error_formatter__rspack_import_2)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_error_formatter__rspack_import_2[__rspack_import_key];
82
+ __webpack_require__.d(__webpack_exports__, __rspack_reexport);
83
+ var _tool_generator__rspack_import_3 = __webpack_require__("./tool-generator");
77
84
  var __rspack_reexport = {};
78
- for(const __rspack_import_key in _tool_generator__rspack_import_2)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_tool_generator__rspack_import_2[__rspack_import_key];
85
+ for(const __rspack_import_key in _tool_generator__rspack_import_3)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_tool_generator__rspack_import_3[__rspack_import_key];
79
86
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
80
- var _types__rspack_import_3 = __webpack_require__("./types");
87
+ var _types__rspack_import_4 = __webpack_require__("./types");
81
88
  var __rspack_reexport = {};
82
- for(const __rspack_import_key in _types__rspack_import_3)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_types__rspack_import_3[__rspack_import_key];
89
+ for(const __rspack_import_key in _types__rspack_import_4)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_types__rspack_import_4[__rspack_import_key];
83
90
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
84
- var _inject_report_html_plugin__rspack_import_4 = __webpack_require__("./inject-report-html-plugin");
91
+ var _inject_report_html_plugin__rspack_import_5 = __webpack_require__("./inject-report-html-plugin");
85
92
  var __rspack_reexport = {};
86
- for(const __rspack_import_key in _inject_report_html_plugin__rspack_import_4)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_inject_report_html_plugin__rspack_import_4[__rspack_import_key];
93
+ for(const __rspack_import_key in _inject_report_html_plugin__rspack_import_5)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_inject_report_html_plugin__rspack_import_5[__rspack_import_key];
87
94
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
88
- var _launcher_helper__rspack_import_5 = __webpack_require__("./launcher-helper");
95
+ var _launcher_helper__rspack_import_6 = __webpack_require__("./launcher-helper");
89
96
  var __rspack_reexport = {};
90
- for(const __rspack_import_key in _launcher_helper__rspack_import_5)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_launcher_helper__rspack_import_5[__rspack_import_key];
97
+ for(const __rspack_import_key in _launcher_helper__rspack_import_6)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_launcher_helper__rspack_import_6[__rspack_import_key];
91
98
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
92
- var _chrome_path__rspack_import_6 = __webpack_require__("./chrome-path");
99
+ var _chrome_path__rspack_import_7 = __webpack_require__("./chrome-path");
93
100
  var __rspack_reexport = {};
94
- for(const __rspack_import_key in _chrome_path__rspack_import_6)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_chrome_path__rspack_import_6[__rspack_import_key];
101
+ for(const __rspack_import_key in _chrome_path__rspack_import_7)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_chrome_path__rspack_import_7[__rspack_import_key];
95
102
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
96
103
  })();
97
104
  for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
@@ -30,9 +30,7 @@ __webpack_require__.d(__webpack_exports__, {
30
30
  const img_namespaceObject = require("@midscene/shared/img");
31
31
  const external_zod_namespaceObject = require("zod");
32
32
  const external_zod_schema_utils_js_namespaceObject = require("../zod-schema-utils.js");
33
- function getErrorMessage(error) {
34
- return error instanceof Error ? error.message : String(error);
35
- }
33
+ const external_error_formatter_js_namespaceObject = require("./error-formatter.js");
36
34
  function describeActionForMCP(action) {
37
35
  const actionDesc = action.description || `Execute ${action.name} action`;
38
36
  if (!action.paramSchema) return `${action.name} action, ${actionDesc}`;
@@ -173,7 +171,7 @@ function serializeArgsToDescription(args) {
173
171
  return `${key}: "${value}"`;
174
172
  }).join(', ');
175
173
  } catch (error) {
176
- const errorMessage = getErrorMessage(error);
174
+ const errorMessage = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
177
175
  console.error('Error serializing args:', errorMessage);
178
176
  return `[args serialization failed: ${errorMessage}]`;
179
177
  }
@@ -241,7 +239,7 @@ async function captureScreenshotResult(agent, actionName, actionResult) {
241
239
  content
242
240
  };
243
241
  } catch (error) {
244
- const errorMessage = getErrorMessage(error);
242
+ const errorMessage = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
245
243
  console.error('Error capturing screenshot:', errorMessage);
246
244
  content[0] = {
247
245
  type: 'text',
@@ -323,13 +321,13 @@ function generateToolsFromActionSpace(actionSpace, getAgent) {
323
321
  try {
324
322
  actionResult = await executeAction(agent, action.name, normalizedArgs);
325
323
  } catch (error) {
326
- const errorMessage = getErrorMessage(error);
324
+ const errorMessage = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
327
325
  console.error(`Error executing action "${action.name}":`, errorMessage);
328
326
  return await captureFailureResult(agent, action.name, errorMessage);
329
327
  }
330
328
  return await captureScreenshotResult(agent, action.name, actionResult);
331
329
  } catch (error) {
332
- const errorMessage = getErrorMessage(error);
330
+ const errorMessage = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
333
331
  console.error(`Error in handler for "${action.name}":`, errorMessage);
334
332
  return createErrorResult(`Failed to get agent or execute action "${action.name}": ${errorMessage}`);
335
333
  }
@@ -359,7 +357,7 @@ function generateCommonTools(getAgent) {
359
357
  ]
360
358
  };
361
359
  } catch (error) {
362
- const errorMessage = getErrorMessage(error);
360
+ const errorMessage = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
363
361
  console.error('Error taking screenshot:', errorMessage);
364
362
  return createErrorResult(`Failed to capture screenshot: ${errorMessage}`);
365
363
  }
@@ -389,7 +387,7 @@ function generateCommonTools(getAgent) {
389
387
  }
390
388
  return screenshotResult;
391
389
  } catch (error) {
392
- const errorMessage = getErrorMessage(error);
390
+ const errorMessage = (0, external_error_formatter_js_namespaceObject.getErrorMessage)(error);
393
391
  console.error('Error executing act:', errorMessage);
394
392
  return createErrorResult(`Failed to execute act: ${errorMessage}`);
395
393
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Extract a human-readable message from an unknown thrown value.
3
+ *
4
+ * Many SDK/transport layers reject with structured objects (e.g.
5
+ * `{ code, message }`, `{ error: { message } }`, `{ cause: { message } }`)
6
+ * rather than `Error` instances. `String(obj)` collapses those to
7
+ * `"[object Object]"`, which is useless for diagnostics. This helper walks
8
+ * the common shapes, falls back to `JSON.stringify`, and finally to
9
+ * `Object.prototype.toString.call` so that callers always get something
10
+ * actionable in logs and surfaced tool results.
11
+ */
12
+ export declare function getErrorMessage(error: unknown): string;
@@ -1,5 +1,6 @@
1
1
  export * from './base-server';
2
2
  export * from './base-tools';
3
+ export * from './error-formatter';
3
4
  export * from './tool-generator';
4
5
  export * from './types';
5
6
  export * from './inject-report-html-plugin';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/shared",
3
- "version": "1.7.4",
3
+ "version": "1.7.5-beta-20260420031652.0",
4
4
  "repository": "https://github.com/web-infra-dev/midscene",
5
5
  "homepage": "https://midscenejs.com/",
6
6
  "types": "./dist/types/index.d.ts",
@@ -9,6 +9,7 @@ import express, {
9
9
  type Request,
10
10
  type Response,
11
11
  } from 'express';
12
+ import { getErrorMessage } from './error-formatter';
12
13
  import type { IMidsceneTools } from './types';
13
14
 
14
15
  export interface BaseMCPServerConfig {
@@ -119,7 +120,7 @@ export abstract class BaseMCPServer {
119
120
  try {
120
121
  await this.toolsManager.initTools();
121
122
  } catch (error: unknown) {
122
- const message = error instanceof Error ? error.message : String(error);
123
+ const message = getErrorMessage(error);
123
124
  console.error(`Failed to initialize tools: ${message}`);
124
125
  console.error('Tools will be initialized on first use');
125
126
  }
@@ -160,7 +161,7 @@ export abstract class BaseMCPServer {
160
161
  try {
161
162
  await this.mcpServer.connect(transport);
162
163
  } catch (error: unknown) {
163
- const message = error instanceof Error ? error.message : String(error);
164
+ const message = getErrorMessage(error);
164
165
  console.error(`Failed to connect MCP stdio transport: ${message}`);
165
166
  throw new Error(`Failed to initialize MCP stdio transport: ${message}`);
166
167
  }
@@ -282,7 +283,7 @@ export abstract class BaseMCPServer {
282
283
  .json({ error: 'Invalid session or GET without session' });
283
284
  }
284
285
  } catch (error: unknown) {
285
- const message = error instanceof Error ? error.message : String(error);
286
+ const message = getErrorMessage(error);
286
287
  const duration = Date.now() - startTime;
287
288
  console.error(
288
289
  `[${new Date().toISOString()}] [${requestId}] MCP request error after ${duration}ms: ${message}`,
@@ -336,8 +337,7 @@ export abstract class BaseMCPServer {
336
337
  try {
337
338
  await session.transport.close();
338
339
  } catch (error: unknown) {
339
- const message =
340
- error instanceof Error ? error.message : String(error);
340
+ const message = getErrorMessage(error);
341
341
  console.error(
342
342
  `Failed to close session ${session.transport.sessionId}: ${message}`,
343
343
  );
@@ -390,7 +390,7 @@ export abstract class BaseMCPServer {
390
390
  try {
391
391
  await this.mcpServer.connect(transport);
392
392
  } catch (error: unknown) {
393
- const message = error instanceof Error ? error.message : String(error);
393
+ const message = getErrorMessage(error);
394
394
  console.error(
395
395
  `[${new Date().toISOString()}] Failed to connect MCP transport: ${message}`,
396
396
  );
@@ -425,8 +425,7 @@ export abstract class BaseMCPServer {
425
425
  `[${new Date().toISOString()}] Session ${sid} cleaned up due to inactivity (remaining: ${sessions.size})`,
426
426
  );
427
427
  } catch (error: unknown) {
428
- const message =
429
- error instanceof Error ? error.message : String(error);
428
+ const message = getErrorMessage(error);
430
429
  console.error(
431
430
  `[${new Date().toISOString()}] Failed to close session ${sid} during cleanup: ${message}`,
432
431
  );
@@ -455,8 +454,7 @@ export abstract class BaseMCPServer {
455
454
  try {
456
455
  session.transport.close();
457
456
  } catch (error: unknown) {
458
- const message =
459
- error instanceof Error ? error.message : String(error);
457
+ const message = getErrorMessage(error);
460
458
  console.error(`Error closing session during shutdown: ${message}`);
461
459
  }
462
460
  }
@@ -475,7 +473,7 @@ export abstract class BaseMCPServer {
475
473
  this.performCleanup().finally(() => process.exit(1));
476
474
  }, 5000);
477
475
  } catch (error: unknown) {
478
- const message = error instanceof Error ? error.message : String(error);
476
+ const message = getErrorMessage(error);
479
477
  console.error(`Error closing HTTP server: ${message}`);
480
478
  this.performCleanup().finally(() => process.exit(1));
481
479
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Extract a human-readable message from an unknown thrown value.
3
+ *
4
+ * Many SDK/transport layers reject with structured objects (e.g.
5
+ * `{ code, message }`, `{ error: { message } }`, `{ cause: { message } }`)
6
+ * rather than `Error` instances. `String(obj)` collapses those to
7
+ * `"[object Object]"`, which is useless for diagnostics. This helper walks
8
+ * the common shapes, falls back to `JSON.stringify`, and finally to
9
+ * `Object.prototype.toString.call` so that callers always get something
10
+ * actionable in logs and surfaced tool results.
11
+ */
12
+ export function getErrorMessage(error: unknown): string {
13
+ if (error instanceof Error) return error.message;
14
+ if (error === null || error === undefined) return String(error);
15
+ if (typeof error !== 'object') return String(error);
16
+
17
+ const candidate = extractStringMessage(error);
18
+ if (candidate) return candidate;
19
+
20
+ try {
21
+ return JSON.stringify(error);
22
+ } catch {
23
+ return Object.prototype.toString.call(error);
24
+ }
25
+ }
26
+
27
+ function extractStringMessage(error: object): string | undefined {
28
+ const anyError = error as {
29
+ message?: unknown;
30
+ error?: { message?: unknown };
31
+ cause?: { message?: unknown };
32
+ };
33
+
34
+ if (typeof anyError.message === 'string' && anyError.message) {
35
+ return anyError.message;
36
+ }
37
+ if (
38
+ anyError.error &&
39
+ typeof anyError.error.message === 'string' &&
40
+ anyError.error.message
41
+ ) {
42
+ return anyError.error.message;
43
+ }
44
+ if (
45
+ anyError.cause &&
46
+ typeof anyError.cause.message === 'string' &&
47
+ anyError.cause.message
48
+ ) {
49
+ return anyError.cause.message;
50
+ }
51
+ return undefined;
52
+ }
package/src/mcp/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './base-server';
2
2
  export * from './base-tools';
3
+ export * from './error-formatter';
3
4
  export * from './tool-generator';
4
5
  export * from './types';
5
6
  export * from './inject-report-html-plugin';
@@ -6,6 +6,7 @@ import {
6
6
  isMidsceneLocatorField,
7
7
  unwrapZodField,
8
8
  } from '../zod-schema-utils';
9
+ import { getErrorMessage } from './error-formatter';
9
10
  import type {
10
11
  ActionSpaceItem,
11
12
  BaseAgent,
@@ -13,13 +14,6 @@ import type {
13
14
  ToolResult,
14
15
  } from './types';
15
16
 
16
- /**
17
- * Extract error message from unknown error type
18
- */
19
- function getErrorMessage(error: unknown): string {
20
- return error instanceof Error ? error.message : String(error);
21
- }
22
-
23
17
  /**
24
18
  * Generate MCP tool description from ActionSpaceItem
25
19
  * Format: "actionName action, description. Parameters: param1 (type) - desc; param2 (type) - desc"