@angular/cli 22.0.0-rc.0 → 22.0.0-rc.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.
Files changed (66) hide show
  1. package/bin/ng.js +1 -1
  2. package/package.json +14 -14
  3. package/src/commands/mcp/ARCHITECTURE.md +117 -0
  4. package/src/commands/mcp/DESIGN.md +266 -0
  5. package/src/commands/mcp/LLM_ERGONOMICS.md +258 -0
  6. package/src/commands/mcp/devserver.d.ts +1 -1
  7. package/src/commands/mcp/devserver.js +5 -8
  8. package/src/commands/mcp/devserver.js.map +1 -1
  9. package/src/commands/mcp/host.d.ts +9 -5
  10. package/src/commands/mcp/host.js +37 -22
  11. package/src/commands/mcp/host.js.map +1 -1
  12. package/src/commands/mcp/mcp-server.d.ts +8 -42
  13. package/src/commands/mcp/mcp-server.js +20 -25
  14. package/src/commands/mcp/mcp-server.js.map +1 -1
  15. package/src/commands/mcp/resources/best-practices.md +0 -1
  16. package/src/commands/mcp/tools/best-practices.js +9 -17
  17. package/src/commands/mcp/tools/best-practices.js.map +1 -1
  18. package/src/commands/mcp/tools/doc-search.js +11 -33
  19. package/src/commands/mcp/tools/doc-search.js.map +1 -1
  20. package/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.d.ts +2 -1
  21. package/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.js +2 -2
  22. package/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.js.map +1 -1
  23. package/src/commands/mcp/tools/onpush-zoneless-migration/migrate-test-file.d.ts +3 -2
  24. package/src/commands/mcp/tools/onpush-zoneless-migration/migrate-test-file.js +8 -8
  25. package/src/commands/mcp/tools/onpush-zoneless-migration/migrate-test-file.js.map +1 -1
  26. package/src/commands/mcp/tools/onpush-zoneless-migration/ts-utils.d.ts +2 -1
  27. package/src/commands/mcp/tools/onpush-zoneless-migration/ts-utils.js +2 -3
  28. package/src/commands/mcp/tools/onpush-zoneless-migration/ts-utils.js.map +1 -1
  29. package/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.d.ts +2 -1
  30. package/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.js +29 -41
  31. package/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.js.map +1 -1
  32. package/src/commands/mcp/tools/projects.d.ts +1 -0
  33. package/src/commands/mcp/tools/projects.js +15 -18
  34. package/src/commands/mcp/tools/projects.js.map +1 -1
  35. package/src/commands/mcp/tools/run-target/build-target-strategy.d.ts +14 -0
  36. package/src/commands/mcp/tools/run-target/build-target-strategy.js +53 -0
  37. package/src/commands/mcp/tools/run-target/build-target-strategy.js.map +1 -0
  38. package/src/commands/mcp/tools/run-target/generic-target-strategy.d.ts +14 -0
  39. package/src/commands/mcp/tools/run-target/generic-target-strategy.js +63 -0
  40. package/src/commands/mcp/tools/run-target/generic-target-strategy.js.map +1 -0
  41. package/src/commands/mcp/tools/run-target/options-serializer.d.ts +13 -0
  42. package/src/commands/mcp/tools/run-target/options-serializer.js +41 -0
  43. package/src/commands/mcp/tools/run-target/options-serializer.js.map +1 -0
  44. package/src/commands/mcp/tools/run-target/run-target.d.ts +34 -0
  45. package/src/commands/mcp/tools/run-target/run-target.js +69 -0
  46. package/src/commands/mcp/tools/run-target/run-target.js.map +1 -0
  47. package/src/commands/mcp/tools/run-target/strategy.d.ts +15 -0
  48. package/src/commands/mcp/tools/run-target/strategy.js +10 -0
  49. package/src/commands/mcp/tools/run-target/strategy.js.map +1 -0
  50. package/src/commands/mcp/tools/run-target/types.d.ts +39 -0
  51. package/src/commands/mcp/tools/run-target/types.js +41 -0
  52. package/src/commands/mcp/tools/run-target/types.js.map +1 -0
  53. package/src/commands/mcp/tools/run-target/unit-test-strategy.d.ts +14 -0
  54. package/src/commands/mcp/tools/run-target/unit-test-strategy.js +56 -0
  55. package/src/commands/mcp/tools/run-target/unit-test-strategy.js.map +1 -0
  56. package/src/utilities/node-version.js +1 -1
  57. package/src/utilities/version.js +1 -1
  58. package/src/commands/mcp/tools/build.d.ts +0 -37
  59. package/src/commands/mcp/tools/build.js +0 -90
  60. package/src/commands/mcp/tools/build.js.map +0 -1
  61. package/src/commands/mcp/tools/e2e.d.ts +0 -44
  62. package/src/commands/mcp/tools/e2e.js +0 -88
  63. package/src/commands/mcp/tools/e2e.js.map +0 -1
  64. package/src/commands/mcp/tools/test.d.ts +0 -35
  65. package/src/commands/mcp/tools/test.js +0 -88
  66. package/src/commands/mcp/tools/test.js.map +0 -1
