@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
@@ -8,21 +8,66 @@
8
8
  * - update_cost_alert
9
9
  * - delete_cost_alert
10
10
  * - get_task_costs
11
+ * - get_body_of_work_costs
12
+ * - get_sprint_costs
11
13
  */
14
+ import { parseArgs, uuidValidator, createEnumValidator, ValidationError } from '../validators.js';
12
15
  import { getApiClient } from '../api-client.js';
16
+ const VALID_PERIODS = ['daily', 'weekly', 'monthly'];
17
+ const VALID_ALERT_TYPES = ['warning', 'critical'];
18
+ // Argument schemas for type-safe parsing
19
+ const getCostSummarySchema = {
20
+ project_id: { type: 'string', required: true, validate: uuidValidator },
21
+ period: { type: 'string', default: 'daily', validate: createEnumValidator(VALID_PERIODS) },
22
+ limit: { type: 'number', default: 30 },
23
+ };
24
+ const getCostAlertsSchema = {
25
+ project_id: { type: 'string', validate: uuidValidator },
26
+ };
27
+ const addCostAlertSchema = {
28
+ project_id: { type: 'string', validate: uuidValidator },
29
+ threshold_amount: { type: 'number', required: true },
30
+ threshold_period: { type: 'string', required: true, validate: createEnumValidator(VALID_PERIODS) },
31
+ alert_type: { type: 'string', default: 'warning', validate: createEnumValidator(VALID_ALERT_TYPES) },
32
+ };
33
+ const updateCostAlertSchema = {
34
+ alert_id: { type: 'string', required: true, validate: uuidValidator },
35
+ threshold_amount: { type: 'number' },
36
+ threshold_period: { type: 'string', validate: createEnumValidator(VALID_PERIODS) },
37
+ alert_type: { type: 'string', validate: createEnumValidator(VALID_ALERT_TYPES) },
38
+ enabled: { type: 'boolean' },
39
+ };
40
+ const deleteCostAlertSchema = {
41
+ alert_id: { type: 'string', required: true, validate: uuidValidator },
42
+ };
43
+ const getTaskCostsSchema = {
44
+ project_id: { type: 'string', required: true, validate: uuidValidator },
45
+ limit: { type: 'number', default: 20 },
46
+ };
47
+ const getBodyOfWorkCostsSchema = {
48
+ body_of_work_id: { type: 'string', validate: uuidValidator },
49
+ project_id: { type: 'string', validate: uuidValidator },
50
+ };
51
+ const getSprintCostsSchema = {
52
+ sprint_id: { type: 'string', validate: uuidValidator },
53
+ project_id: { type: 'string', validate: uuidValidator },
54
+ };
55
+ // Custom validator for positive numbers
56
+ function validatePositiveNumber(value, fieldName) {
57
+ if (value !== undefined && value <= 0) {
58
+ throw new ValidationError(`${fieldName} must be a positive number`, { field: fieldName });
59
+ }
60
+ }
13
61
  /**
14
62
  * Get cost summary for a project (daily, weekly, or monthly)
15
63
  */
