Skip to content

Commit 59b872e

Browse files
staubinaclaude
andcommitted
fix: add enum constraints and validation to unit parameter on metrics tools
- Add JSON Schema enum arrays to all unit properties in tool definitions (quarter/month/week, plus sprint for team_metrics) - Add _validateEnums() to ApiTool that checks enum-constrained params before calling the API, returning actionable error messages - Add tests for enum validation (rejects invalid, accepts valid, skips non-enum fields) Resolves LLMF-123 Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b41e4f8 commit 59b872e

2 files changed

Lines changed: 127 additions & 22 deletions

File tree

server/tools.js

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,28 @@ class ApiTool {
3131
};
3232
}
3333

34+
_validateEnums(params) {
35+
const errors = [];
36+
const props = this.inputSchema?.properties || {};
37+
for (const [key, value] of Object.entries(params)) {
38+
const schema = props[key];
39+
if (schema?.enum && !schema.enum.includes(value)) {
40+
errors.push(`Invalid value "${value}" for "${key}". Valid values: ${schema.enum.join(', ')}`);
41+
}
42+
}
43+
return errors;
44+
}
45+
3446
/**
3547
* Executes the tool by calling the Jellyfish API.
3648
* Override by passing a `call` function in the constructor
3749
* when the endpoint URL requires dynamic path parameters.
3850
*/
3951
async call(params) {
52+
const errors = this._validateEnums(params);
53+
if (errors.length) {
54+
return { error: 'Invalid parameters', message: errors.join('\n') };
55+
}
4056
return api_generic(this._endpoint, params, this.name);
4157
}
4258
}
@@ -88,7 +104,7 @@ const apiTools = [
88104
properties: {
89105
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
90106
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
91-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" }
107+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" }
92108
},
93109
required: []
94110
},
@@ -103,7 +119,7 @@ const apiTools = [
103119
properties: {
104120
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
105121
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
106-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" }
122+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" }
107123
},
108124
required: []
109125
},
@@ -118,7 +134,7 @@ const apiTools = [
118134
properties: {
119135
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
120136
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
121-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
137+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
122138
person_id: { type: "array", items: { type: "integer" }, description: "List of person IDs" }
123139
},
124140
required: ["person_id"]
@@ -134,7 +150,7 @@ const apiTools = [
134150
properties: {
135151
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
136152
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
137-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
153+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
138154
person_id: { type: "array", items: { type: "integer" }, description: "List of person IDs" }
139155
},
140156
required: ["person_id"]
@@ -150,7 +166,7 @@ const apiTools = [
150166
properties: {
151167
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
152168
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
153-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
169+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
154170
team_id: { type: "array", items: { type: "integer" }, description: "List of Jellyfish team IDs" }
155171
},
156172
required: ["team_id"]
@@ -166,7 +182,7 @@ const apiTools = [
166182
properties: {
167183
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
168184
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
169-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
185+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
170186
team_id: { type: "array", items: { type: "integer" }, description: "List of team IDs" }
171187
},
172188
required: ["team_id"]
@@ -183,7 +199,7 @@ const apiTools = [
183199
properties: {
184200
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
185201
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
186-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
202+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
187203
series: { type: "boolean", description: "Whether to return series data" },
188204
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
189205
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -202,7 +218,7 @@ const apiTools = [
202218
properties: {
203219
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
204220
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
205-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
221+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
206222
series: { type: "boolean", description: "Whether to return series data" },
207223
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
208224
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -223,7 +239,7 @@ const apiTools = [
223239
properties: {
224240
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
225241
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
226-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
242+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
227243
series: { type: "boolean", description: "Whether to return series data" },
228244
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
229245
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -242,7 +258,7 @@ const apiTools = [
242258
properties: {
243259
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
244260
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
245-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
261+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
246262
series: { type: "boolean", description: "Whether to return series data" },
247263
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
248264
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -261,7 +277,7 @@ const apiTools = [
261277
properties: {
262278
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
263279
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
264-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
280+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
265281
series: { type: "boolean", description: "Whether to return series data" },
266282
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
267283
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -282,7 +298,7 @@ const apiTools = [
282298
properties: {
283299
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
284300
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
285-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
301+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
286302
series: { type: "boolean", description: "Whether to return series data" },
287303
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
288304
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -302,7 +318,7 @@ const apiTools = [
302318
properties: {
303319
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
304320
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
305-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
321+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
306322
series: { type: "boolean", description: "Whether to return series data" },
307323
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
308324
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -322,7 +338,7 @@ const apiTools = [
322338
properties: {
323339
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
324340
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
325-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
341+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
326342
series: { type: "boolean", description: "Whether to return series data" },
327343
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
328344
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -355,7 +371,7 @@ const apiTools = [
355371
properties: {
356372
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
357373
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
358-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\", \"sprint\")" },
374+
unit: { type: "string", enum: ["quarter", "month", "week", "sprint"], description: "Time unit" },
359375
series: { type: "boolean", description: "Whether to return series data" },
360376
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
361377
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -378,7 +394,7 @@ const apiTools = [
378394
properties: {
379395
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
380396
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
381-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\", \"sprint\")" },
397+
unit: { type: "string", enum: ["quarter", "month", "week", "sprint"], description: "Time unit" },
382398
series: { type: "boolean", description: "Whether to return series data" },
383399
decimal_places: { type: "integer", description: "Show FTE amounts rounded to this many decimal places (1 to 3). Defaults to 1." },
384400
include_below_threshold_card_keys: { type: "boolean", description: "Include allocated card keys that round to 0 FTE. Omit when using max_n_allocation_card_keys (default true works well). Set to false when omitting max_n_allocation_card_keys to limit excessive data volume from minor allocations." },
@@ -404,7 +420,7 @@ const apiTools = [
404420
deliverable_id: { type: "integer", description: "Jellyfish deliverable id" },
405421
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
406422
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
407-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" }
423+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" }
408424
},
409425
required: ["deliverable_id"]
410426
},
@@ -486,7 +502,7 @@ const apiTools = [
486502
properties: {
487503
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
488504
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
489-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
505+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
490506
id: { type: "integer", description: "Jellyfish team id" },
491507
devex_team_ref: { type: "string", description: "Unique identifier for a team in DevEx" },
492508
team_id: { type: "integer", description: "Jellyfish team id" },
@@ -507,7 +523,7 @@ const apiTools = [
507523
properties: {
508524
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
509525
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
510-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
526+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
511527
series: { type: "boolean", description: "Whether to return series data" }
512528
},
513529
required: []
@@ -523,7 +539,7 @@ const apiTools = [
523539
properties: {
524540
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
525541
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
526-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
542+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
527543
series: { type: "boolean", description: "Whether to return series data" },
528544
person_id: { type: "array", items: { type: "integer" }, description: "List of person IDs" }
529545
},
@@ -540,7 +556,7 @@ const apiTools = [
540556
properties: {
541557
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
542558
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
543-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\", \"sprint\")" },
559+
unit: { type: "string", enum: ["quarter", "month", "week", "sprint"], description: "Time unit" },
544560
series: { type: "boolean", description: "Whether to return series data" },
545561
team_id: { type: "array", items: { type: "integer" }, description: "List of team IDs" }
546562
},
@@ -572,7 +588,7 @@ const apiTools = [
572588
properties: {
573589
start_date: { type: "string", description: "Start date (YYYY-MM-DD)" },
574590
end_date: { type: "string", description: "End date (YYYY-MM-DD)" },
575-
unit: { type: "string", description: "Time unit (\"quarter\", \"month\", \"week\")" },
591+
unit: { type: "string", enum: ["quarter", "month", "week"], description: "Time unit" },
576592
series: { type: "boolean", description: "Whether to return series data" },
577593
instance_slug: { type: "array", items: { type: "string" }, description: "List of git instance slugs" },
578594
organization_name: { type: "array", items: { type: "string" }, description: "List of organization names" },

0 commit comments

Comments
 (0)