Skip to content

Commit b7a6250

Browse files
committed
feat(mcp): add invoice tool
1 parent d5d2357 commit b7a6250

File tree

4 files changed

+191
-1
lines changed

4 files changed

+191
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@doitintl/doit-mcp-server",
3-
"version": "0.1.27",
3+
"version": "0.1.28",
44
"description": "DoiT official MCP Server",
55
"keywords": [
66
"doit",

src/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ import {
5353
createTicketTool,
5454
handleCreateTicketRequest,
5555
} from "./tools/tickets.js";
56+
import {
57+
listInvoicesTool,
58+
handleListInvoicesRequest,
59+
getInvoiceTool,
60+
handleGetInvoiceRequest,
61+
} from "./tools/invoices.js";
5662

5763
dotenv.config();
5864

@@ -87,6 +93,8 @@ function createServer() {
8793
dimensionTool,
8894
listTicketsTool,
8995
createTicketTool,
96+
listInvoicesTool,
97+
getInvoiceTool,
9098
],
9199
};
92100
});
@@ -172,6 +180,10 @@ function createServer() {
172180
return await handleListTicketsRequest(args, token);
173181
case "create_ticket":
174182
return await handleCreateTicketRequest(args, token);
183+
case "list_invoices":
184+
return await handleListInvoicesRequest(args, token);
185+
case "get_invoice":
186+
return await handleGetInvoiceRequest(args, token);
175187
default:
176188
return createErrorResponse(`Unknown tool: ${name}`);
177189
}

src/tools/invoices.ts

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { z } from "zod";
2+
import {
3+
createErrorResponse,
4+
createSuccessResponse,
5+
handleGeneralError,
6+
makeDoitRequest,
7+
formatDate,
8+
} from "../utils/util.js";
9+
10+
// Invoice interface matching the API response
11+
export interface Invoice {
12+
id: string;
13+
invoiceDate: number;
14+
platform: string;
15+
dueDate: number;
16+
status: string;
17+
totalAmount: number;
18+
balanceAmount: number;
19+
currency: string;
20+
url: string;
21+
}
22+
23+
export interface InvoicesResponse {
24+
invoices: Invoice[];
25+
pageToken?: string;
26+
rowCount: number;
27+
}
28+
29+
// Arguments schema for listing invoices
30+
export const ListInvoicesArgumentsSchema = z.object({
31+
pageToken: z
32+
.string()
33+
.optional()
34+
.describe(
35+
"Token for pagination. Use this to get the next page of results."
36+
),
37+
});
38+
39+
// Tool definition
40+
export const listInvoicesTool = {
41+
name: "list_invoices",
42+
description:
43+
"List all current and historical invoices for your organization from the DoiT API.",
44+
inputSchema: {
45+
type: "object",
46+
properties: {
47+
pageToken: {
48+
type: "string",
49+
description:
50+
"Token for pagination. Use this to get the next page of results.",
51+
},
52+
},
53+
},
54+
outputSchema: z.object({
55+
invoices: z.array(
56+
z.object({
57+
id: z.string(),
58+
invoiceDate: z.number(),
59+
platform: z.string(),
60+
dueDate: z.number(),
61+
status: z.string(),
62+
totalAmount: z.number(),
63+
balanceAmount: z.number(),
64+
currency: z.string(),
65+
url: z.string(),
66+
})
67+
),
68+
pageToken: z.string().optional(),
69+
rowCount: z.number(),
70+
}),
71+
};
72+
73+
// Handler for the tool
74+
export async function handleListInvoicesRequest(args: any, token: string) {
75+
try {
76+
const params = new URLSearchParams();
77+
if (args.pageToken) params.append("pageToken", args.pageToken);
78+
const url = `https://api.doit.com/billing/v1/invoices${
79+
params.toString() ? `?${params.toString()}` : ""
80+
}`;
81+
const data = await makeDoitRequest<InvoicesResponse>(url, token);
82+
if (!data) {
83+
return createErrorResponse("Failed to fetch invoices: No data returned");
84+
}
85+
// Format invoiceDate and dueDate for each invoice
86+
const formattedData = {
87+
...data,
88+
invoices: data.invoices.map((inv) => ({
89+
...inv,
90+
invoiceDateFormatted: formatDate(inv.invoiceDate),
91+
dueDateFormatted: formatDate(inv.dueDate),
92+
})),
93+
};
94+
return createSuccessResponse(JSON.stringify(formattedData));
95+
} catch (error) {
96+
return handleGeneralError(error, "listing invoices");
97+
}
98+
}
99+
100+
// Arguments schema for getting a single invoice
101+
export const GetInvoiceArgumentsSchema = z.object({
102+
id: z.string().describe("The ID of the invoice to retrieve"),
103+
});
104+
105+
// Tool definition for getting a single invoice
106+
export const getInvoiceTool = {
107+
name: "get_invoice",
108+
description:
109+
"Retrieve the full details of an invoice specified by the invoice number from the DoiT API.",
110+
inputSchema: {
111+
type: "object",
112+
properties: {
113+
id: {
114+
type: "string",
115+
description: "The ID of the invoice to retrieve.",
116+
},
117+
},
118+
required: ["id"],
119+
},
120+
outputSchema: z.object({
121+
id: z.string(),
122+
invoiceDate: z.number(),
123+
platform: z.string(),
124+
dueDate: z.number(),
125+
status: z.string(),
126+
totalAmount: z.number(),
127+
balanceAmount: z.number(),
128+
currency: z.string(),
129+
url: z.string(),
130+
lineItems: z.array(
131+
z.object({
132+
currency: z.string(),
133+
description: z.string(),
134+
details: z.string(),
135+
price: z.number(),
136+
qty: z.number(),
137+
type: z.string(),
138+
})
139+
),
140+
}),
141+
};
142+
143+
// Handler for the tool
144+
export async function handleGetInvoiceRequest(args: any, token: string) {
145+
try {
146+
if (!args.id) {
147+
return createErrorResponse("Invoice ID is required");
148+
}
149+
const url = `https://api.doit.com/billing/v1/invoices/${encodeURIComponent(
150+
args.id
151+
)}`;
152+
const data: Invoice | null = await makeDoitRequest<Invoice>(url, token, {
153+
appendParams: true,
154+
});
155+
if (!data) {
156+
return createErrorResponse("Failed to fetch invoice: No data returned");
157+
}
158+
// Format invoiceDate and dueDate
159+
const formattedData = {
160+
...data,
161+
invoiceDateFormatted: formatDate(data.invoiceDate),
162+
dueDateFormatted: formatDate(data.dueDate),
163+
};
164+
return createSuccessResponse(JSON.stringify(formattedData));
165+
} catch (error) {
166+
return handleGeneralError(error, "retrieving invoice");
167+
}
168+
}

src/utils/util.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,13 @@ export async function makeDoitRequest<T>(
140140
return null;
141141
}
142142
}
143+
144+
/**
145+
* Formats a timestamp (number) as a human-readable date string
146+
* @param timestamp The timestamp in milliseconds
147+
* @returns Formatted date string (e.g., '2024-04-27')
148+
*/
149+
export function formatDate(timestamp: number): string {
150+
if (!timestamp) return "";
151+
return new Date(timestamp).toISOString().split("T")[0];
152+
}

0 commit comments

Comments
 (0)