16
- export const getCostSummary = async (args, ctx) => {
17
- const { project_id, period = 'daily', limit = 30 } = args;
18
- if (!project_id) {
19
- return {
20
- result: { error: 'project_id is required' },
21
- isError: true,
22
- };
23
- }
64
+ export const getCostSummary = async (args, _ctx) => {
65
+ const { project_id, period, limit } = parseArgs(args, getCostSummarySchema);
24
66
  const apiClient = getApiClient();
25
- const response = await apiClient.getCostSummary(project_id, { period, limit });
67
+ const response = await apiClient.getCostSummary(project_id, {
68
+ period: period,
69
+ limit
70
+ });
26
71
  if (!response.ok) {
27
72
  return {
28
73
  result: { error: response.error || 'Failed to get cost summary' },
@@ -34,8 +79,8 @@ export const getCostSummary = async (args, ctx) => {
34
79
  /**
35
80
  * Get cost alerts for the current user
36
81
  */
37
- export const getCostAlerts = async (args, ctx) => {
38
- const { project_id } = args;
82
+ export const getCostAlerts = async (args, _ctx) => {
83
+ const { project_id } = parseArgs(args, getCostAlertsSchema);
39
84
  const apiClient = getApiClient();
40
85
  const response = await apiClient.getCostAlerts();
41
86
  if (!response.ok) {
@@ -49,26 +94,16 @@ export const getCostAlerts = async (args, ctx) => {
49
94
  /**
50
95
  * Add a cost alert
51
96
  */
52
- export const addCostAlert = async (args, ctx) => {
53
- const { project_id, threshold_amount, threshold_period, alert_type = 'warning', } = args;
54
- if (!threshold_amount || threshold_amount <= 0) {
55
- return {
56
- result: { error: 'threshold_amount must be a positive number' },
57
- isError: true,
58
- };
59
- }
60
- if (!threshold_period || !['daily', 'weekly', 'monthly'].includes(threshold_period)) {
61
- return {
62
- result: { error: 'threshold_period must be "daily", "weekly", or "monthly"' },
63
- isError: true,
64
- };
65
- }
97
+ export const addCostAlert = async (args, _ctx) => {
98
+ const { project_id, threshold_amount, threshold_period, alert_type } = parseArgs(args, addCostAlertSchema);
99
+ // Additional validation for positive amount
100
+ validatePositiveNumber(threshold_amount, 'threshold_amount');
66
101
  const apiClient = getApiClient();
67
102
  const response = await apiClient.addCostAlert({
68
103
  project_id,
69
- threshold_amount,
70
- threshold_period,
71
- alert_type
104
+ threshold_amount: threshold_amount,
105
+ threshold_period: threshold_period,
106
+ alert_type: alert_type
72
107
  });
73
108
  if (!response.ok) {
74
109
  return {
@@ -81,11 +116,12 @@ export const addCostAlert = async (args, ctx) => {
81
116
  /**
82
117
  * Update a cost alert
83
118
  */
84
- export const updateCostAlert = async (args, ctx) => {
85
- const { alert_id, threshold_amount, threshold_period, alert_type, enabled, } = args;
86
- if (!alert_id) {
119
+ export const updateCostAlert = async (args, _ctx) => {
120
+ const { alert_id, threshold_amount, threshold_period, alert_type, enabled } = parseArgs(args, updateCostAlertSchema);
121
+ // Check that at least one update is provided
122
+ if (threshold_amount === undefined && threshold_period === undefined && alert_type === undefined && enabled === undefined) {
87
123
  return {
88
- result: { error: 'alert_id is required' },
124
+ result: { error: 'No updates provided' },
89
125
  isError: true,
90
126
  };
91
127
  }
@@ -98,59 +134,83 @@ export const updateCostAlert = async (args, ctx) => {
98
134
  updates.alert_type = alert_type;
99
135
  if (enabled !== undefined)
100
136
  updates.enabled = enabled;
101
- if (Object.keys(updates).length === 0) {
137
+ const apiClient = getApiClient();
138
+ const response = await apiClient.updateCostAlert(alert_id, updates);
139
+ if (!response.ok) {
102
140
  return {
103
- result: { error: 'No updates provided' },
141
+ result: { error: response.error || 'Failed to update cost alert' },
104
142
  isError: true,
105
143
  };
106
144
  }
145
+ return { result: response.data };
146
+ };
147
+ /**
148
+ * Delete a cost alert
149
+ */
150
+ export const deleteCostAlert = async (args, _ctx) => {
151
+ const { alert_id } = parseArgs(args, deleteCostAlertSchema);
107
152
  const apiClient = getApiClient();
108
- const response = await apiClient.updateCostAlert(alert_id, updates);
153
+ const response = await apiClient.deleteCostAlert(alert_id);
109
154
  if (!response.ok) {
110
155
  return {
111
- result: { error: response.error || 'Failed to update cost alert' },
156
+ result: { error: response.error || 'Failed to delete cost alert' },
112
157
  isError: true,
113
158
  };
114
159
  }
115
160
  return { result: response.data };
116
161
  };
117
162
  /**
118
- * Delete a cost alert
163
+ * Get task costs for a project
164
+ */
165
+ export const getTaskCosts = async (args, _ctx) => {
166
+ const { project_id, limit } = parseArgs(args, getTaskCostsSchema);
167
+ const apiClient = getApiClient();
168
+ const response = await apiClient.getTaskCosts(project_id, limit);
169
+ if (!response.ok) {
170
+ return {
171
+ result: { error: response.error || 'Failed to get task costs' },
172
+ isError: true,
173
+ };
174
+ }
175
+ return { result: response.data };
176
+ };
177
+ /**
178
+ * Get body of work costs with phase breakdown
119
179
  */
120
- export const deleteCostAlert = async (args, ctx) => {
121
- const { alert_id } = args;
122
- if (!alert_id) {
180
+ export const getBodyOfWorkCosts = async (args, _ctx) => {
181
+ const { body_of_work_id, project_id } = parseArgs(args, getBodyOfWorkCostsSchema);
182
+ if (!body_of_work_id && !project_id) {
123
183
  return {
124
- result: { error: 'alert_id is required' },
184
+ result: { error: 'Either body_of_work_id or project_id is required' },
125
185
  isError: true,
126
186
  };
127
187
  }
128
188
  const apiClient = getApiClient();
129
- const response = await apiClient.deleteCostAlert(alert_id);
189
+ const response = await apiClient.getBodyOfWorkCosts({ body_of_work_id, project_id });
130
190
  if (!response.ok) {
131
191
  return {
132
- result: { error: response.error || 'Failed to delete cost alert' },
192
+ result: { error: response.error || 'Failed to get body of work costs' },
133
193
  isError: true,
134
194
  };
135
195
  }
136
196
  return { result: response.data };
137
197
  };
138
198
  /**
139
- * Get task costs for a project
199
+ * Get sprint costs with velocity metrics
140
200
  */
141
- export const getTaskCosts = async (args, ctx) => {
142
- const { project_id, limit = 20 } = args;
143
- if (!project_id) {
201
+ export const getSprintCosts = async (args, _ctx) => {
202
+ const { sprint_id, project_id } = parseArgs(args, getSprintCostsSchema);
203
+ if (!sprint_id && !project_id) {
144
204
  return {
145
- result: { error: 'project_id is required' },
205
+ result: { error: 'Either sprint_id or project_id is required' },
146
206
  isError: true,
147
207
  };
148
208
  }
149
209
  const apiClient = getApiClient();
150
- const response = await apiClient.getTaskCosts(project_id, limit);
210
+ const response = await apiClient.getSprintCosts({ sprint_id, project_id });
151
211
  if (!response.ok) {
152
212
  return {
153
- result: { error: response.error || 'Failed to get task costs' },
213
+ result: { error: response.error || 'Failed to get sprint costs' },
154
214
  isError: true,
155
215
  };
156
216
  }
@@ -166,4 +226,6 @@ export const costHandlers = {
166
226
  update_cost_alert: updateCostAlert,
167
227
  delete_cost_alert: deleteCostAlert,
168
228
  get_task_costs: getTaskCosts,
229
+ get_body_of_work_costs: getBodyOfWorkCosts,
230
+ get_sprint_costs: getSprintCosts,
169
231
  };
@@ -8,35 +8,50 @@
8
8
  *
9
9
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
10
10
  */
11
- import { validateRequired, validateUUID } from '../validators.js';
11
+ import { parseArgs, uuidValidator } from '../validators.js';
12
12
  import { getApiClient } from '../api-client.js';
13
+ // Argument schemas for type-safe parsing
14
+ const logDecisionSchema = {
15
+ project_id: { type: 'string', required: true, validate: uuidValidator },
16
+ title: { type: 'string', required: true },
17
+ description: { type: 'string', required: true },
18
+ rationale: { type: 'string' },
19
+ alternatives_considered: { type: 'array' },
20
+ };
21
+ const getDecisionsSchema = {
22
+ project_id: { type: 'string', required: true, validate: uuidValidator },
23
+ limit: { type: 'number', default: 50 },
24
+ offset: { type: 'number', default: 0 },
25
+ search_query: { type: 'string' },
26
+ };
27
+ const deleteDecisionSchema = {
28
+ decision_id: { type: 'string', required: true, validate: uuidValidator },
29
+ };
13
30
  export const logDecision = async (args, ctx) => {
14
- const { project_id, title, description, rationale, alternatives_considered } = args;
15
- validateRequired(project_id, 'project_id');
16
- validateUUID(project_id, 'project_id');
17
- validateRequired(title, 'title');
18
- validateRequired(description, 'description');
31
+ const { project_id, title, description, rationale, alternatives_considered } = parseArgs(args, logDecisionSchema);
19
32
  const { session } = ctx;
20
33
  const apiClient = getApiClient();
21
34
  const response = await apiClient.logDecision(project_id, {
22
35
  title,
23
36
  description,
24
37
  rationale,
25
- alternatives_considered
38
+ alternatives_considered: alternatives_considered
26
39
  }, session.currentSessionId || undefined);
27
40
  if (!response.ok) {
28
- throw new Error(`Failed to log decision: ${response.error}`);
41
+ return { result: { error: response.error || 'Failed to log decision' }, isError: true };
29
42
  }
30
43
  return { result: { success: true, title, decision_id: response.data?.decision_id } };
31
44
  };
32
- export const getDecisions = async (args, ctx) => {
33
- const { project_id } = args;
34
- validateRequired(project_id, 'project_id');
35
- validateUUID(project_id, 'project_id');
45
+ export const getDecisions = async (args, _ctx) => {
46
+ const { project_id, limit, offset, search_query } = parseArgs(args, getDecisionsSchema);
36
47
  const apiClient = getApiClient();
37
- const response = await apiClient.getDecisions(project_id);
48
+ const response = await apiClient.getDecisions(project_id, {
49
+ limit,
50
+ offset,
51
+ search_query
52
+ });
38
53
  if (!response.ok) {
39
- throw new Error(`Failed to fetch decisions: ${response.error}`);
54
+ return { result: { error: response.error || 'Failed to fetch decisions' }, isError: true };
40
55
  }
41
56
  return {
42
57
  result: {
@@ -44,14 +59,12 @@ export const getDecisions = async (args, ctx) => {
44
59
  },
45
60
  };
46
61
  };
47
- export const deleteDecision = async (args, ctx) => {
48
- const { decision_id } = args;
49
- validateRequired(decision_id, 'decision_id');
50
- validateUUID(decision_id, 'decision_id');
62
+ export const deleteDecision = async (args, _ctx) => {
63
+ const { decision_id } = parseArgs(args, deleteDecisionSchema);
51
64
  const apiClient = getApiClient();
52
65
  const response = await apiClient.deleteDecision(decision_id);
53
66
  if (!response.ok) {
54
- throw new Error(`Failed to delete decision: ${response.error}`);
67
+ return { result: { error: response.error || 'Failed to delete decision' }, isError: true };
55
68
  }
56
69
  return { result: { success: true } };
57
70
  };