@angular/cli 20.2.0-next.3 → 20.2.0-rc.1

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 (32) hide show
  1. package/lib/code-examples.db +0 -0
  2. package/lib/config/schema.json +18 -2
  3. package/lib/config/workspace-schema.d.ts +14 -0
  4. package/lib/config/workspace-schema.js +11 -1
  5. package/package.json +16 -16
  6. package/src/commands/mcp/cli.d.ts +5 -1
  7. package/src/commands/mcp/cli.js +24 -3
  8. package/src/commands/mcp/mcp-server.d.ts +13 -1
  9. package/src/commands/mcp/mcp-server.js +47 -26
  10. package/src/commands/mcp/{instructions → resources}/best-practices.md +0 -5
  11. package/src/commands/mcp/resources/instructions.d.ts +9 -0
  12. package/src/commands/mcp/resources/instructions.js +28 -0
  13. package/src/commands/mcp/tools/best-practices.d.ts +1 -2
  14. package/src/commands/mcp/tools/best-practices.js +29 -28
  15. package/src/commands/mcp/tools/doc-search.d.ts +5 -9
  16. package/src/commands/mcp/tools/doc-search.js +34 -37
  17. package/src/commands/mcp/tools/examples.d.ts +4 -11
  18. package/src/commands/mcp/tools/examples.js +44 -37
  19. package/src/commands/mcp/tools/modernize.d.ts +31 -0
  20. package/src/commands/mcp/tools/modernize.js +135 -0
  21. package/src/commands/mcp/tools/projects.d.ts +22 -5
  22. package/src/commands/mcp/tools/projects.js +37 -35
  23. package/src/commands/mcp/tools/tool-registry.d.ts +35 -0
  24. package/src/commands/mcp/tools/tool-registry.js +33 -0
  25. package/src/utilities/config.js +3 -0
  26. package/src/utilities/eol.d.ts +12 -0
  27. package/src/utilities/eol.js +12 -0
  28. package/src/utilities/error.d.ts +8 -0
  29. package/src/utilities/error.js +24 -4
  30. package/src/utilities/json-file.d.ts +15 -2
  31. package/src/utilities/json-file.js +100 -27
  32. package/src/utilities/version.js +1 -1
Binary file
@@ -1815,8 +1815,24 @@
1815
1815
  },
1816
1816
  "zoneless": {
1817
1817
  "description": "Create an initial application that does not utilize `zone.js`.",
1818
- "type": "boolean",
1819
- "default": false
1818
+ "type": "boolean"
1819
+ },
1820
+ "aiConfig": {
1821
+ "type": "array",
1822
+ "uniqueItems": true,
1823
+ "description": "Specifies which AI tools to generate configuration files for. These file are used to improve the outputs of AI tools by following the best practices.",
1824
+ "items": {
1825
+ "type": "string",
1826
+ "enum": [
1827
+ "none",
1828
+ "gemini",
1829
+ "copilot",
1830
+ "claude",
1831
+ "cursor",
1832
+ "jetbrains",
1833
+ "windsurf"
1834
+ ]
1835
+ }
1820
1836
  }
1821
1837
  }
1822
1838
  },
