Skip to content

Commit a963a86

Browse files
authored
List Contact Groups (#67)
* Added in list contact groups * Fix sync issue * Added in ability to optionall provide specific contact group id, which will show the contacts in the provided contact group id.
1 parent 3546d20 commit a963a86

File tree

4 files changed

+112
-1
lines changed

4 files changed

+112
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ NOTE: The `XERO_CLIENT_BEARER_TOKEN` will take precedence over the `XERO_CLIENT_
115115
- `list-payroll-leave-types`: Retrieve a list of all avaliable leave types in Xero Payroll
116116
- `list-aged-receivables-by-contact`: Retrieves aged receivables for a contact
117117
- `list-aged-payables-by-contact`: Retrieves aged payables for a contact
118+
- `list-contact-groups`: Retrieve a list of contact groups
118119
- `create-contact`: Create a new contact
119120
- `create-credit-note`: Create a new credit note
120121
- `create-invoice`: Create a new invoice
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { xeroClient } from "../clients/xero-client.js";
2+
import { ContactGroup } from "xero-node";
3+
import { XeroClientResponse } from "../types/tool-response.js";
4+
import { formatError } from "../helpers/format-error.js";
5+
import { getClientHeaders } from "../helpers/get-client-headers.js";
6+
7+
async function getContactGroups(contactGroupId?: string): Promise<ContactGroup[]> {
8+
await xeroClient.authenticate();
9+
10+
if (contactGroupId) {
11+
const response = await xeroClient.accountingApi.getContactGroup(
12+
xeroClient.tenantId,
13+
contactGroupId,
14+
getClientHeaders(),
15+
);
16+
return response.body.contactGroups ?? [];
17+
}
18+
19+
const response = await xeroClient.accountingApi.getContactGroups(
20+
xeroClient.tenantId,
21+
undefined, // where
22+
undefined, // order
23+
getClientHeaders(),
24+
);
25+
return response.body.contactGroups ?? [];
26+
}
27+
28+
/**
29+
* List all contact groups from Xero. If a contactGroupId is provided, it will return only that contact group.
30+
*/
31+
export async function listXeroContactGroups(contactGroupId?: string): Promise<
32+
XeroClientResponse<ContactGroup[]>
33+
> {
34+
try {
35+
const contactGroups = await getContactGroups(contactGroupId);
36+
37+
return {
38+
result: contactGroups,
39+
isError: false,
40+
error: null,
41+
};
42+
} catch (error) {
43+
return {
44+
result: null,
45+
isError: true,
46+
error: formatError(error),
47+
};
48+
}
49+
}

src/tools/list/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import ListQuotesTool from "./list-quotes.tool.js";
2525
import ListReportBalanceSheetTool from "./list-report-balance-sheet.tool.js";
2626
import ListTaxRatesTool from "./list-tax-rates.tool.js";
2727
import ListTrialBalanceTool from "./list-trial-balance.tool.js";
28+
import ListContactGroupsTool from "./list-contact-groups.tool.js";
2829

2930
export const ListTools = [
3031
ListAccountsTool,
@@ -48,5 +49,6 @@ export const ListTools = [
4849
ListPayrollLeaveTypesTool,
4950
ListAgedReceivablesByContact,
5051
ListAgedPayablesByContact,
51-
ListPayrollTimesheetsTool
52+
ListPayrollTimesheetsTool,
53+
ListContactGroupsTool,
5254
];
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { z } from "zod";
2+
import { listXeroContactGroups } from "../../handlers/list-xero-contact-groups.handler.js";
3+
import { CreateXeroTool } from "../../helpers/create-xero-tool.js";
4+
5+
const ListContactGroupsTool = CreateXeroTool(
6+
"list-contact-groups",
7+
`List all contact groups in Xero.
8+
You can optionally specify a contact group ID to retrieve details for that specific group, including its contacts.`,
9+
{
10+
contactGroupId: z
11+
.string()
12+
.optional()
13+
.describe("Optional ID of the contact group to retrieve"),
14+
},
15+
async (args) => {
16+
const response = await listXeroContactGroups(args?.contactGroupId);
17+
18+
if (response.error !== null) {
19+
return {
20+
content: [
21+
{
22+
type: "text" as const,
23+
text: `Error listing contact groups: ${response.error}`,
24+
},
25+
],
26+
};
27+
}
28+
29+
const contactGroups = response.result;
30+
31+
return {
32+
content: [
33+
{
34+
type: "text" as const,
35+
text: `Found ${contactGroups?.length || 0} contact groups:`,
36+
},
37+
...(contactGroups?.map((contactGroup) => ({
38+
type: "text" as const,
39+
text: [
40+
`Contact Group ID: ${contactGroup.contactGroupID}`,
41+
`Name: ${contactGroup.name}`,
42+
`Status: ${contactGroup.status}`,
43+
contactGroup.contacts
44+
? contactGroup.contacts.map(contact => [
45+
`Contact ID: ${contact.contactID}`,
46+
`Name: ${contact.name}`,
47+
].join('\n')
48+
).join('\n')
49+
: "No contacts in this contact group.",
50+
]
51+
.filter(Boolean)
52+
.join("\n"),
53+
})) || []),
54+
],
55+
};
56+
},
57+
);
58+
59+
export default ListContactGroupsTool;

0 commit comments

Comments
 (0)