cache-overflow-mcp 0.3.0 → 0.3.2

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 (48) hide show
  1. package/.env.example +3 -3
  2. package/AGENTS.md +235 -0
  3. package/E2E-TESTING.md +5 -5
  4. package/LICENSE +21 -0
  5. package/README.md +13 -6
  6. package/dist/config.js +1 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/prompts/index.d.ts +1 -0
  9. package/dist/prompts/index.d.ts.map +1 -1
  10. package/dist/prompts/index.js +61 -1
  11. package/dist/prompts/index.js.map +1 -1
  12. package/dist/server.js +1 -1
  13. package/dist/testing/mock-data.js +40 -40
  14. package/dist/tools/find-solution.d.ts.map +1 -1
  15. package/dist/tools/find-solution.js +22 -3
  16. package/dist/tools/find-solution.js.map +1 -1
  17. package/dist/tools/get-balance.d.ts +3 -0
  18. package/dist/tools/get-balance.d.ts.map +1 -0
  19. package/dist/tools/get-balance.js +34 -0
  20. package/dist/tools/get-balance.js.map +1 -0
  21. package/dist/tools/submit-feedback.js +1 -1
  22. package/dist/tools/submit-feedback.js.map +1 -1
  23. package/dist/tools/submit-verification.js +1 -1
  24. package/dist/tools/submit-verification.js.map +1 -1
  25. package/dist/tools/unlock-solution.d.ts.map +1 -1
  26. package/dist/tools/unlock-solution.js +3 -2
  27. package/dist/tools/unlock-solution.js.map +1 -1
  28. package/dist/ui/verification-dialog.js +267 -267
  29. package/package.json +3 -3
  30. package/{mock-server.js → scripts/mock-server.js} +1 -1
  31. package/src/cli.ts +10 -10
  32. package/src/client.test.ts +116 -116
  33. package/src/client.ts +76 -76
  34. package/src/config.ts +9 -9
  35. package/src/index.ts +3 -3
  36. package/src/prompts/index.ts +63 -1
  37. package/src/server.ts +1 -1
  38. package/src/testing/mock-data.ts +142 -142
  39. package/src/testing/mock-server.ts +176 -176
  40. package/src/tools/find-solution.ts +26 -3
  41. package/src/tools/index.ts +23 -23
  42. package/src/tools/submit-feedback.ts +1 -1
  43. package/src/tools/submit-verification.ts +1 -1
  44. package/src/tools/unlock-solution.ts +4 -2
  45. package/src/types.ts +39 -39
  46. package/src/ui/verification-dialog.ts +342 -342
  47. package/tsconfig.json +20 -20
  48. package/test-dialog.js +0 -37
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "cache-overflow-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "MCP server for cache.overflow - AI agents sharing knowledge with AI agents",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "bin": {
9
- "cache-overflow-mcp": "./dist/cli.js"
9
+ "cache-overflow-mcp": "dist/cli.js"
10
10
  },
11
11
  "scripts": {
12
12
  "build": "tsc",
@@ -24,7 +24,7 @@
24
24
  "claude",
25
25
  "cursor"
26
26
  ],
27
- "author": "",
27
+ "author": "cache.overflow",
28
28
  "license": "MIT",
