Skip to content

Commit 8be48af

Browse files
committed
fix(@angular/cli): fix sourceRoot resolution for MCP projects tool
Ensure that project.sourceRoot is used directly as it is already relative to the workspace root, preventing duplicated path prefixes for sub-projects.
1 parent f1ed025 commit 8be48af

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ async function loadAndParseWorkspace(
467467
const projects = [];
468468
const workspaceRoot = dirname(configFile);
469469
for (const [name, project] of ws.projects.entries()) {
470-
const sourceRoot = posix.join(project.root, project.sourceRoot ?? 'src');
470+
const sourceRoot = project.sourceRoot ?? posix.join(project.root, 'src');
471471
const fullSourceRoot = join(workspaceRoot, sourceRoot);
472472
const unitTestFramework = getUnitTestFramework(project.targets.get('test'));
473473
const styleLanguage = await getProjectStyleLanguage(project, ws, fullSourceRoot);
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { exec, ProcessOutput, silentNpm } from '../../utils/process';
2+
import { updateJsonFile } from '../../utils/project';
3+
import assert from 'node:assert/strict';
4+
5+
const MCP_INSPECTOR_PACKAGE_NAME = '@modelcontextprotocol/inspector-cli';
6+
const MCP_INSPECTOR_PACKAGE_VERSION = '0.16.2';
7+
const MCP_INSPECTOR_COMMAND_NAME = 'mcp-inspector-cli';
8+
9+
async function runInspector(...args: string[]): Promise<ProcessOutput> {
10+
return exec(
11+
MCP_INSPECTOR_COMMAND_NAME,
12+
'--cli',
13+
'npx',
14+
'--no',
15+
'@angular/cli',
16+
'mcp',
17+
...args,
18+
);
19+
}
20+
21+
export default async function () {
22+
await silentNpm(
23+
'install',
24+
'--ignore-scripts',
25+
'-g',
26+
`${MCP_INSPECTOR_PACKAGE_NAME}@${MCP_INSPECTOR_PACKAGE_VERSION}`,
27+
);
28+
29+
try {
30+
// 1. Add a sample project with a non-root path to angular.json
31+
await updateJsonFile('angular.json', (workspaceJson) => {
32+
workspaceJson.projects ??= {};
33+
workspaceJson.projects['sample-lib'] = {
34+
root: 'projects/sample-lib',
35+
sourceRoot: 'projects/sample-lib/src',
36+
projectType: 'library',
37+
};
38+
});
39+
40+
// 2. Call list_projects
41+
const { stdout } = await runInspector(
42+
'--method',
43+
'tools/call',
44+
'--tool-name',
45+
'list_projects',
46+
);
47+
48+
// 3. Verify output
49+
assert.match(stdout, /"name": "sample-lib"/);
50+
// Assert that sourceRoot is NOT duplicated
51+
assert.match(stdout, /"sourceRoot": "projects\/sample-lib\/src"/);
52+
assert.doesNotMatch(stdout, /"sourceRoot": "projects\/sample-lib\/projects\/sample-lib\/src"/);
53+
54+
} finally {
55+
// 4. Cleanup angular.json
56+
await updateJsonFile('angular.json', (workspaceJson) => {
57+
delete workspaceJson.projects['sample-lib'];
58+
});
59+
await silentNpm('uninstall', '-g', MCP_INSPECTOR_PACKAGE_NAME);
60+
}
61+
}

0 commit comments

Comments
 (0)