-
Notifications
You must be signed in to change notification settings - Fork 750
feat(mcp): simplify MCP tool architecture and unify platform tools #1507
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
quanru
wants to merge
19
commits into
v1
Choose a base branch
from
feat/mcp-simplify-tools
base: v1
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
67d24b9
feat(mcp): simplify MCP tool architecture and unify platform tools
quanru 77d2bd1
refactor(shared): remove zod dependency and clean up README files acrβ¦
quanru f3a6a8b
Update packages/shared/src/mcp/base-tools.ts
quanru e0ae1ca
Update packages/mcp/src/web-tools.ts
quanru 9cfd87e
fix(android): update launchUri to launch in AndroidMidsceneTools
quanru 3299734
feat(android, ios): implement AI-driven app loading and update connecβ¦
quanru b843d59
fix(mcp): update type definitions path in package.json and enhance rsβ¦
quanru bcf7a1a
feat(mcp): add default action spaces for Android, iOS, and Web tools;β¦
quanru c94a329
feat(mcp): implement temporary device creation for Android, iOS, and β¦
quanru 8f47ac4
refactor(mcp): remove default action space methods from Android, iOS,β¦
quanru 1c579af
feat(mcp): add HTTP launch support for Android, iOS, and Web MCP servβ¦
quanru f900bc6
refactor(mcp): streamline command line argument parsing and launch loβ¦
quanru a7fbb7d
refactor(mcp): enhance type definitions and improve error handling inβ¦
quanru 5563421
refactor(mcp): delegate web MCP server launch to the main MCP packageβ¦
quanru e18c94b
feat(mcp): refactor MCP server structure and tools
quanru d56be2d
refactor(mcp): improve bridge mode initialization and test organization
quanru 25fb2ee
fix(mcp): address GitHub Copilot review comments from PR #1507
quanru 1637a49
refactor(mcp): improve code quality following yuyutaotao style guide
quanru d380d75
refactor(mcp): centralize app loading timeout constants in shared pacβ¦
quanru File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # Midscene MCP | ||
|
|
||
| docs: https://midscenejs.com/mcp.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| { | ||
| "name": "@midscene/android-mcp", | ||
| "version": "1.0.0", | ||
| "description": "Midscene MCP Server for Android automation", | ||
| "bin": "dist/index.js", | ||
| "files": ["dist"], | ||
| "main": "./dist/server.js", | ||
| "types": "./dist/server.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/server.d.ts", | ||
| "default": "./dist/server.js" | ||
| }, | ||
| "./server": { | ||
| "types": "./dist/server.d.ts", | ||
| "default": "./dist/server.js" | ||
| } | ||
| }, | ||
| "scripts": { | ||
| "build": "rslib build", | ||
| "dev": "npm run build:watch", | ||
| "build:watch": "rslib build --watch", | ||
| "mcp-playground": "npx @modelcontextprotocol/inspector node ./dist/index.js", | ||
| "test": "vitest run", | ||
| "inspect": "node scripts/inspect.mjs" | ||
| }, | ||
| "devDependencies": { | ||
| "@midscene/android": "workspace:*", | ||
| "@midscene/core": "workspace:*", | ||
| "@midscene/shared": "workspace:*", | ||
| "@modelcontextprotocol/inspector": "^0.16.3", | ||
| "@modelcontextprotocol/sdk": "1.10.2", | ||
| "@rslib/core": "^0.11.2", | ||
| "@types/node": "^18.0.0", | ||
| "dotenv": "^16.4.5", | ||
| "typescript": "^5.8.3", | ||
| "vitest": "3.0.5" | ||
| }, | ||
| "dependencies": { | ||
| "@silvia-odwyer/photon": "0.3.3", | ||
| "@silvia-odwyer/photon-node": "0.3.3", | ||
| "bufferutil": "4.0.9", | ||
| "sharp": "^0.34.3", | ||
| "utf-8-validate": "6.0.5" | ||
| }, | ||
| "license": "MIT" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { defineConfig } from '@rslib/core'; | ||
| import { version } from './package.json'; | ||
|
|
||
| export default defineConfig({ | ||
| source: { | ||
| define: { | ||
| __VERSION__: `'${version}'`, | ||
| }, | ||
| entry: { | ||
| index: './src/index.ts', | ||
| server: './src/server.ts', | ||
| }, | ||
| }, | ||
| output: { | ||
| externals: [ | ||
| (data, cb) => { | ||
| if ( | ||
| data.context?.includes('/node_modules/ws/lib') && | ||
| ['bufferutil', 'utf-8-validate'].includes(data.request as string) | ||
| ) { | ||
| cb(undefined, data.request); | ||
| } | ||
| cb(); | ||
| }, | ||
| '@silvia-odwyer/photon', | ||
| '@silvia-odwyer/photon-node', | ||
| '@modelcontextprotocol/sdk', | ||
| ], | ||
| }, | ||
| lib: [ | ||
| { | ||
| format: 'cjs', | ||
| syntax: 'es2021', | ||
| output: { | ||
| distPath: { | ||
| root: 'dist', | ||
| }, | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import { type AndroidAgent, agentFromAdbDevice } from '@midscene/android'; | ||
| import { z } from '@midscene/core'; | ||
| import { getDebug } from '@midscene/shared/logger'; | ||
| import { | ||
| type BaseAgent, | ||
| BaseMidsceneTools, | ||
| type ToolDefinition, | ||
| defaultAppLoadingCheckIntervalMs, | ||
| defaultAppLoadingTimeoutMs, | ||
| } from '@midscene/shared/mcp'; | ||
|
|
||
| const debug = getDebug('mcp:android-tools'); | ||
|
|
||
| /** | ||
| * Android-specific tools manager | ||
| * Extends BaseMidsceneTools to provide Android ADB device connection tools | ||
| */ | ||
| export class AndroidMidsceneTools extends BaseMidsceneTools { | ||
| protected createTemporaryDevice() { | ||
| // Use require to avoid circular dependency with @midscene/android | ||
| const { AndroidDevice } = require('@midscene/android'); | ||
| // Create minimal temporary instance without connecting to device | ||
| // The constructor doesn't establish ADB connection | ||
| return new AndroidDevice('temp-for-action-space', {}); | ||
| } | ||
|
|
||
| protected async ensureAgent(deviceId?: string): Promise<AndroidAgent> { | ||
| if (this.agent && deviceId) { | ||
| // If a specific deviceId is requested and we have an agent, | ||
| // destroy it to create a new one with the new device | ||
| try { | ||
| await this.agent.destroy?.(); | ||
| } catch (error) { | ||
| debug('Failed to destroy agent during cleanup:', error); | ||
| } | ||
| this.agent = undefined; | ||
| } | ||
|
|
||
| if (this.agent) { | ||
| return this.agent as unknown as AndroidAgent; | ||
| } | ||
|
|
||
| debug('Creating Android agent with deviceId:', deviceId || 'auto-detect'); | ||
| const agent = await agentFromAdbDevice(deviceId); | ||
| this.agent = agent as unknown as BaseAgent; | ||
| return agent; | ||
| } | ||
|
|
||
| /** | ||
| * Provide Android-specific platform tools | ||
| */ | ||
| protected preparePlatformTools(): ToolDefinition[] { | ||
| return [ | ||
| { | ||
| name: 'android_connect', | ||
| description: | ||
| 'Connect to Android device and optionally launch an app. If deviceId not provided, uses the first available device.', | ||
| schema: { | ||
| deviceId: z | ||
| .string() | ||
| .optional() | ||
| .describe('Android device ID (from adb devices)'), | ||
| uri: z | ||
| .string() | ||
| .optional() | ||
| .describe( | ||
| 'Optional URI to launch app (e.g., market://details?id=com.example.app)', | ||
| ), | ||
| }, | ||
| handler: async ({ | ||
| deviceId, | ||
| uri, | ||
| }: { | ||
| deviceId?: string; | ||
| uri?: string; | ||
| }) => { | ||
| const agent = await this.ensureAgent(deviceId); | ||
|
|
||
| // If URI is provided, launch the app | ||
| if (uri) { | ||
| await agent.page.launch(uri); | ||
|
|
||
| // Wait for app to finish loading using AI-driven polling | ||
| await agent.aiWaitFor( | ||
| 'the app has finished loading and is ready to use', | ||
| { | ||
| timeoutMs: defaultAppLoadingTimeoutMs, | ||
| checkIntervalMs: defaultAppLoadingCheckIntervalMs, | ||
| }, | ||
| ); | ||
| } | ||
|
|
||
| const screenshot = await agent.page.screenshotBase64(); | ||
|
|
||
| return { | ||
| content: [ | ||
| { | ||
| type: 'text', | ||
| text: `Connected to Android device${deviceId ? `: ${deviceId}` : ' (auto-detected)'}${uri ? ` and launched: ${uri} (app ready)` : ''}`, | ||
| }, | ||
| ...this.buildScreenshotContent(screenshot), | ||
| ], | ||
| isError: false, | ||
| }; | ||
| }, | ||
| autoDestroy: false, // Keep agent alive for subsequent operations | ||
| }, | ||
| ]; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #!/usr/bin/env node | ||
| import { parseArgs } from 'node:util'; | ||
| import { | ||
| type CLIArgs, | ||
| CLI_ARGS_CONFIG, | ||
| launchMCPServer, | ||
| } from '@midscene/shared/mcp'; | ||
| import { AndroidMCPServer } from './server.js'; | ||
|
|
||
| const { values } = parseArgs({ options: CLI_ARGS_CONFIG }); | ||
|
|
||
| launchMCPServer(new AndroidMCPServer(), values as CLIArgs).catch(console.error); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { BaseMCPServer } from '@midscene/shared/mcp'; | ||
| import { AndroidMidsceneTools } from './android-tools.js'; | ||
|
|
||
| declare const __VERSION__: string; | ||
|
|
||
| /** | ||
| * Android MCP Server | ||
| * Provides MCP tools for Android automation through ADB | ||
| */ | ||
| export class AndroidMCPServer extends BaseMCPServer { | ||
| constructor() { | ||
| super({ | ||
| name: '@midscene/android-mcp', | ||
| version: __VERSION__, | ||
| description: 'Midscene MCP Server for Android automation', | ||
| }); | ||
| } | ||
|
|
||
| protected createToolsManager(): AndroidMidsceneTools { | ||
| return new AndroidMidsceneTools(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "extends": "../shared/tsconfig.base.json", | ||
| "compilerOptions": { | ||
| "lib": ["ES2021"], | ||
| "noEmit": true, | ||
| "useDefineForClassFields": true, | ||
| "allowImportingTsExtensions": true, | ||
| "resolveJsonModule": true | ||
| }, | ||
| "include": ["src"] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { defineConfig } from 'vitest/config'; | ||
|
|
||
| export default defineConfig({ | ||
| test: { | ||
| globals: true, | ||
| environment: 'node', | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # @midscene/ios-mcp | ||
|
|
||
| docs: https://midscenejs.com/mcp.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| { | ||
| "name": "@midscene/ios-mcp", | ||
| "version": "1.0.0", | ||
| "description": "Midscene MCP Server for iOS automation", | ||
| "bin": "dist/index.js", | ||
| "files": ["dist"], | ||
| "main": "./dist/server.js", | ||
| "types": "./dist/server.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/server.d.ts", | ||
| "default": "./dist/server.js" | ||
| }, | ||
| "./server": { | ||
| "types": "./dist/server.d.ts", | ||
| "default": "./dist/server.js" | ||
| } | ||
| }, | ||
| "scripts": { | ||
| "build": "rslib build", | ||
| "dev": "npm run build:watch", | ||
| "build:watch": "rslib build --watch", | ||
| "mcp-playground": "npx @modelcontextprotocol/inspector node ./dist/index.js", | ||
| "test": "vitest run", | ||
| "inspect": "node scripts/inspect.mjs" | ||
| }, | ||
| "devDependencies": { | ||
| "@midscene/ios": "workspace:*", | ||
| "@midscene/core": "workspace:*", | ||
| "@midscene/shared": "workspace:*", | ||
| "@modelcontextprotocol/inspector": "^0.16.3", | ||
| "@modelcontextprotocol/sdk": "1.10.2", | ||
| "@rslib/core": "^0.11.2", | ||
| "@types/node": "^18.0.0", | ||
| "dotenv": "^16.4.5", | ||
| "typescript": "^5.8.3", | ||
| "vitest": "3.0.5" | ||
| }, | ||
| "dependencies": { | ||
| "@silvia-odwyer/photon": "0.3.3", | ||
| "@silvia-odwyer/photon-node": "0.3.3", | ||
| "bufferutil": "4.0.9", | ||
| "sharp": "^0.34.3", | ||
| "utf-8-validate": "6.0.5" | ||
| }, | ||
| "license": "MIT" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { defineConfig } from '@rslib/core'; | ||
| import { version } from './package.json'; | ||
|
|
||
| export default defineConfig({ | ||
| source: { | ||
| define: { | ||
| __VERSION__: `'${version}'`, | ||
| }, | ||
| entry: { | ||
| index: './src/index.ts', | ||
| server: './src/server.ts', | ||
| }, | ||
| }, | ||
| output: { | ||
| externals: [ | ||
| (data, cb) => { | ||
| if ( | ||
| data.context?.includes('/node_modules/ws/lib') && | ||
| ['bufferutil', 'utf-8-validate'].includes(data.request as string) | ||
| ) { | ||
| cb(undefined, data.request); | ||
| } | ||
| cb(); | ||
| }, | ||
| '@silvia-odwyer/photon', | ||
| '@silvia-odwyer/photon-node', | ||
quanru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| '@modelcontextprotocol/sdk', | ||
| ], | ||
| }, | ||
| lib: [ | ||
| { | ||
| format: 'cjs', | ||
| syntax: 'es2021', | ||
| output: { | ||
| distPath: { | ||
| root: 'dist', | ||
| }, | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.