Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/handlers/get-xero-invoice-as-pdf.handler.ts
Original file line number Diff line number Diff line change
@@ -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<XeroClientResponse<InvoicePdfResult>> {
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) };
}
}
25 changes: 25 additions & 0 deletions src/handlers/get-xero-invoice-online-url.handler.ts
Original file line number Diff line number Diff line change
@@ -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<XeroClientResponse<string | null>> {
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) };
}
}
46 changes: 46 additions & 0 deletions src/tools/get/get-invoice-as-pdf.tool.ts
Original file line number Diff line number Diff line change
@@ -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;
59 changes: 59 additions & 0 deletions src/tools/get/get-invoice-online-url.tool.ts
Original file line number Diff line number Diff line change
@@ -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;
4 changes: 4 additions & 0 deletions src/tools/get/index.ts
Original file line number Diff line number Diff line change
@@ -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,
];