Skip to content

Commit e87b0a2

Browse files
committed
feat(web-bridge-mcp): add initial implementation of web bridge MCP server and tools
1 parent 26ac176 commit e87b0a2

File tree

13 files changed

+965
-0
lines changed

13 files changed

+965
-0
lines changed
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import { z } from '@midscene/core';
2+
import {
3+
type BaseAgent,
4+
BaseMidsceneTools,
5+
type ToolDefinition,
6+
} from '@midscene/shared/mcp';
7+
8+
const DEPRECATION_MESSAGE = `
9+
⚠️ DEPRECATION NOTICE ⚠️
10+
11+
The @midscene/mcp package is deprecated and no longer maintained.
12+
13+
Please migrate to one of the platform-specific MCP packages:
14+
• @midscene/web-bridge-mcp - For web browser automation (Bridge mode)
15+
• @midscene/android-mcp - For Android device automation
16+
• @midscene/ios-mcp - For iOS device automation
17+
18+
These new packages provide better performance, stability, and platform-specific features.
19+
20+
Migration Guide:
21+
1. Uninstall @midscene/mcp
22+
2. Install the appropriate platform-specific package
23+
3. Update your MCP configuration to use the new package name
24+
25+
For more information, visit: https://midscenejs.com/mcp-migration
26+
`;
27+
28+
/**
29+
* Mock tools definitions that will return deprecation notices
30+
*/
31+
export const tools = {
32+
// Common tools
33+
wait_for: {
34+
name: 'wait_for',
35+
description:
36+
'DEPRECATED: Waits until a specified condition, described in natural language, becomes true on the page. Use platform-specific MCP packages instead.',
37+
},
38+
assert: {
39+
name: 'assert',
40+
description:
41+
'DEPRECATED: Asserts that a specified condition, described in natural language, is true on the page. Use platform-specific MCP packages instead.',
42+
},
43+
take_screenshot: {
44+
name: 'take_screenshot',
45+
description:
46+
'DEPRECATED: Captures a screenshot of the currently active page. Use platform-specific MCP packages instead.',
47+
},
48+
49+
// Web-specific tools
50+
web_connect: {
51+
name: 'web_connect',
52+
description:
53+
'DEPRECATED: Connect to web page by opening new tab with URL. Use @midscene/web-bridge-mcp instead.',
54+
},
55+
56+
// Android-specific tools
57+
midscene_android_connect: {
58+
name: 'midscene_android_connect',
59+
description:
60+
'DEPRECATED: Connect to an Android device via ADB for automation. Use @midscene/android-mcp instead.',
61+
},
62+
midscene_android_list_devices: {
63+
name: 'midscene_android_list_devices',
64+
description:
65+
'DEPRECATED: List all connected Android devices available for automation. Use @midscene/android-mcp instead.',
66+
},
67+
68+
// iOS-specific tools
69+
midscene_ios_connect: {
70+
name: 'midscene_ios_connect',
71+
description:
72+
'DEPRECATED: Connect to an iOS device via WebDriverAgent for automation. Use @midscene/ios-mcp instead.',
73+
},
74+
midscene_ios_list_devices: {
75+
name: 'midscene_ios_list_devices',
76+
description:
77+
'DEPRECATED: List all connected iOS devices available for automation. Use @midscene/ios-mcp instead.',
78+
},
79+
80+
// AI methods from Agent class
81+
aiTap: {
82+
name: 'aiTap',
83+
description:
84+
'DEPRECATED: AI-powered tap/click action on elements. Use platform-specific MCP packages instead.',
85+
},
86+
aiRightClick: {
87+
name: 'aiRightClick',
88+
description:
89+
'DEPRECATED: AI-powered right-click action on elements. Use platform-specific MCP packages instead.',
90+
},
91+
aiDoubleClick: {
92+
name: 'aiDoubleClick',
93+
description:
94+
'DEPRECATED: AI-powered double-click action on elements. Use platform-specific MCP packages instead.',
95+
},
96+
aiHover: {
97+
name: 'aiHover',
98+
description:
99+
'DEPRECATED: AI-powered hover action on elements. Use platform-specific MCP packages instead.',
100+
},
101+
aiInput: {
102+
name: 'aiInput',
103+
description:
104+
'DEPRECATED: AI-powered input text into form fields. Use platform-specific MCP packages instead.',
105+
},
106+
aiKeyboardPress: {
107+
name: 'aiKeyboardPress',
108+
description:
109+
'DEPRECATED: AI-powered keyboard press action. Use platform-specific MCP packages instead.',
110+
},
111+
aiScroll: {
112+
name: 'aiScroll',
113+
description:
114+
'DEPRECATED: AI-powered scroll action on page or elements. Use platform-specific MCP packages instead.',
115+
},
116+
aiAct: {
117+
name: 'aiAct',
118+
description:
119+
'DEPRECATED: AI-powered natural language action execution. Use platform-specific MCP packages instead.',
120+
},
121+
aiAction: {
122+
name: 'aiAction',
123+
description:
124+
'DEPRECATED: Alias for aiAct. Use platform-specific MCP packages instead.',
125+
},
126+
aiQuery: {
127+
name: 'aiQuery',
128+
description:
129+
'DEPRECATED: AI-powered data extraction from the page. Use platform-specific MCP packages instead.',
130+
},
131+
aiBoolean: {
132+
name: 'aiBoolean',
133+
description:
134+
'DEPRECATED: AI-powered boolean query from the page. Use platform-specific MCP packages instead.',
135+
},
136+
aiNumber: {
137+
name: 'aiNumber',
138+
description:
139+
'DEPRECATED: AI-powered number extraction from the page. Use platform-specific MCP packages instead.',
140+
},
141+
aiString: {
142+
name: 'aiString',
143+
description:
144+
'DEPRECATED: AI-powered string extraction from the page. Use platform-specific MCP packages instead.',
145+
},
146+
aiAsk: {
147+
name: 'aiAsk',
148+
description:
149+
'DEPRECATED: AI-powered question answering from the page. Use platform-specific MCP packages instead.',
150+
},
151+
aiLocate: {
152+
name: 'aiLocate',
153+
description:
154+
'DEPRECATED: AI-powered element location on the page. Use platform-specific MCP packages instead.',
155+
},
156+
aiAssert: {
157+
name: 'aiAssert',
158+
description:
159+
'DEPRECATED: AI-powered assertion on page state. Use platform-specific MCP packages instead.',
160+
},
161+
aiWaitFor: {
162+
name: 'aiWaitFor',
163+
description:
164+
'DEPRECATED: AI-powered wait for condition to be true. Use platform-specific MCP packages instead.',
165+
},
166+
ai: {
167+
name: 'ai',
168+
description:
169+
'DEPRECATED: Shorthand for aiAct. Use platform-specific MCP packages instead.',
170+
},
171+
};
172+
173+
/**
174+
* Deprecation tools manager that registers mock tools
175+
* All tools return deprecation notices
176+
*/
177+
export class DeprecationMidsceneTools extends BaseMidsceneTools {
178+
protected createTemporaryDevice() {
179+
// Return a minimal mock device that satisfies the interface
180+
// This device is never actually used since all tools return deprecation messages
181+
return {
182+
async getActionSpace() {
183+
return [];
184+
},
185+
} as any;
186+
}
187+
188+
protected async ensureAgent(_initParam?: string): Promise<BaseAgent> {
189+
// Return a minimal mock agent
190+
// This agent is never actually used since all tools return deprecation messages
191+
return {
192+
async getActionSpace() {
193+
return [];
194+
},
195+
} as BaseAgent;
196+
}
197+
198+
protected preparePlatformTools(): ToolDefinition[] {
199+
// Convert tools object to ToolDefinition array
200+
return Object.values(tools).map((tool) => ({
201+
name: tool.name,
202+
description: tool.description,
203+
schema: {
204+
_deprecated: z.boolean().optional().describe('This tool is deprecated'),
205+
},
206+
handler: async () => ({
207+
content: [
208+
{
209+
type: 'text' as const,
210+
text: DEPRECATION_MESSAGE,
211+
},
212+
],
213+
}),
214+
autoDestroy: false,
215+
}));
216+
}
217+
}

