diff --git a/packages/angular/cli/src/commands/mcp/cli.ts b/packages/angular/cli/src/commands/mcp/cli.ts index 0076752ac6f3..091a9064ca7f 100644 --- a/packages/angular/cli/src/commands/mcp/cli.ts +++ b/packages/angular/cli/src/commands/mcp/cli.ts @@ -13,7 +13,7 @@ import { type CommandModuleImplementation, } from '../../command-builder/command-module'; import { isTTY } from '../../utilities/tty'; -import { EXPERIMENTAL_TOOLS, createMcpServer } from './mcp-server'; +import { EXPERIMENTAL_TOOLS, EXPERIMENTAL_TOOL_GROUPS, createMcpServer } from './mcp-server'; const INTERACTIVE_MESSAGE = ` To start using the Angular CLI MCP Server, add this configuration to your host: @@ -54,7 +54,10 @@ export default class McpCommandModule extends CommandModule implements CommandMo alias: 'E', array: true, describe: 'Enable an experimental tool.', - choices: EXPERIMENTAL_TOOLS.map(({ name }) => name), + choices: [ + ...EXPERIMENTAL_TOOLS.map(({ name }) => name), + ...Object.keys(EXPERIMENTAL_TOOL_GROUPS), + ], hidden: true, }); } diff --git a/packages/angular/cli/src/commands/mcp/dev-server.ts b/packages/angular/cli/src/commands/mcp/devserver.ts similarity index 86% rename from packages/angular/cli/src/commands/mcp/dev-server.ts rename to packages/angular/cli/src/commands/mcp/devserver.ts index e6da33aa7bd3..cf8378294edd 100644 --- a/packages/angular/cli/src/commands/mcp/dev-server.ts +++ b/packages/angular/cli/src/commands/mcp/devserver.ts @@ -30,7 +30,7 @@ export type BuildStatus = 'success' | 'failure' | 'unknown'; /** * An Angular development server managed by the MCP server. */ -export interface DevServer { +export interface Devserver { /** * Launches the dev server and returns immediately. * @@ -64,19 +64,19 @@ export interface DevServer { port: number; } -export function devServerKey(project?: string) { +export function devserverKey(project?: string) { return project ?? ''; } /** * A local Angular development server managed by the MCP server. */ -export class LocalDevServer implements DevServer { +export class LocalDevserver implements Devserver { readonly host: Host; readonly port: number; readonly project?: string; - private devServerProcess: ChildProcess | null = null; + private devserverProcess: ChildProcess | null = null; private serverLogs: string[] = []; private buildInProgress = false; private latestBuildLogStartIndex?: number = undefined; @@ -89,7 +89,7 @@ export class LocalDevServer implements DevServer { } start() { - if (this.devServerProcess) { + if (this.devserverProcess) { throw Error('Dev server already started.'); } @@ -100,14 +100,14 @@ export class LocalDevServer implements DevServer { args.push(`--port=${this.port}`); - this.devServerProcess = this.host.spawn('ng', args, { stdio: 'pipe' }); - this.devServerProcess.stdout?.on('data', (data) => { + this.devserverProcess = this.host.spawn('ng', args, { stdio: 'pipe' }); + this.devserverProcess.stdout?.on('data', (data) => { this.addLog(data.toString()); }); - this.devServerProcess.stderr?.on('data', (data) => { + this.devserverProcess.stderr?.on('data', (data) => { this.addLog(data.toString()); }); - this.devServerProcess.stderr?.on('close', () => { + this.devserverProcess.stderr?.on('close', () => { this.stop(); }); this.buildInProgress = true; @@ -127,8 +127,8 @@ export class LocalDevServer implements DevServer { } stop() { - this.devServerProcess?.kill(); - this.devServerProcess = null; + this.devserverProcess?.kill(); + this.devserverProcess = null; } getServerLogs(): string[] { diff --git a/packages/angular/cli/src/commands/mcp/mcp-server.ts b/packages/angular/cli/src/commands/mcp/mcp-server.ts index dfcd162a44f7..50ba2d39ec44 100644 --- a/packages/angular/cli/src/commands/mcp/mcp-server.ts +++ b/packages/angular/cli/src/commands/mcp/mcp-server.ts @@ -10,14 +10,14 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { join } from 'node:path'; import type { AngularWorkspace } from '../../utilities/config'; import { VERSION } from '../../utilities/version'; -import type { DevServer } from './dev-server'; +import type { Devserver } from './devserver'; import { registerInstructionsResource } from './resources/instructions'; import { AI_TUTOR_TOOL } from './tools/ai-tutor'; import { BEST_PRACTICES_TOOL } from './tools/best-practices'; import { BUILD_TOOL } from './tools/build'; -import { START_DEVSERVER_TOOL } from './tools/devserver/start-devserver'; -import { STOP_DEVSERVER_TOOL } from './tools/devserver/stop-devserver'; -import { WAIT_FOR_DEVSERVER_BUILD_TOOL } from './tools/devserver/wait-for-devserver-build'; +import { DEVSERVER_START_TOOL } from './tools/devserver/devserver-start'; +import { DEVSERVER_STOP_TOOL } from './tools/devserver/devserver-stop'; +import { DEVSERVER_WAIT_FOR_BUILD_TOOL } from './tools/devserver/devserver-wait-for-build'; import { DOC_SEARCH_TOOL } from './tools/doc-search'; import { FIND_EXAMPLE_TOOL } from './tools/examples'; import { MODERNIZE_TOOL } from './tools/modernize'; @@ -28,7 +28,16 @@ import { type AnyMcpToolDeclaration, registerTools } from './tools/tool-registry /** * Tools to manage devservers. Should be bundled together, then added to experimental or stable as a group. */ -const SERVE_TOOLS = [START_DEVSERVER_TOOL, STOP_DEVSERVER_TOOL, WAIT_FOR_DEVSERVER_BUILD_TOOL]; +const DEVSERVER_TOOLS = [DEVSERVER_START_TOOL, DEVSERVER_STOP_TOOL, DEVSERVER_WAIT_FOR_BUILD_TOOL]; + +/** + * Experimental tools that are grouped together under a single name. + * + * Used for enabling them as a group. + */ +export const EXPERIMENTAL_TOOL_GROUPS = { + 'devserver': DEVSERVER_TOOLS, +}; /** * The set of tools that are enabled by default for the MCP server. @@ -47,7 +56,7 @@ const STABLE_TOOLS = [ * The set of tools that are available but not enabled by default. * These tools are considered experimental and may have limitations. */ -export const EXPERIMENTAL_TOOLS = [BUILD_TOOL, MODERNIZE_TOOL, ...SERVE_TOOLS] as const; +export const EXPERIMENTAL_TOOLS = [BUILD_TOOL, MODERNIZE_TOOL, ...DEVSERVER_TOOLS] as const; export async function createMcpServer( options: { @@ -114,7 +123,7 @@ equivalent actions. workspace: options.workspace, logger, exampleDatabasePath: join(__dirname, '../../../lib/code-examples.db'), - devServers: new Map(), + devservers: new Map(), }, toolDeclarations, ); @@ -146,6 +155,13 @@ export function assembleToolDeclarations( if (process.env['NG_MCP_CODE_EXAMPLES'] === '1') { enabledExperimentalTools.add('find_examples'); } + for (const [toolGroupName, toolGroup] of Object.entries(EXPERIMENTAL_TOOL_GROUPS)) { + if (enabledExperimentalTools.delete(toolGroupName)) { + for (const tool of toolGroup) { + enabledExperimentalTools.add(tool.name); + } + } + } if (enabledExperimentalTools.size > 0) { const experimentalToolsMap = new Map(experimentalDeclarations.map((tool) => [tool.name, tool])); diff --git a/packages/angular/cli/src/commands/mcp/tools/build.ts b/packages/angular/cli/src/commands/mcp/tools/build.ts index f2618f8e6309..1daea2c83677 100644 --- a/packages/angular/cli/src/commands/mcp/tools/build.ts +++ b/packages/angular/cli/src/commands/mcp/tools/build.ts @@ -97,7 +97,7 @@ Perform a one-off, non-watched build using "ng build". Use this tool whenever th * This tool runs "ng build" so it expects to run within an Angular workspace. -* If you want a watched build which updates as files are changed, use "start_devserver" instead, which also serves the app. +* If you want a watched build which updates as files are changed, use "devserver_start" instead, which also serves the app. * You can provide a project instead of building the root one. The "list_projects" MCP tool could be used to obtain the list of projects. * This tool defaults to a development environment while a regular "ng build" defaults to a production environment. An unexpected build failure might suggest the project is not configured for the requested environment. diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts similarity index 63% rename from packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts index dbbc4dbd3cfd..08606c89da2b 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts @@ -7,12 +7,12 @@ */ import { z } from 'zod'; -import { LocalDevServer, devServerKey } from '../../dev-server'; +import { LocalDevserver, devserverKey } from '../../devserver'; import { type Host, LocalWorkspaceHost } from '../../host'; import { createStructuredContentOutput } from '../../utils'; import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry'; -const startDevServerToolInputSchema = z.object({ +const devserverStartToolInputSchema = z.object({ project: z .string() .optional() @@ -21,9 +21,9 @@ const startDevServerToolInputSchema = z.object({ ), }); -export type StartDevserverToolInput = z.infer; +export type DevserverStartToolInput = z.infer; -const startDevServerToolOutputSchema = z.object({ +const devserverStartToolOutputSchema = z.object({ message: z.string().describe('A message indicating the result of the operation.'), address: z .string() @@ -33,33 +33,33 @@ const startDevServerToolOutputSchema = z.object({ ), }); -export type StartDevserverToolOutput = z.infer; +export type DevserverStartToolOutput = z.infer; function localhostAddress(port: number) { return `http://localhost:${port}/`; } -export async function startDevServer( - input: StartDevserverToolInput, +export async function startDevserver( + input: DevserverStartToolInput, context: McpToolContext, host: Host, ) { - const projectKey = devServerKey(input.project); + const projectKey = devserverKey(input.project); - let devServer = context.devServers.get(projectKey); - if (devServer) { + let devserver = context.devservers.get(projectKey); + if (devserver) { return createStructuredContentOutput({ message: `Development server for project '${projectKey}' is already running.`, - address: localhostAddress(devServer.port), + address: localhostAddress(devserver.port), }); } const port = await host.getAvailablePort(); - devServer = new LocalDevServer({ host, project: input.project, port }); - devServer.start(); + devserver = new LocalDevserver({ host, project: input.project, port }); + devserver.start(); - context.devServers.set(projectKey, devServer); + context.devservers.set(projectKey, devserver); return createStructuredContentOutput({ message: `Development server for project '${projectKey}' started and watching for workspace changes.`, @@ -67,37 +67,37 @@ export async function startDevServer( }); } -export const START_DEVSERVER_TOOL: McpToolDeclaration< - typeof startDevServerToolInputSchema.shape, - typeof startDevServerToolOutputSchema.shape +export const DEVSERVER_START_TOOL: McpToolDeclaration< + typeof devserverStartToolInputSchema.shape, + typeof devserverStartToolOutputSchema.shape > = declareTool({ - name: 'start_devserver', + name: 'devserver_start', title: 'Start Development Server', description: ` -Starts the Angular development server ("ng serve") as a background process. Follow this up with "wait_for_devserver_build" to wait until +Starts the Angular development server ("ng serve") as a background process. Follow this up with "devserver_wait_for_build" to wait until the first build completes. * **Starting the Server:** Use this tool to begin serving the application. The tool will return immediately while the server runs in the background. -* **Get Initial Build Logs:** Once a dev server has started, use the "wait_for_devserver_build" tool to ensure it's alive. If there are any - build errors, "wait_for_devserver_build" would provide them back and you can give them to the user or rely on them to propose a fix. -* **Get Updated Build Logs:** Important: as long as a devserver is alive (i.e. "stop_devserver" wasn't called), after every time you make a - change to the workspace, re-run "wait_for_devserver_build" to see whether the change was successfully built and wait for the devserver to +* **Get Initial Build Logs:** Once a dev server has started, use the "devserver_wait_for_build" tool to ensure it's alive. If there are any + build errors, "devserver_wait_for_build" would provide them back and you can give them to the user or rely on them to propose a fix. +* **Get Updated Build Logs:** Important: as long as a devserver is alive (i.e. "devserver_stop" wasn't called), after every time you make a + change to the workspace, re-run "devserver_wait_for_build" to see whether the change was successfully built and wait for the devserver to be updated. * This tool manages development servers by itself. It maintains at most a single dev server instance for each project in the monorepo. * This is an asynchronous operation. Subsequent commands can be ran while the server is active. -* Use 'stop_devserver' to gracefully shut down the server and access the full log output. +* Use 'devserver_stop' to gracefully shut down the server and access the full log output. `, isReadOnly: true, isLocalOnly: true, - inputSchema: startDevServerToolInputSchema.shape, - outputSchema: startDevServerToolOutputSchema.shape, + inputSchema: devserverStartToolInputSchema.shape, + outputSchema: devserverStartToolOutputSchema.shape, factory: (context) => (input) => { - return startDevServer(input, context, LocalWorkspaceHost); + return startDevserver(input, context, LocalWorkspaceHost); }, }); diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts similarity index 69% rename from packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts index ed33ff3f0f7d..987a3da7c080 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts @@ -7,11 +7,11 @@ */ import { z } from 'zod'; -import { devServerKey } from '../../dev-server'; +import { devserverKey } from '../../devserver'; import { createStructuredContentOutput } from '../../utils'; import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry'; -const stopDevserverToolInputSchema = z.object({ +const devserverStopToolInputSchema = z.object({ project: z .string() .optional() @@ -20,18 +20,18 @@ const stopDevserverToolInputSchema = z.object({ ), }); -export type StopDevserverToolInput = z.infer; +export type DevserverStopToolInput = z.infer; -const stopDevserverToolOutputSchema = z.object({ +const devserverStopToolOutputSchema = z.object({ message: z.string().describe('A message indicating the result of the operation.'), logs: z.array(z.string()).optional().describe('The full logs from the dev server.'), }); -export type StopDevserverToolOutput = z.infer; +export type DevserverStopToolOutput = z.infer; -export function stopDevserver(input: StopDevserverToolInput, context: McpToolContext) { - const projectKey = devServerKey(input.project); - const devServer = context.devServers.get(projectKey); +export function stopDevserver(input: DevserverStopToolInput, context: McpToolContext) { + const projectKey = devserverKey(input.project); + const devServer = context.devservers.get(projectKey); if (!devServer) { return createStructuredContentOutput({ @@ -41,7 +41,7 @@ export function stopDevserver(input: StopDevserverToolInput, context: McpToolCon } devServer.stop(); - context.devServers.delete(projectKey); + context.devservers.delete(projectKey); return createStructuredContentOutput({ message: `Development server for project '${projectKey}' stopped.`, @@ -49,15 +49,15 @@ export function stopDevserver(input: StopDevserverToolInput, context: McpToolCon }); } -export const STOP_DEVSERVER_TOOL: McpToolDeclaration< - typeof stopDevserverToolInputSchema.shape, - typeof stopDevserverToolOutputSchema.shape +export const DEVSERVER_STOP_TOOL: McpToolDeclaration< + typeof devserverStopToolInputSchema.shape, + typeof devserverStopToolOutputSchema.shape > = declareTool({ - name: 'stop_devserver', + name: 'devserver_stop', title: 'Stop Development Server', description: ` -Stops a running Angular development server ("ng serve") that was started with the "start_devserver" tool. +Stops a running Angular development server ("ng serve") that was started with the "devserver_start" tool. * **Stopping the Server:** Use this tool to terminate a running development server and retrieve the logs. @@ -70,8 +70,8 @@ Stops a running Angular development server ("ng serve") that was started with th `, isReadOnly: true, isLocalOnly: true, - inputSchema: stopDevserverToolInputSchema.shape, - outputSchema: stopDevserverToolOutputSchema.shape, + inputSchema: devserverStopToolInputSchema.shape, + outputSchema: devserverStopToolOutputSchema.shape, factory: (context) => (input) => { return stopDevserver(input, context); }, diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/wait-for-devserver-build.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-wait-for-build.ts similarity index 67% rename from packages/angular/cli/src/commands/mcp/tools/devserver/wait-for-devserver-build.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-wait-for-build.ts index 1a002f4c5b2a..5021cc6e2bd3 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/wait-for-devserver-build.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver-wait-for-build.ts @@ -7,7 +7,7 @@ */ import { z } from 'zod'; -import { devServerKey } from '../../dev-server'; +import { devserverKey } from '../../devserver'; import { createStructuredContentOutput } from '../../utils'; import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry'; @@ -21,7 +21,7 @@ export const WATCH_DELAY = 1000; */ const DEFAULT_TIMEOUT = 180_000; // In milliseconds -const waitForDevserverBuildToolInputSchema = z.object({ +const devserverWaitForBuildToolInputSchema = z.object({ project: z .string() .optional() @@ -36,9 +36,9 @@ const waitForDevserverBuildToolInputSchema = z.object({ ), }); -export type WaitForDevserverBuildToolInput = z.infer; +export type DevserverWaitForBuildToolInput = z.infer; -const waitForDevserverBuildToolOutputSchema = z.object({ +const devserverWaitForBuildToolOutputSchema = z.object({ status: z .enum(['success', 'failure', 'unknown', 'timeout', 'no_devserver_found']) .describe( @@ -50,22 +50,22 @@ const waitForDevserverBuildToolOutputSchema = z.object({ .describe('The logs from the most recent build, if one exists.'), }); -export type WaitForDevserverBuildToolOutput = z.infer; +export type DevserverWaitForBuildToolOutput = z.infer; function wait(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function waitForDevserverBuild( - input: WaitForDevserverBuildToolInput, + input: DevserverWaitForBuildToolInput, context: McpToolContext, ) { - const projectKey = devServerKey(input.project); - const devServer = context.devServers.get(projectKey); + const projectKey = devserverKey(input.project); + const devServer = context.devservers.get(projectKey); const deadline = Date.now() + input.timeout; if (!devServer) { - return createStructuredContentOutput({ + return createStructuredContentOutput({ status: 'no_devserver_found', }); } @@ -73,49 +73,49 @@ export async function waitForDevserverBuild( await wait(WATCH_DELAY); while (devServer.isBuilding()) { if (Date.now() > deadline) { - return createStructuredContentOutput({ + return createStructuredContentOutput({ status: 'timeout', }); } await wait(WATCH_DELAY); } - return createStructuredContentOutput({ + return createStructuredContentOutput({ ...devServer.getMostRecentBuild(), }); } -export const WAIT_FOR_DEVSERVER_BUILD_TOOL: McpToolDeclaration< - typeof waitForDevserverBuildToolInputSchema.shape, - typeof waitForDevserverBuildToolOutputSchema.shape +export const DEVSERVER_WAIT_FOR_BUILD_TOOL: McpToolDeclaration< + typeof devserverWaitForBuildToolInputSchema.shape, + typeof devserverWaitForBuildToolOutputSchema.shape > = declareTool({ - name: 'wait_for_devserver_build', + name: 'devserver_wait_for_build', title: 'Wait for Devserver Build', description: ` -Waits for a dev server that was started with the "start_devserver" tool to complete its build, then reports the build logs from its most +Waits for a dev server that was started with the "devserver_start" tool to complete its build, then reports the build logs from its most recent build. -* **Waiting for a build:** As long as a devserver is alive ("start_devserver" was called for this project and "stop_devserver" wasn't +* **Waiting for a build:** As long as a devserver is alive ("devserver_start" was called for this project and "devserver_stop" wasn't called yet), then if you're making a file change and want to ensure it was successfully built, call this tool instead of any other build tool or command. When it retuns you'll get build logs back **and** you'll know the user's devserver is up-to-date with the latest changes. -* This tool expects that a dev server was launched on the same project with the "start_devserver" tool, otherwise a "no_devserver_found" +* This tool expects that a dev server was launched on the same project with the "devserver_start" tool, otherwise a "no_devserver_found" status will be returned. * This tool will block until the build is complete or the timeout is reached. If you expect a long build process, consider increasing the - timeout. Timeouts on initial run (right after "start_devserver" calls) or after a big change are not necessarily indicative of an error. + timeout. Timeouts on initial run (right after "devserver_start" calls) or after a big change are not necessarily indicative of an error. * If you encountered a timeout and it might be reasonable, just call this tool again. * If the dev server is not building, it will return quickly, with the logs from the last build. -* A 'no_devserver_found' status can indicate the underlying server was stopped for some reason. Try first to call the "start_devserver" +* A 'no_devserver_found' status can indicate the underlying server was stopped for some reason. Try first to call the "devserver_start" tool again, before giving up. `, isReadOnly: true, isLocalOnly: true, - inputSchema: waitForDevserverBuildToolInputSchema.shape, - outputSchema: waitForDevserverBuildToolOutputSchema.shape, + inputSchema: devserverWaitForBuildToolInputSchema.shape, + outputSchema: devserverWaitForBuildToolOutputSchema.shape, factory: (context) => (input) => { return waitForDevserverBuild(input, context); }, diff --git a/packages/angular/cli/src/commands/mcp/tools/devserver/serve_spec.ts b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver_spec.ts similarity index 90% rename from packages/angular/cli/src/commands/mcp/tools/devserver/serve_spec.ts rename to packages/angular/cli/src/commands/mcp/tools/devserver/devserver_spec.ts index 7b9ce0f10a08..b2e5aa2ca566 100644 --- a/packages/angular/cli/src/commands/mcp/tools/devserver/serve_spec.ts +++ b/packages/angular/cli/src/commands/mcp/tools/devserver/devserver_spec.ts @@ -10,9 +10,9 @@ import { EventEmitter } from 'events'; import type { ChildProcess } from 'node:child_process'; import type { MockHost } from '../../testing/mock-host'; import type { McpToolContext } from '../tool-registry'; -import { startDevServer } from './start-devserver'; -import { stopDevserver } from './stop-devserver'; -import { WATCH_DELAY, waitForDevserverBuild } from './wait-for-devserver-build'; +import { startDevserver } from './devserver-start'; +import { stopDevserver } from './devserver-stop'; +import { WATCH_DELAY, waitForDevserverBuild } from './devserver-wait-for-build'; class MockChildProcess extends EventEmitter { stdout = new EventEmitter(); @@ -37,12 +37,12 @@ describe('Serve Tools', () => { } as MockHost; mockContext = { - devServers: new Map(), + devservers: new Map(), } as Partial as McpToolContext; }); it('should start and stop a dev server', async () => { - const startResult = await startDevServer({}, mockContext, mockHost); + const startResult = await startDevserver({}, mockContext, mockHost); expect(startResult.structuredContent.message).toBe( `Development server for project '' started and watching for workspace changes.`, ); @@ -56,7 +56,7 @@ describe('Serve Tools', () => { }); it('should wait for a build to complete', async () => { - await startDevServer({}, mockContext, mockHost); + await startDevserver({}, mockContext, mockHost); const waitPromise = waitForDevserverBuild({ timeout: 10 }, mockContext); @@ -78,7 +78,7 @@ describe('Serve Tools', () => { it('should handle multiple dev servers', async () => { // Start server for project 1. This uses the basic mockProcess created for the tests. - const startResult1 = await startDevServer({ project: 'app-one' }, mockContext, mockHost); + const startResult1 = await startDevserver({ project: 'app-one' }, mockContext, mockHost); expect(startResult1.structuredContent.message).toBe( `Development server for project 'app-one' started and watching for workspace changes.`, ); @@ -87,7 +87,7 @@ describe('Serve Tools', () => { // Start server for project 2, returning a new mock process. const process2 = new MockChildProcess(); mockHost.spawn.and.returnValue(process2 as unknown as ChildProcess); - const startResult2 = await startDevServer({ project: 'app-two' }, mockContext, mockHost); + const startResult2 = await startDevserver({ project: 'app-two' }, mockContext, mockHost); expect(startResult2.structuredContent.message).toBe( `Development server for project 'app-two' started and watching for workspace changes.`, ); @@ -116,7 +116,7 @@ describe('Serve Tools', () => { }); it('should handle server crash', async () => { - await startDevServer({ project: 'crash-app' }, mockContext, mockHost); + await startDevserver({ project: 'crash-app' }, mockContext, mockHost); // Simulate a crash with exit code 1 mockProcess.stdout.emit('data', 'Fatal error.'); @@ -128,7 +128,7 @@ describe('Serve Tools', () => { }); it('wait should timeout if build takes too long', async () => { - await startDevServer({ project: 'timeout-app' }, mockContext, mockHost); + await startDevserver({ project: 'timeout-app' }, mockContext, mockHost); const waitResult = await waitForDevserverBuild( { project: 'timeout-app', timeout: 10 }, mockContext, @@ -139,7 +139,7 @@ describe('Serve Tools', () => { it('should wait through multiple cycles for a build to complete', async () => { jasmine.clock().install(); try { - await startDevServer({}, mockContext, mockHost); + await startDevserver({}, mockContext, mockHost); // Immediately simulate a build starting so isBuilding() is true. mockProcess.stdout.emit('data', '❯ Changes detected. Rebuilding...'); diff --git a/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts b/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts index 9bbce768000b..b18d984bc329 100644 --- a/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts +++ b/packages/angular/cli/src/commands/mcp/tools/tool-registry.ts @@ -9,7 +9,7 @@ import type { McpServer, ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { ZodRawShape } from 'zod'; import type { AngularWorkspace } from '../../../utilities/config'; -import type { DevServer } from '../dev-server'; +import type { Devserver } from '../devserver'; type ToolConfig = Parameters[1]; @@ -18,7 +18,7 @@ export interface McpToolContext { workspace?: AngularWorkspace; logger: { warn(text: string): void }; exampleDatabasePath?: string; - devServers: Map; + devservers: Map; } export type McpToolFactory = (