@stacksfinder/mcp-server 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -1,287 +1,328 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { z } from 'zod';
3
- import { DATA_VERSION, CATEGORIES, CONTEXTS } from './data/index.js';
4
- import { listTechsToolDefinition, executeListTechs, ListTechsInputSchema } from './tools/list-techs.js';
5
- import { analyzeTechToolDefinition, executeAnalyzeTech, AnalyzeTechInputSchema } from './tools/analyze.js';
6
- import { compareTechsToolDefinition, executeCompareTechs, CompareTechsInputSchema } from './tools/compare.js';
7
- import { recommendStackToolDefinition, executeRecommendStack, RecommendStackInputSchema } from './tools/recommend.js';
8
- import { getBlueprintToolDefinition, executeGetBlueprint, GetBlueprintInputSchema, createBlueprintToolDefinition, executeCreateBlueprint, CreateBlueprintInputSchema } from './tools/blueprint.js';
9
- import { recommendStackDemoToolDefinition, executeRecommendStackDemo, RecommendStackDemoInputSchema } from './tools/recommend-demo.js';
10
- import { setupApiKeyToolDefinition, executeSetupApiKey, SetupApiKeyInputSchema, listApiKeysToolDefinition, executeListApiKeys, revokeApiKeyToolDefinition, executeRevokeApiKey, RevokeApiKeyInputSchema, createApiKeyToolDefinition, executeCreateApiKey, CreateApiKeyInputSchema } from './tools/api-keys.js';
11
- import { createAuditToolDefinition, executeCreateAudit, CreateAuditInputSchema, getAuditToolDefinition, executeGetAudit, GetAuditInputSchema, listAuditsToolDefinition, executeListAudits, ListAuditsInputSchema, compareAuditsToolDefinition, executeCompareAudits, CompareAuditsInputSchema, getAuditQuotaToolDefinition, executeGetAuditQuota, getMigrationRecommendationToolDefinition, executeGetMigrationRecommendation, GetMigrationRecommendationInputSchema } from './tools/audit.js';
12
- import { generateMCPKitTool, generateMCPKit, GenerateMCPKitInputSchema, analyzeRepoMcpsTool, analyzeRepo, AnalyzeRepoMCPsInputSchema, PRIORITIES, PROJECT_TYPES, SCALES } from './tools/project-kit/index.js';
13
- import { prepareMCPInstallationTool, prepareMCPInstallation } from './tools/project-kit/prepare-installation.js';
14
- import { executeMCPInstallationTool, executeMCPInstallation } from './tools/project-kit/execute-installation.js';
15
- import { PrepareMCPInstallationInputSchema, ExecuteMCPInstallationInputSchema } from './tools/project-kit/installation-types.js';
16
- import { checkCompatibilityToolDefinition, executeCheckCompatibility, CheckCompatibilityInputSchema } from './tools/check-compatibility.js';
17
- import { getWorkflowGuideToolDefinition, executeGetWorkflowGuide, GetWorkflowGuideInputSchema, WORKFLOW_GOALS, WORKFLOW_CONTEXTS, USER_TIERS } from './tools/workflow-guide.js';
18
- import { estimateProjectToolDefinition, executeEstimateProject, EstimateProjectInputSchema, getEstimateQuotaToolDefinition, executeGetEstimateQuota } from './tools/estimator.js';
19
- import { info, debug } from './utils/logger.js';
20
- import { listTechnologiesAnnotations, analyzeTechAnnotations, compareTechsAnnotations, recommendStackDemoAnnotations, recommendStackAnnotations, getBlueprintAnnotations, createBlueprintAnnotations, setupApiKeyAnnotations, listApiKeysAnnotations, revokeApiKeyAnnotations, createApiKeyAnnotations, createAuditAnnotations, getAuditAnnotations, listAuditsAnnotations, compareAuditsAnnotations, getAuditQuotaAnnotations, getMigrationRecommendationAnnotations, generateMcpKitAnnotations, analyzeRepoMcpsAnnotations, prepareMcpInstallationAnnotations, executeMcpInstallationAnnotations, checkCompatibilityAnnotations, estimateProjectAnnotations, getEstimateQuotaAnnotations, getWorkflowGuideAnnotations } from './annotations.js';
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { DATA_VERSION, CATEGORIES, CONTEXTS } from "./data/index.js";
4
+ import { listTechsToolDefinition, executeListTechs, ListTechsInputSchema, } from "./tools/list-techs.js";
5
+ import { analyzeTechToolDefinition, executeAnalyzeTech, AnalyzeTechInputSchema, } from "./tools/analyze.js";
6
+ import { compareTechsToolDefinition, executeCompareTechs, CompareTechsInputSchema, } from "./tools/compare.js";
7
+ import { recommendStackToolDefinition, executeRecommendStack, RecommendStackInputSchema, } from "./tools/recommend.js";
8
+ import { getBlueprintToolDefinition, executeGetBlueprint, GetBlueprintInputSchema, createBlueprintToolDefinition, executeCreateBlueprint, CreateBlueprintInputSchema, } from "./tools/blueprint.js";
9
+ import { recommendStackDemoToolDefinition, executeRecommendStackDemo, RecommendStackDemoInputSchema, } from "./tools/recommend-demo.js";
10
+ import { setupApiKeyToolDefinition, executeSetupApiKey, SetupApiKeyInputSchema, listApiKeysToolDefinition, executeListApiKeys, revokeApiKeyToolDefinition, executeRevokeApiKey, RevokeApiKeyInputSchema, createApiKeyToolDefinition, executeCreateApiKey, CreateApiKeyInputSchema, } from "./tools/api-keys.js";
11
+ import { createAuditToolDefinition, executeCreateAudit, CreateAuditInputSchema, getAuditToolDefinition, executeGetAudit, GetAuditInputSchema, listAuditsToolDefinition, executeListAudits, ListAuditsInputSchema, compareAuditsToolDefinition, executeCompareAudits, CompareAuditsInputSchema, getAuditQuotaToolDefinition, executeGetAuditQuota, getMigrationRecommendationToolDefinition, executeGetMigrationRecommendation, GetMigrationRecommendationInputSchema, importBetterTStackToolDefinition, executeImportBetterTStack, ImportBetterTStackInputSchema, } from "./tools/audit.js";
12
+ import { generateMCPKitTool, generateMCPKit, GenerateMCPKitInputSchema, analyzeRepoMcpsTool, analyzeRepo, AnalyzeRepoMCPsInputSchema, PRIORITIES, PROJECT_TYPES, SCALES, } from "./tools/project-kit/index.js";
13
+ import { prepareMCPInstallationTool, prepareMCPInstallation, } from "./tools/project-kit/prepare-installation.js";
14
+ import { executeMCPInstallationTool, executeMCPInstallation, } from "./tools/project-kit/execute-installation.js";
15
+ import { PrepareMCPInstallationInputSchema, ExecuteMCPInstallationInputSchema, } from "./tools/project-kit/installation-types.js";
16
+ import { checkCompatibilityToolDefinition, executeCheckCompatibility, CheckCompatibilityInputSchema, } from "./tools/check-compatibility.js";
17
+ import { getWorkflowGuideToolDefinition, executeGetWorkflowGuide, GetWorkflowGuideInputSchema, WORKFLOW_GOALS, WORKFLOW_CONTEXTS, USER_TIERS, } from "./tools/workflow-guide.js";
18
+ import { estimateProjectToolDefinition, executeEstimateProject, EstimateProjectInputSchema, getEstimateQuotaToolDefinition, executeGetEstimateQuota, } from "./tools/estimator.js";
19
+ import { info, debug } from "./utils/logger.js";
20
+ import { listTechnologiesAnnotations, analyzeTechAnnotations, compareTechsAnnotations, recommendStackDemoAnnotations, recommendStackAnnotations, getBlueprintAnnotations, createBlueprintAnnotations, setupApiKeyAnnotations, listApiKeysAnnotations, revokeApiKeyAnnotations, createApiKeyAnnotations, createAuditAnnotations, getAuditAnnotations, listAuditsAnnotations, compareAuditsAnnotations, getAuditQuotaAnnotations, getMigrationRecommendationAnnotations, generateMcpKitAnnotations, analyzeRepoMcpsAnnotations, prepareMcpInstallationAnnotations, executeMcpInstallationAnnotations, checkCompatibilityAnnotations, estimateProjectAnnotations, getEstimateQuotaAnnotations, getWorkflowGuideAnnotations, } from "./annotations.js";
21
21
  /**
22
22
  * Create and configure the MCP server.
23
23
  */
