@midscene/shared 1.9.8-beta-20260618014851.0 → 1.9.8

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 (75) hide show
  1. package/dist/es/cli/cli-runner.mjs +1 -1
  2. package/dist/es/env/parse-model-config.mjs +1 -1
  3. package/dist/es/env/types.mjs +5 -3
  4. package/dist/es/mcp/base-server.mjs +295 -0
  5. package/dist/es/{agent-tools → mcp}/base-tools.mjs +8 -1
  6. package/dist/es/{agent-tools → mcp}/chrome-path.mjs +3 -14
  7. package/dist/es/{agent-tools → mcp}/index.mjs +3 -0
  8. package/dist/es/mcp/inject-report-html-plugin.mjs +53 -0
  9. package/dist/es/mcp/launcher-helper.mjs +52 -0
  10. package/dist/es/{agent-tools → mcp}/tool-generator.mjs +3 -3
  11. package/dist/es/utils.mjs +6 -2
  12. package/dist/lib/cli/cli-runner.js +1 -1
  13. package/dist/lib/env/parse-model-config.js +1 -1
  14. package/dist/lib/env/types.js +10 -5
  15. package/dist/lib/mcp/base-server.js +345 -0
  16. package/dist/lib/{agent-tools → mcp}/base-tools.js +8 -1
  17. package/dist/lib/{agent-tools → mcp}/chrome-path.js +2 -13
  18. package/dist/lib/{agent-tools → mcp}/index.js +37 -16
  19. package/dist/lib/mcp/inject-report-html-plugin.js +98 -0
  20. package/dist/lib/mcp/launcher-helper.js +86 -0
  21. package/dist/lib/{agent-tools → mcp}/tool-generator.js +3 -3
  22. package/dist/lib/utils.js +15 -8
  23. package/dist/types/cli/cli-args.d.ts +1 -1
  24. package/dist/types/cli/cli-runner.d.ts +2 -2
  25. package/dist/types/env/types.d.ts +6 -8
  26. package/dist/types/key-alias-utils.d.ts +2 -2
  27. package/dist/types/mcp/base-server.d.ts +106 -0
  28. package/dist/types/{agent-tools → mcp}/base-tools.d.ts +13 -7
  29. package/dist/types/{agent-tools → mcp}/index.d.ts +3 -0
  30. package/dist/types/{agent-tools → mcp}/init-arg-utils.d.ts +3 -3
  31. package/dist/types/mcp/inject-report-html-plugin.d.ts +18 -0
  32. package/dist/types/mcp/launcher-helper.d.ts +94 -0
  33. package/dist/types/{agent-tools → mcp}/tool-defaults.d.ts +6 -5
  34. package/dist/types/{agent-tools → mcp}/tool-generator.d.ts +1 -1
  35. package/dist/types/{agent-tools → mcp}/types.d.ts +9 -4
  36. package/dist/types/utils.d.ts +1 -0
  37. package/package.json +8 -15
  38. package/src/cli/cli-args.ts +1 -1
  39. package/src/cli/cli-runner.ts +4 -4
  40. package/src/env/types.ts +5 -5
  41. package/src/key-alias-utils.ts +2 -2
  42. package/src/mcp/base-server.ts +529 -0
  43. package/src/{agent-tools → mcp}/base-tools.ts +33 -8
  44. package/src/{agent-tools → mcp}/chrome-path.ts +3 -20
  45. package/src/{agent-tools → mcp}/index.ts +3 -0
  46. package/src/{agent-tools → mcp}/init-arg-utils.ts +3 -3
  47. package/src/mcp/inject-report-html-plugin.ts +119 -0
  48. package/src/mcp/launcher-helper.ts +200 -0
  49. package/src/{agent-tools → mcp}/tool-defaults.ts +6 -5
  50. package/src/{agent-tools → mcp}/tool-generator.ts +6 -6
  51. package/src/{agent-tools → mcp}/types.ts +9 -4
  52. package/src/utils.ts +10 -1
  53. /package/dist/es/{agent-tools → mcp}/agent-behavior-init-args.mjs +0 -0
  54. /package/dist/es/{agent-tools → mcp}/cli-report-session.mjs +0 -0
  55. /package/dist/es/{agent-tools → mcp}/error-formatter.mjs +0 -0
  56. /package/dist/es/{agent-tools → mcp}/init-arg-utils.mjs +0 -0
  57. /package/dist/es/{agent-tools → mcp}/tool-defaults.mjs +0 -0
  58. /package/dist/es/{agent-tools → mcp}/types.mjs +0 -0
  59. /package/dist/es/{agent-tools → mcp}/user-prompt.mjs +0 -0
  60. /package/dist/lib/{agent-tools → mcp}/agent-behavior-init-args.js +0 -0
  61. /package/dist/lib/{agent-tools → mcp}/cli-report-session.js +0 -0
  62. /package/dist/lib/{agent-tools → mcp}/error-formatter.js +0 -0
  63. /package/dist/lib/{agent-tools → mcp}/init-arg-utils.js +0 -0
  64. /package/dist/lib/{agent-tools → mcp}/tool-defaults.js +0 -0
  65. /package/dist/lib/{agent-tools → mcp}/types.js +0 -0
  66. /package/dist/lib/{agent-tools → mcp}/user-prompt.js +0 -0
  67. /package/dist/types/{agent-tools → mcp}/agent-behavior-init-args.d.ts +0 -0
  68. /package/dist/types/{agent-tools → mcp}/chrome-path.d.ts +0 -0
  69. /package/dist/types/{agent-tools → mcp}/cli-report-session.d.ts +0 -0
  70. /package/dist/types/{agent-tools → mcp}/error-formatter.d.ts +0 -0
  71. /package/dist/types/{agent-tools → mcp}/user-prompt.d.ts +0 -0
  72. /package/src/{agent-tools → mcp}/agent-behavior-init-args.ts +0 -0
  73. /package/src/{agent-tools → mcp}/cli-report-session.ts +0 -0
  74. /package/src/{agent-tools → mcp}/error-formatter.ts +0 -0
  75. /package/src/{agent-tools → mcp}/user-prompt.ts +0 -0
