@bigbrain-work/mcp-connect 1.2.1 → 1.3.0
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/LICENSE +21 -0
- package/README.md +34 -53
- package/bin/mcp-connect.js +4 -4
- package/bin/shiliu.js +8 -0
- package/package.json +13 -5
- package/src/arguments.js +78 -18
- package/src/authorization.js +49 -0
- package/src/cli.js +431 -52
- package/src/configurators.js +118 -137
- package/src/constants.js +12 -6
- package/src/credentials.js +166 -148
- package/src/detection.js +16 -21
- package/src/device-auth-client.js +130 -0
- package/src/login-flow.js +39 -0
- package/src/proxy.js +93 -0
- package/src/remote-client.js +37 -0
- package/src/security.js +63 -0
- package/src/status.js +117 -58
- package/src/token-store.js +116 -0
- package/src/updater.js +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bigbrain Work
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,73 +1,54 @@
|
|
|
1
1
|
# @bigbrain-work/mcp-connect
|
|
2
2
|
|
|
3
|
-
石榴 AI
|
|
3
|
+
石榴 AI 官方命令行工具。它负责验证当前凭据、配置常见 Agent、检查远程 MCP、读取实时工具清单,并为只支持 stdio 的客户端提供桥接。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## 使用
|
|
8
|
-
|
|
9
|
-
先在石榴 AI 页面复制当前账户的 API Key。推荐直接运行智能一键配置,安装器会检测本机已有的 Codex、Claude Code 和 Cursor:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npx -y @bigbrain-work/mcp-connect
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
连接完成后,Agent 可通过同一个 MCP 地址调用 `get_openstoryline_installer`,获取 Windows 或 macOS 本地 AI 剪辑工具的短期安装地址。
|
|
16
|
-
|
|
17
|
-
也可以明确指定客户端:
|
|
5
|
+
## 命令
|
|
18
6
|
|
|
19
7
|
```bash
|
|
20
|
-
|
|
21
|
-
npx -y @bigbrain-work/mcp-connect
|
|
22
|
-
npx -y @bigbrain-work/mcp-connect --agent cursor
|
|
23
|
-
```
|
|
8
|
+
# 微信扫码登录;令牌进入操作系统凭据库
|
|
9
|
+
npx -y @bigbrain-work/mcp-connect login
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
npx -y @bigbrain-work/mcp-connect --agent openclaw
|
|
29
|
-
npx -y @bigbrain-work/mcp-connect --agent workbuddy
|
|
30
|
-
```
|
|
11
|
+
# Agent/无头环境:先创建会话并立即返回,再按返回的 poll_command 继续
|
|
12
|
+
npx -y @bigbrain-work/mcp-connect login --no-wait --json
|
|
13
|
+
npx -y @bigbrain-work/mcp-connect login poll --session <会话号> --wait --json
|
|
31
14
|
|
|
32
|
-
|
|
15
|
+
# 自动检测并配置已安装的 Agent
|
|
16
|
+
npx -y @bigbrain-work/mcp-connect install
|
|
33
17
|
|
|
34
|
-
|
|
18
|
+
# 只配置一个 Agent
|
|
19
|
+
npx -y @bigbrain-work/mcp-connect install --agent codex
|
|
35
20
|
|
|
36
|
-
|
|
21
|
+
# 检查本地配置、远程连接和 tools/list
|
|
37
22
|
npx -y @bigbrain-work/mcp-connect status
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
如已设置 `SHILIU_AI_API_KEY` 环境变量,安装器会直接使用该变量,不再提示输入。
|
|
41
|
-
|
|
42
|
-
## 安全说明
|
|
43
23
|
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
- Windows 上,安装器通过标准输入将 API Key 写入当前用户环境变量,避免进入终端历史和子进程命令行。
|
|
47
|
-
- macOS/Linux 上,API Key 保存在权限为 `0600` 的 `~/.config/shiliu-ai/env`,并由用户登录配置加载。
|
|
48
|
-
- `status` 命令只显示“已设置/未设置”,不会输出 API Key。
|
|
24
|
+
# 显示服务实时返回的工具
|
|
25
|
+
npx -y @bigbrain-work/mcp-connect tools
|
|
49
26
|
|
|
50
|
-
|
|
27
|
+
# 撤销刷新令牌并清除本机凭据
|
|
28
|
+
npx -y @bigbrain-work/mcp-connect logout
|
|
29
|
+
```
|
|
51
30
|
|
|
52
|
-
|
|
53
|
-
- MCP 地址是 Agent 连接服务所用的完整 HTTP 接口地址,不需要另配其他域名。
|
|
54
|
-
- 认证方式:`Authorization: Bearer <API_KEY>`
|
|
55
|
-
- 配置名称:`shiliu_mcp`
|
|
31
|
+
安装后也可使用 `shiliu` 命令。原有 `mcp-connect` 命令保持兼容。
|
|
56
32
|
|
|
57
|
-
|
|
33
|
+
`--url` 和 `--auth-url` 默认只接受石榴 AI 正式服务地址。开发者如需连接
|
|
34
|
+
`127.0.0.1`、`localhost` 或 `::1` 上的测试服务,必须显式添加
|
|
35
|
+
`--allow-localhost`;该开关不会放行局域网或公网第三方地址。
|
|
58
36
|
|
|
59
|
-
|
|
37
|
+
## 通用 Agent
|
|
60
38
|
|
|
61
39
|
```bash
|
|
62
|
-
|
|
63
|
-
npm whoami
|
|
64
|
-
npm pack --dry-run
|
|
65
|
-
npm publish --access public
|
|
40
|
+
npx -y @bigbrain-work/mcp-connect install --agent openclaw
|
|
66
41
|
```
|
|
67
42
|
|
|
68
|
-
|
|
43
|
+
对未内置适配器的 Agent,CLI 会输出标准 stdio MCP 配置。名称只用于提示,不要求我们枚举所有 Agent。
|
|
69
44
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
45
|
+
## 凭据安全
|
|
46
|
+
|
|
47
|
+
- 不支持 `--api-key` 参数,避免密钥进入 shell 历史。
|
|
48
|
+
- 默认微信扫码后获得短期访问令牌;过期前使用可轮换刷新令牌自动更新。
|
|
49
|
+
- 令牌保存在 Windows Credential Manager、macOS Keychain 或 Linux Secret Service,不写入 Agent 的 MCP 配置。
|
|
50
|
+
- 登录后先以新访问令牌调用一次 `tools/list`,验证成功后才保存。
|
|
51
|
+
- 非阻塞登录只把设备码暂存在系统凭据库,JSON 输出不包含设备码或访问令牌。
|
|
52
|
+
- `status --json` 与 `tools --json` 不输出任何令牌。
|
|
53
|
+
|
|
54
|
+
迁移期如确需使用现有 API Key,可显式运行 `login --legacy-api-key`;此入口将在设备登录稳定后移除。
|
package/bin/mcp-connect.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { runCli } from
|
|
3
|
+
import { runCli } from "../src/cli.js";
|
|
4
4
|
|
|
5
5
|
runCli().catch((error) => {
|
|
6
|
-
console.error(
|
|
7
|
-
process.exitCode = 1
|
|
8
|
-
})
|
|
6
|
+
console.error(`错误:${error.message}`);
|
|
7
|
+
process.exitCode = 1;
|
|
8
|
+
});
|
package/bin/shiliu.js
ADDED
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigbrain-work/mcp-connect",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "石榴 AI
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "石榴 AI 官方 CLI:登录、配置 Agent、检查 MCP 状态并桥接远程工具。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"mcp-connect": "bin/mcp-connect.js"
|
|
7
|
+
"mcp-connect": "bin/mcp-connect.js",
|
|
8
|
+
"shiliu": "bin/shiliu.js"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"bin",
|
|
11
12
|
"src",
|
|
12
|
-
"README.md"
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
13
15
|
],
|
|
14
16
|
"scripts": {
|
|
15
17
|
"test": "node --test",
|
|
@@ -28,5 +30,11 @@
|
|
|
28
30
|
"publishConfig": {
|
|
29
31
|
"access": "public"
|
|
30
32
|
},
|
|
31
|
-
"
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
35
|
+
"@napi-rs/keyring": "2.0.0",
|
|
36
|
+
"qrcode": "1.5.4",
|
|
37
|
+
"zod": "^3.25.0"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT"
|
|
32
40
|
}
|
package/src/arguments.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import { parseArgs } from
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
|
|
3
|
+
const COMMANDS = new Set([
|
|
4
|
+
"install",
|
|
5
|
+
"login",
|
|
6
|
+
"logout",
|
|
7
|
+
"status",
|
|
8
|
+
"tools",
|
|
9
|
+
"mcp",
|
|
10
|
+
"proxy",
|
|
11
|
+
"update",
|
|
12
|
+
]);
|
|
2
13
|
|
|
3
14
|
export function parseCliArguments(args) {
|
|
4
15
|
const parsed = parseArgs({
|
|
@@ -7,45 +18,94 @@ export function parseCliArguments(args) {
|
|
|
7
18
|
strict: true,
|
|
8
19
|
options: {
|
|
9
20
|
agent: {
|
|
10
|
-
type:
|
|
11
|
-
short:
|
|
21
|
+
type: "string",
|
|
22
|
+
short: "a",
|
|
12
23
|
},
|
|
13
|
-
|
|
14
|
-
type:
|
|
24
|
+
"dry-run": {
|
|
25
|
+
type: "boolean",
|
|
15
26
|
default: false,
|
|
16
27
|
},
|
|
17
28
|
home: {
|
|
18
|
-
type:
|
|
29
|
+
type: "string",
|
|
30
|
+
},
|
|
31
|
+
url: {
|
|
32
|
+
type: "string",
|
|
33
|
+
},
|
|
34
|
+
"auth-url": {
|
|
35
|
+
type: "string",
|
|
36
|
+
},
|
|
37
|
+
"allow-localhost": {
|
|
38
|
+
type: "boolean",
|
|
39
|
+
default: false,
|
|
40
|
+
},
|
|
41
|
+
"legacy-api-key": {
|
|
42
|
+
type: "boolean",
|
|
43
|
+
default: false,
|
|
44
|
+
},
|
|
45
|
+
"no-wait": {
|
|
46
|
+
type: "boolean",
|
|
47
|
+
default: false,
|
|
48
|
+
},
|
|
49
|
+
wait: {
|
|
50
|
+
type: "boolean",
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
53
|
+
session: {
|
|
54
|
+
type: "string",
|
|
55
|
+
},
|
|
56
|
+
json: {
|
|
57
|
+
type: "boolean",
|
|
58
|
+
default: false,
|
|
19
59
|
},
|
|
20
60
|
help: {
|
|
21
|
-
type:
|
|
22
|
-
short:
|
|
61
|
+
type: "boolean",
|
|
62
|
+
short: "h",
|
|
23
63
|
default: false,
|
|
24
64
|
},
|
|
25
65
|
version: {
|
|
26
|
-
type:
|
|
27
|
-
short:
|
|
66
|
+
type: "boolean",
|
|
67
|
+
short: "v",
|
|
28
68
|
default: false,
|
|
29
69
|
},
|
|
30
70
|
},
|
|
31
|
-
})
|
|
71
|
+
});
|
|
32
72
|
|
|
33
|
-
const command = parsed.positionals[0] ||
|
|
34
|
-
if (!
|
|
35
|
-
throw new Error(`不支持的命令:${command}`)
|
|
73
|
+
const command = parsed.positionals[0] || "install";
|
|
74
|
+
if (!COMMANDS.has(command)) {
|
|
75
|
+
throw new Error(`不支持的命令:${command}`);
|
|
76
|
+
}
|
|
77
|
+
const subcommand = parsed.positionals[1];
|
|
78
|
+
if (
|
|
79
|
+
parsed.positionals.length > 2 ||
|
|
80
|
+
(subcommand && !(command === "login" && subcommand === "poll"))
|
|
81
|
+
) {
|
|
82
|
+
throw new Error(`无法识别的参数:${parsed.positionals.slice(1).join(" ")}`);
|
|
36
83
|
}
|
|
37
84
|
|
|
38
|
-
const agent = parsed.values.agent?.toLowerCase()
|
|
85
|
+
const agent = parsed.values.agent?.toLowerCase();
|
|
39
86
|
if (agent && !/^[a-z0-9][a-z0-9_-]{0,63}$/u.test(agent)) {
|
|
40
|
-
throw new Error(
|
|
87
|
+
throw new Error("客户端名称只能包含小写字母、数字、下划线和连字符");
|
|
88
|
+
}
|
|
89
|
+
const session = parsed.values.session;
|
|
90
|
+
if (session && !/^[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$/u.test(session)) {
|
|
91
|
+
throw new Error("登录会话编号格式无效");
|
|
41
92
|
}
|
|
42
93
|
|
|
43
94
|
return {
|
|
44
95
|
command,
|
|
96
|
+
subcommand,
|
|
45
97
|
agent,
|
|
46
|
-
dryRun: parsed.values[
|
|
98
|
+
dryRun: parsed.values["dry-run"],
|
|
47
99
|
home: parsed.values.home,
|
|
100
|
+
url: parsed.values.url,
|
|
101
|
+
authUrl: parsed.values["auth-url"],
|
|
102
|
+
allowLocalhost: parsed.values["allow-localhost"],
|
|
103
|
+
legacyApiKey: parsed.values["legacy-api-key"],
|
|
104
|
+
noWait: parsed.values["no-wait"],
|
|
105
|
+
wait: parsed.values.wait,
|
|
106
|
+
session,
|
|
107
|
+
json: parsed.values.json,
|
|
48
108
|
help: parsed.values.help,
|
|
49
109
|
version: parsed.values.version,
|
|
50
|
-
}
|
|
110
|
+
};
|
|
51
111
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AUTH_URL } from "./constants.js";
|
|
2
|
+
import { readPersistedApiKey } from "./credentials.js";
|
|
3
|
+
import {
|
|
4
|
+
DeviceAuthClient,
|
|
5
|
+
normalizeTokenResponse,
|
|
6
|
+
} from "./device-auth-client.js";
|
|
7
|
+
import { TokenStore } from "./token-store.js";
|
|
8
|
+
|
|
9
|
+
const REFRESH_WINDOW_MS = 60_000;
|
|
10
|
+
|
|
11
|
+
export async function resolveAuthorization({
|
|
12
|
+
home,
|
|
13
|
+
platform = process.platform,
|
|
14
|
+
env = process.env,
|
|
15
|
+
authUrl = AUTH_URL,
|
|
16
|
+
tokenStore = new TokenStore(),
|
|
17
|
+
authClient = new DeviceAuthClient({ baseUrl: authUrl }),
|
|
18
|
+
now = Date.now,
|
|
19
|
+
} = {}) {
|
|
20
|
+
let tokenSet;
|
|
21
|
+
try {
|
|
22
|
+
tokenSet = await tokenStore.load();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
const legacyApiKey = readPersistedApiKey({ home, platform, env })?.trim();
|
|
25
|
+
if (legacyApiKey) {
|
|
26
|
+
return {
|
|
27
|
+
token: legacyApiKey,
|
|
28
|
+
source: "legacy_api_key",
|
|
29
|
+
warning: error.message,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
if (tokenSet) {
|
|
35
|
+
if (tokenSet.expiresAt > now() + REFRESH_WINDOW_MS) {
|
|
36
|
+
return { token: tokenSet.accessToken, source: "device" };
|
|
37
|
+
}
|
|
38
|
+
const refreshed = normalizeTokenResponse(
|
|
39
|
+
await authClient.refresh(tokenSet.refreshToken),
|
|
40
|
+
now(),
|
|
41
|
+
);
|
|
42
|
+
await tokenStore.save(refreshed);
|
|
43
|
+
return { token: refreshed.accessToken, source: "device" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const legacyApiKey = readPersistedApiKey({ home, platform, env })?.trim();
|
|
47
|
+
if (legacyApiKey) return { token: legacyApiKey, source: "legacy_api_key" };
|
|
48
|
+
return { token: "", source: "none" };
|
|
49
|
+
}
|