@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.
Files changed (65) hide show
  1. package/README.md +262 -0
  2. package/dist/data/compatibility_matrix.json +230 -0
  3. package/dist/data/index.d.ts +109 -0
  4. package/dist/data/index.d.ts.map +1 -0
  5. package/dist/data/index.js +203 -0
  6. package/dist/data/index.js.map +1 -0
  7. package/dist/data/technology_scores.json +1031 -0
  8. 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
  9. package/dist/index.d.ts +3 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +50 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/server.d.ts +6 -0
  14. package/dist/server.d.ts.map +1 -0
  15. package/dist/server.js +254 -0
  16. package/dist/server.js.map +1 -0
  17. package/dist/tools/analyze.d.ts +45 -0
  18. package/dist/tools/analyze.d.ts.map +1 -0
  19. package/dist/tools/analyze.js +110 -0
  20. package/dist/tools/analyze.js.map +1 -0
  21. package/dist/tools/api-keys.d.ts +78 -0
  22. package/dist/tools/api-keys.d.ts.map +1 -0
  23. package/dist/tools/api-keys.js +238 -0
  24. package/dist/tools/api-keys.js.map +1 -0
  25. package/dist/tools/blueprint.d.ts +129 -0
  26. package/dist/tools/blueprint.d.ts.map +1 -0
  27. package/dist/tools/blueprint.js +320 -0
  28. package/dist/tools/blueprint.js.map +1 -0
  29. package/dist/tools/compare.d.ts +50 -0
  30. package/dist/tools/compare.d.ts.map +1 -0
  31. package/dist/tools/compare.js +168 -0
  32. package/dist/tools/compare.js.map +1 -0
  33. package/dist/tools/list-techs.d.ts +34 -0
  34. package/dist/tools/list-techs.d.ts.map +1 -0
  35. package/dist/tools/list-techs.js +70 -0
  36. package/dist/tools/list-techs.js.map +1 -0
  37. package/dist/tools/recommend-demo.d.ts +46 -0
  38. package/dist/tools/recommend-demo.d.ts.map +1 -0
  39. package/dist/tools/recommend-demo.js +202 -0
  40. package/dist/tools/recommend-demo.js.map +1 -0
  41. package/dist/tools/recommend.d.ts +68 -0
  42. package/dist/tools/recommend.d.ts.map +1 -0
  43. package/dist/tools/recommend.js +135 -0
  44. package/dist/tools/recommend.js.map +1 -0
  45. package/dist/utils/api-client.d.ts +80 -0
  46. package/dist/utils/api-client.d.ts.map +1 -0
  47. package/dist/utils/api-client.js +197 -0
  48. package/dist/utils/api-client.js.map +1 -0
  49. package/dist/utils/config.d.ts +35 -0
  50. package/dist/utils/config.d.ts.map +1 -0
  51. package/dist/utils/config.js +45 -0
  52. package/dist/utils/config.js.map +1 -0
  53. package/dist/utils/device-id.d.ts +21 -0
  54. package/dist/utils/device-id.d.ts.map +1 -0
  55. package/dist/utils/device-id.js +101 -0
  56. package/dist/utils/device-id.js.map +1 -0
  57. package/dist/utils/errors.d.ts +46 -0
  58. package/dist/utils/errors.d.ts.map +1 -0
  59. package/dist/utils/errors.js +125 -0
  60. package/dist/utils/errors.js.map +1 -0
  61. package/dist/utils/logger.d.ts +37 -0
  62. package/dist/utils/logger.d.ts.map +1 -0
  63. package/dist/utils/logger.js +73 -0
  64. package/dist/utils/logger.js.map +1 -0
  65. package/package.json +63 -0
