Skip to content

Commit 045e53b

Browse files
committed
refactor(@angular/cli): rename devserver MCP tools to be more consistent
1. Rename all devserver tools to have `devserver` as a prefix. 2. Make "devserver" be considered as a single word throughout. 3. Add support for grouping experimental tools and added "devserver" as a group, making it easy to enable all the devserver tools together.
1 parent 138649e commit 045e53b

File tree

9 files changed

+118
-99
lines changed

9 files changed

+118
-99
lines changed

packages/angular/cli/src/commands/mcp/cli.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
type CommandModuleImplementation,
1414
} from '../../command-builder/command-module';
1515
import { isTTY } from '../../utilities/tty';
16-
import { EXPERIMENTAL_TOOLS, createMcpServer } from './mcp-server';
16+
import { EXPERIMENTAL_TOOLS, EXPERIMENTAL_TOOL_GROUPS, createMcpServer } from './mcp-server';
1717

1818
const INTERACTIVE_MESSAGE = `
1919
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
5454
alias: 'E',
5555
array: true,
5656
describe: 'Enable an experimental tool.',
57-
choices: EXPERIMENTAL_TOOLS.map(({ name }) => name),
57+
choices: [
58+
...EXPERIMENTAL_TOOLS.map(({ name }) => name),
59+
...Object.keys(EXPERIMENTAL_TOOL_GROUPS),
60+
],
5861
hidden: true,
5962
});
6063
}

packages/angular/cli/src/commands/mcp/dev-server.ts renamed to packages/angular/cli/src/commands/mcp/devserver.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export type BuildStatus = 'success' | 'failure' | 'unknown';
3030
/**
3131
* An Angular development server managed by the MCP server.
3232
*/
33-
export interface DevServer {
33+
export interface Devserver {
3434
/**
3535
* Launches the dev server and returns immediately.
3636
*
@@ -64,19 +64,19 @@ export interface DevServer {
6464
port: number;
6565
}
6666

67-
export function devServerKey(project?: string) {
67+
export function devserverKey(project?: string) {
6868
return project ?? '<default>';
6969
}
7070

7171
/**
7272
* A local Angular development server managed by the MCP server.
7373
*/
74-
export class LocalDevServer implements DevServer {
74+
export class LocalDevserver implements Devserver {
7575
readonly host: Host;
7676
readonly port: number;
7777
readonly project?: string;
7878

79-
private devServerProcess: ChildProcess | null = null;
79+
private devserverProcess: ChildProcess | null = null;
8080
private serverLogs: string[] = [];
8181
private buildInProgress = false;
8282
private latestBuildLogStartIndex?: number = undefined;
@@ -89,7 +89,7 @@ export class LocalDevServer implements DevServer {
8989
}
9090

9191
start() {
92-
if (this.devServerProcess) {
92+
if (this.devserverProcess) {
9393
throw Error('Dev server already started.');
9494
}
9595

@@ -100,14 +100,14 @@ export class LocalDevServer implements DevServer {
100100

101101
args.push(`--port=${this.port}`);
102102

103-
this.devServerProcess = this.host.spawn('ng', args, { stdio: 'pipe' });
104-
this.devServerProcess.stdout?.on('data', (data) => {
103+
this.devserverProcess = this.host.spawn('ng', args, { stdio: 'pipe' });
104+
this.devserverProcess.stdout?.on('data', (data) => {
105105
this.addLog(data.toString());
106106
});
107-
this.devServerProcess.stderr?.on('data', (data) => {
107+
this.devserverProcess.stderr?.on('data', (data) => {
108108
this.addLog(data.toString());
109109
});
110-
this.devServerProcess.stderr?.on('close', () => {
110+
this.devserverProcess.stderr?.on('close', () => {
111111
this.stop();
112112
});
113113
this.buildInProgress = true;
@@ -127,8 +127,8 @@ export class LocalDevServer implements DevServer {
127127
}
128128

129129
stop() {
130-
this.devServerProcess?.kill();
131-
this.devServerProcess = null;
130+
this.devserverProcess?.kill();
131+
this.devserverProcess = null;
132132
}
133133

134134
getServerLogs(): string[] {

packages/angular/cli/src/commands/mcp/mcp-server.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1010
import { join } from 'node:path';
1111
import type { AngularWorkspace } from '../../utilities/config';
1212
import { VERSION } from '../../utilities/version';
13-
import type { DevServer } from './dev-server';
13+
import type { Devserver } from './devserver';
1414
import { registerInstructionsResource } from './resources/instructions';
1515
import { AI_TUTOR_TOOL } from './tools/ai-tutor';
1616
import { BEST_PRACTICES_TOOL } from './tools/best-practices';
1717
import { BUILD_TOOL } from './tools/build';
18-
import { START_DEVSERVER_TOOL } from './tools/devserver/start-devserver';
19-
import { STOP_DEVSERVER_TOOL } from './tools/devserver/stop-devserver';
20-
import { WAIT_FOR_DEVSERVER_BUILD_TOOL } from './tools/devserver/wait-for-devserver-build';
18+
import { DEVSERVER_START_TOOL } from './tools/devserver/devserver-start';
19+
import { DEVSERVER_STOP_TOOL } from './tools/devserver/devserver-stop';
20+
import { DEVSERVER_WAIT_FOR_BUILD_TOOL } from './tools/devserver/devserver-wait-for-build';
2121
import { DOC_SEARCH_TOOL } from './tools/doc-search';
2222
import { FIND_EXAMPLE_TOOL } from './tools/examples';
2323
import { MODERNIZE_TOOL } from './tools/modernize';
@@ -28,7 +28,16 @@ import { type AnyMcpToolDeclaration, registerTools } from './tools/tool-registry
2828
/**
2929
* Tools to manage devservers. Should be bundled together, then added to experimental or stable as a group.
3030
*/
31-
const SERVE_TOOLS = [START_DEVSERVER_TOOL, STOP_DEVSERVER_TOOL, WAIT_FOR_DEVSERVER_BUILD_TOOL];
31+
const DEVSERVER_TOOLS = [DEVSERVER_START_TOOL, DEVSERVER_STOP_TOOL, DEVSERVER_WAIT_FOR_BUILD_TOOL];
32+
33+
/**
34+
* Experimental tools that are grouped together under a single name.
35+
*
36+
* Used for enabling them as a group.
37+
*/
38+
export const EXPERIMENTAL_TOOL_GROUPS = {
39+
'devserver': DEVSERVER_TOOLS,
40+
};
3241

3342
/**
3443
* The set of tools that are enabled by default for the MCP server.
@@ -47,7 +56,7 @@ const STABLE_TOOLS = [
4756
* The set of tools that are available but not enabled by default.
4857
* These tools are considered experimental and may have limitations.
4958
*/
50-
export const EXPERIMENTAL_TOOLS = [BUILD_TOOL, MODERNIZE_TOOL, ...SERVE_TOOLS] as const;
59+
export const EXPERIMENTAL_TOOLS = [BUILD_TOOL, MODERNIZE_TOOL, ...DEVSERVER_TOOLS] as const;
5160

5261
export async function createMcpServer(
5362
options: {
@@ -114,7 +123,7 @@ equivalent actions.
114123
workspace: options.workspace,
115124
logger,
116125
exampleDatabasePath: join(__dirname, '../../../lib/code-examples.db'),
117-
devServers: new Map<string, DevServer>(),
126+
devservers: new Map<string, Devserver>(),
118127
},
119128
toolDeclarations,
120129
);
@@ -146,6 +155,13 @@ export function assembleToolDeclarations(
146155
if (process.env['NG_MCP_CODE_EXAMPLES'] === '1') {
147156
enabledExperimentalTools.add('find_examples');
148157
}
158+
for (const [toolGroupName, toolGroup] of Object.entries(EXPERIMENTAL_TOOL_GROUPS)) {
159+
if (enabledExperimentalTools.delete(toolGroupName)) {
160+
for (const tool of toolGroup) {
161+
enabledExperimentalTools.add(tool.name);
162+
}
163+
}
164+
}
149165

150166
if (enabledExperimentalTools.size > 0) {
151167
const experimentalToolsMap = new Map(experimentalDeclarations.map((tool) => [tool.name, tool]));

packages/angular/cli/src/commands/mcp/tools/build.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Perform a one-off, non-watched build using "ng build". Use this tool whenever th
9797
</Use Cases>
9898
<Operational Notes>
9999
* This tool runs "ng build" so it expects to run within an Angular workspace.
100-
* If you want a watched build which updates as files are changed, use "start_devserver" instead, which also serves the app.
100+
* If you want a watched build which updates as files are changed, use "devserver_start" instead, which also serves the app.
101101
* 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.
102102
* This tool defaults to a development environment while a regular "ng build" defaults to a production environment. An unexpected build
103103
failure might suggest the project is not configured for the requested environment.

packages/angular/cli/src/commands/mcp/tools/devserver/start-devserver.ts renamed to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-start.ts

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
*/
88

99
import { z } from 'zod';
10-
import { LocalDevServer, devServerKey } from '../../dev-server';
10+
import { LocalDevserver, devserverKey } from '../../devserver';
1111
import { type Host, LocalWorkspaceHost } from '../../host';
1212
import { createStructuredContentOutput } from '../../utils';
1313
import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry';
1414

15-
const startDevServerToolInputSchema = z.object({
15+
const devserverStartToolInputSchema = z.object({
1616
project: z
1717
.string()
1818
.optional()
@@ -21,9 +21,9 @@ const startDevServerToolInputSchema = z.object({
2121
),
2222
});
2323

24-
export type StartDevserverToolInput = z.infer<typeof startDevServerToolInputSchema>;
24+
export type DevserverStartToolInput = z.infer<typeof devserverStartToolInputSchema>;
2525

26-
const startDevServerToolOutputSchema = z.object({
26+
const devserverStartToolOutputSchema = z.object({
2727
message: z.string().describe('A message indicating the result of the operation.'),
2828
address: z
2929
.string()
@@ -33,71 +33,71 @@ const startDevServerToolOutputSchema = z.object({
3333
),
3434
});
3535

36-
export type StartDevserverToolOutput = z.infer<typeof startDevServerToolOutputSchema>;
36+
export type DevserverStartToolOutput = z.infer<typeof devserverStartToolOutputSchema>;
3737

3838
function localhostAddress(port: number) {
3939
return `http://localhost:${port}/`;
4040
}
4141

