rush-ai 0.11.1-rc.0 → 0.11.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
@@ -130,6 +130,8 @@ rush-ai task status <id> --json
130
130
  | `agent info <name>` | 查某个 agent 的描述、技能、MCP 配置 |
131
131
  | `mcp list` / `mcp ls` | 列出 MCP server |
132
132
  | `mcp list-tools <id>` | 列 MCP server 提供的工具 |
133
+ | `mcp install <id>` | 从 Registry 安装 MCP 到 Claude Desktop / Claude Code |
134
+ | `mcp uninstall <id>` | 从 Claude Desktop / Claude Code 移除 MCP |
133
135
  | `mcp serve` | 把 rush-ai 当成 MCP stdio server 启动 |
134
136
 
135
137
  ### 插件分发
@@ -207,6 +209,30 @@ rush-ai plugin install rush --dry-run
207
209
 
208
210
  这条路径和每个 IDE 自己的 `/plugin install` 等价 —— 装完之后 IDE 的 `/plugin list` 能看到、`/plugin uninstall` 也能卸载。不用这条路径就忽略;日常的 `task create` / `task push` 和插件分发完全独立。
209
211
 
212
+ ### MCP 安装
213
+
214
+ ```bash
215
+ # 列出可用的 MCP server
216
+ rush-ai mcp list
217
+
218
+ # 安装到 Claude Desktop + Claude Code(默认两个都装)
219
+ rush-ai mcp install octopus-cli-mcp
220
+
221
+ # 只装到 Claude Desktop
222
+ rush-ai mcp install octopus-cli-mcp --target claude-desktop
223
+
224
+ # 跳过交互 + JSON 输出
225
+ rush-ai mcp install octopus-cli-mcp -y --json
226
+
227
+ # 安装未验证的 MCP(需明确确认)
228
+ rush-ai mcp install some-mcp --allow-unverified
229
+
230
+ # 卸载
231
+ rush-ai mcp uninstall octopus-cli-mcp
232
+ ```
233
+
234
+ stdio MCP 写入 `claude_desktop_config.json`;http/sse MCP 写入 `configLibrary`(managed server,工具自动 allow)。安装后需重启 IDE 生效。
235
+
210
236
  ### CI / 脚本
211
237
 
