@vibescope/mcp-server 0.2.0 → 0.2.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 (104) hide show
  1. package/README.md +60 -7
  2. package/dist/api-client.d.ts +251 -1
  3. package/dist/api-client.js +82 -3
  4. package/dist/handlers/blockers.js +9 -8
  5. package/dist/handlers/bodies-of-work.js +96 -63
  6. package/dist/handlers/connectors.d.ts +45 -0
  7. package/dist/handlers/connectors.js +183 -0
  8. package/dist/handlers/cost.d.ts +10 -0
  9. package/dist/handlers/cost.js +112 -50
  10. package/dist/handlers/decisions.js +32 -19
  11. package/dist/handlers/deployment.js +144 -122
  12. package/dist/handlers/discovery.d.ts +7 -0
  13. package/dist/handlers/discovery.js +96 -7
  14. package/dist/handlers/fallback.js +29 -23
  15. package/dist/handlers/file-checkouts.d.ts +20 -0
  16. package/dist/handlers/file-checkouts.js +133 -0
  17. package/dist/handlers/findings.d.ts +6 -0
  18. package/dist/handlers/findings.js +96 -40
  19. package/dist/handlers/git-issues.js +40 -36
  20. package/dist/handlers/ideas.js +49 -31
  21. package/dist/handlers/index.d.ts +3 -0
  22. package/dist/handlers/index.js +9 -0
  23. package/dist/handlers/milestones.js +39 -32
  24. package/dist/handlers/organizations.js +99 -91
  25. package/dist/handlers/progress.js +24 -13
  26. package/dist/handlers/project.js +68 -28
  27. package/dist/handlers/requests.js +18 -14
  28. package/dist/handlers/roles.d.ts +18 -0
  29. package/dist/handlers/roles.js +130 -0
  30. package/dist/handlers/session.js +58 -17
  31. package/dist/handlers/sprints.js +93 -81
  32. package/dist/handlers/tasks.d.ts +2 -0
  33. package/dist/handlers/tasks.js +189 -91
  34. package/dist/handlers/types.d.ts +64 -2
  35. package/dist/handlers/types.js +48 -1
  36. package/dist/handlers/validation.js +21 -17
  37. package/dist/index.js +7 -2716
  38. package/dist/token-tracking.d.ts +74 -0
  39. package/dist/token-tracking.js +122 -0
  40. package/dist/tools.js +685 -9
  41. package/dist/utils.d.ts +5 -0
  42. package/dist/utils.js +17 -0
  43. package/docs/TOOLS.md +2053 -0
  44. package/package.json +4 -1
  45. package/scripts/generate-docs.ts +212 -0
  46. package/src/api-client.test.ts +718 -0
  47. package/src/api-client.ts +320 -6
  48. package/src/handlers/__test-setup__.ts +16 -0
  49. package/src/handlers/blockers.test.ts +31 -19
  50. package/src/handlers/blockers.ts +9 -8
  51. package/src/handlers/bodies-of-work.test.ts +55 -32
  52. package/src/handlers/bodies-of-work.ts +115 -115
  53. package/src/handlers/connectors.test.ts +834 -0
  54. package/src/handlers/connectors.ts +229 -0
  55. package/src/handlers/cost.test.ts +34 -44
  56. package/src/handlers/cost.ts +136 -85
  57. package/src/handlers/decisions.test.ts +37 -27
  58. package/src/handlers/decisions.ts +35 -30
  59. package/src/handlers/deployment.ts +180 -208
  60. package/src/handlers/discovery.test.ts +4 -5
  61. package/src/handlers/discovery.ts +98 -8
  62. package/src/handlers/fallback.test.ts +26 -22
  63. package/src/handlers/fallback.ts +36 -33
  64. package/src/handlers/file-checkouts.test.ts +670 -0
  65. package/src/handlers/file-checkouts.ts +165 -0
  66. package/src/handlers/findings.test.ts +178 -19
  67. package/src/handlers/findings.ts +112 -74
  68. package/src/handlers/git-issues.test.ts +51 -43
  69. package/src/handlers/git-issues.ts +44 -84
  70. package/src/handlers/ideas.test.ts +28 -23
  71. package/src/handlers/ideas.ts +61 -59
  72. package/src/handlers/index.ts +9 -0
  73. package/src/handlers/milestones.test.ts +33 -28
  74. package/src/handlers/milestones.ts +52 -50
  75. package/src/handlers/organizations.test.ts +104 -83
  76. package/src/handlers/organizations.ts +117 -142
  77. package/src/handlers/progress.test.ts +20 -14
  78. package/src/handlers/progress.ts +26 -24
  79. package/src/handlers/project.test.ts +34 -27
  80. package/src/handlers/project.ts +95 -63
  81. package/src/handlers/requests.test.ts +27 -18
  82. package/src/handlers/requests.ts +21 -17
  83. package/src/handlers/roles.test.ts +303 -0
  84. package/src/handlers/roles.ts +208 -0
  85. package/src/handlers/session.test.ts +47 -0
  86. package/src/handlers/session.ts +71 -26
  87. package/src/handlers/sprints.test.ts +71 -50
  88. package/src/handlers/sprints.ts +113 -146
  89. package/src/handlers/tasks.test.ts +77 -15
  90. package/src/handlers/tasks.ts +231 -156
  91. package/src/handlers/tool-categories.test.ts +66 -0
  92. package/src/handlers/types.ts +81 -2
  93. package/src/handlers/validation.test.ts +78 -45
  94. package/src/handlers/validation.ts +23 -25
  95. package/src/index.ts +12 -2732
  96. package/src/token-tracking.test.ts +453 -0
  97. package/src/token-tracking.ts +164 -0
  98. package/src/tools.ts +685 -9
  99. package/src/utils.test.ts +2 -2
  100. package/src/utils.ts +17 -0
  101. package/dist/config/tool-categories.d.ts +0 -31
  102. package/dist/config/tool-categories.js +0 -253
  103. package/dist/knowledge.d.ts +0 -6
  104. package/dist/knowledge.js +0 -218
