@bigbrain-work/mcp-connect 1.2.0 → 1.2.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.
package/README.md CHANGED
@@ -29,7 +29,13 @@ npx -y @bigbrain-work/mcp-connect --agent openclaw
29
29
  npx -y @bigbrain-work/mcp-connect --agent workbuddy
30
30
  ```
31
31
 
32
- 对于尚未内置配置适配器的 Agent,安装器不会修改未知配置文件,而是输出石榴 AI 的通用 HTTP MCP 地址和认证方式。
32
+ 对于尚未内置配置适配器的 Agent,安装器不会猜测其私有配置文件,而是输出一份标准 stdio MCP 配置。该配置统一运行:
33
+
34
+ ```bash
35
+ npx -y @bigbrain-work/mcp-connect proxy
36
+ ```
37
+
38
+ 桥接器从本机安全存储读取 `SHILIU_AI_API_KEY`,再连接远程 HTTP MCP。这样 WorkBuddy、OpenClaw 以及后续其他支持 stdio MCP 的 Agent 不需要分别开发认证头适配器。支持远程 HTTP MCP 和自定义认证头的客户端仍可直接连接服务地址。
33
39
 
34
40
  安装器会隐藏输入 API Key。配置完成后,重启对应客户端并检查连接:
35
41
 
@@ -46,6 +52,7 @@ npx -y @bigbrain-work/mcp-connect status
46
52
  - Windows 上,安装器通过标准输入将 API Key 写入当前用户环境变量,避免进入终端历史和子进程命令行。
47
53
  - macOS/Linux 上,API Key 保存在权限为 `0600` 的 `~/.config/shiliu-ai/env`,并由用户登录配置加载。
48
54
  - `status` 命令只显示“已设置/未设置”,不会输出 API Key。
55
+ - 通用 `proxy` 的 stdout 只承载 MCP 协议数据,不会输出 API Key。
49
56
 
50
57
  ## 服务信息
51
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigbrain-work/mcp-connect",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "石榴 AI MCP 智能配置工具,支持自动检测、通用 HTTP MCP 接入和本地 AI 剪辑工具安装。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,5 +28,9 @@
28
28
  "publishConfig": {
29
29
  "access": "public"
30
30
  },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "1.29.0",
33
+ "zod": "^3.25.0"
34
+ },
31
35
  "license": "UNLICENSED"
32
36
  }
package/src/arguments.js CHANGED
@@ -17,6 +17,9 @@ export function parseCliArguments(args) {
17
17
  home: {
18
18
  type: 'string',
19
19
  },
20
+ url: {
21
+ type: 'string',
22
+ },
20
23
  help: {
21
24
  type: 'boolean',
22
25
  short: 'h',
@@ -31,7 +34,7 @@ export function parseCliArguments(args) {
31
34
  })
32
35
 
33
36
  const command = parsed.positionals[0] || 'install'
34
- if (!['install', 'status'].includes(command)) {
37
+ if (!['install', 'proxy', 'status'].includes(command)) {
35
38
  throw new Error(`不支持的命令:${command}`)
36
39
  }
37
40
 
@@ -45,6 +48,7 @@ export function parseCliArguments(args) {
45
48
  agent,
46
49
  dryRun: parsed.values['dry-run'],
47
50
  home: parsed.values.home,
51
+ url: parsed.values.url,
48
52
  help: parsed.values.help,
49
53
  version: parsed.values.version,
50
54
  }
package/src/cli.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  SUPPORTED_AGENTS,
14
14
  } from './constants.js'
15
15
  import { persistApiKey, resolveApiKey } from './credentials.js'
16
+ import { runProxy } from './proxy.js'
16
17
  import { printStatus } from './status.js'
17
18
 
18
19
  function printHelp() {
@@ -21,12 +22,14 @@ function printHelp() {
21
22
  用法:
22
23
  mcp-connect
23
24
  mcp-connect --agent <name>
25
+ mcp-connect proxy
24
26
  mcp-connect status
25
27
 
26
28
  选项:
27
29
  -a, --agent <name> 配置指定客户端
28
30
  --dry-run 只显示将执行的操作
29
31
  --home <path> 指定用户目录(主要用于测试)
32
+ --url <url> proxy 使用的远程 MCP 地址
30
33
  -h, --help 显示帮助
31
34
  -v, --version 显示版本
32
35
 
@@ -36,15 +39,36 @@ function printHelp() {
36
39
 
37
40
  智能配置:
38
41
  不指定 --agent 时,自动检测并配置已安装的 ${SUPPORTED_AGENTS.join('、')}。
39
- 其他 Agent 名称也可传入;未内置适配器时会输出通用 HTTP MCP 配置。`)
42
+ 其他 Agent 名称也可传入;未内置适配器时会输出通用 stdio MCP 配置。
43
+ stdio 代理会从本机安全存储读取 API Key,再转发到远程 HTTP MCP。`)
44
+ }
45
+
46
+ export function stdioProxyDefinition(platform = process.platform) {
47
+ if (platform === 'win32') {
48
+ return {
49
+ command: 'cmd',
50
+ args: ['/d', '/s', '/c', 'npx', '-y', PACKAGE_NAME, 'proxy'],
51
+ }
52
+ }
53
+ return {
54
+ command: 'npx',
55
+ args: ['-y', PACKAGE_NAME, 'proxy'],
56
+ }
40
57
  }
41
58
 
42
59
  function printGenericInstructions(agent, { dryRun = false } = {}) {
43
- console.log(`暂未内置 ${agent} 的自动配置适配器,请在该 Agent 中添加远程 HTTP MCP:
60
+ const proxy = stdioProxyDefinition()
61
+ const config = {
62
+ mcpServers: {
63
+ [SERVER_NAME]: proxy,
64
+ },
65
+ }
66
+ console.log(`暂未内置 ${agent} 的自动配置适配器。请在该 Agent 中添加下面这份标准 stdio MCP 配置:
67
+
68
+ ${JSON.stringify(config, null, 2)}
44
69
 
45
- 名称:${SERVER_NAME}
46
- 地址:${MCP_URL}
47
- 认证头:Authorization: Bearer <你的石榴 AI API Key>
70
+ 该命令会在本机启动通用桥接器,由桥接器连接:${MCP_URL}
71
+ Agent 配置中不需要填写 Authorization 请求头,也不保存真实 API Key。
48
72
 
49
73
  ${dryRun ? `演练模式未保存 API Key;正式运行时会安全保存到 ${API_KEY_ENV}。` : `API Key 已安全保存到 ${API_KEY_ENV}。`}请勿将真实 API Key 写入项目代码或提交到 Git。`)