@@ -2,8 +2,8 @@ import { existsSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import dotenv from "dotenv";
5
- import { stripBehaviorFlags } from "../agent-tools/tool-defaults.mjs";
6
5
  import { getDebug } from "../logger.mjs";
6
+ import { stripBehaviorFlags } from "../mcp/tool-defaults.mjs";
7
7
  import { canonicalizeCliArgKeys, formatCliValidationError, getCliOptionDisplay, parseCliArgs, parseValue } from "./cli-args.mjs";
8
8
  import { CLIError, reportCLIError } from "./cli-error.mjs";
9
9
  const debug = getDebug('cli-runner');
@@ -5,7 +5,7 @@ import { assert } from "../utils.mjs";
5
5
  import { maskConfig, parseJson } from "./helper.mjs";
6
6
  import { initDebugConfig } from "./init-debug.mjs";
7
7
  const MODEL_CONFIG_DOC_URL = 'https://midscenejs.com/model-common-config.html';
8
- const getCurrentVersion = ()=>"1.9.8-beta-20260618014851.0";
8
+ const getCurrentVersion = ()=>"1.9.8";
9
9
  const getInvalidModelFamilyMessage = (modelFamily)=>`Invalid MIDSCENE_MODEL_FAMILY value: ${modelFamily}. Current version v${getCurrentVersion()} accepts the following model families: ${MODEL_FAMILY_VALUES.join(', ')}. You can also visit ${MODEL_CONFIG_DOC_URL} for the latest configuration information.`;
10
10
  const KEYS_MAP = {
11
11
  insight: INSIGHT_MODEL_CONFIG_KEYS,
@@ -5,8 +5,9 @@ const MIDSCENE_DEBUG_MODEL_PROFILE = 'MIDSCENE_DEBUG_MODEL_PROFILE';
5
5
  const MIDSCENE_DEBUG_MODEL_RESPONSE = 'MIDSCENE_DEBUG_MODEL_RESPONSE';
6
6
  const MIDSCENE_DANGEROUSLY_PRINT_ALL_CONFIG = 'MIDSCENE_DANGEROUSLY_PRINT_ALL_CONFIG';
7
7
  const MIDSCENE_DEBUG_MODE = 'MIDSCENE_DEBUG_MODE';
8
- const MIDSCENE_CHROME_PATH = 'MIDSCENE_CHROME_PATH';
8
+ const MIDSCENE_MCP_USE_PUPPETEER_MODE = 'MIDSCENE_MCP_USE_PUPPETEER_MODE';
9
9
  const MIDSCENE_MCP_CHROME_PATH = 'MIDSCENE_MCP_CHROME_PATH';
10
+ const MIDSCENE_MCP_ANDROID_MODE = 'MIDSCENE_MCP_ANDROID_MODE';
10
11
  const DOCKER_CONTAINER = 'DOCKER_CONTAINER';
11
12
  const MIDSCENE_LANGSMITH_DEBUG = 'MIDSCENE_LANGSMITH_DEBUG';
12
13
  const MIDSCENE_LANGFUSE_DEBUG = 'MIDSCENE_LANGFUSE_DEBUG';
@@ -89,6 +90,8 @@ const BASIC_ENV_KEYS = [
89
90
  ];
90
91
  const BOOLEAN_ENV_KEYS = [
91
92
  MIDSCENE_CACHE,
93
+ MIDSCENE_MCP_USE_PUPPETEER_MODE,
94
+ MIDSCENE_MCP_ANDROID_MODE,
92
95
  MIDSCENE_LANGSMITH_DEBUG,
93
96
  MIDSCENE_LANGFUSE_DEBUG,
94
97
  MIDSCENE_REPORT_QUIET
@@ -107,7 +110,6 @@ const STRING_ENV_KEYS = [
107
110
  MIDSCENE_REPORT_TAG_NAME,
108
111
  MIDSCENE_PREFERRED_LANGUAGE,
109
112
  MATCH_BY_POSITION,
110
- MIDSCENE_CHROME_PATH,
111
113
  MIDSCENE_MCP_CHROME_PATH,
112
114
  DOCKER_CONTAINER
113
115
  ];
@@ -206,4 +208,4 @@ var types_UITarsModelVersion = /*#__PURE__*/ function(UITarsModelVersion) {
206
208
  UITarsModelVersion["DOUBAO_1_5_20B"] = "doubao-1.5-20B";
207
209
  return UITarsModelVersion;
208
210
  }({});
209
- export { ALL_ENV_KEYS, BASIC_ENV_KEYS, BOOLEAN_ENV_KEYS, DOCKER_CONTAINER, GLOBAL_ENV_KEYS, MATCH_BY_POSITION, MIDSCENE_ADB_PATH, MIDSCENE_ADB_REMOTE_HOST, MIDSCENE_ADB_REMOTE_PORT, MIDSCENE_ANDROID_IME_STRATEGY, MIDSCENE_CACHE, MIDSCENE_CACHE_MAX_FILENAME_LENGTH, MIDSCENE_CHROME_PATH, MIDSCENE_DANGEROUSLY_PRINT_ALL_CONFIG, MIDSCENE_DEBUG_MODE, MIDSCENE_DEBUG_MODEL_PROFILE, MIDSCENE_DEBUG_MODEL_RESPONSE, MIDSCENE_INSIGHT_MODEL_API_KEY, MIDSCENE_INSIGHT_MODEL_BASE_URL, MIDSCENE_INSIGHT_MODEL_EXTRA_BODY_JSON, MIDSCENE_INSIGHT_MODEL_FAMILY, MIDSCENE_INSIGHT_MODEL_HTTP_PROXY, MIDSCENE_INSIGHT_MODEL_INIT_CONFIG_JSON, MIDSCENE_INSIGHT_MODEL_NAME, MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET, MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT, MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED, MIDSCENE_INSIGHT_MODEL_RETRY_COUNT, MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL, MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY, MIDSCENE_INSIGHT_MODEL_TEMPERATURE, MIDSCENE_INSIGHT_MODEL_TIMEOUT, MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE, MIDSCENE_IOS_DEVICE_UDID, MIDSCENE_IOS_SIMULATOR_UDID, MIDSCENE_LANGFUSE_DEBUG, MIDSCENE_LANGSMITH_DEBUG, MIDSCENE_MCP_CHROME_PATH, MIDSCENE_MODEL_API_KEY, MIDSCENE_MODEL_BASE_URL, MIDSCENE_MODEL_EXTRA_BODY_JSON, MIDSCENE_MODEL_FAMILY, MIDSCENE_MODEL_HTTP_PROXY, MIDSCENE_MODEL_INIT_CONFIG_JSON, MIDSCENE_MODEL_NAME, MIDSCENE_MODEL_REASONING_BUDGET, MIDSCENE_MODEL_REASONING_EFFORT, MIDSCENE_MODEL_REASONING_ENABLED, MIDSCENE_MODEL_RETRY_COUNT, MIDSCENE_MODEL_RETRY_INTERVAL, MIDSCENE_MODEL_SOCKS_PROXY, MIDSCENE_MODEL_TEMPERATURE, MIDSCENE_MODEL_TIMEOUT, MIDSCENE_OPENAI_HTTP_PROXY, MIDSCENE_OPENAI_INIT_CONFIG_JSON, MIDSCENE_OPENAI_SOCKS_PROXY, MIDSCENE_PLANNING_MODEL_API_KEY, MIDSCENE_PLANNING_MODEL_BASE_URL, MIDSCENE_PLANNING_MODEL_EXTRA_BODY_JSON, MIDSCENE_PLANNING_MODEL_FAMILY, MIDSCENE_PLANNING_MODEL_HTTP_PROXY, MIDSCENE_PLANNING_MODEL_INIT_CONFIG_JSON, MIDSCENE_PLANNING_MODEL_NAME, MIDSCENE_PLANNING_MODEL_REASONING_BUDGET, MIDSCENE_PLANNING_MODEL_REASONING_EFFORT, MIDSCENE_PLANNING_MODEL_REASONING_ENABLED, MIDSCENE_PLANNING_MODEL_RETRY_COUNT, MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL, MIDSCENE_PLANNING_MODEL_SOCKS_PROXY, MIDSCENE_PLANNING_MODEL_TEMPERATURE, MIDSCENE_PLANNING_MODEL_TIMEOUT, MIDSCENE_PREFERRED_LANGUAGE, MIDSCENE_REPLANNING_CYCLE_LIMIT, MIDSCENE_REPORT_QUIET, MIDSCENE_REPORT_TAG_NAME, MIDSCENE_RUN_DIR, MIDSCENE_USE_DOUBAO_VISION, MIDSCENE_USE_GEMINI, MIDSCENE_USE_QWEN3_VL, MIDSCENE_USE_QWEN_VL, MIDSCENE_USE_VLM_UI_TARS, MIDSCENE_USE_VL_MODEL, MODEL_ENV_KEYS, MODEL_FAMILY_VALUES, NUMBER_ENV_KEYS, OPENAI_API_KEY, OPENAI_BASE_URL, STRING_ENV_KEYS, types_UITarsModelVersion as UITarsModelVersion, UNUSED_ENV_KEYS };
211
+ export { ALL_ENV_KEYS, BASIC_ENV_KEYS, BOOLEAN_ENV_KEYS, DOCKER_CONTAINER, GLOBAL_ENV_KEYS, MATCH_BY_POSITION, MIDSCENE_ADB_PATH, MIDSCENE_ADB_REMOTE_HOST, MIDSCENE_ADB_REMOTE_PORT, MIDSCENE_ANDROID_IME_STRATEGY, MIDSCENE_CACHE, MIDSCENE_CACHE_MAX_FILENAME_LENGTH, MIDSCENE_DANGEROUSLY_PRINT_ALL_CONFIG, MIDSCENE_DEBUG_MODE, MIDSCENE_DEBUG_MODEL_PROFILE, MIDSCENE_DEBUG_MODEL_RESPONSE, MIDSCENE_INSIGHT_MODEL_API_KEY, MIDSCENE_INSIGHT_MODEL_BASE_URL, MIDSCENE_INSIGHT_MODEL_EXTRA_BODY_JSON, MIDSCENE_INSIGHT_MODEL_FAMILY, MIDSCENE_INSIGHT_MODEL_HTTP_PROXY, MIDSCENE_INSIGHT_MODEL_INIT_CONFIG_JSON, MIDSCENE_INSIGHT_MODEL_NAME, MIDSCENE_INSIGHT_MODEL_REASONING_BUDGET, MIDSCENE_INSIGHT_MODEL_REASONING_EFFORT, MIDSCENE_INSIGHT_MODEL_REASONING_ENABLED, MIDSCENE_INSIGHT_MODEL_RETRY_COUNT, MIDSCENE_INSIGHT_MODEL_RETRY_INTERVAL, MIDSCENE_INSIGHT_MODEL_SOCKS_PROXY, MIDSCENE_INSIGHT_MODEL_TEMPERATURE, MIDSCENE_INSIGHT_MODEL_TIMEOUT, MIDSCENE_IOS_DEVICE_CLASS_OVERRIDE, MIDSCENE_IOS_DEVICE_UDID, MIDSCENE_IOS_SIMULATOR_UDID, MIDSCENE_LANGFUSE_DEBUG, MIDSCENE_LANGSMITH_DEBUG, MIDSCENE_MCP_ANDROID_MODE, MIDSCENE_MCP_CHROME_PATH, MIDSCENE_MCP_USE_PUPPETEER_MODE, MIDSCENE_MODEL_API_KEY, MIDSCENE_MODEL_BASE_URL, MIDSCENE_MODEL_EXTRA_BODY_JSON, MIDSCENE_MODEL_FAMILY, MIDSCENE_MODEL_HTTP_PROXY, MIDSCENE_MODEL_INIT_CONFIG_JSON, MIDSCENE_MODEL_NAME, MIDSCENE_MODEL_REASONING_BUDGET, MIDSCENE_MODEL_REASONING_EFFORT, MIDSCENE_MODEL_REASONING_ENABLED, MIDSCENE_MODEL_RETRY_COUNT, MIDSCENE_MODEL_RETRY_INTERVAL, MIDSCENE_MODEL_SOCKS_PROXY, MIDSCENE_MODEL_TEMPERATURE, MIDSCENE_MODEL_TIMEOUT, MIDSCENE_OPENAI_HTTP_PROXY, MIDSCENE_OPENAI_INIT_CONFIG_JSON, MIDSCENE_OPENAI_SOCKS_PROXY, MIDSCENE_PLANNING_MODEL_API_KEY, MIDSCENE_PLANNING_MODEL_BASE_URL, MIDSCENE_PLANNING_MODEL_EXTRA_BODY_JSON, MIDSCENE_PLANNING_MODEL_FAMILY, MIDSCENE_PLANNING_MODEL_HTTP_PROXY, MIDSCENE_PLANNING_MODEL_INIT_CONFIG_JSON, MIDSCENE_PLANNING_MODEL_NAME, MIDSCENE_PLANNING_MODEL_REASONING_BUDGET, MIDSCENE_PLANNING_MODEL_REASONING_EFFORT, MIDSCENE_PLANNING_MODEL_REASONING_ENABLED, MIDSCENE_PLANNING_MODEL_RETRY_COUNT, MIDSCENE_PLANNING_MODEL_RETRY_INTERVAL, MIDSCENE_PLANNING_MODEL_SOCKS_PROXY, MIDSCENE_PLANNING_MODEL_TEMPERATURE, MIDSCENE_PLANNING_MODEL_TIMEOUT, MIDSCENE_PREFERRED_LANGUAGE, MIDSCENE_REPLANNING_CYCLE_LIMIT, MIDSCENE_REPORT_QUIET, MIDSCENE_REPORT_TAG_NAME, MIDSCENE_RUN_DIR, MIDSCENE_USE_DOUBAO_VISION, MIDSCENE_USE_GEMINI, MIDSCENE_USE_QWEN3_VL, MIDSCENE_USE_QWEN_VL, MIDSCENE_USE_VLM_UI_TARS, MIDSCENE_USE_VL_MODEL, MODEL_ENV_KEYS, MODEL_FAMILY_VALUES, NUMBER_ENV_KEYS, OPENAI_API_KEY, OPENAI_BASE_URL, STRING_ENV_KEYS, types_UITarsModelVersion as UITarsModelVersion, UNUSED_ENV_KEYS };
@@ -0,0 +1,295 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { setIsMcp } from "@midscene/shared/utils";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
6
+ import express from "express";
7
+ import { getErrorMessage } from "./error-formatter.mjs";
8
+ import { TOOL_BEHAVIOR_FLAGS, mergeToolDefaults, resolveToolDefaults } from "./tool-defaults.mjs";
9
+ function _define_property(obj, key, value) {
10
+ if (key in obj) Object.defineProperty(obj, key, {
11
+ value: value,
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true
15
+ });
16
+ else obj[key] = value;
17
+ return obj;
18
+ }
19
+ const CLI_ARGS_CONFIG = {
20
+ mode: {
21
+ type: 'string',
22
+ default: 'stdio'
23
+ },
24
+ port: {
25
+ type: 'string',
26
+ default: '3000'
27
+ },
28
+ host: {
29
+ type: 'string',
30
+ default: 'localhost'
31
+ },
32
+ ...Object.fromEntries(TOOL_BEHAVIOR_FLAGS.map((flag)=>[
33
+ flag.cli,
34
+ {
35
+ type: 'boolean'
36
+ }
37
+ ]))
38
+ };
39
+ function launchMCPServer(server, args) {
40
+ server.setToolDefaults(resolveToolDefaults((cli)=>true === args[cli]));
41
+ if ('http' === args.mode) return server.launchHttp({
42
+ port: Number.parseInt(args.port || '3000', 10),
43
+ host: args.host || 'localhost'
44
+ });
45
+ return server.launch();
46
+ }
47
+ const SESSION_TIMEOUT_MS = 1800000;
48
+ const CLEANUP_INTERVAL_MS = 300000;
49
+ const MAX_SESSIONS = 100;
50
+ class BaseMCPServer {
51
+ setToolDefaults(toolDefaults) {
52
+ this.toolDefaults = mergeToolDefaults(this.toolDefaults, toolDefaults);
53
+ }
54
+ async initializeToolsManager() {
55
+ setIsMcp(true);
56
+ this.toolsManager = this.providedToolsManager || this.createToolsManager();
57
+ this.toolsManager.setToolDefaults?.(this.toolDefaults);
58
+ try {
59
+ await this.toolsManager.initTools();
60
+ } catch (error) {
61
+ const message = getErrorMessage(error);
62
+ console.error(`Failed to initialize tools: ${message}`);
63
+ console.error('Tools will be initialized on first use');
64
+ }
65
+ this.toolsManager.attachToServer(this.mcpServer);
66
+ }
67
+ async performCleanup() {
68
+ console.error(`${this.config.name} closing...`);
69
+ this.mcpServer.close();
70
+ await this.toolsManager?.destroy?.().catch(console.error);
71
+ }
72
+ async launch() {
73
+ console.log = (...args)=>{
74
+ console.error('[LOG]', ...args);
75
+ };
76
+ console.info = (...args)=>{
77
+ console.error('[INFO]', ...args);
78
+ };
79
+ console.debug = (...args)=>{
80
+ console.error('[DEBUG]', ...args);
81
+ };
82
+ await this.initializeToolsManager();
83
+ const transport = new StdioServerTransport();
84
+ try {
85
+ await this.mcpServer.connect(transport);
86
+ } catch (error) {
87
+ const message = getErrorMessage(error);
88
+ console.error(`Failed to connect MCP stdio transport: ${message}`);
89
+ throw new Error(`Failed to initialize MCP stdio transport: ${message}`);
90
+ }
91
+ let isShuttingDown = false;
92
+ const cleanup = ()=>{
93
+ if (isShuttingDown) return;
94
+ isShuttingDown = true;
95
+ console.error(`${this.config.name} shutting down...`);
96
+ this.performCleanup().finally(()=>process.exit(0));
97
+ };
98
+ process.on('uncaughtException', (error)=>{
99
+ if ('EPIPE' === error.code || 'ERR_STREAM_DESTROYED' === error.code) return void cleanup();
100
+ console.error(`[${this.config.name}] Uncaught Exception:`, error);
101
+ console.error('Stack:', error.stack);
102
+ });
103
+ process.on('unhandledRejection', (reason)=>{
104
+ console.error(`[${this.config.name}] Unhandled Rejection:`, reason);
105
+ if (reason instanceof Error) console.error('Stack:', reason.stack);
106
+ });
107
+ process.stdin.on('close', cleanup);
108
+ process.stdin.on('end', cleanup);
109
+ process.stdout.on('error', cleanup);
110
+ process.once('SIGINT', cleanup);
111
+ process.once('SIGTERM', cleanup);
112
+ process.once('SIGHUP', cleanup);
113
+ return {
114
+ close: async ()=>{
115
+ this.performCleanup();
116
+ }
117
+ };
118
+ }
119
+ async launchHttp(options) {
120
+ if (!Number.isInteger(options.port) || options.port < 1 || options.port > 65535) throw new Error(`Invalid port number: ${options.port}. Port must be between 1 and 65535.`);
121
+ await this.initializeToolsManager();
122
+ const app = express();
123
+ app.use(express.json({
124
+ limit: '10mb'
125
+ }));
126
+ const sessions = new Map();
127
+ app.all('/mcp', async (req, res)=>{
128
+ const startTime = Date.now();
129
+ const requestId = randomUUID().substring(0, 8);
130
+ try {
131
+ const rawSessionId = req.headers['mcp-session-id'];
132
+ const sessionId = Array.isArray(rawSessionId) ? rawSessionId[0] : rawSessionId;
133
+ let session = sessionId ? sessions.get(sessionId) : void 0;
134
+ if (!session && 'POST' === req.method) {
135
+ if (sessions.size >= MAX_SESSIONS) {
136
+ console.error(`[${new Date().toISOString()}] [${requestId}] Session limit reached: ${sessions.size}/${MAX_SESSIONS}`);
137
+ res.status(503).json({
138
+ error: 'Too many active sessions',
139
+ message: 'Server is at maximum capacity. Please try again later.'
140
+ });
141
+ return;
142
+ }
143
+ session = await this.createHttpSession(sessions);
144
+ console.log(`[${new Date().toISOString()}] [${requestId}] New session created: ${session.transport.sessionId}`);
145
+ }
146
+ if (session) {
147
+ session.lastAccessedAt = new Date();
148
+ await session.transport.handleRequest(req, res, req.body);
149
+ const duration = Date.now() - startTime;
150
+ console.log(`[${new Date().toISOString()}] [${requestId}] Request completed in ${duration}ms`);
151
+ } else {
152
+ console.error(`[${new Date().toISOString()}] [${requestId}] Invalid session or GET without session`);
153
+ res.status(400).json({
154
+ error: 'Invalid session or GET without session'
155
+ });
156
+ }
157
+ } catch (error) {
158
+ const message = getErrorMessage(error);
159
+ const duration = Date.now() - startTime;
160
+ console.error(`[${new Date().toISOString()}] [${requestId}] MCP request error after ${duration}ms: ${message}`);
161
+ if (!res.headersSent) res.status(500).json({
162
+ error: 'Internal server error',
163
+ message: 'Failed to process MCP request'
164
+ });
165
+ }
166
+ });
167
+ const host = options.host || 'localhost';
168
+ const server = app.listen(options.port, host, ()=>{
169
+ console.log(`${this.config.name} HTTP server listening on http://${host}:${options.port}/mcp`);
170
+ }).on('error', (error)=>{
171
+ if ('EADDRINUSE' === error.code) console.error(`ERROR: Port ${options.port} is already in use.\nPlease try a different port: --port=<number>\nExample: --mode=http --port=${options.port + 1}`);
172
+ else if ('EACCES' === error.code) console.error(`ERROR: Permission denied to bind to port ${options.port}.\nPorts below 1024 require root/admin privileges.\nPlease use a port above 1024 or run with elevated privileges.`);
173
+ else console.error(`ERROR: Failed to start HTTP server on ${host}:${options.port}\nReason: ${error.message}\nCode: ${error.code || 'unknown'}`);
174
+ process.exit(1);
175
+ });
176
+ const cleanupInterval = this.startSessionCleanup(sessions);
177
+ this.setupHttpShutdownHandlers(server, sessions, cleanupInterval);
178
+ return {
179
+ port: options.port,
180
+ host,
181
+ close: async ()=>{
182
+ clearInterval(cleanupInterval);
183
+ for (const session of sessions.values())try {
184
+ await session.transport.close();
185
+ } catch (error) {
186
+ const message = getErrorMessage(error);
187
+ console.error(`Failed to close session ${session.transport.sessionId}: ${message}`);
188
+ }
189
+ sessions.clear();
190
+ return new Promise((resolve)=>{
191
+ server.close(async (err)=>{
192
+ if (err) console.error('Error closing HTTP server:', err);
193
+ await this.performCleanup();
194
+ resolve();
195
+ });
196
+ });
197
+ }
198
+ };
199
+ }
200
+ async createHttpSession(sessions) {
201
+ const transport = new StreamableHTTPServerTransport({
202
+ sessionIdGenerator: ()=>randomUUID(),
203
+ onsessioninitialized: (sid)=>{
204
+ sessions.set(sid, {
205
+ transport,
206
+ createdAt: new Date(),
207
+ lastAccessedAt: new Date()
208
+ });
209
+ console.log(`[${new Date().toISOString()}] Session ${sid} initialized (total: ${sessions.size})`);
210
+ }
211
+ });
212
+ transport.onclose = ()=>{
213
+ if (transport.sessionId) {
214
+ sessions.delete(transport.sessionId);
215
+ console.log(`[${new Date().toISOString()}] Session ${transport.sessionId} closed (remaining: ${sessions.size})`);
216
+ }
217
+ };
218
+ try {
219
+ await this.mcpServer.connect(transport);
220
+ } catch (error) {
221
+ const message = getErrorMessage(error);
222
+ console.error(`[${new Date().toISOString()}] Failed to connect MCP transport: ${message}`);
223
+ if (transport.sessionId) sessions.delete(transport.sessionId);
224
+ throw new Error(`Failed to initialize MCP session: ${message}`);
225
+ }
226
+ return {
227
+ transport,
228
+ createdAt: new Date(),
229
+ lastAccessedAt: new Date()
230
+ };
231
+ }
232
+ startSessionCleanup(sessions) {
233
+ return setInterval(()=>{
234
+ const now = Date.now();
235
+ for (const [sid, session] of sessions)if (now - session.lastAccessedAt.getTime() > SESSION_TIMEOUT_MS) try {
236
+ session.transport.close();
237
+ sessions.delete(sid);
238
+ console.log(`[${new Date().toISOString()}] Session ${sid} cleaned up due to inactivity (remaining: ${sessions.size})`);
239
+ } catch (error) {
240
+ const message = getErrorMessage(error);
241
+ console.error(`[${new Date().toISOString()}] Failed to close session ${sid} during cleanup: ${message}`);
242
+ sessions.delete(sid);
243
+ }
244
+ }, CLEANUP_INTERVAL_MS);
245
+ }
246
+ setupHttpShutdownHandlers(server, sessions, cleanupInterval) {
247
+ const cleanup = ()=>{
248
+ console.error(`${this.config.name} shutting down...`);
249
+ clearInterval(cleanupInterval);
250
+ for (const session of sessions.values())try {
251
+ session.transport.close();
252
+ } catch (error) {
253
+ const message = getErrorMessage(error);
254
+ console.error(`Error closing session during shutdown: ${message}`);
255
+ }
256
+ sessions.clear();
257
+ try {
258
+ server.close(()=>{
259
+ this.performCleanup().finally(()=>process.exit(0));
260
+ });
261
+ setTimeout(()=>{
262
+ console.error('Forcefully shutting down after timeout');
263
+ this.performCleanup().finally(()=>process.exit(1));
264
+ }, 5000);
265
+ } catch (error) {
266
+ const message = getErrorMessage(error);
267
+ console.error(`Error closing HTTP server: ${message}`);
268
+ this.performCleanup().finally(()=>process.exit(1));
269
+ }
270
+ };
271
+ process.once('SIGINT', cleanup);
272
+ process.once('SIGTERM', cleanup);
273
+ }
274
+ getServer() {
275
+ return this.mcpServer;
276
+ }
277
+ getToolsManager() {
278
+ return this.toolsManager;
279
+ }
280
+ constructor(config, toolsManager){
281
+ _define_property(this, "mcpServer", void 0);
282
+ _define_property(this, "toolsManager", void 0);
283
+ _define_property(this, "config", void 0);
284
+ _define_property(this, "providedToolsManager", void 0);
285
+ _define_property(this, "toolDefaults", {});
286
+ this.config = config;
287
+ this.mcpServer = new McpServer({
288
+ name: config.name,
289
+ version: config.version,
290
+ description: config.description
291
+ });
292
+ this.providedToolsManager = toolsManager;
293
+ }
294
+ }
295
+ export { BaseMCPServer, CLI_ARGS_CONFIG, launchMCPServer };
@@ -15,7 +15,7 @@ function _define_property(obj, key, value) {
15
15
  else obj[key] = value;
16
16
  return obj;
17
17
  }
18
- const debug = getDebug('agent-tools:base-tools');
18
+ const debug = getDebug('mcp:base-tools');
19
19
  class BaseMidsceneTools {
20
20
  getInitArgKeys() {
21
21
  return this.initArgSpec ? Object.keys(this.initArgSpec.shape) : [];
@@ -104,6 +104,12 @@ class BaseMidsceneTools {
104
104
  this.toolDefinitions.push(...actionTools, ...commonTools);
105
105
  debug('Total tools prepared:', this.toolDefinitions.length);
106
106
  }
107
+ attachToServer(server) {
108
+ this.mcpServer = server;
109
+ if (0 === this.toolDefinitions.length) debug('Warning: No tools to register. Tools may be initialized lazily.');
110
+ for (const toolDef of this.toolDefinitions)this.mcpServer.tool(toolDef.name, toolDef.description, toolDef.schema, toolDef.handler);
111
+ debug('Registered', this.toolDefinitions.length, 'tools');
112
+ }
107
113
  async destroy() {
108
114
  await this.agent?.destroy?.();
109
115
  }
@@ -149,6 +155,7 @@ class BaseMidsceneTools {
149
155
  };
150
156
  }
151
157
  constructor(){
158
+ _define_property(this, "mcpServer", void 0);
152
159
  _define_property(this, "agent", void 0);
153
160
  _define_property(this, "toolDefinitions", []);
154
161
  _define_property(this, "toolDefaults", {});
@@ -1,10 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
- import { MIDSCENE_CHROME_PATH, MIDSCENE_MCP_CHROME_PATH, globalConfigManager } from "../env/index.mjs";
3
- import { getDebug } from "../logger.mjs";
4
- const warnChromePath = getDebug('agent-tools:chrome-path', {
5
- console: true
6
- });
7
- let hasWarnedLegacyChromePath = false;
2
+ import { MIDSCENE_MCP_CHROME_PATH, globalConfigManager } from "../env/index.mjs";
8
3
  function getSystemChromePath() {
9
4
  const platform = process.platform;
10
5
  const chromePaths = {
@@ -31,16 +26,10 @@ function getSystemChromePath() {
31
26
  return paths.find((p)=>existsSync(p));
32
27
  }
33
28
  function resolveChromePath() {
34
- const primaryEnvPath = globalConfigManager.getEnvConfigValue(MIDSCENE_CHROME_PATH);
35
- const legacyEnvPath = globalConfigManager.getEnvConfigValue(MIDSCENE_MCP_CHROME_PATH);
36
- const envPath = primaryEnvPath || legacyEnvPath;
37
- if (!primaryEnvPath && legacyEnvPath && !hasWarnedLegacyChromePath) {
38
- warnChromePath('MIDSCENE_MCP_CHROME_PATH is deprecated. Use MIDSCENE_CHROME_PATH instead.');
39
- hasWarnedLegacyChromePath = true;
40
- }
29
+ const envPath = globalConfigManager.getEnvConfigValue(MIDSCENE_MCP_CHROME_PATH);
41
30
  if (envPath && 'auto' !== envPath && existsSync(envPath)) return envPath;
42
31
  const systemPath = getSystemChromePath();
43
32
  if (systemPath) return systemPath;
44
- throw new Error('Chrome not found. Install Google Chrome or set MIDSCENE_CHROME_PATH environment variable.');
33
+ throw new Error('Chrome not found. Install Google Chrome or set MIDSCENE_MCP_CHROME_PATH environment variable.');
45
34
  }
46
35
  export { getSystemChromePath, resolveChromePath };
@@ -1,3 +1,4 @@
1
+ export * from "./base-server.mjs";
1
2
  export * from "./base-tools.mjs";
2
3
  export * from "./tool-defaults.mjs";
3
4
  export * from "./agent-behavior-init-args.mjs";
@@ -5,4 +6,6 @@ export * from "./init-arg-utils.mjs";
5
6
  export * from "./error-formatter.mjs";
6
7
  export * from "./tool-generator.mjs";
7
8
  export * from "./types.mjs";
9
+ export * from "./inject-report-html-plugin.mjs";
10
+ export * from "./launcher-helper.mjs";
8
11
  export * from "./chrome-path.mjs";
@@ -0,0 +1,53 @@
1
+ import node_fs from "node:fs";
2
+ import node_path from "node:path";
3
+ const MAGIC_STRING = 'REPLACE_ME_WITH_REPORT_HTML';
4
+ const REPLACED_MARK = '/*REPORT_HTML_REPLACED*/';
5
+ const REG_EXP_FOR_REPLACE = /\/\*REPORT_HTML_REPLACED\*\/.*/;
6
+ function injectReportHtmlFromCore(packageDir) {
7
+ return {
8
+ name: 'inject-report-html-from-core',
9
+ setup (api) {
10
+ api.onAfterBuild(()=>{
11
+ const coreUtilsPath = node_path.resolve(packageDir, '..', 'core', 'dist', 'lib', 'utils.js');
12
+ if (!node_fs.existsSync(coreUtilsPath)) return void console.warn('[inject-report-html] @midscene/core dist not found, skipping');
13
+ const coreContent = node_fs.readFileSync(coreUtilsPath, 'utf-8');
14
+ if (!coreContent.includes(REPLACED_MARK)) return void console.warn('[inject-report-html] HTML not found in core dist. Ensure report builds first.');
15
+ const markerIndex = coreContent.indexOf(REPLACED_MARK);
16
+ const jsonStart = markerIndex + REPLACED_MARK.length;
17
+ let jsonEnd = jsonStart;
18
+ if ('"' === coreContent[jsonStart]) {
19
+ jsonEnd = jsonStart + 1;
20
+ while(jsonEnd < coreContent.length)if ('\\' === coreContent[jsonEnd]) jsonEnd += 2;
21
+ else if ('"' === coreContent[jsonEnd]) {
22
+ jsonEnd += 1;
23
+ break;
24
+ } else jsonEnd += 1;
25
+ }
26
+ const jsonString = coreContent.slice(jsonStart, jsonEnd);
27
+ if (!jsonString || jsonString.length < 10) return void console.warn('[inject-report-html] Failed to extract HTML from core');
28
+ const finalContent = `${REPLACED_MARK}${jsonString}`;
29
+ const distDir = node_path.join(packageDir, 'dist');
30
+ if (!node_fs.existsSync(distDir)) return;
31
+ const jsFiles = node_fs.readdirSync(distDir).filter((f)=>f.endsWith('.js'));
32
+ let injectedCount = 0;
33
+ for (const file of jsFiles){
34
+ const filePath = node_path.join(distDir, file);
35
+ const content = node_fs.readFileSync(filePath, 'utf-8');
36
+ if (content.includes(REPLACED_MARK)) {
37
+ if (REG_EXP_FOR_REPLACE.test(content)) {
38
+ node_fs.writeFileSync(filePath, content.replace(REG_EXP_FOR_REPLACE, ()=>finalContent));
39
+ console.log(`[inject-report-html] Updated: ${file}`);
40
+ injectedCount++;
41
+ }
42
+ } else if (content.includes(`'${MAGIC_STRING}'`)) {
43
+ node_fs.writeFileSync(filePath, content.replace(`'${MAGIC_STRING}'`, ()=>finalContent));
44
+ console.log(`[inject-report-html] Injected: ${file}`);
45
+ injectedCount++;
46
+ }
47
+ }
48
+ if (injectedCount > 0) console.log(`[inject-report-html] Completed: ${injectedCount} file(s)`);
49
+ });
50
+ }
51
+ };
52
+ }
53
+ export { injectReportHtmlFromCore };
@@ -0,0 +1,52 @@
1
+ function createMCPServerLauncher(config) {
2
+ const { agent, platformName, ToolsManagerClass, MCPServerClass } = config;
3
+ function validateAgent() {
4
+ const device = agent.interface;
5
+ if (!device) throw new Error(`Agent must have an 'interface' property that references the underlying device.
6
+ Please ensure your agent instance is properly initialized with a device interface.
7
+ Expected: agent.interface to be defined, but got: ${typeof device}
8
+ Solution: Check that your agent constructor properly sets the interface property.`);
9
+ }
10
+ function createToolsManager() {
11
+ const toolsManager = new ToolsManagerClass();
12
+ toolsManager.agent = agent;
13
+ return toolsManager;
14
+ }
15
+ function logStartupInfo(mode, additionalInfo) {
16
+ const device = agent.interface;
17
+ console.log(`Starting Midscene ${platformName} MCP Server (${mode})...`);
18
+ console.log(`Agent: ${agent.constructor.name}`);
19
+ console.log(`Device: ${device.constructor.name}`);
20
+ if (additionalInfo?.port !== void 0) console.log(`Port: ${additionalInfo.port}`);
21
+ if (additionalInfo?.host) console.log(`Host: ${additionalInfo.host}`);
22
+ }
23
+ return {
24
+ async launch (options = {}) {
25
+ const { verbose = true } = options;
26
+ validateAgent();
27
+ if (verbose) logStartupInfo('stdio');
28
+ const toolsManager = createToolsManager();
29
+ const server = new MCPServerClass(toolsManager);
30
+ const result = await server.launch();
31
+ if (verbose) console.log(`${platformName} MCP Server started (stdio mode)`);
32
+ return result;
33
+ },
34
+ async launchHttp (options) {
35
+ const { port, host = 'localhost', verbose = true } = options;
36
+ validateAgent();
37
+ if (verbose) logStartupInfo('HTTP', {
38
+ port,
39
+ host
40
+ });
41
+ const toolsManager = createToolsManager();
42
+ const server = new MCPServerClass(toolsManager);
43
+ const result = await server.launchHttp({
44
+ port,
45
+ host
46
+ });
47
+ if (verbose) console.log(`${platformName} MCP Server started on http://${result.host}:${result.port}/mcp`);
48
+ return result;
49
+ }
50
+ };
51
+ }
52
+ export { createMCPServerLauncher };
@@ -3,7 +3,7 @@ import { z } from "zod";
3
3
  import { getZodDescription, getZodTypeName, isMidsceneLocatorField, unwrapZodField } from "../zod-schema-utils.mjs";
4
4
  import { getErrorMessage } from "./error-formatter.mjs";
5
5
  import { composeUserPrompt, promptInputExtraSchema } from "./user-prompt.mjs";
6
- function describeActionForTool(action) {
6
+ function describeActionForMCP(action) {
7
7
  const actionDesc = action.description || `Execute ${action.name} action`;
8
8
  if (!action.paramSchema) return `${action.name} action, ${actionDesc}`;
9
9
  const shape = getZodObjectShape(action.paramSchema);
@@ -75,7 +75,7 @@ function extractActionSchema(paramSchema, actionName) {
75
75
  const shape = getZodObjectShape(paramSchema);
76
76
  if (!shape) {
77
77
  const typeName = paramSchema?._def?.typeName ?? 'unknown';
78
- throw new Error(`Action "${actionName}" declared a non-object paramSchema (${typeName}). CLI tool schemas must be a ZodObject (e.g. z.object({ uri: z.string() })) or undefined. Wrap primitive fields in an object schema.`);
78
+ throw new Error(`Action "${actionName}" declared a non-object paramSchema (${typeName}). CLI and MCP tool schemas must be a ZodObject (e.g. z.object({ uri: z.string() })) or undefined. Wrap primitive fields in an object schema.`);
79
79
  }
80
80
  return Object.fromEntries(Object.entries(shape).map(([key, value])=>transformSchemaField(key, value)));
81
81
  }
@@ -327,7 +327,7 @@ function generateToolsFromActionSpace(actionSpace, getAgent, sanitizeArgs = (arg
327
327
  };
328
328
  return {
329
329
  name: action.name,
330
- description: describeActionForTool(action),
330
+ description: describeActionForMCP(action),
331
331
  schema,
332
332
  cli: initArgCliMetadata,
333
333
  handler: async (args)=>{
package/dist/es/utils.mjs CHANGED
@@ -34,8 +34,12 @@ function generateHashId(rect, content = '') {
34
34
  function assert(condition, message) {
35
35
  if (!condition) throw new Error(message || 'Assertion failed');
36
36
  }
37
+ let isMcp = false;
38
+ function setIsMcp(value) {
39
+ isMcp = value;
40
+ }
37
41
  function logMsg(...message) {
38
- console.log(...message);
42
+ if (!isMcp) console.log(...message);
39
43
  }
40
44
  async function repeat(times, fn) {
41
45
  for(let i = 0; i < times; i++)await fn(i);
@@ -65,4 +69,4 @@ function mergeAndNormalizeAppNameMapping(defaultMapping, userMapping) {
65
69
  if (userMapping) for (const [key, value] of Object.entries(userMapping))result[normalizeForComparison(key)] = value;
66
70
  return result;
67
71
  }
68
- export { antiEscapeScriptTag, assert, escapeScriptTag, generateHashId, ifInBrowser, ifInNode, ifInWorker, isPlainObject, logMsg, mergeAndNormalizeAppNameMapping, normalizeForComparison, repeat, replaceIllegalPathCharsAndSpace, uuid };
72
+ export { antiEscapeScriptTag, assert, escapeScriptTag, generateHashId, ifInBrowser, ifInNode, ifInWorker, isPlainObject, logMsg, mergeAndNormalizeAppNameMapping, normalizeForComparison, repeat, replaceIllegalPathCharsAndSpace, setIsMcp, uuid };
@@ -45,8 +45,8 @@ const external_node_os_namespaceObject = require("node:os");
45
45
  const external_node_path_namespaceObject = require("node:path");
46
46
  const external_dotenv_namespaceObject = require("dotenv");
47
47
  var external_dotenv_default = /*#__PURE__*/ __webpack_require__.n(external_dotenv_namespaceObject);
48
- const tool_defaults_js_namespaceObject = require("../agent-tools/tool-defaults.js");
49
48
  const external_logger_js_namespaceObject = require("../logger.js");
49
+ const tool_defaults_js_namespaceObject = require("../mcp/tool-defaults.js");
50
50
  const external_cli_args_js_namespaceObject = require("./cli-args.js");
51
51
  const external_cli_error_js_namespaceObject = require("./cli-error.js");
52
52
  const debug = (0, external_logger_js_namespaceObject.getDebug)('cli-runner');
@@ -37,7 +37,7 @@ const external_utils_js_namespaceObject = require("../utils.js");
37
37
  const external_helper_js_namespaceObject = require("./helper.js");
38
38
  const external_init_debug_js_namespaceObject = require("./init-debug.js");
39
39
  const MODEL_CONFIG_DOC_URL = 'https://midscenejs.com/model-common-config.html';
40
- const getCurrentVersion = ()=>"1.9.8-beta-20260618014851.0";
40
+ const getCurrentVersion = ()=>"1.9.8";
41
41
  const getInvalidModelFamilyMessage = (modelFamily)=>`Invalid MIDSCENE_MODEL_FAMILY value: ${modelFamily}. Current version v${getCurrentVersion()} accepts the following model families: ${external_types_js_namespaceObject.MODEL_FAMILY_VALUES.join(', ')}. You can also visit ${MODEL_CONFIG_DOC_URL} for the latest configuration information.`;
42
42
  const KEYS_MAP = {
43
43
  insight: external_constants_js_namespaceObject.INSIGHT_MODEL_CONFIG_KEYS,