cache-overflow-mcp 0.3.7 → 0.3.8

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 (39) hide show
  1. package/README.md +28 -76
  2. package/dist/client.d.ts +5 -1
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +157 -9
  5. package/dist/client.js.map +1 -1
  6. package/dist/config.js +1 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/tools/find-solution.d.ts.map +1 -1
  9. package/dist/tools/find-solution.js +74 -3
  10. package/dist/tools/find-solution.js.map +1 -1
  11. package/dist/tools/publish-solution.d.ts.map +1 -1
  12. package/dist/tools/publish-solution.js +68 -3
  13. package/dist/tools/publish-solution.js.map +1 -1
  14. package/dist/tools/submit-feedback.d.ts.map +1 -1
  15. package/dist/tools/submit-feedback.js +62 -2
  16. package/dist/tools/submit-feedback.js.map +1 -1
  17. package/dist/tools/submit-verification.d.ts.map +1 -1
  18. package/dist/tools/submit-verification.js +62 -2
  19. package/dist/tools/submit-verification.js.map +1 -1
  20. package/dist/tools/unlock-solution.d.ts.map +1 -1
  21. package/dist/tools/unlock-solution.js +57 -2
  22. package/dist/tools/unlock-solution.js.map +1 -1
  23. package/dist/ui/verification-dialog.d.ts +1 -1
  24. package/dist/ui/verification-dialog.d.ts.map +1 -1
  25. package/dist/ui/verification-dialog.js +78 -4
  26. package/dist/ui/verification-dialog.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/client.ts +177 -9
  29. package/src/config.ts +1 -1
  30. package/src/tools/find-solution.ts +75 -3
  31. package/src/tools/publish-solution.ts +71 -3
  32. package/src/tools/submit-feedback.ts +62 -2
  33. package/src/tools/submit-verification.ts +62 -2
  34. package/src/tools/unlock-solution.ts +56 -2
  35. package/src/ui/verification-dialog.ts +84 -4
  36. package/E2E-TESTING.md +0 -195
  37. package/TROUBLESHOOTING.md +0 -219
  38. package/scripts/mock-server.js +0 -37
  39. package/scripts/view-logs.js +0 -125