50
74
  }
@@ -61,6 +85,10 @@ export async function runCli(argv = process.argv.slice(2)) {
61
85
  }
62
86
 
63
87
  const home = path.resolve(options.home || os.homedir())
88
+ if (options.command === 'proxy') {
89
+ await runProxy({ home, url: options.url || MCP_URL })
90
+ return
91
+ }
64
92
  if (options.command === 'status') {
65
93
  const result = await printStatus({ home })
66
94
  if (!result.remote.ok) {
@@ -1,3 +1,4 @@
1
+ import { existsSync } from 'node:fs'
1
2
  import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
2
3
  import path from 'node:path'
3
4
  import { spawnSync } from 'node:child_process'
@@ -42,13 +43,65 @@ function commandText(command, args) {
42
43
  )).join(' ')
43
44
  }
44
45
 
46
+ export function commandInvocation(command, args, {
47
+ platform = process.platform,
48
+ env = process.env,
49
+ fileExists = existsSync,
50
+ nodeExecutable = process.execPath,
51
+ } = {}) {
52
+ if (platform !== 'win32') {
53
+ return { command, args }
54
+ }
55
+
56
+ const searchPath = env.PATH || env.Path || ''
57
+ const npmDirectory = searchPath
58
+ .split(';')
59
+ .map((entry) => entry.trim().replace(/^"|"$/gu, ''))
60
+ .filter(Boolean)
61
+ .find((entry) => fileExists(path.join(entry, `${command}.cmd`)))
62
+
63
+ if (!npmDirectory) {
64
+ return { command, args }
65
+ }
66
+
67
+ if (command === 'claude') {
68
+ const executable = path.join(
69
+ npmDirectory,
70
+ 'node_modules',
71
+ '@anthropic-ai',
72
+ 'claude-code',
73
+ 'bin',
74
+ 'claude.exe',
75
+ )
76
+ if (fileExists(executable)) {
77
+ return { command: executable, args }
78
+ }
79
+ }
80
+ if (command === 'codex') {
81
+ const script = path.join(
82
+ npmDirectory,
83
+ 'node_modules',
84
+ '@openai',
85
+ 'codex',
86
+ 'bin',
87
+ 'codex.js',
88
+ )
89
+ if (fileExists(script)) {
90
+ return { command: nodeExecutable, args: [script, ...args] }
91
+ }
92
+ }
93
+
94
+ return { command, args }
95
+ }
96
+
45
97
  function execute(command, args, { dryRun = false } = {}) {
46
98
  if (dryRun) {
47
99
  console.log(`[dry-run] ${commandText(command, args)}`)
48
100
  return
49
101
  }
50
102
 
51
- const result = spawnSync(command, args, {
103
+ const invocation = commandInvocation(command, args)
104
+ const result = spawnSync(invocation.command, invocation.args, {
52
105
  encoding: 'utf8',
53
106
  windowsHide: true,
54
107
  shell: false,
package/src/constants.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PACKAGE_NAME = '@bigbrain-work/mcp-connect'
2
- export const PACKAGE_VERSION = '1.2.0'
2
+ export const PACKAGE_VERSION = '1.2.2'
3
3
  export const SERVER_NAME = 'shiliu_mcp'
4
4
  export const MCP_URL = 'https://api.bigbrain.work/shiliu/mcp'
5
5
  export const API_KEY_ENV = 'SHILIU_AI_API_KEY'
package/src/proxy.js ADDED
@@ -0,0 +1,92 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js'
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
3
+ import {
4
+ CallToolRequestSchema,
5
+ GetPromptRequestSchema,
6
+ ListPromptsRequestSchema,
7
+ ListResourcesRequestSchema,
8
+ ListResourceTemplatesRequestSchema,
9
+ ListToolsRequestSchema,
10
+ ReadResourceRequestSchema,
11
+ } from '@modelcontextprotocol/sdk/types.js'
12
+
13
+ import { API_KEY_ENV, PACKAGE_VERSION } from './constants.js'
14
+ import { readPersistedApiKey } from './credentials.js'
15
+ import { connectRemote } from './remote-client.js'
16
+
17
+ function resolveProxyApiKey({ home, platform = process.platform, env = process.env }) {
18
+ const apiKey = readPersistedApiKey({ home, platform, env })?.trim()
19
+ if (!apiKey) {
20
+ throw new Error(`未找到 API Key,请先运行 mcp-connect 安装,或设置 ${API_KEY_ENV}`)
21
+ }
22
+ return apiKey
23
+ }
24
+
25
+ export async function runProxy({
26
+ home,
27
+ url,
28
+ platform = process.platform,
29
+ env = process.env,
30
+ } = {}) {
31
+ const apiKey = resolveProxyApiKey({ home, platform, env })
32
+ const remote = await connectRemote({ apiKey, url })
33
+ const remoteCapabilities = remote.getServerCapabilities() ?? {}
34
+ const capabilities = {}
35
+ if (remoteCapabilities.tools) capabilities.tools = {}
36
+ if (remoteCapabilities.resources) capabilities.resources = {}
37
+ if (remoteCapabilities.prompts) capabilities.prompts = {}
38
+
39
+ const server = new Server(
40
+ {
41
+ name: 'shiliu-ai-mcp-proxy',
42
+ version: PACKAGE_VERSION,
43
+ },
44
+ {
45
+ capabilities,
46
+ instructions: 'Authenticated stdio bridge for the Shiliu AI remote MCP service.',
47
+ },
48
+ )
49
+
50
+ if (remoteCapabilities.tools) {
51
+ server.setRequestHandler(ListToolsRequestSchema, (request) => (
52
+ remote.listTools(request.params)
53
+ ))
54
+ server.setRequestHandler(CallToolRequestSchema, (request) => (
55
+ remote.callTool(request.params)
56
+ ))
57
+ }
58
+ if (remoteCapabilities.resources) {
59
+ server.setRequestHandler(ListResourcesRequestSchema, (request) => (
60
+ remote.listResources(request.params)
61
+ ))
62
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, (request) => (
63
+ remote.listResourceTemplates(request.params)
64
+ ))
65
+ server.setRequestHandler(ReadResourceRequestSchema, (request) => (
66
+ remote.readResource(request.params)
67
+ ))
68
+ }
69
+ if (remoteCapabilities.prompts) {
70
+ server.setRequestHandler(ListPromptsRequestSchema, (request) => (
71
+ remote.listPrompts(request.params)
72
+ ))
73
+ server.setRequestHandler(GetPromptRequestSchema, (request) => (
74
+ remote.getPrompt(request.params)
75
+ ))
76
+ }
77
+
78
+ let closing = false
79
+ const shutdown = async () => {
80
+ if (closing) return
81
+ closing = true
82
+ await Promise.allSettled([server.close(), remote.close()])
83
+ }
84
+ process.once('SIGINT', shutdown)
85
+ process.once('SIGTERM', shutdown)
86
+ try {
87
+ await server.connect(new StdioServerTransport())
88
+ } catch (error) {
89
+ await shutdown()
90
+ throw error
91
+ }
92
+ }
@@ -0,0 +1,20 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js'
2
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
3
+
4
+ import { PACKAGE_VERSION } from './constants.js'
5
+
6
+ export async function connectRemote({ apiKey, url }) {
7
+ const client = new Client({
8
+ name: 'shiliu-ai-mcp-connect',
9
+ version: PACKAGE_VERSION,
10
+ })
11
+ const transport = new StreamableHTTPClientTransport(new URL(url), {
12
+ requestInit: {
13
+ headers: {
14
+ Authorization: `Bearer ${apiKey}`,
15
+ },
16
+ },
17
+ })
18
+ await client.connect(transport)
19
+ return client
20
+ }