packages/web-bridge-mcp/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Midscene MCP
2+
3+
docs: https://midscenejs.com/mcp.html
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "@midscene/web-bridge-mcp",
3+
"version": "1.0.0",
4+
"description": "Midscene MCP Server for Web automation (Bridge mode)",
5+
"bin": "dist/index.js",
6+
"files": ["dist"],
7+
"main": "./dist/server.js",
8+
"types": "./dist/src/server.d.ts",
9+
"exports": {
10+
".": {
11+
"types": "./dist/src/server.d.ts",
12+
"default": "./dist/server.js"
13+
},
14+
"./server": {
15+
"types": "./dist/src/server.d.ts",
16+
"default": "./dist/server.js"
17+
}
18+
},
19+
"scripts": {
20+
"build": "rslib build",
21+
"dev": "npm run build:watch",
22+
"build:watch": "rslib build --watch",
23+
"mcp-playground": "npx @modelcontextprotocol/inspector node ./dist/index.js",
24+
"test": "vitest run",
25+
"inspect": "node scripts/inspect.mjs"
26+
},
27+
"devDependencies": {
28+
"@midscene/core": "workspace:*",
29+
"@midscene/report": "workspace:*",
30+
"@midscene/shared": "workspace:*",
31+
"@midscene/web": "workspace:*",
32+
"@modelcontextprotocol/inspector": "^0.16.3",
33+
"@modelcontextprotocol/sdk": "1.10.2",
34+
"@rslib/core": "^0.11.2",
35+
"@types/node": "^18.0.0",
36+
"dotenv": "^16.4.5",
37+
"puppeteer-core": "24.2.0",
38+
"typescript": "^5.8.3",
39+
"vitest": "3.0.5"
40+
},
41+
"dependencies": {
42+
"@silvia-odwyer/photon": "0.3.3",
43+
"@silvia-odwyer/photon-node": "0.3.3",
44+
"bufferutil": "4.0.9",
45+
"sharp": "^0.34.3",
46+
"utf-8-validate": "6.0.5"
47+
},
48+
"license": "MIT"
49+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { defineConfig } from '@rslib/core';
2+
import { version } from './package.json';
3+
4+
export default defineConfig({
5+
source: {
6+
define: {
7+
__VERSION__: `'${version}'`,
8+
},
9+
entry: {
10+
index: './src/index.ts',
11+
server: './src/server.ts',
12+
},
13+
},
14+
output: {
15+
externals: [
16+
(data, cb) => {
17+
if (
18+
data.context?.includes('/node_modules/ws/lib') &&
19+
['bufferutil', 'utf-8-validate'].includes(data.request as string)
20+
) {
21+
cb(undefined, data.request);
22+
}
23+
cb();
24+
},
25+
'@silvia-odwyer/photon',
26+
'@silvia-odwyer/photon-node',
27+
'@modelcontextprotocol/sdk',
28+
],
29+
},
30+
lib: [
31+
{
32+
format: 'cjs',
33+
syntax: 'es2021',
34+
dts: {
35+
bundle: false,
36+
distPath: 'dist',
37+
},
38+
output: {
39+
distPath: {
40+
root: 'dist',
41+
},
42+
},
43+
},
44+
],
45+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env node
2+
import { parseArgs } from 'node:util';
3+
import { type CLIArgs, CLI_ARGS_CONFIG } from '@midscene/shared/mcp';
4+
import { WebMCPServer } from './server.js';
5+
6+
const { values } = parseArgs({ options: CLI_ARGS_CONFIG });
7+
const args = values as CLIArgs;
8+
9+
const server = new WebMCPServer();
10+
11+
if (args.mode === 'http') {
12+
server
13+
.launchHttp({
14+
port: Number.parseInt(args.port || '3000', 10),
15+
host: args.host || 'localhost',
16+
})
17+
.catch(console.error);
18+
} else {
19+
server.launch().catch(console.error);
20+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { BaseMCPServer } from '@midscene/shared/mcp';
2+
import { WebMidsceneTools } from './web-tools.js';
3+
4+
declare const __VERSION__: string;
5+
6+
/**
7+
* Web MCP Server class
8+
* Usage:
9+
* const server = new WebMCPServer();
10+
* await server.launch();
11+
*/
12+
export class WebMCPServer extends BaseMCPServer {
13+
constructor() {
14+
super({
15+
name: '@midscene/web-bridge-mcp',
16+
version: __VERSION__,
17+
description: 'Midscene MCP Server for Web automation (Bridge mode)',
18+
});
19+
}
20+
21+
protected createToolsManager(): WebMidsceneTools {
22+
return new WebMidsceneTools();
23+
}
24+
}

0 commit comments

Comments
 (0)