shizai-agent-mcp 0.2.4 → 0.2.5

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.
package/README.md CHANGED
@@ -1,19 +1,19 @@
1
1
  # shizai-agent-mcp
2
2
 
3
- 实在 Agent 本地 MCP 的 npm/npx 安装器。一个 npm 包同时提供 Shizai MCP、BrowserMCP 和 Playwright MCP 注册能力,用户不需要分别安装多个 npm 包。
3
+ 实在 Agent 本地 MCP 的轻量 npm/npx 安装器。它只安装和注册 Shizai MCP,浏览器识别与控制复用实在 Agent 客户端自带的 Chrome 扩展和 DeepCapture。
4
4
 
5
5
  ## 使用
6
6
 
7
7
  ```powershell
8
8
  npx -y shizai-agent-mcp install
9
- npx -y shizai-agent-mcp register codex --with-browsers
9
+ npx -y shizai-agent-mcp register codex
10
10
  ```
11
11
 
12
12
  也可以注册 Cursor 或 Claude Desktop:
13
13
 
14
14
  ```powershell
15
- npx -y shizai-agent-mcp register cursor --with-browsers
16
- npx -y shizai-agent-mcp register claude --with-browsers
15
+ npx -y shizai-agent-mcp register cursor
16
+ npx -y shizai-agent-mcp register claude
17
17
  ```
18
18
 
19
19
  常用命令:
@@ -24,15 +24,13 @@ npx -y shizai-agent-mcp path
24
24
  npx -y shizai-agent-mcp uninstall
25
25
  ```
26
26
 
27
- 当前 npm launcher 版本为 `0.2.4`,默认下载最新的 `v0.6.4` Windows x64 资产:
28
- `shizai-agent-v0.6.4-windows-x64.zip`。`@playwright/mcp` 会作为 npm 依赖自动安装。
27
+ 当前 npm launcher 版本为 `0.2.5`,默认下载最新的 `v0.6.5` Windows x64 资产:
28
+ `shizai-agent-v0.6.5-windows-x64.zip`。npm 包不附带浏览器自动化依赖,也不要求用户安装额外扩展。
29
29
 
30
- 注册默认会加入 `browsermcp` 服务,它配合 Browser MCP Chrome 扩展连接当前已登录标签页;
31
- `--with-playwright` 或 `--with-browsers` 会额外加入隔离的 `playwright` 服务。
32
- `--playwright-only` 可关闭默认的 BrowserMCP。
33
-
34
- 发布 Release 时需要同时上传同名 `.sha256` 文件。测试企业内网镜像时,可设置
35
- `SHIZAI_AGENT_RELEASE_BASE_URL`;测试其他版本可设置 `SHIZAI_AGENT_VERSION`。
30
+ 发布 Release 时必须同时上传同名 `.sha256` 文件。安装器先校验、解压到同盘暂存目录,
31
+ 再原子替换现有版本;失败会恢复旧安装。测试企业内网镜像时可设置
32
+ `SHIZAI_AGENT_RELEASE_BASE_URL`,测试其他版本可设置 `SHIZAI_AGENT_VERSION`。
33
+ 仅临时调试未签名镜像时可显式设置 `SHIZAI_AGENT_ALLOW_UNVERIFIED=1`。
36
34
 
37
35
  本地设计器需要正在运行且已登录。MCP 是本地 stdio 服务,Codex 显示 `Auth: Unsupported`
38
36
  属于正常状态,实在 Agent 的登录会话由桌面设计器提供。
@@ -7,21 +7,27 @@ const os = require('node:os');
7
7
  const crypto = require('node:crypto');
8
8
  const { execFileSync, spawn } = require('node:child_process');
9
9
 
10
- const SERVER = 'shizai-agent';
11
- const REPO = process.env.SHIZAI_AGENT_REPO || 'yimoxzy/shizai-agent';
12
- const DEFAULT_VERSION = process.env.SHIZAI_AGENT_VERSION || '0.6.4';
13
- const BROWSERMCP_VERSION = process.env.SHIZAI_BROWSERMCP_VERSION || '0.1.3';
14
- const PLAYWRIGHT_VERSION = process.env.SHIZAI_PLAYWRIGHT_VERSION || '0.0.79';
10
+ const SERVER = 'shizai-agent';
11
+ const REPO = process.env.SHIZAI_AGENT_REPO || 'yimoxzy/shizai-agent';
12
+ const DEFAULT_VERSION = process.env.SHIZAI_AGENT_VERSION || '0.6.5';
15
13
  const installRoot = process.env.SHIZAI_AGENT_HOME || path.join(
16
14
  process.env.LOCALAPPDATA || path.join(os.homedir(), '.local'), 'Programs', 'ShizaiAgent'
17
15
  );
18
16
 
19
17
  function fail(message) { console.error(`shizai-agent-mcp: ${message}`); process.exitCode = 1; }
20
18
  function json(value) { console.log(JSON.stringify(value, null, 2)); }
21
- function help() {
22
- console.log(`Shizai Agent MCP installer\n\nUsage:\n shizai-agent-mcp install [--version VERSION]\n shizai-agent-mcp register <codex|cursor|claude> [--with-browsermcp]\n shizai-agent-mcp register <codex|cursor|claude> [--with-playwright]\n shizai-agent-mcp register <codex|cursor|claude> --with-browsers\n shizai-agent-mcp status\n shizai-agent-mcp path\n shizai-agent-mcp uninstall\n\nEnvironment:\n SHIZAI_AGENT_VERSION Release version (default: ${DEFAULT_VERSION})\n SHIZAI_AGENT_HOME Installation directory\n SHIZAI_AGENT_REPO GitHub repository owner/name\n SHIZAI_AGENT_RELEASE_BASE_URL Override release asset base URL\n SHIZAI_BROWSERMCP_VERSION BrowserMCP version (default: ${BROWSERMCP_VERSION})\n SHIZAI_PLAYWRIGHT_VERSION Bundled Playwright MCP version (default: ${PLAYWRIGHT_VERSION})\n`);
19
+ function help() {
20
+ console.log(`Shizai Agent MCP installer\n\nUsage:\n shizai-agent-mcp install [--version VERSION]\n shizai-agent-mcp register <codex|cursor|claude>\n shizai-agent-mcp status\n shizai-agent-mcp path\n shizai-agent-mcp uninstall\n\nEnvironment:\n SHIZAI_AGENT_VERSION Release version (default: ${DEFAULT_VERSION})\n SHIZAI_AGENT_HOME Installation directory\n SHIZAI_AGENT_REPO GitHub repository owner/name\n SHIZAI_AGENT_RELEASE_BASE_URL Override release asset base URL\n`);
23
21
  }
24
- function binaryPath() { return path.join(installRoot, 'shizai-agent-mcp.exe'); }
22
+ function binaryPath() { return path.join(installRoot, 'shizai-agent-mcp.exe'); }
23
+ function validateInstallRoot() {
24
+ const resolved = path.resolve(installRoot);
25
+ const parsed = path.parse(resolved);
26
+ if (resolved === parsed.root || resolved === path.resolve(os.homedir())) {
27
+ throw new Error(`拒绝使用过宽的安装目录:${resolved}`);
28
+ }
29
+ return resolved;
30
+ }
25
31
  function psQuote(value) { return `'${String(value).replaceAll("'", "''")}'`; }
