Skip to content

Commit 51e75cf

Browse files
authored
Retire MCP packages and keep Skills support (#2694)
* refactor(workflow): retire mcp packages * test(playground): fix local execution adapter mock types * docs(site): note final mcp-supported version
1 parent 71e9398 commit 51e75cf

163 files changed

Lines changed: 466 additions & 6058 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTRIBUTING.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ git config user.email "SOME_EMAIL@example.com"
8787
Playwright/Puppeteer integration and main web e2e coverage. The package name is `@midscene/web`.
8888
- `packages/shared`: shared utilities used across the monorepo. The package name is `@midscene/shared`.
8989
- `packages/{android,ios,computer,harmony}`: platform runtimes. Matching
90-
`*-mcp` and `*-playground` packages live alongside them. The package names are `@midscene/android`, `@midscene/ios`, `@midscene/computer`, `@midscene/harmony`.
90+
`*-playground` packages live alongside them. The package names are `@midscene/android`, `@midscene/ios`, `@midscene/computer`, `@midscene/harmony`.
9191
- `packages/visualizer` and `apps/report`: report rendering and viewer UI.
9292
- `apps/site`: documentation site. The Nx project name is `doc`, not `site`.
9393
- `apps/chrome-extension`, `apps/playground`, `apps/report`,
@@ -288,14 +288,13 @@ Every commit **must** include a scope. The scope must be one of the following:
288288
* `llm`
289289
* `playwright`
290290
* `puppeteer`
291-
* `mcp`
292291
* `bridge`
293292
* *(All top-level directories in the apps and packages directories)*
294293
* *(Consider adding other relevant top-level packages or areas here if needed)*
295294

296295
**Examples:**
297296

298-
* `feat(mcp): add screenshot tool with element selection`
297+
* `feat(bridge): add screenshot tool with element selection`
299298
* `fix(android): correct adb connection issue on windows`
300299
* `refactor(llm): simplify prompt generation logic`
301300
* `chore(workflow): update commitlint configuration`

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ Use [Midscene Skills](https://github.com/web-infra-dev/midscene-skills) to contr
4040
* [iOS Automation - Auto-like the first @midscene_ai tweet](https://midscenejs.com/showcases#ios)
4141
* [Android Automation - DCar: Xiaomi SU7 specs](https://midscenejs.com/showcases#android)
4242
* [Android Automation - Booking a hotel for Christmas](https://midscenejs.com/showcases#android)
43-
* [MCP Integration - Midscene MCP UI prepatch release](https://midscenejs.com/showcases#mcp)
4443
* [robotic arm + vision + voice for in-vehicle testing](https://midscenejs.com/showcases#community-showcases)
4544

4645
## 💡 Why Midscene
@@ -50,13 +49,13 @@ Most UI automation — including AI tools that read the DOM or the accessibility
5049
- **Less maintenance** — no selectors to chase when the UI changes.
5150
- **Reach every element and surface** — if a human can see it, Midscene can target it, even with no semantic annotations, on `<canvas>`, native apps, and cross-origin iframes.
5251
- **Assert what users actually see** — verify colors, highlights, layout, and rendered state, not just whether a DOM node exists.
53-
- **Two ways to test** — add Midscene to your [Playwright](https://midscenejs.com/integrate-with-playwright) / Vitest suite, or let an AI agent test autonomously via [Skills](https://midscenejs.com/skills) and [MCP](https://midscenejs.com/mcp).
52+
- **Two ways to test** — add Midscene to your [Playwright](https://midscenejs.com/integrate-with-playwright) / Vitest suite, or let an AI agent test autonomously via [Skills](https://midscenejs.com/skills).
5453

5554
Midscene is built for UI testing first, but the same vision-driven engine handles any UI automation task.
5655

5756
## 💡 What you can automate
5857

59-
Midscene works anywhere you can take a screenshot — web browsers, Android, iOS, HarmonyOS, desktop apps, and [any custom interface](https://midscenejs.com/integrate-with-any-interface) — all through one API. Write automation with the JavaScript SDK or in YAML, hand it to AI agents via [Skills](https://midscenejs.com/skills) and [MCP](https://midscenejs.com/mcp), and look up every method (`aiAct`, `aiQuery`, `aiAssert`, and more) in the [API reference](https://midscenejs.com/api).
58+
Midscene works anywhere you can take a screenshot — web browsers, Android, iOS, HarmonyOS, desktop apps, and [any custom interface](https://midscenejs.com/integrate-with-any-interface) — all through one API. Write automation with the JavaScript SDK or in YAML, hand it to AI agents via [Skills](https://midscenejs.com/skills), and look up every method (`aiAct`, `aiQuery`, `aiAssert`, and more) in the [API reference](https://midscenejs.com/api).
6059

6160
## 🚀 Get started
6261

README.zh.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
* [iOS 自动化 - 自动点赞 @midscene_ai 的第一条推文](https://midscenejs.com/zh/showcases#ios)
4141
* [Android 自动化 - 懂车帝:查看小米 SU7 参数](https://midscenejs.com/zh/showcases#android)
4242
* [Android 自动化 - 预订圣诞节酒店](https://midscenejs.com/zh/showcases#android)
43-
* [MCP 集成 - Midscene MCP UI prepatch 版本发布](https://midscenejs.com/zh/showcases#mcp)
4443
* [车机测试中的机械臂 + 视觉 + 语音方案](https://midscenejs.com/zh/showcases#community-showcases)
4544

4645
## 💡 为什么选择 Midscene
@@ -52,15 +51,15 @@ Midscene 仅凭截图工作。你只需用自然语言描述每一步:
5251
- **更低的维护成本**:UI 变化时,无需再追着改选择器。
5352
- **触达每个元素与界面**:只要人眼能看到,Midscene 就能定位。即使元素没有语义化标注,或位于 `<canvas>`、原生应用、跨域 iframe 上,也可以定位。
5453
- **校验用户真正看到的效果**:验证颜色、高亮、布局与渲染状态,而不只是判断 DOM 节点是否存在。
55-
- **两种测试方式**:接入你的 [Playwright](https://midscenejs.com/zh/integrate-with-playwright) / Vitest 测试,或让 AI Agent 通过 [Skills](https://midscenejs.com/zh/skills) [MCP](https://midscenejs.com/zh/mcp) 自主测试。
54+
- **两种测试方式**:接入你的 [Playwright](https://midscenejs.com/zh/integrate-with-playwright) / Vitest 测试,或让 AI Agent 通过 [Skills](https://midscenejs.com/zh/skills) 自主测试。
5655

5756
Midscene 首先为 UI 测试而生,但同一套视觉驱动引擎也能胜任任意 UI 自动化任务。
5857

5958
## 💡 能自动化什么
6059

6160
只要能截图,Midscene 就能工作。Web 浏览器、Android、iOS、HarmonyOS、桌面应用,以及[任意自定义界面](https://midscenejs.com/zh/integrate-with-any-interface),全部通过同一套 API。
6261

63-
你可以用 JavaScript SDK 或 YAML 编写自动化,也可以通过 [Skills](https://midscenejs.com/zh/skills) [MCP](https://midscenejs.com/zh/mcp) 交给 AI Agent。所有方法都可以在 [API 参考](https://midscenejs.com/zh/api) 中查阅,包括 `aiAct``aiQuery``aiAssert`
62+
你可以用 JavaScript SDK 或 YAML 编写自动化,也可以通过 [Skills](https://midscenejs.com/zh/skills) 交给 AI Agent。所有方法都可以在 [API 参考](https://midscenejs.com/zh/api) 中查阅,包括 `aiAct``aiQuery``aiAssert`
6463

6564
## 🚀 开始使用
6665

apps/chrome-extension/src/scripts/worker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ self.addEventListener('unhandledrejection', (event) => {
2727
console.error('[ServiceWorker] Unhandled promise rejection:', event.reason);
2828
});
2929

30-
// Background Bridge for MCP connection
30+
// Background Bridge for external automation connections
3131
const BRIDGE_PERMISSION_KEY = 'midscene_bridge_permission';
3232
const BRIDGE_STOPPED_KEY = 'midscene_bridge_stopped';
3333
let backgroundBridge: BridgeConnector | null = null;

apps/report/rsbuild.config.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill';
77
import { pluginReact } from '@rsbuild/plugin-react';
88
import { pluginSvgr } from '@rsbuild/plugin-svgr';
99
import { pluginWorkspaceDev } from 'rsbuild-plugin-workspace-dev';
10+
import {
11+
buildReportTemplateInjection,
12+
isReportTemplateInjectableFile,
13+
reportTemplateMagicString,
14+
reportTemplateReplacedMark,
15+
reportTemplateReplacementRegExp,
16+
} from '../../scripts/report-template-utils.mjs';
1017
import {
1118
commonIgnoreWarnings,
1219
createTypeCheckPlugin,
@@ -35,20 +42,14 @@ const copyReportTemplate = () => ({
3542
onAfterBuild: (arg0: ({ compiler }: { compiler: any }) => void) => void;
3643
}) {
3744
api.onAfterBuild(({ compiler }) => {
38-
const magicString = 'REPLACE_ME_WITH_REPORT_HTML';
39-
const replacedMark = '/*REPORT_HTML_REPLACED*/';
40-
const regExpForReplace = /\/\*REPORT_HTML_REPLACED\*\/.*/;
41-
4245
// read the template file
4346
const srcPath = path.join(__dirname, 'dist', 'index.html');
44-
const tplFileContent = fs
45-
.readFileSync(srcPath, 'utf-8')
46-
.replaceAll(magicString, '');
47+
const { sanitizedTplFileContent, finalContent } =
48+
buildReportTemplateInjection(fs.readFileSync(srcPath, 'utf-8'));
4749
assert(
48-
!tplFileContent.includes(magicString),
50+
!sanitizedTplFileContent.includes(reportTemplateMagicString),
4951
'magic string should not be in the template file',
5052
);
51-
const finalContent = `${replacedMark}${JSON.stringify(tplFileContent)}`;
5253

5354
// find the core package
5455
const corePkgDir = path.join(__dirname, '..', '..', 'packages', 'core');
@@ -65,35 +66,33 @@ const copyReportTemplate = () => ({
6566
const jsFiles = fs.readdirSync(corePkgDistDir, { recursive: true });
6667
let replacedCount = 0;
6768
for (const file of jsFiles) {
68-
if (
69-
typeof file === 'string' &&
70-
(file.endsWith('.js') || file.endsWith('.mjs'))
71-
) {
69+
if (isReportTemplateInjectableFile(file)) {
7270
const filePath = path.join(corePkgDistDir, file.toString());
7371
const fileContent = fs.readFileSync(filePath, 'utf-8');
74-
if (fileContent.includes(replacedMark)) {
72+
if (fileContent.includes(reportTemplateReplacedMark)) {
7573
assert(
76-
regExpForReplace.test(fileContent),
74+
reportTemplateReplacementRegExp.test(fileContent),
7775
'a replaced mark is found but cannot match',
7876
);
7977

8078
const replacedContent = fileContent.replace(
81-
regExpForReplace,
79+
reportTemplateReplacementRegExp,
8280
() => finalContent,
8381
);
8482
fs.writeFileSync(filePath, replacedContent);
8583
replacedCount++;
8684
console.log(`Template updated in file ${filePath}`);
87-
} else if (fileContent.includes(magicString)) {
85+
} else if (fileContent.includes(reportTemplateMagicString)) {
8886
const magicStringCount = (
89-
fileContent.match(new RegExp(magicString, 'g')) || []
87+
fileContent.match(new RegExp(reportTemplateMagicString, 'g')) ||
88+
[]
9089
).length;
9190
assert(
9291
magicStringCount === 1,
9392
'magic string shows more than once in the file, cannot process',
9493
);
9594
const replacedContent = fileContent.replace(
96-
`'${magicString}'`,
95+
`'${reportTemplateMagicString}'`,
9796
() => finalContent, // there are some $- code in the tpl, so we have to use a function as the second argument
9897
);
9998
fs.writeFileSync(filePath, replacedContent);

apps/report/scripts/inject-report-template.mjs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,32 @@ import assert from 'node:assert';
33
import fs from 'node:fs';
44
import path from 'node:path';
55
import { fileURLToPath } from 'node:url';
6+
import {
7+
buildReportTemplateInjection,
8+
isReportTemplateInjectableFile,
9+
reportTemplateMagicString,
10+
reportTemplateReplacedMark,
11+
reportTemplateReplacementRegExp,
12+
} from '../../../scripts/report-template-utils.mjs';
613

714
const __dirname = path.dirname(fileURLToPath(import.meta.url));
815
const reportRoot = path.resolve(__dirname, '..');
916
const repoRoot = path.resolve(reportRoot, '..', '..');
1017

11-
const magicString = 'REPLACE_ME_WITH_REPORT_HTML';
12-
const replacedMark = '/*REPORT_HTML_REPLACED*/';
13-
const regExpForReplace = /\/\*REPORT_HTML_REPLACED\*\/.*/;
14-
1518
const srcPath = path.join(reportRoot, 'dist', 'index.html');
1619
if (!fs.existsSync(srcPath)) {
1720
throw new Error(
1821
`Report template not found at ${srcPath}. Run "nx build @midscene/report" first.`,
1922
);
2023
}
2124

22-
const tplFileContent = fs
23-
.readFileSync(srcPath, 'utf-8')
24-
.replaceAll(magicString, '');
25+
const { sanitizedTplFileContent, finalContent } = buildReportTemplateInjection(
26+
fs.readFileSync(srcPath, 'utf-8'),
27+
);
2528
assert(
26-
!tplFileContent.includes(magicString),
29+
!sanitizedTplFileContent.includes(reportTemplateMagicString),
2730
'magic string should not be in the template file',
2831
);
29-
const finalContent = `${replacedMark}${JSON.stringify(tplFileContent)}`;
3032

3133
const corePkgDir = path.join(repoRoot, 'packages', 'core');
3234
const corePkgJson = JSON.parse(
@@ -41,34 +43,31 @@ const corePkgDistDir = path.join(corePkgDir, 'dist');
4143
const jsFiles = fs.readdirSync(corePkgDistDir, { recursive: true });
4244
let replacedCount = 0;
4345
for (const file of jsFiles) {
44-
if (
45-
typeof file === 'string' &&
46-
(file.endsWith('.js') || file.endsWith('.mjs'))
47-
) {
46+
if (isReportTemplateInjectableFile(file)) {
4847
const filePath = path.join(corePkgDistDir, file);
4948
const fileContent = fs.readFileSync(filePath, 'utf-8');
50-
if (fileContent.includes(replacedMark)) {
49+
if (fileContent.includes(reportTemplateReplacedMark)) {
5150
assert(
52-
regExpForReplace.test(fileContent),
51+
reportTemplateReplacementRegExp.test(fileContent),
5352
'a replaced mark is found but cannot match',
5453
);
5554
const replacedContent = fileContent.replace(
56-
regExpForReplace,
55+
reportTemplateReplacementRegExp,
5756
() => finalContent,
5857
);
5958
fs.writeFileSync(filePath, replacedContent);
6059
replacedCount++;
6160
console.log(`Template updated in file ${filePath}`);
62-
} else if (fileContent.includes(magicString)) {
61+
} else if (fileContent.includes(reportTemplateMagicString)) {
6362
const magicStringCount = (
64-
fileContent.match(new RegExp(magicString, 'g')) || []
63+
fileContent.match(new RegExp(reportTemplateMagicString, 'g')) || []
6564
).length;
6665
assert(
6766
magicStringCount === 1,
6867
'magic string shows more than once in the file, cannot process',
6968
);
7069
const replacedContent = fileContent.replace(
71-
`'${magicString}'`,
70+
`'${reportTemplateMagicString}'`,
7271
() => finalContent,
7372
);
7473
fs.writeFileSync(filePath, replacedContent);

apps/report/test-data/mcp-release-workflow.json

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { describe, expect, it } from 'vitest';
2+
import {
3+
buildReportTemplateInjection,
4+
reportTemplateMagicString,
5+
reportTemplateReplacedMark,
6+
reportTemplateReplacementRegExp,
7+
} from '../../../scripts/report-template-utils.mjs';
8+
9+
describe('report template utils', () => {
10+
it('should remove placeholders and sanitize nested injected report templates', () => {
11+
const html = [
12+
'<html>',
13+
`${reportTemplateMagicString}<body>latest report</body>`,
14+
`<script>window.__REPORT__=${reportTemplateReplacedMark}"<html>old report</html>"</script>`,
15+
'</html>',
16+
].join('');
17+
18+
const { sanitizedTplFileContent, finalContent } =
19+
buildReportTemplateInjection(html);
20+
21+
expect(sanitizedTplFileContent).not.toContain(reportTemplateMagicString);
22+
expect(sanitizedTplFileContent).toContain(
23+
`${reportTemplateReplacedMark}""`,
24+
);
25+
expect(finalContent).toMatch(reportTemplateReplacementRegExp);
26+
expect(
27+
JSON.parse(finalContent.slice(reportTemplateReplacedMark.length)),
28+
).toBe(sanitizedTplFileContent);
29+
});
30+
});

apps/site/docs/en/api.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ All agents share these base options:
3939
- For mobile devices, setting `screenshotShrinkFactor` to 2 can reduce token consumption while maintaining clarity, but it is not recommended to set it higher than 3, as this may cause the image to be too blurry and affect the AI model's understanding.
4040
- For web pages, if the content is complex or contains a lot of details, it is not recommended to set `screenshotShrinkFactor` to avoid overly blurry screenshots. Additionally, if you want higher clarity for web page screenshots, you can configure Puppeteer or Playwright's `deviceScaleFactor` to 2, which will allow Puppeteer or Playwright to render the page as if it were a high-definition screen.
4141

42-
The MCP tools and device CLIs expose these same Agent behavior options per call. In CLIs, convert the camelCase API option to a bare kebab-case flag, such as `waitAfterAction` -> `--wait-after-action`. In MCP calls, keep the camelCase option under the platform namespace, such as `android.waitAfterAction` or `web.waitAfterAction`. See [Configure Agent behavior per call](./mcp#configure-agent-behavior-per-call) for examples.
42+
The device CLIs expose these same Agent behavior options per call. Convert the camelCase API option to a bare kebab-case flag, such as `waitAfterAction` -> `--wait-after-action`. See [Skills](./skills) for the platform CLI entry points.
4343

4444
### Custom model configuration
4545

apps/site/docs/en/changelog.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,26 @@ Midscene now includes an AndroidWorld benchmark report. With v1.9.5, Midscene ac
1414

1515
### Model and Planning Updates
1616

17+
- `aiAct` now supports image prompts.
1718
- `MIDSCENE_MODEL_REASONING_ENABLED` now supports `default`, so Midscene can follow each model family's default thinking behavior.
19+
- Gemini thinking content and GPT-5 reasoning configuration are now handled more completely.
1820
- `aiAct` falls back to model planning when a cached plan becomes invalid, and clears the corresponding cache entry.
21+
- AI request errors now include retry-attempt details, parse failures preserve raw model responses, and parsed locate results are validated before use.
22+
- Model dumps now expose more model response metadata, including raw choice messages and response model names in usage data.
1923
- The `deepLocate` search area is now displayed in reports.
2024

2125
### Chrome Extension
2226

2327
- Chrome extension Bridge mode now supports file upload.
28+
- Bridge-mode file uploads now honor file chooser accept filters and WSL file paths.
2429

2530
### YAML, CLI, and MCP
2631

32+
- `1.9.8` is the final Midscene version that includes MCP support. Later versions retire MCP server packages in favor of Skills and the platform CLIs.
33+
- Platform CLIs now accept agent behavior init args.
2734
- YAML scripts now support the HarmonyOS target in the CLI, so HarmonyOS automation can be driven through the same script runner workflow as Web, Android, iOS, and Computer. See: [Automate with Scripts in YAML](./automate-with-scripts-in-yaml), [HarmonyOS API](./harmony-api-reference)
2835
- YAML Web config supports custom HTTP headers for browser automation.
36+
- YAML Web config supports `downloadPath` for browser downloads.
2937
- YAML runs now surface real execution errors instead of ending as a silent "not executed" case.
3038
- YAML batch runs can retry failed cases.
3139
- Successful YAML runs now print the report path.
@@ -36,13 +44,26 @@ Midscene now includes an AndroidWorld benchmark report. With v1.9.5, Midscene ac
3644

3745
### Reports
3846

47+
- `recordToReport` now supports custom screenshots.
3948
- Report exports keep image paths aligned with exported screenshots.
4049
- Reports include a JSON tree view for inspecting structured task and model data.
50+
- Report screenshots, labels, playground server origin handling, and context spacing were refined.
51+
52+
### Studio and Recorder
53+
54+
- Studio recorder descriptions and preview input coalescing were stabilized.
55+
- Studio now handles invalid model environment configuration more safely.
56+
- Recorder workflows can generate Markdown replay output.
4157

4258
### Android Automation
4359

4460
- Android action controls and planning guidance were improved for native mobile automation flows.
4561

62+
### Computer Automation
63+
64+
- Computer desktop automation now ships Intel packaging.
65+
- Libnut scrolling now emits one full wheel delta per scroll tick.
66+
4667
### Bug Fixes
4768

4869
- Fixed `longPress` duration in Web integration being capped at 600ms.

0 commit comments

Comments
 (0)