fix(mcp): allow null optional fields on transaction list tools (#484)

* fix(mcp): allow null optional fields on transaction list tools

Signed-off-by: antonisoaho <anton@isoaho.se>

* test(mcp): assert null optional fields on list_transactions_without_documents

* Update extensions/general/mcp-server/__tests__/list-uncategorized-transactions.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Signed-off-by: antonisoaho <anton@isoaho.se>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Anton
2026-06-08 09:41:43 +02:00
committed by GitHub
co-authored by greptile-apps[bot]
parent 3f422b9dc8
commit 5f79a74a2e
3 changed files with 102 additions and 8 deletions
@@ -55,6 +55,45 @@ describe('gnubok_list_transactions_without_documents', () => {
expect(result.transactions[0].journal_entry_id).toBe('je-1')
})
it('returns rows when DB has null merchant_name, reference, is_business, category (MCP structured output)', async () => {
const rows = [
{
id: 't-no-doc-1',
date: '2026-03-17',
description: 'Nolla skuld',
amount: -2745,
currency: 'SEK',
merchant_name: null,
reference: null,
is_business: null,
category: null,
journal_entry_id: 'je-nolla-1',
},
]
const { supabase, enqueue } = createQueuedMockSupabase()
enqueue({ data: null, error: null, count: 1 })
enqueue({ data: rows, error: null })
const result = (await tool.execute(
{ limit: 20 },
'company-1',
'user-1',
supabase as never
)) as {
transactions: typeof rows
count: number
total_count: number
has_more: boolean
}
expect(result.count).toBe(1)
expect(result.transactions[0].journal_entry_id).toBe('je-nolla-1')
expect(result.transactions[0].merchant_name).toBeNull()
expect(result.transactions[0].reference).toBeNull()
expect(result.transactions[0].is_business).toBeNull()
expect(result.transactions[0].category).toBeNull()
})
it('returns empty result when nothing matches', async () => {
const { supabase, enqueue } = createQueuedMockSupabase()
enqueue({ data: null, error: null, count: 0 })
@@ -0,0 +1,55 @@
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { createQueuedMockSupabase } from '@/tests/helpers'
import { tools } from '../server'
const tool = tools.find((t) => t.name === 'gnubok_list_uncategorized_transactions')!
beforeEach(() => {
vi.clearAllMocks()
})
describe('gnubok_list_uncategorized_transactions', () => {
it('is registered as a read-only paginated tool', () => {
expect(tool).toBeDefined()
expect(tool.annotations?.readOnlyHint).toBe(true)
const schema = tool.outputSchema as Record<string, unknown>
expect((schema.properties as Record<string, unknown>).transactions).toBeDefined()
expect((schema.properties as Record<string, unknown>).total_count).toBeDefined()
})
it('returns rows when DB has null merchant_name, reference, is_business (MCP structured output)', async () => {
const rows = [
{
id: 't-uncat-1',
date: '2026-03-09',
description: 'Transfer',
amount: -6000,
currency: 'SEK',
merchant_name: null,
reference: null,
is_business: null,
category: null,
},
]
const { supabase, enqueue } = createQueuedMockSupabase()
enqueue({ data: null, error: null, count: 1 })
enqueue({ data: rows, error: null })
const result = (await tool.execute(
{ limit: 20 },
'company-1',
'user-1',
supabase as never
)) as {
transactions: typeof rows
count: number
total_count: number
has_more: boolean
}
expect(result.count).toBe(1)
expect(result.transactions[0].merchant_name).toBeNull()
expect(result.transactions[0].reference).toBeNull()
expect(result.transactions[0].is_business).toBeNull()
})
})
+8 -8
View File
@@ -2231,10 +2231,10 @@ export const tools: McpTool[] = [
description: { type: 'string' },
amount: { type: 'number' },
currency: { type: 'string' },
merchant_name: { type: 'string' },
reference: { type: 'string' },
is_business: { type: 'boolean' },
category: { type: 'string' },
merchant_name: { type: ['string', 'null'] },
reference: { type: ['string', 'null'] },
is_business: { type: ['boolean', 'null'] },
category: { type: ['string', 'null'] },
},
}),
annotations: {
@@ -2303,10 +2303,10 @@ export const tools: McpTool[] = [
description: { type: 'string' },
amount: { type: 'number' },
currency: { type: 'string' },
merchant_name: { type: 'string' },
reference: { type: 'string' },
is_business: { type: 'boolean' },
category: { type: 'string' },
merchant_name: { type: ['string', 'null'] },
reference: { type: ['string', 'null'] },
is_business: { type: ['boolean', 'null'] },
category: { type: ['string', 'null'] },
journal_entry_id: { type: 'string' },
},
}),