Skip to content

Commit eed2573

Browse files
committed
fix(jira): expose customFields as an array to LLMs and reject empty text
Round 7 (Cursor): - customFields tool param was type: 'json', which the schema builder exposes to LLMs as a JSON Schema object while the contract requires an array — a model emitting an object/map would 400. Switch to type: 'array' with an items schema describing { fieldId, type, value }, matching the array params in jira/write.ts. - text custom-field values must be non-empty (min 1), so '' is rejected at the boundary instead of passing validation and being silently skipped — consistent with select/userpicker/cascading. Adds a route test.
1 parent 19cd38e commit eed2573

3 files changed

Lines changed: 25 additions & 2 deletions

File tree

apps/sim/app/api/tools/jira/update/route.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ describe('Jira update route custom-field serialization', () => {
105105
expect(response.status).toBe(400)
106106
})
107107

108+
it('rejects a text entry with an empty value', async () => {
109+
const { response } = await update({
110+
customFields: [{ fieldId: 'customfield_10007', type: 'text', value: '' }],
111+
})
112+
expect(response.status).toBe(400)
113+
})
114+
108115
it('rejects a select entry with an empty option value', async () => {
109116
const { response } = await update({
110117
customFields: [{ fieldId: 'customfield_10001', type: 'select', value: { value: '' } }],

apps/sim/lib/api/contracts/selectors/jira.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ export const jiraCustomFieldEntrySchema = z
167167
z.object({
168168
fieldId: customFieldIdSchema,
169169
type: z.literal('text'),
170-
value: z.string(),
170+
value: z.string().min(1, 'text value cannot be empty'),
171171
}),
172172
z.object({ fieldId: customFieldIdSchema, type: z.literal('number'), value: numberInputSchema }),
173173
z.object({ fieldId: customFieldIdSchema, type: z.literal('select'), value: optionInputSchema }),

apps/sim/tools/jira/update.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,27 @@ export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> =
101101
description: 'Raw value for the legacy single custom field. Prefer `customFields`.',
102102
},
103103
customFields: {
104-
type: 'json',
104+
type: 'array',
105105
required: false,
106106
visibility: 'user-or-llm',
107107
description:
108108
'Structured custom fields to set, as an array of { fieldId, type, value }. type is one of text | number | select | multiselect | userpicker | multiuserpicker | cascading | raw. Serialization: select→{value} (or {id} for numeric option ids); multiselect→[{value}]; userpicker→{accountId}; multiuserpicker→[{accountId}]; cascading→{value, child:{value}} (value = [parent, child] or {parent, child}); text/number→scalar; raw→passed through untouched.',
109+
items: {
110+
type: 'object',
111+
required: ['fieldId', 'type', 'value'],
112+
properties: {
113+
fieldId: { type: 'string', description: 'Custom field id, e.g. customfield_10001' },
114+
type: {
115+
type: 'string',
116+
description:
117+
'One of: text, number, select, multiselect, userpicker, multiuserpicker, cascading, raw',
118+
},
119+
value: {
120+
description:
121+
'The value to set; its shape depends on type (scalar, option, accountId, array, or cascading object)',
122+
},
123+
},
124+
},
109125
},
110126
notifyUsers: {
111127
type: 'boolean',

0 commit comments

Comments
 (0)