diff --git a/src/handlers/get-xero-invoice-as-pdf.handler.ts b/src/handlers/get-xero-invoice-as-pdf.handler.ts new file mode 100644 index 00000000..cc9ffc59 --- /dev/null +++ b/src/handlers/get-xero-invoice-as-pdf.handler.ts @@ -0,0 +1,38 @@ +import { xeroClient } from "../clients/xero-client.js"; +import { XeroClientResponse } from "../types/tool-response.js"; +import { formatError } from "../helpers/format-error.js"; +import { getClientHeaders } from "../helpers/get-client-headers.js"; + +export interface InvoicePdfResult { + mimeType: "application/pdf"; + contentLength: number; + base64: string; +} + +export async function getXeroInvoiceAsPdf( + invoiceId: string, +): Promise> { + try { + await xeroClient.authenticate(); + + const response = await xeroClient.accountingApi.getInvoiceAsPdf( + xeroClient.tenantId, + invoiceId, + getClientHeaders(), + ); + + const buffer = response.body; + + return { + result: { + mimeType: "application/pdf", + contentLength: buffer.length, + base64: buffer.toString("base64"), + }, + isError: false, + error: null, + }; + } catch (error) { + return { result: null, isError: true, error: formatError(error) }; + } +} diff --git a/src/handlers/get-xero-invoice-online-url.handler.ts b/src/handlers/get-xero-invoice-online-url.handler.ts new file mode 100644 index 00000000..d6607abb --- /dev/null +++ b/src/handlers/get-xero-invoice-online-url.handler.ts @@ -0,0 +1,25 @@ +import { xeroClient } from "../clients/xero-client.js"; +import { XeroClientResponse } from "../types/tool-response.js"; +import { formatError } from "../helpers/format-error.js"; +import { getClientHeaders } from "../helpers/get-client-headers.js"; + +export async function getXeroInvoiceOnlineUrl( + invoiceId: string, +): Promise> { + try { + await xeroClient.authenticate(); + + const response = await xeroClient.accountingApi.getOnlineInvoice( + xeroClient.tenantId, + invoiceId, + getClientHeaders(), + ); + + const url = + response.body.onlineInvoices?.[0]?.onlineInvoiceUrl?.trim() || null; + + return { result: url, isError: false, error: null }; + } catch (error) { + return { result: null, isError: true, error: formatError(error) }; + } +} diff --git a/src/tools/get/get-invoice-as-pdf.tool.ts b/src/tools/get/get-invoice-as-pdf.tool.ts new file mode 100644 index 00000000..b795dfe0 --- /dev/null +++ b/src/tools/get/get-invoice-as-pdf.tool.ts @@ -0,0 +1,46 @@ +import { z } from "zod"; +import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; +import { getXeroInvoiceAsPdf } from "../../handlers/get-xero-invoice-as-pdf.handler.js"; + +const GetInvoiceAsPdfTool = CreateXeroTool( + "get-invoice-as-pdf", + `Download a Xero invoice rendered as a PDF. +Returns base64-encoded PDF content (can be large).`, + { + invoiceId: z.string().describe("The Xero Invoice ID (UUID)"), + }, + async ({ invoiceId }) => { + const response = await getXeroInvoiceAsPdf(invoiceId); + + if (response.isError) { + return { + content: [ + { + type: "text" as const, + text: `Error getting invoice PDF: ${response.error}`, + }, + ], + }; + } + + const pdf = response.result; + + return { + content: [ + { + type: "text" as const, + text: [ + `Invoice ID: ${invoiceId}`, + `MIME Type: ${pdf.mimeType}`, + `Content Length: ${pdf.contentLength} bytes`, + `Encoding: base64`, + `Content:`, + pdf.base64, + ].join("\n"), + }, + ], + }; + }, +); + +export default GetInvoiceAsPdfTool; diff --git a/src/tools/get/get-invoice-online-url.tool.ts b/src/tools/get/get-invoice-online-url.tool.ts new file mode 100644 index 00000000..5011740e --- /dev/null +++ b/src/tools/get/get-invoice-online-url.tool.ts @@ -0,0 +1,59 @@ +import { z } from "zod"; +import { CreateXeroTool } from "../../helpers/create-xero-tool.js"; +import { getXeroInvoiceOnlineUrl } from "../../handlers/get-xero-invoice-online-url.handler.js"; + +const GetInvoiceOnlineUrlTool = CreateXeroTool( + "get-invoice-online-url", + `Get a shareable online URL for a Xero invoice. +Note: Xero may return no URL if the invoice is not enabled for online viewing / online payments.`, + { + invoiceId: z.string().describe("The Xero Invoice ID (UUID)"), + }, + async ({ invoiceId }) => { + const response = await getXeroInvoiceOnlineUrl(invoiceId); + + if (response.isError) { + return { + content: [ + { + type: "text" as const, + text: `Error getting online invoice URL: ${response.error}`, + }, + ], + }; + } + + const url = response.result; + + if (!url) { + return { + content: [ + { + type: "text" as const, + text: [ + `Invoice ID: ${invoiceId}`, + `Online Invoice URL: (not available)`, + ``, + `Xero did not return an online URL for this invoice.`, + `This usually means online viewing / online payments are not enabled for the invoice.`, + ].join("\n"), + }, + ], + }; + } + + return { + content: [ + { + type: "text" as const, + text: [ + `Invoice ID: ${invoiceId}`, + `Online Invoice URL: ${url}`, + ].join("\n"), + }, + ], + }; + }, +); + +export default GetInvoiceOnlineUrlTool; diff --git a/src/tools/get/index.ts b/src/tools/get/index.ts index c4bfbd55..8370ee2a 100644 --- a/src/tools/get/index.ts +++ b/src/tools/get/index.ts @@ -1,5 +1,9 @@ import GetPayrollTimesheetTool from "./get-payroll-timesheet.tool.js"; +import GetInvoiceOnlineUrlTool from "./get-invoice-online-url.tool.js"; +import GetInvoiceAsPdfTool from "./get-invoice-as-pdf.tool.js"; export const GetTools = [ GetPayrollTimesheetTool, + GetInvoiceOnlineUrlTool, + GetInvoiceAsPdfTool, ];