@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
@@ -10,6 +10,16 @@
10
10
  */
11
11
 
12
12
  import type { Handler, HandlerRegistry } from './types.js';
13
+ import { parseArgs } from '../validators.js';
14
+
15
+ // Argument schemas for type-safe parsing
16
+ const discoverToolsSchema = {
17
+ category: { type: 'string' as const },
18
+ };
19
+
20
+ const getToolInfoSchema = {
21
+ tool_name: { type: 'string' as const, required: true as const },
22
+ };
13
23
 
14
24
  // Lazy-loaded tool documentation cache
15
25
  let toolInfoCache: Record<string, string> | null = null;
@@ -28,8 +38,8 @@ async function getToolDocs(): Promise<Record<string, string>> {
28
38
  return toolInfoCache;
29
39
  }
30
40
 
31
- // Tool categories with brief descriptions
32
- const TOOL_CATEGORIES: Record<string, { description: string; tools: Array<{ name: string; brief: string }> }> = {
41
+ // Tool categories with brief descriptions (exported for documentation generation)
42
+ export const TOOL_CATEGORIES: Record<string, { description: string; tools: Array<{ name: string; brief: string }> }> = {
33
43
  session: {
34
44
  description: 'Session lifecycle and monitoring',
35
45
  tools: [
@@ -114,6 +124,7 @@ const TOOL_CATEGORIES: Record<string, { description: string; tools: Array<{ name
114
124
  tools: [
115
125
  { name: 'add_finding', brief: 'Record audit finding' },
116
126
  { name: 'get_findings', brief: 'List findings' },
127
+ { name: 'get_findings_stats', brief: 'Get findings statistics' },
117
128
  { name: 'update_finding', brief: 'Update finding status' },
118
129
  { name: 'delete_finding', brief: 'Remove finding' },
119
130
  ],
@@ -167,6 +178,26 @@ const TOOL_CATEGORIES: Record<string, { description: string; tools: Array<{ name
167
178
  { name: 'add_task_to_body_of_work', brief: 'Add task to group' },
168
179
  { name: 'remove_task_from_body_of_work', brief: 'Remove from group' },
169
180
  { name: 'activate_body_of_work', brief: 'Activate for work' },
181
+ { name: 'add_task_dependency', brief: 'Add task dependency' },
182
+ { name: 'remove_task_dependency', brief: 'Remove task dependency' },
183
+ { name: 'get_task_dependencies', brief: 'List task dependencies' },
184
+ { name: 'get_next_body_of_work_task', brief: 'Get next available task' },
185
+ ],
186
+ },
187
+ sprints: {
188
+ description: 'Time-bounded sprints with velocity tracking',
189
+ tools: [
190
+ { name: 'create_sprint', brief: 'Create new sprint' },
191
+ { name: 'update_sprint', brief: 'Update sprint details' },
192
+ { name: 'get_sprint', brief: 'Get sprint with tasks' },
193
+ { name: 'get_sprints', brief: 'List project sprints' },
194
+ { name: 'delete_sprint', brief: 'Delete sprint' },
195
+ { name: 'start_sprint', brief: 'Start sprint' },
196
+ { name: 'complete_sprint', brief: 'Complete sprint' },
197
+ { name: 'add_task_to_sprint', brief: 'Add task to sprint' },
198
+ { name: 'remove_task_from_sprint', brief: 'Remove from sprint' },
199
+ { name: 'get_sprint_backlog', brief: 'Get available tasks' },
200
+ { name: 'get_sprint_velocity', brief: 'Velocity metrics' },
170
201
  ],
171
202
  },
172
203
  requests: {
@@ -204,6 +235,17 @@ const TOOL_CATEGORIES: Record<string, { description: string; tools: Array<{ name
204
235
  { name: 'update_cost_alert', brief: 'Update alert config' },
205
236
  { name: 'delete_cost_alert', brief: 'Remove alert' },
206
237
  { name: 'get_task_costs', brief: 'Cost per task' },
238
+ { name: 'get_body_of_work_costs', brief: 'Cost per body of work' },
239
+ { name: 'get_sprint_costs', brief: 'Cost per sprint' },
240
+ ],
241
+ },
242
+ git_issues: {
243
+ description: 'Git conflict and issue tracking',
244
+ tools: [
245
+ { name: 'add_git_issue', brief: 'Record git issue' },
246
+ { name: 'resolve_git_issue', brief: 'Mark resolved' },
247
+ { name: 'get_git_issues', brief: 'List git issues' },
248
+ { name: 'delete_git_issue', brief: 'Delete git issue' },
207
249
  ],
208
250
  },
209
251
  knowledge: {
@@ -212,10 +254,62 @@ const TOOL_CATEGORIES: Record<string, { description: string; tools: Array<{ name
212
254
  { name: 'query_knowledge_base', brief: 'Aggregated project knowledge in one call' },
213
255
  ],
214
256
  },
257
+ discovery: {
258
+ description: 'Tool discovery and documentation',
259
+ tools: [
260
+ { name: 'discover_tools', brief: 'List tools by category' },
261
+ { name: 'get_tool_info', brief: 'Get detailed tool docs' },
262
+ ],
263
+ },
264
+ subtasks: {
265
+ description: 'Break tasks into smaller pieces',
266
+ tools: [
267
+ { name: 'add_subtask', brief: 'Add subtask to task' },
268
+ { name: 'get_subtasks', brief: 'List task subtasks' },
269
+ ],
270
+ },
271
+ worktrees: {
272
+ description: 'Git worktree management',
273
+ tools: [
274
+ { name: 'get_stale_worktrees', brief: 'Find orphaned worktrees' },
275
+ { name: 'clear_worktree_path', brief: 'Clear worktree from task' },
276
+ ],
277
+ },
278
+ roles: {
279
+ description: 'Agent role management',
280
+ tools: [
281
+ { name: 'get_role_settings', brief: 'Get project role settings' },
282
+ { name: 'update_role_settings', brief: 'Configure role behavior' },
283
+ { name: 'set_session_role', brief: 'Set session role' },
284
+ { name: 'get_agents_by_role', brief: 'List agents by role' },
285
+ ],
286
+ },
287
+ file_locks: {
288
+ description: 'File checkout/locking for multi-agent',
289
+ tools: [
290
+ { name: 'checkout_file', brief: 'Lock file for editing' },
291
+ { name: 'checkin_file', brief: 'Release file lock' },
292
+ { name: 'get_file_checkouts', brief: 'List file locks' },
293
+ { name: 'abandon_checkout', brief: 'Force-release lock' },
294
+ { name: 'is_file_available', brief: 'Check if file is free' },
295
+ ],
296
+ },
297
+ connectors: {
298
+ description: 'External integration connectors',
299
+ tools: [
300
+ { name: 'get_connectors', brief: 'List project connectors' },
301
+ { name: 'get_connector', brief: 'Get connector details' },
302
+ { name: 'add_connector', brief: 'Create new connector' },
303
+ { name: 'update_connector', brief: 'Update connector config' },
304
+ { name: 'delete_connector', brief: 'Remove connector' },
305
+ { name: 'test_connector', brief: 'Send test event' },
306
+ { name: 'get_connector_events', brief: 'Event history' },
307
+ ],
308
+ },
215
309
  };
216
310
 
217
311
  export const discoverTools: Handler = async (args) => {
218
- const { category } = args as { category?: string };
312
+ const { category } = parseArgs(args, discoverToolsSchema);
219
313
 
220
314
  if (category) {
221
315
  // Return tools in specific category
@@ -254,11 +348,7 @@ export const discoverTools: Handler = async (args) => {
254
348
  };
255
349
 
256
350
  export const getToolInfo: Handler = async (args) => {
257
- const { tool_name } = args as { tool_name: string };
258
-
259
- if (!tool_name) {
260
- return { result: { error: 'tool_name is required' } };
261
- }
351
+ const { tool_name } = parseArgs(args, getToolInfoSchema);
262
352
 
263
353
  // Lazy-load tool documentation
264
354
  const toolDocs = await getToolDocs();
@@ -48,7 +48,7 @@ describe('startFallbackActivity', () => {
48
48
  project_id: '123e4567-e89b-12d3-a456-426614174000',
49
49
  activity: 'invalid_activity',
50
50
  }, ctx)
51
- ).rejects.toThrow('Invalid activity');
51
+ ).rejects.toThrow(ValidationError);
52
52
  });
53
53
 
54
54
  it('should start fallback activity successfully', async () => {
@@ -131,19 +131,20 @@ describe('startFallbackActivity', () => {
131
131
  }
132
132
  });
133
133
 
134
- it('should throw error when API call fails', async () => {
134
+ it('should return error when API call fails', async () => {
135
135
  mockApiClient.startFallbackActivity.mockResolvedValue({
136
136
  ok: false,
137
137
  error: 'Failed to start activity',
138
138
  });
139
139
  const ctx = createMockContext();
140
140
 
141
- await expect(
142
- startFallbackActivity({
143
- project_id: '123e4567-e89b-12d3-a456-426614174000',
144
- activity: 'code_review',
145
- }, ctx)
146
- ).rejects.toThrow('Failed to start fallback activity');
141
+ const result = await startFallbackActivity({
142
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
143
+ activity: 'code_review',
144
+ }, ctx);
145
+
146
+ expect(result.isError).toBe(true);
147
+ expect(result.result).toMatchObject({ error: 'Failed to start activity' });
147
148
  });
148
149
 
149
150
  it('should pass through worktree guidance when API returns it', async () => {
@@ -286,18 +287,19 @@ describe('stopFallbackActivity', () => {
286
287
  );
287
288
  });
288
289
 
289
- it('should throw error when API call fails', async () => {
290
+ it('should return error when API call fails', async () => {
290
291
  mockApiClient.stopFallbackActivity.mockResolvedValue({
291
292
  ok: false,
292
293
  error: 'Failed to stop activity',
293
294
  });
294
295
  const ctx = createMockContext();
295
296
 
296
- await expect(
297
- stopFallbackActivity({
298
- project_id: '123e4567-e89b-12d3-a456-426614174000',
299
- }, ctx)
300
- ).rejects.toThrow('Failed to stop fallback activity');
297
+ const result = await stopFallbackActivity({
298
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
299
+ }, ctx);
300
+
301
+ expect(result.isError).toBe(true);
302
+ expect(result.result).toMatchObject({ error: 'Failed to stop activity' });
301
303
  });
302
304
  });
303
305
 
@@ -405,16 +407,17 @@ describe('getActivityHistory', () => {
405
407
  );
406
408
  });
407
409
 
408
- it('should throw error when API call fails', async () => {
410
+ it('should return error when API call fails', async () => {
409
411
  mockApiClient.proxy.mockResolvedValue({
410
412
  ok: false,
411
413
  error: 'Query failed',
412
414
  });
413
415
  const ctx = createMockContext();
414
416
 
415
- await expect(
416
- getActivityHistory({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
417
- ).rejects.toThrow('Failed to get activity history');
417
+ const result = await getActivityHistory({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx);
418
+
419
+ expect(result.isError).toBe(true);
420
+ expect(result.result).toMatchObject({ error: 'Query failed' });
418
421
  });
419
422
  });
420
423
 
@@ -518,15 +521,16 @@ describe('getActivitySchedules', () => {
518
521
  );
519
522
  });
520
523
 
521
- it('should throw error when API call fails', async () => {
524
+ it('should return error when API call fails', async () => {
522
525
  mockApiClient.proxy.mockResolvedValue({
523
526
  ok: false,
524
527
  error: 'Query failed',
525
528
  });
526
529
  const ctx = createMockContext();
527
530
 
528
- await expect(
529
- getActivitySchedules({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
530
- ).rejects.toThrow('Failed to get activity schedules');
531
+ const result = await getActivitySchedules({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx);
532
+
533
+ expect(result.isError).toBe(true);
534
+ expect(result.result).toMatchObject({ error: 'Query failed' });
531
535
  });
532
536
  });
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { Handler, HandlerRegistry } from './types.js';
14
- import { validateRequired, validateUUID } from '../validators.js';
14
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
15
15
  import { FALLBACK_ACTIVITIES } from '../utils.js';
16
16
  import { getApiClient } from '../api-client.js';
17
17
 
@@ -26,26 +26,42 @@ const VALID_ACTIVITIES = [
26
26
  'documentation_review',
27
27
  'dependency_audit',
28
28
  'validate_completed_tasks',
29
- ];
29
+ 'worktree_cleanup',
30
+ ] as const;
30
31
 
31
- export const startFallbackActivity: Handler = async (args, ctx) => {
32
- const { project_id, activity } = args as { project_id: string; activity: string };
32
+ type FallbackActivity = typeof VALID_ACTIVITIES[number];
33
33
 
34
- validateRequired(project_id, 'project_id');
35
- validateUUID(project_id, 'project_id');
36
- validateRequired(activity, 'activity');
34
+ // Argument schemas for type-safe parsing
35
+ const startFallbackActivitySchema = {
36
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
37
+ activity: { type: 'string' as const, required: true as const, validate: createEnumValidator(VALID_ACTIVITIES) },
38
+ };
37
39
 
38
- if (!VALID_ACTIVITIES.includes(activity)) {
39
- throw new Error(`Invalid activity. Must be one of: ${VALID_ACTIVITIES.join(', ')}`);
40
- }
40
+ const stopFallbackActivitySchema = {
41
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
42
+ summary: { type: 'string' as const },
43
+ };
44
+
45
+ const getActivityHistorySchema = {
46
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
47
+ activity_type: { type: 'string' as const },
48
+ limit: { type: 'number' as const, default: 50 },
49
+ };
50
+
51
+ const getActivitySchedulesSchema = {
52
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
53
+ };
54
+
55
+ export const startFallbackActivity: Handler = async (args, ctx) => {
56
+ const { project_id, activity } = parseArgs(args, startFallbackActivitySchema);
41
57
 
42
58
  const { session } = ctx;
43
59
  const apiClient = getApiClient();
44
60
 
45
- const response = await apiClient.startFallbackActivity(project_id, activity, session.currentSessionId || undefined);
61
+ const response = await apiClient.startFallbackActivity(project_id, activity as FallbackActivity, session.currentSessionId || undefined);
46
62
 
47
63
  if (!response.ok) {
48
- throw new Error(`Failed to start fallback activity: ${response.error}`);
64
+ return { result: { error: response.error || 'Failed to start fallback activity' }, isError: true };
49
65
  }
50
66
 
51
67
  // Get the activity details for the response
@@ -75,10 +91,7 @@ export const startFallbackActivity: Handler = async (args, ctx) => {
75
91
  };
76
92
 
77
93
  export const stopFallbackActivity: Handler = async (args, ctx) => {
78
- const { project_id, summary } = args as { project_id: string; summary?: string };
79
-
80
- validateRequired(project_id, 'project_id');
81
- validateUUID(project_id, 'project_id');
94
+ const { project_id, summary } = parseArgs(args, stopFallbackActivitySchema);
82
95
 
83
96
  const { session } = ctx;
84
97
  const apiClient = getApiClient();
@@ -86,7 +99,7 @@ export const stopFallbackActivity: Handler = async (args, ctx) => {
86
99
  const response = await apiClient.stopFallbackActivity(project_id, summary, session.currentSessionId || undefined);
87
100
 
88
101
  if (!response.ok) {
89
- throw new Error(`Failed to stop fallback activity: ${response.error}`);
102
+ return { result: { error: response.error || 'Failed to stop fallback activity' }, isError: true };
90
103
  }
91
104
 
92
105
  return {
@@ -97,15 +110,8 @@ export const stopFallbackActivity: Handler = async (args, ctx) => {
97
110
  };
98
111
  };
99
112
 
100
- export const getActivityHistory: Handler = async (args, ctx) => {
101
- const { project_id, activity_type, limit = 50 } = args as {
102
- project_id: string;
103
- activity_type?: string;
104
- limit?: number;
105
- };
106
-
107
- validateRequired(project_id, 'project_id');
108
- validateUUID(project_id, 'project_id');
113
+ export const getActivityHistory: Handler = async (args, _ctx) => {
114
+ const { project_id, activity_type, limit } = parseArgs(args, getActivityHistorySchema);
109
115
 
110
116
  const apiClient = getApiClient();
111
117
 
@@ -126,7 +132,7 @@ export const getActivityHistory: Handler = async (args, ctx) => {
126
132
  });
127
133
 
128
134
  if (!response.ok) {
129
- throw new Error(`Failed to get activity history: ${response.error}`);
135
+ return { result: { error: response.error || 'Failed to get activity history' }, isError: true };
130
136
  }
131
137
 
132
138
  return {
@@ -138,11 +144,8 @@ export const getActivityHistory: Handler = async (args, ctx) => {
138
144
  };
139
145
  };
140
146
 
141
- export const getActivitySchedules: Handler = async (args, ctx) => {
142
- const { project_id } = args as { project_id: string };
143
-
144
- validateRequired(project_id, 'project_id');
145
- validateUUID(project_id, 'project_id');
147
+ export const getActivitySchedules: Handler = async (args, _ctx) => {
148
+ const { project_id } = parseArgs(args, getActivitySchedulesSchema);
146
149
 
147
150
  const apiClient = getApiClient();
148
151
 
@@ -162,7 +165,7 @@ export const getActivitySchedules: Handler = async (args, ctx) => {
162
165
  });
163
166
 
164
167
  if (!response.ok) {
165
- throw new Error(`Failed to get activity schedules: ${response.error}`);
168
+ return { result: { error: response.error || 'Failed to get activity schedules' }, isError: true };
166
169
  }
167
170
 
168
171
  return {