ccw: fix view JSON errors and WSL MCP install

This commit is contained in:
catlog22
2026-03-05 14:29:06 +08:00
parent dc1dc87023
commit f6c7c14042
6 changed files with 258 additions and 187 deletions

View File

@@ -1210,40 +1210,68 @@ export async function handleMcpRoutes(ctx: RouteContext): Promise<boolean> {
return true;
}
// API: Install CCW MCP server to project
// API: Install CCW MCP server (global or project scope)
if (pathname === '/api/mcp-install-ccw' && req.method === 'POST') {
handlePostRequest(req, res, async (body) => {
if (!isRecord(body)) {
return { error: 'Invalid request body', status: 400 };
}
const projectPath = body.projectPath;
if (typeof projectPath !== 'string' || !projectPath.trim()) {
return { error: 'projectPath is required', status: 400 };
const rawScope = body.scope;
const scope = rawScope === 'global' || rawScope === 'project' ? rawScope : undefined;
// Backward compatibility: allow legacy fields at top-level as well as body.env
const envInput = (isRecord(body.env) ? body.env : body) as Record<string, unknown>;
const projectPath = typeof body.projectPath === 'string' ? body.projectPath : undefined;
const enableSandbox = envInput.enableSandbox === true;
const enabledToolsRaw = envInput.enabledTools;
let enabledToolsEnv: string;
if (enabledToolsRaw === undefined || enabledToolsRaw === null) {
enabledToolsEnv = 'write_file,edit_file,read_file,core_memory,ask_question,smart_search';
} else if (Array.isArray(enabledToolsRaw)) {
enabledToolsEnv = enabledToolsRaw.filter((t): t is string => typeof t === 'string').join(',');
} else if (typeof enabledToolsRaw === 'string') {
enabledToolsEnv = enabledToolsRaw;
} else {
enabledToolsEnv = 'write_file,edit_file,read_file,core_memory,ask_question,smart_search';
}
// Check if sandbox should be enabled
const enableSandbox = body.enableSandbox === true;
const projectRoot = typeof envInput.projectRoot === 'string' ? envInput.projectRoot : undefined;
// Parse enabled tools from request body
const enabledTools = Array.isArray(body.enabledTools) && body.enabledTools.length > 0
? (body.enabledTools as string[]).join(',')
: 'write_file,edit_file,read_file,core_memory,ask_question,smart_search';
const allowedDirsRaw = envInput.allowedDirs;
let allowedDirsEnv: string | undefined;
if (Array.isArray(allowedDirsRaw)) {
allowedDirsEnv = allowedDirsRaw.filter((d): d is string => typeof d === 'string').join(',');
} else if (typeof allowedDirsRaw === 'string') {
allowedDirsEnv = allowedDirsRaw;
}
// Generate CCW MCP server config
// Use cmd /c on Windows to inherit Claude Code's working directory
// Generate CCW MCP server config using *server-side* platform detection.
// On WSL/Linux, this ensures we produce `npx -y ccw-mcp` even when the browser runs on Windows.
const isWin = process.platform === 'win32';
const env: Record<string, string> = { CCW_ENABLED_TOOLS: enabledToolsEnv };
if (projectRoot) env.CCW_PROJECT_ROOT = projectRoot;
if (allowedDirsEnv) env.CCW_ALLOWED_DIRS = allowedDirsEnv;
if (enableSandbox) env.CCW_ENABLE_SANDBOX = '1';
const ccwMcpConfig: Record<string, any> = {
command: isWin ? "cmd" : "npx",
args: isWin ? ["/c", "npx", "-y", "ccw-mcp"] : ["-y", "ccw-mcp"],
env: {
CCW_ENABLED_TOOLS: enabledTools,
...(enableSandbox && { CCW_ENABLE_SANDBOX: "1" })
}
command: isWin ? 'cmd' : 'npx',
args: isWin ? ['/c', 'npx', '-y', 'ccw-mcp'] : ['-y', 'ccw-mcp'],
env
};
// Use existing addMcpServerToProject to install CCW MCP
return addMcpServerToProject(projectPath, 'ccw-tools', ccwMcpConfig);
const resolvedScope: 'global' | 'project' = scope ?? (projectPath ? 'project' : 'global');
if (resolvedScope === 'project') {
if (!projectPath || !projectPath.trim()) {
return { error: 'projectPath is required for project scope', status: 400 };
}
return addMcpServerToProject(projectPath, 'ccw-tools', ccwMcpConfig);
}
return addGlobalMcpServer('ccw-tools', ccwMcpConfig);
});
return true;
}