Skip to content

Commit b81b880

Browse files
clydinalan-agius4
authored andcommitted
refactor(@angular/cli): migrate MCP server to @modelcontextprotocol/server v2
Upgrades the Model Context Protocol (MCP) server implementation from @modelcontextprotocol/sdk v1.30.0 to the role-specific @modelcontextprotocol/server v2.0.0 package. The monolithic SDK dependency has been replaced in both package.json and BUILD.bazel, with StdioServerTransport now resolved from the @modelcontextprotocol/server/stdio subpath. In the MCP server initialization, notification handler registration for roots list changes now uses the spec method string instead of a Zod schema object. Additionally, tool-registry.ts introduces McpToolCallback and centrally wraps raw Zod schemas with z.object() during tool registration so individual tool declarations do not require schema syntax changes. Finally, low-level handlers in the zoneless migration tool have been updated to use ServerContext in place of RequestHandlerExtra, routing notifications and sampling requests through ctx.mcpReq.log and ctx.mcpReq.send.
1 parent 5c8a055 commit b81b880

12 files changed

Lines changed: 67 additions & 156 deletions

File tree

packages/angular/cli/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ ts_project(
6060
":node_modules/@angular-devkit/schematics",
6161
":node_modules/@inquirer/prompts",
6262
":node_modules/@listr2/prompt-adapter-inquirer",
63-
":node_modules/@modelcontextprotocol/sdk",
63+
":node_modules/@modelcontextprotocol/server",
6464
":node_modules/jsonc-parser",
6565
":node_modules/listr2",
6666
":node_modules/npm-package-arg",
@@ -113,7 +113,7 @@ ts_project(
113113
":angular-cli",
114114
":node_modules/@angular-devkit/core",
115115
":node_modules/@angular-devkit/schematics",
116-
":node_modules/@modelcontextprotocol/sdk",
116+
":node_modules/@modelcontextprotocol/server",
117117
":node_modules/yargs",
118118
"//:node_modules/@types/semver",
119119
"//:node_modules/@types/yargs",

packages/angular/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"@angular-devkit/schematics": "workspace:0.0.0-PLACEHOLDER",
1818
"@inquirer/prompts": "8.5.2",
1919
"@listr2/prompt-adapter-inquirer": "4.2.5",
20-
"@modelcontextprotocol/sdk": "1.30.0",
20+
"@modelcontextprotocol/server": "2.0.0",
2121
"@schematics/angular": "workspace:0.0.0-PLACEHOLDER",
2222
"jsonc-parser": "3.3.1",
2323
"listr2": "11.0.0",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9+
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
1010
import type { Argv } from 'yargs';
1111
import {
1212
CommandModule,

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10-
import { RootsListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
9+
import { McpServer } from '@modelcontextprotocol/server';
1110
import { join, normalize } from 'node:path';
1211
import { fileURLToPath } from 'node:url';
1312
import type { AngularWorkspace } from '../../utilities/config';
@@ -134,7 +133,7 @@ for equivalent actions.
134133
restrictedHost.setRoots(searchRoots);
135134

136135
if (clientCapabilities.roots.listChanged) {
137-
server.server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
136+
server.server.setNotificationHandler('notifications/roots/list_changed', async () => {
138137
try {
139138
const { roots: updatedRoots } = await server.server.listRoots();
140139
const updatedSearchRoots =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9+
import type { McpServer } from '@modelcontextprotocol/server';
1010
import { readFile } from 'node:fs/promises';
1111
import { join } from 'node:path';
1212

packages/angular/cli/src/commands/mcp/testing/test-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { workspaces } from '@angular-devkit/core';
10-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
10+
import { McpServer } from '@modelcontextprotocol/server';
1111
import { AngularWorkspace } from '../../../utilities/config';
1212
import { type Devserver } from '../devserver';
1313
import { Host } from '../host';

packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol';
10-
import type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types';
9+
import type { ServerContext } from '@modelcontextprotocol/server';
1110
import type { SourceFile } from 'typescript';
1211
import type { Host } from '../../host';
1312
import { analyzeForUnsupportedZoneUses } from './analyze-for-unsupported-zone-uses';
@@ -22,7 +21,7 @@ const supportedStrategies: ReadonlySet<string> = new Set(['OnPush', 'Default', '
2221
export async function migrateSingleFile(
2322
sourceFile: SourceFile,
2423
host: Host,
25-
extras: RequestHandlerExtra<ServerRequest, ServerNotification>,
24+
extras: ServerContext,
2625
): Promise<MigrationResponse | null> {
2726
const testBedSpecifier = await getImportSpecifier(sourceFile, '@angular/core/testing', 'TestBed');
2827
const isTestFile = sourceFile.fileName.endsWith('.spec.ts') || !!testBedSpecifier;

packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/migrate-single-file_spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol';
10-
import type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types';
9+
import type { ServerContext } from '@modelcontextprotocol/server';
1110
import ts from 'typescript';
1211
import { createMockHost } from '../../testing/test-utils';
1312
import { migrateSingleFile } from './migrate-single-file';
1413

1514
const fakeExtras = {
16-
sendDebugMessage: jasmine.createSpy(),
17-
sendNotification: jasmine.createSpy(),
18-
} as unknown as RequestHandlerExtra<ServerRequest, ServerNotification>;
15+
mcpReq: {
16+
log: jasmine.createSpy(),
17+
notify: jasmine.createSpy(),
18+
},
19+
} as unknown as ServerContext;
1920

2021
describe('migrateSingleFile', () => {
2122
const mockHost = createMockHost();

packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/send-debug-message.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,8 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol';
10-
import type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types';
9+
import type { ServerContext } from '@modelcontextprotocol/server';
1110

12-
export function sendDebugMessage(
13-
message: string,
14-
{ sendNotification }: RequestHandlerExtra<ServerRequest, ServerNotification>,
15-
): void {
16-
void sendNotification({
17-
method: 'notifications/message',
18-
params: {
19-
level: 'debug',
20-
data: message,
21-
},
22-
});
11+
export function sendDebugMessage(message: string, ctx: ServerContext): void {
12+
void ctx.mcpReq.log('debug', message);
2313
}

packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol';
10-
import type { ServerNotification, ServerRequest } from '@modelcontextprotocol/sdk/types';
9+
import type { ServerContext } from '@modelcontextprotocol/server';
1110
import { join } from 'node:path';
1211
import type { SourceFile } from 'typescript';
1312
import { z } from 'zod';
@@ -57,7 +56,7 @@ change detection (a prerequisite for zoneless applications).
5756
export async function registerZonelessMigrationTool(
5857
fileOrDirPath: string,
5958
host: Host,
60-
extras: RequestHandlerExtra<ServerRequest, ServerNotification>,
59+
extras: ServerContext,
6160
) {
6261
let filesWithComponents, componentTestFiles, zoneFiles, categorizationErrors;
6362
try {
@@ -114,7 +113,7 @@ export async function registerZonelessMigrationTool(
114113
async function discoverAndCategorizeFiles(
115114
fileOrDirPath: string,
116115
host: Host,
117-
extras: RequestHandlerExtra<ServerRequest, ServerNotification>,
116+
extras: ServerContext,
118117
) {
119118
const filePaths: string[] = [];
120119
const componentTestFiles = new Set<SourceFile>();
@@ -175,7 +174,7 @@ async function discoverAndCategorizeFiles(
175174
async function categorizeFile(
176175
sourceFile: SourceFile,
177176
host: Host,
178-
extras: RequestHandlerExtra<ServerRequest, ServerNotification>,
177+
extras: ServerContext,
179178
categorizedFiles: {
180179
filesWithComponents: Set<SourceFile>;
181180
componentTestFiles: Set<SourceFile>;
@@ -216,11 +215,11 @@ async function categorizeFile(
216215
}
217216

218217
async function rankComponentFilesForMigration(
219-
{ sendRequest }: RequestHandlerExtra<ServerRequest, ServerNotification>,
218+
ctx: ServerContext,
220219
componentFiles: SourceFile[],
221220
): Promise<SourceFile[]> {
222221
try {
223-
const response = await sendRequest(
222+
const response = await ctx.mcpReq.send(
224223
{
225224
method: 'sampling/createMessage',
226225
params: {

0 commit comments

Comments
 (0)