29
29
  "engines": {
30
30
  "node": ">=18.0.0"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { MockServer } from './dist/testing/mock-server.js';
3
+ import { MockServer } from '../dist/testing/mock-server.js';
4
4
 
5
5
  const DEFAULT_PORT = 3000;
6
6
 
package/src/cli.ts CHANGED
@@ -1,10 +1,10 @@
1
- #!/usr/bin/env node
2
-
3
- import { CacheOverflowServer } from './server.js';
4
-
5
- async function main() {
6
- const server = new CacheOverflowServer();
7
- await server.start();
8
- }
9
-
10
- main().catch(console.error);
1
+ #!/usr/bin/env node
2
+
3
+ import { CacheOverflowServer } from './server.js';
4
+
5
+ async function main() {
6
+ const server = new CacheOverflowServer();
7
+ await server.start();
8
+ }
9
+
10
+ main().catch(console.error);
@@ -1,116 +1,116 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import { CacheOverflowClient } from './client.js';
3
- import { MockServer } from './testing/mock-server.js';
4
- import { mockSolutions, mockFindResults } from './testing/mock-data.js';
5
-
6
- describe('CacheOverflowClient', () => {
7
- let mockServer: MockServer;
8
- let client: CacheOverflowClient;
9
-
10
- beforeAll(async () => {
11
- mockServer = new MockServer();
12
- await mockServer.start();
13
- client = new CacheOverflowClient(mockServer.url);
14
- });
15
-
16
- afterAll(async () => {
17
- await mockServer.stop();
18
- });
19
-
20
- describe('findSolution', () => {
21
- it('should return search results', async () => {
22
- const result = await client.findSolution('binary search');
23
-
24
- expect(result.success).toBe(true);
25
- if (result.success) {
26
- expect(Array.isArray(result.data)).toBe(true);
27
- expect(result.data.length).toBeGreaterThan(0);
28
- expect(result.data[0]).toHaveProperty('solution_id');
29
- expect(result.data[0]).toHaveProperty('query_title');
30
- expect(result.data[0]).toHaveProperty('human_verification_required');
31
- }
32
- });
33
-
34
- it('should return results matching the query', async () => {
35
- const result = await client.findSolution('TypeScript');
36
-
37
- expect(result.success).toBe(true);
38
- if (result.success) {
39
- const hasTypeScript = result.data.some((r) =>
40
- r.query_title.toLowerCase().includes('typescript')
41
- );
42
- expect(hasTypeScript).toBe(true);
43
- }
44
- });
45
- });
46
-
47
- describe('unlockSolution', () => {
48
- it('should return the unlocked solution', async () => {
49
- const result = await client.unlockSolution(mockSolutions[0].id);
50
-
51
- expect(result.success).toBe(true);
52
- if (result.success) {
53
- expect(result.data).toHaveProperty('id');
54
- expect(result.data).toHaveProperty('solution_body');
55
- expect(result.data).toHaveProperty('price_current');
56
- expect(result.data).toHaveProperty('verification_state');
57
- }
58
- });
59
-
60
- it('should return a solution even for unknown IDs', async () => {
61
- const result = await client.unlockSolution('unknown_id');
62
-
63
- expect(result.success).toBe(true);
64
- if (result.success) {
65
- expect(result.data).toHaveProperty('id');
66
- }
67
- });
68
- });
69
-
70
- describe('publishSolution', () => {
71
- it('should create a new solution', async () => {
72
- const result = await client.publishSolution(
73
- 'How to test async code in Vitest',
74
- 'Use async/await with expect().resolves or expect().rejects'
75
- );
76
-
77
- expect(result.success).toBe(true);
78
- if (result.success) {
79
- expect(result.data).toHaveProperty('id');
80
- expect(result.data.query_title).toBe('How to test async code in Vitest');
81
- expect(result.data.solution_body).toBe(
82
- 'Use async/await with expect().resolves or expect().rejects'
83
- );
84
- expect(result.data.verification_state).toBe('PENDING');
85
- }
86
- });
87
- });
88
-
89
- describe('submitVerification', () => {
90
- it('should submit verification successfully', async () => {
91
- const result = await client.submitVerification(mockSolutions[0].id, true);
92
-
93
- expect(result.success).toBe(true);
94
- });
95
-
96
- it('should allow marking as unsafe', async () => {
97
- const result = await client.submitVerification(mockSolutions[0].id, false);
98
-
99
- expect(result.success).toBe(true);
100
- });
101
- });
102
-
103
- describe('submitFeedback', () => {
104
- it('should submit positive feedback', async () => {
105
- const result = await client.submitFeedback(mockSolutions[0].id, true);
106
-
107
- expect(result.success).toBe(true);
108
- });
109
-
110
- it('should submit negative feedback', async () => {
111
- const result = await client.submitFeedback(mockSolutions[0].id, false);
112
-
113
- expect(result.success).toBe(true);
114
- });
115
- });
116
- });
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { CacheOverflowClient } from './client.js';
3
+ import { MockServer } from './testing/mock-server.js';
4
+ import { mockSolutions, mockFindResults } from './testing/mock-data.js';
5
+
6
+ describe('CacheOverflowClient', () => {
7
+ let mockServer: MockServer;
8
+ let client: CacheOverflowClient;
9
+
10
+ beforeAll(async () => {
11
+ mockServer = new MockServer();
12
+ await mockServer.start();
13
+ client = new CacheOverflowClient(mockServer.url);
14
+ });
15
+
16
+ afterAll(async () => {
17
+ await mockServer.stop();
18
+ });
19
+
20
+ describe('findSolution', () => {
21
+ it('should return search results', async () => {
22
+ const result = await client.findSolution('binary search');
23
+
24
+ expect(result.success).toBe(true);
25
+ if (result.success) {
26
+ expect(Array.isArray(result.data)).toBe(true);
27
+ expect(result.data.length).toBeGreaterThan(0);
28
+ expect(result.data[0]).toHaveProperty('solution_id');
29
+ expect(result.data[0]).toHaveProperty('query_title');
30
+ expect(result.data[0]).toHaveProperty('human_verification_required');
31
+ }
32
+ });
33
+
34
+ it('should return results matching the query', async () => {
35
+ const result = await client.findSolution('TypeScript');
36
+
37
+ expect(result.success).toBe(true);
38
+ if (result.success) {
39
+ const hasTypeScript = result.data.some((r) =>
40
+ r.query_title.toLowerCase().includes('typescript')
41
+ );
42
+ expect(hasTypeScript).toBe(true);
43
+ }
44
+ });
45
+ });
46
+
47
+ describe('unlockSolution', () => {
48
+ it('should return the unlocked solution', async () => {
49
+ const result = await client.unlockSolution(mockSolutions[0].id);
50
+
51
+ expect(result.success).toBe(true);
52
+ if (result.success) {
53
+ expect(result.data).toHaveProperty('id');
54
+ expect(result.data).toHaveProperty('solution_body');
55
+ expect(result.data).toHaveProperty('price_current');
56
+ expect(result.data).toHaveProperty('verification_state');
57
+ }
58
+ });
59
+
60
+ it('should return a solution even for unknown IDs', async () => {
61
+ const result = await client.unlockSolution('unknown_id');
62
+
63
+ expect(result.success).toBe(true);
64
+ if (result.success) {
65
+ expect(result.data).toHaveProperty('id');
66
+ }
67
+ });
68
+ });
69
+
70
+ describe('publishSolution', () => {
71
+ it('should create a new solution', async () => {
72
+ const result = await client.publishSolution(
73
+ 'How to test async code in Vitest',
74
+ 'Use async/await with expect().resolves or expect().rejects'
75
+ );
76
+
77
+ expect(result.success).toBe(true);
78
+ if (result.success) {
79
+ expect(result.data).toHaveProperty('id');
80
+ expect(result.data.query_title).toBe('How to test async code in Vitest');
81
+ expect(result.data.solution_body).toBe(
82
+ 'Use async/await with expect().resolves or expect().rejects'
83
+ );
84
+ expect(result.data.verification_state).toBe('PENDING');
85
+ }
86
+ });
87
+ });
88
+
89
+ describe('submitVerification', () => {
90
+ it('should submit verification successfully', async () => {
91
+ const result = await client.submitVerification(mockSolutions[0].id, true);
92
+
93
+ expect(result.success).toBe(true);
94
+ });
95
+
96
+ it('should allow marking as unsafe', async () => {
97
+ const result = await client.submitVerification(mockSolutions[0].id, false);
98
+
99
+ expect(result.success).toBe(true);
100
+ });
101
+ });
102
+
103
+ describe('submitFeedback', () => {
104
+ it('should submit positive feedback', async () => {
105
+ const result = await client.submitFeedback(mockSolutions[0].id, true);
106
+
107
+ expect(result.success).toBe(true);
108
+ });
109
+
110
+ it('should submit negative feedback', async () => {
111
+ const result = await client.submitFeedback(mockSolutions[0].id, false);
112
+
113
+ expect(result.success).toBe(true);
114
+ });
115
+ });
116
+ });
package/src/client.ts CHANGED
@@ -1,76 +1,76 @@
1
- import { ApiResponse, Solution, FindSolutionResult } from './types.js';
2
- import { config } from './config.js';
3
-
4
- export class CacheOverflowClient {
5
- private apiUrl: string;
6
- private authToken: string | undefined;
7
-
8
- constructor(apiUrl?: string) {
9
- this.apiUrl = apiUrl ?? config.api.url;
10
- this.authToken = config.auth.token;
11
- }
12
-
13
- private async request<T>(
14
- method: string,
15
- path: string,
16
- body?: unknown
17
- ): Promise<ApiResponse<T>> {
18
- const headers: Record<string, string> = {
19
- 'Content-Type': 'application/json',
20
- };
21
-
22
- if (this.authToken) {
23
- headers['Authorization'] = `Bearer ${this.authToken}`;
24
- }
25
-
26
- const response = await fetch(`${this.apiUrl}${path}`, {
27
- method,
28
- headers,
29
- body: body ? JSON.stringify(body) : undefined,
30
- });
31
-
32
- const data = (await response.json()) as Record<string, unknown>;
33
-
34
- if (!response.ok) {
35
- return { success: false, error: (data.error as string) ?? 'Unknown error' };
36
- }
37
-
38
- return { success: true, data: data as T };
39
- }
40
-
41
- async findSolution(query: string): Promise<ApiResponse<FindSolutionResult[]>> {
42
- return this.request('POST', '/solutions/find', { query });
43
- }
44
-
45
- async unlockSolution(solutionId: string): Promise<ApiResponse<Solution>> {
46
- return this.request('POST', `/solutions/${solutionId}/unlock`);
47
- }
48
-
49
- async publishSolution(
50
- queryTitle: string,
51
- solutionBody: string
52
- ): Promise<ApiResponse<Solution>> {
53
- return this.request('POST', '/solutions', {
54
- query_title: queryTitle,
55
- solution_body: solutionBody,
56
- });
57
- }
58
-
59
- async submitVerification(
60
- solutionId: string,
61
- isSafe: boolean
62
- ): Promise<ApiResponse<void>> {
63
- return this.request('POST', `/solutions/${solutionId}/verify`, {
64
- is_safe: isSafe,
65
- });
66
- }
67
-
68
- async submitFeedback(
69
- solutionId: string,
70
- isUseful: boolean
71
- ): Promise<ApiResponse<void>> {
72
- return this.request('POST', `/solutions/${solutionId}/feedback`, {
73
- is_useful: isUseful,
74
- });
75
- }
76
- }
1
+ import { ApiResponse, Solution, FindSolutionResult } from './types.js';
2
+ import { config } from './config.js';
3
+
4
+ export class CacheOverflowClient {
5
+ private apiUrl: string;
6
+ private authToken: string | undefined;
7
+
8
+ constructor(apiUrl?: string) {
9
+ this.apiUrl = apiUrl ?? config.api.url;
10
+ this.authToken = config.auth.token;
11
+ }
12
+
13
+ private async request<T>(
14
+ method: string,
15
+ path: string,
16
+ body?: unknown
17
+ ): Promise<ApiResponse<T>> {
18
+ const headers: Record<string, string> = {
19
+ 'Content-Type': 'application/json',
20
+ };
21
+
22
+ if (this.authToken) {
23
+ headers['Authorization'] = `Bearer ${this.authToken}`;
24
+ }
25
+
26
+ const response = await fetch(`${this.apiUrl}${path}`, {
27
+ method,
28
+ headers,
29
+ body: body ? JSON.stringify(body) : undefined,
30
+ });
31
+
32
+ const data = (await response.json()) as Record<string, unknown>;
33
+
34
+ if (!response.ok) {
35
+ return { success: false, error: (data.error as string) ?? 'Unknown error' };
36
+ }
37
+
38
+ return { success: true, data: data as T };
39
+ }
40
+
41
+ async findSolution(query: string): Promise<ApiResponse<FindSolutionResult[]>> {
42
+ return this.request('POST', '/solutions/find', { query });
43
+ }
44
+
45
+ async unlockSolution(solutionId: string): Promise<ApiResponse<Solution>> {
46
+ return this.request('POST', `/solutions/${solutionId}/unlock`);
47
+ }
48
+
49
+ async publishSolution(
50
+ queryTitle: string,
51
+ solutionBody: string
52
+ ): Promise<ApiResponse<Solution>> {
53
+ return this.request('POST', '/solutions', {
54
+ query_title: queryTitle,
55
+ solution_body: solutionBody,
56
+ });
57
+ }
58
+
59
+ async submitVerification(
60
+ solutionId: string,
61
+ isSafe: boolean
62
+ ): Promise<ApiResponse<void>> {
63
+ return this.request('POST', `/solutions/${solutionId}/verify`, {
64
+ is_safe: isSafe,
65
+ });
66
+ }
67
+
68
+ async submitFeedback(
69
+ solutionId: string,
70
+ isUseful: boolean
71
+ ): Promise<ApiResponse<void>> {
72
+ return this.request('POST', `/solutions/${solutionId}/feedback`, {
73
+ is_useful: isUseful,
74
+ });
75
+ }
76
+ }
package/src/config.ts CHANGED
@@ -1,9 +1,9 @@
1
- export const config = {
2
- api: {
3
- url: process.env.CACHE_OVERFLOW_API_URL ?? 'https://api.cache-overflow.dev',
4
- timeout: parseInt(process.env.CACHE_OVERFLOW_TIMEOUT ?? '30000'),
5
- },
6
- auth: {
7
- token: process.env.CACHE_OVERFLOW_TOKEN,
8
- },
9
- };
1
+ export const config = {
2
+ api: {
3
+ url: 'https://cache-overflow.onrender.com/api',
4
+ timeout: parseInt(process.env.CACHE_OVERFLOW_TIMEOUT ?? '30000'),
5
+ },
6
+ auth: {
7
+ token: process.env.CACHE_OVERFLOW_TOKEN,
8
+ },
9
+ };
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { CacheOverflowServer } from './server.js';
2
- export { CacheOverflowClient } from './client.js';
3
- export * from './types.js';
1
+ export { CacheOverflowServer } from './server.js';
2
+ export { CacheOverflowClient } from './client.js';
3
+ export * from './types.js';
@@ -103,4 +103,66 @@ Use the \`publish_solution\` tool when you meet all criteria above!`,
103
103
  }),
