z-mcp-codesign 0.1.1 → 0.1.2

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 (3) hide show
  1. package/README.md +28 -94
  2. package/dist/index.js +236 -134
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,132 +1,66 @@
1
1
  # 前言
2
2
 
3
-
4
-
5
3
  这是一个基于 `@modelcontextprotocol/sdk` 实现的 **MCP stdio 服务**,用于在本机通过 Edge 打开腾讯 CoDesign 页面并抓取当前画板图片。
6
4
 
7
-
8
-
9
5
  # 配置说明
10
6
 
11
-
12
-
13
7
  在 Cursor 工作区根目录创建或编辑 `.cursor/mcp.json`。保存后在 Cursor 的 MCP 相关设置中启用该服务,必要时重新加载窗口或刷新 MCP 列表。
14
8
 
15
-
16
-
17
9
  **前提**:本机需已安装 **Microsoft Edge**;服务通过 Playwright 使用系统 Edge(`channel: "msedge"`)。
18
10
 
19
-
20
-
21
- ## 使用已发布的 npm 包(推荐)
22
-
23
-
24
-
25
- 在任意项目中均可使用,无需克隆本仓库:
26
-
27
-
11
+ 在任意项目中均可使用:
28
12
 
29
13
  ```json
30
-
31
14
  {
32
-
33
15
  "mcpServers": {
34
-
35
16
  "z-mcp-codesign": {
36
-
37
17
  "command": "npx",
38
-
39
18
  "args": ["-y", "z-mcp-codesign", "--stdio"]
40
-
41
- }
42
-
43
- }
44
-
45
- }
46
-
47
- ```
48
-
49
-
50
-
51
- ## 使用本地源码(开发与调试)
52
-
53
-
54
-
55
- 在克隆后的仓库根目录配置 `.cursor/mcp.json`,并先执行 `npm install` 与 `npm run build`(生成 `dist/`)。`args` 中的 `"."` 表示使用当前目录作为 npm 包:
56
-
57
-
58
-
59
- ```json
60
-
61
- {
62
-
63
- "mcpServers": {
64
-
65
- "z-mcp-codesign": {
66
-
67
- "command": "npx",
68
-
69
- "args": ["-y", ".", "--stdio"]
70
-
71
19
  }
72
-
73
20
  }
74
-
75
21
  }
76
-
77
22
  ```
78
23
 
79
24
 
80
25
 
81
- 若 MCP 启动时工作目录不是仓库根目录,可在该条目中增加 `"cwd"`,值为本仓库的绝对路径(Windows 下 JSON 字符串里反斜杠需写成 `\\`)。
82
-
83
-
84
-
85
- ## 环境变量与用户数据目录
86
-
87
-
88
-
89
- 调用 **`codesign_open_login`** 成功后,进程会设置环境变量 **`Z_MCP_CODESIGN_EDGE_USER_DATA`**,指向本次使用的 Edge 用户数据根目录;随后在同一 MCP 会话里调用 **`codesign_screen_inspect`** 且不显式传 `userDataDir` 时,会自动复用该目录下的登录态。
90
-
91
-
92
-
93
- 也可在两条工具调用里显式传入相同的 **`userDataDir`**(绝对路径);若使用非 `Default` 的 Edge profile,请在登录与抓图两次调用中传入相同的 **`profileDirectory`**。
94
-
95
-
96
-
97
- # 使用 MCP 登录 CoDesign
98
-
99
-
100
-
101
- 1. 按上文在 Cursor 中启用本 MCP 服务。
102
-
103
- 2. 在对话中让助手调用工具 **`codesign_open_login`**(参数可全部省略;默认打开 `https://codesign.qq.com/app/login`)。
104
-
105
- 3. 在弹出的 **Edge** 窗口中完成腾讯 CoDesign 登录(扫码或账号密码等)。
106
-
107
- 4. **关闭该 Edge 窗口**(释放用户数据目录锁),否则后续抓图可能启动失败。
108
-
109
- 5. 再调用 **`codesign_screen_inspect`**,传入目标 CoDesign 页面 **`url`**(例如设计稿 inspect 链接);返回的 JSON 中含 **`imageBase64`**(PNG),供客户端展示或落盘。
110
-
111
-
112
-
113
26
  # 提供的工具
