@procurementexpress.com/mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +376 -0
- package/dist/api-client.d.ts +31 -0
- package/dist/api-client.js +108 -0
- package/dist/auth.d.ts +29 -0
- package/dist/auth.js +59 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +119 -0
- package/dist/tool-helpers.d.ts +34 -0
- package/dist/tool-helpers.js +26 -0
- package/dist/tools/approval-flows.d.ts +3 -0
- package/dist/tools/approval-flows.js +121 -0
- package/dist/tools/budgets.d.ts +3 -0
- package/dist/tools/budgets.js +73 -0
- package/dist/tools/comments.d.ts +3 -0
- package/dist/tools/comments.js +24 -0
- package/dist/tools/companies.d.ts +3 -0
- package/dist/tools/companies.js +67 -0
- package/dist/tools/departments.d.ts +3 -0
- package/dist/tools/departments.js +69 -0
- package/dist/tools/invoices.d.ts +3 -0
- package/dist/tools/invoices.js +132 -0
- package/dist/tools/payments.d.ts +3 -0
- package/dist/tools/payments.js +65 -0
- package/dist/tools/products.d.ts +3 -0
- package/dist/tools/products.js +57 -0
- package/dist/tools/purchase-orders.d.ts +3 -0
- package/dist/tools/purchase-orders.js +152 -0
- package/dist/tools/supplementary.d.ts +3 -0
- package/dist/tools/supplementary.js +70 -0
- package/dist/tools/suppliers.d.ts +3 -0
- package/dist/tools/suppliers.js +80 -0
- package/dist/tools/tax-rates.d.ts +3 -0
- package/dist/tools/tax-rates.js +44 -0
- package/dist/tools/users.d.ts +3 -0
- package/dist/tools/users.js +37 -0
- package/dist/tools/webhooks.d.ts +3 -0
- package/dist/tools/webhooks.js +61 -0
- package/dist/types.d.ts +732 -0
- package/dist/types.js +3 -0
- package/package.json +50 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
const invoiceLineItemSchema = z.object({
|
|
4
|
+
description: z.string().describe("Line item description"),
|
|
5
|
+
unit_price: z.number().describe("Unit price"),
|
|
6
|
+
quantity: z.number().describe("Quantity"),
|
|
7
|
+
vat: z.number().optional().describe("VAT amount"),
|
|
8
|
+
net_amount: z.number().optional().describe("Net amount"),
|
|
9
|
+
purchase_order_id: z.number().int().optional().describe("Related PO ID"),
|
|
10
|
+
purchase_order_item_id: z.number().int().optional().describe("Related PO item ID"),
|
|
11
|
+
});
|
|
12
|
+
export function registerInvoiceTools(server, apiClient) {
|
|
13
|
+
server.registerTool("list_invoices", {
|
|
14
|
+
description: "List invoices with pagination and filters (100 per page)",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
page: z.number().int().positive().optional().describe("Page number"),
|
|
17
|
+
archived: z.boolean().optional().describe("Filter by archived status"),
|
|
18
|
+
requester_id: z.number().int().optional().describe("Filter by requester ID"),
|
|
19
|
+
approver_id: z.number().int().optional().describe("Filter by approver ID"),
|
|
20
|
+
supplier_id: z.number().int().optional().describe("Filter by supplier ID"),
|
|
21
|
+
invoice_date_filter: z.string().optional().describe("Date filter (e.g. 'last 7days')"),
|
|
22
|
+
},
|
|
23
|
+
}, withErrorHandling(async (args) => {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (args.page)
|
|
26
|
+
params.set("page", String(args.page));
|
|
27
|
+
if (args.archived !== undefined)
|
|
28
|
+
params.set("archived", String(args.archived));
|
|
29
|
+
if (args.requester_id)
|
|
30
|
+
params.set("requester_id", String(args.requester_id));
|
|
31
|
+
if (args.approver_id)
|
|
32
|
+
params.set("approver_id", String(args.approver_id));
|
|
33
|
+
if (args.supplier_id)
|
|
34
|
+
params.set("supplier_id", String(args.supplier_id));
|
|
35
|
+
if (args.invoice_date_filter)
|
|
36
|
+
params.set("invoice_date_filter", args.invoice_date_filter);
|
|
37
|
+
const query = params.toString();
|
|
38
|
+
const path = `${apiClient.buildPath("/invoices")}${query ? `?${query}` : ""}`;
|
|
39
|
+
const result = await apiClient.get(path);
|
|
40
|
+
return jsonResponse(result);
|
|
41
|
+
}));
|
|
42
|
+
server.registerTool("get_invoice", {
|
|
43
|
+
description: "Get invoice details by ID",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
id: z.number().int().positive().describe("Invoice ID"),
|
|
46
|
+
},
|
|
47
|
+
}, withErrorHandling(async (args) => {
|
|
48
|
+
const invoice = await apiClient.get(apiClient.buildPath(`/invoices/${args.id}`));
|
|
49
|
+
return jsonResponse(invoice);
|
|
50
|
+
}));
|
|
51
|
+
server.registerTool("create_invoice", {
|
|
52
|
+
description: "Create a new invoice",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
invoice_number: z.string().optional().describe("Invoice number"),
|
|
55
|
+
issue_date: z.string().optional().describe("Issue date"),
|
|
56
|
+
supplier_id: z.number().int().optional().describe("Supplier ID"),
|
|
57
|
+
received_date: z.string().optional().describe("Received date"),
|
|
58
|
+
due_date: z.string().optional().describe("Due date"),
|
|
59
|
+
gross_amount: z.number().describe("Gross amount"),
|
|
60
|
+
currency_id: z.number().int().describe("Currency ID"),
|
|
61
|
+
company_id: z.number().int().describe("Company ID"),
|
|
62
|
+
line_items: z.array(invoiceLineItemSchema).optional().describe("Invoice line items"),
|
|
63
|
+
},
|
|
64
|
+
}, withErrorHandling(async (args) => {
|
|
65
|
+
const { line_items, ...invoiceData } = args;
|
|
66
|
+
const body = {
|
|
67
|
+
invoice: {
|
|
68
|
+
...invoiceData,
|
|
69
|
+
...(line_items ? { invoice_line_items_attributes: line_items } : {}),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const invoice = await apiClient.post(apiClient.buildPath("/invoices"), body);
|
|
73
|
+
return jsonResponse(invoice);
|
|
74
|
+
}));
|
|
75
|
+
server.registerTool("update_invoice", {
|
|
76
|
+
description: "Update an existing invoice",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
id: z.number().int().positive().describe("Invoice ID"),
|
|
79
|
+
invoice_number: z.string().optional().describe("Invoice number"),
|
|
80
|
+
issue_date: z.string().optional().describe("Issue date"),
|
|
81
|
+
supplier_id: z.number().int().optional().describe("Supplier ID"),
|
|
82
|
+
due_date: z.string().optional().describe("Due date"),
|
|
83
|
+
gross_amount: z.number().optional().describe("Gross amount"),
|
|
84
|
+
},
|
|
85
|
+
}, withErrorHandling(async (args) => {
|
|
86
|
+
const { id, ...data } = args;
|
|
87
|
+
const invoice = await apiClient.put(apiClient.buildPath(`/invoices/${id}`), { invoice: data });
|
|
88
|
+
return jsonResponse(invoice);
|
|
89
|
+
}));
|
|
90
|
+
server.registerTool("accept_invoice", {
|
|
91
|
+
description: "Accept an invoice that is awaiting review",
|
|
92
|
+
inputSchema: { id: z.number().int().positive().describe("Invoice ID") },
|
|
93
|
+
}, withErrorHandling(async (args) => {
|
|
94
|
+
const result = await apiClient.put(apiClient.buildPath(`/invoices/${args.id}/accept`));
|
|
95
|
+
return jsonResponse(result);
|
|
96
|
+
}));
|
|
97
|
+
server.registerTool("approve_invoice", {
|
|
98
|
+
description: "Approve an invoice",
|
|
99
|
+
inputSchema: { id: z.number().int().positive().describe("Invoice ID") },
|
|
100
|
+
}, withErrorHandling(async (args) => {
|
|
101
|
+
const result = await apiClient.put(apiClient.buildPath(`/invoices/${args.id}/approve`));
|
|
102
|
+
return jsonResponse(result);
|
|
103
|
+
}));
|
|
104
|
+
server.registerTool("reject_invoice", {
|
|
105
|
+
description: "Reject an invoice",
|
|
106
|
+
inputSchema: { id: z.number().int().positive().describe("Invoice ID") },
|
|
107
|
+
}, withErrorHandling(async (args) => {
|
|
108
|
+
const result = await apiClient.put(apiClient.buildPath(`/invoices/${args.id}/reject`));
|
|
109
|
+
return jsonResponse(result);
|
|
110
|
+
}));
|
|
111
|
+
server.registerTool("cancel_invoice", {
|
|
112
|
+
description: "Cancel an invoice",
|
|
113
|
+
inputSchema: { id: z.number().int().positive().describe("Invoice ID") },
|
|
114
|
+
}, withErrorHandling(async (args) => {
|
|
115
|
+
const result = await apiClient.put(apiClient.buildPath(`/invoices/${args.id}/cancel`));
|
|
116
|
+
return jsonResponse(result);
|
|
117
|
+
}));
|
|
118
|
+
server.registerTool("archive_invoice", {
|
|
119
|
+
description: "Archive an invoice",
|
|
120
|
+
inputSchema: { id: z.number().int().positive().describe("Invoice ID") },
|
|
121
|
+
}, withErrorHandling(async (args) => {
|
|
122
|
+
const result = await apiClient.put(apiClient.buildPath(`/invoices/${args.id}/archive`));
|
|
123
|
+
return jsonResponse(result);
|
|
124
|
+
}));
|
|
125
|
+
server.registerTool("dearchive_invoice", {
|
|
126
|
+
description: "Restore an archived invoice",
|
|
127
|
+
inputSchema: { id: z.number().int().positive().describe("Invoice ID") },
|
|
128
|
+
}, withErrorHandling(async (args) => {
|
|
129
|
+
const result = await apiClient.put(apiClient.buildPath(`/invoices/${args.id}/dearchive`));
|
|
130
|
+
return jsonResponse(result);
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
export function registerPaymentTools(server, apiClient) {
|
|
4
|
+
server.registerTool("create_payment", {
|
|
5
|
+
description: "Create a payment (feature flagged). ptype values: bank_transfer, card, credit_card, check, cash, one_time_card, letter_of_credit, other",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
user_id: z.number().int().describe("Payment creator user ID"),
|
|
8
|
+
reference: z.string().optional().describe("Reference number"),
|
|
9
|
+
supplier_id: z.number().int().describe("Supplier ID"),
|
|
10
|
+
ptype: z
|
|
11
|
+
.enum([
|
|
12
|
+
"bank_transfer",
|
|
13
|
+
"card",
|
|
14
|
+
"credit_card",
|
|
15
|
+
"check",
|
|
16
|
+
"cash",
|
|
17
|
+
"one_time_card",
|
|
18
|
+
"letter_of_credit",
|
|
19
|
+
"other",
|
|
20
|
+
])
|
|
21
|
+
.describe("Payment type"),
|
|
22
|
+
date: z.string().describe("Payment date"),
|
|
23
|
+
currency_id: z.number().int().describe("Currency ID"),
|
|
24
|
+
amount: z.number().describe("Total amount (must match sum of invoice amounts)"),
|
|
25
|
+
invoices: z
|
|
26
|
+
.array(z.object({
|
|
27
|
+
invoice_id: z.number().int().describe("Invoice ID"),
|
|
28
|
+
gross_amount: z.number().describe("Amount for this invoice"),
|
|
29
|
+
}))
|
|
30
|
+
.describe("Invoices to pay"),
|
|
31
|
+
},
|
|
32
|
+
}, withErrorHandling(async (args) => {
|
|
33
|
+
const { invoices, ...paymentData } = args;
|
|
34
|
+
const body = {
|
|
35
|
+
npayment: {
|
|
36
|
+
...paymentData,
|
|
37
|
+
npayment_invoices_attributes: invoices,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const payment = await apiClient.post(apiClient.buildPath("/npayments"), body);
|
|
41
|
+
return jsonResponse(payment);
|
|
42
|
+
}));
|
|
43
|
+
server.registerTool("create_po_payment", {
|
|
44
|
+
description: "Create a payment for a purchase order",
|
|
45
|
+
inputSchema: {
|
|
46
|
+
purchase_order_id: z.number().int().positive().describe("Purchase Order ID"),
|
|
47
|
+
note: z.string().optional().describe("Payment note"),
|
|
48
|
+
item_payments: z
|
|
49
|
+
.array(z.object({
|
|
50
|
+
purchase_order_item_id: z.number().int().describe("PO item ID"),
|
|
51
|
+
amount: z.number().describe("Payment amount for this item"),
|
|
52
|
+
}))
|
|
53
|
+
.describe("Item-level payment amounts"),
|
|
54
|
+
},
|
|
55
|
+
}, withErrorHandling(async (args) => {
|
|
56
|
+
const body = {
|
|
57
|
+
payment: {
|
|
58
|
+
note: args.note,
|
|
59
|
+
purchase_order_item_payments_attributes: args.item_payments,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const result = await apiClient.post(apiClient.buildPath(`/purchase_orders/${args.purchase_order_id}/payments`), body);
|
|
63
|
+
return jsonResponse(result);
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
export function registerProductTools(server, apiClient) {
|
|
4
|
+
server.registerTool("list_products", {
|
|
5
|
+
description: "List products with optional filters for supplier and archived status",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
supplier_id: z.number().int().optional().describe("Filter by supplier ID"),
|
|
8
|
+
archived: z.boolean().optional().describe("Filter by archived status"),
|
|
9
|
+
},
|
|
10
|
+
}, withErrorHandling(async (args) => {
|
|
11
|
+
const params = new URLSearchParams();
|
|
12
|
+
if (args.supplier_id)
|
|
13
|
+
params.set("supplier_id", String(args.supplier_id));
|
|
14
|
+
if (args.archived !== undefined)
|
|
15
|
+
params.set("archived", String(args.archived));
|
|
16
|
+
const query = params.toString();
|
|
17
|
+
const path = `${apiClient.buildPath("/products")}${query ? `?${query}` : ""}`;
|
|
18
|
+
const products = await apiClient.get(path);
|
|
19
|
+
return jsonResponse(products);
|
|
20
|
+
}));
|
|
21
|
+
server.registerTool("get_product", {
|
|
22
|
+
description: "Get a specific product by ID",
|
|
23
|
+
inputSchema: {
|
|
24
|
+
id: z.number().int().positive().describe("Product ID"),
|
|
25
|
+
},
|
|
26
|
+
}, withErrorHandling(async (args) => {
|
|
27
|
+
const product = await apiClient.get(apiClient.buildPath(`/products/${args.id}`));
|
|
28
|
+
return jsonResponse(product);
|
|
29
|
+
}));
|
|
30
|
+
server.registerTool("create_product", {
|
|
31
|
+
description: "Create a new product",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
description: z.string().describe("Product description (required)"),
|
|
34
|
+
sku: z.string().optional().describe("SKU code"),
|
|
35
|
+
unit_price: z.number().optional().describe("Unit price"),
|
|
36
|
+
supplier_id: z.number().int().optional().describe("Supplier ID"),
|
|
37
|
+
},
|
|
38
|
+
}, withErrorHandling(async (args) => {
|
|
39
|
+
const product = await apiClient.post(apiClient.buildPath("/products"), { product: args });
|
|
40
|
+
return jsonResponse(product);
|
|
41
|
+
}));
|
|
42
|
+
server.registerTool("update_product", {
|
|
43
|
+
description: "Update an existing product",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
id: z.number().int().positive().describe("Product ID"),
|
|
46
|
+
description: z.string().optional().describe("Product description"),
|
|
47
|
+
sku: z.string().optional().describe("SKU code"),
|
|
48
|
+
unit_price: z.number().optional().describe("Unit price"),
|
|
49
|
+
supplier_id: z.number().int().optional().describe("Supplier ID"),
|
|
50
|
+
archived: z.boolean().optional().describe("Archive status"),
|
|
51
|
+
},
|
|
52
|
+
}, withErrorHandling(async (args) => {
|
|
53
|
+
const { id, ...data } = args;
|
|
54
|
+
const product = await apiClient.put(apiClient.buildPath(`/products/${id}`), { product: data });
|
|
55
|
+
return jsonResponse(product);
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, textResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
const lineItemSchema = z.object({
|
|
4
|
+
description: z.string().describe("Item description"),
|
|
5
|
+
quantity: z.number().describe("Quantity"),
|
|
6
|
+
unit_price: z.number().describe("Unit price"),
|
|
7
|
+
budget_id: z.number().int().optional().describe("Budget ID"),
|
|
8
|
+
vat: z.number().optional().describe("VAT amount"),
|
|
9
|
+
item_number: z.string().optional().describe("Item number"),
|
|
10
|
+
sequence_no: z.number().int().optional().describe("Sequence number"),
|
|
11
|
+
tax_rate_id: z.number().int().optional().describe("Tax rate ID"),
|
|
12
|
+
});
|
|
13
|
+
export function registerPurchaseOrderTools(server, apiClient) {
|
|
14
|
+
server.registerTool("list_purchase_orders", {
|
|
15
|
+
description: "List purchase orders with pagination and search",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
orders_page: z.number().int().positive().optional().describe("Page number (default: 1)"),
|
|
18
|
+
search: z.string().optional().describe("Search term"),
|
|
19
|
+
requests: z.boolean().optional().describe("Set to true to list pending approval requests"),
|
|
20
|
+
sort: z.string().optional().describe("Sort field"),
|
|
21
|
+
direction: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
|
|
22
|
+
},
|
|
23
|
+
}, withErrorHandling(async (args) => {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (args.orders_page)
|
|
26
|
+
params.set("orders_page", String(args.orders_page));
|
|
27
|
+
if (args.search)
|
|
28
|
+
params.set("search", args.search);
|
|
29
|
+
if (args.requests)
|
|
30
|
+
params.set("requests", "true");
|
|
31
|
+
if (args.sort)
|
|
32
|
+
params.set("sort", args.sort);
|
|
33
|
+
if (args.direction)
|
|
34
|
+
params.set("direction", args.direction);
|
|
35
|
+
const query = params.toString();
|
|
36
|
+
const path = `${apiClient.buildPath("/purchase_orders")}${query ? `?${query}` : ""}`;
|
|
37
|
+
const result = await apiClient.get(path);
|
|
38
|
+
return jsonResponse(result);
|
|
39
|
+
}));
|
|
40
|
+
server.registerTool("get_purchase_order", {
|
|
41
|
+
description: "Get purchase order details including line items, comments, approvals, and status flags",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
44
|
+
},
|
|
45
|
+
}, withErrorHandling(async (args) => {
|
|
46
|
+
const po = await apiClient.get(apiClient.buildPath(`/purchase_orders/${args.id}`));
|
|
47
|
+
return jsonResponse(po);
|
|
48
|
+
}));
|
|
49
|
+
server.registerTool("create_purchase_order", {
|
|
50
|
+
description: "Create a new purchase order. Use commit='Send' to submit or 'Draft' to save as draft",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
commit: z.enum(["Send", "Draft"]).describe("'Send' to submit for approval, 'Draft' to save as draft"),
|
|
53
|
+
department_id: z.number().int().optional().describe("Department ID"),
|
|
54
|
+
creator_id: z.number().int().describe("Creator user ID"),
|
|
55
|
+
supplier_id: z.number().int().optional().describe("Supplier ID"),
|
|
56
|
+
supplier_name: z.string().optional().describe("Supplier name (for new suppliers)"),
|
|
57
|
+
currency_id: z.number().int().describe("Currency ID"),
|
|
58
|
+
notes: z.string().optional().describe("PO notes"),
|
|
59
|
+
line_items: z.array(lineItemSchema).min(1).describe("Line items (at least one required)"),
|
|
60
|
+
approver_list: z.array(z.number().int()).optional().describe("Approver IDs"),
|
|
61
|
+
},
|
|
62
|
+
}, withErrorHandling(async (args) => {
|
|
63
|
+
const { commit, line_items, approver_list, ...poData } = args;
|
|
64
|
+
const body = {
|
|
65
|
+
commit,
|
|
66
|
+
purchase_order: {
|
|
67
|
+
...poData,
|
|
68
|
+
purchase_order_items_attributes: line_items,
|
|
69
|
+
...(approver_list ? { approver_list } : {}),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const po = await apiClient.post(apiClient.buildPath("/purchase_orders"), body);
|
|
73
|
+
return jsonResponse(po);
|
|
74
|
+
}));
|
|
75
|
+
server.registerTool("approve_purchase_order", {
|
|
76
|
+
description: "Approve a purchase order using the accept token from the approver request",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
79
|
+
token: z.string().describe("Accept token from the approver request"),
|
|
80
|
+
},
|
|
81
|
+
}, withErrorHandling(async (args) => {
|
|
82
|
+
const result = await apiClient.get(`${apiClient.buildPath(`/purchase_orders/${args.id}/approve`)}?token=${args.token}`);
|
|
83
|
+
return jsonResponse(result);
|
|
84
|
+
}));
|
|
85
|
+
server.registerTool("reject_purchase_order", {
|
|
86
|
+
description: "Reject a purchase order using the reject token from the approver request",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
89
|
+
token: z.string().describe("Reject token from the approver request"),
|
|
90
|
+
},
|
|
91
|
+
}, withErrorHandling(async (args) => {
|
|
92
|
+
const result = await apiClient.get(`${apiClient.buildPath(`/purchase_orders/${args.id}/reject`)}?token=${args.token}`);
|
|
93
|
+
return jsonResponse(result);
|
|
94
|
+
}));
|
|
95
|
+
server.registerTool("override_and_approve_purchase_order", {
|
|
96
|
+
description: "Override and approve a purchase order (finance users only, no token required)",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
99
|
+
},
|
|
100
|
+
}, withErrorHandling(async (args) => {
|
|
101
|
+
const result = await apiClient.get(apiClient.buildPath(`/purchase_orders/${args.id}/override_and_approve`));
|
|
102
|
+
return jsonResponse(result);
|
|
103
|
+
}));
|
|
104
|
+
server.registerTool("cancel_purchase_order", {
|
|
105
|
+
description: "Cancel a purchase order",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
108
|
+
},
|
|
109
|
+
}, withErrorHandling(async (args) => {
|
|
110
|
+
const result = await apiClient.post(apiClient.buildPath(`/purchase_orders/${args.id}/cancel`));
|
|
111
|
+
return jsonResponse(result);
|
|
112
|
+
}));
|
|
113
|
+
server.registerTool("archive_purchase_order", {
|
|
114
|
+
description: "Archive a purchase order",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
117
|
+
},
|
|
118
|
+
}, withErrorHandling(async (args) => {
|
|
119
|
+
const result = await apiClient.post(apiClient.buildPath(`/purchase_orders/${args.id}/archive`));
|
|
120
|
+
return jsonResponse(result);
|
|
121
|
+
}));
|
|
122
|
+
server.registerTool("get_pending_request_count", {
|
|
123
|
+
description: "Get the count of pending approval requests",
|
|
124
|
+
inputSchema: {},
|
|
125
|
+
}, withErrorHandling(async () => {
|
|
126
|
+
const result = await apiClient.get(apiClient.buildPath("/purchase_orders/pending_request_count"));
|
|
127
|
+
return textResponse(`Pending requests: ${result.total_pending_request}`);
|
|
128
|
+
}));
|
|
129
|
+
server.registerTool("receive_purchase_order_items", {
|
|
130
|
+
description: "Mark items as received for a purchase order",
|
|
131
|
+
inputSchema: {
|
|
132
|
+
id: z.number().int().positive().describe("Purchase Order ID"),
|
|
133
|
+
items: z
|
|
134
|
+
.array(z.object({
|
|
135
|
+
id: z.number().int().describe("PO item ID"),
|
|
136
|
+
quantity: z.number().describe("Quantity received"),
|
|
137
|
+
}))
|
|
138
|
+
.describe("Items with received quantities"),
|
|
139
|
+
delivered_on: z.string().describe("Delivery date"),
|
|
140
|
+
notes: z.string().optional().describe("Delivery notes"),
|
|
141
|
+
},
|
|
142
|
+
}, withErrorHandling(async (args) => {
|
|
143
|
+
const result = await apiClient.post(apiClient.buildPath(`/purchase_orders/${args.id}/receiving_items`), {
|
|
144
|
+
purchase_order: {
|
|
145
|
+
items: args.items,
|
|
146
|
+
delivered_on: args.delivered_on,
|
|
147
|
+
notes: args.notes,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
return jsonResponse(result);
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
export function registerSupplementaryTools(server, apiClient) {
|
|
4
|
+
server.registerTool("list_chart_of_accounts", {
|
|
5
|
+
description: "List chart of accounts with optional search",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
search: z.string().optional().describe("Search term"),
|
|
8
|
+
},
|
|
9
|
+
}, withErrorHandling(async (args) => {
|
|
10
|
+
const params = new URLSearchParams();
|
|
11
|
+
if (args.search)
|
|
12
|
+
params.set("search", args.search);
|
|
13
|
+
const query = params.toString();
|
|
14
|
+
const path = `${apiClient.buildPath("/chart_of_accounts")}${query ? `?${query}` : ""}`;
|
|
15
|
+
const result = await apiClient.get(path);
|
|
16
|
+
return jsonResponse(result);
|
|
17
|
+
}));
|
|
18
|
+
server.registerTool("list_qbo_customers", {
|
|
19
|
+
description: "List QuickBooks customers with optional search",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
search: z.string().optional().describe("Search term"),
|
|
22
|
+
},
|
|
23
|
+
}, withErrorHandling(async (args) => {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (args.search)
|
|
26
|
+
params.set("search", args.search);
|
|
27
|
+
const query = params.toString();
|
|
28
|
+
const path = `${apiClient.buildPath("/qbo_customers")}${query ? `?${query}` : ""}`;
|
|
29
|
+
const result = await apiClient.get(path);
|
|
30
|
+
return jsonResponse(result);
|
|
31
|
+
}));
|
|
32
|
+
server.registerTool("list_qbo_classes", {
|
|
33
|
+
description: "List QuickBooks classes with optional search",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
search: z.string().optional().describe("Search term"),
|
|
36
|
+
},
|
|
37
|
+
}, withErrorHandling(async (args) => {
|
|
38
|
+
const params = new URLSearchParams();
|
|
39
|
+
if (args.search)
|
|
40
|
+
params.set("search", args.search);
|
|
41
|
+
const query = params.toString();
|
|
42
|
+
const path = `${apiClient.buildPath("/qbo_classes")}${query ? `?${query}` : ""}`;
|
|
43
|
+
const result = await apiClient.get(path);
|
|
44
|
+
return jsonResponse(result);
|
|
45
|
+
}));
|
|
46
|
+
server.registerTool("list_send_to_supplier_templates", {
|
|
47
|
+
description: "List email templates for sending POs to suppliers",
|
|
48
|
+
inputSchema: {},
|
|
49
|
+
}, withErrorHandling(async () => {
|
|
50
|
+
const templates = await apiClient.get(apiClient.buildPath("/send_to_supplier_templates"));
|
|
51
|
+
return jsonResponse(templates);
|
|
52
|
+
}));
|
|
53
|
+
server.registerTool("forward_purchase_order", {
|
|
54
|
+
description: "Forward a purchase order to supplier(s) via email",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
purchase_order_id: z.number().int().positive().describe("Purchase Order ID"),
|
|
57
|
+
emails: z.array(z.string().email()).describe("Recipient email addresses"),
|
|
58
|
+
cc: z.string().email().optional().describe("CC email address"),
|
|
59
|
+
notes: z.string().optional().describe("Email body / template text"),
|
|
60
|
+
email_subject: z.string().optional().describe("Email subject"),
|
|
61
|
+
template_label: z.string().optional().describe("Template label"),
|
|
62
|
+
save_template: z.boolean().optional().describe("Save as new template"),
|
|
63
|
+
is_default: z.boolean().optional().describe("Mark as default template"),
|
|
64
|
+
},
|
|
65
|
+
}, withErrorHandling(async (args) => {
|
|
66
|
+
const { purchase_order_id, ...data } = args;
|
|
67
|
+
const result = await apiClient.post(apiClient.buildPath(`/purchase_orders/${purchase_order_id}/forward`), data);
|
|
68
|
+
return jsonResponse(result);
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
export function registerSupplierTools(server, apiClient) {
|
|
4
|
+
server.registerTool("list_suppliers", {
|
|
5
|
+
description: "List suppliers with pagination and filters",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
page: z.number().int().positive().optional().describe("Page number"),
|
|
8
|
+
department_id: z.number().int().optional().describe("Filter by department ID"),
|
|
9
|
+
archived: z.boolean().optional().describe("Filter by archived status"),
|
|
10
|
+
top: z.number().int().positive().optional().describe("Limit to top N suppliers"),
|
|
11
|
+
},
|
|
12
|
+
}, withErrorHandling(async (args) => {
|
|
13
|
+
const params = new URLSearchParams();
|
|
14
|
+
if (args.page)
|
|
15
|
+
params.set("page", String(args.page));
|
|
16
|
+
if (args.department_id)
|
|
17
|
+
params.set("department_id", String(args.department_id));
|
|
18
|
+
if (args.archived !== undefined)
|
|
19
|
+
params.set("archived", String(args.archived));
|
|
20
|
+
if (args.top)
|
|
21
|
+
params.set("top", String(args.top));
|
|
22
|
+
const query = params.toString();
|
|
23
|
+
const path = args.top
|
|
24
|
+
? `${apiClient.buildPath("/suppliers/top")}?${query}`
|
|
25
|
+
: `${apiClient.buildPath("/suppliers")}${query ? `?${query}` : ""}`;
|
|
26
|
+
const result = await apiClient.get(path);
|
|
27
|
+
return jsonResponse(result);
|
|
28
|
+
}));
|
|
29
|
+
server.registerTool("get_supplier", {
|
|
30
|
+
description: "Get a specific supplier by ID",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
id: z.number().int().positive().describe("Supplier ID"),
|
|
33
|
+
},
|
|
34
|
+
}, withErrorHandling(async (args) => {
|
|
35
|
+
const supplier = await apiClient.get(apiClient.buildPath(`/suppliers/${args.id}`));
|
|
36
|
+
return jsonResponse(supplier);
|
|
37
|
+
}));
|
|
38
|
+
server.registerTool("create_supplier", {
|
|
39
|
+
description: "Create a new supplier (name must be unique)",
|
|
40
|
+
inputSchema: {
|
|
41
|
+
name: z.string().describe("Supplier name (must be unique)"),
|
|
42
|
+
email: z.string().email().optional().describe("Supplier email"),
|
|
43
|
+
address: z.string().optional().describe("Address"),
|
|
44
|
+
notes: z.string().optional().describe("Notes"),
|
|
45
|
+
payment_details: z.string().optional().describe("Payment details"),
|
|
46
|
+
phone_number: z.string().optional().describe("Phone number"),
|
|
47
|
+
tax_number: z.string().optional().describe("Tax number"),
|
|
48
|
+
contact_person: z.string().optional().describe("Contact person"),
|
|
49
|
+
department_ids: z.array(z.number().int()).optional().describe("Department IDs"),
|
|
50
|
+
},
|
|
51
|
+
}, withErrorHandling(async (args) => {
|
|
52
|
+
const { department_ids, ...supplierData } = args;
|
|
53
|
+
const body = { supplier: supplierData };
|
|
54
|
+
if (department_ids)
|
|
55
|
+
body.department_ids = department_ids;
|
|
56
|
+
const supplier = await apiClient.post(apiClient.buildPath("/suppliers"), body);
|
|
57
|
+
return jsonResponse(supplier);
|
|
58
|
+
}));
|
|
59
|
+
server.registerTool("update_supplier", {
|
|
60
|
+
description: "Update an existing supplier",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
id: z.number().int().positive().describe("Supplier ID"),
|
|
63
|
+
name: z.string().optional().describe("Supplier name"),
|
|
64
|
+
email: z.string().email().optional().describe("Email"),
|
|
65
|
+
address: z.string().optional().describe("Address"),
|
|
66
|
+
notes: z.string().optional().describe("Notes"),
|
|
67
|
+
payment_details: z.string().optional().describe("Payment details"),
|
|
68
|
+
phone_number: z.string().optional().describe("Phone number"),
|
|
69
|
+
archived: z.boolean().optional().describe("Archive status"),
|
|
70
|
+
tax_number: z.string().optional().describe("Tax number"),
|
|
71
|
+
contact_person: z.string().optional().describe("Contact person"),
|
|
72
|
+
},
|
|
73
|
+
}, withErrorHandling(async (args) => {
|
|
74
|
+
const { id, ...data } = args;
|
|
75
|
+
const supplier = await apiClient.put(apiClient.buildPath(`/suppliers/${id}`), {
|
|
76
|
+
supplier: data,
|
|
77
|
+
});
|
|
78
|
+
return jsonResponse(supplier);
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { jsonResponse, withErrorHandling } from "../tool-helpers.js";
|
|
3
|
+
export function registerTaxRateTools(server, apiClient) {
|
|
4
|
+
server.registerTool("list_tax_rates", {
|
|
5
|
+
description: "List all tax rates (single and combined types)",
|
|
6
|
+
inputSchema: {},
|
|
7
|
+
}, withErrorHandling(async () => {
|
|
8
|
+
const taxRates = await apiClient.get(apiClient.buildPath("/tax_rates"));
|
|
9
|
+
return jsonResponse(taxRates);
|
|
10
|
+
}));
|
|
11
|
+
server.registerTool("get_tax_rate", {
|
|
12
|
+
description: "Get a specific tax rate by ID",
|
|
13
|
+
inputSchema: {
|
|
14
|
+
id: z.number().int().positive().describe("Tax Rate ID"),
|
|
15
|
+
},
|
|
16
|
+
}, withErrorHandling(async (args) => {
|
|
17
|
+
const taxRate = await apiClient.get(apiClient.buildPath(`/tax_rates/${args.id}`));
|
|
18
|
+
return jsonResponse(taxRate);
|
|
19
|
+
}));
|
|
20
|
+
server.registerTool("create_tax_rate", {
|
|
21
|
+
description: "Create a new tax rate",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
name: z.string().describe("Tax rate name"),
|
|
24
|
+
value: z.number().describe("Tax rate value (percentage)"),
|
|
25
|
+
company_id: z.number().int().describe("Company ID"),
|
|
26
|
+
},
|
|
27
|
+
}, withErrorHandling(async (args) => {
|
|
28
|
+
const taxRate = await apiClient.post(apiClient.buildPath("/tax_rates"), { tax_rate: args });
|
|
29
|
+
return jsonResponse(taxRate);
|
|
30
|
+
}));
|
|
31
|
+
server.registerTool("update_tax_rate", {
|
|
32
|
+
description: "Update an existing tax rate",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
id: z.number().int().positive().describe("Tax Rate ID"),
|
|
35
|
+
name: z.string().optional().describe("Tax rate name"),
|
|
36
|
+
value: z.number().optional().describe("Tax rate value"),
|
|
37
|
+
archived: z.boolean().optional().describe("Archive status"),
|
|
38
|
+
},
|
|
39
|
+
}, withErrorHandling(async (args) => {
|
|
40
|
+
const { id, ...data } = args;
|
|
41
|
+
const taxRate = await apiClient.put(apiClient.buildPath(`/tax_rates/${id}`), { tax_rate: data });
|
|
42
|
+
return jsonResponse(taxRate);
|
|
43
|
+
}));
|
|
44
|
+
}
|