@stacksfinder/mcp-server 1.0.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/README.md +262 -0
- package/dist/data/compatibility_matrix.json +230 -0
- package/dist/data/index.d.ts +109 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +203 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/technology_scores.json +1031 -0
- package/dist/data/357/200/242/357/200/212cp H:bac_/303/240_guigui_v2stack_finderpackagesmcp-serversrcdatacompatibility_matrix.json H:bac_/303/240_guigui_v2stack_finderpackagesmcp-serverdistdata/357/200/242" +226 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +254 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/analyze.d.ts +45 -0
- package/dist/tools/analyze.d.ts.map +1 -0
- package/dist/tools/analyze.js +110 -0
- package/dist/tools/analyze.js.map +1 -0
- package/dist/tools/api-keys.d.ts +78 -0
- package/dist/tools/api-keys.d.ts.map +1 -0
- package/dist/tools/api-keys.js +238 -0
- package/dist/tools/api-keys.js.map +1 -0
- package/dist/tools/blueprint.d.ts +129 -0
- package/dist/tools/blueprint.d.ts.map +1 -0
- package/dist/tools/blueprint.js +320 -0
- package/dist/tools/blueprint.js.map +1 -0
- package/dist/tools/compare.d.ts +50 -0
- package/dist/tools/compare.d.ts.map +1 -0
- package/dist/tools/compare.js +168 -0
- package/dist/tools/compare.js.map +1 -0
- package/dist/tools/list-techs.d.ts +34 -0
- package/dist/tools/list-techs.d.ts.map +1 -0
- package/dist/tools/list-techs.js +70 -0
- package/dist/tools/list-techs.js.map +1 -0
- package/dist/tools/recommend-demo.d.ts +46 -0
- package/dist/tools/recommend-demo.d.ts.map +1 -0
- package/dist/tools/recommend-demo.js +202 -0
- package/dist/tools/recommend-demo.js.map +1 -0
- package/dist/tools/recommend.d.ts +68 -0
- package/dist/tools/recommend.d.ts.map +1 -0
- package/dist/tools/recommend.js +135 -0
- package/dist/tools/recommend.js.map +1 -0
- package/dist/utils/api-client.d.ts +80 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +197 -0
- package/dist/utils/api-client.js.map +1 -0
- package/dist/utils/config.d.ts +35 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +45 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/device-id.d.ts +21 -0
- package/dist/utils/device-id.d.ts.map +1 -0
- package/dist/utils/device-id.js +101 -0
- package/dist/utils/device-id.js.map +1 -0
- package/dist/utils/errors.d.ts +46 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +125 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +37 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +73 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getBlueprintRequest, createBlueprintRequest, getJobStatusRequest } from '../utils/api-client.js';
|
|
3
|
+
import { McpError, ErrorCode } from '../utils/errors.js';
|
|
4
|
+
import { debug } from '../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Input schema for get_blueprint tool.
|
|
7
|
+
*/
|
|
8
|
+
export const GetBlueprintInputSchema = z.object({
|
|
9
|
+
blueprintId: z.string().uuid().describe('Blueprint UUID')
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Tool definition for MCP registration.
|
|
13
|
+
*/
|
|
14
|
+
export const getBlueprintToolDefinition = {
|
|
15
|
+
name: 'get_blueprint',
|
|
16
|
+
description: 'Fetches an existing blueprint by ID. Blueprints are generated via the StacksFinder web UI. Requires API key.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
blueprintId: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
format: 'uuid',
|
|
23
|
+
description: 'Blueprint UUID'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
required: ['blueprintId']
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Format blueprint for MCP output.
|
|
31
|
+
*/
|
|
32
|
+
function formatBlueprint(blueprint) {
|
|
33
|
+
const projectName = blueprint.projectContext?.projectName || 'Unnamed Project';
|
|
34
|
+
const projectType = blueprint.projectContext?.projectType || 'Unknown';
|
|
35
|
+
const scale = blueprint.projectContext?.scale || 'mvp';
|
|
36
|
+
const createdDate = new Date(blueprint.createdAt).toLocaleDateString('en-US', {
|
|
37
|
+
year: 'numeric',
|
|
38
|
+
month: 'long',
|
|
39
|
+
day: 'numeric'
|
|
40
|
+
});
|
|
41
|
+
let text = `## Blueprint: ${projectName}
|
|
42
|
+
|
|
43
|
+
**ID**: ${blueprint.id}
|
|
44
|
+
**Type**: ${projectType}
|
|
45
|
+
**Scale**: ${scale}
|
|
46
|
+
**Created**: ${createdDate}
|
|
47
|
+
|
|
48
|
+
### Selected Stack
|
|
49
|
+
| Category | Technology |
|
|
50
|
+
|----------|------------|
|
|
51
|
+
`;
|
|
52
|
+
for (const tech of blueprint.selectedTechs) {
|
|
53
|
+
text += `| ${tech.category} | ${tech.technology} |\n`;
|
|
54
|
+
}
|
|
55
|
+
if (blueprint.narrative) {
|
|
56
|
+
text += `
|
|
57
|
+
### Narrative
|
|
58
|
+
${blueprint.narrative}`;
|
|
59
|
+
}
|
|
60
|
+
return text;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Execute get_blueprint tool.
|
|
64
|
+
*/
|
|
65
|
+
export async function executeGetBlueprint(input) {
|
|
66
|
+
const { blueprintId } = input;
|
|
67
|
+
debug('Fetching blueprint', { blueprintId });
|
|
68
|
+
try {
|
|
69
|
+
const response = await getBlueprintRequest(blueprintId);
|
|
70
|
+
const text = formatBlueprint(response);
|
|
71
|
+
return { text };
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err instanceof McpError) {
|
|
75
|
+
// Add helpful suggestion for NOT_FOUND
|
|
76
|
+
if (err.code === ErrorCode.NOT_FOUND) {
|
|
77
|
+
err.suggestions = [
|
|
78
|
+
'Blueprints are generated via the StacksFinder web UI.',
|
|
79
|
+
'Visit https://stacksfinder.com to create a new blueprint.'
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
return { text: err.toResponseText(), isError: true };
|
|
83
|
+
}
|
|
84
|
+
const error = new McpError(ErrorCode.API_ERROR, err instanceof Error ? err.message : 'Failed to fetch blueprint');
|
|
85
|
+
return { text: error.toResponseText(), isError: true };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// CREATE BLUEPRINT TOOL
|
|
90
|
+
// ============================================================================
|
|
91
|
+
/**
|
|
92
|
+
* Valid project types for blueprint creation.
|
|
93
|
+
*/
|
|
94
|
+
const PROJECT_TYPES = [
|
|
95
|
+
'web-app',
|
|
96
|
+
'mobile-app',
|
|
97
|
+
'api',
|
|
98
|
+
'desktop',
|
|
99
|
+
'cli',
|
|
100
|
+
'library',
|
|
101
|
+
'e-commerce',
|
|
102
|
+
'saas',
|
|
103
|
+
'marketplace'
|
|
104
|
+
];
|
|
105
|
+
/**
|
|
106
|
+
* Valid scales for blueprint creation.
|
|
107
|
+
*/
|
|
108
|
+
const SCALES = ['mvp', 'startup', 'growth', 'enterprise'];
|
|
109
|
+
/**
|
|
110
|
+
* Valid priorities for blueprint creation.
|
|
111
|
+
*/
|
|
112
|
+
const PRIORITIES = [
|
|
113
|
+
'time-to-market',
|
|
114
|
+
'scalability',
|
|
115
|
+
'developer-experience',
|
|
116
|
+
'cost-efficiency',
|
|
117
|
+
'performance',
|
|
118
|
+
'security',
|
|
119
|
+
'maintainability'
|
|
120
|
+
];
|
|
121
|
+
/**
|
|
122
|
+
* Input schema for create_blueprint tool.
|
|
123
|
+
*/
|
|
124
|
+
export const CreateBlueprintInputSchema = z.object({
|
|
125
|
+
projectName: z.string().min(1).max(100).optional().describe('Project name (optional)'),
|
|
126
|
+
projectType: z
|
|
127
|
+
.enum(PROJECT_TYPES)
|
|
128
|
+
.describe('Type of project (e.g., web-app, saas, api)'),
|
|
129
|
+
scale: z.enum(SCALES).describe('Project scale (mvp, startup, growth, enterprise)'),
|
|
130
|
+
projectDescription: z
|
|
131
|
+
.string()
|
|
132
|
+
.max(2000)
|
|
133
|
+
.optional()
|
|
134
|
+
.describe('Brief project description (optional)'),
|
|
135
|
+
priorities: z
|
|
136
|
+
.array(z.enum(PRIORITIES))
|
|
137
|
+
.max(3)
|
|
138
|
+
.optional()
|
|
139
|
+
.describe('Top 3 priorities (optional)'),
|
|
140
|
+
constraints: z
|
|
141
|
+
.array(z.string())
|
|
142
|
+
.max(20)
|
|
143
|
+
.optional()
|
|
144
|
+
.describe('Technology constraint IDs (optional)'),
|
|
145
|
+
waitForCompletion: z
|
|
146
|
+
.boolean()
|
|
147
|
+
.optional()
|
|
148
|
+
.default(true)
|
|
149
|
+
.describe('Wait for blueprint generation to complete (default: true)')
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* Tool definition for MCP registration.
|
|
153
|
+
*/
|
|
154
|
+
export const createBlueprintToolDefinition = {
|
|
155
|
+
name: 'create_blueprint',
|
|
156
|
+
description: `Creates a new tech stack blueprint for a project. Requires API key with 'blueprint:write' scope.
|
|
157
|
+
|
|
158
|
+
The blueprint generation is asynchronous. By default, this tool waits for completion and returns the full blueprint.
|
|
159
|
+
Set waitForCompletion=false to get the job ID immediately for manual polling.
|
|
160
|
+
|
|
161
|
+
Example usage:
|
|
162
|
+
- Create a SaaS MVP: projectType="saas", scale="mvp", priorities=["time-to-market", "cost-efficiency"]
|
|
163
|
+
- Create an enterprise API: projectType="api", scale="enterprise", priorities=["security", "scalability"]`,
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: 'object',
|
|
166
|
+
properties: {
|
|
167
|
+
projectName: {
|
|
168
|
+
type: 'string',
|
|
169
|
+
description: 'Project name (optional)',
|
|
170
|
+
maxLength: 100
|
|
171
|
+
},
|
|
172
|
+
projectType: {
|
|
173
|
+
type: 'string',
|
|
174
|
+
enum: PROJECT_TYPES,
|
|
175
|
+
description: 'Type of project'
|
|
176
|
+
},
|
|
177
|
+
scale: {
|
|
178
|
+
type: 'string',
|
|
179
|
+
enum: SCALES,
|
|
180
|
+
description: 'Project scale'
|
|
181
|
+
},
|
|
182
|
+
projectDescription: {
|
|
183
|
+
type: 'string',
|
|
184
|
+
description: 'Brief project description (optional)',
|
|
185
|
+
maxLength: 2000
|
|
186
|
+
},
|
|
187
|
+
priorities: {
|
|
188
|
+
type: 'array',
|
|
189
|
+
items: { type: 'string', enum: PRIORITIES },
|
|
190
|
+
maxItems: 3,
|
|
191
|
+
description: 'Top 3 priorities (optional)'
|
|
192
|
+
},
|
|
193
|
+
constraints: {
|
|
194
|
+
type: 'array',
|
|
195
|
+
items: { type: 'string' },
|
|
196
|
+
maxItems: 20,
|
|
197
|
+
description: 'Technology constraint IDs (optional)'
|
|
198
|
+
},
|
|
199
|
+
waitForCompletion: {
|
|
200
|
+
type: 'boolean',
|
|
201
|
+
description: 'Wait for blueprint generation to complete (default: true)',
|
|
202
|
+
default: true
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
required: ['projectType', 'scale']
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Poll job status until completion or failure.
|
|
210
|
+
*/
|
|
211
|
+
async function pollJobUntilComplete(jobId, maxAttempts = 30, intervalMs = 2000) {
|
|
212
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
213
|
+
const status = await getJobStatusRequest(jobId);
|
|
214
|
+
if (status.status === 'completed') {
|
|
215
|
+
return status;
|
|
216
|
+
}
|
|
217
|
+
if (status.status === 'failed' || status.status === 'cancelled') {
|
|
218
|
+
throw new McpError(ErrorCode.API_ERROR, status.errorMessage || `Job ${status.status}: ${status.errorCode || 'Unknown error'}`);
|
|
219
|
+
}
|
|
220
|
+
// Wait before next poll
|
|
221
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
222
|
+
}
|
|
223
|
+
throw new McpError(ErrorCode.TIMEOUT, `Blueprint generation timed out after ${(maxAttempts * intervalMs) / 1000} seconds`, ['The job is still running. Use get_blueprint with the job resultRef to check later.']);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Execute create_blueprint tool.
|
|
227
|
+
*/
|
|
228
|
+
export async function executeCreateBlueprint(input) {
|
|
229
|
+
const { projectName, projectType, scale, projectDescription, priorities, constraints, waitForCompletion = true } = input;
|
|
230
|
+
debug('Creating blueprint', { projectType, scale, waitForCompletion });
|
|
231
|
+
try {
|
|
232
|
+
// Build request body
|
|
233
|
+
const requestBody = {
|
|
234
|
+
projectName,
|
|
235
|
+
projectContext: {
|
|
236
|
+
projectName,
|
|
237
|
+
projectType,
|
|
238
|
+
projectDescription,
|
|
239
|
+
scale,
|
|
240
|
+
priorities: priorities,
|
|
241
|
+
constraintIds: constraints
|
|
242
|
+
},
|
|
243
|
+
source: 'mcp',
|
|
244
|
+
mcpToolName: 'create_blueprint'
|
|
245
|
+
};
|
|
246
|
+
// Create the blueprint job
|
|
247
|
+
const createResponse = await createBlueprintRequest(requestBody);
|
|
248
|
+
// If already completed (cached result), fetch and return
|
|
249
|
+
if (createResponse.status === 'completed' && createResponse.resultRef) {
|
|
250
|
+
const blueprint = await getBlueprintRequest(createResponse.resultRef);
|
|
251
|
+
const text = formatBlueprint(blueprint);
|
|
252
|
+
return {
|
|
253
|
+
text: `## Blueprint Created (Cached)\n\n${text}\n\n---\n*Source: MCP Server*`
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// If not waiting, return job info
|
|
257
|
+
if (!waitForCompletion) {
|
|
258
|
+
return {
|
|
259
|
+
text: `## Blueprint Generation Started
|
|
260
|
+
|
|
261
|
+
**Job ID**: ${createResponse.jobId}
|
|
262
|
+
**Project ID**: ${createResponse.projectId}
|
|
263
|
+
**Status**: ${createResponse.status}
|
|
264
|
+
**Progress**: ${createResponse.progress}%
|
|
265
|
+
|
|
266
|
+
Poll the job status at: ${createResponse._links.job}
|
|
267
|
+
Once complete, fetch the blueprint at: ${createResponse._links.blueprint || 'TBD'}
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
*Source: MCP Server*`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// Poll until completion
|
|
274
|
+
debug('Polling job until completion', { jobId: createResponse.jobId });
|
|
275
|
+
const finalStatus = await pollJobUntilComplete(createResponse.jobId);
|
|
276
|
+
if (!finalStatus.resultRef) {
|
|
277
|
+
throw new McpError(ErrorCode.API_ERROR, 'Job completed but no blueprint ID returned');
|
|
278
|
+
}
|
|
279
|
+
// Fetch the completed blueprint
|
|
280
|
+
const blueprint = await getBlueprintRequest(finalStatus.resultRef);
|
|
281
|
+
const text = formatBlueprint(blueprint);
|
|
282
|
+
return {
|
|
283
|
+
text: `## Blueprint Created Successfully
|
|
284
|
+
|
|
285
|
+
${text}
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
**Job ID**: ${createResponse.jobId}
|
|
289
|
+
**Project ID**: ${createResponse.projectId}
|
|
290
|
+
*Source: MCP Server*`
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
if (err instanceof McpError) {
|
|
295
|
+
// Add helpful suggestions for common errors
|
|
296
|
+
if (err.code === ErrorCode.CONFIG_ERROR) {
|
|
297
|
+
err.suggestions = [
|
|
298
|
+
'Ensure STACKSFINDER_API_KEY is set with a valid Pro or Team API key.',
|
|
299
|
+
'Get your API key from https://stacksfinder.com/settings/api'
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
else if (err.code === ErrorCode.UNAUTHORIZED) {
|
|
303
|
+
err.suggestions = [
|
|
304
|
+
"Your API key may be invalid or missing the 'blueprint:write' scope.",
|
|
305
|
+
'Generate a new API key with the correct permissions at https://stacksfinder.com/settings/api'
|
|
306
|
+
];
|
|
307
|
+
}
|
|
308
|
+
else if (err.code === ErrorCode.RATE_LIMITED) {
|
|
309
|
+
err.suggestions = [
|
|
310
|
+
'You have exceeded your monthly blueprint quota.',
|
|
311
|
+
'Upgrade your plan at https://stacksfinder.com/pricing'
|
|
312
|
+
];
|
|
313
|
+
}
|
|
314
|
+
return { text: err.toResponseText(), isError: true };
|
|
315
|
+
}
|
|
316
|
+
const error = new McpError(ErrorCode.API_ERROR, err instanceof Error ? err.message : 'Failed to create blueprint');
|
|
317
|
+
return { text: error.toResponseText(), isError: true };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=blueprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../../src/tools/blueprint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Input schema for compare_techs tool.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CompareTechsInputSchema: z.ZodObject<{
|
|
6
|
+
technologies: z.ZodArray<z.ZodString, "many">;
|
|
7
|
+
context: z.ZodDefault<z.ZodOptional<z.ZodEnum<["default", "mvp", "enterprise"]>>>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
context: "default" | "mvp" | "enterprise";
|
|
10
|
+
technologies: string[];
|
|
11
|
+
}, {
|
|
12
|
+
technologies: string[];
|
|
13
|
+
context?: "default" | "mvp" | "enterprise" | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export type CompareTechsInput = z.infer<typeof CompareTechsInputSchema>;
|
|
16
|
+
/**
|
|
17
|
+
* Tool definition for MCP registration.
|
|
18
|
+
*/
|
|
19
|
+
export declare const compareTechsToolDefinition: {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object";
|
|
24
|
+
properties: {
|
|
25
|
+
technologies: {
|
|
26
|
+
type: string;
|
|
27
|
+
items: {
|
|
28
|
+
type: string;
|
|
29
|
+
};
|
|
30
|
+
minItems: number;
|
|
31
|
+
maxItems: number;
|
|
32
|
+
description: string;
|
|
33
|
+
};
|
|
34
|
+
context: {
|
|
35
|
+
type: string;
|
|
36
|
+
enum: readonly ["default", "mvp", "enterprise"];
|
|
37
|
+
description: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
required: string[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Execute compare_techs tool.
|
|
45
|
+
*/
|
|
46
|
+
export declare function executeCompareTechs(input: CompareTechsInput): {
|
|
47
|
+
text: string;
|
|
48
|
+
isError?: boolean;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/tools/compare.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;EAOlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;CAqBtC,CAAC;AAuDF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CA4GjG"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CONTEXTS, DATA_VERSION, DIMENSION_LABELS, SCORE_DIMENSIONS, calculateOverallScore, getAllTechIds, getCompatibility, getCompatibilityVerdict, getScores, getTechnology, scoreToGrade, techExists } from '../data/index.js';
|
|
3
|
+
import { McpError, ErrorCode, techNotFoundError } from '../utils/errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* Input schema for compare_techs tool.
|
|
6
|
+
*/
|
|
7
|
+
export const CompareTechsInputSchema = z.object({
|
|
8
|
+
technologies: z
|
|
9
|
+
.array(z.string().min(1))
|
|
10
|
+
.min(2)
|
|
11
|
+
.max(4)
|
|
12
|
+
.describe('Technology IDs to compare (2-4 technologies)'),
|
|
13
|
+
context: z.enum(CONTEXTS).optional().default('default').describe('Context for score lookup')
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Tool definition for MCP registration.
|
|
17
|
+
*/
|
|
18
|
+
export const compareTechsToolDefinition = {
|
|
19
|
+
name: 'compare_techs',
|
|
20
|
+
description: 'Side-by-side comparison of 2-4 technologies with per-dimension winners and compatibility matrix.',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
technologies: {
|
|
25
|
+
type: 'array',
|
|
26
|
+
items: { type: 'string' },
|
|
27
|
+
minItems: 2,
|
|
28
|
+
maxItems: 4,
|
|
29
|
+
description: 'Technology IDs to compare (e.g., ["nextjs", "sveltekit", "nuxt"])'
|
|
30
|
+
},
|
|
31
|
+
context: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
enum: CONTEXTS,
|
|
34
|
+
description: 'Context for scoring (default, mvp, enterprise)'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
required: ['technologies']
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Determine winner for each dimension.
|
|
42
|
+
*/
|
|
43
|
+
function determineDimensionWinners(techs) {
|
|
44
|
+
const winners = [];
|
|
45
|
+
for (const dim of SCORE_DIMENSIONS) {
|
|
46
|
+
const sorted = [...techs].sort((a, b) => b.scores[dim] - a.scores[dim]);
|
|
47
|
+
const first = sorted[0];
|
|
48
|
+
const second = sorted[1];
|
|
49
|
+
const margin = first.scores[dim] - second.scores[dim];
|
|
50
|
+
let winner;
|
|
51
|
+
let notes;
|
|
52
|
+
if (margin < 3) {
|
|
53
|
+
// Tie if margin is less than 3
|
|
54
|
+
winner = null;
|
|
55
|
+
notes = 'Tie';
|
|
56
|
+
}
|
|
57
|
+
else if (margin < 10) {
|
|
58
|
+
winner = first.name;
|
|
59
|
+
notes = 'Close competition';
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
winner = first.name;
|
|
63
|
+
notes = 'Clear winner';
|
|
64
|
+
}
|
|
65
|
+
winners.push({
|
|
66
|
+
dimension: DIMENSION_LABELS[dim],
|
|
67
|
+
winner,
|
|
68
|
+
margin,
|
|
69
|
+
notes
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return winners;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Execute compare_techs tool.
|
|
76
|
+
*/
|
|
77
|
+
export function executeCompareTechs(input) {
|
|
78
|
+
const { technologies, context = 'default' } = input;
|
|
79
|
+
// Validate all technologies exist
|
|
80
|
+
const allTechIds = getAllTechIds();
|
|
81
|
+
const invalidTechs = [];
|
|
82
|
+
for (const techId of technologies) {
|
|
83
|
+
if (!techExists(techId)) {
|
|
84
|
+
invalidTechs.push(techId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (invalidTechs.length > 0) {
|
|
88
|
+
const error = techNotFoundError(invalidTechs[0], allTechIds);
|
|
89
|
+
return { text: error.toResponseText(), isError: true };
|
|
90
|
+
}
|
|
91
|
+
// Check for duplicates
|
|
92
|
+
const uniqueTechs = [...new Set(technologies)];
|
|
93
|
+
if (uniqueTechs.length !== technologies.length) {
|
|
94
|
+
const error = new McpError(ErrorCode.INVALID_INPUT, 'Duplicate technologies in comparison list');
|
|
95
|
+
return { text: error.toResponseText(), isError: true };
|
|
96
|
+
}
|
|
97
|
+
// Build comparison data
|
|
98
|
+
const comparisons = technologies.map((techId) => {
|
|
99
|
+
const tech = getTechnology(techId);
|
|
100
|
+
const scores = getScores(techId, context);
|
|
101
|
+
const overall = calculateOverallScore(scores);
|
|
102
|
+
return {
|
|
103
|
+
id: techId,
|
|
104
|
+
name: tech.name,
|
|
105
|
+
scores,
|
|
106
|
+
overall,
|
|
107
|
+
grade: scoreToGrade(overall)
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
// Sort by overall score
|
|
111
|
+
const sorted = [...comparisons].sort((a, b) => b.overall - a.overall);
|
|
112
|
+
// Determine dimension winners
|
|
113
|
+
const dimensionWinners = determineDimensionWinners(comparisons);
|
|
114
|
+
// Build response
|
|
115
|
+
const techNames = comparisons.map((t) => t.name).join(' vs ');
|
|
116
|
+
let text = `## Comparison: ${techNames} (context: ${context})
|
|
117
|
+
|
|
118
|
+
### Overall Scores
|
|
119
|
+
| Technology | Score | Grade |
|
|
120
|
+
|------------|-------|-------|
|
|
121
|
+
`;
|
|
122
|
+
for (const tech of sorted) {
|
|
123
|
+
text += `| ${tech.name} | ${tech.overall} | ${tech.grade} |\n`;
|
|
124
|
+
}
|
|
125
|
+
// Per-dimension breakdown
|
|
126
|
+
text += `
|
|
127
|
+
### Per-Dimension Winners
|
|
128
|
+
| Dimension | Winner | Margin | Notes |
|
|
129
|
+
|-----------|--------|--------|-------|
|
|
130
|
+
`;
|
|
131
|
+
for (const w of dimensionWinners) {
|
|
132
|
+
const winnerDisplay = w.winner ?? 'Tie';
|
|
133
|
+
const marginDisplay = w.winner ? `+${w.margin}` : '-';
|
|
134
|
+
text += `| ${w.dimension} | ${winnerDisplay} | ${marginDisplay} | ${w.notes} |\n`;
|
|
135
|
+
}
|
|
136
|
+
// Compatibility matrix (for all pairs)
|
|
137
|
+
text += '\n### Compatibility Matrix\n| Pair | Score | Verdict |\n|------|-------|---------|\n';
|
|
138
|
+
for (let i = 0; i < comparisons.length; i++) {
|
|
139
|
+
for (let j = i + 1; j < comparisons.length; j++) {
|
|
140
|
+
const a = comparisons[i];
|
|
141
|
+
const b = comparisons[j];
|
|
142
|
+
const score = getCompatibility(a.id, b.id);
|
|
143
|
+
const verdict = getCompatibilityVerdict(score);
|
|
144
|
+
text += `| ${a.id} ↔ ${b.id} | ${score} | ${verdict} |\n`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Verdict
|
|
148
|
+
const leader = sorted[0];
|
|
149
|
+
const runnerUp = sorted[1];
|
|
150
|
+
const overallMargin = leader.overall - runnerUp.overall;
|
|
151
|
+
text += '\n';
|
|
152
|
+
if (overallMargin < 3) {
|
|
153
|
+
text += `**Verdict**: Close call between ${leader.name} and ${runnerUp.name}\n`;
|
|
154
|
+
text += `**Recommendation**: Both are strong choices; consider your specific priorities.`;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
text += `**Verdict**: ${leader.name} leads with ${leader.overall}/100\n`;
|
|
158
|
+
// Find what leader is best at
|
|
159
|
+
const leaderStrengths = dimensionWinners.filter((w) => w.winner === leader.name).map((w) => w.dimension);
|
|
160
|
+
if (leaderStrengths.length > 0) {
|
|
161
|
+
const strengthsText = leaderStrengths.slice(0, 2).join(' and ');
|
|
162
|
+
text += `**Recommendation**: Consider ${leader.name} for ${strengthsText} priorities.`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
text += `\n\nData version: ${DATA_VERSION}`;
|
|
166
|
+
return { text };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/tools/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Input schema for list_technologies tool.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ListTechsInputSchema: z.ZodObject<{
|
|
6
|
+
category: z.ZodOptional<z.ZodEnum<["frontend", "backend", "meta-framework", "database", "orm", "auth", "hosting", "payments"]>>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
category?: "frontend" | "backend" | "meta-framework" | "database" | "orm" | "auth" | "hosting" | "payments" | undefined;
|
|
9
|
+
}, {
|
|
10
|
+
category?: "frontend" | "backend" | "meta-framework" | "database" | "orm" | "auth" | "hosting" | "payments" | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
export type ListTechsInput = z.infer<typeof ListTechsInputSchema>;
|
|
13
|
+
/**
|
|
14
|
+
* Tool definition for MCP registration.
|
|
15
|
+
*/
|
|
16
|
+
export declare const listTechsToolDefinition: {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: "object";
|
|
21
|
+
properties: {
|
|
22
|
+
category: {
|
|
23
|
+
type: string;
|
|
24
|
+
enum: readonly ["frontend", "backend", "meta-framework", "database", "orm", "auth", "hosting", "payments"];
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Execute list_technologies tool.
|
|
32
|
+
*/
|
|
33
|
+
export declare function executeListTechs(input: ListTechsInput): string;
|
|
34
|
+
//# sourceMappingURL=list-techs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-techs.d.ts","sourceRoot":"","sources":["../../src/tools/list-techs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;EAK/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;CAcnC,CAAC;AAYF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAyC9D"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CATEGORIES, DATA_VERSION, getTechnologiesByCategory, getTechnologiesGroupedByCategory } from '../data/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Input schema for list_technologies tool.
|
|
5
|
+
*/
|
|
6
|
+
export const ListTechsInputSchema = z.object({
|
|
7
|
+
category: z
|
|
8
|
+
.enum(CATEGORIES)
|
|
9
|
+
.optional()
|
|
10
|
+
.describe('Filter by category. Omit to list all technologies.')
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Tool definition for MCP registration.
|
|
14
|
+
*/
|
|
15
|
+
export const listTechsToolDefinition = {
|
|
16
|
+
name: 'list_technologies',
|
|
17
|
+
description: 'Lists all available technology IDs for use with other tools. Essential for discovering valid technology identifiers.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
category: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
enum: CATEGORIES,
|
|
24
|
+
description: 'Filter by category (optional)'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Format technologies for a single category.
|
|
31
|
+
*/
|
|
32
|
+
function formatCategory(category, techs) {
|
|
33
|
+
if (techs.length === 0)
|
|
34
|
+
return '';
|
|
35
|
+
const lines = techs.map((t) => `- ${t.id} (${t.name})`);
|
|
36
|
+
return `## ${category}\n${lines.join('\n')}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Execute list_technologies tool.
|
|
40
|
+
*/
|
|
41
|
+
export function executeListTechs(input) {
|
|
42
|
+
const { category } = input;
|
|
43
|
+
if (category) {
|
|
44
|
+
// Filter by specific category
|
|
45
|
+
const techs = getTechnologiesByCategory(category);
|
|
46
|
+
const formatted = formatCategory(category, techs.map((t) => ({ id: t.id, name: t.name })));
|
|
47
|
+
return `Available technologies in "${category}" (${techs.length} total):
|
|
48
|
+
|
|
49
|
+
${formatted}
|
|
50
|
+
|
|
51
|
+
Data version: ${DATA_VERSION}`;
|
|
52
|
+
}
|
|
53
|
+
// List all technologies grouped by category
|
|
54
|
+
const grouped = getTechnologiesGroupedByCategory();
|
|
55
|
+
const sections = [];
|
|
56
|
+
let total = 0;
|
|
57
|
+
for (const cat of CATEGORIES) {
|
|
58
|
+
const techs = grouped[cat];
|
|
59
|
+
if (techs.length > 0) {
|
|
60
|
+
sections.push(formatCategory(cat, techs.map((t) => ({ id: t.id, name: t.name }))));
|
|
61
|
+
total += techs.length;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return `Available technologies (${total} total):
|
|
65
|
+
|
|
66
|
+
${sections.join('\n\n')}
|
|
67
|
+
|
|
68
|
+
Data version: ${DATA_VERSION}`;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=list-techs.js.map
|