package/bin/ng.js CHANGED
@@ -57,7 +57,7 @@ if (major === 23 || major === 25) {
57
57
 
58
58
  require('./bootstrap');
59
59
  } else if (!nodeUtils.isNodeVersionSupported()) {
60
- // Error and exit if less than 22.22 or 24.13.1
60
+ // Error and exit if less than the supported versions.
61
61
  console.error(
62
62
  'Node.js version ' +
63
63
  process.version +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/cli",
3
- "version": "22.0.0-rc.0",
3
+ "version": "22.0.0-rc.2",
4
4
  "description": "CLI tool for Angular",
5
5
  "main": "lib/cli/index.js",
6
6
  "bin": {
@@ -15,13 +15,13 @@
15
15
  "sdk"
16
16
  ],
17
17
  "dependencies": {
18
- "@angular-devkit/architect": "0.2200.0-rc.0",
19
- "@angular-devkit/core": "22.0.0-rc.0",
20
- "@angular-devkit/schematics": "22.0.0-rc.0",
18
+ "@angular-devkit/architect": "0.2200.0-rc.2",
19
+ "@angular-devkit/core": "22.0.0-rc.2",
20
+ "@angular-devkit/schematics": "22.0.0-rc.2",
21
21
  "@inquirer/prompts": "8.4.2",
22
22
  "@listr2/prompt-adapter-inquirer": "4.2.3",
23
23
  "@modelcontextprotocol/sdk": "1.29.0",
24
- "@schematics/angular": "22.0.0-rc.0",
24
+ "@schematics/angular": "22.0.0-rc.2",
25
25
  "@yarnpkg/lockfile": "1.1.0",
26
26
  "algoliasearch": "5.52.0",
27
27
  "ini": "6.0.0",
@@ -37,14 +37,14 @@
37
37
  "ng-update": {
38
38
  "migrations": "@schematics/angular/migrations/migration-collection.json",
39
39
  "packageGroup": {
40
- "@angular/cli": "22.0.0-rc.0",
41
- "@angular/build": "22.0.0-rc.0",
42
- "@angular/ssr": "22.0.0-rc.0",
43
- "@angular-devkit/architect": "0.2200.0-rc.0",
44
- "@angular-devkit/build-angular": "22.0.0-rc.0",
45
- "@angular-devkit/build-webpack": "0.2200.0-rc.0",
46
- "@angular-devkit/core": "22.0.0-rc.0",
47
- "@angular-devkit/schematics": "22.0.0-rc.0"
40
+ "@angular/cli": "22.0.0-rc.2",
41
+ "@angular/build": "22.0.0-rc.2",
42
+ "@angular/ssr": "22.0.0-rc.2",
43
+ "@angular-devkit/architect": "0.2200.0-rc.2",
44
+ "@angular-devkit/build-angular": "22.0.0-rc.2",
45
+ "@angular-devkit/build-webpack": "0.2200.0-rc.2",
46
+ "@angular-devkit/core": "22.0.0-rc.2",
47
+ "@angular-devkit/schematics": "22.0.0-rc.2"
48
48
  }
49
49
  },
50
50
  "repository": {
@@ -52,7 +52,7 @@
52
52
  "url": "git+https://github.com/angular/angular-cli.git"
53
53
  },
54
54
  "engines": {
55
- "node": "^22.22.0 || ^24.13.1 || >=26.0.0",
55
+ "node": "^22.22.3 || ^24.15.0 || >=26.0.0",
56
56
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
57
57
  "yarn": ">= 1.13.0"
58
58
  },
@@ -0,0 +1,117 @@
1
+ # Comprehensive Architecture Review & Analysis: Angular CLI MCP Server
2
+
3
+ The Model Context Protocol (MCP) server implementation within the Angular CLI (`packages/angular/cli/src/commands/mcp`) represents a state-of-the-art bridge between AI assistants (LLMs/agents) and the Angular development ecosystem.
4
+
5
+ Rather than forcing AI assistants to rely on generic, error-prone shell execution tools (`run_shell_command`), the Angular CLI MCP server exposes a highly structured, secure, and context-aware suite of tools. It encapsulates complex Angular workflows—such as monorepo project discovery, background dev server management, headless testing, AST-based migrations, and RAG-assisted documentation lookups—into clean, programmatic APIs.
6
+
7
+ Below is a detailed architectural review and analysis of the implementation, broken down by core infrastructure, tool subsystems, architectural strengths, and potential areas for future enhancement.
8
+
9
+ ---
10
+
11
+ ## 1. High-Level Architecture & Security Model
12
+
13
+ ```
14
+ ┌──────────────────────────────────────────────────────────────────────────┐
15
+ │ McpCommandModule (cli.ts) │
16
+ │ Handles Yargs CLI flags (--read-only, --local-only, --exp) │
17
+ └────────────────────────────────────┬─────────────────────────────────────┘
18
+
19
+ ┌──────────────────────────────────────────────────────────────────────────┐
20
+ │ McpServer Initialization │
21
+ │ Configures Capabilities (tools, resources, roots, sampling, logging) │
22
+ └────────────────────────────────────┬─────────────────────────────────────┘
23
+
24
+ ┌──────────────────────────────────────────────────────────────────────────┐
25
+ │ Root-Restricted Host (host.ts) │
26
+ │ Sandboxes fs/child_process to client roots; resolves local ng binary │
27
+ └────────────────────────────────────┬─────────────────────────────────────┘
28
+
29
+ ┌──────────────────────────────────────────────────────────────────────────┐
30
+ │ Tool Registry & Context (tool-registry.ts) │
31
+ │ Assembles Stable & Experimental tools; injects Host & Devserver Map │
32
+ └────────────────────────────────────┬─────────────────────────────────────┘
33
+
34
+ ┌──────────────────────────────────────────────────────────────────────────┐
35
+ │ Specialized Tool Implementations │
36
+ │ [Projects] [Devserver] [Build/Test/E2E] [Doc Search] [Migrations] │
37
+ └──────────────────────────────────────────────────────────────────────────┘
38
+ ```
39
+
40
+ ### Command Module & Lifecycle (`cli.ts`, `mcp-server.ts`)
41
+ * **Interactive vs. Programmatic Execution**: The CLI command checks `isTTY()`. If a user runs `ng mcp` directly in an interactive terminal, it gracefully outputs a JSON configuration snippet demonstrating how to configure an MCP client (e.g., Claude Desktop, Cursor) to attach the server via `npx -y @angular/cli mcp`. When run in a non-TTY environment, it establishes an MCP `StdioServerTransport`.
42
+ * **Granular Security Flags**: It supports `--read-only` and `--local-only` flags. `assembleToolDeclarations` filters registered tools dynamically based on declarative tool properties (`tool.isReadOnly`, `tool.isLocalOnly`).
43
+ * **Server Instructions**: The server initializes with robust, overarching system instructions (`instructions` capability) that orient the LLM on mandatory workflows, such as discovering project structure first (`list_projects`) and fetching version-specific coding standards (`get_best_practices`).
44
+
45
+ ### Sandboxed Host Abstraction (`host.ts`)
46
+ To ensure safe execution within arbitrary client environments, all operating system and filesystem interactions are routed through a concrete `Host` interface (`LocalWorkspaceHost`), wrapped by `createRootRestrictedHost`.
47
+ * **Dynamic Root Restriction**: The server negotiates the `roots` capability with the MCP client. It resolves allowed workspace roots via `server.server.listRoots()` and actively listens for `RootsListChangedNotificationSchema` to dynamically update the sandboxed boundaries.
48
+ * **Path Traversal Prevention**: Every filesystem operation (`stat`, `existsSync`, `readFile`, `glob`) and process execution (`executeNgCommand`, `startNgProcess`) is validated against the allowed roots. It uses `realpathSync` to resolve symlinks and strictly rejects any relative path traversal (`..`) or absolute paths escaping the root hierarchy.
49
+ * **Smart Local Binary Resolution**: `resolveNgCommand` uses `createRequire` from the target working directory's `package.json` to locate `@angular/cli/package.json` and resolve the local `ng.js` binary executed via `process.execPath`. This ensures that background builds and tests always run using the exact Angular CLI version installed in the workspace rather than relying on a potentially mismatched global `ng` binary.
50
+
51
+ ---
52
+
53
+ ## 2. Deep Dive into Tool Subsystems
54
+
55
+ ### A. Project & Workspace Discovery (`projects.ts`)
56
+ The `list_projects` tool serves as the foundational entry point for the AI assistant to understand the workspace layout.
57
+ * **High-Performance BFS Traversal**: Instead of naive recursive directory searching (which risks call-stack limits or memory bloat), `findAngularJsonFiles` implements an iterative, queue-based Breadth-First Search with a strict concurrency limit (`CONCURRENCY_LIMIT = 50`) to prevent file descriptor exhaustion (`EMFILE` errors).
58
+ * **Symlink & Loop Defense**: It tracks visited directory inodes (`seenInodes`) to prevent infinite loops in complex symlinked monorepos while validating that symlink targets remain within allowed MCP roots. It explicitly skips irrelevant heavy directories (`node_modules`, `dist`, `out`, `coverage`).
59
+ * **Intelligent Project Heuristics**:
60
+ * **Test Framework Detection**: `getUnitTestFramework` inspects target builders. It distinguishes between modern `@angular/build:unit-test` (parsing the `runner` option for `karma` vs. `vitest`) and older builders (`karma`, `jest`, `web-test-runner`).
61
+ * **Style Language Heuristics**: `getProjectStyleLanguage` evaluates project-level schematics, workspace-level schematics, build target `inlineStyleLanguage`, explicit `styles` arrays, and finally probes the filesystem for `styles.(scss|sass|less|css)`.
62
+ * **Framework Versioning**: `findAngularCoreVersion` traverses upwards looking for `@angular/core` in `package.json` to determine the major framework version, caching intermediate paths to optimize monorepo scanning.
63
+
64
+ ### B. Development Server Management (`devserver.ts`, `devserver-*.ts`)
65
+ Managing long-running background processes over a request-response protocol like MCP requires sophisticated state handling.
66
+ * **Stateful Process Map**: `tool-registry.ts` injects a shared `devservers` Map into the tool context, tracking active `LocalDevserver` instances keyed by `workspacePath:projectName`.
67
+ * **Stream Parsing & Build Tracking**: `LocalDevserver` spawns `ng serve` as a background child process. It attaches listeners to `stdout`/`stderr` to monitor build lifecycle strings (`BUILD_SUCCEEDED_MESSAGE`, `BUILD_FAILED_MESSAGE`, `CHANGES_DETECTED_START_MESSAGE`). This allows the server to maintain an accurate `isBuilding()` state and cache the latest build logs.
68
+ * **Synchronous Rebuild Verification (`devserver.wait_for_build`)**: LLMs often struggle with asynchronous file watchers. This tool allows the AI to edit a file and immediately call `devserver.wait_for_build`. The tool polls `devserver.isBuilding()` (with a `WATCH_DELAY` of 1000ms and configurable timeout), blocking until the live rebuild completes and returning the fresh build status and logs.
69
+ * **Automated Port Assignment**: If a port is not provided, `host.getAvailablePort()` spins up a temporary TCP server on port `0` to let the OS assign a guaranteed free port, closing it immediately before launching `ng serve`.
70
+
71
+ ### C. Build, Test, and E2E Runners (`build.ts`, `test.ts`, `e2e.ts`)
72
+ These one-off execution tools wrap `ng build`, `ng test`, and `ng e2e` with smart environment adaptations:
73
+ * **Build Tool**: Defaults to the `development` configuration (unlike standard `ng build` which defaults to `production`), ensuring faster feedback loops for the AI. It parses the execution logs for `Output location: (.*)` to return the exact artifact path.
74
+ * **Test Tool**: Automatically configures headless execution to ensure non-interactive compatibility. If the builder is `@angular/build:unit-test` (Vitest), it appends `--headless true`. For Karma-based runners, it appends `--browsers ChromeHeadless`. It forces `--watch false` and supports granular `--filter` execution.
75
+ * **E2E Tool**: Includes pre-execution verification, checking the `angular.json` AST/model to ensure an `e2e` target actually exists before attempting execution, providing informative error messages if setup is required.
76
+
77
+ ### D. Documentation Search (`doc-search.ts`)
78
+ The `search_documentation` tool provides RAG capabilities by querying official `angular.dev` documentation via Algolia.
79
+ * **Secure API Key Decryption**: To prevent scraping or abuse of the Algolia search key, the key is stored in an encrypted form (`constants.ts`) and decrypted at runtime using `node:crypto` `createDecipheriv` (AES-256-GCM).
80
+ * **Version Clamping & Safe Harbor**: It aligns queries with the user's local Angular major version. It clamps minimum versions to v17 (`MIN_SUPPORTED_DOCS_VERSION`). To prevent race conditions when a new major Angular version is released before its Algolia index is fully published, it implements a fallback to a hardcoded stable safe harbor (`LATEST_KNOWN_DOCS_VERSION = 20`).
81
+ * **Stream-Based HTML Rewriting**: When `includeTopContent` is requested, it fetches the top hit URL (strictly validating the hostname is `angular.dev` or `*.angular.dev`). To avoid overwhelming the LLM's context window with bloated HTML (navigation, sidebars, footers), it uses `parse5-html-rewriting-stream` to stream and extract *only* the inner text of the `<main>` DOM element.
82
+
83
+ ### E. AI Tutor & Best Practices (`ai-tutor.ts`, `best-practices.ts`, `BUILD.bazel`)
84
+ * **Prompt & Persona Injection**: The `ai_tutor` tool acts as a RAG source that injects a specialized curriculum. It leverages MCP annotations (`annotations: { audience: ['assistant'], priority: 1.0 }`) to instruct the client to treat the content as high-priority system instructions rather than displaying it to the user.
85
+ * **Version-Aware Standards**: `get_best_practices` inspects the user's installed `@angular/core/package.json` for custom `angular.bestPractices` metadata to load version-matched guidelines. If missing or if path traversal/size limits (>1MB) are breached, it falls back to a generic guide bundled directly into the CLI package via Bazel genrules (`BUILD.bazel`).
86
+
87
+ ### F. OnPush & Zoneless Migration (`zoneless-migration.ts` & Subsystem)
88
+ This subsystem provides an advanced, AST-driven refactoring workflow designed for iterative execution (one actionable step at a time).
89
+ * **TypeScript AST Categorization**: It uses the TypeScript Compiler API (`ts-utils.ts`) to parse source files and categorize them into `zoneFiles` (containing unsupported `NgZone` imports), `filesWithComponents` (lacking `OnPush`), and `componentTestFiles`.
90
+ * **Advanced MCP Client Sampling (`sampling/createMessage`)**: In an elegant display of advanced MCP capabilities, when multiple component files require migration, `rankComponentFilesForMigration` invokes the MCP `sampling/createMessage` request. It asks the LLM client to evaluate the file list and rank them so that shared/common components (`shared/`, `common/`, `ui/`) are migrated first.
91
+ * **Granular Prompting**: It provides highly specific, diff-illustrated refactoring prompts for replacing `onMicrotaskEmpty`/`onStable` with `afterNextRender`/`afterEveryRender`, converting state to Signals or `markForCheck()`, and debugging test failures (`ExpressionChangedAfterItHasBeenCheckedError`).
92
+
93
+ ---
94
+
95
+ ## 3. Summary of Architectural Strengths
96
+
97
+ 1. **Flawless MCP Specification Alignment**: The implementation takes full advantage of the MCP specification, utilizing Tool Annotations (`readOnlyHint`, `openWorldHint`, `audience`, `priority`), Client Sampling (`sampling/createMessage`), Dynamic Roots (`listRoots`), and Stdio lifecycle management.
98
+ 2. **Defensive & Resilient Design**: The codebase exhibits rigorous defensive programming. It includes strict sandboxing in `host.ts`, inode cycle detection in `projects.ts`, fallback safe-harbor versioning in `doc-search.ts`, and graceful handling of ignorable filesystem locks/permission errors (`EACCES`, `EPERM`, `EBUSY`).
99
+ 3. **Token & Context Optimization**: Recognizing the bandwidth and context constraints of LLMs, the server avoids dumping raw data. It filters out non-essential files, streams only `<main>` content from documentation web pages, and forces iterative, single-action responses during complex migrations.
100
+
101
+ ---
102
+
103
+ ## 4. Potential Limitations & Recommendations for Future Enhancement
104
+
105
+ While the implementation is exceptionally robust, a thorough analysis reveals a few edge cases and opportunities for refinement:
106
+
107
+ ### 1. Devserver Log Matching Fragility
108
+ * **Observation**: In `devserver.ts`, `addLog()` evaluates build status using exact string matching (`log.startsWith(BUILD_SUCCEEDED_MESSAGE)`). If the underlying Angular CLI builder output includes ANSI color codes, terminal escape sequences, or if log formatting changes in future CLI releases, `log.startsWith` may fail to match, leaving the server in an incorrect `isBuilding() = true` state.
109
+ * **Recommendation**: Strip ANSI codes from incoming data chunks before matching, or better yet, configure the child process to use a structured JSON IPC/logging mechanism if supported by the Angular CLI dev server builder.
110
+
111
+ ### 2. Global Binary Fallback Risk
112
+ * **Observation**: In `host.ts`, `resolveNgCommand` attempts to find the local `ng.js` binary. If `createRequire` fails (e.g., in an unusual monorepo or hoisting setup), it falls back to `{ command: 'ng', args }`. If the host environment lacks a globally installed `@angular/cli` or has an outdated global version, process execution will fail unexpectedly.
113
+ * **Recommendation**: When local resolution fails, verify the existence of global `ng` via `where`/`which` or inspect `node_modules/.bin/ng` before falling back, providing a clear diagnostic warning to the logger if a global binary fallback is triggered.
114
+
115
+ ### 3. AST Decorator Matching in Migrations
116
+ * **Observation**: In `migrate-single-file.ts`, the AST visitor explicitly checks for `ts.isDecorator(node)` where the expression is a call to `Component`. If an enterprise codebase aliases the `@Component` decorator, wraps it in a custom decorator (e.g., `@CustomUIComponent`), or relies on intermediate base class inheritance for change detection strategies, the migration tool will bypass the file.
117
+ * **Recommendation**: Utilize the TypeScript type checker (if a full program can be cheaply instantiated) to resolve base classes/decorator aliases, or expand the AST heuristic to inspect imported decorator aliases.
@@ -0,0 +1,266 @@
1
+ # Architectural Design Document: Evolving the Angular CLI MCP Server
2
+
3
+ **Title**: Unified Target Execution, Structured Feedback & Generalized Watch Management
4
+ **Status**: Proposed / Architectural Roadmap
5
+ **Author**: Antigravity AI Assistant
6
+ **Target Area**: `packages/angular/cli/src/commands/mcp`
7
+
8
+ ---
9
+
10
+ ## 1. Executive Summary & Problem Statement
11
+
12
+ The initial implementation of the Angular CLI Model Context Protocol (MCP) server successfully established a secure, programmatic sandbox for AI assistants. However, as AI agent workflows mature, four distinct architectural bottlenecks have emerged:
13
+
14
+ 1. **Tool Proliferation & Schema Bloat**: Hardcoding separate MCP tools (`build`, `test`, `e2e`, `devserver.start`, `devserver.stop`, `devserver.wait`) for individual Angular CLI commands increases the tool registry size. This inflates the system prompt token overhead on every LLM request and forces schema divergence for overlapping flags.
15
+ 2. **Opaque Workspace Capabilities**: The foundational `list_projects` tool exposes project roots and test frameworks but omits configured architectural targets (e.g., `lint`, `e2e`, `prerender`, `deploy`, `storybook`). This forces AI agents into an inefficient "guess and check" execution pattern.
16
+ 3. **Unstructured Log Dumping**: The `logs` output across existing tools is currently populated by raw OS stream chunks (`data.toString()`). These chunks contain partial lines, ANSI color escapes, and progress spinner artifacts (`\r`). When JSON-escaped, this raw stream becomes exceptionally difficult and token-heavy for LLMs to parse, undermining the goal of providing structured data.
17
+ 4. **Fragmented Watch Mode Management**: Watch mode is currently restricted entirely to `ng serve` via the `devserver.*` toolset. There is no generalized mechanism to support long-running watched builds (`ng build --watch`) or watched unit tests (`ng test --watch`), limiting the AI's ability to receive rapid, iterative feedback across different target types.
18
+
19
+ ---
20
+
21
+ ## 2. Proposed Architectural Vision
22
+
23
+ We propose evolving the MCP server toward a unified, target-driven architecture that mirrors Angular’s native `architect` model (**Workspace ➔ Project ➔ Target ➔ Builder**).
24
+
25
+ By pairing declarative target discovery in `list_projects` with a unified `run_target` facade, structured JSON reporters, and a generalized `WatchedTargetManager`, the server will achieve ultimate scalability, massive token savings, and pinpoint diagnostic accuracy across both one-off and long-running execution modes.
26
+
27
+ ```
28
+ ┌──────────────────────────────────────────────────────────────────────────┐
29
+ │ Declarative AI Workflow │
30
+ │ │
31
+ │ 1. list_projects ──> Returns metadata + targets: ['build', 'test'] │
32
+ │ 2. Code Edit ──> AI performs workspace modifications │
33
+ │ 3. run_target ──> { project: 'app', target: 'test', watch: true } │
34
+ └────────────────────────────────────┬─────────────────────────────────────┘
35
+
36
+ ┌──────────────────────────────────────────────────────────────────────────┐
37
+ │ Target Dispatcher (Strategy Pattern) │
38
+ │ Inspects target name & builder from angular.json AST │
39
+ └────────────────────┬───────────────┬───────────────┬─────────────────────┘
40
+ ▼ ▼ ▼
41
+ ┌─────────────────┐┌───────────┐┌──────────────────┐
42
+ │ UnitTestHandler ││E2EHandler ││ BuildHandler │ ... [Custom]
43
+ └────────┬────────┘└─────┬─────┘└────────┬─────────┘
44
+ ▼ ▼ ▼
45
+ ┌─────────────────┐┌───────────┐┌──────────────────┐
46
+ │Vitest JSON Parse││Cypress/PW ││Parse Output Path │ ... [Sanitized]
47
+ └────────┬────────┘└─────┬─────┘└────────┬─────────┘
48
+ ▼ ▼ ▼
49
+ ┌──────────────────────────────────────────────────────────────────────────┐
50
+ │ WatchedTargetManager (If watch: true requested) │
51
+ │ Maintains active background processes & broadcasts rebuild events │
52
+ └──────────────────────────────────────────────────────────────────────────┘
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 3. Detailed Component Design
58
+
59
+ ### 3.1 Declarative Target Discovery (`list_projects`)
60
+ Update `listProjectsOutputSchema` and `loadAndParseWorkspace` in `projects.ts` to extract and expose configured architect targets for each project.
61
+
62
+ ```typescript
63
+ // Proposed addition to listProjectsOutputSchema
64
+ projects: z.array(
65
+ z.object({
66
+ name: z.string(),
67
+ root: z.string(),
68
+ sourceRoot: z.string(),
69
+ projectType: z.enum(['application', 'library']).optional(),
70
+ unitTestFramework: z.enum(['jasmine', 'jest', 'vitest', 'unknown']).optional(),
71
+ // NEW: Array of available architect target names
72
+ targets: z.array(z.string()).describe('Available architect targets (e.g., ["build", "test", "lint", "e2e"])'),
73
+ })
74
+ )
75
+ ```
76
+ * **AI Impact**: Eliminates blind execution. An AI can immediately verify if `e2e` or `lint` is supported before attempting execution.
77
+
78
+ ### 3.2 Stream Sanitization & Structured Reporters
79
+ To solve the unstructured log dumping problem, we establish a dual-horizon reporting architecture:
80
+
81
+ #### Near-Term: Stream Sanitization (`host.ts`)
82
+ Modify `executeNgCommand` in `host.ts` to buffer incoming `stdout`/`stderr` streams, split them cleanly by newlines (`\n`), and strip out ANSI color escapes and carriage returns (`\r`).
83
+ ```typescript
84
+ // Conceptual sanitization buffer
85
+ const cleanLogs: string[] = rawStreamBuffer
86
+ .split('\n')
87
+ .map(line => line.replace(/\x1B\[\d+m|\r/g, '').trim())
88
+ .filter(line => line.length > 0);
89
+ ```
90
+
91
+ #### Long-Term: Structured JSON Reporters (`test.ts`, `e2e.ts`)
92
+ Instead of capturing `stdout` for verification workflows, configure the underlying runners to generate structured JSON summaries. The MCP tool parses the JSON artifact and returns a concise, semantic contract to the LLM:
93
+ ```json
94
+ {
95
+ "status": "failure",
96
+ "summary": {
97
+ "total": 25,
98
+ "passed": 24,
99
+ "failed": 1
100
+ },
101
+ "failures": [
102
+ {
103
+ "spec": "auth.component.spec.ts",
104
+ "test": "should redirect on expired token",
105
+ "errorMessage": "Expected status 302, but received 200."
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+ ### 3.3 The Unified `run_target` Facade
112
+ Deprecate standalone `build`, `test`, and `e2e` tools in favor of a single `run_target` MCP tool declaration.
113
+
114
+ ```typescript
115
+ const runTargetInputSchema = z.object({
116
+ workspace: z.string().optional(),
117
+ project: z.string().optional(),
118
+ target: z.string().describe('The architect target to execute (e.g., "build", "test", "lint", "e2e", "deploy")'),
119
+ configuration: z.string().optional().describe('Target configuration (e.g., "development", "production")'),
120
+ options: z.record(z.unknown()).optional().describe('Optional key-value flags to pass to the builder (e.g., { watch: true, instanceId: "preview" })'),
121
+ });
122
+ ```
123
+
124
+ #### The Strategy Dispatcher (`TargetDispatcher`)
125
+ Under the hood, `run_target` implements the Strategy Pattern to route execution based on the target name and underlying builder definition:
126
+
127
+ 1. **`UnitTestStrategy` (Target: `test`)**:
128
+ * Inspects builder (`@angular/build:unit-test` vs Karma).
129
+ * Automatically injects `--headless true` or `--browsers ChromeHeadless`.
130
+ * Attaches JSON reporter and returns structured semantic test results.
131
+ 2. **`E2EStrategy` (Target: `e2e`)**:
132
+ * Inspects builder (`@cypress/schematic:cypress`, `@playwright/test`).
133
+ * Injects CI/headless execution flags.
134
+ * Parses runner JSON summary and returns structured E2E results.
135
+ 3. **`BuildStrategy` (Target: `build`)**:
136
+ * Defaults to `development`.
137
+ * Parses output logs for `Output location: (.*)` to return exact artifact paths.
138
+ 4. **`LintStrategy` (Target: `lint`)**:
139
+ * Injects `--format json`.
140
+ * Parses ESLint output and returns structured file/line error diagnostics.
141
+ 5. **`GenericStrategy` (Target: Custom / Unknown)**:
142
+ * Executes custom community builders (e.g., `storybook`, `prerender`, `compodoc`).
143
+ * Returns sanitized, line-buffered `stdout`/`stderr`.
144
+
145
+ ---
146
+
147
+ ### 3.4 Generalized Watch Mode Management (`WatchedTargetManager`)
148
+ To support long-running, watched execution across all target types (e.g., `ng serve`, `ng build --watch`, `ng test --watch`), we transition from the legacy `devservers` Map to a generalized `WatchedTargetManager`.
149
+
150
+ We evaluated three architectural approaches for exposing watch mode management to AI agents:
151
+
152
+ #### Approach 1: The `watchMode` Lifecycle Flag (Single-Tool Encapsulation)
153
+ `run_target` remains the exclusive tool, using a `watchMode: 'start' | 'wait' | 'stop' | 'none'` parameter to manage the background lifecycle.
154
+ * **Pros**: Absolute minimal tool registry size (exactly one tool).
155
+ * **Cons**: Overloads the `run_target` schema. The LLM must understand the stateful `start ➔ wait ➔ stop` sequence via parameter flags.
156
+
157
+ #### Approach 2: Dedicated Companion Tools (Separation of Concerns)
158
+ `run_target` is used purely for spawning (`watch: true`), while two generalized companion tools manage active background jobs.
159
+ ```
160
+ ┌──────────────────────────────────────────────────────────────────────────┐
161
+ │ run_target { ..., watch: true } │
162
+ │ Spawns process & registers in context.watchedTargets Map │
163
+ └────────────────────────────────────┬─────────────────────────────────────┘
164
+
165
+ ┌──────────────────────────────────────────────────────────────────────────┐
166
+ │ watched_target.wait { project: 'app', target: 'serve' } │
167
+ │ Blocks until active rebuild completes; returns fresh logs │
168
+ ├──────────────────────────────────────────────────────────────────────────┤
169
+ │ watched_target.stop { project: 'app', target: 'serve' } │
170
+ │ Terminates background process & cleans Map │
171
+ └──────────────────────────────────────────────────────────────────────────┘
172
+ ```
173
+ * **Pros**: Keeps `run_target` input schema extremely clean. Clear separation of concerns between spawning work and inspecting active background jobs.
174
+ * **Cons**: Adds 2 companion tools to the registry (`watched_target.wait`, `watched_target.stop`).
175
+
176
+ #### Approach 3: MCP Server Push / Notifications (The Reactive Agent Horizon)
177
+ Instead of the AI actively polling via a `wait` tool, the server pushes custom MCP notifications (e.g., `notifications/target_watch_event`) containing structured rebuild summaries whenever the OS file watcher triggers a background rebuild.
178
+ * **Pros**: True asynchronous elegance. Completely eliminates active polling (`wait_for_build`) and saves significant tool-calling overhead.
179
+ * **Cons**: Requires the MCP client (Cursor, Claude Desktop, custom agent runner) to support and react to custom server notifications.
180
+
181
+ #### Watch Mode Comparative Summary & Architectural Verdict
182
+
183
+ | Dimension | Approach 1 (`watchMode` Flag) | Approach 2 (Companion Tools) | Approach 3 (MCP Notifications) |
184
+ | :--- | :--- | :--- | :--- |
185
+ | **Tool Registry Size** | **1 Tool** (Lowest) | **3 Tools** (Moderate) | **1 Tool** (Lowest) |
186
+ | **LLM Schema Complexity** | Moderate (Union flags) | **Low** (Clean separation) | **Low** (Clean separation) |
187
+ | **Client Compatibility** | **Universal** (All MCP clients) | **Universal** (All MCP clients) | **Restricted** (Requires notification support) |
188
+ | **Execution Overhead** | Requires polling (`wait`) | Requires polling (`wait`) | **Zero Polling** (Reactive push) |
189
+
190
+ **Architectural Recommendation**:
191
+ For immediate compatibility with existing MCP clients (which primarily rely on request/response tool calling), **Approach 2 (Dedicated Companion Tools)** is the most ergonomic and reliable choice. It keeps tool schemas clean while providing a clear, predictable contract for LLMs.
192
+
193
+ However, the underlying `WatchedTargetManager` should be designed to emit internal event streams. This ensures the server is perfectly positioned to adopt **Approach 3 (MCP Notifications)** as agentic platforms evolve to support reactive notification wakeups.
194
+
195
+ ---
196
+
197
+ ## 3.5 Multi-Instance Watch Mode & Configuration Clobbering
198
+ A critical edge case in AI agent workflows is handling multiple watch mode requests for the exact same target (e.g., calling `run_target({ project: 'app', target: 'serve', watch: true })` multiple times with different ports or flags).
199
+
200
+ To provide an exceptionally ergonomic, self-healing experience, the `WatchedTargetManager` implements a **Smart Hybrid** tracking strategy:
201
+
202
+ ```
203
+ ┌──────────────────────────────────────────────────────────────────────────┐
204
+ │ AI Agent calls run_target { project: 'app', target: 'serve' } │
205
+ └────────────────────────────────────┬─────────────────────────────────────┘
206
+
207
+ ┌──────────────────────────────────────────────────────────────────────────┐
208
+ │ Does active process exist for app:serve:default? │
209
+ └────────────────────┬───────────────────────────────┬─────────────────────┘
210
+ │ YES │ NO
211
+ ▼ ▼
212
+ ┌────────────────────────────────────────┐ ┌───────────────────────────────┐
213
+ │ Are requested options identical? │ │ Spawn fresh process & store │
214
+ └─────────┬────────────────────┬─────────┘ └───────────────────────────────┘
215
+ │ YES │ NO (e.g. new port/config)
216
+ ▼ ▼
217
+ ┌────────────────────┐ ┌───────────────────────────────────────────────────┐
218
+ │ Idempotent No-Op │ │ Graceful Restart (Clobber) │
219
+ │ Return active URL │ │ Auto-kill old process & spawn fresh with new flags│
220
+ └────────────────────┘ └───────────────────────────────────────────────────┘
221
+ ```
222
+
223
+ 1. **Default Idempotency (If options match)**: If the AI calls `run_target` again with the *exact same options*, treat it as an idempotent no-op. Return the active process status/address immediately without incurring a restart penalty.
224
+ 2. **Auto-Clobbering (If options differ)**: If the AI calls `run_target` again with *different options* (e.g., a new port or configuration flag), automatically terminate the old process and spawn a fresh one. This provides seamless self-healing without requiring the LLM to manually call `watched_target.stop`.
225
+ 3. **Explicit Concurrency (If `instanceId` provided)**: If the AI explicitly provides an `instanceId` in the options (e.g., `instanceId: 'preview'` vs `instanceId: 'e2e'`), isolate the process in the internal map (`workspace:project:target:instanceId`), allowing side-by-side execution of the same target.
226
+
227
+ ---
228
+
229
+ ## 4. Summary of Architectural Advantages
230
+
231
+ | Metric | Current Architecture | Proposed Architecture (`run_target` + Companion Tools) |
232
+ | :--- | :--- | :--- |
233
+ | **Tool Registry Size** | Multiple growing tools (`build`, `test`, `e2e`, `devserver.*`) | **Exactly Three Tools** (`run_target`, `watched_target.wait`, `watched_target.stop`) |
234
+ | **LLM Prompt Overhead** | High (multiple verbose tool definitions) | **Minimal** (clean, modular tool definitions) |
235
+ | **Target Discovery** | Opaque (guess and check) | **Declarative** (exposed via `list_projects`) |
236
+ | **Log Parsing Ergonomics**| Poor (JSON-escaped raw stream chunks) | **Excellent** (Structured JSON summaries & clean lines) |
237
+ | **Watch Mode Scope** | Restricted to `ng serve` only | **Universal** (supports watched builds, tests, and custom targets) |
238
+ | **Multi-Instance Watch**| Idempotent no-op only (rigid) | **Smart Hybrid** (idempotent reuse + auto-clobbering + aliasing) |
239
+ | **Extensibility** | Requires new MCP tool code per command | **Instant** (supports all custom builders via generic fallback) |
240
+
241
+ ---
242
+
243
+ ## 5. LLM Ergonomics & Context Window Optimization
244
+
245
+ Exposing numerous granular tools to an LLM introduces significant technical and cognitive overhead. Evolving to the unified `run_target` design directly optimizes how AI assistants consume the MCP server within their context constraints:
246
+
247
+ ### 5.1 System Prompt Token Footprint
248
+ MCP clients inject tool names, descriptions, and JSON parameter schemas directly into the system prompt of every query. A single, highly descriptive tool averages **200 to 500 tokens** of overhead.
249
+ * **Granular Architecture (8+ Tools)**: Creates a permanent tax of **1,600 to 4,000 tokens** on every single user query.
250
+ * **Unified Architecture (3 Tools)**: Slashes this permanent tax down to **600 to 1,500 tokens** (a **60%+ permanent reduction**), maximizing the remaining context window for actual project files and code analysis.
251
+
252
+ ### 5.2 Attention Window & Selection Accuracy
253
+ LLMs utilize attention mechanisms that suffer from **"Lost in the Middle"** retrieval degradation when presented with massive, flat lists of choices (15+ tools). Tool selection accuracy drops significantly in the middle of a long prompt.
254
+ Furthermore, exposing overlapping tools (e.g., separate `build`, `test_unit`, `test_e2e`) leads to semantic blur, causing the LLM to hallucinate parameters or select incorrect tools. Collapsing the entire workspace capabilities into exactly three semantically distinct axes (`list_projects` for discovery, `run_target` for execution, `watched_target` for background lifecycles) ensures **near-100% tool selection accuracy** and eliminates parameter clashing.
255
+
256
+ ---
257
+
258
+ ## 6. Execution Roadmap
259
+
260
+ 1. **Step 1**: Update `projects.ts` to include `targets: z.array(z.string())` in the `list_projects` output schema. [**Completed**]
261
+ 2. **Step 2**: Implement stream line buffering, native VT-stripping, and process deduplication in `host.ts`. [**Completed**]
262
+ 3. **Step 3**: Implement the `run_target` tool declaration and the base Strategy Dispatcher.
263
+ 4. **Step 4**: Migrate `build.ts`, `test.ts`, and `e2e.ts` logic into their respective internal Strategy Handlers (`BuildStrategy`, `UnitTestStrategy`, `E2EStrategy`).
264
+ 5. **Step 5**: Implement `WatchedTargetManager` with the Smart Hybrid tracking strategy (idempotency, clobbering, aliasing).
265
+ 6. **Step 6**: Implement `watched_target.wait` and `watched_target.stop` companion tools.
266
+ 7. **Step 7**: Deprecate legacy standalone tool declarations (`build`, `test`, `e2e`, `devserver.*`) from `mcp-server.ts`.