26
32
  function runPowerShell(script, args = []) {
27
33
  return execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script, ...args], { encoding: 'utf8' });
@@ -44,80 +50,84 @@ function download(url, destination) {
44
50
  const script = `Invoke-WebRequest -UseBasicParsing -Uri ${psQuote(url)} -OutFile ${psQuote(destination)}`;
45
51
  runPowerShell(script);
46
52
  }
47
- function install(version) {
48
- const asset = assetName(version);
49
- const url = releaseUrl(version, asset);
50
- const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'shizai-agent-'));
51
- const archive = path.join(temp, asset);
52
- const checksum = path.join(temp, `${asset}.sha256`);
53
- try {
54
- console.log(`下载 ${url}`);
55
- download(url, archive);
56
- try {
57
- download(`${url}.sha256`, checksum);
58
- const text = fs.readFileSync(checksum, 'utf8');
59
- const expected = (text.match(/[a-f0-9]{64}/i) || [])[0];
60
- if (expected) verifySha256(archive, expected);
61
- } catch (error) {
62
- if (process.env.SHIZAI_AGENT_REQUIRE_CHECKSUM === '1') throw error;
63
- console.warn('未找到校验文件,继续解压;发布正式版本时建议上传 .sha256。');
64
- }
65
- fs.mkdirSync(installRoot, { recursive: true });
66
- runPowerShell(`if (Test-Path -LiteralPath ${psQuote(installRoot)}) { Remove-Item -LiteralPath ${psQuote(installRoot)} -Recurse -Force }; New-Item -ItemType Directory -Force -Path ${psQuote(installRoot)} | Out-Null; Expand-Archive -LiteralPath ${psQuote(archive)} -DestinationPath ${psQuote(installRoot)} -Force; $nested = Get-ChildItem -LiteralPath ${psQuote(installRoot)} -Directory | Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName 'shizai-agent-mcp.exe') } | Select-Object -First 1; if ($nested) { Get-ChildItem -LiteralPath $nested.FullName -Force | Move-Item -Destination ${psQuote(installRoot)} -Force }`);
67
- if (!fs.existsSync(binaryPath())) throw new Error(`安装包中未找到 ${binaryPath()}`);
68
- fs.writeFileSync(path.join(installRoot, 'version.json'), JSON.stringify({ version, asset, installedAt: new Date().toISOString() }, null, 2));
69
- json({ installed: true, version, directory: installRoot, command: binaryPath() });
70
- } finally {
71
- fs.rmSync(temp, { recursive: true, force: true });
72
- }
73
- }
53
+ function install(version) {
54
+ const destination = validateInstallRoot();
55
+ const asset = assetName(version);
56
+ const url = releaseUrl(version, asset);
57
+ const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'shizai-agent-'));
58
+ const archive = path.join(temp, asset);
59
+ const checksum = path.join(temp, `${asset}.sha256`);
60
+ const parent = path.dirname(destination);
61
+ const nonce = `${process.pid}-${Date.now()}`;
62
+ const stage = path.join(parent, `.shizai-agent-stage-${nonce}`);
63
+ const backup = path.join(parent, `.shizai-agent-backup-${nonce}`);
64
+ let verified = false;
65
+ try {
66
+ console.log(`下载 ${url}`);
67
+ download(url, archive);
68
+ try {
69
+ download(`${url}.sha256`, checksum);
70
+ const text = fs.readFileSync(checksum, 'utf8');
71
+ const expected = (text.match(/[a-f0-9]{64}/i) || [])[0];
72
+ if (!expected) throw new Error('校验文件中没有 SHA-256');
73
+ verifySha256(archive, expected);
74
+ verified = true;
75
+ } catch (error) {
76
+ if (process.env.SHIZAI_AGENT_ALLOW_UNVERIFIED !== '1') throw error;
77
+ console.warn('SHIZAI_AGENT_ALLOW_UNVERIFIED=1:本次跳过发布包校验。');
78
+ }
79
+ fs.mkdirSync(parent, { recursive: true });
80
+ fs.mkdirSync(stage);
81
+ runPowerShell(`Expand-Archive -LiteralPath ${psQuote(archive)} -DestinationPath ${psQuote(stage)} -Force`);
82
+ let source = stage;
83
+ if (!fs.existsSync(path.join(source, 'shizai-agent-mcp.exe'))) {
84
+ const nested = fs.readdirSync(stage, { withFileTypes: true })
85
+ .filter(entry => entry.isDirectory())
86
+ .map(entry => path.join(stage, entry.name))
87
+ .find(candidate => fs.existsSync(path.join(candidate, 'shizai-agent-mcp.exe')));
88
+ if (!nested) throw new Error('发布包中未找到 shizai-agent-mcp.exe');
89
+ source = nested;
90
+ }
91
+ fs.writeFileSync(path.join(source, 'version.json'), JSON.stringify({ version, asset, installedAt: new Date().toISOString() }, null, 2));
92
+ let previousMoved = false;
93
+ try {
94
+ if (fs.existsSync(destination)) {
95
+ fs.renameSync(destination, backup);
96
+ previousMoved = true;
97
+ }
98
+ fs.renameSync(source, destination);
99
+ if (previousMoved) fs.rmSync(backup, { recursive: true, force: true });
100
+ } catch (error) {
101
+ if (!fs.existsSync(destination) && previousMoved && fs.existsSync(backup)) {
102
+ fs.renameSync(backup, destination);
103
+ }
104
+ throw error;
105
+ }
106
+ json({ installed: true, version, directory: destination, command: binaryPath(), verified });
107
+ } finally {
108
+ fs.rmSync(temp, { recursive: true, force: true });
109
+ fs.rmSync(stage, { recursive: true, force: true });
110
+ if (fs.existsSync(backup) && fs.existsSync(destination)) fs.rmSync(backup, { recursive: true, force: true });
111
+ }
112
+ }
74
113
  function replaceTomlSection(content, section, replacement) {
75
114
  const escaped = section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
76
115
  const pattern = new RegExp(`^\\[${escaped}\\]\\r?\\n[\\s\\S]*?(?=^\\[|$)`, 'm');
77
116
  const next = content.replace(pattern, '').trimEnd();
78
117
  return `${next}${next ? '\r\n\r\n' : ''}${replacement.trim()}\r\n`;
79
118
  }