212
238
  ```bash
package/dist/index.js CHANGED
@@ -3917,7 +3917,7 @@ function registerMcpCommand(program) {
3917
3917
  "Comma-separated targets: claude-desktop,claude-code (default: both)"
3918
3918
  ).option("--allow-unverified", "Allow installing unverified MCP servers").action(async (mcpId, opts) => {
3919
3919
  const format = resolveFormat(program.opts());
3920
- const { runMcpInstall, printMcpInstallSummary } = await import("./install-7J3LP75O.js");
3920
+ const { runMcpInstall, printMcpInstallSummary } = await import("./install-DNSVANLM.js");
3921
3921
  try {
3922
3922
  const result = await runMcpInstall({
3923
3923
  mcpId,
@@ -3957,7 +3957,7 @@ function registerMcpCommand(program) {
3957
3957
  "Comma-separated targets: claude-desktop,claude-code (default: both)"
3958
3958
  ).action(async (mcpId, opts) => {
3959
3959
  const format = resolveFormat(program.opts());
3960
- const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-7J3LP75O.js");
3960
+ const { runMcpUninstall, printMcpUninstallSummary } = await import("./install-DNSVANLM.js");
3961
3961
  try {
3962
3962
  const result = await runMcpUninstall({
3963
3963
  mcpId,
@@ -58,32 +58,42 @@ async function writeClaudeDesktopManaged(name, config, tools) {
58
58
  metaPath,
59
59
  () => ({ entries: [] })
60
60
  );
61
- let entryId;
62
- const existingEntry = meta.entries.find((e) => e.name === name);
63
- if (existingEntry) {
64
- entryId = existingEntry.id;
65
- } else {
66
- entryId = randomUUID();
67
- meta.entries.push({ id: entryId, name });
61
+ let appliedId = meta.appliedId;
62
+ if (!appliedId && meta.entries.length > 0) {
63
+ appliedId = meta.entries[0].id;
68
64
  }
69
- if (!meta.appliedId) {
70
- meta.appliedId = entryId;
65
+ if (!appliedId) {
66
+ appliedId = randomUUID();
67
+ meta.entries.push({ id: appliedId, name: "Default" });
68
+ meta.appliedId = appliedId;
69
+ await writeJsonFile(metaPath, meta);
71
70
  }
71
+ const appliedFilePath = join(libDir, `${appliedId}.json`);
72
+ const { data: appliedConfig } = await readJsonFile(
73
+ appliedFilePath,
74
+ () => ({})
75
+ );
72
76
  const toolPolicy = {};
73
77
  for (const tool of tools) {
74
78
  toolPolicy[tool.name] = "allow";
75
79
  }
76
- const serverFile = {
80
+ const serverEntry = {
77
81
  name,
82
+ transport: config.type || "sse",
78
83
  url: config.url,
79
- type: config.type || "sse",
80
84
  ...config.headers && Object.keys(config.headers).length > 0 ? { headers: config.headers } : {},
81
85
  ...Object.keys(toolPolicy).length > 0 ? { toolPolicy } : {}
82
86
  };
83
- const serverFilePath = join(libDir, `${entryId}.json`);
84
- await writeJsonFile(serverFilePath, serverFile);
85
- await writeJsonFile(metaPath, meta);
86
- return serverFilePath;
87
+ const mcpServers = appliedConfig.managedMcpServers ?? [];
88
+ const existingIdx = mcpServers.findIndex((s) => s.name === name);
89
+ if (existingIdx !== -1) {
90
+ mcpServers[existingIdx] = serverEntry;
91
+ } else {
92
+ mcpServers.push(serverEntry);
93
+ }
94
+ appliedConfig.managedMcpServers = mcpServers;
95
+ await writeJsonFile(appliedFilePath, appliedConfig);
96
+ return appliedFilePath;
87
97
  }
88
98
  async function writeClaudeCode(name, transportType, config) {
89
99
  const filePath = claudeCodeSettingsPath();
@@ -130,17 +140,20 @@ async function removeFromClaudeDesktopManaged(name) {
130
140
  metaPath,
131
141
  () => ({ entries: [] })
132
142
  );
133
- const idx = meta.entries.findIndex((e) => e.name === name);
143
+ const appliedId = meta.appliedId ?? meta.entries[0]?.id;
144
+ if (!appliedId) return false;
145
+ const appliedFilePath = join(configLibraryDir(), `${appliedId}.json`);
146
+ if (!await pathExists(appliedFilePath)) return false;
147
+ const { data: appliedConfig } = await readJsonFile(
148
+ appliedFilePath,
149
+ () => ({})
150
+ );
151
+ const mcpServers = appliedConfig.managedMcpServers ?? [];
152
+ const idx = mcpServers.findIndex((s) => s.name === name);
134
153
  if (idx === -1) return false;
135
- const entryId = meta.entries[idx].id;
136
- meta.entries.splice(idx, 1);
137
- if (meta.appliedId === entryId) {
138
- meta.appliedId = meta.entries[0]?.id;
139
- }
140
- const serverFilePath = join(configLibraryDir(), `${entryId}.json`);
141
- const { rm } = await import("fs/promises");
142
- await rm(serverFilePath, { force: true });
143
- await writeJsonFile(metaPath, meta);
154
+ mcpServers.splice(idx, 1);
155
+ appliedConfig.managedMcpServers = mcpServers;
156
+ await writeJsonFile(appliedFilePath, appliedConfig);
144
157
  return true;
145
158
  }
146
159
  async function removeFromClaudeCode(name) {
@@ -545,4 +558,4 @@ export {
545
558
  runMcpInstall,
546
559
  runMcpUninstall
547
560
  };
548
- //# sourceMappingURL=install-7J3LP75O.js.map
561
+ //# sourceMappingURL=install-DNSVANLM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/mcp/config-writers.ts","../src/commands/mcp/prompt-utils.ts","../src/commands/mcp/template-utils.ts","../src/commands/mcp/install.ts"],"sourcesContent":["/**\n * Claude Desktop + Claude Code 配置文件写入器。\n *\n * 支持 4 种写入场景:\n * - stdio → claude_desktop_config.json (Claude Desktop 3p)\n * - http/sse → configLibrary/<uuid>.json (Claude Desktop 3p managed server)\n * - 所有类型 → ~/.claude/settings.json (Claude Code)\n * - 移除 server(uninstall)\n *\n * 复用 installers/claude-code/atomic-json.ts 的原子读写。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n pathExists,\n readJsonFile,\n writeJsonFile,\n} from '../../installers/claude-code/atomic-json.js';\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nfunction claudeDesktop3pDir(): string {\n return join(homedir(), 'Library', 'Application Support', 'Claude-3p');\n}\n\nfunction claudeDesktopConfigPath(): string {\n return join(claudeDesktop3pDir(), 'claude_desktop_config.json');\n}\n\nfunction configLibraryDir(): string {\n return join(claudeDesktop3pDir(), 'configLibrary');\n}\n\nfunction configLibraryMetaPath(): string {\n return join(configLibraryDir(), '_meta.json');\n}\n\nfunction claudeCodeSettingsPath(): string {\n return join(homedir(), '.claude', 'settings.json');\n}\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\nexport async function detectClaudeDesktop(): Promise<boolean> {\n return pathExists(claudeDesktop3pDir());\n}\n\nexport async function detectClaudeCode(): Promise<boolean> {\n return pathExists(join(homedir(), '.claude'));\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface StdioConfig {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n}\n\nexport interface HttpSseConfig {\n url: string;\n type?: string;\n headers?: Record<string, string>;\n}\n\ninterface ConfigLibraryEntry {\n id: string;\n name: string;\n}\n\ninterface ConfigLibraryMeta {\n appliedId?: string;\n entries: ConfigLibraryEntry[];\n}\n\ninterface ManagedServerEntry {\n name: string;\n transport: string;\n url?: string;\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n headers?: Record<string, string>;\n toolPolicy?: Record<string, string>;\n}\n\ninterface AppliedConfigFile {\n managedMcpServers?: ManagedServerEntry[];\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Claude Desktop — stdio\n// ---------------------------------------------------------------------------\n\nexport async function writeClaudeDesktopStdio(\n name: string,\n config: StdioConfig\n): Promise<string> {\n const filePath = claudeDesktopConfigPath();\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n\n const mcpServers = (data.mcpServers as Record<string, unknown>) || {};\n mcpServers[name] = {\n command: config.command,\n ...(config.args?.length ? { args: config.args } : {}),\n ...(config.env && Object.keys(config.env).length > 0\n ? { env: config.env }\n : {}),\n };\n data.mcpServers = mcpServers;\n\n await writeJsonFile(filePath, data);\n return filePath;\n}\n\n// ---------------------------------------------------------------------------\n// Claude Desktop — http/sse (managed server via configLibrary)\n// ---------------------------------------------------------------------------\n\nexport async function writeClaudeDesktopManaged(\n name: string,\n config: HttpSseConfig,\n tools: Array<{ name: string; description?: string }>\n): Promise<string> {\n const libDir = configLibraryDir();\n const metaPath = configLibraryMetaPath();\n\n // 1. Read _meta.json to find appliedId\n const { data: meta } = await readJsonFile<ConfigLibraryMeta>(\n metaPath,\n () => ({ entries: [] })\n );\n\n // 2. Resolve the active config file (appliedId)\n let appliedId = meta.appliedId;\n if (!appliedId && meta.entries.length > 0) {\n appliedId = meta.entries[0].id;\n }\n if (!appliedId) {\n // No config exists yet — create a new one\n appliedId = randomUUID();\n meta.entries.push({ id: appliedId, name: 'Default' });\n meta.appliedId = appliedId;\n await writeJsonFile(metaPath, meta);\n }\n\n const appliedFilePath = join(libDir, `${appliedId}.json`);\n\n // 3. Read the active config file\n const { data: appliedConfig } = await readJsonFile<AppliedConfigFile>(\n appliedFilePath,\n () => ({})\n );\n\n // 4. Build the MCP server entry\n const toolPolicy: Record<string, string> = {};\n for (const tool of tools) {\n toolPolicy[tool.name] = 'allow';\n }\n\n const serverEntry: ManagedServerEntry = {\n name,\n transport: config.type || 'sse',\n url: config.url,\n ...(config.headers && Object.keys(config.headers).length > 0\n ? { headers: config.headers }\n : {}),\n ...(Object.keys(toolPolicy).length > 0 ? { toolPolicy } : {}),\n };\n\n // 5. Merge into managedMcpServers array (replace existing by name, or append)\n const mcpServers = appliedConfig.managedMcpServers ?? [];\n const existingIdx = mcpServers.findIndex((s) => s.name === name);\n if (existingIdx !== -1) {\n mcpServers[existingIdx] = serverEntry;\n } else {\n mcpServers.push(serverEntry);\n }\n appliedConfig.managedMcpServers = mcpServers;\n\n // 6. Write back\n await writeJsonFile(appliedFilePath, appliedConfig);\n\n return appliedFilePath;\n}\n\n// ---------------------------------------------------------------------------\n// Claude Code — settings.json (all transport types)\n// ---------------------------------------------------------------------------\n\nexport async function writeClaudeCode(\n name: string,\n transportType: string,\n config: Record<string, unknown>\n): Promise<string> {\n const filePath = claudeCodeSettingsPath();\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n\n const mcpServers =\n (data.mcpServers as Record<string, Record<string, unknown>>) || {};\n\n if (transportType === 'stdio') {\n mcpServers[name] = {\n command: config.command as string,\n ...(config.args ? { args: config.args } : {}),\n ...(config.env ? { env: config.env } : {}),\n };\n } else {\n // http / sse\n mcpServers[name] = {\n type: transportType,\n url: config.url as string,\n ...(config.headers ? { headers: config.headers } : {}),\n };\n }\n\n data.mcpServers = mcpServers;\n await writeJsonFile(filePath, data);\n return filePath;\n}\n\n// ---------------------------------------------------------------------------\n// Uninstall — remove from all targets\n// ---------------------------------------------------------------------------\n\nexport async function removeFromClaudeDesktopStdio(\n name: string\n): Promise<boolean> {\n const filePath = claudeDesktopConfigPath();\n if (!(await pathExists(filePath))) return false;\n\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n const mcpServers = (data.mcpServers as Record<string, unknown>) || {};\n if (!(name in mcpServers)) return false;\n\n delete mcpServers[name];\n data.mcpServers = mcpServers;\n await writeJsonFile(filePath, data);\n return true;\n}\n\nexport async function removeFromClaudeDesktopManaged(\n name: string\n): Promise<boolean> {\n const metaPath = configLibraryMetaPath();\n if (!(await pathExists(metaPath))) return false;\n\n const { data: meta } = await readJsonFile<ConfigLibraryMeta>(\n metaPath,\n () => ({ entries: [] })\n );\n\n // Find the active config file\n const appliedId = meta.appliedId ?? meta.entries[0]?.id;\n if (!appliedId) return false;\n\n const appliedFilePath = join(configLibraryDir(), `${appliedId}.json`);\n if (!(await pathExists(appliedFilePath))) return false;\n\n const { data: appliedConfig } = await readJsonFile<AppliedConfigFile>(\n appliedFilePath,\n () => ({})\n );\n\n const mcpServers = appliedConfig.managedMcpServers ?? [];\n const idx = mcpServers.findIndex((s) => s.name === name);\n if (idx === -1) return false;\n\n mcpServers.splice(idx, 1);\n appliedConfig.managedMcpServers = mcpServers;\n await writeJsonFile(appliedFilePath, appliedConfig);\n return true;\n}\n\nexport async function removeFromClaudeCode(name: string): Promise<boolean> {\n const filePath = claudeCodeSettingsPath();\n if (!(await pathExists(filePath))) return false;\n\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n const mcpServers = (data.mcpServers as Record<string, unknown>) || {};\n if (!(name in mcpServers)) return false;\n\n delete mcpServers[name];\n data.mcpServers = mcpServers;\n await writeJsonFile(filePath, data);\n return true;\n}\n","/**\n * 交互式凭证收集(raw stdin,无第三方依赖)。\n *\n * 复用 commands/task/deploy.ts:460-475 的 readOneLine 模式。\n */\n\nexport interface ExtraConfigFieldMeta {\n helpUrl?: string;\n type?: 'text' | 'secret';\n required?: boolean;\n defaultValue?: string;\n}\n\n/**\n * 从 stdin 读取一行。非 TTY 时如果 stdin 已关闭则 resolve 空字符串。\n */\nfunction readOneLine(): Promise<string> {\n return new Promise((resolve) => {\n let acc = '';\n const onData = (chunk: Buffer) => {\n acc += chunk.toString('utf-8');\n const nlIdx = acc.indexOf('\\n');\n if (nlIdx !== -1) {\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n resolve(acc.slice(0, nlIdx));\n }\n };\n const onEnd = () => {\n process.stdin.removeListener('data', onData);\n resolve(acc);\n };\n process.stdin.resume();\n process.stdin.on('data', onData);\n process.stdin.once('end', onEnd);\n });\n}\n\n/**\n * 交互式收集凭证。\n *\n * - TTY: 提示用户输入每个 required 字段\n * - 非 TTY / --yes: 使用 defaultValue,required 且无 default 的字段 → 抛错\n */\nexport async function promptCredentials(\n extraConfigMeta: Record<string, ExtraConfigFieldMeta>,\n options?: { yes?: boolean }\n): Promise<Record<string, string>> {\n const values: Record<string, string> = {};\n const isTTY = Boolean(process.stdin.isTTY);\n const skipPrompt = options?.yes || !isTTY;\n\n for (const [key, meta] of Object.entries(extraConfigMeta)) {\n if (!meta.required) {\n if (meta.defaultValue) {\n values[key] = meta.defaultValue;\n }\n continue;\n }\n\n if (skipPrompt) {\n if (meta.defaultValue) {\n values[key] = meta.defaultValue;\n } else {\n throw new Error(\n `Credential \"${key}\" is required but no default value is available. ` +\n `Run interactively or provide credentials via the Rush web UI.` +\n (meta.helpUrl ? ` Help: ${meta.helpUrl}` : '')\n );\n }\n continue;\n }\n\n // Interactive prompt\n const typeHint = meta.type === 'secret' ? ' (secret)' : '';\n const defaultHint = meta.defaultValue ? ` [${meta.defaultValue}]` : '';\n const helpHint = meta.helpUrl ? `\\n Help: ${meta.helpUrl}` : '';\n process.stderr.write(` ${key}${typeHint}${defaultHint}:${helpHint} `);\n\n const input = await readOneLine();\n const trimmed = input.trim();\n\n if (trimmed) {\n values[key] = trimmed;\n } else if (meta.defaultValue) {\n values[key] = meta.defaultValue;\n } else {\n throw new Error(\n `Credential \"${key}\" is required but was not provided.` +\n (meta.helpUrl ? ` Help: ${meta.helpUrl}` : '')\n );\n }\n }\n\n return values;\n}\n","/**\n * MCP server_config 模板变量替换工具。\n *\n * 移植自 apps/web/lib/core/mcp/template-utils.ts + install-utils.ts,\n * 零外部依赖,纯函数。\n */\n\nconst VAR_PATTERN = /\\$\\{([^}]+)\\}/g;\n\n/**\n * 替换单个字符串中的 ${var} 占位符。\n */\nfunction substituteString(\n template: string,\n variables: Record<string, string>\n): { value: string; keys: string[] } {\n const keys: string[] = [];\n const value = template.replace(VAR_PATTERN, (match, varName: string) => {\n if (Object.hasOwn(variables, varName)) {\n keys.push(varName);\n return variables[varName];\n }\n return match;\n });\n return { value, keys };\n}\n\n/**\n * 替换 Record<string, string> 中所有 value 的 ${var} 占位符。\n */\nfunction substituteRecord(\n record: Record<string, string>,\n variables: Record<string, string>,\n usedKeys: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, val] of Object.entries(record)) {\n if (typeof val === 'string') {\n const { value, keys } = substituteString(val, variables);\n result[key] = value;\n for (const k of keys) usedKeys.add(k);\n } else {\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * 对 server_config 中 headers / env 的 value 执行 ${varName} 模板替换。\n *\n * 来源: apps/web/lib/core/mcp/template-utils.ts:66-96\n */\nexport function substituteConfigVariables(\n config: Record<string, unknown>,\n variables: Record<string, string>\n): { result: Record<string, unknown>; usedKeys: Set<string> } {\n if (!variables || Object.keys(variables).length === 0) {\n return { result: { ...config }, usedKeys: new Set() };\n }\n\n const usedKeys = new Set<string>();\n const result: Record<string, unknown> = { ...config };\n\n if (result.headers && typeof result.headers === 'object') {\n result.headers = substituteRecord(\n result.headers as Record<string, string>,\n variables,\n usedKeys\n );\n }\n\n if (result.env && typeof result.env === 'object') {\n result.env = substituteRecord(\n result.env as Record<string, string>,\n variables,\n usedKeys\n );\n }\n\n return { result, usedKeys };\n}\n\n/**\n * 将 extra_config 值合并到 server_config 中。\n *\n * 1. 模板替换:替换 headers/env value 中的 ${var}\n * 2. 未被模板消费的 key → stdio 追加到 env,http/sse 追加到 headers\n *\n * 来源: apps/web/lib/core/mcp/install-utils.ts:127-177\n */\nexport function mergeExtraConfigIntoServerConfig(\n transportType: string,\n serverConfig: Record<string, unknown>,\n extraConfigValues: Record<string, string>\n): Record<string, unknown> {\n if (Object.keys(extraConfigValues).length === 0) {\n return serverConfig;\n }\n\n // 1. 模板替换\n const { result: substituted, usedKeys } = substituteConfigVariables(\n serverConfig,\n extraConfigValues\n );\n\n // 2. 计算未被模板消费的 key\n const remainingExtra: Record<string, string> = {};\n for (const [key, value] of Object.entries(extraConfigValues)) {\n if (!usedKeys.has(key)) {\n remainingExtra[key] = value;\n }\n }\n\n if (Object.keys(remainingExtra).length === 0) {\n return substituted;\n }\n\n // 3. 未消费的 key 按原逻辑追加\n if (transportType === 'stdio') {\n const existingEnv = (substituted.env as Record<string, string>) || {};\n const existingArgs = (substituted.args as string[]) || [];\n\n const mergedArgs = existingArgs.map((arg: string) => {\n const match = Object.entries(remainingExtra).find(([key]) => arg === key);\n return match ? match[1] : arg;\n });\n\n return {\n ...substituted,\n env: { ...existingEnv, ...remainingExtra },\n args: mergedArgs,\n };\n }\n\n const existingHeaders = (substituted.headers as Record<string, string>) || {};\n return {\n ...substituted,\n headers: { ...existingHeaders, ...remainingExtra },\n };\n}\n","/**\n * `rush-ai mcp install <mcp-id>` — 从 Rush Registry 安装 MCP Server 到 Claude Desktop + Claude Code\n */\n\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { ApiError } from '../../util/errors.js';\nimport {\n detectClaudeCode,\n detectClaudeDesktop,\n removeFromClaudeCode,\n removeFromClaudeDesktopManaged,\n removeFromClaudeDesktopStdio,\n writeClaudeCode,\n writeClaudeDesktopManaged,\n writeClaudeDesktopStdio,\n} from './config-writers.js';\nimport type { ExtraConfigFieldMeta } from './prompt-utils.js';\nimport { promptCredentials } from './prompt-utils.js';\nimport { mergeExtraConfigIntoServerConfig } from './template-utils.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction readOneLine(): Promise<string> {\n return new Promise((resolve) => {\n let acc = '';\n const onData = (chunk: Buffer) => {\n acc += chunk.toString('utf-8');\n const nlIdx = acc.indexOf('\\n');\n if (nlIdx !== -1) {\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n resolve(acc.slice(0, nlIdx));\n }\n };\n const onEnd = () => {\n process.stdin.removeListener('data', onData);\n resolve(acc);\n };\n process.stdin.resume();\n process.stdin.on('data', onData);\n process.stdin.once('end', onEnd);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface McpInstallInput {\n mcpId: string;\n yes?: boolean;\n targetRaw?: string;\n allowUnverified?: boolean;\n}\n\ntype InstallTarget = 'claude-desktop' | 'claude-code';\n\ninterface TargetResult {\n target: InstallTarget;\n status: 'ok' | 'skipped' | 'error';\n configPath?: string;\n error?: string;\n}\n\nexport interface McpInstallResult {\n mcpId: string;\n displayName: string;\n transportType: string;\n targets: TargetResult[];\n}\n\n/** Full detail from GET /api/mcp-registry/:id */\ninterface McpDetailData {\n id: string;\n name: string;\n displayName: string;\n description: string;\n transportType: string;\n serverConfig: Record<string, unknown>;\n tools: Array<{ name: string; description: string }> | null;\n extraConfig?: Record<string, string>;\n extraConfigMeta?: Record<string, ExtraConfigFieldMeta>;\n validationStatus?: {\n status: string;\n errorCode?: string;\n errorMessage?: string;\n } | null;\n author?: string;\n [key: string]: unknown;\n}\n\nconst VALID_TARGETS: InstallTarget[] = ['claude-desktop', 'claude-code'];\nconst TRUSTED_STATUSES = new Set(['verified']);\n\ninterface McpDetailApiResponse {\n success: boolean;\n data: McpDetailData;\n}\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\nfunction parseTargets(raw?: string): InstallTarget[] {\n if (!raw) return ['claude-desktop', 'claude-code'];\n const tokens = raw.split(',').map((t) => t.trim().toLowerCase());\n const unknown = tokens.filter(\n (t) => !VALID_TARGETS.includes(t as InstallTarget)\n );\n if (unknown.length > 0) {\n throw new Error(\n `Unknown target(s): ${unknown.join(', ')}. Valid targets: ${VALID_TARGETS.join(', ')}`\n );\n }\n return tokens as InstallTarget[];\n}\n\nexport async function runMcpInstall(\n input: McpInstallInput\n): Promise<McpInstallResult> {\n // 0. Validate targets early (before any network calls)\n const requestedTargets = parseTargets(input.targetRaw);\n\n const client = createClient();\n\n // 1. Fetch MCP detail\n let detail: McpDetailData;\n try {\n const { data } = await client.get<McpDetailApiResponse>(\n `/api/mcp-registry/${encodeURIComponent(input.mcpId)}`\n );\n detail = data.data;\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n throw new Error(`MCP server '${input.mcpId}' not found in the registry.`);\n }\n throw err;\n }\n\n const { transportType, serverConfig, extraConfigMeta, tools } = detail;\n\n // 2. Security check: warn on unverified MCP\n const vstatus = detail.validationStatus?.status;\n if (!TRUSTED_STATUSES.has(vstatus ?? '') && !input.allowUnverified) {\n const statusLabel = vstatus || 'unknown';\n const isTTY = Boolean(process.stdin.isTTY);\n\n if (input.yes || !isTTY) {\n throw new Error(\n `MCP server '${input.mcpId}' is not verified (status: ${statusLabel}). ` +\n `Use --allow-unverified to install anyway.`\n );\n }\n\n // Interactive confirmation\n output.newline();\n output.warn(\n `MCP server '${input.mcpId}' is not verified (status: ${statusLabel}).`\n );\n output.log(` Author: ${detail.author || 'unknown'}`);\n output.log(` Transport: ${transportType}`);\n if (transportType === 'stdio') {\n output.log(` Command: ${serverConfig.command}`);\n if (serverConfig.args) {\n output.log(` Args: ${(serverConfig.args as string[]).join(' ')}`);\n }\n } else {\n output.log(` URL: ${serverConfig.url}`);\n }\n output.newline();\n process.stderr.write('Install anyway? [y/N] ');\n const answer = await readOneLine();\n if (!answer.trim().toLowerCase().startsWith('y')) {\n throw new Error('Installation cancelled by user.');\n }\n }\n\n // 3. Collect credentials if needed\n let extraConfigValues: Record<string, string> = {};\n if (extraConfigMeta && Object.keys(extraConfigMeta).length > 0) {\n const hasRequired = Object.values(extraConfigMeta).some((m) => m.required);\n if (hasRequired) {\n output.newline();\n output.info(`${detail.displayName} requires credentials:`);\n }\n extraConfigValues = await promptCredentials(extraConfigMeta, {\n yes: input.yes,\n });\n }\n\n // 4. Merge extra config into server config\n const mergedConfig = mergeExtraConfigIntoServerConfig(\n transportType,\n serverConfig,\n extraConfigValues\n );\n\n // 5. Write to targets\n const targetResults: TargetResult[] = [];\n\n for (const target of requestedTargets) {\n try {\n if (target === 'claude-desktop') {\n const detected = await detectClaudeDesktop();\n if (!detected) {\n targetResults.push({\n target,\n status: 'skipped',\n error: 'Claude Desktop not detected',\n });\n continue;\n }\n\n let configPath: string;\n if (transportType === 'stdio') {\n configPath = await writeClaudeDesktopStdio(input.mcpId, {\n command: mergedConfig.command as string,\n args: mergedConfig.args as string[] | undefined,\n env: mergedConfig.env as Record<string, string> | undefined,\n });\n } else {\n configPath = await writeClaudeDesktopManaged(\n input.mcpId,\n {\n url: mergedConfig.url as string,\n type: transportType,\n headers: mergedConfig.headers as\n | Record<string, string>\n | undefined,\n },\n tools ?? []\n );\n }\n targetResults.push({ target, status: 'ok', configPath });\n } else {\n // claude-code\n const detected = await detectClaudeCode();\n if (!detected) {\n targetResults.push({\n target,\n status: 'skipped',\n error: 'Claude Code not detected',\n });\n continue;\n }\n\n const configPath = await writeClaudeCode(\n input.mcpId,\n transportType,\n mergedConfig\n );\n targetResults.push({ target, status: 'ok', configPath });\n }\n } catch (err) {\n targetResults.push({\n target,\n status: 'error',\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return {\n mcpId: input.mcpId,\n displayName: detail.displayName,\n transportType,\n targets: targetResults,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Uninstall\n// ---------------------------------------------------------------------------\n\nexport interface McpUninstallInput {\n mcpId: string;\n targetRaw?: string;\n}\n\nexport interface McpUninstallResult {\n mcpId: string;\n targets: Array<{\n target: InstallTarget;\n removed: boolean;\n error?: string;\n }>;\n}\n\nexport async function runMcpUninstall(\n input: McpUninstallInput\n): Promise<McpUninstallResult> {\n const requestedTargets = parseTargets(input.targetRaw); // validates early\n const targets: McpUninstallResult['targets'] = [];\n\n for (const target of requestedTargets) {\n try {\n let removed = false;\n if (target === 'claude-desktop') {\n // Try both stdio config and managed server\n const removedStdio = await removeFromClaudeDesktopStdio(input.mcpId);\n const removedManaged = await removeFromClaudeDesktopManaged(\n input.mcpId\n );\n removed = removedStdio || removedManaged;\n } else {\n removed = await removeFromClaudeCode(input.mcpId);\n }\n targets.push({ target, removed });\n } catch (err) {\n targets.push({\n target,\n removed: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { mcpId: input.mcpId, targets };\n}\n\n// ---------------------------------------------------------------------------\n// Print helpers\n// ---------------------------------------------------------------------------\n\nexport function printMcpInstallSummary(result: McpInstallResult): void {\n output.newline();\n output.success(\n `Installed ${output.bold(result.displayName)} (${result.mcpId})`\n );\n output.log(` Transport: ${result.transportType}`);\n output.newline();\n\n for (const t of result.targets) {\n const label =\n t.target === 'claude-desktop' ? 'Claude Desktop' : 'Claude Code';\n if (t.status === 'ok') {\n output.log(` ${label}: ✓ ${t.configPath}`);\n } else if (t.status === 'skipped') {\n output.dim(` ${label}: skipped (${t.error})`);\n } else {\n output.error(` ${label}: ${t.error}`);\n }\n }\n\n const anyOk = result.targets.some((t) => t.status === 'ok');\n if (anyOk) {\n output.newline();\n output.dim('Restart the IDE to activate the new MCP server.');\n }\n}\n\nexport function printMcpUninstallSummary(result: McpUninstallResult): void {\n output.newline();\n\n let anyRemoved = false;\n for (const t of result.targets) {\n const label =\n t.target === 'claude-desktop' ? 'Claude Desktop' : 'Claude Code';\n if (t.error) {\n output.error(` ${label}: ${t.error}`);\n } else if (t.removed) {\n output.log(` ${label}: ✓ removed`);\n anyRemoved = true;\n } else {\n output.dim(` ${label}: not found`);\n }\n }\n\n if (anyRemoved) {\n output.success(`Uninstalled ${output.bold(result.mcpId)}`);\n output.dim('Restart the IDE to apply changes.');\n } else {\n output.info(`${result.mcpId} was not installed in any target.`);\n }\n}\n"],"mappings":"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","names":["readOneLine"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rush-ai",
3
- "version": "0.11.1-rc.0",
3
+ "version": "0.11.2",
4
4
  "description": "Rush CLI - Command-line interface for the Rush AI platform",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/mcp/config-writers.ts","../src/commands/mcp/prompt-utils.ts","../src/commands/mcp/template-utils.ts","../src/commands/mcp/install.ts"],"sourcesContent":["/**\n * Claude Desktop + Claude Code 配置文件写入器。\n *\n * 支持 4 种写入场景:\n * - stdio → claude_desktop_config.json (Claude Desktop 3p)\n * - http/sse → configLibrary/<uuid>.json (Claude Desktop 3p managed server)\n * - 所有类型 → ~/.claude/settings.json (Claude Code)\n * - 移除 server(uninstall)\n *\n * 复用 installers/claude-code/atomic-json.ts 的原子读写。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n pathExists,\n readJsonFile,\n writeJsonFile,\n} from '../../installers/claude-code/atomic-json.js';\n\n// ---------------------------------------------------------------------------\n// Paths\n// ---------------------------------------------------------------------------\n\nfunction claudeDesktop3pDir(): string {\n return join(homedir(), 'Library', 'Application Support', 'Claude-3p');\n}\n\nfunction claudeDesktopConfigPath(): string {\n return join(claudeDesktop3pDir(), 'claude_desktop_config.json');\n}\n\nfunction configLibraryDir(): string {\n return join(claudeDesktop3pDir(), 'configLibrary');\n}\n\nfunction configLibraryMetaPath(): string {\n return join(configLibraryDir(), '_meta.json');\n}\n\nfunction claudeCodeSettingsPath(): string {\n return join(homedir(), '.claude', 'settings.json');\n}\n\n// ---------------------------------------------------------------------------\n// Detection\n// ---------------------------------------------------------------------------\n\nexport async function detectClaudeDesktop(): Promise<boolean> {\n return pathExists(claudeDesktop3pDir());\n}\n\nexport async function detectClaudeCode(): Promise<boolean> {\n return pathExists(join(homedir(), '.claude'));\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface StdioConfig {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n}\n\nexport interface HttpSseConfig {\n url: string;\n type?: string;\n headers?: Record<string, string>;\n}\n\ninterface ConfigLibraryEntry {\n id: string;\n name: string;\n}\n\ninterface ConfigLibraryMeta {\n appliedId?: string;\n entries: ConfigLibraryEntry[];\n}\n\ninterface ManagedServerFile {\n name: string;\n url: string;\n type: string;\n headers?: Record<string, string>;\n toolPolicy?: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Claude Desktop — stdio\n// ---------------------------------------------------------------------------\n\nexport async function writeClaudeDesktopStdio(\n name: string,\n config: StdioConfig\n): Promise<string> {\n const filePath = claudeDesktopConfigPath();\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n\n const mcpServers = (data.mcpServers as Record<string, unknown>) || {};\n mcpServers[name] = {\n command: config.command,\n ...(config.args?.length ? { args: config.args } : {}),\n ...(config.env && Object.keys(config.env).length > 0\n ? { env: config.env }\n : {}),\n };\n data.mcpServers = mcpServers;\n\n await writeJsonFile(filePath, data);\n return filePath;\n}\n\n// ---------------------------------------------------------------------------\n// Claude Desktop — http/sse (managed server via configLibrary)\n// ---------------------------------------------------------------------------\n\nexport async function writeClaudeDesktopManaged(\n name: string,\n config: HttpSseConfig,\n tools: Array<{ name: string; description?: string }>\n): Promise<string> {\n const libDir = configLibraryDir();\n const metaPath = configLibraryMetaPath();\n\n // 1. Read _meta.json\n const { data: meta } = await readJsonFile<ConfigLibraryMeta>(\n metaPath,\n () => ({ entries: [] })\n );\n\n // 2. Find existing entry or create new UUID\n let entryId: string;\n const existingEntry = meta.entries.find((e) => e.name === name);\n if (existingEntry) {\n entryId = existingEntry.id;\n } else {\n entryId = randomUUID();\n meta.entries.push({ id: entryId, name });\n }\n\n // Ensure appliedId is set\n if (!meta.appliedId) {\n meta.appliedId = entryId;\n }\n\n // 3. Build toolPolicy — all tools default to \"allow\"\n const toolPolicy: Record<string, string> = {};\n for (const tool of tools) {\n toolPolicy[tool.name] = 'allow';\n }\n\n // 4. Write configLibrary/<uuid>.json\n const serverFile: ManagedServerFile = {\n name,\n url: config.url,\n type: config.type || 'sse',\n ...(config.headers && Object.keys(config.headers).length > 0\n ? { headers: config.headers }\n : {}),\n ...(Object.keys(toolPolicy).length > 0 ? { toolPolicy } : {}),\n };\n\n const serverFilePath = join(libDir, `${entryId}.json`);\n await writeJsonFile(serverFilePath, serverFile);\n\n // 5. Update _meta.json\n await writeJsonFile(metaPath, meta);\n\n return serverFilePath;\n}\n\n// ---------------------------------------------------------------------------\n// Claude Code — settings.json (all transport types)\n// ---------------------------------------------------------------------------\n\nexport async function writeClaudeCode(\n name: string,\n transportType: string,\n config: Record<string, unknown>\n): Promise<string> {\n const filePath = claudeCodeSettingsPath();\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n\n const mcpServers =\n (data.mcpServers as Record<string, Record<string, unknown>>) || {};\n\n if (transportType === 'stdio') {\n mcpServers[name] = {\n command: config.command as string,\n ...(config.args ? { args: config.args } : {}),\n ...(config.env ? { env: config.env } : {}),\n };\n } else {\n // http / sse\n mcpServers[name] = {\n type: transportType,\n url: config.url as string,\n ...(config.headers ? { headers: config.headers } : {}),\n };\n }\n\n data.mcpServers = mcpServers;\n await writeJsonFile(filePath, data);\n return filePath;\n}\n\n// ---------------------------------------------------------------------------\n// Uninstall — remove from all targets\n// ---------------------------------------------------------------------------\n\nexport async function removeFromClaudeDesktopStdio(\n name: string\n): Promise<boolean> {\n const filePath = claudeDesktopConfigPath();\n if (!(await pathExists(filePath))) return false;\n\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n const mcpServers = (data.mcpServers as Record<string, unknown>) || {};\n if (!(name in mcpServers)) return false;\n\n delete mcpServers[name];\n data.mcpServers = mcpServers;\n await writeJsonFile(filePath, data);\n return true;\n}\n\nexport async function removeFromClaudeDesktopManaged(\n name: string\n): Promise<boolean> {\n const metaPath = configLibraryMetaPath();\n if (!(await pathExists(metaPath))) return false;\n\n const { data: meta } = await readJsonFile<ConfigLibraryMeta>(\n metaPath,\n () => ({ entries: [] })\n );\n\n const idx = meta.entries.findIndex((e) => e.name === name);\n if (idx === -1) return false;\n\n const entryId = meta.entries[idx].id;\n meta.entries.splice(idx, 1);\n\n // Clear appliedId if it pointed to the removed entry\n if (meta.appliedId === entryId) {\n meta.appliedId = meta.entries[0]?.id;\n }\n\n // Remove the server file\n const serverFilePath = join(configLibraryDir(), `${entryId}.json`);\n const { rm } = await import('node:fs/promises');\n await rm(serverFilePath, { force: true });\n\n // Update _meta.json\n await writeJsonFile(metaPath, meta);\n return true;\n}\n\nexport async function removeFromClaudeCode(name: string): Promise<boolean> {\n const filePath = claudeCodeSettingsPath();\n if (!(await pathExists(filePath))) return false;\n\n const { data } = await readJsonFile<Record<string, unknown>>(\n filePath,\n () => ({})\n );\n const mcpServers = (data.mcpServers as Record<string, unknown>) || {};\n if (!(name in mcpServers)) return false;\n\n delete mcpServers[name];\n data.mcpServers = mcpServers;\n await writeJsonFile(filePath, data);\n return true;\n}\n","/**\n * 交互式凭证收集(raw stdin,无第三方依赖)。\n *\n * 复用 commands/task/deploy.ts:460-475 的 readOneLine 模式。\n */\n\nexport interface ExtraConfigFieldMeta {\n helpUrl?: string;\n type?: 'text' | 'secret';\n required?: boolean;\n defaultValue?: string;\n}\n\n/**\n * 从 stdin 读取一行。非 TTY 时如果 stdin 已关闭则 resolve 空字符串。\n */\nfunction readOneLine(): Promise<string> {\n return new Promise((resolve) => {\n let acc = '';\n const onData = (chunk: Buffer) => {\n acc += chunk.toString('utf-8');\n const nlIdx = acc.indexOf('\\n');\n if (nlIdx !== -1) {\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n resolve(acc.slice(0, nlIdx));\n }\n };\n const onEnd = () => {\n process.stdin.removeListener('data', onData);\n resolve(acc);\n };\n process.stdin.resume();\n process.stdin.on('data', onData);\n process.stdin.once('end', onEnd);\n });\n}\n\n/**\n * 交互式收集凭证。\n *\n * - TTY: 提示用户输入每个 required 字段\n * - 非 TTY / --yes: 使用 defaultValue,required 且无 default 的字段 → 抛错\n */\nexport async function promptCredentials(\n extraConfigMeta: Record<string, ExtraConfigFieldMeta>,\n options?: { yes?: boolean }\n): Promise<Record<string, string>> {\n const values: Record<string, string> = {};\n const isTTY = Boolean(process.stdin.isTTY);\n const skipPrompt = options?.yes || !isTTY;\n\n for (const [key, meta] of Object.entries(extraConfigMeta)) {\n if (!meta.required) {\n if (meta.defaultValue) {\n values[key] = meta.defaultValue;\n }\n continue;\n }\n\n if (skipPrompt) {\n if (meta.defaultValue) {\n values[key] = meta.defaultValue;\n } else {\n throw new Error(\n `Credential \"${key}\" is required but no default value is available. ` +\n `Run interactively or provide credentials via the Rush web UI.` +\n (meta.helpUrl ? ` Help: ${meta.helpUrl}` : '')\n );\n }\n continue;\n }\n\n // Interactive prompt\n const typeHint = meta.type === 'secret' ? ' (secret)' : '';\n const defaultHint = meta.defaultValue ? ` [${meta.defaultValue}]` : '';\n const helpHint = meta.helpUrl ? `\\n Help: ${meta.helpUrl}` : '';\n process.stderr.write(` ${key}${typeHint}${defaultHint}:${helpHint} `);\n\n const input = await readOneLine();\n const trimmed = input.trim();\n\n if (trimmed) {\n values[key] = trimmed;\n } else if (meta.defaultValue) {\n values[key] = meta.defaultValue;\n } else {\n throw new Error(\n `Credential \"${key}\" is required but was not provided.` +\n (meta.helpUrl ? ` Help: ${meta.helpUrl}` : '')\n );\n }\n }\n\n return values;\n}\n","/**\n * MCP server_config 模板变量替换工具。\n *\n * 移植自 apps/web/lib/core/mcp/template-utils.ts + install-utils.ts,\n * 零外部依赖,纯函数。\n */\n\nconst VAR_PATTERN = /\\$\\{([^}]+)\\}/g;\n\n/**\n * 替换单个字符串中的 ${var} 占位符。\n */\nfunction substituteString(\n template: string,\n variables: Record<string, string>\n): { value: string; keys: string[] } {\n const keys: string[] = [];\n const value = template.replace(VAR_PATTERN, (match, varName: string) => {\n if (Object.hasOwn(variables, varName)) {\n keys.push(varName);\n return variables[varName];\n }\n return match;\n });\n return { value, keys };\n}\n\n/**\n * 替换 Record<string, string> 中所有 value 的 ${var} 占位符。\n */\nfunction substituteRecord(\n record: Record<string, string>,\n variables: Record<string, string>,\n usedKeys: Set<string>\n): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, val] of Object.entries(record)) {\n if (typeof val === 'string') {\n const { value, keys } = substituteString(val, variables);\n result[key] = value;\n for (const k of keys) usedKeys.add(k);\n } else {\n result[key] = val;\n }\n }\n return result;\n}\n\n/**\n * 对 server_config 中 headers / env 的 value 执行 ${varName} 模板替换。\n *\n * 来源: apps/web/lib/core/mcp/template-utils.ts:66-96\n */\nexport function substituteConfigVariables(\n config: Record<string, unknown>,\n variables: Record<string, string>\n): { result: Record<string, unknown>; usedKeys: Set<string> } {\n if (!variables || Object.keys(variables).length === 0) {\n return { result: { ...config }, usedKeys: new Set() };\n }\n\n const usedKeys = new Set<string>();\n const result: Record<string, unknown> = { ...config };\n\n if (result.headers && typeof result.headers === 'object') {\n result.headers = substituteRecord(\n result.headers as Record<string, string>,\n variables,\n usedKeys\n );\n }\n\n if (result.env && typeof result.env === 'object') {\n result.env = substituteRecord(\n result.env as Record<string, string>,\n variables,\n usedKeys\n );\n }\n\n return { result, usedKeys };\n}\n\n/**\n * 将 extra_config 值合并到 server_config 中。\n *\n * 1. 模板替换:替换 headers/env value 中的 ${var}\n * 2. 未被模板消费的 key → stdio 追加到 env,http/sse 追加到 headers\n *\n * 来源: apps/web/lib/core/mcp/install-utils.ts:127-177\n */\nexport function mergeExtraConfigIntoServerConfig(\n transportType: string,\n serverConfig: Record<string, unknown>,\n extraConfigValues: Record<string, string>\n): Record<string, unknown> {\n if (Object.keys(extraConfigValues).length === 0) {\n return serverConfig;\n }\n\n // 1. 模板替换\n const { result: substituted, usedKeys } = substituteConfigVariables(\n serverConfig,\n extraConfigValues\n );\n\n // 2. 计算未被模板消费的 key\n const remainingExtra: Record<string, string> = {};\n for (const [key, value] of Object.entries(extraConfigValues)) {\n if (!usedKeys.has(key)) {\n remainingExtra[key] = value;\n }\n }\n\n if (Object.keys(remainingExtra).length === 0) {\n return substituted;\n }\n\n // 3. 未消费的 key 按原逻辑追加\n if (transportType === 'stdio') {\n const existingEnv = (substituted.env as Record<string, string>) || {};\n const existingArgs = (substituted.args as string[]) || [];\n\n const mergedArgs = existingArgs.map((arg: string) => {\n const match = Object.entries(remainingExtra).find(([key]) => arg === key);\n return match ? match[1] : arg;\n });\n\n return {\n ...substituted,\n env: { ...existingEnv, ...remainingExtra },\n args: mergedArgs,\n };\n }\n\n const existingHeaders = (substituted.headers as Record<string, string>) || {};\n return {\n ...substituted,\n headers: { ...existingHeaders, ...remainingExtra },\n };\n}\n","/**\n * `rush-ai mcp install <mcp-id>` — 从 Rush Registry 安装 MCP Server 到 Claude Desktop + Claude Code\n */\n\nimport { output } from '../../output/logger.js';\nimport { createClient } from '../../util/client.js';\nimport { ApiError } from '../../util/errors.js';\nimport {\n detectClaudeCode,\n detectClaudeDesktop,\n removeFromClaudeCode,\n removeFromClaudeDesktopManaged,\n removeFromClaudeDesktopStdio,\n writeClaudeCode,\n writeClaudeDesktopManaged,\n writeClaudeDesktopStdio,\n} from './config-writers.js';\nimport type { ExtraConfigFieldMeta } from './prompt-utils.js';\nimport { promptCredentials } from './prompt-utils.js';\nimport { mergeExtraConfigIntoServerConfig } from './template-utils.js';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction readOneLine(): Promise<string> {\n return new Promise((resolve) => {\n let acc = '';\n const onData = (chunk: Buffer) => {\n acc += chunk.toString('utf-8');\n const nlIdx = acc.indexOf('\\n');\n if (nlIdx !== -1) {\n process.stdin.removeListener('data', onData);\n process.stdin.pause();\n resolve(acc.slice(0, nlIdx));\n }\n };\n const onEnd = () => {\n process.stdin.removeListener('data', onData);\n resolve(acc);\n };\n process.stdin.resume();\n process.stdin.on('data', onData);\n process.stdin.once('end', onEnd);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface McpInstallInput {\n mcpId: string;\n yes?: boolean;\n targetRaw?: string;\n allowUnverified?: boolean;\n}\n\ntype InstallTarget = 'claude-desktop' | 'claude-code';\n\ninterface TargetResult {\n target: InstallTarget;\n status: 'ok' | 'skipped' | 'error';\n configPath?: string;\n error?: string;\n}\n\nexport interface McpInstallResult {\n mcpId: string;\n displayName: string;\n transportType: string;\n targets: TargetResult[];\n}\n\n/** Full detail from GET /api/mcp-registry/:id */\ninterface McpDetailData {\n id: string;\n name: string;\n displayName: string;\n description: string;\n transportType: string;\n serverConfig: Record<string, unknown>;\n tools: Array<{ name: string; description: string }> | null;\n extraConfig?: Record<string, string>;\n extraConfigMeta?: Record<string, ExtraConfigFieldMeta>;\n validationStatus?: {\n status: string;\n errorCode?: string;\n errorMessage?: string;\n } | null;\n author?: string;\n [key: string]: unknown;\n}\n\nconst VALID_TARGETS: InstallTarget[] = ['claude-desktop', 'claude-code'];\nconst TRUSTED_STATUSES = new Set(['verified']);\n\ninterface McpDetailApiResponse {\n success: boolean;\n data: McpDetailData;\n}\n\n// ---------------------------------------------------------------------------\n// Core\n// ---------------------------------------------------------------------------\n\nfunction parseTargets(raw?: string): InstallTarget[] {\n if (!raw) return ['claude-desktop', 'claude-code'];\n const tokens = raw.split(',').map((t) => t.trim().toLowerCase());\n const unknown = tokens.filter(\n (t) => !VALID_TARGETS.includes(t as InstallTarget)\n );\n if (unknown.length > 0) {\n throw new Error(\n `Unknown target(s): ${unknown.join(', ')}. Valid targets: ${VALID_TARGETS.join(', ')}`\n );\n }\n return tokens as InstallTarget[];\n}\n\nexport async function runMcpInstall(\n input: McpInstallInput\n): Promise<McpInstallResult> {\n // 0. Validate targets early (before any network calls)\n const requestedTargets = parseTargets(input.targetRaw);\n\n const client = createClient();\n\n // 1. Fetch MCP detail\n let detail: McpDetailData;\n try {\n const { data } = await client.get<McpDetailApiResponse>(\n `/api/mcp-registry/${encodeURIComponent(input.mcpId)}`\n );\n detail = data.data;\n } catch (err) {\n if (err instanceof ApiError && err.status === 404) {\n throw new Error(`MCP server '${input.mcpId}' not found in the registry.`);\n }\n throw err;\n }\n\n const { transportType, serverConfig, extraConfigMeta, tools } = detail;\n\n // 2. Security check: warn on unverified MCP\n const vstatus = detail.validationStatus?.status;\n if (!TRUSTED_STATUSES.has(vstatus ?? '') && !input.allowUnverified) {\n const statusLabel = vstatus || 'unknown';\n const isTTY = Boolean(process.stdin.isTTY);\n\n if (input.yes || !isTTY) {\n throw new Error(\n `MCP server '${input.mcpId}' is not verified (status: ${statusLabel}). ` +\n `Use --allow-unverified to install anyway.`\n );\n }\n\n // Interactive confirmation\n output.newline();\n output.warn(\n `MCP server '${input.mcpId}' is not verified (status: ${statusLabel}).`\n );\n output.log(` Author: ${detail.author || 'unknown'}`);\n output.log(` Transport: ${transportType}`);\n if (transportType === 'stdio') {\n output.log(` Command: ${serverConfig.command}`);\n if (serverConfig.args) {\n output.log(` Args: ${(serverConfig.args as string[]).join(' ')}`);\n }\n } else {\n output.log(` URL: ${serverConfig.url}`);\n }\n output.newline();\n process.stderr.write('Install anyway? [y/N] ');\n const answer = await readOneLine();\n if (!answer.trim().toLowerCase().startsWith('y')) {\n throw new Error('Installation cancelled by user.');\n }\n }\n\n // 3. Collect credentials if needed\n let extraConfigValues: Record<string, string> = {};\n if (extraConfigMeta && Object.keys(extraConfigMeta).length > 0) {\n const hasRequired = Object.values(extraConfigMeta).some((m) => m.required);\n if (hasRequired) {\n output.newline();\n output.info(`${detail.displayName} requires credentials:`);\n }\n extraConfigValues = await promptCredentials(extraConfigMeta, {\n yes: input.yes,\n });\n }\n\n // 4. Merge extra config into server config\n const mergedConfig = mergeExtraConfigIntoServerConfig(\n transportType,\n serverConfig,\n extraConfigValues\n );\n\n // 5. Write to targets\n const targetResults: TargetResult[] = [];\n\n for (const target of requestedTargets) {\n try {\n if (target === 'claude-desktop') {\n const detected = await detectClaudeDesktop();\n if (!detected) {\n targetResults.push({\n target,\n status: 'skipped',\n error: 'Claude Desktop not detected',\n });\n continue;\n }\n\n let configPath: string;\n if (transportType === 'stdio') {\n configPath = await writeClaudeDesktopStdio(input.mcpId, {\n command: mergedConfig.command as string,\n args: mergedConfig.args as string[] | undefined,\n env: mergedConfig.env as Record<string, string> | undefined,\n });\n } else {\n configPath = await writeClaudeDesktopManaged(\n input.mcpId,\n {\n url: mergedConfig.url as string,\n type: transportType,\n headers: mergedConfig.headers as\n | Record<string, string>\n | undefined,\n },\n tools ?? []\n );\n }\n targetResults.push({ target, status: 'ok', configPath });\n } else {\n // claude-code\n const detected = await detectClaudeCode();\n if (!detected) {\n targetResults.push({\n target,\n status: 'skipped',\n error: 'Claude Code not detected',\n });\n continue;\n }\n\n const configPath = await writeClaudeCode(\n input.mcpId,\n transportType,\n mergedConfig\n );\n targetResults.push({ target, status: 'ok', configPath });\n }\n } catch (err) {\n targetResults.push({\n target,\n status: 'error',\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return {\n mcpId: input.mcpId,\n displayName: detail.displayName,\n transportType,\n targets: targetResults,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Uninstall\n// ---------------------------------------------------------------------------\n\nexport interface McpUninstallInput {\n mcpId: string;\n targetRaw?: string;\n}\n\nexport interface McpUninstallResult {\n mcpId: string;\n targets: Array<{\n target: InstallTarget;\n removed: boolean;\n error?: string;\n }>;\n}\n\nexport async function runMcpUninstall(\n input: McpUninstallInput\n): Promise<McpUninstallResult> {\n const requestedTargets = parseTargets(input.targetRaw); // validates early\n const targets: McpUninstallResult['targets'] = [];\n\n for (const target of requestedTargets) {\n try {\n let removed = false;\n if (target === 'claude-desktop') {\n // Try both stdio config and managed server\n const removedStdio = await removeFromClaudeDesktopStdio(input.mcpId);\n const removedManaged = await removeFromClaudeDesktopManaged(\n input.mcpId\n );\n removed = removedStdio || removedManaged;\n } else {\n removed = await removeFromClaudeCode(input.mcpId);\n }\n targets.push({ target, removed });\n } catch (err) {\n targets.push({\n target,\n removed: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { mcpId: input.mcpId, targets };\n}\n\n// ---------------------------------------------------------------------------\n// Print helpers\n// ---------------------------------------------------------------------------\n\nexport function printMcpInstallSummary(result: McpInstallResult): void {\n output.newline();\n output.success(\n `Installed ${output.bold(result.displayName)} (${result.mcpId})`\n );\n output.log(` Transport: ${result.transportType}`);\n output.newline();\n\n for (const t of result.targets) {\n const label =\n t.target === 'claude-desktop' ? 'Claude Desktop' : 'Claude Code';\n if (t.status === 'ok') {\n output.log(` ${label}: ✓ ${t.configPath}`);\n } else if (t.status === 'skipped') {\n output.dim(` ${label}: skipped (${t.error})`);\n } else {\n output.error(` ${label}: ${t.error}`);\n }\n }\n\n const anyOk = result.targets.some((t) => t.status === 'ok');\n if (anyOk) {\n output.newline();\n output.dim('Restart the IDE to activate the new MCP server.');\n }\n}\n\nexport function printMcpUninstallSummary(result: McpUninstallResult): void {\n output.newline();\n\n let anyRemoved = false;\n for (const t of result.targets) {\n const label =\n t.target === 'claude-desktop' ? 'Claude Desktop' : 'Claude Code';\n if (t.error) {\n output.error(` ${label}: ${t.error}`);\n } else if (t.removed) {\n output.log(` ${label}: ✓ removed`);\n anyRemoved = true;\n } else {\n output.dim(` ${label}: not found`);\n }\n }\n\n if (anyRemoved) {\n output.success(`Uninstalled ${output.bold(result.mcpId)}`);\n output.dim('Restart the IDE to apply changes.');\n } else {\n output.info(`${result.mcpId} was not installed in any target.`);\n }\n}\n"],"mappings":"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","names":["readOneLine"]}