99import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' ;
1010import { readFile } from 'node:fs/promises' ;
1111import path from 'node:path' ;
12+ import { z } from 'zod' ;
1213import type { AngularWorkspace } from '../../utilities/config' ;
1314import { VERSION } from '../../utilities/version' ;
1415
@@ -30,10 +31,10 @@ export async function createMcpServer(context: {
3031 {
3132 title : 'Angular Best Practices and Code Generation Guide' ,
3233 description :
33- "A comprehensive guide detailing Angular's best practices for code generation and development. " +
34- 'This guide should be used as a reference by an LLM to ensure any generated code ' +
35- 'adheres to modern Angular standards, including the use of standalone components, ' +
36- 'typed forms, modern control flow syntax, and other current conventions.' ,
34+ "A comprehensive guide detailing Angular's best practices for code generation and development." +
35+ ' This guide should be used as a reference by an LLM to ensure any generated code' +
36+ ' adheres to modern Angular standards, including the use of standalone components,' +
37+ ' typed forms, modern control flow syntax, and other current conventions.' ,
3738 mimeType : 'text/markdown' ,
3839 } ,
3940 async ( ) => {
@@ -56,6 +57,34 @@ export async function createMcpServer(context: {
5657 annotations : {
5758 readOnlyHint : true ,
5859 } ,
60+ outputSchema : {
61+ projects : z . array (
62+ z . object ( {
63+ name : z
64+ . string ( )
65+ . describe ( 'The name of the project, as defined in the `angular.json` file.' ) ,
66+ type : z
67+ . enum ( [ 'application' , 'library' ] )
68+ . optional ( )
69+ . describe ( `The type of the project, either 'application' or 'library'.` ) ,
70+ root : z
71+ . string ( )
72+ . describe ( 'The root directory of the project, relative to the workspace root.' ) ,
73+ sourceRoot : z
74+ . string ( )
75+ . describe (
76+ `The root directory of the project's source files, relative to the workspace root.` ,
77+ ) ,
78+ selectorPrefix : z
79+ . string ( )
80+ . optional ( )
81+ . describe (
82+ 'The prefix to use for component selectors.' +
83+ ` For example, a prefix of 'app' would result in selectors like '<app-my-component>'.` ,
84+ ) ,
85+ } ) ,
86+ ) ,
87+ } ,
5988 } ,
6089 async ( ) => {
6190 const { workspace } = context ;
@@ -74,13 +103,28 @@ export async function createMcpServer(context: {
74103 } ;
75104 }
76105
106+ const projects = [ ] ;
107+ // Convert to output format
108+ for ( const [ name , project ] of workspace . projects . entries ( ) ) {
109+ projects . push ( {
110+ name,
111+ type : project . extensions [ 'projectType' ] as 'application' | 'library' | undefined ,
112+ root : project . root ,
113+ sourceRoot : project . sourceRoot ?? path . posix . join ( project . root , 'src' ) ,
114+ selectorPrefix : project . extensions [ 'prefix' ] as string ,
115+ } ) ;
116+ }
117+
118+ // The structuredContent field is newer and may not be supported by all hosts.
119+ // A text representation of the content is also provided for compatibility.
77120 return {
78121 content : [
79122 {
80123 type : 'text' as const ,
81- text : ' Projects in the Angular workspace: ' + [ ... workspace . projects . keys ( ) ] . join ( ',' ) ,
124+ text : ` Projects in the Angular workspace:\n ${ JSON . stringify ( projects ) } ` ,
82125 } ,
83126 ] ,
127+ structuredContent : { projects } ,
84128 } ;
85129 } ,
86130 ) ;
0 commit comments