80
- function playwrightSpec() {
81
- try {
82
- const packageJSON = require.resolve('@playwright/mcp/package.json');
83
- const packageRoot = path.dirname(packageJSON);
84
- return { command: process.execPath, args: [path.join(packageRoot, 'cli.js')] };
85
- } catch (error) {
86
- return { command: 'npx', args: ['-y', `@playwright/mcp@${PLAYWRIGHT_VERSION}`] };
87
- }
88
- }
89
- function browserMcpSpec() {
90
- try {
91
- const packageJSON = require.resolve('@browsermcp/mcp/package.json');
92
- const packageRoot = path.dirname(packageJSON);
93
- return { command: process.execPath, args: [path.join(packageRoot, 'dist', 'index.js')] };
94
- } catch (error) {
95
- return { command: 'npx', args: ['-y', `@browsermcp/mcp@${BROWSERMCP_VERSION}`] };
96
- }
97
- }
98
- function tomlString(value) { return JSON.stringify(String(value)); }
99
- function tomlArray(values) { return `[${values.map(tomlString).join(', ')}]`; }
100
- function registerCodex(withPlaywright = false, withBrowserMcp = true) {
119
+ function tomlString(value) { return JSON.stringify(String(value)); }
120
+ function registerCodex() {
101
121
  const config = process.env.SHIZAI_CODEX_CONFIG || path.join(os.homedir(), '.codex', 'config.toml');
102
122
  fs.mkdirSync(path.dirname(config), { recursive: true });
103
123
  const old = fs.existsSync(config) ? fs.readFileSync(config, 'utf8') : '';
104
124
  if (old) fs.copyFileSync(config, `${config}.shizai-agent.bak`);
105
- const section = `[mcp_servers.${SERVER}]\ncommand = ${psQuote(binaryPath())}\nargs = []\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
106
- let next = replaceTomlSection(old, `mcp_servers.${SERVER}`, section);
107
- if (withPlaywright) {
108
- const spec = playwrightSpec();
109
- const browserSection = `[mcp_servers.playwright]\ncommand = ${tomlString(spec.command)}\nargs = ${tomlArray(spec.args)}\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
110
- next = replaceTomlSection(next, 'mcp_servers.playwright', browserSection);
111
- }
112
- if (withBrowserMcp) {
113
- const spec = browserMcpSpec();
114
- const browserMcpSection = `[mcp_servers.browsermcp]\ncommand = ${tomlString(spec.command)}\nargs = ${tomlArray(spec.args)}\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
115
- next = replaceTomlSection(next, 'mcp_servers.browsermcp', browserMcpSection);
116
- }
117
- fs.writeFileSync(config, next, 'utf8');
118
- json({ registered: true, client: 'codex', config, command: binaryPath(), browsermcp: withBrowserMcp ? browserMcpSpec() : null, playwright: withPlaywright ? playwrightSpec() : null });
119
- }
120
- function registerJsonClient(client, withPlaywright = false, withBrowserMcp = true) {
125
+ const section = `[mcp_servers.${SERVER}]\ncommand = ${tomlString(binaryPath())}\nargs = []\nstartup_timeout_sec = 30\ntool_timeout_sec = 120`;
126
+ const next = replaceTomlSection(old, `mcp_servers.${SERVER}`, section);
127
+ fs.writeFileSync(config, next, 'utf8');
128
+ json({ registered: true, client: 'codex', config, command: binaryPath() });
129
+ }
130
+ function registerJsonClient(client) {
121
131
  const locations = {
122
132
  cursor: process.env.SHIZAI_CURSOR_CONFIG || path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Cursor', 'User', 'globalStorage', 'mcp.json'),
123
133
  claude: process.env.SHIZAI_CLAUDE_CONFIG || path.join(process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json')
@@ -126,41 +136,34 @@ function registerJsonClient(client, withPlaywright = false, withBrowserMcp = tru
126
136
  if (!file) throw new Error(`未知客户端:${client}`);
127
137
  fs.mkdirSync(path.dirname(file), { recursive: true });
128
138
  const data = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {};
129
- data.mcpServers = data.mcpServers || {};
130
- data.mcpServers[SERVER] = { command: binaryPath(), args: [] };
131
- if (withPlaywright) {
132
- const spec = playwrightSpec();
133
- data.mcpServers.playwright = { command: spec.command, args: spec.args };
134
- }
135
- if (withBrowserMcp) {
136
- const spec = browserMcpSpec();
137
- data.mcpServers.browsermcp = { command: spec.command, args: spec.args };
138
- }
139
- fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
140
- json({ registered: true, client, config: file, command: binaryPath(), browsermcp: withBrowserMcp ? browserMcpSpec() : null, playwright: withPlaywright ? playwrightSpec() : null });
139
+ data.mcpServers = data.mcpServers || {};
140
+ data.mcpServers[SERVER] = { command: binaryPath(), args: [] };
141
+ fs.writeFileSync(file, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
142
+ json({ registered: true, client, config: file, command: binaryPath() });
141
143
  }
142
144
  function launch() {
143
145
  if (!fs.existsSync(binaryPath())) install(DEFAULT_VERSION);
144
146
  const child = spawn(binaryPath(), process.argv.slice(2), { stdio: 'inherit', windowsHide: true });
145
147
  child.on('exit', code => { process.exitCode = code ?? 0; });
146
148
  }
147
- function main() {
148
- const [command, target, ...rest] = process.argv.slice(2);
149
+ function main() {
150
+ const [command, target, ...rest] = process.argv.slice(2);
149
151
  if (!command) return launch();
150
152
  if (command === '--help' || command === '-h') return help();
151
- if (command === 'install') {
152
- const index = rest.indexOf('--version');
153
- return install(index >= 0 ? rest[index + 1] : (target && !target.startsWith('-') ? target : DEFAULT_VERSION));
154
- }
155
- if (command === 'register') {
156
- if (!fs.existsSync(binaryPath())) throw new Error('尚未安装,请先运行 install');
157
- const withPlaywright = rest.includes('--with-playwright') || rest.includes('--with-browsers');
158
- const withBrowserMcp = !rest.includes('--playwright-only');
159
- if (target === 'codex') return registerCodex(withPlaywright, withBrowserMcp);
160
- if (target === 'cursor' || target === 'claude') return registerJsonClient(target, withPlaywright, withBrowserMcp);
161
- throw new Error('请指定 codex、cursor 或 claude');
162
- }
163
- if (command === 'status') return json({ installed: fs.existsSync(binaryPath()), directory: installRoot, command: binaryPath(), versionFile: path.join(installRoot, 'version.json'), browsermcp: browserMcpSpec(), playwright: playwrightSpec() });
153
+ if (command === 'install') {
154
+ const args = [target, ...rest].filter(value => value !== undefined);
155
+ const index = args.indexOf('--version');
156
+ const version = index >= 0 ? args[index + 1] : args.find(value => !value.startsWith('-'));
157
+ if (index >= 0 && !version) throw new Error('--version 后需要版本号');
158
+ return install(version || DEFAULT_VERSION);
159
+ }
160
+ if (command === 'register') {
161
+ if (!fs.existsSync(binaryPath())) throw new Error('尚未安装,请先运行 install');
162
+ if (target === 'codex') return registerCodex();
163
+ if (target === 'cursor' || target === 'claude') return registerJsonClient(target);
164
+ throw new Error('请指定 codex、cursor 或 claude');
165
+ }
166
+ if (command === 'status') return json({ installed: fs.existsSync(binaryPath()), directory: installRoot, command: binaryPath(), versionFile: path.join(installRoot, 'version.json') });
164
167
  if (command === 'path') return console.log(binaryPath());
165
168
  if (command === 'uninstall') { if (fs.existsSync(installRoot)) fs.rmSync(installRoot, { recursive: true, force: true }); return json({ uninstalled: true, directory: installRoot }); }
166
169
  throw new Error(`未知命令:${command}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shizai-agent-mcp",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Installer and launcher for the Shizai Agent local MCP server",
5
5
  "bin": {
6
6
  "shizai-agent-mcp": "bin/shizai-agent-mcp.js"
@@ -12,10 +12,12 @@
12
12
  "bin",
13
13
  "README.md"
14
14
  ],
15
- "dependencies": {
16
- "@browsermcp/mcp": "0.1.3",
17
- "@playwright/mcp": "0.0.79"
18
- },
19
- "keywords": ["mcp", "shizai-agent", "codex", "cursor", "claude"],
15
+ "keywords": [
16
+ "mcp",
17
+ "shizai-agent",
18
+ "codex",
19
+ "cursor",
20
+ "claude"
21
+ ],
20
22
  "license": "MIT"
21
23
  }