fix: expose inbox filenames via MCP (#1328)
* fix: expose inbox filenames via MCP * fix: handle joined inbox attachments
This commit is contained in:
@@ -0,0 +1,78 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { createQueuedMockSupabase } from '@/tests/helpers'
|
||||||
|
import { tools } from '../server'
|
||||||
|
|
||||||
|
const tool = tools.find((candidate) => candidate.name === 'gnubok_list_inbox_items')!
|
||||||
|
|
||||||
|
describe('gnubok_list_inbox_items', () => {
|
||||||
|
it('advertises file_name in its item output contract', () => {
|
||||||
|
const schema = tool.outputSchema as {
|
||||||
|
properties: {
|
||||||
|
items: {
|
||||||
|
items: {
|
||||||
|
properties: Record<string, unknown>
|
||||||
|
required: string[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(schema.properties.items.items.properties.file_name).toEqual({
|
||||||
|
type: ['string', 'null'],
|
||||||
|
description: 'Original document file name, or null when the inbox item has no document',
|
||||||
|
})
|
||||||
|
expect(schema.properties.items.items.required).toContain('file_name')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('joins the document and returns its file_name on each list row', async () => {
|
||||||
|
const { supabase, enqueue, findCall } = createQueuedMockSupabase()
|
||||||
|
enqueue({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 'inbox-1',
|
||||||
|
status: 'received',
|
||||||
|
source: 'upload',
|
||||||
|
created_at: '2026-07-31T12:00:00Z',
|
||||||
|
extracted_data: null,
|
||||||
|
matched_supplier_id: null,
|
||||||
|
matched_transaction_id: null,
|
||||||
|
created_supplier_invoice_id: null,
|
||||||
|
created_journal_entry_id: null,
|
||||||
|
email_from: null,
|
||||||
|
email_subject: null,
|
||||||
|
error_message: null,
|
||||||
|
document_attachments: [{ file_name: 'dooer-export-2026-07.pdf' }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'inbox-2',
|
||||||
|
status: 'received',
|
||||||
|
source: 'email',
|
||||||
|
created_at: '2026-07-30T12:00:00Z',
|
||||||
|
extracted_data: null,
|
||||||
|
matched_supplier_id: null,
|
||||||
|
matched_transaction_id: null,
|
||||||
|
created_supplier_invoice_id: null,
|
||||||
|
created_journal_entry_id: null,
|
||||||
|
email_from: null,
|
||||||
|
email_subject: null,
|
||||||
|
error_message: null,
|
||||||
|
document_attachments: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
error: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = (await tool.execute({}, 'company-1', 'user-1', supabase as never)) as {
|
||||||
|
items: Array<{ file_name: string | null }>
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const select = findCall('invoice_inbox_items', 'select')?.[0]
|
||||||
|
expect(select).toContain('document_attachments(file_name)')
|
||||||
|
expect(result.items.map((item) => item.file_name)).toEqual([
|
||||||
|
'dooer-export-2026-07.pdf',
|
||||||
|
null,
|
||||||
|
])
|
||||||
|
expect(result.count).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -9159,7 +9159,7 @@ export const tools: McpTool[] = [
|
|||||||
{
|
{
|
||||||
name: 'gnubok_list_inbox_items',
|
name: 'gnubok_list_inbox_items',
|
||||||
title: 'List Inbox Items',
|
title: 'List Inbox Items',
|
||||||
description: 'List document inbox items. `processed` covers all terminal links (transaction, supplier invoice, journal entry); booked receipts count as done. unprocessed_only=true returns docs still needing handling.',
|
description: 'List document inbox items, including each original file_name. `processed` covers all terminal links (transaction, supplier invoice, journal entry); booked receipts count as done. unprocessed_only=true returns docs still needing handling.',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
@@ -9173,7 +9173,19 @@ export const tools: McpTool[] = [
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
properties: {
|
properties: {
|
||||||
items: { type: 'array', items: { type: 'object' } },
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
file_name: {
|
||||||
|
type: ['string', 'null'],
|
||||||
|
description: 'Original document file name, or null when the inbox item has no document',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['file_name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
count: { type: 'number' },
|
count: { type: 'number' },
|
||||||
},
|
},
|
||||||
required: ['items', 'count'],
|
required: ['items', 'count'],
|
||||||
@@ -9191,7 +9203,11 @@ export const tools: McpTool[] = [
|
|||||||
|
|
||||||
let query = supabase
|
let query = supabase
|
||||||
.from('invoice_inbox_items')
|
.from('invoice_inbox_items')
|
||||||
.select('id, status, source, created_at, extracted_data, matched_supplier_id, matched_transaction_id, created_supplier_invoice_id, created_journal_entry_id, email_from, email_subject, error_message')
|
.select(`
|
||||||
|
id, status, source, created_at, extracted_data, matched_supplier_id,
|
||||||
|
matched_transaction_id, created_supplier_invoice_id, created_journal_entry_id,
|
||||||
|
email_from, email_subject, error_message, document_attachments(file_name)
|
||||||
|
`)
|
||||||
.eq('company_id', companyId)
|
.eq('company_id', companyId)
|
||||||
.order('created_at', { ascending: false })
|
.order('created_at', { ascending: false })
|
||||||
// Fetch a wider window when filtering client-side so the limit
|
// Fetch a wider window when filtering client-side so the limit
|
||||||
@@ -9234,6 +9250,7 @@ export const tools: McpTool[] = [
|
|||||||
status: item.status,
|
status: item.status,
|
||||||
source: item.source,
|
source: item.source,
|
||||||
created_at: item.created_at,
|
created_at: item.created_at,
|
||||||
|
file_name: item.document_attachments?.[0]?.file_name ?? null,
|
||||||
vendor_name: vendorName,
|
vendor_name: vendorName,
|
||||||
amount,
|
amount,
|
||||||
invoice_date: invoiceDate,
|
invoice_date: invoiceDate,
|
||||||
|
|||||||
Reference in New Issue
Block a user