24
24
  export function createServer() {
25
25
  const server = new McpServer({
26
- name: 'stacksfinder',
27
- version: '1.0.0'
26
+ name: "stacksfinder",
27
+ version: "1.0.0",
28
28
  });
29
29
  info(`StacksFinder MCP Server v1.0.0 (data version: ${DATA_VERSION})`);
30
30
  // Register list_technologies tool (local, discovery)
31
31
  server.registerTool(listTechsToolDefinition.name, {
32
- title: 'List Technologies',
32
+ title: "List Technologies",
33
33
  description: listTechsToolDefinition.description,
34
34
  inputSchema: {
35
- category: z.enum(CATEGORIES).optional().describe('Filter by category')
35
+ category: z.enum(CATEGORIES).optional().describe("Filter by category"),
36
36
  },
37
- annotations: listTechnologiesAnnotations
37
+ annotations: listTechnologiesAnnotations,
38
38
  }, async (args) => {
39
- debug('list_technologies called', args);
39
+ debug("list_technologies called", args);
40
40
  const input = ListTechsInputSchema.parse(args);
41
41
  const text = executeListTechs(input);
42
42
  return {
43
- content: [{ type: 'text', text }]
43
+ content: [{ type: "text", text }],
44
44
  };
45
45
  });
46
46
  // Register analyze_tech tool (local)
47
47
  server.registerTool(analyzeTechToolDefinition.name, {
48
- title: 'Analyze Technology',
48
+ title: "Analyze Technology",
49
49
  description: analyzeTechToolDefinition.description,
50
50
  inputSchema: {
51
- technology: z.string().min(1).describe('Technology ID to analyze'),
52
- context: z.enum(CONTEXTS).optional().describe('Context for scoring')
51
+ technology: z.string().min(1).describe("Technology ID to analyze"),
52
+ context: z.enum(CONTEXTS).optional().describe("Context for scoring"),
53
53
  },
54
- annotations: analyzeTechAnnotations
54
+ annotations: analyzeTechAnnotations,
55
55
  }, async (args) => {
56
- debug('analyze_tech called', args);
56
+ debug("analyze_tech called", args);
57
57
  const input = AnalyzeTechInputSchema.parse(args);
58
58
  const { text, isError } = executeAnalyzeTech(input);
59
59
  return {
60
- content: [{ type: 'text', text }],
61
- isError
60
+ content: [{ type: "text", text }],
61
+ isError,
62
62
  };
63
63
  });
64
64
  // Register compare_techs tool (local)
65
65
  server.registerTool(compareTechsToolDefinition.name, {
66
- title: 'Compare Technologies',
66
+ title: "Compare Technologies",
67
67
  description: compareTechsToolDefinition.description,
68
68
  inputSchema: {
69
- technologies: z.array(z.string().min(1)).min(2).max(4).describe('Technologies to compare'),
70
- context: z.enum(CONTEXTS).optional().describe('Context for scoring')
69
+ technologies: z
70
+ .array(z.string().min(1))
71
+ .min(2)
72
+ .max(4)
73
+ .describe("Technologies to compare"),
74
+ context: z.enum(CONTEXTS).optional().describe("Context for scoring"),
71
75
  },
72
- annotations: compareTechsAnnotations
76
+ annotations: compareTechsAnnotations,
73
77
  }, async (args) => {
74
- debug('compare_techs called', args);
78
+ debug("compare_techs called", args);
75
79
  const input = CompareTechsInputSchema.parse(args);
76
80
  const { text, isError } = executeCompareTechs(input);
77
81
  return {
78
- content: [{ type: 'text', text }],
79
- isError
82
+ content: [{ type: "text", text }],
83
+ isError,
80
84
  };
81
85
  });
82
86
  // Register recommend_stack_demo tool (FREE, local scoring, 1/day limit)
83
87
  server.registerTool(recommendStackDemoToolDefinition.name, {
84
- title: 'Recommend Stack (Demo)',
88
+ title: "Recommend Stack (Demo)",
85
89
  description: recommendStackDemoToolDefinition.description,
86
90
  inputSchema: {
87
91
  projectType: z
88
92
  .enum([
89
- 'web-app',
90
- 'mobile-app',
91
- 'api',
92
- 'desktop',
93
- 'cli',
94
- 'library',
95
- 'e-commerce',
96
- 'saas',
97
- 'marketplace'
93
+ "web-app",
94
+ "mobile-app",
95
+ "api",
96
+ "desktop",
97
+ "cli",
98
+ "library",
99
+ "e-commerce",
100
+ "saas",
101
+ "marketplace",
98
102
  ])
99
- .describe('Type of project'),
100
- scale: z.enum(['mvp', 'startup', 'growth', 'enterprise']).optional().describe('Project scale')
103
+ .describe("Type of project"),
104
+ scale: z
105
+ .enum(["mvp", "startup", "growth", "enterprise"])
106
+ .optional()
107
+ .describe("Project scale"),
101
108
  },
102
- annotations: recommendStackDemoAnnotations
109
+ annotations: recommendStackDemoAnnotations,
103
110
  }, async (args) => {
104
- debug('recommend_stack_demo called', args);
111
+ debug("recommend_stack_demo called", args);
105
112
  const input = RecommendStackDemoInputSchema.parse(args);
106
113
  const { text, isError } = executeRecommendStackDemo(input);
107
114
  return {
108
- content: [{ type: 'text', text }],
109
- isError
115
+ content: [{ type: "text", text }],
116
+ isError,
110
117
  };
111
118
  });
112
119
  // Register recommend_stack tool (API-based, requires API key)
113
120
  server.registerTool(recommendStackToolDefinition.name, {
114
- title: 'Recommend Stack',
121
+ title: "Recommend Stack",
115
122
  description: recommendStackToolDefinition.description,
116
123
  inputSchema: {
117
124
  projectType: z
118
125
  .enum([
119
- 'web-app',
120
- 'mobile-app',
121
- 'api',
122
- 'desktop',
123
- 'cli',
124
- 'library',
125
- 'e-commerce',
126
- 'saas',
127
- 'marketplace'
126
+ "web-app",
127
+ "mobile-app",
128
+ "api",
129
+ "desktop",
130
+ "cli",
131
+ "library",
132
+ "e-commerce",
133
+ "saas",
134
+ "marketplace",
128
135
  ])
129
- .describe('Type of project'),
130
- scale: z.enum(['mvp', 'startup', 'growth', 'enterprise']).optional().describe('Project scale'),
136
+ .describe("Type of project"),
137
+ scale: z
138
+ .enum(["mvp", "startup", "growth", "enterprise"])
139
+ .optional()
140
+ .describe("Project scale"),
131
141
  priorities: z
132
142
  .array(z.enum([
133
- 'time-to-market',
134
- 'scalability',
135
- 'developer-experience',
136
- 'cost-efficiency',
137
- 'performance',
138
- 'security',
139
- 'maintainability'
143
+ "time-to-market",
144
+ "scalability",
145
+ "developer-experience",
146
+ "cost-efficiency",
147
+ "performance",
148
+ "security",
149
+ "maintainability",
140
150
  ]))
141
151
  .max(3)
142
152
  .optional()
143
- .describe('Top priorities (max 3)'),
144
- constraints: z.array(z.string()).optional().describe('Project constraints')
153
+ .describe("Top priorities (max 3)"),
154
+ constraints: z
155
+ .array(z.string())
156
+ .optional()
157
+ .describe("Project constraints"),
145
158
  },
146
- annotations: recommendStackAnnotations
159
+ annotations: recommendStackAnnotations,
147
160
  }, async (args) => {
148
- debug('recommend_stack called', args);
161
+ debug("recommend_stack called", args);
149
162
  const input = RecommendStackInputSchema.parse(args);
150
163
  const { text, isError } = await executeRecommendStack(input);
151
164
  return {
152
- content: [{ type: 'text', text }],
153
- isError
165
+ content: [{ type: "text", text }],
166
+ isError,
154
167
  };
155
168
  });
156
169
  // Register get_blueprint tool (API-based)
157
170
  server.registerTool(getBlueprintToolDefinition.name, {
158
- title: 'Get Blueprint',
171
+ title: "Get Blueprint",
159
172
  description: getBlueprintToolDefinition.description,
160
173
  inputSchema: {
161
- blueprintId: z.string().uuid().describe('Blueprint UUID')
174
+ blueprintId: z.string().uuid().describe("Blueprint UUID"),
162
175
  },
163
- annotations: getBlueprintAnnotations
176
+ annotations: getBlueprintAnnotations,
164
177
  }, async (args) => {
165
- debug('get_blueprint called', args);
178
+ debug("get_blueprint called", args);
166
179
  const input = GetBlueprintInputSchema.parse(args);
167
180
  const { text, isError } = await executeGetBlueprint(input);
168
181
  return {
169
- content: [{ type: 'text', text }],
170
- isError
182
+ content: [{ type: "text", text }],
183
+ isError,
171
184
  };
172
185
  });
173
186
  // Register create_blueprint tool (API-based, requires API key with blueprint:write)
174
187
  server.registerTool(createBlueprintToolDefinition.name, {
175
- title: 'Create Blueprint',
188
+ title: "Create Blueprint",
176
189
  description: createBlueprintToolDefinition.description,
177
190
  inputSchema: {
178
- projectName: z.string().max(100).optional().describe('Project name (optional)'),
191
+ projectName: z
192
+ .string()
193
+ .max(100)
194
+ .optional()
195
+ .describe("Project name (optional)"),
179
196
  projectType: z
180
197
  .enum([
181
- 'web-app',
182
- 'mobile-app',
183
- 'api',
184
- 'desktop',
185
- 'cli',
186
- 'library',
187
- 'e-commerce',
188
- 'saas',
189
- 'marketplace'
198
+ "web-app",
199
+ "mobile-app",
200
+ "api",
201
+ "desktop",
202
+ "cli",
203
+ "library",
204
+ "e-commerce",
205
+ "saas",
206
+ "marketplace",
190
207
  ])
191
- .describe('Type of project'),
192
- scale: z.enum(['mvp', 'startup', 'growth', 'enterprise']).describe('Project scale'),
193
- projectDescription: z.string().max(2000).optional().describe('Brief description (optional)'),
208
+ .describe("Type of project"),
209
+ scale: z
210
+ .enum(["mvp", "startup", "growth", "enterprise"])
211
+ .describe("Project scale"),
212
+ projectDescription: z
213
+ .string()
214
+ .max(2000)
215
+ .optional()
216
+ .describe("Brief description (optional)"),
194
217
  priorities: z
195
218
  .array(z.enum([
196
- 'time-to-market',
197
- 'scalability',
198
- 'developer-experience',
199
- 'cost-efficiency',
200
- 'performance',
201
- 'security',
202
- 'maintainability'
219
+ "time-to-market",
220
+ "scalability",
221
+ "developer-experience",
222
+ "cost-efficiency",
223
+ "performance",
224
+ "security",
225
+ "maintainability",
203
226
  ]))
204
227
  .max(3)
205
228
  .optional()
206
- .describe('Top 3 priorities (optional)'),
207
- constraints: z.array(z.string()).max(20).optional().describe('Technology constraint IDs (optional)'),
208
- waitForCompletion: z.boolean().optional().describe('Wait for completion (default: true)')
229
+ .describe("Top 3 priorities (optional)"),
230
+ constraints: z
231
+ .array(z.string())
232
+ .max(20)
233
+ .optional()
234
+ .describe("Technology constraint IDs (optional)"),
235
+ waitForCompletion: z
236
+ .boolean()
237
+ .optional()
238
+ .describe("Wait for completion (default: true)"),
209
239
  },
210
- annotations: createBlueprintAnnotations
240
+ annotations: createBlueprintAnnotations,
211
241
  }, async (args) => {
212
- debug('create_blueprint called', args);
242
+ debug("create_blueprint called", args);
213
243
  const input = CreateBlueprintInputSchema.parse(args);
214
244
  const { text, isError } = await executeCreateBlueprint(input);
215
245
  return {
216
- content: [{ type: 'text', text }],
217
- isError
246
+ content: [{ type: "text", text }],
247
+ isError,
218
248
  };
219
249
  });
220
250
  // Register setup_api_key tool (API-based, no auth required)
221
251
  server.registerTool(setupApiKeyToolDefinition.name, {
222
- title: 'Setup API Key',
252
+ title: "Setup API Key",
223
253
  description: setupApiKeyToolDefinition.description,
224
254
  inputSchema: {
225
- email: z.string().email().describe('Your StacksFinder account email'),
226
- password: z.string().min(1).describe('Your StacksFinder account password'),
227
- keyName: z.string().max(100).optional().describe('Optional name for the API key')
255
+ email: z.string().email().describe("Your StacksFinder account email"),
256
+ password: z
257
+ .string()
258
+ .min(1)
259
+ .describe("Your StacksFinder account password"),
260
+ keyName: z
261
+ .string()
262
+ .max(100)
263
+ .optional()
264
+ .describe("Optional name for the API key"),
228
265
  },
229
- annotations: setupApiKeyAnnotations
266
+ annotations: setupApiKeyAnnotations,
230
267
  }, async (args) => {
231
- debug('setup_api_key called', args.email);
268
+ debug("setup_api_key called", args.email);
232
269
  const input = SetupApiKeyInputSchema.parse(args);
233
270
  const { text, isError } = await executeSetupApiKey(input);
234
271
  return {
235
- content: [{ type: 'text', text }],
236
- isError
272
+ content: [{ type: "text", text }],
273
+ isError,
237
274
  };
238
275
  });
239
276
  // Register list_api_keys tool (API-based, requires API key)
240
277
  server.registerTool(listApiKeysToolDefinition.name, {
241
- title: 'List API Keys',
278
+ title: "List API Keys",
242
279
  description: listApiKeysToolDefinition.description,
243
280
  inputSchema: {},
244
- annotations: listApiKeysAnnotations
281
+ annotations: listApiKeysAnnotations,
245
282
  }, async () => {
246
- debug('list_api_keys called');
283
+ debug("list_api_keys called");
247
284
  const { text, isError } = await executeListApiKeys();
248
285
  return {
249
- content: [{ type: 'text', text }],
250
- isError
286
+ content: [{ type: "text", text }],
287
+ isError,
251
288
  };
252
289
  });
253
290
  // Register revoke_api_key tool (API-based, requires API key)
254
291
  server.registerTool(revokeApiKeyToolDefinition.name, {
255
- title: 'Revoke API Key',
292
+ title: "Revoke API Key",
256
293
  description: revokeApiKeyToolDefinition.description,
257
294
  inputSchema: {
258
- keyId: z.string().uuid().describe('The UUID of the API key to revoke')
295
+ keyId: z.string().uuid().describe("The UUID of the API key to revoke"),
259
296
  },
260
- annotations: revokeApiKeyAnnotations
297
+ annotations: revokeApiKeyAnnotations,
261
298
  }, async (args) => {
262
- debug('revoke_api_key called', args.keyId);
299
+ debug("revoke_api_key called", args.keyId);
263
300
  const input = RevokeApiKeyInputSchema.parse(args);
264
301
  const { text, isError } = await executeRevokeApiKey(input);
265
302
  return {
266
- content: [{ type: 'text', text }],
267
- isError
303
+ content: [{ type: "text", text }],
304
+ isError,
268
305
  };
269
306
  });
270
307
  // Register create_api_key tool (OAuth-based, no email/password required)
271
308
  server.registerTool(createApiKeyToolDefinition.name, {
272
- title: 'Create API Key',
309
+ title: "Create API Key",
273
310
  description: createApiKeyToolDefinition.description,
274
311
  inputSchema: {
275
- keyName: z.string().max(100).optional().describe('Optional name for the API key')
312
+ keyName: z
313
+ .string()
314
+ .max(100)
315
+ .optional()
316
+ .describe("Optional name for the API key"),
276
317
  },
277
- annotations: createApiKeyAnnotations
318
+ annotations: createApiKeyAnnotations,
278
319
  }, async (args) => {
279
- debug('create_api_key called');
320
+ debug("create_api_key called");
280
321
  const input = CreateApiKeyInputSchema.parse(args);
281
322
  const { text, isError } = await executeCreateApiKey(input);
282
323
  return {
283
- content: [{ type: 'text', text }],
284
- isError
324
+ content: [{ type: "text", text }],
325
+ isError,
285
326
  };
286
327
  });
287
328
  // ========================================================================
@@ -289,112 +330,141 @@ export function createServer() {
289
330
  // ========================================================================
290
331
  // Register create_audit tool (API-based, requires API key with audit:write)
291
332
  server.registerTool(createAuditToolDefinition.name, {
292
- title: 'Create Technical Debt Audit',
333
+ title: "Create Technical Debt Audit",
293
334
  description: createAuditToolDefinition.description,
294
335
  inputSchema: {
295
- name: z.string().min(1).max(200).describe('Name for the audit report'),
336
+ name: z.string().min(1).max(200).describe("Name for the audit report"),
296
337
  technologies: z
297
338
  .array(z.object({
298
- name: z.string().min(1).describe('Technology name'),
299
- version: z.string().optional().describe('Version string'),
300
- category: z.string().optional().describe('Category')
339
+ name: z.string().min(1).describe("Technology name"),
340
+ version: z.string().optional().describe("Version string"),
341
+ category: z.string().optional().describe("Category"),
301
342
  }))
302
343
  .min(1)
303
344
  .max(50)
304
- .describe('Technologies to audit')
345
+ .describe("Technologies to audit"),
305
346
  },
306
- annotations: createAuditAnnotations
347
+ annotations: createAuditAnnotations,
307
348
  }, async (args) => {
308
- debug('create_audit called', args);
349
+ debug("create_audit called", args);
309
350
  const input = CreateAuditInputSchema.parse(args);
310
351
  const { text, isError } = await executeCreateAudit(input);
311
352
  return {
312
- content: [{ type: 'text', text }],
313
- isError
353
+ content: [{ type: "text", text }],
354
+ isError,
314
355
  };
315
356
  });
316
357
  // Register get_audit tool (API-based, requires API key with audit:read)
317
358
  server.registerTool(getAuditToolDefinition.name, {
318
- title: 'Get Audit Report',
359
+ title: "Get Audit Report",
319
360
  description: getAuditToolDefinition.description,
320
361
  inputSchema: {
321
- auditId: z.string().uuid().describe('Audit report UUID')
362
+ auditId: z.string().uuid().describe("Audit report UUID"),
322
363
  },
323
- annotations: getAuditAnnotations
364
+ annotations: getAuditAnnotations,
324
365
  }, async (args) => {
325
- debug('get_audit called', args);
366
+ debug("get_audit called", args);
326
367
  const input = GetAuditInputSchema.parse(args);
327
368
  const { text, isError } = await executeGetAudit(input);
328
369
  return {
329
- content: [{ type: 'text', text }],
330
- isError
370
+ content: [{ type: "text", text }],
371
+ isError,
331
372
  };
332
373
  });
333
374
  // Register list_audits tool (API-based, requires API key with audit:read)
334
375
  server.registerTool(listAuditsToolDefinition.name, {
335
- title: 'List Audit Reports',
376
+ title: "List Audit Reports",
336
377
  description: listAuditsToolDefinition.description,
337
378
  inputSchema: {
338
- limit: z.number().min(1).max(50).optional().describe('Max results'),
339
- offset: z.number().min(0).optional().describe('Pagination offset')
379
+ limit: z.number().min(1).max(50).optional().describe("Max results"),
380
+ offset: z.number().min(0).optional().describe("Pagination offset"),
340
381
  },
341
- annotations: listAuditsAnnotations
382
+ annotations: listAuditsAnnotations,
342
383
  }, async (args) => {
343
- debug('list_audits called', args);
384
+ debug("list_audits called", args);
344
385
  const input = ListAuditsInputSchema.parse(args);
345
386
  const { text, isError } = await executeListAudits(input);
346
387
  return {
347
- content: [{ type: 'text', text }],
348
- isError
388
+ content: [{ type: "text", text }],
389
+ isError,
349
390
  };
350
391
  });
351
392
  // Register compare_audits tool (API-based, requires API key with audit:read)
352
393
  server.registerTool(compareAuditsToolDefinition.name, {
353
- title: 'Compare Audit Reports',
394
+ title: "Compare Audit Reports",
354
395
  description: compareAuditsToolDefinition.description,
355
396
  inputSchema: {
356
- baseAuditId: z.string().uuid().describe('Base (older) audit ID'),
357
- compareAuditId: z.string().uuid().describe('Compare (newer) audit ID')
397
+ baseAuditId: z.string().uuid().describe("Base (older) audit ID"),
398
+ compareAuditId: z.string().uuid().describe("Compare (newer) audit ID"),
358
399
  },
359
- annotations: compareAuditsAnnotations
400
+ annotations: compareAuditsAnnotations,
360
401
  }, async (args) => {
361
- debug('compare_audits called', args);
402
+ debug("compare_audits called", args);
362
403
  const input = CompareAuditsInputSchema.parse(args);
363
404
  const { text, isError } = await executeCompareAudits(input);
364
405
  return {
365
- content: [{ type: 'text', text }],
366
- isError
406
+ content: [{ type: "text", text }],
407
+ isError,
367
408
  };
368
409
  });
369
410
  // Register get_audit_quota tool (API-based, requires API key)
370
411
  server.registerTool(getAuditQuotaToolDefinition.name, {
371
- title: 'Get Audit Quota',
412
+ title: "Get Audit Quota",
372
413
  description: getAuditQuotaToolDefinition.description,
373
414
  inputSchema: {},
374
- annotations: getAuditQuotaAnnotations
415
+ annotations: getAuditQuotaAnnotations,
375
416
  }, async () => {
376
- debug('get_audit_quota called');
417
+ debug("get_audit_quota called");
377
418
  const { text, isError } = await executeGetAuditQuota();
378
419
  return {
379
- content: [{ type: 'text', text }],
380
- isError
420
+ content: [{ type: "text", text }],
421
+ isError,
381
422
  };
382
423
  });
383
424
  // Register get_migration_recommendation tool (API-based, requires API key with audit:read)
384
425
  server.registerTool(getMigrationRecommendationToolDefinition.name, {
385
- title: 'Get Migration Recommendation',
426
+ title: "Get Migration Recommendation",
386
427
  description: getMigrationRecommendationToolDefinition.description,
387
428
  inputSchema: {
388
- auditId: z.string().uuid().describe('Audit report UUID to analyze for migration')
429
+ auditId: z
430
+ .string()
431
+ .uuid()
432
+ .describe("Audit report UUID to analyze for migration"),
389
433
  },
390
- annotations: getMigrationRecommendationAnnotations
434
+ annotations: getMigrationRecommendationAnnotations,
391
435
  }, async (args) => {
392
- debug('get_migration_recommendation called', args);
436
+ debug("get_migration_recommendation called", args);
393
437
  const input = GetMigrationRecommendationInputSchema.parse(args);
394
438
  const { text, isError } = await executeGetMigrationRecommendation(input);
395
439
  return {
396
- content: [{ type: 'text', text }],
397
- isError
440
+ content: [{ type: "text", text }],
441
+ isError,
442
+ };
443
+ });
444
+ // Register import_better_t_stack tool (API-based, requires API key with audit:write)
445
+ server.registerTool(importBetterTStackToolDefinition.name, {
446
+ title: "Import Better-T-Stack Project",
447
+ description: importBetterTStackToolDefinition.description,
448
+ inputSchema: {
449
+ type: z.enum(["github", "package-json"]).describe("Import source type"),
450
+ url: z
451
+ .string()
452
+ .optional()
453
+ .describe("GitHub repository URL (for type=github)"),
454
+ content: z
455
+ .string()
456
+ .optional()
457
+ .describe("Raw package.json content (for type=package-json)"),
458
+ name: z.string().max(200).optional().describe("Custom audit name"),
459
+ },
460
+ annotations: createAuditAnnotations, // Reuse audit annotations
461
+ }, async (args) => {
462
+ debug("import_better_t_stack called", args);
463
+ const input = ImportBetterTStackInputSchema.parse(args);
464
+ const { text, isError } = await executeImportBetterTStack(input);
465
+ return {
466
+ content: [{ type: "text", text }],
467
+ isError,
398
468
  };
399
469
  });
400
470
  // ========================================================================
@@ -402,81 +472,133 @@ export function createServer() {
402
472
  // ========================================================================
403
473
  // Register generate_mcp_kit tool (local, no API key required)
404
474
  server.registerTool(generateMCPKitTool.name, {
405
- title: 'Generate MCP Kit',
475
+ title: "Generate MCP Kit",
406
476
  description: generateMCPKitTool.description,
407
477
  inputSchema: {
408
- projectDescription: z.string().min(50).max(5000).describe('Describe your project (50-5000 chars)'),
409
- priorities: z.array(z.enum(PRIORITIES)).max(3).optional().describe('Top priorities (max 3)'),
410
- constraints: z.array(z.string()).optional().describe('Tech constraints (e.g., must-use-postgresql)'),
411
- projectType: z.enum(PROJECT_TYPES).optional().describe('Project type (if known)'),
412
- scale: z.enum(SCALES).optional().describe('Project scale (if known)')
478
+ projectDescription: z
479
+ .string()
480
+ .min(50)
481
+ .max(5000)
482
+ .describe("Describe your project (50-5000 chars)"),
483
+ priorities: z
484
+ .array(z.enum(PRIORITIES))
485
+ .max(3)
486
+ .optional()
487
+ .describe("Top priorities (max 3)"),
488
+ constraints: z
489
+ .array(z.string())
490
+ .optional()
491
+ .describe("Tech constraints (e.g., must-use-postgresql)"),
492
+ projectType: z
493
+ .enum(PROJECT_TYPES)
494
+ .optional()
495
+ .describe("Project type (if known)"),
496
+ scale: z.enum(SCALES).optional().describe("Project scale (if known)"),
413
497
  },
414
- annotations: generateMcpKitAnnotations
498
+ annotations: generateMcpKitAnnotations,
415
499
  }, async (args) => {
416
- debug('generate_mcp_kit called', args);
500
+ debug("generate_mcp_kit called", args);
417
501
  const input = GenerateMCPKitInputSchema.parse(args);
418
502
  const result = generateMCPKit(input);
419
503
  return {
420
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
504
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
421
505
  };
422
506
  });
423
507
  // Register analyze_repo_mcps tool (local, no API key required)
424
508
  server.registerTool(analyzeRepoMcpsTool.name, {
425
- title: 'Analyze Repository MCPs',
509
+ title: "Analyze Repository MCPs",
426
510
  description: analyzeRepoMcpsTool.description,
427
511
  inputSchema: {
428
- includeInstalled: z.boolean().optional().describe('Include already installed MCPs (default: false)'),
429
- mcpConfigPath: z.string().optional().describe('Override path to MCP configuration file'),
430
- workspaceRoot: z.string().optional().describe('Override workspace root directory (default: current directory)')
512
+ includeInstalled: z
513
+ .boolean()
514
+ .optional()
515
+ .describe("Include already installed MCPs (default: false)"),
516
+ mcpConfigPath: z
517
+ .string()
518
+ .optional()
519
+ .describe("Override path to MCP configuration file"),
520
+ workspaceRoot: z
521
+ .string()
522
+ .optional()
523
+ .describe("Override workspace root directory (default: current directory)"),
431
524
  },
432
- annotations: analyzeRepoMcpsAnnotations
525
+ annotations: analyzeRepoMcpsAnnotations,
433
526
  }, async (args) => {
434
- debug('analyze_repo_mcps called', args);
527
+ debug("analyze_repo_mcps called", args);
435
528
  const input = AnalyzeRepoMCPsInputSchema.parse(args);
436
529
  const result = await analyzeRepo(input);
437
530
  // Format as markdown for better readability
438
531
  return {
439
- content: [{ type: 'text', text: formatAnalysisResult(result) }]
532
+ content: [{ type: "text", text: formatAnalysisResult(result) }],
440
533
  };
441
534
  });
442
535
  // Register prepare_mcp_installation tool (local, no API key required)
443
536
  server.registerTool(prepareMCPInstallationTool.name, {
444
- title: 'Prepare MCP Installation',
537
+ title: "Prepare MCP Installation",
445
538
  description: prepareMCPInstallationTool.description,
446
539
  inputSchema: {
447
- workspaceRoot: z.string().optional().describe('Workspace root directory (default: current directory)'),
448
- mcpConfigPath: z.string().optional().describe('Override path to existing MCP configuration file'),
449
- includeInstalled: z.boolean().optional().describe('Include already installed MCPs in the preparation (default: false)'),
450
- envMcpPath: z.string().optional().describe('Path where .env-mcp will be created (default: .env-mcp in workspaceRoot)')
540
+ workspaceRoot: z
541
+ .string()
542
+ .optional()
543
+ .describe("Workspace root directory (default: current directory)"),
544
+ mcpConfigPath: z
545
+ .string()
546
+ .optional()
547
+ .describe("Override path to existing MCP configuration file"),
548
+ includeInstalled: z
549
+ .boolean()
550
+ .optional()
551
+ .describe("Include already installed MCPs in the preparation (default: false)"),
552
+ envMcpPath: z
553
+ .string()
554
+ .optional()
555
+ .describe("Path where .env-mcp will be created (default: .env-mcp in workspaceRoot)"),
451
556
  },
452
- annotations: prepareMcpInstallationAnnotations
557
+ annotations: prepareMcpInstallationAnnotations,
453
558
  }, async (args) => {
454
- debug('prepare_mcp_installation called', args);
559
+ debug("prepare_mcp_installation called", args);
455
560
  const input = PrepareMCPInstallationInputSchema.parse(args);
456
561
  const result = await prepareMCPInstallation(input);
457
562
  return {
458
- content: [{ type: 'text', text: result.message + '\n\n' + formatPreparationSummary(result) }]
563
+ content: [
564
+ {
565
+ type: "text",
566
+ text: result.message + "\n\n" + formatPreparationSummary(result),
567
+ },
568
+ ],
459
569
  };
460
570
  });
461
571
  // Register execute_mcp_installation tool (local, no API key required)
462
572
  server.registerTool(executeMCPInstallationTool.name, {
463
- title: 'Execute MCP Installation',
573
+ title: "Execute MCP Installation",
464
574
  description: executeMCPInstallationTool.description,
465
575
  inputSchema: {
466
- envMcpPath: z.string().optional().describe('Path to .env-mcp file (default: .env-mcp in current directory)'),
576
+ envMcpPath: z
577
+ .string()
578
+ .optional()
579
+ .describe("Path to .env-mcp file (default: .env-mcp in current directory)"),
467
580
  targetClient: z
468
- .enum(['claude-code', 'claude-desktop', 'cursor', 'vscode', 'windsurf'])
581
+ .enum([
582
+ "claude-code",
583
+ "claude-desktop",
584
+ "cursor",
585
+ "vscode",
586
+ "windsurf",
587
+ ])
469
588
  .optional()
470
- .describe('Target IDE/client for installation (default: claude-code)'),
471
- dryRun: z.boolean().optional().describe('Only generate commands without marking ready to execute (default: false)')
589
+ .describe("Target IDE/client for installation (default: claude-code)"),
590
+ dryRun: z
591
+ .boolean()
592
+ .optional()
593
+ .describe("Only generate commands without marking ready to execute (default: false)"),
472
594
  },
473
- annotations: executeMcpInstallationAnnotations
595
+ annotations: executeMcpInstallationAnnotations,
474
596
  }, async (args) => {
475
- debug('execute_mcp_installation called', args);
597
+ debug("execute_mcp_installation called", args);
476
598
  const input = ExecuteMCPInstallationInputSchema.parse(args);
477
599
  const result = await executeMCPInstallation(input);
478
600
  return {
479
- content: [{ type: 'text', text: formatExecutionResult(result) }]
601
+ content: [{ type: "text", text: formatExecutionResult(result) }],
480
602
  };
481
603
  });
482
604
  // ========================================================================
@@ -484,46 +606,64 @@ export function createServer() {
484
606
  // ========================================================================
485
607
  // Register check_mcp_compatibility tool (local, no API key required)
486
608
  server.registerTool(checkCompatibilityToolDefinition.name, {
487
- title: 'Check MCP Compatibility',
609
+ title: "Check MCP Compatibility",
488
610
  description: checkCompatibilityToolDefinition.description,
489
611
  inputSchema: {
490
612
  mcps: z
491
613
  .array(z.string().min(1))
492
614
  .min(1)
493
615
  .max(20)
494
- .describe('Array of MCP server IDs to check compatibility between')
616
+ .describe("Array of MCP server IDs to check compatibility between"),
495
617
  },
496
- annotations: checkCompatibilityAnnotations
618
+ annotations: checkCompatibilityAnnotations,
497
619
  }, async (args) => {
498
- debug('check_mcp_compatibility called', args);
620
+ debug("check_mcp_compatibility called", args);
499
621
  const input = CheckCompatibilityInputSchema.parse(args);
500
622
  const { text, data, isError } = executeCheckCompatibility(input);
501
623
  return {
502
624
  content: [
503
- { type: 'text', text },
504
- { type: 'text', text: `\n---\n\n**Structured Data:**\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\`` }
625
+ { type: "text", text },
626
+ {
627
+ type: "text",
628
+ text: `\n---\n\n**Structured Data:**\n\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\``,
629
+ },
505
630
  ],
506
- isError
631
+ isError,
507
632
  };
508
633
  });
509
634
  // Register get_workflow_guide tool (local, FREE, no API key required)
510
635
  server.registerTool(getWorkflowGuideToolDefinition.name, {
511
- title: 'Get Workflow Guide',
636
+ title: "Get Workflow Guide",
512
637
  description: getWorkflowGuideToolDefinition.description,
513
638
  inputSchema: {
514
- current_goal: z.enum(WORKFLOW_GOALS).optional().describe('What the user is trying to accomplish'),
515
- completed_tools: z.array(z.string()).optional().describe('Tools already called in this session'),
516
- user_tier: z.enum(USER_TIERS).optional().describe('User tier: free, pro, or unknown'),
517
- known_constraints: z.array(z.string()).optional().describe('Constraints like must_use_postgresql'),
518
- context: z.enum(WORKFLOW_CONTEXTS).optional().describe('Client context for adapted snippets')
639
+ current_goal: z
640
+ .enum(WORKFLOW_GOALS)
641
+ .optional()
642
+ .describe("What the user is trying to accomplish"),
643
+ completed_tools: z
644
+ .array(z.string())
645
+ .optional()
646
+ .describe("Tools already called in this session"),
647
+ user_tier: z
648
+ .enum(USER_TIERS)
649
+ .optional()
650
+ .describe("User tier: free, pro, or unknown"),
651
+ known_constraints: z
652
+ .array(z.string())
653
+ .optional()
654
+ .describe("Constraints like must_use_postgresql"),
655
+ context: z
656
+ .enum(WORKFLOW_CONTEXTS)
657
+ .optional()
658
+ .describe("Client context for adapted snippets"),
519
659
  },
520
- annotations: getWorkflowGuideAnnotations
660
+ annotations: getWorkflowGuideAnnotations,
521
661
  }, async (args) => {
522
- debug('get_workflow_guide called', args);
662
+ debug("get_workflow_guide called", args);
523
663
  const input = GetWorkflowGuideInputSchema.parse(args);
524
664
  const { text } = executeGetWorkflowGuide(input);
525
665
  return {
526
- content: [{ type: 'text', text }]
666
+ content: [{ type: "text", text }],
527
667
  };
528
668
  });
529
669
  // ========================================================================
@@ -531,50 +671,60 @@ export function createServer() {
531
671
  // ========================================================================
532
672
  // Register estimate_project tool (API-based, requires API key with estimate:write)
533
673
  server.registerTool(estimateProjectToolDefinition.name, {
534
- title: 'Estimate Project',
674
+ title: "Estimate Project",
535
675
  description: estimateProjectToolDefinition.description,
536
676
  inputSchema: {
537
677
  specs: z
538
678
  .string()
539
679
  .min(100)
540
680
  .max(10000)
541
- .describe('Project specifications (min 100 chars, max 10,000)'),
542
- teamSize: z.number().min(1).max(100).optional().describe('Number of developers'),
681
+ .describe("Project specifications (min 100 chars, max 10,000)"),
682
+ teamSize: z
683
+ .number()
684
+ .min(1)
685
+ .max(100)
686
+ .optional()
687
+ .describe("Number of developers"),
543
688
  seniorityLevel: z
544
- .enum(['junior', 'mid', 'senior', 'expert'])
689
+ .enum(["junior", "mid", "senior", "expert"])
545
690
  .optional()
546
- .describe('Average team seniority (default: mid)'),
691
+ .describe("Average team seniority (default: mid)"),
547
692
  region: z
548
- .enum(['france', 'us', 'uk', 'remote-global'])
693
+ .enum(["france", "us", "uk", "remote-global"])
694
+ .optional()
695
+ .describe("Region for pricing (default: france)"),
696
+ includeMarket: z
697
+ .boolean()
549
698
  .optional()
550
- .describe('Region for pricing (default: france)'),
551
- includeMarket: z.boolean().optional().describe('Include market analysis (default: true)')
699
+ .describe("Include market analysis (default: true)"),
552
700
  },
553
- annotations: estimateProjectAnnotations
701
+ annotations: estimateProjectAnnotations,
554
702
  }, async (args) => {
555
- debug('estimate_project called', { specsLength: args.specs?.length });
703
+ debug("estimate_project called", {
704
+ specsLength: args.specs?.length,
705
+ });
556
706
  const input = EstimateProjectInputSchema.parse(args);
557
707
  const { text, isError } = await executeEstimateProject(input);
558
708
  return {
559
- content: [{ type: 'text', text }],
560
- isError
709
+ content: [{ type: "text", text }],
710
+ isError,
561
711
  };
562
712
  });
563
713
  // Register get_estimate_quota tool (API-based, requires API key)
564
714
  server.registerTool(getEstimateQuotaToolDefinition.name, {
565
- title: 'Get Estimate Quota',
715
+ title: "Get Estimate Quota",
566
716
  description: getEstimateQuotaToolDefinition.description,
567
717
  inputSchema: {},
568
- annotations: getEstimateQuotaAnnotations
718
+ annotations: getEstimateQuotaAnnotations,
569
719
  }, async () => {
570
- debug('get_estimate_quota called');
720
+ debug("get_estimate_quota called");
571
721
  const { text, isError } = await executeGetEstimateQuota({});
572
722
  return {
573
- content: [{ type: 'text', text }],
574
- isError
723
+ content: [{ type: "text", text }],
724
+ isError,
575
725
  };
576
726
  });
577
- info('Registered 25 tools: list_technologies, analyze_tech, compare_techs, recommend_stack_demo, recommend_stack, get_blueprint, create_blueprint, setup_api_key, list_api_keys, revoke_api_key, create_api_key, create_audit, get_audit, list_audits, compare_audits, get_audit_quota, get_migration_recommendation, generate_mcp_kit, analyze_repo_mcps, prepare_mcp_installation, execute_mcp_installation, check_mcp_compatibility, get_workflow_guide, estimate_project, get_estimate_quota');
727
+ info("Registered 26 tools: list_technologies, analyze_tech, compare_techs, recommend_stack_demo, recommend_stack, get_blueprint, create_blueprint, setup_api_key, list_api_keys, revoke_api_key, create_api_key, create_audit, get_audit, list_audits, compare_audits, get_audit_quota, get_migration_recommendation, import_better_t_stack, generate_mcp_kit, analyze_repo_mcps, prepare_mcp_installation, execute_mcp_installation, check_mcp_compatibility, get_workflow_guide, estimate_project, get_estimate_quota");
578
728
  return server;
579
729
  }
580
730
  // ============================================================================
@@ -585,102 +735,102 @@ export function createServer() {
585
735
  */
586
736
  function formatAnalysisResult(result) {
587
737
  const lines = [];
588
- lines.push('# Repository Analysis\n');
738
+ lines.push("# Repository Analysis\n");
589
739
  // Detected Stack
590
- lines.push('## Detected Technologies\n');
740
+ lines.push("## Detected Technologies\n");
591
741
  const stackItems = [
592
742
  result.detectedStack.frontend &&
593
- `- **Frontend**: ${result.detectedStack.frontend.name}${result.detectedStack.frontend.version ? ` (${result.detectedStack.frontend.version})` : ''}`,
743
+ `- **Frontend**: ${result.detectedStack.frontend.name}${result.detectedStack.frontend.version ? ` (${result.detectedStack.frontend.version})` : ""}`,
594
744
  result.detectedStack.backend &&
595
- `- **Backend**: ${result.detectedStack.backend.name}${result.detectedStack.backend.version ? ` (${result.detectedStack.backend.version})` : ''}`,
745
+ `- **Backend**: ${result.detectedStack.backend.name}${result.detectedStack.backend.version ? ` (${result.detectedStack.backend.version})` : ""}`,
596
746
  result.detectedStack.database &&
597
- `- **Database**: ${result.detectedStack.database.name}${result.detectedStack.database.version ? ` (${result.detectedStack.database.version})` : ''}`,
747
+ `- **Database**: ${result.detectedStack.database.name}${result.detectedStack.database.version ? ` (${result.detectedStack.database.version})` : ""}`,
598
748
  result.detectedStack.orm &&
599
- `- **ORM**: ${result.detectedStack.orm.name}${result.detectedStack.orm.version ? ` (${result.detectedStack.orm.version})` : ''}`,
749
+ `- **ORM**: ${result.detectedStack.orm.name}${result.detectedStack.orm.version ? ` (${result.detectedStack.orm.version})` : ""}`,
600
750
  result.detectedStack.auth &&
601
- `- **Auth**: ${result.detectedStack.auth.name}${result.detectedStack.auth.version ? ` (${result.detectedStack.auth.version})` : ''}`,
751
+ `- **Auth**: ${result.detectedStack.auth.name}${result.detectedStack.auth.version ? ` (${result.detectedStack.auth.version})` : ""}`,
602
752
  result.detectedStack.hosting &&
603
- `- **Hosting**: ${result.detectedStack.hosting.name}${result.detectedStack.hosting.version ? ` (${result.detectedStack.hosting.version})` : ''}`,
753
+ `- **Hosting**: ${result.detectedStack.hosting.name}${result.detectedStack.hosting.version ? ` (${result.detectedStack.hosting.version})` : ""}`,
604
754
  result.detectedStack.payments &&
605
- `- **Payments**: ${result.detectedStack.payments.name}${result.detectedStack.payments.version ? ` (${result.detectedStack.payments.version})` : ''}`
755
+ `- **Payments**: ${result.detectedStack.payments.name}${result.detectedStack.payments.version ? ` (${result.detectedStack.payments.version})` : ""}`,
606
756
  ].filter((item) => Boolean(item));
607
757
  if (stackItems.length > 0) {
608
758
  lines.push(...stackItems);
609
759
  }
610
760
  else {
611
- lines.push('_No technologies detected from project files._');
761
+ lines.push("_No technologies detected from project files._");
612
762
  }
613
763
  if (result.detectedStack.services.length > 0) {
614
- lines.push('\n**Services**:');
764
+ lines.push("\n**Services**:");
615
765
  for (const service of result.detectedStack.services) {
616
766
  lines.push(`- ${service.name}`);
617
767
  }
618
768
  }
619
- lines.push('');
769
+ lines.push("");
620
770
  // Files Analyzed
621
- lines.push('## Files Analyzed\n');
771
+ lines.push("## Files Analyzed\n");
622
772
  if (result.metadata.filesAnalyzed.length > 0) {
623
- lines.push(result.metadata.filesAnalyzed.map((f) => `- \`${f}\``).join('\n'));
773
+ lines.push(result.metadata.filesAnalyzed.map((f) => `- \`${f}\``).join("\n"));
624
774
  }
625
775
  else {
626
- lines.push('_No recognized configuration files found._');
776
+ lines.push("_No recognized configuration files found._");
627
777
  }
628
- lines.push('');
778
+ lines.push("");
629
779
  // Installed MCPs
630
780
  if (result.installedMcps.length > 0) {
631
- lines.push('## Already Installed MCPs\n');
632
- lines.push(result.installedMcps.map((m) => `- ${m}`).join('\n'));
633
- lines.push('');
781
+ lines.push("## Already Installed MCPs\n");
782
+ lines.push(result.installedMcps.map((m) => `- ${m}`).join("\n"));
783
+ lines.push("");
634
784
  }
635
785
  // Recommended MCPs
636
- lines.push('## Recommended MCPs\n');
786
+ lines.push("## Recommended MCPs\n");
637
787
  if (result.recommendedMcps.length === 0) {
638
- lines.push('_No additional MCPs recommended. You have everything you need!_');
788
+ lines.push("_No additional MCPs recommended. You have everything you need!_");
639
789
  }
640
790
  else {
641
791
  // Group by priority
642
- const highPriority = result.recommendedMcps.filter((m) => m.priority === 'high');
643
- const mediumPriority = result.recommendedMcps.filter((m) => m.priority === 'medium');
644
- const lowPriority = result.recommendedMcps.filter((m) => m.priority === 'low');
792
+ const highPriority = result.recommendedMcps.filter((m) => m.priority === "high");
793
+ const mediumPriority = result.recommendedMcps.filter((m) => m.priority === "medium");
794
+ const lowPriority = result.recommendedMcps.filter((m) => m.priority === "low");
645
795
  if (highPriority.length > 0) {
646
- lines.push('### High Priority\n');
796
+ lines.push("### High Priority\n");
647
797
  for (const mcp of highPriority) {
648
798
  lines.push(`**${mcp.name}** (\`${mcp.slug}\`)`);
649
799
  lines.push(`- ${mcp.description}`);
650
800
  lines.push(`- _Matched: ${mcp.matchedTech}_`);
651
- lines.push('');
801
+ lines.push("");
652
802
  }
653
803
  }
654
804
  if (mediumPriority.length > 0) {
655
- lines.push('### Medium Priority\n');
805
+ lines.push("### Medium Priority\n");
656
806
  for (const mcp of mediumPriority) {
657
807
  lines.push(`**${mcp.name}** (\`${mcp.slug}\`)`);
658
808
  lines.push(`- ${mcp.description}`);
659
809
  lines.push(`- _Matched: ${mcp.matchedTech}_`);
660
- lines.push('');
810
+ lines.push("");
661
811
  }
662
812
  }
663
813
  if (lowPriority.length > 0) {
664
- lines.push('### Low Priority\n');
814
+ lines.push("### Low Priority\n");
665
815
  for (const mcp of lowPriority) {
666
816
  lines.push(`**${mcp.name}** (\`${mcp.slug}\`)`);
667
817
  lines.push(`- ${mcp.description}`);
668
818
  lines.push(`- _Matched: ${mcp.matchedTech}_`);
669
- lines.push('');
819
+ lines.push("");
670
820
  }
671
821
  }
672
822
  }
673
823
  // Quick Install
674
824
  if (result.recommendedMcps.length > 0) {
675
- lines.push('## Quick Install\n');
676
- lines.push('Add to your Claude Desktop config (`claude_desktop_config.json`):\n');
677
- lines.push('```json');
825
+ lines.push("## Quick Install\n");
826
+ lines.push("Add to your Claude Desktop config (`claude_desktop_config.json`):\n");
827
+ lines.push("```json");
678
828
  lines.push(JSON.stringify(result.installConfig.claudeDesktop, null, 2));
679
- lines.push('```\n');
829
+ lines.push("```\n");
680
830
  }
681
831
  // Metadata
682
832
  lines.push(`---\n_Analysis completed: ${result.metadata.analysisDate}_`);
683
- return lines.join('\n');
833
+ return lines.join("\n");
684
834
  }
685
835
  /**
686
836
  * Format prepare_mcp_installation result summary.
@@ -689,44 +839,44 @@ function formatPreparationSummary(result) {
689
839
  const lines = [];
690
840
  // MCPs to install grouped by priority
691
841
  if (result.mcpsToInstall.length > 0) {
692
- lines.push('## MCPs to Install\n');
693
- const highPriority = result.mcpsToInstall.filter((m) => m.priority === 'high');
694
- const mediumPriority = result.mcpsToInstall.filter((m) => m.priority === 'medium');
695
- const lowPriority = result.mcpsToInstall.filter((m) => m.priority === 'low');
842
+ lines.push("## MCPs to Install\n");
843
+ const highPriority = result.mcpsToInstall.filter((m) => m.priority === "high");
844
+ const mediumPriority = result.mcpsToInstall.filter((m) => m.priority === "medium");
845
+ const lowPriority = result.mcpsToInstall.filter((m) => m.priority === "low");
696
846
  if (highPriority.length > 0) {
697
- lines.push('### 🔴 High Priority');
847
+ lines.push("### 🔴 High Priority");
698
848
  for (const mcp of highPriority) {
699
- const requiredVars = mcp.envVars.filter((v) => v.requirement === 'required').length;
849
+ const requiredVars = mcp.envVars.filter((v) => v.requirement === "required").length;
700
850
  lines.push(`- **${mcp.name}** (${requiredVars} required vars)`);
701
851
  }
702
- lines.push('');
852
+ lines.push("");
703
853
  }
704
854
  if (mediumPriority.length > 0) {
705
- lines.push('### 🟡 Medium Priority');
855
+ lines.push("### 🟡 Medium Priority");
706
856
  for (const mcp of mediumPriority) {
707
- const requiredVars = mcp.envVars.filter((v) => v.requirement === 'required').length;
857
+ const requiredVars = mcp.envVars.filter((v) => v.requirement === "required").length;
708
858
  lines.push(`- **${mcp.name}** (${requiredVars} required vars)`);
709
859
  }
710
- lines.push('');
860
+ lines.push("");
711
861
  }
712
862
  if (lowPriority.length > 0) {
713
- lines.push('### 🟢 Low Priority');
863
+ lines.push("### 🟢 Low Priority");
714
864
  for (const mcp of lowPriority) {
715
- const requiredVars = mcp.envVars.filter((v) => v.requirement === 'required').length;
865
+ const requiredVars = mcp.envVars.filter((v) => v.requirement === "required").length;
716
866
  lines.push(`- **${mcp.name}** (${requiredVars} required vars)`);
717
867
  }
718
- lines.push('');
868
+ lines.push("");
719
869
  }
720
870
  }
721
871
  // Already installed
722
872
  if (result.installedMcps.length > 0) {
723
- lines.push('## Already Installed');
873
+ lines.push("## Already Installed");
724
874
  for (const mcp of result.installedMcps) {
725
875
  lines.push(`- ✅ ${mcp}`);
726
876
  }
727
- lines.push('');
877
+ lines.push("");
728
878
  }
729
- return lines.join('\n');
879
+ return lines.join("\n");
730
880
  }
731
881
  /**
732
882
  * Format execute_mcp_installation result.
@@ -734,33 +884,33 @@ function formatPreparationSummary(result) {
734
884
  function formatExecutionResult(result) {
735
885
  const lines = [];
736
886
  lines.push(result.message);
737
- lines.push('');
887
+ lines.push("");
738
888
  // Show aggregate command for Claude Code
739
889
  if (result.aggregateCommand) {
740
- lines.push('---\n');
741
- lines.push('## Claude Code Installation\n');
742
- lines.push('Run this command to install all ready MCPs:\n');
743
- lines.push('```bash');
890
+ lines.push("---\n");
891
+ lines.push("## Claude Code Installation\n");
892
+ lines.push("Run this command to install all ready MCPs:\n");
893
+ lines.push("```bash");
744
894
  lines.push(result.aggregateCommand);
745
- lines.push('```\n');
895
+ lines.push("```\n");
746
896
  }
747
897
  // Show JSON config for other clients
748
898
  if (result.aggregateConfig && !result.aggregateCommand) {
749
- lines.push('---\n');
750
- lines.push('## JSON Configuration\n');
751
- lines.push('Add this to your MCP configuration file:\n');
752
- lines.push('```json');
899
+ lines.push("---\n");
900
+ lines.push("## JSON Configuration\n");
901
+ lines.push("Add this to your MCP configuration file:\n");
902
+ lines.push("```json");
753
903
  lines.push(JSON.stringify(result.aggregateConfig, null, 2));
754
- lines.push('```\n');
904
+ lines.push("```\n");
755
905
  }
756
906
  // Post-install instructions
757
907
  if (result.postInstallInstructions.length > 0) {
758
- lines.push('---\n');
759
- lines.push('## Post-Installation\n');
908
+ lines.push("---\n");
909
+ lines.push("## Post-Installation\n");
760
910
  for (const instruction of result.postInstallInstructions) {
761
911
  lines.push(instruction);
762
912
  }
763
913
  }
764
- return lines.join('\n');
914
+ return lines.join("\n");
765
915
  }
766
916
  //# sourceMappingURL=server.js.map