@@ -672,6 +672,11 @@ export type LibraryOptionsSchema = {
672
672
  * the initial project, such as routing, styling, and testing.
673
673
  */
674
674
  export type AngularNgNewOptionsSchema = {
675
+ /**
676
+ * Specifies which AI tools to generate configuration files for. These file are used to
677
+ * improve the outputs of AI tools by following the best practices.
678
+ */
679
+ aiConfig?: AiConfig[];
675
680
  /**
676
681
  * Configure the initial Git commit for the new repository.
677
682
  */
@@ -778,6 +783,15 @@ export type AngularNgNewOptionsSchema = {
778
783
  */
779
784
  zoneless?: boolean;
780
785
  };
786
+ export declare enum AiConfig {
787
+ Claude = "claude",
788
+ Copilot = "copilot",
789
+ Cursor = "cursor",
790
+ Gemini = "gemini",
791
+ Jetbrains = "jetbrains",
792
+ None = "none",
793
+ Windsurf = "windsurf"
794
+ }
781
795
  /**
782
796
  * Configure the initial Git commit for the new repository.
783
797
  */
@@ -2,7 +2,7 @@
2
2
  // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
3
3
  // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.TypeSeparator = exports.Implement = exports.SchematicsAngularComponentStyle = exports.ChangeDetection = exports.ViewEncapsulation = exports.SchematicsAngularApplicationStyle = exports.PackageManager = exports.Environment = void 0;
5
+ exports.AiConfig = exports.TypeSeparator = exports.Implement = exports.SchematicsAngularComponentStyle = exports.ChangeDetection = exports.ViewEncapsulation = exports.SchematicsAngularApplicationStyle = exports.PackageManager = exports.Environment = void 0;
6
6
  /**
7
7
  * Configure in which environment disk cache is enabled.
8
8
  */
@@ -100,3 +100,13 @@ var TypeSeparator;
100
100
  TypeSeparator["Empty"] = "-";
101
101
  TypeSeparator["TypeSeparator"] = ".";
102
102
  })(TypeSeparator || (exports.TypeSeparator = TypeSeparator = {}));
