rush-ai 0.13.0 → 0.14.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/dist/{chunk-V7P2TZZ4.js → chunk-GDSJUMK4.js} +66 -2
- package/dist/chunk-GDSJUMK4.js.map +1 -0
- package/dist/index.js +672 -274
- package/dist/index.js.map +1 -1
- package/dist/{install-RTV2VWCC.js → install-KY7BF54H.js} +5 -67
- package/dist/install-KY7BF54H.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-V7P2TZZ4.js.map +0 -1
- package/dist/install-RTV2VWCC.js.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
pathExists,
|
|
4
|
+
promptCredentials,
|
|
4
5
|
readJsonFile,
|
|
5
6
|
writeJsonFile
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GDSJUMK4.js";
|
|
7
8
|
import {
|
|
8
9
|
ApiError,
|
|
9
10
|
createClient,
|
|
@@ -171,69 +172,6 @@ async function removeFromClaudeCode(name) {
|
|
|
171
172
|
return true;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
// src/commands/mcp/prompt-utils.ts
|
|
175
|
-
function readOneLine() {
|
|
176
|
-
return new Promise((resolve) => {
|
|
177
|
-
let acc = "";
|
|
178
|
-
const onData = (chunk) => {
|
|
179
|
-
acc += chunk.toString("utf-8");
|
|
180
|
-
const nlIdx = acc.indexOf("\n");
|
|
181
|
-
if (nlIdx !== -1) {
|
|
182
|
-
process.stdin.removeListener("data", onData);
|
|
183
|
-
process.stdin.pause();
|
|
184
|
-
resolve(acc.slice(0, nlIdx));
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
const onEnd = () => {
|
|
188
|
-
process.stdin.removeListener("data", onData);
|
|
189
|
-
resolve(acc);
|
|
190
|
-
};
|
|
191
|
-
process.stdin.resume();
|
|
192
|
-
process.stdin.on("data", onData);
|
|
193
|
-
process.stdin.once("end", onEnd);
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
async function promptCredentials(extraConfigMeta, options) {
|
|
197
|
-
const values = {};
|
|
198
|
-
const isTTY = Boolean(process.stdin.isTTY);
|
|
199
|
-
const skipPrompt = options?.yes || !isTTY;
|
|
200
|
-
for (const [key, meta] of Object.entries(extraConfigMeta)) {
|
|
201
|
-
if (!meta.required) {
|
|
202
|
-
if (meta.defaultValue) {
|
|
203
|
-
values[key] = meta.defaultValue;
|
|
204
|
-
}
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (skipPrompt) {
|
|
208
|
-
if (meta.defaultValue) {
|
|
209
|
-
values[key] = meta.defaultValue;
|
|
210
|
-
} else {
|
|
211
|
-
throw new Error(
|
|
212
|
-
`Credential "${key}" is required but no default value is available. Run interactively or provide credentials via the Rush web UI.` + (meta.helpUrl ? ` Help: ${meta.helpUrl}` : "")
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
const typeHint = meta.type === "secret" ? " (secret)" : "";
|
|
218
|
-
const defaultHint = meta.defaultValue ? ` [${meta.defaultValue}]` : "";
|
|
219
|
-
const helpHint = meta.helpUrl ? `
|
|
220
|
-
Help: ${meta.helpUrl}` : "";
|
|
221
|
-
process.stderr.write(` ${key}${typeHint}${defaultHint}:${helpHint} `);
|
|
222
|
-
const input = await readOneLine();
|
|
223
|
-
const trimmed = input.trim();
|
|
224
|
-
if (trimmed) {
|
|
225
|
-
values[key] = trimmed;
|
|
226
|
-
} else if (meta.defaultValue) {
|
|
227
|
-
values[key] = meta.defaultValue;
|
|
228
|
-
} else {
|
|
229
|
-
throw new Error(
|
|
230
|
-
`Credential "${key}" is required but was not provided.` + (meta.helpUrl ? ` Help: ${meta.helpUrl}` : "")
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return values;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
175
|
// src/commands/mcp/template-utils.ts
|
|
238
176
|
var VAR_PATTERN = /\$\{([^}]+)\}/g;
|
|
239
177
|
function substituteString(template, variables) {
|
|
@@ -320,7 +258,7 @@ function mergeExtraConfigIntoServerConfig(transportType, serverConfig, extraConf
|
|
|
320
258
|
}
|
|
321
259
|
|
|
322
260
|
// src/commands/mcp/install.ts
|
|
323
|
-
function
|
|
261
|
+
function readOneLine() {
|
|
324
262
|
return new Promise((resolve) => {
|
|
325
263
|
let acc = "";
|
|
326
264
|
const onData = (chunk) => {
|
|
@@ -397,7 +335,7 @@ async function runMcpInstall(input) {
|
|
|
397
335
|
}
|
|
398
336
|
output.newline();
|
|
399
337
|
process.stderr.write("Install anyway? [y/N] ");
|
|
400
|
-
const answer = await
|
|
338
|
+
const answer = await readOneLine();
|
|
401
339
|
if (!answer.trim().toLowerCase().startsWith("y")) {
|
|
402
340
|
throw new Error("Installation cancelled by user.");
|
|
403
341
|
}
|
|
@@ -570,4 +508,4 @@ export {
|
|
|
570
508
|
runMcpInstall,
|
|
571
509
|
runMcpUninstall
|
|
572
510
|
};
|
|
573
|
-
//# sourceMappingURL=install-
|
|
511
|
+
//# sourceMappingURL=install-KY7BF54H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/mcp/config-writers.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 * 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 setValues?: Record<string, string>;\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\n // 3a. Start with --set values (highest priority)\n if (input.setValues) {\n extraConfigValues = { ...input.setValues };\n }\n\n // 3b. Prompt for remaining credentials not already provided by --set\n if (extraConfigMeta && Object.keys(extraConfigMeta).length > 0) {\n // Filter out keys already provided by --set\n const remainingMeta: Record<string, ExtraConfigFieldMeta> = {};\n for (const [key, meta] of Object.entries(extraConfigMeta)) {\n if (!(key in extraConfigValues)) {\n remainingMeta[key] = meta;\n }\n }\n\n if (Object.keys(remainingMeta).length > 0) {\n const hasRequired = Object.values(remainingMeta).some((m) => m.required);\n if (hasRequired) {\n output.newline();\n output.info(`${detail.displayName} requires credentials:`);\n }\n const prompted = await promptCredentials(remainingMeta, {\n yes: input.yes,\n });\n extraConfigValues = { ...prompted, ...extraConfigValues };\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":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/installers/claude-code/atomic-json.ts"],"sourcesContent":["/**\n * 通用的原子 JSON 读-改-写 helper(task-6 产物)。\n *\n * 契约(对齐 spec §2.1 / §3.3):\n * - 读不到 / 文件不存在 → 视为空(返回调用方传入的 `defaults`),mtime = null\n * - JSON 损坏 / 顶层 shape 非 object → 抛 `AtomicJsonCorruptError`(**不尝试修复**)\n * - 写入走 write-.tmp → rename 原子替换(POSIX 同文件系统原子)\n * - mtime 冲突检测留给调用方(Installer 在 rollback 逻辑里自己处理)——本 helper\n * 是纯磁盘 IO,不持 mtime state\n *\n * 为什么不复用 `registry.ts` 里的 `atomicWrite`:\n * - 那个函数是模块内 private(未 export)\n * - 它耦合了 `RushRegistryData` schema(load 会做 schemaVersion assert)\n * - task-6 需要写 3 个不同 schema 的 JSON(known_marketplaces / installed_plugins /\n * settings),独立 helper 更适配\n *\n * 为什么不 depend on 第三方库(如 `atomically`):\n * - rush-ai 已有极简依赖集,新增 dep 需要独立评估\n * - 语义简单到 20 行能 cover 所有场景\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { constants as fsConstants } from 'node:fs';\nimport {\n access,\n mkdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\n// ---------------------------------------------------------------------------\n// Error 类\n// ---------------------------------------------------------------------------\n\nexport class AtomicJsonError extends Error {\n constructor(\n message: string,\n public readonly filePath: string\n ) {\n super(message);\n this.name = 'AtomicJsonError';\n }\n}\n\n/** JSON 损坏或顶层非 object。spec §5.2:不尝试修复,让用户处理。 */\nexport class AtomicJsonCorruptError extends AtomicJsonError {\n constructor(\n filePath: string,\n public readonly cause: unknown\n ) {\n super(\n `${filePath} 解析失败,拒绝读取。请手工修复或备份后删除。原因:${String(\n (cause as Error | undefined)?.message ?? cause\n )}`,\n filePath\n );\n this.name = 'AtomicJsonCorruptError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// 读取\n// ---------------------------------------------------------------------------\n\nexport interface ReadJsonResult<T> {\n data: T;\n /** 文件 mtime(ms);文件不存在时为 `null`(视为首次写入) */\n mtimeMs: number | null;\n /** 文件是否在磁盘上存在(区分\"空 registry 视图 vs 真实空文件\") */\n existed: boolean;\n}\n\n/**\n * 读 JSON 文件。不存在 → `{ data: defaults(), mtimeMs: null, existed: false }`。\n *\n * `defaults` 是函数而不是值——避免默认对象被多次共享引用。\n *\n * 不做任何 shape 校验(只 gate JSON 语法 + 顶层必须是 object);调用方在拿到 `data`\n * 后自行做更严格的 schema 校验。\n */\nexport async function readJsonFile<T>(\n filePath: string,\n defaults: () => T\n): Promise<ReadJsonResult<T>> {\n if (!(await pathExists(filePath))) {\n return { data: defaults(), mtimeMs: null, existed: false };\n }\n const stats = await stat(filePath);\n const raw = await readFile(filePath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new AtomicJsonCorruptError(filePath, err);\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new AtomicJsonCorruptError(\n filePath,\n new Error('顶层必须是 JSON object,不允许数组或基本类型')\n );\n }\n return {\n data: parsed as T,\n mtimeMs: stats.mtimeMs,\n existed: true,\n };\n}\n\n// ---------------------------------------------------------------------------\n// 写入\n// ---------------------------------------------------------------------------\n\nexport interface WriteJsonOptions {\n /** 缩进空格数,默认 2(方便人工审查) */\n indent?: number;\n /** 末尾是否加换行,默认 true(符合 POSIX 文本规范) */\n trailingNewline?: boolean;\n}\n\n/**\n * 原子写 JSON 文件。\n *\n * 步骤:\n * 1. `mkdir -p` 确保父目录存在\n * 2. 写入 `<filePath>.<uuid>.tmp`\n * 3. `rename(tmp, filePath)` 原子替换\n *\n * 失败时 best-effort 清 tmp 文件(不覆盖原始错误)。\n *\n * **不做**:\n * - mtime 比对(留给调用方)\n * - JSON shape 校验(调用方已经知道自己在写什么)\n */\nexport async function writeJsonFile(\n filePath: string,\n data: unknown,\n options: WriteJsonOptions = {}\n): Promise<void> {\n const { indent = 2, trailingNewline = true } = options;\n await mkdir(dirname(filePath), { recursive: true });\n const payload =\n JSON.stringify(data, null, indent) + (trailingNewline ? '\\n' : '');\n const tmp = `${filePath}.${randomUUID()}.tmp`;\n try {\n await writeFile(tmp, payload, { encoding: 'utf8', flag: 'w' });\n await rename(tmp, filePath);\n } catch (err) {\n await rm(tmp, { force: true }).catch(() => {});\n throw err;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Read-modify-write(便于 installer 用法)\n// ---------------------------------------------------------------------------\n\n/**\n * Read-modify-write 组合操作:原子读 → 同步 mutate → 原子写。\n *\n * 典型场景:Installer 更新 known_marketplaces.json 的某个 key,不碰其他字段。\n *\n * **mtime 保护策略**:\n * - 读时记 mtimeMs\n * - 写前再 stat 比对;如果 mtime 变了 → 抛 `AtomicJsonConflictError`\n * - 调用方(Installer)可选择 retry(本 helper 不做 retry,避免隐藏状态)\n *\n * **注意**:若 `mutator` 返回 `undefined`(表示\"没变化\"),仍会写回——caller 需要\n * 自己判断是否需要写(fast path 省略 write 是 caller 的优化空间)。\n */\nexport async function updateJsonFile<T>(\n filePath: string,\n defaults: () => T,\n mutator: (data: T) => T | Promise<T>,\n options: WriteJsonOptions = {}\n): Promise<void> {\n const { data, mtimeMs } = await readJsonFile(filePath, defaults);\n const next = await mutator(data);\n\n // 写前 mtime 检测\n if (mtimeMs !== null) {\n let currentMtime: number | null = null;\n try {\n const s = await stat(filePath);\n currentMtime = s.mtimeMs;\n } catch {\n currentMtime = null;\n }\n if (currentMtime !== null && currentMtime !== mtimeMs) {\n throw new AtomicJsonConflictError(filePath);\n }\n }\n\n await writeJsonFile(filePath, next, options);\n}\n\n/** 写前 mtime 变化——caller 可 catch + retry。 */\nexport class AtomicJsonConflictError extends AtomicJsonError {\n constructor(filePath: string) {\n super(\n `${filePath} 在 read-modify-write 期间被其他进程修改。请重试。`,\n filePath\n );\n this.name = 'AtomicJsonConflictError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nexport async function pathExists(p: string): Promise<boolean> {\n try {\n await access(p, fsConstants.F_OK);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;AAqBA,SAAS,kBAAkB;AAC3B,SAAS,aAAa,mBAAmB;AACzC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe;AAMjB,IAAM,kBAAN,cAA8B,MAAM;AAAA,EACzC,YACE,SACgB,UAChB;AACA,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,yBAAN,cAAqC,gBAAgB;AAAA,EAC1D,YACE,UACgB,OAChB;AACA;AAAA,MACE,GAAG,QAAQ,0JAA6B;AAAA,QACrC,OAA6B,WAAW;AAAA,MAC3C,CAAC;AAAA,MACD;AAAA,IACF;AAPgB;AAQhB,SAAK,OAAO;AAAA,EACd;AACF;AAsBA,eAAsB,aACpB,UACA,UAC4B;AAC5B,MAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;AACjC,WAAO,EAAE,MAAM,SAAS,GAAG,SAAS,MAAM,SAAS,MAAM;AAAA,EAC3D;AACA,QAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,QAAM,MAAM,MAAM,SAAS,UAAU,MAAM;AAC3C,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,KAAK;AACZ,UAAM,IAAI,uBAAuB,UAAU,GAAG;AAAA,EAChD;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAClE,UAAM,IAAI;AAAA,MACR;AAAA,MACA,IAAI,MAAM,8GAA8B;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,MAAM;AAAA,IACf,SAAS;AAAA,EACX;AACF;AA2BA,eAAsB,cACpB,UACA,MACA,UAA4B,CAAC,GACd;AACf,QAAM,EAAE,SAAS,GAAG,kBAAkB,KAAK,IAAI;AAC/C,QAAM,MAAM,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,QAAM,UACJ,KAAK,UAAU,MAAM,MAAM,MAAM,KAAK,kBAAkB,OAAO;AACjE,QAAM,MAAM,GAAG,QAAQ,IAAI,WAAW,CAAC;AACvC,MAAI;AACF,UAAM,UAAU,KAAK,SAAS,EAAE,UAAU,QAAQ,MAAM,IAAI,CAAC;AAC7D,UAAM,OAAO,KAAK,QAAQ;AAAA,EAC5B,SAAS,KAAK;AACZ,UAAM,GAAG,KAAK,EAAE,OAAO,KAAK,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAC7C,UAAM;AAAA,EACR;AACF;AA8CO,IAAM,0BAAN,cAAsC,gBAAgB;AAAA,EAC3D,YAAY,UAAkB;AAC5B;AAAA,MACE,GAAG,QAAQ;AAAA,MACX;AAAA,IACF;AACA,SAAK,OAAO;AAAA,EACd;AACF;AAMA,eAAsB,WAAW,GAA6B;AAC5D,MAAI;AACF,UAAM,OAAO,GAAG,YAAY,IAAI;AAChC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -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 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 setValues?: Record<string, string>;\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\n // 3a. Start with --set values (highest priority)\n if (input.setValues) {\n extraConfigValues = { ...input.setValues };\n }\n\n // 3b. Prompt for remaining credentials not already provided by --set\n if (extraConfigMeta && Object.keys(extraConfigMeta).length > 0) {\n // Filter out keys already provided by --set\n const remainingMeta: Record<string, ExtraConfigFieldMeta> = {};\n for (const [key, meta] of Object.entries(extraConfigMeta)) {\n if (!(key in extraConfigValues)) {\n remainingMeta[key] = meta;\n }\n }\n\n if (Object.keys(remainingMeta).length > 0) {\n const hasRequired = Object.values(remainingMeta).some((m) => m.required);\n if (hasRequired) {\n output.newline();\n output.info(`${detail.displayName} requires credentials:`);\n }\n const prompted = await promptCredentials(remainingMeta, {\n yes: input.yes,\n });\n extraConfigValues = { ...prompted, ...extraConfigValues };\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"]}
|