diff --git a/packages/ng-schematics/src/cli-config/index.ts b/packages/ng-schematics/src/cli-config/index.ts index 01296af62..1eef4e351 100644 --- a/packages/ng-schematics/src/cli-config/index.ts +++ b/packages/ng-schematics/src/cli-config/index.ts @@ -1,6 +1,7 @@ import * as ts from "typescript"; import { DependencyNotFoundException } from "@angular-devkit/core"; import { chain, FileDoesNotExistException, Rule, SchematicContext, Tree } from "@angular-devkit/schematics"; +import * as jsonc from "jsonc-parser"; import { addClassToBody, FormatSettings, NPM_ANGULAR, resolvePackage, TypeScriptAstTransformer, TypeScriptUtils } from "@igniteui/cli-core"; import { AngularTypeScriptFileUpdate } from "@igniteui/angular-templates"; import { createCliConfig } from "../utils/cli-config"; @@ -120,6 +121,10 @@ function importStyles(): Rule { export function addAIConfig(): Rule { return (tree: Tree) => { const mcpFilePath = "/.vscode/mcp.json"; + const angularCliServer = { + command: "npx", + args: ["-y", "@angular/cli", "mcp"] + }; const igniteuiServer = { command: "npx", args: ["-y", "igniteui-cli@next", "mcp"] @@ -130,24 +135,31 @@ export function addAIConfig(): Rule { }; if (tree.exists(mcpFilePath)) { - const content = JSON.parse(tree.read(mcpFilePath)!.toString()); + let text = tree.read(mcpFilePath)!.toString(); + const content = jsonc.parse(text); const servers = content.servers ?? {}; - let modified = false; + const formattingOptions: jsonc.FormattingOptions = { tabSize: 2, insertSpaces: true }; + const newServers: Record = {}; + if (!servers["angular-cli"]) { + newServers["angular-cli"] = angularCliServer; + } if (!servers["igniteui-cli"]) { - servers["igniteui-cli"] = igniteuiServer; - modified = true; + newServers["igniteui-cli"] = igniteuiServer; } if (!servers["igniteui-theming"]) { - servers["igniteui-theming"] = igniteuiThemingServer; - modified = true; + newServers["igniteui-theming"] = igniteuiThemingServer; + } + for (const [key, value] of Object.entries(newServers)) { + const edits = jsonc.modify(text, ["servers", key], value, { formattingOptions }); + text = jsonc.applyEdits(text, edits); } - if (modified) { - content.servers = servers; - tree.overwrite(mcpFilePath, JSON.stringify(content, null, 2)); + if (Object.keys(newServers).length > 0) { + tree.overwrite(mcpFilePath, text); } } else { const mcpConfig = { servers: { + "angular-cli": angularCliServer, "igniteui-cli": igniteuiServer, "igniteui-theming": igniteuiThemingServer } diff --git a/packages/ng-schematics/src/cli-config/index_spec.ts b/packages/ng-schematics/src/cli-config/index_spec.ts index 1ac8a548a..19a959452 100644 --- a/packages/ng-schematics/src/cli-config/index_spec.ts +++ b/packages/ng-schematics/src/cli-config/index_spec.ts @@ -363,6 +363,7 @@ export const appConfig: ApplicationConfig = { it("should not modify .vscode/mcp.json if both servers are already present", async () => { const existing = { servers: { + "angular-cli": { command: "npx", args: ["-y", "@angular/cli", "mcp"] }, "igniteui-cli": { command: "npx", args: ["-y", "igniteui-cli@next", "mcp"] }, "igniteui-theming": { command: "npx", args: ["-y", "igniteui-theming", "igniteui-theming-mcp"] } } diff --git a/packages/ng-schematics/src/collection.json b/packages/ng-schematics/src/collection.json index ac4f616c9..2fa9699ef 100644 --- a/packages/ng-schematics/src/collection.json +++ b/packages/ng-schematics/src/collection.json @@ -33,6 +33,10 @@ "description": "Installs the needed dependencies onto the host application.", "factory": "./cli-config/index" }, + "ai-config": { + "description": "Adds AI/MCP server configuration to .vscode/mcp.json.", + "factory": "./cli-config/index#addAIConfig" + }, "upgrade-packages": { "description": "Upgrades to the licensed Ignite UI for Angular packages", "factory": "./upgrade-packages/index",