@@ -60,7 +60,7 @@ describe('addGitIssue', () => {
60
60
  issue_type: 'invalid_type',
61
61
  branch: 'feature/test',
62
62
  }, ctx)
63
- ).rejects.toThrow('Invalid issue_type');
63
+ ).rejects.toThrow(ValidationError);
64
64
  });
65
65
 
66
66
  it('should throw error for invalid task_id UUID', async () => {
@@ -180,35 +180,37 @@ describe('addGitIssue', () => {
180
180
  }
181
181
  });
182
182
 
183
- it('should throw error when API call fails', async () => {
183
+ it('should return error when API call fails', async () => {
184
184
  mockApiClient.addGitIssue.mockResolvedValue({
185
185
  ok: false,
186
186
  error: 'Insert failed',
187
187
  });
188
188
  const ctx = createMockContext();
189
189
 
190
- await expect(
191
- addGitIssue({
192
- project_id: VALID_PROJECT_ID,
193
- issue_type: 'merge_conflict',
194
- branch: 'feature/test',
195
- }, ctx)
196
- ).rejects.toThrow('Insert failed');
190
+ const result = await addGitIssue({
191
+ project_id: VALID_PROJECT_ID,
192
+ issue_type: 'merge_conflict',
193
+ branch: 'feature/test',
194
+ }, ctx);
195
+
196
+ expect(result.isError).toBe(true);
197
+ expect(result.result).toMatchObject({ error: 'Insert failed' });
197
198
  });
198
199
 
199
- it('should throw default error message when API fails without error', async () => {
200
+ it('should return default error message when API fails without error', async () => {
200
201
  mockApiClient.addGitIssue.mockResolvedValue({
201
202
  ok: false,
202
203
  });
203
204
  const ctx = createMockContext();
204
205
 
205
- await expect(
206
- addGitIssue({
207
- project_id: VALID_PROJECT_ID,
208
- issue_type: 'merge_conflict',
209
- branch: 'feature/test',
210
- }, ctx)
211
- ).rejects.toThrow('Failed to add git issue');
206
+ const result = await addGitIssue({
207
+ project_id: VALID_PROJECT_ID,
208
+ issue_type: 'merge_conflict',
209
+ branch: 'feature/test',
210
+ }, ctx);
211
+
212
+ expect(result.isError).toBe(true);
213
+ expect(result.result).toMatchObject({ error: 'Failed to add git issue' });
212
214
  });
213
215
  });
214
216
 
@@ -314,27 +316,29 @@ describe('resolveGitIssue', () => {
314
316
  );
315
317
  });
316
318
 
317
- it('should throw error when API call fails', async () => {
319
+ it('should return error when API call fails', async () => {
318
320
  mockApiClient.resolveGitIssue.mockResolvedValue({
319
321
  ok: false,
320
322
  error: 'Update failed',
321
323
  });
322
324
  const ctx = createMockContext();
323
325
 
324
- await expect(
325
- resolveGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx)
326
- ).rejects.toThrow('Update failed');
326
+ const result = await resolveGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
327
+
328
+ expect(result.isError).toBe(true);
329
+ expect(result.result).toMatchObject({ error: 'Update failed' });
327
330
  });
328
331
 
329
- it('should throw default error message when API fails without error', async () => {
332
+ it('should return default error message when API fails without error', async () => {
330
333
  mockApiClient.resolveGitIssue.mockResolvedValue({
331
334
  ok: false,
332
335
  });
333
336
  const ctx = createMockContext();
334
337
 
335
- await expect(
336
- resolveGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx)
337
- ).rejects.toThrow('Failed to resolve git issue');
338
+ const result = await resolveGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
339
+
340
+ expect(result.isError).toBe(true);
341
+ expect(result.result).toMatchObject({ error: 'Failed to resolve git issue' });
338
342
  });
339
343
  });
340
344
 
@@ -364,7 +368,7 @@ describe('getGitIssues', () => {
364
368
 
365
369
  await expect(
366
370
  getGitIssues({ project_id: VALID_PROJECT_ID, status: 'invalid_status' }, ctx)
367
- ).rejects.toThrow('Invalid status');
371
+ ).rejects.toThrow(ValidationError);
368
372
  });
369
373
 
370
374
  it('should throw error for invalid issue_type filter', async () => {
@@ -372,7 +376,7 @@ describe('getGitIssues', () => {
372
376
 
373
377
  await expect(
374
378
  getGitIssues({ project_id: VALID_PROJECT_ID, issue_type: 'invalid_type' }, ctx)
375
- ).rejects.toThrow('Invalid issue_type');
379
+ ).rejects.toThrow(ValidationError);
376
380
  });
377
381
 
378
382
  it('should return empty list when no git issues', async () => {
@@ -519,27 +523,29 @@ describe('getGitIssues', () => {
519
523
  );
520
524
  });
521
525
 
522
- it('should throw error when API call fails', async () => {
526
+ it('should return error when API call fails', async () => {
523
527
  mockApiClient.getGitIssues.mockResolvedValue({
524
528
  ok: false,
525
529
  error: 'Query failed',
526
530
  });
527
531
  const ctx = createMockContext();
528
532
 
529
- await expect(
530
- getGitIssues({ project_id: VALID_PROJECT_ID }, ctx)
531
- ).rejects.toThrow('Query failed');
533
+ const result = await getGitIssues({ project_id: VALID_PROJECT_ID }, ctx);
534
+
535
+ expect(result.isError).toBe(true);
536
+ expect(result.result).toMatchObject({ error: 'Query failed' });
532
537
  });
533
538
 
534
- it('should throw default error message when API fails without error', async () => {
539
+ it('should return default error message when API fails without error', async () => {
535
540
  mockApiClient.getGitIssues.mockResolvedValue({
536
541
  ok: false,
537
542
  });
538
543
  const ctx = createMockContext();
539
544
 
540
- await expect(
541
- getGitIssues({ project_id: VALID_PROJECT_ID }, ctx)
542
- ).rejects.toThrow('Failed to fetch git issues');
545
+ const result = await getGitIssues({ project_id: VALID_PROJECT_ID }, ctx);
546
+
547
+ expect(result.isError).toBe(true);
548
+ expect(result.result).toMatchObject({ error: 'Failed to fetch git issues' });
543
549
  });
544
550
  });
545
551
 
@@ -598,26 +604,28 @@ describe('deleteGitIssue', () => {
598
604
  );
599
605
  });
600
606
 
601
- it('should throw error when API call fails', async () => {
607
+ it('should return error when API call fails', async () => {
602
608
  mockApiClient.deleteGitIssue.mockResolvedValue({
603
609
  ok: false,
604
610
  error: 'Delete failed',
605
611
  });
606
612
  const ctx = createMockContext();
607
613
 
608
- await expect(
609
- deleteGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx)
610
- ).rejects.toThrow('Delete failed');
614
+ const result = await deleteGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
615
+
616
+ expect(result.isError).toBe(true);
617
+ expect(result.result).toMatchObject({ error: 'Delete failed' });
611
618
  });
612
619
 
613
- it('should throw default error message when API fails without error', async () => {
620
+ it('should return default error message when API fails without error', async () => {
614
621
  mockApiClient.deleteGitIssue.mockResolvedValue({
615
622
  ok: false,
616
623
  });
617
624
  const ctx = createMockContext();
618
625
 
619
- await expect(
620
- deleteGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx)
621
- ).rejects.toThrow('Failed to delete git issue');
626
+ const result = await deleteGitIssue({ git_issue_id: VALID_GIT_ISSUE_ID }, ctx);
627
+
628
+ expect(result.isError).toBe(true);
629
+ expect(result.result).toMatchObject({ error: 'Failed to delete git issue' });
622
630
  });
623
631
  });
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import type { Handler, HandlerRegistry } from './types.js';
12
- import { validateRequired, validateUUID } from '../validators.js';
12
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
13
13
  import { getApiClient } from '../api-client.js';
14
14
 
15
15
  const VALID_GIT_ISSUE_TYPES = [
@@ -25,41 +25,38 @@ const VALID_GIT_ISSUE_STATUSES = ['open', 'resolved'] as const;
25
25
  type GitIssueType = (typeof VALID_GIT_ISSUE_TYPES)[number];
26
26
  type GitIssueStatus = (typeof VALID_GIT_ISSUE_STATUSES)[number];
27
27
 
28
- export const addGitIssue: Handler = async (args, ctx) => {
29
- const {
30
- project_id,
31
- issue_type,
32
- branch,
33
- target_branch,
34
- pr_url,
35
- conflicting_files,
36
- error_message,
37
- task_id,
38
- } = args as {
39
- project_id: string;
40
- issue_type: string;
41
- branch: string;
42
- target_branch?: string;
43
- pr_url?: string;
44
- conflicting_files?: string[];
45
- error_message?: string;
46
- task_id?: string;
47
- };
48
-
49
- validateRequired(project_id, 'project_id');
50
- validateUUID(project_id, 'project_id');
51
- validateRequired(issue_type, 'issue_type');
52
- validateRequired(branch, 'branch');
53
-
54
- if (!VALID_GIT_ISSUE_TYPES.includes(issue_type as GitIssueType)) {
55
- throw new Error(
56
- `Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`
57
- );
58
- }
28
+ // Argument schemas for type-safe parsing
29
+ const addGitIssueSchema = {
30
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
31
+ issue_type: { type: 'string' as const, required: true as const, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
32
+ branch: { type: 'string' as const, required: true as const },
33
+ target_branch: { type: 'string' as const },
34
+ pr_url: { type: 'string' as const },
35
+ conflicting_files: { type: 'array' as const },
36
+ error_message: { type: 'string' as const },
37
+ task_id: { type: 'string' as const, validate: uuidValidator },
38
+ };
59
39
 
60
- if (task_id) {
61
- validateUUID(task_id, 'task_id');
62
- }
40
+ const resolveGitIssueSchema = {
41
+ git_issue_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
42
+ resolution_note: { type: 'string' as const },
43
+ auto_resolved: { type: 'boolean' as const },
44
+ };
45
+
46
+ const getGitIssuesSchema = {
47
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
48
+ status: { type: 'string' as const, default: 'open', validate: createEnumValidator(VALID_GIT_ISSUE_STATUSES) },
49
+ issue_type: { type: 'string' as const, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
50
+ branch: { type: 'string' as const },
51
+ limit: { type: 'number' as const, default: 50 },
52
+ };
53
+
54
+ const deleteGitIssueSchema = {
55
+ git_issue_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
56
+ };
57
+
58
+ export const addGitIssue: Handler = async (args, ctx) => {
59
+ const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id } = parseArgs(args, addGitIssueSchema);
63
60
 
64
61
  const apiClient = getApiClient();
65
62
  const response = await apiClient.addGitIssue(project_id, {
@@ -67,27 +64,20 @@ export const addGitIssue: Handler = async (args, ctx) => {
67
64
  branch,
68
65
  target_branch,
69
66
  pr_url,
70
- conflicting_files,
67
+ conflicting_files: conflicting_files as string[] | undefined,
71
68
  error_message,
72
69
  task_id
73
70
  }, ctx.session.currentSessionId || undefined);
74
71
 
75
72
  if (!response.ok) {
76
- throw new Error(response.error || 'Failed to add git issue');
73
+ return { result: { error: response.error || 'Failed to add git issue' }, isError: true };
77
74
  }
78
75
 
79
76
  return { result: response.data };
80
77
  };
81
78
 
82
79
  export const resolveGitIssue: Handler = async (args, ctx) => {
83
- const { git_issue_id, resolution_note, auto_resolved } = args as {
84
- git_issue_id: string;
85
- resolution_note?: string;
86
- auto_resolved?: boolean;
87
- };
88
-
89
- validateRequired(git_issue_id, 'git_issue_id');
90
- validateUUID(git_issue_id, 'git_issue_id');
80
+ const { git_issue_id, resolution_note, auto_resolved } = parseArgs(args, resolveGitIssueSchema);
91
81
 
92
82
  const apiClient = getApiClient();
93
83
  const response = await apiClient.resolveGitIssue(git_issue_id, {
@@ -96,68 +86,38 @@ export const resolveGitIssue: Handler = async (args, ctx) => {
96
86
  }, ctx.session.currentSessionId || undefined);
97
87
 
98
88
  if (!response.ok) {
99
- throw new Error(response.error || 'Failed to resolve git issue');
89
+ return { result: { error: response.error || 'Failed to resolve git issue' }, isError: true };
100
90
  }
101
91
 
102
92
  return { result: response.data };
103
93
  };
104
94
 
105
- export const getGitIssues: Handler = async (args, ctx) => {
106
- const {
107
- project_id,
108
- status = 'open',
109
- issue_type,
110
- branch,
111
- limit = 50,
112
- } = args as {
113
- project_id: string;
114
- status?: string;
115
- issue_type?: string;
116
- branch?: string;
117
- limit?: number;
118
- };
119
-
120
- validateRequired(project_id, 'project_id');
121
- validateUUID(project_id, 'project_id');
122
-
123
- if (status && !VALID_GIT_ISSUE_STATUSES.includes(status as GitIssueStatus)) {
124
- throw new Error(
125
- `Invalid status. Valid statuses: ${VALID_GIT_ISSUE_STATUSES.join(', ')}`
126
- );
127
- }
128
-
129
- if (issue_type && !VALID_GIT_ISSUE_TYPES.includes(issue_type as GitIssueType)) {
130
- throw new Error(
131
- `Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`
132
- );
133
- }
95
+ export const getGitIssues: Handler = async (args, _ctx) => {
96
+ const { project_id, status, issue_type, branch, limit } = parseArgs(args, getGitIssuesSchema);
134
97
 
135
98
  const apiClient = getApiClient();
136
99
  const response = await apiClient.getGitIssues(project_id, {
137
- status,
138
- issue_type,
100
+ status: status as GitIssueStatus | undefined,
101
+ issue_type: issue_type as GitIssueType | undefined,
139
102
  branch,
140
103
  limit
141
104
  });
142
105
 
143
106
  if (!response.ok) {
144
- throw new Error(response.error || 'Failed to fetch git issues');
107
+ return { result: { error: response.error || 'Failed to fetch git issues' }, isError: true };
145
108
  }
146
109
 
147
110
  return { result: response.data };
148
111
  };
149
112
 
150
- export const deleteGitIssue: Handler = async (args, ctx) => {
151
- const { git_issue_id } = args as { git_issue_id: string };
152
-
153
- validateRequired(git_issue_id, 'git_issue_id');
154
- validateUUID(git_issue_id, 'git_issue_id');
113
+ export const deleteGitIssue: Handler = async (args, _ctx) => {
114
+ const { git_issue_id } = parseArgs(args, deleteGitIssueSchema);
155
115
 
156
116
  const apiClient = getApiClient();
157
117
  const response = await apiClient.deleteGitIssue(git_issue_id);
158
118
 
159
119
  if (!response.ok) {
160
- throw new Error(response.error || 'Failed to delete git issue');
120
+ return { result: { error: response.error || 'Failed to delete git issue' }, isError: true };
161
121
  }
162
122
 
163
123
  return { result: response.data };
@@ -138,19 +138,20 @@ describe('addIdea', () => {
138
138
  );
139
139
  });
140
140
 
141
- it('should throw error when API call fails', async () => {
141
+ it('should return error when API call fails', async () => {
142
142
  mockApiClient.addIdea.mockResolvedValue({
143
143
  ok: false,
144
144
  error: 'Insert failed',
145
145
  });
146
146
  const ctx = createMockContext();
147
147
 
148
- await expect(
149
- addIdea({
150
- project_id: '123e4567-e89b-12d3-a456-426614174000',
151
- title: 'New Feature',
152
- }, ctx)
153
- ).rejects.toThrow('Failed to add idea');
148
+ const result = await addIdea({
149
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
150
+ title: 'New Feature',
151
+ }, ctx);
152
+
153
+ expect(result.isError).toBe(true);
154
+ expect(result.result).toMatchObject({ error: 'Insert failed' });
154
155
  });
155
156
  });
156
157
 
@@ -248,16 +249,17 @@ describe('updateIdea', () => {
248
249
  );
249
250
  });
250
251
 
251
- it('should throw error when API call fails', async () => {
252
+ it('should return error when API call fails', async () => {
252
253
  mockApiClient.updateIdea.mockResolvedValue({
253
254
  ok: false,
254
255
  error: 'Idea not found',
255
256
  });
256
257
  const ctx = createMockContext();
257
258
 
258
- await expect(
259
- updateIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000', title: 'New' }, ctx)
260
- ).rejects.toThrow('Failed to update idea');
259
+ const result = await updateIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000', title: 'New' }, ctx);
260
+
261
+ expect(result.isError).toBe(true);
262
+ expect(result.result).toMatchObject({ error: 'Idea not found' });
261
263
  });
262
264
  });
263
265
 
@@ -363,16 +365,17 @@ describe('getIdeas', () => {
363
365
  );
364
366
  });
365
367
 
366
- it('should throw error when API call fails', async () => {
368
+ it('should return error when API call fails', async () => {
367
369
  mockApiClient.getIdeas.mockResolvedValue({
368
370
  ok: false,
369
371
  error: 'Query failed',
370
372
  });
371
373
  const ctx = createMockContext();
372
374
 
373
- await expect(
374
- getIdeas({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
375
- ).rejects.toThrow('Failed to fetch ideas');
375
+ const result = await getIdeas({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx);
376
+
377
+ expect(result.isError).toBe(true);
378
+ expect(result.result).toMatchObject({ error: 'Query failed' });
376
379
  });
377
380
  });
378
381
 
@@ -429,16 +432,17 @@ describe('deleteIdea', () => {
429
432
  );
430
433
  });
431
434
 
432
- it('should throw error when API call fails', async () => {
435
+ it('should return error when API call fails', async () => {
433
436
  mockApiClient.deleteIdea.mockResolvedValue({
434
437
  ok: false,
435
438
  error: 'Delete failed',
436
439
  });
437
440
  const ctx = createMockContext();
438
441
 
439
- await expect(
440
- deleteIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
441
- ).rejects.toThrow('Failed to delete idea');
442
+ const result = await deleteIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx);
443
+
444
+ expect(result.isError).toBe(true);
445
+ expect(result.result).toMatchObject({ error: 'Delete failed' });
442
446
  });
443
447
  });
444
448
 
@@ -625,15 +629,16 @@ describe('convertIdeaToTask', () => {
625
629
  );
626
630
  });
627
631
 
628
- it('should throw error when API call fails', async () => {
632
+ it('should return error when API call fails', async () => {
629
633
  mockApiClient.proxy.mockResolvedValue({
630
634
  ok: false,
631
635
  error: 'Idea not found',
632
636
  });
633
637
  const ctx = createMockContext();
634
638
 
635
- await expect(
636
- convertIdeaToTask({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
637
- ).rejects.toThrow('Failed to convert idea');
639
+ const result = await convertIdeaToTask({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx);
640
+
641
+ expect(result.isError).toBe(true);
642
+ expect(result.result).toMatchObject({ error: 'Idea not found' });
638
643
  });
639
644
  });