{"record":{"id":"c34fafe04038cc34","repo":"angular/angular-cli","slug":"workspace-path-is-outside-the-allowed-mcp-roots-c34faf","errorCode":null,"errorMessage":"Workspace path is outside the allowed MCP roots: ${workspacePathInput}. You can use 'list_projects' to find available workspaces.","messagePattern":"Workspace path is outside the allowed MCP roots: (.+?)\\. You can use 'list_projects' to find available workspaces\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/angular/cli/src/commands/mcp/workspace-utils.ts","lineNumber":165,"sourceCode":"  let workspacePath: string;\n  let workspace: AngularWorkspace;\n\n  if (workspacePathInput) {\n    if (!host.existsSync(workspacePathInput)) {\n      throw new Error(\n        `Workspace path does not exist: ${workspacePathInput}. ` +\n          \"You can use 'list_projects' to find available workspaces.\",\n      );\n    }\n    if (!host.existsSync(join(workspacePathInput, 'angular.json'))) {\n      throw new Error(\n        `No angular.json found at ${workspacePathInput}. ` +\n          \"You can use 'list_projects' to find available workspaces.\",\n      );\n    }\n    if (server) {\n      if (!(await isAllowedWorkspacePath(server, workspacePathInput))) {\n        throw new Error(\n          `Workspace path is outside the allowed MCP roots: ${workspacePathInput}. ` +\n            \"You can use 'list_projects' to find available workspaces.\",\n        );\n      }\n    }\n\n    workspacePath = workspacePathInput;\n    const configPath = join(workspacePath, 'angular.json');\n    try {\n      workspace = await AngularWorkspace.load(configPath);\n    } catch (e) {\n      throw new Error(`Failed to load workspace configuration at ${configPath}`, { cause: e });\n    }\n  } else if (mcpWorkspace) {\n    workspace = mcpWorkspace;\n    workspacePath = workspace.basePath;\n  } else {\n    const found = findAngularJsonDir(process.cwd(), host);","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/cli/src/commands/mcp/workspace-utils.ts#L147-L183","documentation":"The Angular CLI MCP server restricts all workspace operations to a configured set of allowed root directories (the MCP roots, derived from the client's workspace folders). Before using an explicitly supplied `workspacePath`, `resolveWorkspaceAndProject` checks it (after realpath resolution) against these roots via `isAllowedWorkspacePath`. If the path is not contained within any allowed root, this error is thrown to prevent the MCP tool from reading or modifying directories outside the sandbox the client approved.","triggerScenarios":"Calling an MCP tool (e.g. find_tests, build, generators) with a `workspacePath` argument pointing to a directory outside the IDE/MCP client's opened workspace folders — or to a path that resolves (via symlink/realpath) outside them — while the `server` context defines allowed roots.","commonSituations":"Passing an absolute path to another checkout or monorepo on disk while only one project is open in the IDE; the target directory is reached through a symlink whose real target lies outside the roots; a moved/renamed project folder that is no longer under the opened workspace; running the MCP server standalone without roots configured to include the path.","solutions":["Open the target project's folder (or add it as a workspace folder) in the IDE/MCP client so its real path falls under an allowed MCP root, then retry","Call the `list_projects` MCP tool to see which workspaces are currently allowed and use one of those paths","Verify the path has no symlink redirecting outside the allowed roots (the check uses realpathSync on both sides)","Restart or reconfigure the MCP server so its allowed roots include the directory"],"exampleFix":"// before\nawait mcp.callTool('ng_search', { workspacePath: '/home/me/other-repo/apps/web' });\n// after\nawait mcp.callTool('ng_search', { workspacePath: '/home/me/my-repo/apps/web' }); // opened in the IDE, inside MCP roots","handlingStrategy":"validation","validationCode":"import { realpathSync } from 'node:fs';\n// allowedRoots: string[] obtained from the MCP client/IDE workspace folders\nfunction isInsideAllowedRoots(workspacePath: string, allowedRoots: string[]): boolean {\n  const real = realpathSync(workspacePath);\n  return allowedRoots.some((root) => {\n    const realRoot = realpathSync(root);\n    return real === realRoot || real.startsWith(realRoot + '/');\n  });\n}\nif (!isInsideAllowedRoots(workspacePath, allowedRoots)) {\n  throw new Error(`Refusing to call MCP tool: ${workspacePath} is outside allowed MCP roots`);\n}","typeGuard":"function isPathWithinRoot(childPath: string, rootPath: string): boolean {\n  const rel = path.relative(path.resolve(rootPath), path.resolve(childPath));\n  return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));\n}","tryCatchPattern":"try {\n  await mcp.callTool('ng_search', { workspacePath });\n} catch (e) {\n  if (String(e?.message).includes('outside the allowed MCP roots')) {\n    // fall back to listing allowed workspaces and picking a valid one\n    const projects = await mcp.callTool('list_projects', {});\n    console.warn('Workspace not allowed; available:', projects);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Only pass workspacePath values that match folders opened in the IDE / registered with the MCP server","Call list_projects first when unsure which paths are permitted","Resolve symlinks before passing paths — the check compares realpaths","Keep the MCP server's configured roots in sync with multi-root IDE workspaces","Avoid passing paths across repository checkouts not opened in the client"],"tags":["mcp","security","path-validation","workspace","angular-cli"],"backgroundTag":"path-outside-allowed-roots","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}