11import { resolve } from 'node:path'
22import { promises as fs } from 'node:fs'
3+ import { homedir } from 'node:os'
34import type { NetlifyAPI } from '@netlify/api'
45
56import { chalk , log , logAndThrowError , type APIError } from '../../utils/command-helpers.js'
@@ -80,71 +81,55 @@ const detectIDE = async (): Promise<ConsumerConfig | null> => {
8081}
8182
8283// Generate MCP configuration for the detected IDE
83- const generateMcpConfig = ( ide : ConsumerConfig ) : string => {
84- const configs : Record < string , string > = {
85- vscode : JSON . stringify (
86- {
87- servers : {
88- netlify : {
89- type : 'stdio' ,
90- command : 'npx' ,
91- args : [ '-y' , '@netlify/mcp' ] ,
92- } ,
84+ const generateMcpConfig = ( ide : ConsumerConfig ) : Record < string , unknown > => {
85+ const configs : Record < string , Record < string , unknown > > = {
86+ vscode : {
87+ servers : {
88+ netlify : {
89+ type : 'stdio' ,
90+ command : 'npx' ,
91+ args : [ '-y' , '@netlify/mcp' ] ,
9392 } ,
9493 } ,
95- null ,
96- 2 ,
97- ) ,
98- cursor : JSON . stringify (
99- {
100- mcpServers : {
101- netlify : {
102- command : 'npx' ,
103- args : [ '-y' , '@netlify/mcp' ] ,
104- } ,
94+ } ,
95+ cursor : {
96+ mcpServers : {
97+ netlify : {
98+ command : 'npx' ,
99+ args : [ '-y' , '@netlify/mcp' ] ,
105100 } ,
106101 } ,
107- null ,
108- 2 ,
109- ) ,
110- windsurf : JSON . stringify (
111- {
112- mcpServers : {
113- netlify : {
114- command : 'npx' ,
115- args : [ '-y' , '@netlify/mcp' ] ,
116- } ,
102+ } ,
103+ windsurf : {
104+ mcpServers : {
105+ netlify : {
106+ command : 'npx' ,
107+ args : [ '-y' , '@netlify/mcp' ] ,
117108 } ,
118109 } ,
119- null ,
120- 2 ,
121- ) ,
110+ } ,
122111 }
123112
124113 return (
125- configs [ ide . key ] ||
126- JSON . stringify (
127- {
128- mcpServers : {
129- netlify : {
130- command : 'npx' ,
131- args : [ '-y' , '@netlify/mcp' ] ,
132- } ,
114+ configs [ ide . key ] ?? {
115+ mcpServers : {
116+ netlify : {
117+ command : 'npx' ,
118+ args : [ '-y' , '@netlify/mcp' ] ,
133119 } ,
134120 } ,
135- null ,
136- 2 ,
137- )
121+ }
138122 )
139123}
140124
141125// VS Code specific MCP configuration
142- const configureMcpForVSCode = async ( config : string , projectPath : string ) : Promise < void > => {
143- const configPath = resolve ( projectPath , '.vscode' , 'mcp.json' )
126+ const configureMcpForVSCode = async ( config : Record < string , unknown > , projectPath : string ) : Promise < void > => {
127+ const vscodeDirPath = resolve ( projectPath , '.vscode' )
128+ const configPath = resolve ( vscodeDirPath , 'mcp.json' )
144129
145130 try {
146131 // Create .vscode directory if it doesn't exist
147- await fs . mkdir ( resolve ( projectPath , '.vscode' ) , { recursive : true } )
132+ await fs . mkdir ( vscodeDirPath , { recursive : true } )
148133
149134 // Write or update mcp.json
150135 let existingConfig : Record < string , unknown > = { }
@@ -155,8 +140,7 @@ const configureMcpForVSCode = async (config: string, projectPath: string): Promi
155140 // File doesn't exist or is invalid JSON
156141 }
157142
158- const mcpConfig = JSON . parse ( config ) as Record < string , unknown >
159- const updatedConfig = { ...existingConfig , ...mcpConfig }
143+ const updatedConfig = { ...existingConfig , ...config }
160144
161145 await fs . writeFile ( configPath , JSON . stringify ( updatedConfig , null , 2 ) , 'utf-8' )
162146 log ( `${ chalk . green ( 'β
' ) } VS Code MCP configuration saved to ${ chalk . cyan ( '.vscode/mcp.json' ) } ` )
@@ -166,26 +150,26 @@ const configureMcpForVSCode = async (config: string, projectPath: string): Promi
166150}
167151
168152// Cursor specific MCP configuration
169- const configureMcpForCursor = async ( config : string , projectPath : string ) : Promise < void > => {
153+ const configureMcpForCursor = async ( config : Record < string , unknown > , projectPath : string ) : Promise < void > => {
170154 const configPath = resolve ( projectPath , '.cursor' , 'mcp.json' )
171155
172156 try {
173157 await fs . mkdir ( resolve ( projectPath , '.cursor' ) , { recursive : true } )
174- await fs . writeFile ( configPath , config , 'utf-8' )
158+ await fs . writeFile ( configPath , JSON . stringify ( config , null , 2 ) , 'utf-8' )
175159 log ( `${ chalk . green ( 'β
' ) } Cursor MCP configuration saved to ${ chalk . cyan ( '.cursor/mcp.json' ) } ` )
176160 } catch ( error ) {
177161 throw new Error ( `Failed to configure Cursor MCP: ${ error instanceof Error ? error . message : 'Unknown error' } ` )
178162 }
179163}
180164
181165// Windsurf specific MCP configuration
182- const configureMcpForWindsurf = async ( config : string , _projectPath : string ) : Promise < void > => {
183- const { homedir } = await import ( 'node:os ')
184- const configPath = resolve ( homedir ( ) , '.codeium' , 'windsurf' , 'mcp_config.json' )
166+ const configureMcpForWindsurf = async ( config : Record < string , unknown > , _projectPath : string ) : Promise < void > => {
167+ const windsurfDirPath = resolve ( homedir ( ) , '.codeium' , 'windsurf ')
168+ const configPath = resolve ( windsurfDirPath , 'mcp_config.json' )
185169
186170 try {
187171 // Create .codeium/windsurf directory if it doesn't exist
188- await fs . mkdir ( resolve ( homedir ( ) , '.codeium' , 'windsurf' ) , { recursive : true } )
172+ await fs . mkdir ( windsurfDirPath , { recursive : true } )
189173
190174 // Read existing config or create new one
191175 let existingConfig : Record < string , unknown > = { }
@@ -196,11 +180,9 @@ const configureMcpForWindsurf = async (config: string, _projectPath: string): Pr
196180 // File doesn't exist or is invalid JSON
197181 }
198182
199- const mcpConfig = JSON . parse ( config ) as Record < string , unknown >
200-
201183 // Merge mcpServers from both configs
202184 const existingServers = ( existingConfig . mcpServers as Record < string , unknown > | undefined ) ?? { }
203- const newServers = ( mcpConfig . mcpServers as Record < string , unknown > | undefined ) ?? { }
185+ const newServers = ( config . mcpServers as Record < string , unknown > | undefined ) ?? { }
204186
205187 const updatedConfig = {
206188 ...existingConfig ,
@@ -211,19 +193,19 @@ const configureMcpForWindsurf = async (config: string, _projectPath: string): Pr
211193 }
212194
213195 await fs . writeFile ( configPath , JSON . stringify ( updatedConfig , null , 2 ) , 'utf-8' )
214- log ( `${ chalk . green ( 'β
' ) } Windsurf MCP configuration saved to global config ` )
196+ log ( `${ chalk . green ( 'β
' ) } Windsurf MCP configuration saved` )
215197 log ( `${ chalk . gray ( 'π‘' ) } Restart Windsurf to activate the MCP server` )
216198 } catch ( error ) {
217199 throw new Error ( `Failed to configure Windsurf MCP: ${ error instanceof Error ? error . message : 'Unknown error' } ` )
218200 }
219201}
220202
221203// Generic MCP configuration display
222- const showGenericMcpConfig = ( config : string , ideName : string ) : void => {
204+ const showGenericMcpConfig = ( config : Record < string , unknown > , ideName : string ) : void => {
223205 log ( `\n${ chalk . yellow ( 'π Manual Configuration Required' ) } ` )
224206 log ( `Please add the following configuration to your ${ ideName } settings:` )
225207 log ( `\n${ chalk . gray ( '--- Configuration ---' ) } ` )
226- log ( config )
208+ log ( JSON . stringify ( config , null , 2 ) )
227209 log ( `${ chalk . gray ( '--- End Configuration ---' ) } \n` )
228210}
229211
@@ -241,7 +223,9 @@ const triggerMcpConfiguration = async (ide: ConsumerConfig, projectPath: string)
241223 ] )
242224
243225 if ( ! shouldConfigure ) {
244- log ( chalk . gray ( 'Skipped MCP configuration. You can set it up manually later.' ) )
226+ log (
227+ chalk . gray ( 'Skipped MCP configuration. You can set it up manually later by changing MCP settings in your editor' ) ,
228+ )
245229 return false
246230 }
247231
@@ -289,7 +273,7 @@ const fetchProjectInfo = async (url: string): Promise<ProjectInfo> => {
289273 } )
290274
291275 if ( ! response . ok ) {
292- throw new Error ( `HTTP error! status : ${ String ( response . status ) } ` )
276+ throw new Error ( `Failed to fetch project information : ${ response . statusText } ` )
293277 }
294278 const data = ( await response . text ( ) ) as unknown as string
295279 const parsedData = JSON . parse ( data ) as unknown as ProjectInfo
@@ -346,7 +330,6 @@ export const initWithAiRules = async (hash: string, command: BaseCommand): Promi
346330 const { api } = command . netlify
347331
348332 log ( `${ chalk . blue ( 'π€ Initializing AI project' ) } with rules...` )
349- log ( `${ chalk . gray ( 'Hash:' ) } ${ hash } ` )
350333 log ( `${ chalk . gray ( 'User:' ) } ${ api . accessToken ? 'Authenticated β
' : 'Not authenticated β' } ` )
351334
352335 try {
@@ -399,7 +382,7 @@ export const initWithAiRules = async (hash: string, command: BaseCommand): Promi
399382 let mcpConfigured = false
400383
401384 if ( detectedIDE ) {
402- log ( `${ chalk . green ( 'β
' ) } Detected IDE : ${ chalk . cyan ( detectedIDE . presentedName ) } ` )
385+ log ( `${ chalk . green ( 'β
' ) } Detected development environment : ${ chalk . cyan ( detectedIDE . presentedName ) } ` )
403386 mcpConfigured = await triggerMcpConfiguration ( detectedIDE , targetDir )
404387 }
405388
@@ -423,7 +406,7 @@ export const initWithAiRules = async (hash: string, command: BaseCommand): Promi
423406 if ( mcpConfigured ) {
424407 log ( chalk . yellowBright ( `π§ Step 2: MCP Server Configured` ) )
425408 log ( ` ${ chalk . green ( 'β
' ) } ${ chalk . cyan ( detectedIDE . key ) } is ready with Netlify MCP server` )
426- log ( ` ${ chalk . gray ( 'π‘ MCP will activate when you reload/restart your IDE window ' ) } ` )
409+ log ( ` ${ chalk . gray ( 'π‘ MCP will activate when you reload/restart your development environment ' ) } ` )
427410 } else {
428411 log ( chalk . yellowBright ( `π§ Step 2: Manual MCP Configuration` ) )
429412 log ( ` ${ chalk . cyan ( detectedIDE . key ) } detected - MCP setup was skipped` )
0 commit comments