@@ -0,0 +1,197 @@
1
+ import { getConfig, hasApiKey } from './config.js';
2
+ import { McpError, ErrorCode, apiError } from './errors.js';
3
+ import { debug, error as logError } from './logger.js';
4
+ /**
5
+ * Simple semaphore for concurrency limiting.
6
+ */
7
+ class Semaphore {
8
+ permits;
9
+ queue = [];
10
+ constructor(permits) {
11
+ this.permits = permits;
12
+ }
13
+ async acquire() {
14
+ if (this.permits > 0) {
15
+ this.permits--;
16
+ return;
17
+ }
18
+ return new Promise((resolve) => {
19
+ this.queue.push(resolve);
20
+ });
21
+ }
22
+ release() {
23
+ const next = this.queue.shift();
24
+ if (next) {
25
+ next();
26
+ }
27
+ else {
28
+ this.permits++;
29
+ }
30
+ }
31
+ }
32
+ class TTLCache {
33
+ cache = new Map();
34
+ ttlMs;
35
+ constructor(ttlSeconds) {
36
+ this.ttlMs = ttlSeconds * 1000;
37
+ }
38
+ get(key) {
39
+ const entry = this.cache.get(key);
40
+ if (!entry)
41
+ return undefined;
42
+ if (Date.now() > entry.expiresAt) {
43
+ this.cache.delete(key);
44
+ return undefined;
45
+ }
46
+ return entry.data;
47
+ }
48
+ set(key, data) {
49
+ this.cache.set(key, {
50
+ data,
51
+ expiresAt: Date.now() + this.ttlMs
52
+ });
53
+ }
54
+ clear() {
55
+ this.cache.clear();
56
+ }
57
+ }
58
+ // Max 2 concurrent API requests
59
+ const semaphore = new Semaphore(2);
60
+ // 60 second TTL cache for score API
61
+ const scoreCache = new TTLCache(60);
62
+ // Default timeout of 15 seconds
63
+ const DEFAULT_TIMEOUT_MS = 15000;
64
+ /**
65
+ * Generate cache key for score API request.
66
+ */
67
+ function scoreCacheKey(params) {
68
+ const sorted = Object.keys(params)
69
+ .sort()
70
+ .reduce((acc, key) => {
71
+ acc[key] = params[key];
72
+ return acc;
73
+ }, {});
74
+ return JSON.stringify(sorted);
75
+ }
76
+ /**
77
+ * Make an authenticated API request with concurrency limiting and caching.
78
+ */
79
+ export async function apiRequest(path, options = {}) {
80
+ const config = getConfig();
81
+ const { method = 'GET', body, timeoutMs = DEFAULT_TIMEOUT_MS, useCache = false } = options;
82
+ // Check for API key
83
+ if (!hasApiKey()) {
84
+ throw new McpError(ErrorCode.CONFIG_ERROR, 'API key not configured. Set STACKSFINDER_API_KEY environment variable.', ['Get your API key from https://stacksfinder.com/settings/api']);
85
+ }
86
+ // Check cache for score API
87
+ const cacheKey = useCache && body ? scoreCacheKey(body) : null;
88
+ if (cacheKey) {
89
+ const cached = scoreCache.get(cacheKey);
90
+ if (cached) {
91
+ debug(`Cache hit for ${path}`);
92
+ return cached;
93
+ }
94
+ }
95
+ const url = `${config.apiUrl}${path}`;
96
+ debug(`API request: ${method} ${url}`);
97
+ // Acquire semaphore permit
98
+ await semaphore.acquire();
99
+ try {
100
+ const controller = new AbortController();
101
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
102
+ try {
103
+ const response = await fetch(url, {
104
+ method,
105
+ headers: {
106
+ 'Content-Type': 'application/json',
107
+ Authorization: `Bearer ${config.apiKey}`
108
+ },
109
+ body: body ? JSON.stringify(body) : undefined,
110
+ signal: controller.signal
111
+ });
112
+ clearTimeout(timeoutId);
113
+ if (!response.ok) {
114
+ const errorText = await response.text().catch(() => '');
115
+ let errorMessage;
116
+ try {
117
+ const errorJson = JSON.parse(errorText);
118
+ errorMessage = errorJson.error || errorJson.message;
119
+ }
120
+ catch {
121
+ // Not JSON, use raw text if short
122
+ if (errorText.length < 200) {
123
+ errorMessage = errorText;
124
+ }
125
+ }
126
+ throw apiError(response.status, errorMessage);
127
+ }
128
+ const contentType = response.headers.get('content-type');
129
+ if (!contentType?.includes('application/json')) {
130
+ throw new McpError(ErrorCode.API_ERROR, 'API returned non-JSON response');
131
+ }
132
+ const data = (await response.json());
133
+ // Cache successful score responses
134
+ if (cacheKey) {
135
+ scoreCache.set(cacheKey, data);
136
+ debug(`Cached response for ${path}`);
137
+ }
138
+ return data;
139
+ }
140
+ catch (err) {
141
+ clearTimeout(timeoutId);
142
+ if (err instanceof McpError) {
143
+ throw err;
144
+ }
145
+ if (err instanceof Error && err.name === 'AbortError') {
146
+ throw new McpError(ErrorCode.TIMEOUT, `Request timed out after ${timeoutMs}ms`, [
147
+ 'The API may be under heavy load. Please try again.'
148
+ ]);
149
+ }
150
+ logError('API request failed', err);
151
+ throw new McpError(ErrorCode.API_ERROR, err instanceof Error ? err.message : 'Unknown API error');
152
+ }
153
+ }
154
+ finally {
155
+ semaphore.release();
156
+ }
157
+ }
158
+ /**
159
+ * POST to the score API with caching.
160
+ */
161
+ export async function scoreRequest(body) {
162
+ return apiRequest('/api/v1/score', {
163
+ method: 'POST',
164
+ body,
165
+ useCache: true
166
+ });
167
+ }
168
+ /**
169
+ * GET a blueprint by ID.
170
+ */
171
+ export async function getBlueprintRequest(blueprintId) {
172
+ return apiRequest(`/api/v1/blueprints/${blueprintId}`);
173
+ }
174
+ /**
175
+ * POST to create a new blueprint.
176
+ * This creates a project and queues a job for blueprint generation.
177
+ */
178
+ export async function createBlueprintRequest(body) {
179
+ return apiRequest('/api/v1/blueprints', {
180
+ method: 'POST',
181
+ body: body,
182
+ timeoutMs: 30000 // 30 second timeout for creation
183
+ });
184
+ }
185
+ /**
186
+ * GET job status by ID.
187
+ */
188
+ export async function getJobStatusRequest(jobId) {
189
+ return apiRequest(`/api/v1/jobs/${jobId}`);
190
+ }
191
+ /**
192
+ * Clear the score cache (useful for testing).
193
+ */
194
+ export function clearScoreCache() {
195
+ scoreCache.clear();
196
+ }
197
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/utils/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod';
2
+ declare const ConfigSchema: z.ZodObject<{
3
+ apiUrl: z.ZodDefault<z.ZodString>;
4
+ apiKey: z.ZodOptional<z.ZodString>;
5
+ debug: z.ZodDefault<z.ZodBoolean>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ apiUrl: string;
8
+ debug: boolean;
9
+ apiKey?: string | undefined;
10
+ }, {
11
+ apiUrl?: string | undefined;
12
+ apiKey?: string | undefined;
13
+ debug?: boolean | undefined;
14
+ }>;
15
+ export type Config = z.infer<typeof ConfigSchema>;
16
+ /**
17
+ * Load configuration from environment variables.
18
+ * Call this once at startup.
19
+ */
20
+ export declare function loadConfig(): Config;
21
+ /**
22
+ * Get the current configuration.
23
+ * Throws if loadConfig() hasn't been called.
24
+ */
25
+ export declare function getConfig(): Config;
26
+ /**
27
+ * Check if API key is configured.
28
+ */
29
+ export declare function hasApiKey(): boolean;
30
+ /**
31
+ * Reset config (useful for testing).
32
+ */
33
+ export declare function resetConfig(): void;
34
+ export {};
35
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,YAAY;;;;;;;;;;;;EAIhB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAIlD;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAUnC;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ const ConfigSchema = z.object({
3
+ apiUrl: z.string().url().default('https://stacksfinder.com'),
4
+ apiKey: z.string().optional(),
5
+ debug: z.boolean().default(false)
6
+ });
7
+ let _config = null;
8
+ /**
9
+ * Load configuration from environment variables.
10
+ * Call this once at startup.
11
+ */
12
+ export function loadConfig() {
13
+ if (_config)
14
+ return _config;
15
+ _config = ConfigSchema.parse({
16
+ apiUrl: process.env.STACKSFINDER_API_URL || 'https://stacksfinder.com',
17
+ apiKey: process.env.STACKSFINDER_API_KEY,
18
+ debug: process.env.STACKSFINDER_MCP_DEBUG === 'true'
19
+ });
20
+ return _config;
21
+ }
22
+ /**
23
+ * Get the current configuration.
24
+ * Throws if loadConfig() hasn't been called.
25
+ */
26
+ export function getConfig() {
27
+ if (!_config) {
28
+ throw new Error('Config not loaded. Call loadConfig() first.');
29
+ }
30
+ return _config;
31
+ }
32
+ /**
33
+ * Check if API key is configured.
34
+ */
35
+ export function hasApiKey() {
36
+ const config = getConfig();
37
+ return !!config.apiKey;
38
+ }
39
+ /**
40
+ * Reset config (useful for testing).
41
+ */
42
+ export function resetConfig() {
43
+ _config = null;
44
+ }
45
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,0BAA0B,CAAC;IAC5D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACjC,CAAC,CAAC;AAIH,IAAI,OAAO,GAAkB,IAAI,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,UAAU;IACzB,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAE5B,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC;QAC5B,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,0BAA0B;QACtE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB;QACxC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,MAAM;KACpD,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS;IACxB,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS;IACxB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IAC1B,OAAO,GAAG,IAAI,CAAC;AAChB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Get the current device ID.
3
+ */
4
+ export declare function getDeviceId(): string;
5
+ /**
6
+ * Check if demo was used today.
7
+ */
8
+ export declare function wasDemoUsedToday(): boolean;
9
+ /**
10
+ * Record demo usage.
11
+ */
12
+ export declare function recordDemoUsage(): void;
13
+ /**
14
+ * Get demo usage stats.
15
+ */
16
+ export declare function getDemoUsageStats(): {
17
+ usedToday: boolean;
18
+ totalUsage: number;
19
+ deviceId: string;
20
+ };
21
+ //# sourceMappingURL=device-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-id.d.ts","sourceRoot":"","sources":["../../src/utils/device-id.ts"],"names":[],"mappings":"AA2EA;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAgB1C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAMtC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAOhG"}
@@ -0,0 +1,101 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
3
+ import { homedir } from 'os';
4
+ import { join } from 'path';
5
+ import { debug, warn } from './logger.js';
6
+ /**
7
+ * Get the path to the device data file.
8
+ */
9
+ function getDeviceFilePath() {
10
+ const configDir = join(homedir(), '.stacksfinder');
11
+ return join(configDir, 'device.json');
12
+ }
13
+ /**
14
+ * Ensure the config directory exists.
15
+ */
16
+ function ensureConfigDir() {
17
+ const configDir = join(homedir(), '.stacksfinder');
18
+ if (!existsSync(configDir)) {
19
+ mkdirSync(configDir, { recursive: true });
20
+ debug('Created config directory', { path: configDir });
21
+ }
22
+ }
23
+ /**
24
+ * Load device data from file, or create new if doesn't exist.
25
+ */
26
+ function loadDeviceData() {
27
+ const filePath = getDeviceFilePath();
28
+ if (existsSync(filePath)) {
29
+ try {
30
+ const raw = readFileSync(filePath, 'utf-8');
31
+ const data = JSON.parse(raw);
32
+ debug('Loaded device data', { deviceId: data.deviceId.slice(0, 8) + '...' });
33
+ return data;
34
+ }
35
+ catch (err) {
36
+ warn('Failed to parse device data, creating new', err);
37
+ }
38
+ }
39
+ // Create new device data
40
+ ensureConfigDir();
41
+ const newData = {
42
+ deviceId: randomUUID(),
43
+ createdAt: new Date().toISOString(),
44
+ lastDemoUsedAt: null,
45
+ demoUsageCount: 0
46
+ };
47
+ saveDeviceData(newData);
48
+ debug('Created new device data', { deviceId: newData.deviceId.slice(0, 8) + '...' });
49
+ return newData;
50
+ }
51
+ /**
52
+ * Save device data to file.
53
+ */
54
+ function saveDeviceData(data) {
55
+ ensureConfigDir();
56
+ const filePath = getDeviceFilePath();
57
+ writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
58
+ }
59
+ /**
60
+ * Get the current device ID.
61
+ */
62
+ export function getDeviceId() {
63
+ return loadDeviceData().deviceId;
64
+ }
65
+ /**
66
+ * Check if demo was used today.
67
+ */
68
+ export function wasDemoUsedToday() {
69
+ const data = loadDeviceData();
70
+ if (!data.lastDemoUsedAt) {
71
+ return false;
72
+ }
73
+ const lastUsed = new Date(data.lastDemoUsedAt);
74
+ const today = new Date();
75
+ // Compare date only (ignore time)
76
+ return (lastUsed.getUTCFullYear() === today.getUTCFullYear() &&
77
+ lastUsed.getUTCMonth() === today.getUTCMonth() &&
78
+ lastUsed.getUTCDate() === today.getUTCDate());
79
+ }
80
+ /**
81
+ * Record demo usage.
82
+ */
83
+ export function recordDemoUsage() {
84
+ const data = loadDeviceData();
85
+ data.lastDemoUsedAt = new Date().toISOString();
86
+ data.demoUsageCount++;
87
+ saveDeviceData(data);
88
+ debug('Recorded demo usage', { count: data.demoUsageCount });
89
+ }
90
+ /**
91
+ * Get demo usage stats.
92
+ */
93
+ export function getDemoUsageStats() {
94
+ const data = loadDeviceData();
95
+ return {
96
+ usedToday: wasDemoUsedToday(),
97
+ totalUsage: data.demoUsageCount,
98
+ deviceId: data.deviceId
99
+ };
100
+ }
101
+ //# sourceMappingURL=device-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-id.js","sourceRoot":"","sources":["../../src/utils/device-id.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Error codes for MCP tool responses.
3
+ */
4
+ export declare enum ErrorCode {
5
+ UNAUTHORIZED = "UNAUTHORIZED",
6
+ NOT_FOUND = "NOT_FOUND",
7
+ RATE_LIMITED = "RATE_LIMITED",
8
+ INVALID_INPUT = "INVALID_INPUT",
9
+ API_ERROR = "API_ERROR",
10
+ TECH_NOT_FOUND = "TECH_NOT_FOUND",
11
+ TIMEOUT = "TIMEOUT",
12
+ CONFIG_ERROR = "CONFIG_ERROR"
13
+ }
14
+ /**
15
+ * MCP-friendly error with code and optional suggestions.
16
+ */
17
+ export declare class McpError extends Error {
18
+ code: ErrorCode;
19
+ suggestions?: string[];
20
+ constructor(code: ErrorCode, message: string, suggestions?: string[]);
21
+ /**
22
+ * Format error for MCP response text.
23
+ */
24
+ toResponseText(): string;
25
+ }
26
+ /**
27
+ * Calculate Levenshtein distance between two strings.
28
+ */
29
+ export declare function levenshteinDistance(a: string, b: string): number;
30
+ /**
31
+ * Find similar strings using Levenshtein distance.
32
+ */
33
+ export declare function findSimilar(input: string, candidates: string[], limit?: number): string[];
34
+ /**
35
+ * Create error for unknown technology ID.
36
+ */
37
+ export declare function techNotFoundError(techId: string, availableTechs: string[]): McpError;
38
+ /**
39
+ * Map HTTP status codes to error codes.
40
+ */
41
+ export declare function httpStatusToErrorCode(status: number): ErrorCode;
42
+ /**
43
+ * Create error from HTTP response.
44
+ */
45
+ export declare function apiError(status: number, message?: string): McpError;
46
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,SAAS;IACpB,YAAY,iBAAiB;IAC7B,SAAS,cAAc;IACvB,YAAY,iBAAiB;IAC7B,aAAa,kBAAkB;IAC/B,SAAS,cAAc;IACvB,cAAc,mBAAmB;IACjC,OAAO,YAAY;IACnB,YAAY,iBAAiB;CAC7B;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;gBAEX,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE;IAOpE;;OAEG;IACH,cAAc,IAAI,MAAM;CAOxB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,SAAI,GAAG,MAAM,EAAE,CAYpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,QAAQ,CAQpF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAe/D;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAcnE"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Error codes for MCP tool responses.
3
+ */
4
+ export var ErrorCode;
5
+ (function (ErrorCode) {
6
+ ErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
7
+ ErrorCode["NOT_FOUND"] = "NOT_FOUND";
8
+ ErrorCode["RATE_LIMITED"] = "RATE_LIMITED";
9
+ ErrorCode["INVALID_INPUT"] = "INVALID_INPUT";
10
+ ErrorCode["API_ERROR"] = "API_ERROR";
11
+ ErrorCode["TECH_NOT_FOUND"] = "TECH_NOT_FOUND";
12
+ ErrorCode["TIMEOUT"] = "TIMEOUT";
13
+ ErrorCode["CONFIG_ERROR"] = "CONFIG_ERROR";
14
+ })(ErrorCode || (ErrorCode = {}));
15
+ /**
16
+ * MCP-friendly error with code and optional suggestions.
17
+ */
18
+ export class McpError extends Error {
19
+ code;
20
+ suggestions;
21
+ constructor(code, message, suggestions) {
22
+ super(message);
23
+ this.name = 'McpError';
24
+ this.code = code;
25
+ this.suggestions = suggestions;
26
+ }
27
+ /**
28
+ * Format error for MCP response text.
29
+ */
30
+ toResponseText() {
31
+ let text = `**Error (${this.code})**: ${this.message}`;
32
+ if (this.suggestions && this.suggestions.length > 0) {
33
+ text += `\n\n**Suggestions**:\n${this.suggestions.map((s) => `- ${s}`).join('\n')}`;
34
+ }
35
+ return text;
36
+ }
37
+ }
38
+ /**
39
+ * Calculate Levenshtein distance between two strings.
40
+ */
41
+ export function levenshteinDistance(a, b) {
42
+ const matrix = [];
43
+ for (let i = 0; i <= b.length; i++) {
44
+ matrix[i] = [i];
45
+ }
46
+ for (let j = 0; j <= a.length; j++) {
47
+ matrix[0][j] = j;
48
+ }
49
+ for (let i = 1; i <= b.length; i++) {
50
+ for (let j = 1; j <= a.length; j++) {
51
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
52
+ matrix[i][j] = matrix[i - 1][j - 1];
53
+ }
54
+ else {
55
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
56
+ matrix[i][j - 1] + 1, // insertion
57
+ matrix[i - 1][j] + 1 // deletion
58
+ );
59
+ }
60
+ }
61
+ }
62
+ return matrix[b.length][a.length];
63
+ }
64
+ /**
65
+ * Find similar strings using Levenshtein distance.
66
+ */
67
+ export function findSimilar(input, candidates, limit = 3) {
68
+ const inputLower = input.toLowerCase();
69
+ return candidates
70
+ .map((candidate) => ({
71
+ candidate,
72
+ distance: levenshteinDistance(inputLower, candidate.toLowerCase())
73
+ }))
74
+ .filter((item) => item.distance <= 3) // Max 3 edits
75
+ .sort((a, b) => a.distance - b.distance)
76
+ .slice(0, limit)
77
+ .map((item) => item.candidate);
78
+ }
79
+ /**
80
+ * Create error for unknown technology ID.
81
+ */
82
+ export function techNotFoundError(techId, availableTechs) {
83
+ const similar = findSimilar(techId, availableTechs);
84
+ const suggestions = similar.length > 0
85
+ ? [`Did you mean: ${similar.join(', ')}?`, 'Use list_technologies to see all available IDs.']
86
+ : ['Use list_technologies to see all available technology IDs.'];
87
+ return new McpError(ErrorCode.TECH_NOT_FOUND, `Unknown technology: "${techId}"`, suggestions);
88
+ }
89
+ /**
90
+ * Map HTTP status codes to error codes.
91
+ */
92
+ export function httpStatusToErrorCode(status) {
93
+ switch (status) {
94
+ case 401:
95
+ case 403:
96
+ return ErrorCode.UNAUTHORIZED;
97
+ case 404:
98
+ return ErrorCode.NOT_FOUND;
99
+ case 429:
100
+ return ErrorCode.RATE_LIMITED;
101
+ case 400:
102
+ case 422:
103
+ return ErrorCode.INVALID_INPUT;
104
+ default:
105
+ return ErrorCode.API_ERROR;
106
+ }
107
+ }
108
+ /**
109
+ * Create error from HTTP response.
110
+ */
111
+ export function apiError(status, message) {
112
+ const code = httpStatusToErrorCode(status);
113
+ const defaultMessages = {
114
+ [ErrorCode.UNAUTHORIZED]: 'API key is invalid or missing. Set STACKSFINDER_API_KEY.',
115
+ [ErrorCode.NOT_FOUND]: 'Resource not found.',
116
+ [ErrorCode.RATE_LIMITED]: 'Rate limit exceeded. Please try again later.',
117
+ [ErrorCode.INVALID_INPUT]: 'Invalid request parameters.',
118
+ [ErrorCode.API_ERROR]: `API request failed with status ${status}.`,
119
+ [ErrorCode.TECH_NOT_FOUND]: 'Technology not found.',
120
+ [ErrorCode.TIMEOUT]: 'Request timed out.',
121
+ [ErrorCode.CONFIG_ERROR]: 'Configuration error.'
122
+ };
123
+ return new McpError(code, message || defaultMessages[code]);
124
+ }
125
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * stderr-only logger for MCP server.
3
+ * stdout is reserved for the MCP protocol (stdio transport).
4
+ */
5
+ /**
6
+ * Enable or disable debug logging.
7
+ */
8
+ export declare function setDebug(enabled: boolean): void;
9
+ /**
10
+ * Check if debug logging is enabled.
11
+ */
12
+ export declare function isDebugEnabled(): boolean;
13
+ /**
14
+ * Log a debug message to stderr (only if debug is enabled).
15
+ */
16
+ export declare function debug(message: string, data?: unknown): void;
17
+ /**
18
+ * Log an info message to stderr.
19
+ */
20
+ export declare function info(message: string): void;
21
+ /**
22
+ * Log a warning message to stderr.
23
+ */
24
+ export declare function warn(message: string, err?: unknown): void;
25
+ /**
26
+ * Log an error message to stderr.
27
+ */
28
+ export declare function error(message: string, err?: unknown): void;
29
+ export declare const log: {
30
+ debug: typeof debug;
31
+ info: typeof info;
32
+ warn: typeof warn;
33
+ error: typeof error;
34
+ setDebug: typeof setDebug;
35
+ isDebugEnabled: typeof isDebugEnabled;
36
+ };
37
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAQ3D;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG1C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CASzD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CAY1D;AAED,eAAO,MAAM,GAAG;;;;;;;CAAyD,CAAC"}