billingengine-mcp 0.1.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/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/client.js +78 -0
- package/dist/index.js +166 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Timo Kleemann
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# BillingEngine MCP server
|
|
2
|
+
|
|
3
|
+
Create customers and invoices, look up open invoices and record payments in
|
|
4
|
+
[BillingEngine](https://www.billingengine.com) by talking to Claude, Cursor or any other
|
|
5
|
+
[MCP](https://modelcontextprotocol.io) client:
|
|
6
|
+
|
|
7
|
+
> "Create an invoice for Malermeister Müller over 100 euros."
|
|
8
|
+
>
|
|
9
|
+
> "Which invoices are overdue?"
|
|
10
|
+
>
|
|
11
|
+
> "Müller paid invoice 2610030028, record the payment."
|
|
12
|
+
|
|
13
|
+
BillingEngine is invoicing software for freelancers and small businesses with
|
|
14
|
+
[e-invoicing](https://www.billingengine.com/en/features) (XRechnung and ZUGFeRD / Factur-X),
|
|
15
|
+
DATEV export and a German and English interface. The server is a thin layer over the
|
|
16
|
+
[BillingEngine API](https://www.billingengine.com/en/api) and runs on your computer.
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Node.js 20 or newer
|
|
21
|
+
- A BillingEngine account on a paid plan. The API is not available in the Free plan.
|
|
22
|
+
- Your API key from **Settings > API** in BillingEngine
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
### Claude Desktop
|
|
27
|
+
|
|
28
|
+
Add this to `claude_desktop_config.json`:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"billingengine": {
|
|
34
|
+
"command": "npx",
|
|
35
|
+
"args": ["-y", "billingengine-mcp"],
|
|
36
|
+
"env": { "BILLINGENGINE_API_KEY": "your-api-key" }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Claude Code
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
claude mcp add billingengine --env BILLINGENGINE_API_KEY=your-api-key -- npx -y billingengine-mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Cursor and other clients
|
|
49
|
+
|
|
50
|
+
Use the same `command`, `args` and `env` as above in the MCP settings of your client.
|
|
51
|
+
|
|
52
|
+
## Tools
|
|
53
|
+
|
|
54
|
+
| Tool | What it does |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `find_customers` | Searches customers by company, name or email |
|
|
57
|
+
| `create_customer` | Creates a customer |
|
|
58
|
+
| `update_customer` | Changes a customer |
|
|
59
|
+
| `create_invoice` | Creates a **draft** invoice for a customer |
|
|
60
|
+
| `update_invoice` | Changes a draft invoice |
|
|
61
|
+
| `list_invoices` | Lists invoices, filtered by status, customer, number or date |
|
|
62
|
+
| `get_invoice` | Returns one invoice with items and payment status |
|
|
63
|
+
| `download_invoice` | Saves the PDF or the XML e-invoice to your Downloads folder |
|
|
64
|
+
| `record_payment` | Records a payment for sent invoices |
|
|
65
|
+
| `list_payments` | Lists payments, optionally for one invoice |
|
|
66
|
+
|
|
67
|
+
## What it does and does not do
|
|
68
|
+
|
|
69
|
+
- Invoices are created as **drafts**. Nothing is sent to your customers; you review and send
|
|
70
|
+
the invoice in BillingEngine. Every result contains a link to open the draft.
|
|
71
|
+
- Prices are net. Number, language, texts, VAT treatment and payment terms come from your
|
|
72
|
+
account settings, exactly as in the invoice form.
|
|
73
|
+
- **Nothing can be deleted** through this server.
|
|
74
|
+
- New invoices count towards the monthly invoice maximum of your plan.
|
|
75
|
+
- The API allows 60 requests per minute per key.
|
|
76
|
+
- Your key stays on your computer and is only sent to BillingEngine.
|
|
77
|
+
|
|
78
|
+
If a key is ever exposed, generate a new one under **Settings > API**. The old key stops working
|
|
79
|
+
immediately. The settings also show when the key was last used.
|
|
80
|
+
|
|
81
|
+
## Configuration
|
|
82
|
+
|
|
83
|
+
| Variable | Meaning |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `BILLINGENGINE_API_KEY` | Your API key (required) |
|
|
86
|
+
| `BILLINGENGINE_URL` | Base URL, defaults to `https://www.billingengine.com` |
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
npm install
|
|
92
|
+
npm run build
|
|
93
|
+
BILLINGENGINE_API_KEY=... node dist/index.js
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
MIT. BillingEngine itself is a commercial service; see [billingengine.com](https://www.billingengine.com).
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export class BillingEngineError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export class BillingEngineClient {
|
|
4
|
+
baseUrl;
|
|
5
|
+
apiKey;
|
|
6
|
+
constructor(baseUrl, apiKey) {
|
|
7
|
+
this.baseUrl = baseUrl;
|
|
8
|
+
this.apiKey = apiKey;
|
|
9
|
+
}
|
|
10
|
+
async get(path, query = {}) {
|
|
11
|
+
const response = await this.fetch("GET", path, query);
|
|
12
|
+
return response.json();
|
|
13
|
+
}
|
|
14
|
+
async getFile(path) {
|
|
15
|
+
const response = await this.fetch("GET", path);
|
|
16
|
+
const disposition = response.headers.get("content-disposition") ?? "";
|
|
17
|
+
const match = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(disposition);
|
|
18
|
+
return {
|
|
19
|
+
data: Buffer.from(await response.arrayBuffer()),
|
|
20
|
+
filename: match ? decodeURIComponent(match[1]) : path.split("/").pop(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async post(path, body) {
|
|
24
|
+
const response = await this.fetch("POST", path, {}, body);
|
|
25
|
+
return response.json();
|
|
26
|
+
}
|
|
27
|
+
async patch(path, body) {
|
|
28
|
+
const response = await this.fetch("PATCH", path, {}, body);
|
|
29
|
+
return response.json();
|
|
30
|
+
}
|
|
31
|
+
async fetch(method, path, query = {}, body) {
|
|
32
|
+
const url = new URL(`/api/v1${path}`, this.baseUrl);
|
|
33
|
+
for (const [key, value] of Object.entries(query)) {
|
|
34
|
+
if (value !== undefined)
|
|
35
|
+
url.searchParams.set(key, String(value));
|
|
36
|
+
}
|
|
37
|
+
let response;
|
|
38
|
+
try {
|
|
39
|
+
response = await fetch(url, {
|
|
40
|
+
method,
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
43
|
+
Accept: "application/json",
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
"User-Agent": "billingengine-mcp",
|
|
46
|
+
},
|
|
47
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new BillingEngineError(`BillingEngine could not be reached at ${this.baseUrl}: ${error.message}`);
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok)
|
|
54
|
+
throw new BillingEngineError(await describeFailure(response));
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function describeFailure(response) {
|
|
59
|
+
const text = await response.text();
|
|
60
|
+
let messages = [];
|
|
61
|
+
try {
|
|
62
|
+
const parsed = JSON.parse(text);
|
|
63
|
+
messages = parsed.errors ?? (parsed.error ? [parsed.error] : []);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Not JSON, e.g. a proxy error page.
|
|
67
|
+
}
|
|
68
|
+
switch (response.status) {
|
|
69
|
+
case 401:
|
|
70
|
+
return "The API key was rejected. Check BILLINGENGINE_API_KEY (Settings > API in BillingEngine).";
|
|
71
|
+
case 403:
|
|
72
|
+
return messages.join("; ") || "This API key is not allowed to do that. The API needs a paid plan.";
|
|
73
|
+
case 429:
|
|
74
|
+
return "Too many requests: the limit is 60 per minute. Wait a moment and try again.";
|
|
75
|
+
default:
|
|
76
|
+
return `BillingEngine answered ${response.status}: ${messages.join("; ") || text.slice(0, 200)}`;
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { BillingEngineClient, BillingEngineError } from "./client.js";
|
|
9
|
+
const apiKey = process.env.BILLINGENGINE_API_KEY;
|
|
10
|
+
if (!apiKey) {
|
|
11
|
+
console.error("BILLINGENGINE_API_KEY is not set. You find your key in BillingEngine under Settings > API.");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const baseUrl = process.env.BILLINGENGINE_URL ?? "https://www.billingengine.com";
|
|
15
|
+
const client = new BillingEngineClient(baseUrl, apiKey);
|
|
16
|
+
const INSTRUCTIONS = [
|
|
17
|
+
"BillingEngine is an invoicing application for freelancers and small businesses.",
|
|
18
|
+
"Look a customer up with find_customers before creating an invoice and never guess a customer id; ask which one is meant when several match.",
|
|
19
|
+
"Prices are net amounts, the tax rate and everything else not given come from the account's settings.",
|
|
20
|
+
"Invoices are created as drafts and are not sent: tell the user to review and send them in BillingEngine, using the app_url in the result.",
|
|
21
|
+
"Nothing can be deleted through this server.",
|
|
22
|
+
].join(" ");
|
|
23
|
+
const server = new McpServer({ name: "billingengine", version: "0.1.0" }, { instructions: INSTRUCTIONS });
|
|
24
|
+
const date = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Use YYYY-MM-DD");
|
|
25
|
+
const page = z.number().int().min(1).optional().describe("Page of the list, starting at 1");
|
|
26
|
+
const address = z.object({
|
|
27
|
+
line_1: z.string().describe("Street and number"),
|
|
28
|
+
line_2: z.string().optional(),
|
|
29
|
+
postal_code: z.string(),
|
|
30
|
+
city: z.string(),
|
|
31
|
+
region: z.string().optional(),
|
|
32
|
+
country: z.string().length(2).describe("ISO country code such as DE, AT or CH"),
|
|
33
|
+
});
|
|
34
|
+
const customerFields = {
|
|
35
|
+
company: z.string().optional(),
|
|
36
|
+
first_name: z.string().optional(),
|
|
37
|
+
last_name: z.string().optional(),
|
|
38
|
+
email: z.string().optional(),
|
|
39
|
+
phone: z.string().optional(),
|
|
40
|
+
vat_number: z.string().optional(),
|
|
41
|
+
buyer_reference: z.string().optional().describe("Leitweg-ID or buyer reference for e-invoices"),
|
|
42
|
+
};
|
|
43
|
+
const item = z.object({
|
|
44
|
+
title: z.string(),
|
|
45
|
+
price: z.number().describe("Net price per unit"),
|
|
46
|
+
description: z.string().optional(),
|
|
47
|
+
quantity: z.number().optional().describe("Defaults to 1"),
|
|
48
|
+
unit_code: z.string().optional().describe("UN/ECE unit code such as C62 (piece) or HUR (hour)"),
|
|
49
|
+
tax_rate: z.number().optional().describe("VAT rate in percent; defaults to the account's default tax"),
|
|
50
|
+
});
|
|
51
|
+
const invoiceFields = {
|
|
52
|
+
date: date.optional().describe("Invoice date, defaults to today"),
|
|
53
|
+
due_days: z.number().int().min(0).optional().describe("Days until payment is due"),
|
|
54
|
+
introduction: z.string().optional(),
|
|
55
|
+
signature: z.string().optional(),
|
|
56
|
+
service_period_start_date: date.optional(),
|
|
57
|
+
service_period_end_date: date.optional(),
|
|
58
|
+
};
|
|
59
|
+
const READ = { readOnlyHint: true, openWorldHint: false };
|
|
60
|
+
const WRITE = { readOnlyHint: false, destructiveHint: false, openWorldHint: false };
|
|
61
|
+
async function respond(action) {
|
|
62
|
+
try {
|
|
63
|
+
const value = await action();
|
|
64
|
+
return { content: [{ type: "text", text: typeof value === "string" ? value : JSON.stringify(value, null, 2) }] };
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const message = error instanceof BillingEngineError ? error.message : `Unexpected error: ${error.message}`;
|
|
68
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function withAppUrl(invoice) {
|
|
72
|
+
return { ...invoice, app_url: `${baseUrl}/invoices/${invoice.id}/edit` };
|
|
73
|
+
}
|
|
74
|
+
function withInvoiceLinks(result) {
|
|
75
|
+
const body = result;
|
|
76
|
+
if (body.data)
|
|
77
|
+
return { ...body, data: body.data.map(withAppUrl) };
|
|
78
|
+
return body.id === undefined ? body : withAppUrl(body);
|
|
79
|
+
}
|
|
80
|
+
server.registerTool("find_customers", {
|
|
81
|
+
title: "Find customers",
|
|
82
|
+
description: "Lists customers, optionally filtered by a search text that matches company, names and email.",
|
|
83
|
+
inputSchema: { query: z.string().optional(), page },
|
|
84
|
+
annotations: READ,
|
|
85
|
+
}, ({ query, page }) => respond(() => client.get("/customers", { query, page })));
|
|
86
|
+
server.registerTool("create_customer", {
|
|
87
|
+
title: "Create a customer",
|
|
88
|
+
description: "Creates a customer. Needs a company or a last name and a full address (line 1, postal code, city, country).",
|
|
89
|
+
inputSchema: { ...customerFields, address },
|
|
90
|
+
annotations: WRITE,
|
|
91
|
+
}, ({ address, ...fields }) => respond(() => client.post("/customers", { ...fields, address_attributes: address })));
|
|
92
|
+
server.registerTool("update_customer", {
|
|
93
|
+
title: "Update a customer",
|
|
94
|
+
description: "Changes the given fields of a customer; fields that are left out stay as they are.",
|
|
95
|
+
inputSchema: { id: z.number().int(), ...customerFields, address: address.partial().optional() },
|
|
96
|
+
annotations: { ...WRITE, idempotentHint: true },
|
|
97
|
+
}, ({ id, address, ...fields }) => respond(() => client.patch(`/customers/${id}`, { ...fields, ...(address && { address_attributes: address }) })));
|
|
98
|
+
server.registerTool("create_invoice", {
|
|
99
|
+
title: "Create a draft invoice",
|
|
100
|
+
description: "Creates a draft invoice for a customer. Number, language, texts and VAT treatment come from the account settings. " +
|
|
101
|
+
"The invoice is not sent; it counts towards the monthly invoice maximum of the plan.",
|
|
102
|
+
inputSchema: { customer_id: z.number().int(), items: z.array(item).min(1), ...invoiceFields },
|
|
103
|
+
annotations: WRITE,
|
|
104
|
+
}, (input) => respond(async () => withInvoiceLinks(await client.post("/invoices", input))));
|
|
105
|
+
server.registerTool("update_invoice", {
|
|
106
|
+
title: "Update a draft invoice",
|
|
107
|
+
description: "Changes a draft invoice. Passing items replaces all existing items, so send the complete list. Sent invoices cannot be changed.",
|
|
108
|
+
inputSchema: { id: z.number().int(), items: z.array(item).min(1).optional(), ...invoiceFields },
|
|
109
|
+
annotations: { ...WRITE, idempotentHint: true },
|
|
110
|
+
}, ({ id, ...changes }) => respond(async () => withInvoiceLinks(await client.patch(`/invoices/${id}`, changes))));
|
|
111
|
+
server.registerTool("list_invoices", {
|
|
112
|
+
title: "List invoices",
|
|
113
|
+
description: "Lists invoices, newest first. Status: draft, open (sent and unpaid), overdue (open and past due) or paid.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
status: z.enum(["draft", "open", "overdue", "paid"]).optional(),
|
|
116
|
+
customer_id: z.number().int().optional(),
|
|
117
|
+
number: z.string().optional(),
|
|
118
|
+
after: date.optional().describe("Invoice date from"),
|
|
119
|
+
before: date.optional().describe("Invoice date until"),
|
|
120
|
+
page,
|
|
121
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
122
|
+
},
|
|
123
|
+
annotations: READ,
|
|
124
|
+
}, (filters) => respond(async () => withInvoiceLinks(await client.get("/invoices", filters))));
|
|
125
|
+
server.registerTool("get_invoice", {
|
|
126
|
+
title: "Get an invoice",
|
|
127
|
+
description: "Returns one invoice with its items, amounts and payment status.",
|
|
128
|
+
inputSchema: { id: z.number().int() },
|
|
129
|
+
annotations: READ,
|
|
130
|
+
}, ({ id }) => respond(async () => withInvoiceLinks(await client.get(`/invoices/${id}`))));
|
|
131
|
+
server.registerTool("download_invoice", {
|
|
132
|
+
title: "Download an invoice file",
|
|
133
|
+
description: "Saves the PDF or the XML e-invoice of an invoice on this computer and returns the file path. " +
|
|
134
|
+
"Defaults to the Downloads folder.",
|
|
135
|
+
inputSchema: {
|
|
136
|
+
id: z.number().int(),
|
|
137
|
+
format: z.enum(["pdf", "xml"]).default("pdf"),
|
|
138
|
+
directory: z.string().optional().describe("Target folder, defaults to ~/Downloads"),
|
|
139
|
+
},
|
|
140
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
141
|
+
}, ({ id, format, directory }) => respond(async () => {
|
|
142
|
+
const file = await client.getFile(`/invoices/${id}.${format}`);
|
|
143
|
+
const folder = directory ?? path.join(homedir(), "Downloads");
|
|
144
|
+
await mkdir(folder, { recursive: true });
|
|
145
|
+
const target = path.join(folder, path.basename(file.filename));
|
|
146
|
+
await writeFile(target, file.data);
|
|
147
|
+
return `Saved to ${target}`;
|
|
148
|
+
}));
|
|
149
|
+
server.registerTool("record_payment", {
|
|
150
|
+
title: "Record a payment",
|
|
151
|
+
description: "Records a payment for one or more sent, open invoices of the same customer and tax rate. The amount is gross.",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
invoice_ids: z.array(z.number().int()).min(1),
|
|
154
|
+
amount: z.number().positive().describe("Gross amount received"),
|
|
155
|
+
payment_method: z.enum(["transfer", "cash"]).optional().describe("Defaults to transfer"),
|
|
156
|
+
date: date.optional().describe("Defaults to today"),
|
|
157
|
+
},
|
|
158
|
+
annotations: WRITE,
|
|
159
|
+
}, (input) => respond(() => client.post("/payments", input)));
|
|
160
|
+
server.registerTool("list_payments", {
|
|
161
|
+
title: "List payments",
|
|
162
|
+
description: "Lists recorded payments, newest first, optionally only those for one invoice.",
|
|
163
|
+
inputSchema: { invoice_id: z.number().int().optional(), page },
|
|
164
|
+
annotations: READ,
|
|
165
|
+
}, (filters) => respond(() => client.get("/payments", filters)));
|
|
166
|
+
await server.connect(new StdioServerTransport());
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "billingengine-mcp",
|
|
3
|
+
"mcpName": "com.billingengine/mcp",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "MCP server for BillingEngine: create customers and invoices, look up invoices and record payments from Claude, Cursor and other MCP clients.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"invoicing",
|
|
10
|
+
"invoice",
|
|
11
|
+
"e-invoice",
|
|
12
|
+
"billingengine",
|
|
13
|
+
"xrechnung",
|
|
14
|
+
"zugferd"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://www.billingengine.com/mcp",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/billingengine/billingengine-mcp.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/billingengine/billingengine-mcp/issues"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "Timo Kleemann",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"billingengine-mcp": "dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
40
|
+
"prepublishOnly": "npm run build"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
44
|
+
"zod": "^4.6.5"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^26.6.4",
|
|
48
|
+
"typescript": "^7.0.2"
|
|
49
|
+
}
|
|
50
|
+
}
|