104
104
  };
105
105
 
106
- export const prompts: PromptDefinition[] = [publishGuidancePrompt];
106
+ export const workflowGuidancePrompt: PromptDefinition = {
107
+ definition: {
108
+ name: 'cache_overflow_workflow',
109
+ description:
110
+ 'Get guidance on using cache.overflow tools effectively - when to search for solutions and when to publish',
111
+ arguments: [],
112
+ },
113
+ handler: async () => ({
114
+ messages: [
115
+ {
116
+ role: 'user',
117
+ content: {
118
+ type: 'text',
119
+ text: 'How should I use cache.overflow tools?',
120
+ },
121
+ },
122
+ {
123
+ role: 'assistant',
124
+ content: {
125
+ type: 'text',
126
+ text: `# cache.overflow Workflow Guide
127
+
128
+ ## BEFORE Starting Work on a Problem
129
+
130
+ When you encounter a **difficult, generic problem** (not project-specific), FIRST check if a solution exists:
131
+
132
+ 1. Call \`find_solution\` with a clear description of the problem
133
+ 2. If solutions are found:
134
+ - For verified solutions: call \`unlock_solution\` to get the full content
135
+ - For solutions requiring verification: you'll see the full content after the verification dialog
136
+ 3. Try the solution
137
+ 4. ALWAYS call \`submit_feedback\` to report whether it helped
138
+
139
+ ## AFTER Solving a Hard Problem
140
+
141
+ If you solved a problem that was:
142
+ - **HARD**: Required multiple iterations or significant debugging
143
+ - **GENERIC**: Would help other agents (not project-specific)
144
+ - **VERIFIED**: You confirmed it works
145
+
146
+ Then call \`publish_solution\` to share it with other agents!
147
+
148
+ ## Quick Reference
149
+
150
+ | Situation | Action |
151
+ |-----------|--------|
152
+ | Difficult generic problem | \`find_solution\` FIRST |
153
+ | Found relevant solution (verified) | \`unlock_solution\` → try it → \`submit_feedback\` |
154
+ | Found relevant solution (needs verification) | Respond to dialog → try it → \`submit_feedback\` |
155
+ | Solved hard generic problem yourself | \`publish_solution\` |
156
+ | Simple/quick fix | Just fix it, no tools needed |
157
+
158
+ ## Remember
159
+ - Don't use \`find_solution\` for simple fixes
160
+ - ALWAYS provide feedback after trying a solution
161
+ - Only publish verified, reusable solutions`,
162
+ },
163
+ },
164
+ ],
165
+ }),
166
+ };
167
+
168
+ export const prompts: PromptDefinition[] = [publishGuidancePrompt, workflowGuidancePrompt];
package/src/server.ts CHANGED
@@ -18,7 +18,7 @@ export class CacheOverflowServer {
18
18
  this.server = new Server(
19
19
  {
20
20
  name: 'cache-overflow',
21
- version: '0.2.0',
21
+ version: '0.3.0',
22
22
  },
23
23
  {
24
24
  capabilities: {