package/src/client.ts CHANGED
@@ -5,10 +5,32 @@ import { logger } from './logger.js';
5
5
  export class CacheOverflowClient {
6
6
  private apiUrl: string;
7
7
  private authToken: string | undefined;
8
+ private timeout: number;
8
9
 
9
- constructor(apiUrl?: string) {
10
+ constructor(apiUrl?: string, token?: string, timeout?: number) {
10
11
  this.apiUrl = apiUrl ?? config.api.url;
11
- this.authToken = config.auth.token;
12
+ this.authToken = token ?? config.auth.token;
13
+ this.timeout = timeout ?? config.api.timeout;
14
+
15
+ // Validate URL format (#13 - URL Validation)
16
+ try {
17
+ new URL(this.apiUrl);
18
+ } catch (error) {
19
+ logger.error('Invalid CACHE_OVERFLOW_URL format', error as Error, {
20
+ url: this.apiUrl,
21
+ errorType: 'INVALID_API_URL',
22
+ });
23
+ throw new Error(`Invalid API URL: ${this.apiUrl}`);
24
+ }
25
+
26
+ // Validate token format (#12 - Token Validation)
27
+ if (!this.authToken) {
28
+ logger.warn('No CACHE_OVERFLOW_TOKEN provided - all API calls will fail', {});
29
+ } else if (!this.authToken.startsWith('co_')) {
30
+ logger.warn('Invalid token format - tokens should start with "co_"', {
31
+ tokenPrefix: this.authToken.substring(0, 3),
32
+ });
33
+ }
12
34
  }
13
35
 
14
36
  private async request<T>(
@@ -26,11 +48,33 @@ export class CacheOverflowClient {
26
48
 
27
49
  const url = `${this.apiUrl}${path}`;
28
50
 
51
+ // #11 - Add request logging for debugging
52
+ logger.info('API request', {
53
+ method,
54
+ path,
55
+ hasBody: !!body,
56
+ });
57
+
58
+ // #1 - Enforce request timeouts
59
+ const controller = new AbortController();
60
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
61
+
29
62
  try {
30
63
  const response = await fetch(url, {
31
64
  method,
32
65
  headers,
33
66
  body: body ? JSON.stringify(body) : undefined,
67
+ signal: controller.signal,
68
+ });
69
+
70
+ clearTimeout(timeoutId);
71
+
72
+ // #11 - Add response logging
73
+ logger.info('API response', {
74
+ method,
75
+ path,
76
+ status: response.status,
77
+ ok: response.ok,
34
78
  });
35
79
 
36
80
  // Read response as text first, then try to parse as JSON
@@ -60,18 +104,66 @@ export class CacheOverflowClient {
60
104
 
61
105
  if (!response.ok) {
62
106
  const errorMessage = (data.error as string) ?? 'Unknown error';
107
+
108
+ // #7 - Detect and handle rate limiting (429)
109
+ if (response.status === 429) {
110
+ const retryAfter = response.headers.get('Retry-After') || '60';
111
+ logger.warn('Rate limit exceeded', {
112
+ method,
113
+ path,
114
+ retryAfter,
115
+ errorType: 'RATE_LIMIT',
116
+ });
117
+ return {
118
+ success: false,
119
+ error: `Rate limit exceeded. Please wait ${retryAfter} seconds before trying again. Consider using solutions with human_verification_required=false to avoid token costs.`,
120
+ };
121
+ }
122
+
123
+ // #8 - Add status code to error messages with categorization
124
+ let category = '';
125
+ if (response.status >= 500) {
126
+ category = 'Server error - try again later';
127
+ } else if (response.status === 401 || response.status === 403) {
128
+ category = 'Authentication error - check your CACHE_OVERFLOW_TOKEN';
129
+ } else if (response.status >= 400) {
130
+ category = 'Request error - check your input';
131
+ }
132
+
63
133
  logger.error('API request failed', undefined, {
64
134
  method,
65
135
  path,
66
136
  statusCode: response.status,
67
137
  errorMessage,
138
+ category,
68
139
  errorType: 'API_ERROR',
69
140
  });
70
- return { success: false, error: errorMessage };
141
+
142
+ return {
143
+ success: false,
144
+ error: `${errorMessage} (${category})`,
145
+ };
71
146
  }
72
147
 
73
148
  return { success: true, data: data as T };
74
149
  } catch (error) {
150
+ clearTimeout(timeoutId);
151
+
152
+ // #1 - Handle timeout errors specifically
153
+ if ((error as Error).name === 'AbortError') {
154
+ logger.error('Request timed out', error as Error, {
155
+ method,
156
+ path,
157
+ url,
158
+ timeout: this.timeout,
159
+ errorType: 'TIMEOUT',
160
+ });
161
+ return {
162
+ success: false,
163
+ error: `Request timed out after ${this.timeout}ms. The server may be experiencing issues.`,
164
+ };
165
+ }
166
+
75
167
  logger.error('Network or fetch error during API request', error as Error, {
76
168
  method,
77
169
  path,
@@ -79,13 +171,89 @@ export class CacheOverflowClient {
79
171
  errorType: 'NETWORK_ERROR',
80
172
  });
81
173
 
82
- // Re-throw network errors so they can be handled by the caller
174
+ // Re-throw network errors so they can be handled by retry logic
83
175
  throw error;
84
176
  }
85
177
  }
86
178
 
179
+ // #2 - Add retry logic for network failures
180
+ private async requestWithRetry<T>(
181
+ method: string,
182
+ path: string,
183
+ body?: unknown,
184
+ retries = 3
185
+ ): Promise<ApiResponse<T>> {
186
+ let lastError: Error | undefined;
187
+
188
+ for (let attempt = 0; attempt < retries; attempt++) {
189
+ try {
190
+ const result = await this.request<T>(method, path, body);
191
+
192
+ // If successful or last attempt, return the result
193
+ if (result.success || attempt === retries - 1) {
194
+ return result;
195
+ }
196
+
197
+ // Check if we should retry based on the error
198
+ const shouldRetry = this.shouldRetryError(result.error);
199
+ if (shouldRetry) {
200
+ const delay = Math.pow(3, attempt) * 100; // 0, 300, 900ms exponential backoff
201
+ logger.info('Retrying request after delay', {
202
+ method,
203
+ path,
204
+ attempt: attempt + 1,
205
+ maxRetries: retries,
206
+ delay,
207
+ });
208
+ await new Promise(resolve => setTimeout(resolve, delay));
209
+ continue;
210
+ }
211
+
212
+ // Don't retry client errors (4xx)
213
+ return result;
214
+ } catch (error) {
215
+ lastError = error as Error;
216
+
217
+ // Only retry network errors, not client errors
218
+ if (attempt < retries - 1 && this.isNetworkError(error as Error)) {
219
+ const delay = Math.pow(3, attempt) * 100;
220
+ logger.info('Retrying after network error', {
221
+ method,
222
+ path,
223
+ attempt: attempt + 1,
224
+ maxRetries: retries,
225
+ delay,
226
+ error: (error as Error).message,
227
+ });
228
+ await new Promise(resolve => setTimeout(resolve, delay));
229
+ } else {
230
+ throw error;
231
+ }
232
+ }
233
+ }
234
+
235
+ throw lastError;
236
+ }
237
+
238
+ private shouldRetryError(error?: string): boolean {
239
+ if (!error) return false;
240
+
241
+ // Retry on server errors (5xx) or timeout errors
242
+ return error.includes('Server error') ||
243
+ error.includes('timed out') ||
244
+ error.includes('network');
245
+ }
246
+
247
+ private isNetworkError(error: Error): boolean {
248
+ // Network errors typically have these names or messages
249
+ return error.name === 'TypeError' ||
250
+ error.name === 'NetworkError' ||
251
+ error.message.includes('fetch') ||
252
+ error.message.includes('network');
253
+ }
254
+
87
255
  async findSolution(query: string): Promise<ApiResponse<FindSolutionResult[]>> {
88
- const result = await this.request<Array<{ id: string; query_title: string; solution_body?: string; human_verification_required: boolean }>>(
256
+ const result = await this.requestWithRetry<Array<{ id: string; query_title: string; solution_body?: string; human_verification_required: boolean }>>(
89
257
  'GET',
90
258
  `/solutions/search?query=${encodeURIComponent(query)}`
91
259
  );
@@ -105,14 +273,14 @@ export class CacheOverflowClient {
105
273
  }
106
274
 
107
275
  async unlockSolution(solutionId: string): Promise<ApiResponse<Solution>> {
108
- return this.request('POST', `/solutions/${solutionId}/unlock`);
276
+ return this.requestWithRetry('POST', `/solutions/${solutionId}/unlock`);
109
277
  }
110
278
 
111
279
  async publishSolution(
112
280
  queryTitle: string,
113
281
  solutionBody: string
114
282
  ): Promise<ApiResponse<Solution>> {
115
- return this.request('POST', '/solutions', {
283
+ return this.requestWithRetry('POST', '/solutions', {
116
284
  query_title: queryTitle,
117
285
  solution_body: solutionBody,
118
286
  });
@@ -122,7 +290,7 @@ export class CacheOverflowClient {
122
290
  solutionId: string,
123
291
  isSafe: boolean
124
292
  ): Promise<ApiResponse<void>> {
125
- return this.request('POST', `/solutions/${solutionId}/verify`, {
293
+ return this.requestWithRetry('POST', `/solutions/${solutionId}/verify`, {
126
294
  is_safe: isSafe,
127
295
  });
128
296
  }
@@ -131,7 +299,7 @@ export class CacheOverflowClient {
131
299
  solutionId: string,
132
300
  isUseful: boolean
133
301
  ): Promise<ApiResponse<void>> {
134
- return this.request('POST', `/solutions/${solutionId}/feedback`, {
302
+ return this.requestWithRetry('POST', `/solutions/${solutionId}/feedback`, {
135
303
  is_useful: isUseful,
136
304
  });
137
305
  }
package/src/config.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export const config = {
2
2
  api: {
3
- url: process.env.CACHE_OVERFLOW_URL ?? 'https://cache-overflow.onrender.com/api',
3
+ url: process.env.CACHE_OVERFLOW_URL ?? 'https://cacheoverflow.dev/api',
4
4
  timeout: parseInt(process.env.CACHE_OVERFLOW_TIMEOUT ?? '30000'),
5
5
  },
6
6
  auth: {
@@ -1,5 +1,35 @@
1
1
  import { ToolDefinition } from './index.js';
2
2
  import { showVerificationDialog } from '../ui/verification-dialog.js';
3
+ import { config } from '../config.js';
4
+
5
+ // #6 - Improve error messages with context
6
+ function getErrorTitle(error: string): string {
7
+ if (error.includes('timeout') || error.includes('timed out')) return 'Request Timed Out';
8
+ if (error.includes('network') || error.includes('fetch')) return 'Network Connection Failed';
9
+ if (error.includes('balance') || error.includes('insufficient')) return 'Insufficient Token Balance';
10
+ if (error.includes('auth') || error.includes('Authentication')) return 'Authentication Failed';
11
+ if (error.includes('Rate limit')) return 'Rate Limit Exceeded';
12
+ return 'Operation Failed';
13
+ }
14
+
15
+ function getRecoverySuggestions(error: string): string {
16
+ if (error.includes('timeout') || error.includes('timed out')) {
17
+ return '- Check your internet connection\n- Try again in a moment\n- The server may be experiencing high load';
18
+ }
19
+ if (error.includes('balance') || error.includes('insufficient')) {
20
+ return '- Check your balance with another tool or API call\n- Earn tokens by publishing solutions\n- Look for solutions with human_verification_required=false';
21
+ }
22
+ if (error.includes('auth') || error.includes('Authentication')) {
23
+ return '- Verify your CACHE_OVERFLOW_TOKEN environment variable is set correctly\n- Token should start with "co_"\n- Check if your token has expired';
24
+ }
25
+ if (error.includes('Rate limit')) {
26
+ return '- Wait the specified time before retrying\n- Consider using solutions with human_verification_required=false to avoid token costs';
27
+ }
28
+ if (error.includes('network') || error.includes('fetch')) {
29
+ return '- Check your internet connection\n- Verify the CACHE_OVERFLOW_URL is correct\n- Try again in a moment';
30
+ }
31
+ return '- Check the log file for details\n- Verify your CACHE_OVERFLOW_TOKEN is valid\n- Try again in a moment';
32
+ }
3
33
 
4
34
  export const findSolution: ToolDefinition = {
5
35
  definition: {
@@ -18,12 +48,44 @@ export const findSolution: ToolDefinition = {
18
48
  },
19
49
  },
20
50
  handler: async (args, client) => {
21
- const query = args.query as string;
51
+ // #5 - Add input validation
52
+ const query = (args.query as string || '').trim();
53
+
54
+ if (!query) {
55
+ return {
56
+ content: [{ type: 'text', text: 'Error: Query cannot be empty. Please provide a description of the problem you are trying to solve.' }],
57
+ };
58
+ }
59
+
60
+ if (query.length < 5) {
61
+ return {
62
+ content: [{ type: 'text', text: 'Error: Query must be at least 5 characters long. Please provide more details about the problem.' }],
63
+ };
64
+ }
65
+
66
+ if (query.length > 500) {
67
+ return {
68
+ content: [{ type: 'text', text: 'Error: Query must be less than 500 characters. Please provide a more concise description.' }],
69
+ };
70
+ }
71
+
22
72
  const result = await client.findSolution(query);
23
73
 
24
74
  if (!result.success) {
75
+ // #6 - Improve error messages with context
76
+ const errorMessage = [
77
+ `❌ ${getErrorTitle(result.error || '')}`,
78
+ '',
79
+ result.error,
80
+ '',
81
+ '💡 **What to try:**',
82
+ getRecoverySuggestions(result.error || ''),
83
+ '',
84
+ `📋 **Logs**: Check ${config.logging.logDir || '~/.cache-overflow'}/cache-overflow-mcp.log for details`,
85
+ ].join('\n');
86
+
25
87
  return {
26
- content: [{ type: 'text', text: `Error: ${result.error}` }],
88
+ content: [{ type: 'text', text: errorMessage }],
27
89
  };
28
90
  }
29
91
 
@@ -37,7 +99,17 @@ export const findSolution: ToolDefinition = {
37
99
 
38
100
  // If user made a choice (not cancelled), submit verification
39
101
  if (verificationResult !== null) {
40
- await client.submitVerification(solution.solution_id, verificationResult);
102
+ // #10 - Catch verification submission errors
103
+ try {
104
+ const submitResult = await client.submitVerification(solution.solution_id, verificationResult);
105
+ if (!submitResult.success) {
106
+ // Log but don't fail - user still gets the solution content
107
+ console.warn(`Warning: Failed to submit verification for solution ${solution.solution_id}: ${submitResult.error}`);
108
+ }
109
+ } catch (error) {
110
+ // Log but don't fail - user still gets the solution content
111
+ console.warn(`Warning: Error submitting verification for solution ${solution.solution_id}:`, error);
112
+ }
41
113
  }
42
114
  }
43
115
  }
@@ -1,4 +1,34 @@
1
1
  import { ToolDefinition } from './index.js';
2
+ import { config } from '../config.js';
3
+
4
+ // #6 - Improve error messages with context
5
+ function getErrorTitle(error: string): string {
6
+ if (error.includes('timeout') || error.includes('timed out')) return 'Request Timed Out';
7
+ if (error.includes('network') || error.includes('fetch')) return 'Network Connection Failed';
8
+ if (error.includes('auth') || error.includes('Authentication')) return 'Authentication Failed';
9
+ if (error.includes('Rate limit')) return 'Rate Limit Exceeded';
10
+ if (error.includes('duplicate') || error.includes('already exists')) return 'Duplicate Solution';
11
+ return 'Operation Failed';
12
+ }
13
+
14
+ function getRecoverySuggestions(error: string): string {
15
+ if (error.includes('timeout') || error.includes('timed out')) {
16
+ return '- Check your internet connection\n- Try again in a moment\n- The server may be experiencing high load';
17
+ }
18
+ if (error.includes('auth') || error.includes('Authentication')) {
19
+ return '- Verify your CACHE_OVERFLOW_TOKEN environment variable is set correctly\n- Token should start with "co_"\n- Check if your token has expired';
20
+ }
21
+ if (error.includes('Rate limit')) {
22
+ return '- Wait the specified time before retrying';
23
+ }
24
+ if (error.includes('duplicate') || error.includes('already exists')) {
25
+ return '- A similar solution may already exist\n- Try searching first with find_solution\n- Consider adding more specific details to your solution';
26
+ }
27
+ if (error.includes('network') || error.includes('fetch')) {
28
+ return '- Check your internet connection\n- Verify the CACHE_OVERFLOW_URL is correct\n- Try again in a moment';
29
+ }
30
+ return '- Check the log file for details\n- Verify your CACHE_OVERFLOW_TOKEN is valid\n- Try again in a moment';
31
+ }
2
32
 
3
33
  export const publishSolution: ToolDefinition = {
4
34
  definition: {
@@ -21,13 +51,51 @@ export const publishSolution: ToolDefinition = {
21
51
  },
22
52
  },
23
53
  handler: async (args, client) => {
24
- const queryTitle = args.query_title as string;
25
- const solutionBody = args.solution_body as string;
54
+ // #5 - Add input validation
55
+ const queryTitle = (args.query_title as string || '').trim();
56
+ const solutionBody = (args.solution_body as string || '').trim();
57
+
58
+ if (!queryTitle || queryTitle.length < 5) {
59
+ return {
60
+ content: [{ type: 'text', text: 'Error: Title must be at least 5 characters. Please provide a clear, descriptive title.' }],
61
+ };
62
+ }
63
+
64
+ if (queryTitle.length > 200) {
65
+ return {
66
+ content: [{ type: 'text', text: 'Error: Title must be less than 200 characters. Please use a more concise title.' }],
67
+ };
68
+ }
69
+
70
+ if (!solutionBody || solutionBody.length < 10) {
71
+ return {
72
+ content: [{ type: 'text', text: 'Error: Solution body must be at least 10 characters. Please provide a detailed solution with Problem, Root Cause, Solution, and Verification sections.' }],
73
+ };
74
+ }
75
+
76
+ if (solutionBody.length > 50000) {
77
+ return {
78
+ content: [{ type: 'text', text: 'Error: Solution body must be less than 50,000 characters. Please be more concise.' }],
79
+ };
80
+ }
81
+
26
82
  const result = await client.publishSolution(queryTitle, solutionBody);
27
83
 
28
84
  if (!result.success) {
85
+ // #6 - Improve error messages with context
86
+ const errorMessage = [
87
+ `❌ ${getErrorTitle(result.error || '')}`,
88
+ '',
89
+ result.error,
90
+ '',
91
+ '💡 **What to try:**',
92
+ getRecoverySuggestions(result.error || ''),
93
+ '',
94
+ `📋 **Logs**: Check ${config.logging.logDir || '~/.cache-overflow'}/cache-overflow-mcp.log for details`,
95
+ ].join('\n');
96
+
29
97
  return {
30
- content: [{ type: 'text', text: `Error: ${result.error}` }],
98
+ content: [{ type: 'text', text: errorMessage }],
31
99
  };
32
100
  }
33
101
 
@@ -1,4 +1,38 @@
1
1
  import { ToolDefinition } from './index.js';
2
+ import { config } from '../config.js';
3
+
4
+ // #6 - Improve error messages with context
5
+ function getErrorTitle(error: string): string {
6
+ if (error.includes('timeout') || error.includes('timed out')) return 'Request Timed Out';
7
+ if (error.includes('network') || error.includes('fetch')) return 'Network Connection Failed';
8
+ if (error.includes('auth') || error.includes('Authentication')) return 'Authentication Failed';
9
+ if (error.includes('Rate limit')) return 'Rate Limit Exceeded';
10
+ if (error.includes('not found') || error.includes('404')) return 'Solution Not Found';
11
+ if (error.includes('already submitted')) return 'Feedback Already Submitted';
12
+ return 'Operation Failed';
13
+ }
14
+
15
+ function getRecoverySuggestions(error: string): string {
16
+ if (error.includes('timeout') || error.includes('timed out')) {
17
+ return '- Check your internet connection\n- Try again in a moment\n- The server may be experiencing high load';
18
+ }
19
+ if (error.includes('auth') || error.includes('Authentication')) {
20
+ return '- Verify your CACHE_OVERFLOW_TOKEN environment variable is set correctly\n- Token should start with "co_"\n- Check if your token has expired';
21
+ }
22
+ if (error.includes('Rate limit')) {
23
+ return '- Wait the specified time before retrying';
24
+ }
25
+ if (error.includes('not found') || error.includes('404')) {
26
+ return '- Verify the solution_id is correct\n- The solution may have been deleted';
27
+ }
28
+ if (error.includes('already submitted')) {
29
+ return '- You have already submitted feedback for this solution\n- No action needed';
30
+ }
31
+ if (error.includes('network') || error.includes('fetch')) {
32
+ return '- Check your internet connection\n- Verify the CACHE_OVERFLOW_URL is correct\n- Try again in a moment';
33
+ }
34
+ return '- Check the log file for details\n- Verify your CACHE_OVERFLOW_TOKEN is valid\n- Try again in a moment';
35
+ }
2
36
 
3
37
  export const submitFeedback: ToolDefinition = {
4
38
  definition: {
@@ -21,13 +55,39 @@ export const submitFeedback: ToolDefinition = {
21
55
  },
22
56
  },
23
57
  handler: async (args, client) => {
24
- const solutionId = args.solution_id as string;
58
+ // #5 - Add input validation
59
+ const solutionId = (args.solution_id as string || '').trim();
25
60
  const isUseful = args.is_useful as boolean;
61
+
62
+ if (!solutionId) {
63
+ return {
64
+ content: [{ type: 'text', text: 'Error: solution_id cannot be empty. Please provide a valid solution ID.' }],
65
+ };
66
+ }
67
+
68
+ if (typeof isUseful !== 'boolean') {
69
+ return {
70
+ content: [{ type: 'text', text: 'Error: is_useful must be a boolean (true or false).' }],
71
+ };
72
+ }
73
+
26
74
  const result = await client.submitFeedback(solutionId, isUseful);
27
75
 
28
76
  if (!result.success) {
77
+ // #6 - Improve error messages with context
78
+ const errorMessage = [
79
+ `❌ ${getErrorTitle(result.error || '')}`,
80
+ '',
81
+ result.error,
82
+ '',
83
+ '💡 **What to try:**',
84
+ getRecoverySuggestions(result.error || ''),
85
+ '',
86
+ `📋 **Logs**: Check ${config.logging.logDir || '~/.cache-overflow'}/cache-overflow-mcp.log for details`,
87
+ ].join('\n');
88
+
29
89
  return {
30
- content: [{ type: 'text', text: `Error: ${result.error}` }],
90
+ content: [{ type: 'text', text: errorMessage }],
31
91
  };
32
92
  }
33
93
 
@@ -1,4 +1,38 @@
1
1
  import { ToolDefinition } from './index.js';
2
+ import { config } from '../config.js';
3
+
4
+ // #6 - Improve error messages with context
5
+ function getErrorTitle(error: string): string {
6
+ if (error.includes('timeout') || error.includes('timed out')) return 'Request Timed Out';
7
+ if (error.includes('network') || error.includes('fetch')) return 'Network Connection Failed';
8
+ if (error.includes('auth') || error.includes('Authentication')) return 'Authentication Failed';
9
+ if (error.includes('Rate limit')) return 'Rate Limit Exceeded';
10
+ if (error.includes('not found') || error.includes('404')) return 'Solution Not Found';
11
+ if (error.includes('already verified')) return 'Already Verified';
12
+ return 'Operation Failed';
13
+ }
14
+
15
+ function getRecoverySuggestions(error: string): string {
16
+ if (error.includes('timeout') || error.includes('timed out')) {
17
+ return '- Check your internet connection\n- Try again in a moment\n- The server may be experiencing high load';
18
+ }
19
+ if (error.includes('auth') || error.includes('Authentication')) {
20
+ return '- Verify your CACHE_OVERFLOW_TOKEN environment variable is set correctly\n- Token should start with "co_"\n- Check if your token has expired';
21
+ }
22
+ if (error.includes('Rate limit')) {
23
+ return '- Wait the specified time before retrying';
24
+ }
25
+ if (error.includes('not found') || error.includes('404')) {
26
+ return '- Verify the solution_id is correct\n- The solution may have been deleted';
27
+ }
28
+ if (error.includes('already verified')) {
29
+ return '- You have already verified this solution\n- No action needed';
30
+ }
31
+ if (error.includes('network') || error.includes('fetch')) {
32
+ return '- Check your internet connection\n- Verify the CACHE_OVERFLOW_URL is correct\n- Try again in a moment';
33
+ }
34
+ return '- Check the log file for details\n- Verify your CACHE_OVERFLOW_TOKEN is valid\n- Try again in a moment';
35
+ }
2
36
 
3
37
  export const submitVerification: ToolDefinition = {
4
38
  definition: {
@@ -21,13 +55,39 @@ export const submitVerification: ToolDefinition = {
21
55
  },
22
56
  },
23
57
  handler: async (args, client) => {
24
- const solutionId = args.solution_id as string;
58
+ // #5 - Add input validation
59
+ const solutionId = (args.solution_id as string || '').trim();
25
60
  const isSafe = args.is_safe as boolean;
61
+
62
+ if (!solutionId) {
63
+ return {
64
+ content: [{ type: 'text', text: 'Error: solution_id cannot be empty. Please provide a valid solution ID.' }],
65
+ };
66
+ }
67
+
68
+ if (typeof isSafe !== 'boolean') {
69
+ return {
70
+ content: [{ type: 'text', text: 'Error: is_safe must be a boolean (true or false).' }],
71
+ };
72
+ }
73
+
26
74
  const result = await client.submitVerification(solutionId, isSafe);
27
75
 
28
76
  if (!result.success) {
77
+ // #6 - Improve error messages with context
78
+ const errorMessage = [
79
+ `❌ ${getErrorTitle(result.error || '')}`,
80
+ '',
81
+ result.error,
82
+ '',
83
+ '💡 **What to try:**',
84
+ getRecoverySuggestions(result.error || ''),
85
+ '',
86
+ `📋 **Logs**: Check ${config.logging.logDir || '~/.cache-overflow'}/cache-overflow-mcp.log for details`,
87
+ ].join('\n');
88
+
29
89
  return {
30
- content: [{ type: 'text', text: `Error: ${result.error}` }],
90
+ content: [{ type: 'text', text: errorMessage }],
31
91
  };
32
92
  }
33
93