114
27
 
28
+ | 工具名 | 说明 |
29
+ | ------ | ---- |
30
+ | `codesign_open_login` | 有界面打开 CoDesign 登录页,用于在本机 Edge 用户数据目录中完成登录 |
31
+ | `codesign_screen_inspect` | 打开指定 CoDesign 页面,截取最大的可见 `.screen-inspect` 区域,以 PNG 的 base64 返回 |
32
+ | `codesign_screen_inspect_save` | 打开指定 CoDesign 页面,截取最大的可见 `.screen-inspect` 区域,保存为本地 PNG 文件并返回保存路径(默认保存到项目根目录 `.codesign_screen_imgs/`) |
33
+ | `codesign_screen_list` | 获取 CoDesign 设计页面左侧菜单中的所有页面名称和访问地址 |
115
34
 
35
+ # 使用示例
116
36
 
117
- | 工具名 | 说明 |
37
+ **打开登录页(用于后续抓图)**
118
38
 
119
- | ------ | ---- |
39
+ ```bash
40
+ 请在本机用可见浏览器打开 CoDesign 登录页
41
+ ```
120
42
 
121
- | `codesign_open_login` | 有界面打开 CoDesign 登录页,用于在本机 Edge 用户数据目录中完成登录 |
43
+ **列出当前项目的页面列表**
122
44
 
123
- | `codesign_screen_inspect` | 打开指定 CoDesign 页面,截取最大的可见 `.screen-inspect` 区域,以 PNG 的 base64 返回 |
45
+ ```bash
46
+ 请打开这个 CoDesign inspect 链接,并把左侧菜单里的页面列表整理出来(包含每个页面的名称和对应的 inspect 访问地址):https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
47
+ ```
124
48
 
49
+ **抓图并返回图片(base64)**
125
50
 
51
+ ```bash
52
+ 请打开这个 CoDesign inspect 链接 把截图以 base64 返回给我:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
53
+ ```
126
54
 
127
- # 使用示例
55
+ **抓图并保存到本地**
128
56
 
57
+ ```bash
58
+ 请打开这个 CoDesign inspect 链接,获取图片,把截图保存到本地,并告诉我保存路径:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
59
+ ```
129
60
 
61
+ **生成项目**
130
62
 
131
- 在已登录(步骤见上一节)的前提下,调用 **`codesign_screen_inspect`**,传入 `url`(CoDesign 页面地址)。工具成功时返回的文本内容为 JSON,字段包括 `imageBase64`、`title`、`boundingBox` 等;将 `imageBase64` 解码即可得到 PNG 二进制数据。
63
+ ```bash
64
+ 请打开这个 CoDesign inspect 链接,获取图片,把截图保存到本地,并识别图片内容生成Vue3+Vite项目:https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect
65
+ ```
132
66
 
package/dist/index.js CHANGED
@@ -3222,8 +3222,8 @@ var require_utils = __commonJS({
3222
3222
  }
3223
3223
  return ind;
3224
3224
  }