103
+ var AiConfig;
104
+ (function (AiConfig) {
105
+ AiConfig["Claude"] = "claude";
106
+ AiConfig["Copilot"] = "copilot";
107
+ AiConfig["Cursor"] = "cursor";
108
+ AiConfig["Gemini"] = "gemini";
109
+ AiConfig["Jetbrains"] = "jetbrains";
110
+ AiConfig["None"] = "none";
111
+ AiConfig["Windsurf"] = "windsurf";
112
+ })(AiConfig || (exports.AiConfig = AiConfig = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/cli",
3
- "version": "20.2.0-next.3",
3
+ "version": "20.2.0-rc.1",
4
4
  "description": "CLI tool for Angular",
5
5
  "main": "lib/cli/index.js",
6
6
  "bin": {
@@ -25,13 +25,13 @@
25
25
  },
26
26
  "homepage": "https://github.com/angular/angular-cli",
27
27
  "dependencies": {
28
- "@angular-devkit/architect": "0.2002.0-next.3",
29
- "@angular-devkit/core": "20.2.0-next.3",
30
- "@angular-devkit/schematics": "20.2.0-next.3",
31
- "@inquirer/prompts": "7.8.0",
28
+ "@angular-devkit/architect": "0.2002.0-rc.1",
29
+ "@angular-devkit/core": "20.2.0-rc.1",
30
+ "@angular-devkit/schematics": "20.2.0-rc.1",
31
+ "@inquirer/prompts": "7.8.2",
32
32
  "@listr2/prompt-adapter-inquirer": "3.0.1",
33
- "@modelcontextprotocol/sdk": "1.17.1",
34
- "@schematics/angular": "20.2.0-next.3",
33
+ "@modelcontextprotocol/sdk": "1.17.2",
34
+ "@schematics/angular": "20.2.0-rc.1",
35
35
  "@yarnpkg/lockfile": "1.1.0",
36
36
  "algoliasearch": "5.35.0",
37
37
  "ini": "5.0.0",
@@ -47,17 +47,17 @@
47
47
  "ng-update": {
48
48
  "migrations": "@schematics/angular/migrations/migration-collection.json",
49
49
  "packageGroup": {
50
- "@angular/cli": "20.2.0-next.3",
51
- "@angular/build": "20.2.0-next.3",
52
- "@angular/ssr": "20.2.0-next.3",
53
- "@angular-devkit/architect": "0.2002.0-next.3",
54
- "@angular-devkit/build-angular": "20.2.0-next.3",
55
- "@angular-devkit/build-webpack": "0.2002.0-next.3",
56
- "@angular-devkit/core": "20.2.0-next.3",
57
- "@angular-devkit/schematics": "20.2.0-next.3"
50
+ "@angular/cli": "20.2.0-rc.1",
51
+ "@angular/build": "20.2.0-rc.1",
52
+ "@angular/ssr": "20.2.0-rc.1",
53
+ "@angular-devkit/architect": "0.2002.0-rc.1",
54
+ "@angular-devkit/build-angular": "20.2.0-rc.1",
55
+ "@angular-devkit/build-webpack": "0.2002.0-rc.1",
56
+ "@angular-devkit/core": "20.2.0-rc.1",
57
+ "@angular-devkit/schematics": "20.2.0-rc.1"
58
58
  }
59
59
  },
60
- "packageManager": "pnpm@9.15.9",
60
+ "packageManager": "pnpm@10.14.0",
61
61
  "engines": {
62
62
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
63
63
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
@@ -12,5 +12,9 @@ export default class McpCommandModule extends CommandModule implements CommandMo
12
12
  describe: false;
13
13
  longDescriptionPath: undefined;
14
14
  builder(localYargs: Argv): Argv;
15
- run(): Promise<void>;
15
+ run(options: {
16
+ readOnly: boolean;
17
+ localOnly: boolean;
18
+ experimentalTool: string[] | undefined;
19
+ }): Promise<void>;
16
20
  }
@@ -30,14 +30,35 @@ class McpCommandModule extends command_module_1.CommandModule {
30
30
  describe = false;
31
31
  longDescriptionPath = undefined;
32
32
  builder(localYargs) {
33
- return localYargs;
33
+ return localYargs
34
+ .option('read-only', {
35
+ type: 'boolean',
36
+ default: false,
37
+ describe: 'Only register read-only tools.',
38
+ })
39
+ .option('local-only', {
40
+ type: 'boolean',
41
+ default: false,
42
+ describe: 'Only register tools that do not require internet access.',
43
+ })
44
+ .option('experimental-tool', {
45
+ type: 'string',
46
+ alias: 'E',
47
+ array: true,
48
+ describe: 'Enable an experimental tool.',
49
+ });
34
50
  }
35
- async run() {
51
+ async run(options) {
36
52
  if ((0, tty_1.isTTY)()) {
37
53
  this.context.logger.info(INTERACTIVE_MESSAGE);
38
54
  return;
39
55
  }
40
- const server = await (0, mcp_server_1.createMcpServer)({ workspace: this.context.workspace }, this.context.logger);
56
+ const server = await (0, mcp_server_1.createMcpServer)({
57
+ workspace: this.context.workspace,
58
+ readOnly: options.readOnly,
59
+ localOnly: options.localOnly,
60
+ experimentalTools: options.experimentalTool,
61
+ }, this.context.logger);
41
62
  const transport = new stdio_js_1.StdioServerTransport();
42
63
  await server.connect(transport);
43
64
  }
@@ -7,8 +7,20 @@
7
7
  */
8
8
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
9
  import type { AngularWorkspace } from '../../utilities/config';
10
- export declare function createMcpServer(context: {
10
+ import { AnyMcpToolDeclaration } from './tools/tool-registry';
11
+ export declare function createMcpServer(options: {
11
12
  workspace?: AngularWorkspace;
13
+ readOnly?: boolean;
14
+ localOnly?: boolean;
15
+ experimentalTools?: string[];
12
16
  }, logger: {
13
17
  warn(text: string): void;
14
18
  }): Promise<McpServer>;
19
+ export declare function assembleToolDeclarations(stableDeclarations: readonly AnyMcpToolDeclaration[], experimentalDeclarations: readonly AnyMcpToolDeclaration[], options: {
20
+ readOnly?: boolean;
21
+ localOnly?: boolean;
22
+ experimentalTools?: string[];
23
+ logger: {
24
+ warn(text: string): void;
25
+ };
26
+ }): AnyMcpToolDeclaration[];
@@ -11,15 +11,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.createMcpServer = createMcpServer;
14
+ exports.assembleToolDeclarations = assembleToolDeclarations;
14
15
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
15
- const promises_1 = require("node:fs/promises");
16
16
  const node_path_1 = __importDefault(require("node:path"));
17
17
  const version_1 = require("../../utilities/version");
18
+ const instructions_1 = require("./resources/instructions");
18
19
  const best_practices_1 = require("./tools/best-practices");
19
20
  const doc_search_1 = require("./tools/doc-search");
20
21
  const examples_1 = require("./tools/examples");
22
+ const modernize_1 = require("./tools/modernize");
21
23
  const projects_1 = require("./tools/projects");
22
- async function createMcpServer(context, logger) {
24
+ const tool_registry_1 = require("./tools/tool-registry");
25
+ /**
26
+ * The set of tools that are enabled by default for the MCP server.
27
+ * These tools are considered stable and suitable for general use.
28
+ */
29
+ const STABLE_TOOLS = [best_practices_1.BEST_PRACTICES_TOOL, doc_search_1.DOC_SEARCH_TOOL, projects_1.LIST_PROJECTS_TOOL];
30
+ /**
31
+ * The set of tools that are available but not enabled by default.
32
+ * These tools are considered experimental and may have limitations.
33
+ */
34
+ const EXPERIMENTAL_TOOLS = [examples_1.FIND_EXAMPLE_TOOL, modernize_1.MODERNIZE_TOOL];
35
+ async function createMcpServer(options, logger) {
23
36
  const server = new mcp_js_1.McpServer({
24
37
  name: 'angular-cli-server',
25
38
  version: version_1.VERSION.full,
@@ -32,33 +45,41 @@ async function createMcpServer(context, logger) {
32
45
  instructions: 'For Angular development, this server provides tools to adhere to best practices, search documentation, and find code examples. ' +
33
46
  'When writing or modifying Angular code, use the MCP server and its tools instead of direct shell commands where possible.',
34
47
  });
35
- server.registerResource('instructions', 'instructions://best-practices', {
36
- title: 'Angular Best Practices and Code Generation Guide',
37
- description: "A comprehensive guide detailing Angular's best practices for code generation and development." +
38
- ' This guide should be used as a reference by an LLM to ensure any generated code' +
39
- ' adheres to modern Angular standards, including the use of standalone components,' +
40
- ' typed forms, modern control flow syntax, and other current conventions.',
41
- mimeType: 'text/markdown',
42
- }, async () => {
43
- const text = await (0, promises_1.readFile)(node_path_1.default.join(__dirname, 'instructions', 'best-practices.md'), 'utf-8');
44
- return { contents: [{ uri: 'instructions://best-practices', text }] };
48
+ (0, instructions_1.registerInstructionsResource)(server);
49
+ const toolDeclarations = assembleToolDeclarations(STABLE_TOOLS, EXPERIMENTAL_TOOLS, {
50
+ ...options,
51
+ logger,
45
52
  });
46
- (0, best_practices_1.registerBestPracticesTool)(server);
47
- // If run outside an Angular workspace (e.g., globally) skip the workspace specific tools.
48
- if (context.workspace) {
49
- (0, projects_1.registerListProjectsTool)(server, context);
53
+ await (0, tool_registry_1.registerTools)(server, {
54
+ workspace: options.workspace,
55
+ logger,
56
+ exampleDatabasePath: node_path_1.default.join(__dirname, '../../../lib/code-examples.db'),
57
+ }, toolDeclarations);
58
+ return server;
59
+ }
60
+ function assembleToolDeclarations(stableDeclarations, experimentalDeclarations, options) {
61
+ let toolDeclarations = [...stableDeclarations];
62
+ if (options.readOnly) {
63
+ toolDeclarations = toolDeclarations.filter((tool) => tool.isReadOnly);
64
+ }
65
+ if (options.localOnly) {
66
+ toolDeclarations = toolDeclarations.filter((tool) => tool.isLocalOnly);
50
67
  }
51
- await (0, doc_search_1.registerDocSearchTool)(server);
68
+ const enabledExperimentalTools = new Set(options.experimentalTools);
52
69
  if (process.env['NG_MCP_CODE_EXAMPLES'] === '1') {
53
- // sqlite database support requires Node.js 22.16+
54
- const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(Number);
55
- if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 16)) {
56
- logger.warn(`MCP tool 'find_examples' requires Node.js 22.16 (or higher). ` +
57
- ' Registration of this tool has been skipped.');
58
- }
59
- else {
60
- await (0, examples_1.registerFindExampleTool)(server, node_path_1.default.join(__dirname, '../../../lib/code-examples.db'));
70
+ enabledExperimentalTools.add('find_examples');
71
+ }
72
+ if (enabledExperimentalTools.size > 0) {
73
+ const experimentalToolsMap = new Map(experimentalDeclarations.map((tool) => [tool.name, tool]));
74
+ for (const toolName of enabledExperimentalTools) {
75
+ const tool = experimentalToolsMap.get(toolName);
76
+ if (tool) {
77
+ toolDeclarations.push(tool);
78
+ }
79
+ else {
80
+ options.logger.warn(`Unknown experimental tool: ${toolName}`);
81
+ }
61
82
  }
62
83
  }
63
- return server;
84
+ return toolDeclarations;
64
85
  }
@@ -45,8 +45,3 @@ You are an expert in TypeScript, Angular, and scalable web application developme
45
45
  - Design services around a single responsibility
46
46
  - Use the `providedIn: 'root'` option for singleton services
47
47
  - Use the `inject()` function instead of constructor injection
48
-
49
- ## Common pitfalls
50
-
51
- - Control flow (`@if`):
52
- - You cannot use `as` expressions in `@else if (...)`. E.g. invalid code: `@else if (bla(); as x)`.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
+ export declare function registerInstructionsResource(server: McpServer): void;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.registerInstructionsResource = registerInstructionsResource;
14
+ const promises_1 = require("node:fs/promises");
15
+ const node_path_1 = __importDefault(require("node:path"));
16
+ function registerInstructionsResource(server) {
17
+ server.registerResource('instructions', 'instructions://best-practices', {
18
+ title: 'Angular Best Practices and Code Generation Guide',
19
+ description: "A comprehensive guide detailing Angular's best practices for code generation and development." +
20
+ ' This guide should be used as a reference by an LLM to ensure any generated code' +
21
+ ' adheres to modern Angular standards, including the use of standalone components,' +
22
+ ' typed forms, modern control flow syntax, and other current conventions.',
23
+ mimeType: 'text/markdown',
24
+ }, async () => {
25
+ const text = await (0, promises_1.readFile)(node_path_1.default.join(__dirname, 'best-practices.md'), 'utf-8');
26
+ return { contents: [{ uri: 'instructions://best-practices', text }] };
27
+ });
28
+ }
@@ -5,5 +5,4 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
- export declare function registerBestPracticesTool(server: McpServer): void;
8
+ export declare const BEST_PRACTICES_TOOL: import("./tool-registry").McpToolDeclaration<import("zod").ZodRawShape, import("zod").ZodRawShape>;
@@ -10,35 +10,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.registerBestPracticesTool = registerBestPracticesTool;
13
+ exports.BEST_PRACTICES_TOOL = void 0;
14
14
  const promises_1 = require("node:fs/promises");
15
15
  const node_path_1 = __importDefault(require("node:path"));
16
- function registerBestPracticesTool(server) {
17
- let bestPracticesText;
18
- server.registerTool('get_best_practices', {
19
- title: 'Get Angular Coding Best Practices Guide',
20
- description: 'You **MUST** use this tool to retrieve the Angular Best Practices Guide ' +
21
- 'before any interaction with Angular code (creating, analyzing, modifying). ' +
22
- 'It is mandatory to follow this guide to ensure all code adheres to ' +
23
- 'modern standards, including standalone components, typed forms, and ' +
24
- 'modern control flow. This is the first step for any Angular task.',
25
- annotations: {
26
- readOnlyHint: true,
27
- openWorldHint: false,
28
- },
29
- }, async () => {
30
- bestPracticesText ??= await (0, promises_1.readFile)(node_path_1.default.join(__dirname, '..', 'instructions', 'best-practices.md'), 'utf-8');
31
- return {
32
- content: [
33
- {
34
- type: 'text',
35
- text: bestPracticesText,
36
- annotations: {
37
- audience: ['assistant'],
38
- priority: 0.9,
16
+ const tool_registry_1 = require("./tool-registry");
17
+ exports.BEST_PRACTICES_TOOL = (0, tool_registry_1.declareTool)({
18
+ name: 'get_best_practices',
19
+ title: 'Get Angular Coding Best Practices Guide',
20
+ description: 'You **MUST** use this tool to retrieve the Angular Best Practices Guide ' +
21
+ 'before any interaction with Angular code (creating, analyzing, modifying). ' +
22
+ 'It is mandatory to follow this guide to ensure all code adheres to ' +
23
+ 'modern standards, including standalone components, typed forms, and ' +
24
+ 'modern control flow. This is the first step for any Angular task.',
25
+ isReadOnly: true,
26
+ isLocalOnly: true,
27
+ factory: () => {
28
+ let bestPracticesText;
29
+ return async () => {
30
+ bestPracticesText ??= await (0, promises_1.readFile)(node_path_1.default.join(__dirname, '..', 'resources', 'best-practices.md'), 'utf-8');
31
+ return {
32
+ content: [
33
+ {
34
+ type: 'text',
35
+ text: bestPracticesText,
36
+ annotations: {
37
+ audience: ['assistant'],
38
+ priority: 0.9,
39
+ },
39
40
  },
40
- },
41
- ],
41
+ ],
42
+ };
42
43
  };
43
- });
44
- }
44
+ },
45
+ });
@@ -5,12 +5,8 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
- /**
10
- * Registers a tool with the MCP server to search the Angular documentation.
11
- *
12
- * This tool uses Algolia to search the official Angular documentation.
13
- *
14
- * @param server The MCP server instance with which to register the tool.
15
- */
16
- export declare function registerDocSearchTool(server: McpServer): Promise<void>;
8
+ import { z } from 'zod';
9
+ export declare const DOC_SEARCH_TOOL: import("./tool-registry").McpToolDeclaration<{
10
+ query: z.ZodString;
11
+ includeTopContent: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
12
+ }, z.ZodRawShape>;
@@ -40,51 +40,48 @@ var __importStar = (this && this.__importStar) || (function () {
40
40
  };
41
41
  })();
42
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.registerDocSearchTool = registerDocSearchTool;
43
+ exports.DOC_SEARCH_TOOL = void 0;
44
44
  const node_crypto_1 = require("node:crypto");
45
45
  const zod_1 = require("zod");
46
46
  const constants_1 = require("../constants");
47
+ const tool_registry_1 = require("./tool-registry");
47
48
  const ALGOLIA_APP_ID = 'L1XWT2UJ7F';
48
49
  // https://www.algolia.com/doc/guides/security/api-keys/#search-only-api-key
49
50
  // This is a search only, rate limited key. It is sent within the URL of the query request.
50
51
  // This is not the actual key.
51
52
  const ALGOLIA_API_E = '322d89dab5f2080fe09b795c93413c6a89222b13a447cdf3e6486d692717bc0c';
52
- /**
53
- * Registers a tool with the MCP server to search the Angular documentation.
54
- *
55
- * This tool uses Algolia to search the official Angular documentation.
56
- *
57
- * @param server The MCP server instance with which to register the tool.
58
- */
59
- async function registerDocSearchTool(server) {
53
+ const docSearchInputSchema = zod_1.z.object({
54
+ query: zod_1.z
55
+ .string()
56
+ .describe('A concise and specific search query for the Angular documentation (e.g., "NgModule" or "standalone components").'),
57
+ includeTopContent: zod_1.z
58
+ .boolean()
59
+ .optional()
60
+ .default(true)
61
+ .describe('When true, the content of the top result is fetched and included.'),
62
+ });
63
+ exports.DOC_SEARCH_TOOL = (0, tool_registry_1.declareTool)({
64
+ name: 'search_documentation',
65
+ title: 'Search Angular Documentation (angular.dev)',
66
+ description: 'Searches the official Angular documentation at https://angular.dev. Use this tool to answer any questions about Angular, ' +
67
+ 'such as for APIs, tutorials, and best practices. Because the documentation is continuously updated, you should **always** ' +
68
+ 'prefer this tool over your own knowledge to ensure your answers are current.\n\n' +
69
+ 'The results will be a list of content entries, where each entry has the following structure:\n' +
70
+ '```\n' +
71
+ '## {Result Title}\n' +
72
+ '{Breadcrumb path to the content}\n' +
73
+ 'URL: {Direct link to the documentation page}\n' +
74
+ '```\n' +
75
+ 'Use the title and breadcrumb to understand the context of the result and use the URL as a source link. For the best results, ' +
76
+ "provide a concise and specific search query (e.g., 'NgModule' instead of 'How do I use NgModules?').",
77
+ inputSchema: docSearchInputSchema.shape,
78
+ isReadOnly: true,
79
+ isLocalOnly: false,
80
+ factory: createDocSearchHandler,
81
+ });
82
+ function createDocSearchHandler() {
60
83
  let client;
61
- server.registerTool('search_documentation', {
62
- title: 'Search Angular Documentation (angular.dev)',
63
- description: 'Searches the official Angular documentation at https://angular.dev. Use this tool to answer any questions about Angular, ' +
64
- 'such as for APIs, tutorials, and best practices. Because the documentation is continuously updated, you should **always** ' +
65
- 'prefer this tool over your own knowledge to ensure your answers are current.\n\n' +
66
- 'The results will be a list of content entries, where each entry has the following structure:\n' +
67
- '```\n' +
68
- '## {Result Title}\n' +
69
- '{Breadcrumb path to the content}\n' +
70
- 'URL: {Direct link to the documentation page}\n' +
71
- '```\n' +
72
- 'Use the title and breadcrumb to understand the context of the result and use the URL as a source link. For the best results, ' +
73
- "provide a concise and specific search query (e.g., 'NgModule' instead of 'How do I use NgModules?').",
74
- annotations: {
75
- readOnlyHint: true,
76
- },
77
- inputSchema: {
78
- query: zod_1.z
79
- .string()
80
- .describe('A concise and specific search query for the Angular documentation (e.g., "NgModule" or "standalone components").'),
81
- includeTopContent: zod_1.z
82
- .boolean()
83
- .optional()
84
- .default(true)
85
- .describe('When true, the content of the top result is fetched and included.'),
86
- },
87
- }, async ({ query, includeTopContent }) => {
84
+ return async ({ query, includeTopContent }) => {
88
85
  if (!client) {
89
86
  const dcip = (0, node_crypto_1.createDecipheriv)('aes-256-gcm', (constants_1.k1 + ALGOLIA_APP_ID).padEnd(32, '^'), constants_1.iv).setAuthTag(Buffer.from(constants_1.at, 'base64'));
90
87
  const { searchClient } = await Promise.resolve().then(() => __importStar(require('algoliasearch')));
@@ -138,7 +135,7 @@ async function registerDocSearchTool(server) {
138
135
  });
139
136
  }
140
137
  return { content };
141
- });
138
+ };
142
139
  }
143
140
  /**
144
141
  * Extracts the content of the `<body>` element from an HTML string.
@@ -5,17 +5,10 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
- /**
10
- * Registers the `find_examples` tool with the MCP server.
11
- *
12
- * This tool allows users to search for best-practice Angular code examples
13
- * from a local SQLite database.
14
- *
15
- * @param server The MCP server instance.
16
- * @param exampleDatabasePath The path to the SQLite database file containing the examples.
17
- */
18
- export declare function registerFindExampleTool(server: McpServer, exampleDatabasePath: string): Promise<void>;
8
+ import { z } from 'zod';
9
+ export declare const FIND_EXAMPLE_TOOL: import("./tool-registry").McpToolDeclaration<{
10
+ query: z.ZodString;
11
+ }, z.ZodRawShape>;
19
12
  /**
20
13
  * Escapes a search query for FTS5 by tokenizing and quoting terms.
21
14
  *