1
1
import { resolve } from 'node:path'
2
2
import { promises as fs } from 'node:fs'
3
+ import { homedir } from 'node:os'
3
4
import type { NetlifyAPI } from '@netlify/api'
4
5
5
6
import { chalk , log , logAndThrowError , type APIError } from '../../utils/command-helpers.js'
@@ -80,71 +81,55 @@ const detectIDE = async (): Promise<ConsumerConfig | null> => {
80
81
}
81
82
82
83
// 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' ] ,
93
92
} ,
94
93
} ,
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' ] ,
105
100
} ,
106
101
} ,
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' ] ,
117
108
} ,
118
109
} ,
119
- null ,
120
- 2 ,
121
- ) ,
110
+ } ,
122
111
}
123
112
124
113
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' ] ,
133
119
} ,
134
120
} ,
135
- null ,
136
- 2 ,
137
- )
121
+ }
138
122
)
139
123
}
140
124
141
125
// 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' )
144
129
145
130
try {
146
131
// Create .vscode directory if it doesn't exist
147
- await fs . mkdir ( resolve ( projectPath , '.vscode' ) , { recursive : true } )
132
+ await fs . mkdir ( vscodeDirPath , { recursive : true } )
148
133
149
134
// Write or update mcp.json
150
135
let existingConfig : Record < string , unknown > = { }
@@ -155,8 +140,7 @@ const configureMcpForVSCode = async (config: string, projectPath: string): Promi
155
140
// File doesn't exist or is invalid JSON
156
141
}
157
142
158
- const mcpConfig = JSON . parse ( config ) as Record < string , unknown >
159
- const updatedConfig = { ...existingConfig , ...mcpConfig }
143
+ const updatedConfig = { ...existingConfig , ...config }
160
144
161
145
await fs . writeFile ( configPath , JSON . stringify ( updatedConfig , null , 2 ) , 'utf-8' )
162
146
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
166
150
}
167
151
168
152
// 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 > => {
170
154
const configPath = resolve ( projectPath , '.cursor' , 'mcp.json' )
171
155
172
156
try {
173
157
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' )
175
159
log ( `${ chalk . green ( 'β
' ) } Cursor MCP configuration saved to ${ chalk . cyan ( '.cursor/mcp.json' ) } ` )
176
160
} catch ( error ) {
177
161
throw new Error ( `Failed to configure Cursor MCP: ${ error instanceof Error ? error . message : 'Unknown error' } ` )
178
162
}
179
163
}
180
164
181
165
// 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' )
185
169
186
170
try {
187
171
// 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 } )
189
173
190
174
// Read existing config or create new one
191
175
let existingConfig : Record < string , unknown > = { }
@@ -196,11 +180,9 @@ const configureMcpForWindsurf = async (config: string, _projectPath: string): Pr
196
180
// File doesn't exist or is invalid JSON
197
181
}
198
182
199
- const mcpConfig = JSON . parse ( config ) as Record < string , unknown >
200
-
201
183
// Merge mcpServers from both configs
202
184
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 ) ?? { }
204
186
205
187
const updatedConfig = {
206
188
...existingConfig ,
@@ -211,19 +193,19 @@ const configureMcpForWindsurf = async (config: string, _projectPath: string): Pr
211
193
}
212
194
213
195
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` )
215
197
log ( `${ chalk . gray ( 'π‘' ) } Restart Windsurf to activate the MCP server` )
216
198
} catch ( error ) {
217
199
throw new Error ( `Failed to configure Windsurf MCP: ${ error instanceof Error ? error . message : 'Unknown error' } ` )
218
200
}
219
201
}
220
202
221
203
// Generic MCP configuration display
222
- const showGenericMcpConfig = ( config : string , ideName : string ) : void => {
204
+ const showGenericMcpConfig = ( config : Record < string , unknown > , ideName : string ) : void => {
223
205
log ( `\n${ chalk . yellow ( 'π Manual Configuration Required' ) } ` )
224
206
log ( `Please add the following configuration to your ${ ideName } settings:` )
225
207
log ( `\n${ chalk . gray ( '--- Configuration ---' ) } ` )
226
- log ( config )
208
+ log ( JSON . stringify ( config , null , 2 ) )
227
209
log ( `${ chalk . gray ( '--- End Configuration ---' ) } \n` )
228
210
}
229
211
@@ -241,7 +223,9 @@ const triggerMcpConfiguration = async (ide: ConsumerConfig, projectPath: string)
241
223
] )
242
224
243
225
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
+ )
245
229
return false
246
230
}
247
231
@@ -289,7 +273,7 @@ const fetchProjectInfo = async (url: string): Promise<ProjectInfo> => {
289
273
} )
290
274
291
275
if ( ! response . ok ) {
292
- throw new Error ( `HTTP error! status : ${ String ( response . status ) } ` )
276
+ throw new Error ( `Failed to fetch project information : ${ response . statusText } ` )
293
277
}
294
278
const data = ( await response . text ( ) ) as unknown as string
295
279
const parsedData = JSON . parse ( data ) as unknown as ProjectInfo
@@ -346,7 +330,6 @@ export const initWithAiRules = async (hash: string, command: BaseCommand): Promi
346
330
const { api } = command . netlify
347
331
348
332
log ( `${ chalk . blue ( 'π€ Initializing AI project' ) } with rules...` )
349
- log ( `${ chalk . gray ( 'Hash:' ) } ${ hash } ` )
350
333
log ( `${ chalk . gray ( 'User:' ) } ${ api . accessToken ? 'Authenticated β
' : 'Not authenticated β' } ` )
351
334
352
335
try {
@@ -399,7 +382,7 @@ export const initWithAiRules = async (hash: string, command: BaseCommand): Promi
399
382
let mcpConfigured = false
400
383
401
384
if ( detectedIDE ) {
402
- log ( `${ chalk . green ( 'β
' ) } Detected IDE : ${ chalk . cyan ( detectedIDE . presentedName ) } ` )
385
+ log ( `${ chalk . green ( 'β
' ) } Detected development environment : ${ chalk . cyan ( detectedIDE . presentedName ) } ` )
403
386
mcpConfigured = await triggerMcpConfiguration ( detectedIDE , targetDir )
404
387
}
405
388
@@ -423,7 +406,7 @@ export const initWithAiRules = async (hash: string, command: BaseCommand): Promi
423
406
if ( mcpConfigured ) {
424
407
log ( chalk . yellowBright ( `π§ Step 2: MCP Server Configured` ) )
425
408
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 ' ) } ` )
427
410
} else {
428
411
log ( chalk . yellowBright ( `π§ Step 2: Manual MCP Configuration` ) )
429
412
log ( ` ${ chalk . cyan ( detectedIDE . key ) } detected - MCP setup was skipped` )
0 commit comments