3225
- function removeDotSegments(path) {
3226
- let input = path;
3225
+ function removeDotSegments(path2) {
3226
+ let input = path2;
3227
3227
  const output = [];
3228
3228
  let nextSlash = -1;
3229
3229
  let len = 0;
@@ -3422,8 +3422,8 @@ var require_schemes = __commonJS({
3422
3422
  wsComponent.secure = void 0;
3423
3423
  }
3424
3424
  if (wsComponent.resourceName) {
3425
- const [path, query] = wsComponent.resourceName.split("?");
3426
- wsComponent.path = path && path !== "/" ? path : void 0;
3425
+ const [path2, query] = wsComponent.resourceName.split("?");
3426
+ wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
3427
3427
  wsComponent.query = query;
3428
3428
  wsComponent.resourceName = void 0;
3429
3429
  }
@@ -7281,8 +7281,8 @@ function getErrorMap() {
7281
7281
 
7282
7282
  // node_modules/zod/v3/helpers/parseUtil.js
7283
7283
  var makeIssue = (params) => {
7284
- const { data, path, errorMaps, issueData } = params;
7285
- const fullPath = [...path, ...issueData.path || []];
7284
+ const { data, path: path2, errorMaps, issueData } = params;
7285
+ const fullPath = [...path2, ...issueData.path || []];
7286
7286
  const fullIssue = {
7287
7287
  ...issueData,
7288
7288
  path: fullPath
@@ -7398,11 +7398,11 @@ var errorUtil;
7398
7398
 
7399
7399
  // node_modules/zod/v3/types.js
7400
7400
  var ParseInputLazyPath = class {
7401
- constructor(parent, value, path, key) {
7401
+ constructor(parent, value, path2, key) {
7402
7402
  this._cachedPath = [];
7403
7403
  this.parent = parent;
7404
7404
  this.data = value;
7405
- this._path = path;
7405
+ this._path = path2;
7406
7406
  this._key = key;
7407
7407
  }
7408
7408
  get path() {
@@ -11039,10 +11039,10 @@ function assignProp(target, prop, value) {
11039
11039
  configurable: true
11040
11040
  });
11041
11041
  }
11042
- function getElementAtPath(obj, path) {
11043
- if (!path)
11042
+ function getElementAtPath(obj, path2) {
11043
+ if (!path2)
11044
11044
  return obj;
11045
- return path.reduce((acc, key) => acc?.[key], obj);
11045
+ return path2.reduce((acc, key) => acc?.[key], obj);
11046
11046
  }
11047
11047
  function promiseAllObject(promisesObj) {
11048
11048
  const keys = Object.keys(promisesObj);
@@ -11362,11 +11362,11 @@ function aborted(x, startIndex = 0) {
11362
11362
  }
11363
11363
  return false;
11364
11364
  }
11365
- function prefixIssues(path, issues) {
11365
+ function prefixIssues(path2, issues) {
11366
11366
  return issues.map((iss) => {
11367
11367
  var _a;
11368
11368
  (_a = iss).path ?? (_a.path = []);
11369
- iss.path.unshift(path);
11369
+ iss.path.unshift(path2);
11370
11370
  return iss;
11371
11371
  });
11372
11372
  }
@@ -21323,8 +21323,60 @@ function registerSaveImageTool(mcpServer2) {
21323
21323
  );
21324
21324
  }
21325
21325
 
21326
- // src/tools/screen-inspect.ts
21326
+ // src/lib/codesign-page.ts
21327
21327
  import { chromium as chromium2 } from "playwright";
21328
+ var DEFAULT_CODESIGN_PAGE_DEFAULTS = {
21329
+ headless: true,
21330
+ timeoutMs: 6e4,
21331
+ afterLoadWaitMs: 1e3,
21332
+ networkIdleMaxWaitMs: 3e4
21333
+ };
21334
+ async function withCodesignPage(deps2, input, fn, opts = {}) {
21335
+ const merged = {
21336
+ ...DEFAULT_CODESIGN_PAGE_DEFAULTS,
21337
+ ...opts.defaults ?? {}
21338
+ };
21339
+ const url = input.url;
21340
+ const headless = input.headless ?? merged.headless;
21341
+ const timeoutMs = input.timeoutMs ?? merged.timeoutMs;
21342
+ const persistentDir = resolveEdgeUserDataDir(input.userDataDir);
21343
+ const context = await chromium2.launchPersistentContext(
21344
+ persistentDir,
21345
+ persistentBrowserContextOptions({
21346
+ headless,
21347
+ profileDirectory: input.profileDirectory,
21348
+ channel: deps2.browserChannel,
21349
+ edgeExecutablePath: deps2.edgeExecutableArg
21350
+ })
21351
+ );
21352
+ try {
21353
+ const page = await context.newPage();
21354
+ page.setDefaultTimeout(timeoutMs);
21355
+ await page.goto(url, { waitUntil: "domcontentloaded" });
21356
+ try {
21357
+ await page.waitForLoadState("networkidle", {
21358
+ timeout: Math.max(5e3, Math.min(timeoutMs, merged.networkIdleMaxWaitMs))
21359
+ });
21360
+ } catch {
21361
+ }
21362
+ if (opts.waitForSelector?.selector) {
21363
+ const waitTimeout = opts.waitForSelector.timeoutMs ?? Math.max(5e3, Math.floor(timeoutMs * 2 / 3));
21364
+ try {
21365
+ await page.waitForSelector(opts.waitForSelector.selector, {
21366
+ state: opts.waitForSelector.state ?? "attached",
21367
+ timeout: Math.min(timeoutMs, waitTimeout)
21368
+ });
21369
+ } catch {
21370
+ }
21371
+ }
21372
+ if (merged.afterLoadWaitMs > 0) {
21373
+ await page.waitForTimeout(merged.afterLoadWaitMs);
21374
+ }
21375
+ return await fn(page, { persistentDir, headless, timeoutMs });
21376
+ } finally {
21377
+ await context.close();
21378
+ }
21379
+ }
21328
21380
 
21329
21381
  // src/lib/screen-inspect-png.ts
21330
21382
  async function screenshotLargestScreenInspectPng(page) {
@@ -21353,152 +21405,201 @@ var screenInspectInput = external_exports.object({
21353
21405
  profileDirectory: external_exports.string().optional().describe("User Data \u4E0B\u914D\u7F6E\u540D\uFF0C\u5982 Default\u3002")
21354
21406
  });
21355
21407
  async function runScreenInspect(deps2, input) {
21356
- const url = input.url;
21357
- const headless = input.headless ?? true;
21358
- const timeoutMs = input.timeoutMs ?? 6e4;
21359
- const persistentDir = resolveEdgeUserDataDir(input.userDataDir);
21360
- const context = await chromium2.launchPersistentContext(
21361
- persistentDir,
21362
- persistentBrowserContextOptions({
21363
- headless,
21364
- profileDirectory: input.profileDirectory,
21365
- channel: deps2.browserChannel,
21366
- edgeExecutablePath: deps2.edgeExecutableArg
21367
- })
21368
- );
21369
- try {
21370
- const page = await context.newPage();
21371
- page.setDefaultTimeout(timeoutMs);
21372
- await page.goto(url, { waitUntil: "domcontentloaded" });
21373
- try {
21374
- await page.waitForLoadState("networkidle", {
21375
- timeout: Math.max(5e3, Math.min(timeoutMs, 3e4))
21376
- });
21377
- } catch {
21378
- }
21379
- await page.waitForTimeout(1e3);
21380
- const shot = await screenshotLargestScreenInspectPng(page);
21381
- if (!shot) {
21382
- return toolError("\u672A\u627E\u5230\u53EF\u89C1\u7684 .screen-inspect \u533A\u57DF\u3002");
21408
+ return withCodesignPage(
21409
+ deps2,
21410
+ input,
21411
+ async (page) => {
21412
+ const shot = await screenshotLargestScreenInspectPng(page);
21413
+ if (!shot) {
21414
+ return toolError("\u672A\u627E\u5230\u53EF\u89C1\u7684 .screen-inspect \u533A\u57DF\u3002");
21415
+ }
21416
+ return toolResult(
21417
+ JSON.stringify(
21418
+ {
21419
+ url: input.url,
21420
+ title: await page.title(),
21421
+ mimeType: "image/png",
21422
+ imageBase64: shot.buffer.toString("base64"),
21423
+ boundingBox: shot.boundingBox
21424
+ },
21425
+ null,
21426
+ 2
21427
+ )
21428
+ );
21429
+ },
21430
+ {
21431
+ waitForSelector: { selector: ".screen-inspect", state: "visible" },
21432
+ defaults: { afterLoadWaitMs: 250 }
21383
21433
  }
21384
- return toolResult(
21385
- JSON.stringify(
21386
- {
21387
- url,
21388
- title: await page.title(),
21389
- mimeType: "image/png",
21390
- imageBase64: shot.buffer.toString("base64"),
21391
- boundingBox: shot.boundingBox
21392
- },
21393
- null,
21394
- 2
21395
- )
21396
- );
21397
- } finally {
21398
- await context.close();
21399
- }
21434
+ );
21400
21435
  }
21401
21436
  function registerScreenInspectTool(mcpServer2, deps2) {
21402
21437
  mcpServer2.registerTool(
21403
21438
  "codesign_screen_inspect",
21404
21439
  {
21405
- description: "\u6253\u5F00 CoDesign \u9875\u9762\uFF0C\u4EC5\u622A\u53D6\u9762\u79EF\u6700\u5927\u7684\u53EF\u89C1 .screen-inspect \u533A\u57DF\uFF0C\u4EE5 PNG \u7684 base64 \u8FD4\u56DE\uFF0C\u4E0D\u5199\u78C1\u76D8\u3002",
21440
+ description: "\u6253\u5F00 CoDesign \u9875\u9762\uFF0C\u4EC5\u622A\u53D6\u9762\u79EF\u6700\u5927\u7684\u53EF\u89C1 .screen-inspect \u533A\u57DF\uFF0C\u4EE5 PNG \u7684 base64 \u8FD4\u56DE\u3002",
21406
21441
  inputSchema: screenInspectInput
21407
21442
  },
21408
21443
  (input) => runScreenInspect(deps2, input)
21409
21444
  );
21410
21445
  }
21411
21446
 
21412
- // src/tools/screen-list.ts
21413
- import { chromium as chromium3 } from "playwright";
21414
- var screenListInput = external_exports.object({
21415
- url: external_exports.string().url().describe("CoDesign \u8BBE\u8BA1\u9875\u9762\u5730\u5740\uFF0C\u5982 https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect"),
21447
+ // src/tools/screen-inspect-save.ts
21448
+ import { mkdir as mkdir3, writeFile as writeFile2 } from "node:fs/promises";
21449
+ import path from "node:path";
21450
+ function safeSegment(s) {
21451
+ return s.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
21452
+ }
21453
+ function timestampCompact(d) {
21454
+ const pad = (n) => String(n).padStart(2, "0");
21455
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
21456
+ }
21457
+ function deriveCodesignIds(url) {
21458
+ try {
21459
+ const u = new URL(url);
21460
+ const parts = u.pathname.split("/").filter(Boolean);
21461
+ const projectId = parts.find((p) => /^\d{10,}$/.test(p));
21462
+ const pageId = parts.find((p) => /^\d+$/.test(p));
21463
+ return { projectId, pageId };
21464
+ } catch {
21465
+ return {};
21466
+ }
21467
+ }
21468
+ var screenInspectSaveInput = external_exports.object({
21469
+ url: external_exports.string().url().describe("CoDesign \u9875\u9762\u5730\u5740"),
21416
21470
  headless: external_exports.boolean().optional().describe("\u65E0\u5934\u6A21\u5F0F\uFF0C\u9ED8\u8BA4 true"),
21417
21471
  timeoutMs: external_exports.number().int().positive().optional().describe("\u6574\u4F53\u8D85\u65F6\u6BEB\u79D2\u6570\uFF0C\u9ED8\u8BA4 60000"),
21418
21472
  userDataDir: external_exports.string().optional().describe(
21419
- "\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF1B\u672A\u4F20\u65F6\u540C\u6293\u56FE\u5DE5\u5177\uFF08\u73AF\u5883\u53D8\u91CF\u6216\u4E34\u65F6\u76EE\u5F55\uFF09\u3002"
21473
+ "\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF08\u987B\u5148\u9000\u51FA\u5360\u7528\u8BE5\u76EE\u5F55\u7684\u6D4F\u89C8\u5668\uFF09\u3002\u672A\u4F20\u65F6\u4F7F\u7528\u73AF\u5883\u53D8\u91CF Z_MCP_CODESIGN_EDGE_USER_DATA \u6216\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55\u4E0B\u9ED8\u8BA4\u914D\u7F6E\u3002"
21420
21474
  ),
21421
21475
  profileDirectory: external_exports.string().optional().describe("User Data \u4E0B\u914D\u7F6E\u540D\uFF0C\u5982 Default\u3002")
21422
21476
  });
21423
- async function runScreenList(deps2, input) {
21424
- const url = input.url;
21425
- const headless = input.headless ?? true;
21426
- const timeoutMs = input.timeoutMs ?? 6e4;
21427
- const persistentDir = resolveEdgeUserDataDir(input.userDataDir);
21428
- const context = await chromium3.launchPersistentContext(
21429
- persistentDir,
21430
- persistentBrowserContextOptions({
21431
- headless,
21432
- profileDirectory: input.profileDirectory,
21433
- channel: deps2.browserChannel,
21434
- edgeExecutablePath: deps2.edgeExecutableArg
21435
- })
21477
+ async function runScreenInspectSave(deps2, input) {
21478
+ return withCodesignPage(
21479
+ deps2,
21480
+ input,
21481
+ async (page) => {
21482
+ const shot = await screenshotLargestScreenInspectPng(page);
21483
+ if (!shot) {
21484
+ return toolError("\u672A\u627E\u5230\u53EF\u89C1\u7684 .screen-inspect \u533A\u57DF\u3002");
21485
+ }
21486
+ const outDir = path.resolve(process.cwd(), ".codesign_screen_imgs");
21487
+ const { projectId, pageId } = deriveCodesignIds(input.url);
21488
+ const filenameParts = [
21489
+ "codesign",
21490
+ projectId ? `p${safeSegment(projectId)}` : void 0,
21491
+ pageId ? `s${safeSegment(pageId)}` : void 0,
21492
+ timestampCompact(/* @__PURE__ */ new Date())
21493
+ ].filter(Boolean);
21494
+ const finalPath = path.join(outDir, `${filenameParts.join("_")}.png`);
21495
+ await mkdir3(path.dirname(finalPath), { recursive: true });
21496
+ await writeFile2(finalPath, shot.buffer);
21497
+ return toolResult(
21498
+ JSON.stringify(
21499
+ {
21500
+ url: input.url,
21501
+ title: await page.title(),
21502
+ mimeType: "image/png",
21503
+ savedPath: finalPath,
21504
+ boundingBox: shot.boundingBox
21505
+ },
21506
+ null,
21507
+ 2
21508
+ )
21509
+ );
21510
+ },
21511
+ {
21512
+ waitForSelector: { selector: ".screen-inspect", state: "visible" },
21513
+ defaults: { afterLoadWaitMs: 250 }
21514
+ }
21436
21515
  );
21437
- try {
21438
- const page = await context.newPage();
21439
- page.setDefaultTimeout(timeoutMs);
21440
- await page.goto(url, { waitUntil: "domcontentloaded" });
21441
- try {
21442
- await page.waitForLoadState("networkidle", {
21443
- timeout: Math.max(5e3, Math.min(timeoutMs, 3e4))
21516
+ }
21517
+ function registerScreenInspectSaveTool(mcpServer2, deps2) {
21518
+ mcpServer2.registerTool(
21519
+ "codesign_screen_inspect_save",
21520
+ {
21521
+ description: "\u6253\u5F00 CoDesign \u9875\u9762\uFF0C\u4EC5\u622A\u53D6\u9762\u79EF\u6700\u5927\u7684\u53EF\u89C1 .screen-inspect \u533A\u57DF\uFF0C\u4FDD\u5B58\u4E3A\u672C\u5730 PNG \u6587\u4EF6\u5E76\u8FD4\u56DE\u8DEF\u5F84\uFF08\u9ED8\u8BA4\u4FDD\u5B58\u5230\u9879\u76EE\u6839\u76EE\u5F55 .codesign_screen_imgs\uFF09\u3002",
21522
+ inputSchema: screenInspectSaveInput
21523
+ },
21524
+ (input) => runScreenInspectSave(deps2, input)
21525
+ );
21526
+ }
21527
+
21528
+ // src/tools/screen-list.ts
21529
+ var screenListInput = external_exports.object({
21530
+ url: external_exports.string().url().describe(
21531
+ "CoDesign \u8BBE\u8BA1\u9875\u9762\u5730\u5740\uFF0C\u5982 https://codesign.qq.com/app/design/<projectId>/<pageId>/inspect"
21532
+ ),
21533
+ headless: external_exports.boolean().optional().describe("\u65E0\u5934\u6A21\u5F0F\uFF0C\u9ED8\u8BA4 true"),
21534
+ timeoutMs: external_exports.number().int().positive().optional().describe("\u6574\u4F53\u8D85\u65F6\u6BEB\u79D2\u6570\uFF0C\u9ED8\u8BA4 60000"),
21535
+ userDataDir: external_exports.string().optional().describe("\u6D4F\u89C8\u5668\u7528\u6237\u6570\u636E\u6839\u76EE\u5F55\uFF1B\u672A\u4F20\u65F6\u540C\u6293\u56FE\u5DE5\u5177\uFF08\u73AF\u5883\u53D8\u91CF\u6216\u4E34\u65F6\u76EE\u5F55\uFF09\u3002"),
21536
+ profileDirectory: external_exports.string().optional().describe("User Data \u4E0B\u914D\u7F6E\u540D\uFF0C\u5982 Default\u3002")
21537
+ });
21538
+ async function runScreenList(deps2, input) {
21539
+ return withCodesignPage(
21540
+ deps2,
21541
+ input,
21542
+ async (page) => {
21543
+ const pages = await page.evaluate(() => {
21544
+ const items = document.querySelectorAll(".screen-list__item");
21545
+ const result = [];
21546
+ for (const item of items) {
21547
+ const id = item.getAttribute("id") || "";
21548
+ const match = id.match(/^screen-(\d+)$/);
21549
+ if (!match) continue;
21550
+ const pageId = match[1];
21551
+ let name = "";
21552
+ const nameEl = item.querySelector(
21553
+ "[class*='name'], [class*='title'], [class*='label']"
21554
+ );
21555
+ if (nameEl) {
21556
+ name = nameEl.textContent?.trim() || "";
21557
+ }
21558
+ if (!name) {
21559
+ name = item.textContent?.trim().replace(/\s+/g, " ") || "";
21560
+ }
21561
+ result.push({
21562
+ name,
21563
+ pageId
21564
+ });
21565
+ }
21566
+ return result;
21444
21567
  });
21445
- } catch {
21568
+ if (pages.length === 0) {
21569
+ return toolError("\u672A\u627E\u5230\u5DE6\u4FA7\u83DC\u5355\u4E2D\u7684\u9875\u9762\u5217\u8868\u3002");
21570
+ }
21571
+ const currentUrl = new URL(input.url);
21572
+ const pathParts = currentUrl.pathname.split("/");
21573
+ const projectId = pathParts.find((p) => /^\d{10,}$/.test(p)) || "";
21574
+ const currentPageId = pathParts.find((p) => /^\d+$/.test(p)) || "";
21575
+ const resultData = pages.map((p) => ({
21576
+ name: p.name,
21577
+ pageId: p.pageId,
21578
+ url: projectId ? `https://codesign.qq.com/app/design/${projectId}/${p.pageId}/inspect` : currentPageId ? input.url.replace(`/${currentPageId}/`, `/${p.pageId}/`) : input.url
21579
+ }));
21580
+ return toolResult(
21581
+ JSON.stringify(
21582
+ {
21583
+ projectId,
21584
+ totalCount: resultData.length,
21585
+ pages: resultData
21586
+ },
21587
+ null,
21588
+ 2
21589
+ )
21590
+ );
21591
+ },
21592
+ {
21593
+ waitForSelector: { selector: ".screen-list__item", state: "attached" },
21594
+ defaults: { afterLoadWaitMs: 250 }
21446
21595
  }
21447
- await page.waitForTimeout(2e3);
21448
- const pages = await page.evaluate(() => {
21449
- const items = document.querySelectorAll(".screen-list__item");
21450
- const result = [];
21451
- for (const item of items) {
21452
- const id = item.getAttribute("id") || "";
21453
- const match = id.match(/^screen-(\d+)$/);
21454
- if (!match) continue;
21455
- const pageId = match[1];
21456
- let name = "";
21457
- const nameEl = item.querySelector("[class*='name'], [class*='title'], [class*='label']");
21458
- if (nameEl) {
21459
- name = nameEl.textContent?.trim() || "";
21460
- }
21461
- if (!name) {
21462
- name = item.textContent?.trim().replace(/\s+/g, " ") || "";
21463
- }
21464
- result.push({
21465
- name,
21466
- pageId
21467
- });
21468
- }
21469
- return result;
21470
- });
21471
- if (pages.length === 0) {
21472
- return toolError("\u672A\u627E\u5230\u5DE6\u4FA7\u83DC\u5355\u4E2D\u7684\u9875\u9762\u5217\u8868\u3002");
21473
- }
21474
- const currentUrl = new URL(url);
21475
- const pathParts = currentUrl.pathname.split("/");
21476
- const projectId = pathParts.find((p) => /^\d{10,}$/.test(p)) || "";
21477
- const resultData = pages.map((p) => ({
21478
- name: p.name,
21479
- pageId: p.pageId,
21480
- url: projectId ? `https://codesign.qq.com/app/design/${projectId}/${p.pageId}/inspect` : p.url.replace("PROJECT_ID", projectId)
21481
- }));
21482
- return toolResult(
21483
- JSON.stringify(
21484
- {
21485
- projectId,
21486
- totalCount: resultData.length,
21487
- pages: resultData
21488
- },
21489
- null,
21490
- 2
21491
- )
21492
- );
21493
- } finally {
21494
- await context.close();
21495
- }
21596
+ );
21496
21597
  }
21497
21598
  function registerScreenListTool(mcpServer2, deps2) {
21498
21599
  mcpServer2.registerTool(
21499
21600
  "codesign_screen_list",
21500
21601
  {
21501
- description: "\u83B7\u53D6 CoDesign \u8BBE\u8BA1\u9875\u9762\u5DE6\u4FA7\u83DC\u5355\u4E2D\u7684\u6240\u6709\u9875\u9762\u540D\u79F0\u548C\u8BBF\u95EE\u5730\u5740\u3002",
21602
+ description: "\u6839\u636ECoDesign\u9875\u9762\u5730\u5740\u83B7\u53D6\u9879\u76EE\u6240\u6709\u9875\u9762\u5217\u8868\uFF0C\u8FD4\u56DE\u9879\u76EEID\u3001\u603B\u9875\u9762\u6570\u3001\u9875\u9762\u5217\u8868\u3002",
21502
21603
  inputSchema: screenListInput
21503
21604
  },
21504
21605
  (input) => runScreenList(deps2, input)
@@ -21508,6 +21609,7 @@ function registerScreenListTool(mcpServer2, deps2) {
21508
21609
  // src/tools/register-all.ts
21509
21610
  function registerAllCodesignTools(mcpServer2, deps2) {
21510
21611
  registerScreenInspectTool(mcpServer2, deps2);
21612
+ registerScreenInspectSaveTool(mcpServer2, deps2);
21511
21613
  registerScreenListTool(mcpServer2, deps2);
21512
21614
  registerOpenLoginTool(mcpServer2, deps2);
21513
21615
  registerSaveImageTool(mcpServer2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "z-mcp-codesign",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "An MCP server to capture images from Tencent CoDesign pages via Edge",
6
6
  "main": "dist/index.js",