{"record":{"id":"538ae44fde18a7a1","repo":"angular/angular-cli","slug":"workspace-path-is-outside-the-allowed-mcp-roots","errorCode":null,"errorMessage":"Workspace path is outside the allowed MCP roots: ${workspacePath}. 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/tools/best-practices.ts","lineNumber":113,"sourceCode":"  workspacePath: string,\n  logger: McpToolContext['logger'],\n  server: McpToolContext['server'],\n): Promise<{ content: string; source: string } | undefined> {\n  if (server) {\n    let isAllowed: boolean;\n    try {\n      isAllowed = await isAllowedWorkspacePath(server, workspacePath);\n    } catch (e) {\n      logger.warn(\n        `Failed to verify workspace path '${workspacePath}': ` +\n          `${e instanceof Error ? e.message : e}. Falling back to the bundled guide.`,\n      );\n\n      return undefined;\n    }\n\n    if (!isAllowed) {\n      throw new Error(\n        `Workspace path is outside the allowed MCP roots: ${workspacePath}. ` +\n          \"You can use 'list_projects' to find available workspaces.\",\n      );\n    }\n  }\n\n  // 1. Resolve the path to package.json\n  let pkgJsonPath: string;\n  try {\n    const workspaceRequire = createRequire(workspacePath);\n    pkgJsonPath = workspaceRequire.resolve('@angular/core/package.json');\n  } catch (e) {\n    logger.warn(\n      `Could not resolve '@angular/core/package.json' from '${workspacePath}'. ` +\n        'Is Angular installed in this project? Falling back to the bundled guide.',\n    );\n\n    return undefined;","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular/cli/src/commands/mcp/tools/best-practices.ts#L95-L131","documentation":"Before serving version-specific Angular best practices, the tool verifies the resolved workspace path is inside the allowed MCP roots. If workspacePath falls outside every root, it throws with a hint to use list_projects to find accessible workspaces.","triggerScenarios":"Calling a version-specific best-practices tool when the target workspace path (from the tool input or an inferred cwd) resolves outside the roots configured on the MCP host.","commonSituations":"MCP client opened on a directory different from the Angular workspace; workspace inside a symlink that resolves outside roots; multi-root setups where only some projects are registered as roots.","solutions":["Run the MCP server with the Angular workspace directory as the working directory or add it to the roots.","Call setRoots with the workspace path before invoking the tool.","Use the list_projects tool to find which workspaces are actually accessible.","Remove symlinks so the real workspace path is inside an allowed root."],"exampleFix":"// before\ngetVersionSpecificBestPractices({ workspacePath: '/shared/legacy-app' });\n// after\nhost.setRoots(['/home/dev/legacy-app']);\ngetVersionSpecificBestPractices({ workspacePath: '/home/dev/legacy-app' });","handlingStrategy":"validation","validationCode":"import { relative, isAbsolute } from 'node:path';\nfunction workspaceAllowed(workspacePath: string, roots: string[]): boolean {\n  return roots.some((root) => {\n    const rel = relative(root, workspacePath);\n    return !rel.startsWith('..') && !isAbsolute(rel);\n  });\n}\nif (!workspaceAllowed(ws, allowedRoots)) {\n  console.warn('use list_projects to find accessible workspaces');\n}","typeGuard":"function isInAllowedWorkspace(p: string, roots: string[]): p is string {\n  return roots.some((r) => !relative(r, p).startsWith('..') && !isAbsolute(relative(r, p)));\n}","tryCatchPattern":"try {\n  await versionSpecificBestPractices({ workspacePath });\n} catch (e) {\n  if ((e as Error).message.includes('outside the allowed MCP roots')) {\n    const projects = await listProjects();\n    await versionSpecificBestPractices({ workspacePath: projects[0].path });\n  } else throw e;\n}","preventionTips":["Start the MCP server from the Angular workspace directory","Keep the real (non-symlinked) workspace path inside a configured root","Discover valid workspaces with list_projects before invoking tools","Update roots via setRoots when the client switches projects"],"tags":["security","workspace","angular-cli","mcp"],"backgroundTag":"path-outside-allowed-roots","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}