42-
export async function startDevServer(
43-
input: StartDevserverToolInput,
42+
export async function startDevserver(
43+
input: DevserverStartToolInput,
4444
context: McpToolContext,
4545
host: Host,
4646
) {
47-
const projectKey = devServerKey(input.project);
47+
const projectKey = devserverKey(input.project);
4848

49-
let devServer = context.devServers.get(projectKey);
50-
if (devServer) {
49+
let devserver = context.devservers.get(projectKey);
50+
if (devserver) {
5151
return createStructuredContentOutput({
5252
message: `Development server for project '${projectKey}' is already running.`,
53-
address: localhostAddress(devServer.port),
53+
address: localhostAddress(devserver.port),
5454
});
5555
}
5656

5757
const port = await host.getAvailablePort();
5858

59-
devServer = new LocalDevServer({ host, project: input.project, port });
60-
devServer.start();
59+
devserver = new LocalDevserver({ host, project: input.project, port });
60+
devserver.start();
6161

62-
context.devServers.set(projectKey, devServer);
62+
context.devservers.set(projectKey, devserver);
6363

6464
return createStructuredContentOutput({
6565
message: `Development server for project '${projectKey}' started and watching for workspace changes.`,
6666
address: localhostAddress(port),
6767
});
6868
}
6969

70-
export const START_DEVSERVER_TOOL: McpToolDeclaration<
71-
typeof startDevServerToolInputSchema.shape,
72-
typeof startDevServerToolOutputSchema.shape
70+
export const DEVSERVER_START_TOOL: McpToolDeclaration<
71+
typeof devserverStartToolInputSchema.shape,
72+
typeof devserverStartToolOutputSchema.shape
7373
> = declareTool({
74-
name: 'start_devserver',
74+
name: 'devserver_start',
7575
title: 'Start Development Server',
7676
description: `
7777
<Purpose>
78-
Starts the Angular development server ("ng serve") as a background process. Follow this up with "wait_for_devserver_build" to wait until
78+
Starts the Angular development server ("ng serve") as a background process. Follow this up with "devserver_wait_for_build" to wait until
7979
the first build completes.
8080
</Purpose>
8181
<Use Cases>
8282
* **Starting the Server:** Use this tool to begin serving the application. The tool will return immediately while the server runs in the
8383
background.
84-
* **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
85-
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.
86-
* **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
87-
change to the workspace, re-run "wait_for_devserver_build" to see whether the change was successfully built and wait for the devserver to
84+
* **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
85+
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.
86+
* **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
87+
change to the workspace, re-run "devserver_wait_for_build" to see whether the change was successfully built and wait for the devserver to
8888
be updated.
8989
</Use Cases>
9090
<Operational Notes>
9191
* This tool manages development servers by itself. It maintains at most a single dev server instance for each project in the monorepo.
9292
* This is an asynchronous operation. Subsequent commands can be ran while the server is active.
93-
* Use 'stop_devserver' to gracefully shut down the server and access the full log output.
93+
* Use 'devserver_stop' to gracefully shut down the server and access the full log output.
9494
</Operational Notes>
9595
`,
9696
isReadOnly: true,
9797
isLocalOnly: true,
98-
inputSchema: startDevServerToolInputSchema.shape,
99-
outputSchema: startDevServerToolOutputSchema.shape,
98+
inputSchema: devserverStartToolInputSchema.shape,
99+
outputSchema: devserverStartToolOutputSchema.shape,
100100
factory: (context) => (input) => {
101-
return startDevServer(input, context, LocalWorkspaceHost);
101+
return startDevserver(input, context, LocalWorkspaceHost);
102102
},
103103
});

packages/angular/cli/src/commands/mcp/tools/devserver/stop-devserver.ts renamed to packages/angular/cli/src/commands/mcp/tools/devserver/devserver-stop.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
*/
88

99
import { z } from 'zod';
10-
import { devServerKey } from '../../dev-server';
10+
import { devserverKey } from '../../devserver';
1111
import { createStructuredContentOutput } from '../../utils';
1212
import { type McpToolContext, type McpToolDeclaration, declareTool } from '../tool-registry';
1313

14-
const stopDevserverToolInputSchema = z.object({
14+
const devserverStopToolInputSchema = z.object({
1515
project: z
1616
.string()
1717
.optional()
@@ -20,18 +20,18 @@ const stopDevserverToolInputSchema = z.object({
2020
),
2121
});
2222

23-
export type StopDevserverToolInput = z.infer<typeof stopDevserverToolInputSchema>;
23+
export type DevserverStopToolInput = z.infer<typeof devserverStopToolInputSchema>;
2424

25-
const stopDevserverToolOutputSchema = z.object({
25+
const devserverStopToolOutputSchema = z.object({
2626
message: z.string().describe('A message indicating the result of the operation.'),
2727
logs: z.array(z.string()).optional().describe('The full logs from the dev server.'),
2828
});
2929

30-
export type StopDevserverToolOutput = z.infer<typeof stopDevserverToolOutputSchema>;
30+
export type DevserverStopToolOutput = z.infer<typeof devserverStopToolOutputSchema>;
3131

32-
export function stopDevserver(input: StopDevserverToolInput, context: McpToolContext) {
33-
const projectKey = devServerKey(input.project);
34-
const devServer = context.devServers.get(projectKey);
32+
export function stopDevserver(input: DevserverStopToolInput, context: McpToolContext) {
33+
const projectKey = devserverKey(input.project);
34+
const devServer = context.devservers.get(projectKey);
3535

3636
if (!devServer) {
3737
return createStructuredContentOutput({
@@ -41,23 +41,23 @@ export function stopDevserver(input: StopDevserverToolInput, context: McpToolCon
4141
}
4242

4343
devServer.stop();
44-
context.devServers.delete(projectKey);
44+
context.devservers.delete(projectKey);
4545

4646
return createStructuredContentOutput({
4747
message: `Development server for project '${projectKey}' stopped.`,
4848
logs: devServer.getServerLogs(),
4949
});
5050
}
5151

52-
export const STOP_DEVSERVER_TOOL: McpToolDeclaration<
53-
typeof stopDevserverToolInputSchema.shape,
54-
typeof stopDevserverToolOutputSchema.shape
52+
export const DEVSERVER_STOP_TOOL: McpToolDeclaration<
53+
typeof devserverStopToolInputSchema.shape,
54+
typeof devserverStopToolOutputSchema.shape
5555
> = declareTool({
56-
name: 'stop_devserver',
56+
name: 'devserver_stop',
5757
title: 'Stop Development Server',
5858
description: `
5959
<Purpose>
60-
Stops a running Angular development server ("ng serve") that was started with the "start_devserver" tool.
60+
Stops a running Angular development server ("ng serve") that was started with the "devserver_start" tool.
6161
</Purpose>
6262
<Use Cases>
6363
* **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
7070
`,
7171
isReadOnly: true,
7272
isLocalOnly: true,
73-
inputSchema: stopDevserverToolInputSchema.shape,
74-
outputSchema: stopDevserverToolOutputSchema.shape,
73+
inputSchema: devserverStopToolInputSchema.shape,
74+
outputSchema: devserverStopToolOutputSchema.shape,
7575
factory: (context) => (input) => {
7676
return stopDevserver(input, context);
7777
},

0 commit comments

Comments
 (0)