@codespar/mcp-alegra 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/README.md +53 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +423 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# MCP Alegra
|
|
2
|
+
|
|
3
|
+
MCP server for **Alegra** — cloud accounting platform for LATAM (Colombian-founded), supporting invoicing, contacts, inventory, and payments across CO, MX, AR, CL, and more.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Set your credentials
|
|
9
|
+
export ALEGRA_EMAIL="your-email@example.com"
|
|
10
|
+
export ALEGRA_API_TOKEN="your-api-token"
|
|
11
|
+
|
|
12
|
+
# Run via stdio
|
|
13
|
+
npx tsx packages/colombia/alegra/src/index.ts
|
|
14
|
+
|
|
15
|
+
# Run via HTTP
|
|
16
|
+
npx tsx packages/colombia/alegra/src/index.ts --http
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Environment Variables
|
|
20
|
+
|
|
21
|
+
| Variable | Required | Description |
|
|
22
|
+
|----------|----------|-------------|
|
|
23
|
+
| `ALEGRA_EMAIL` | Yes | Account email address |
|
|
24
|
+
| `ALEGRA_API_TOKEN` | Yes | API token from Alegra settings |
|
|
25
|
+
| `MCP_HTTP` | No | Set to `"true"` to enable HTTP transport |
|
|
26
|
+
| `MCP_PORT` | No | HTTP port (default: 3000) |
|
|
27
|
+
|
|
28
|
+
## Tools
|
|
29
|
+
|
|
30
|
+
| Tool | Description |
|
|
31
|
+
|------|-------------|
|
|
32
|
+
| `create_invoice` | Create an invoice |
|
|
33
|
+
| `get_invoice` | Get invoice details by ID |
|
|
34
|
+
| `list_invoices` | List invoices |
|
|
35
|
+
| `create_contact` | Create a contact (customer/supplier) |
|
|
36
|
+
| `list_contacts` | List contacts |
|
|
37
|
+
| `create_item` | Create a product/service item |
|
|
38
|
+
| `list_items` | List products and services |
|
|
39
|
+
| `list_payments` | List payments |
|
|
40
|
+
| `create_payment` | Record a payment |
|
|
41
|
+
| `get_company` | Get company profile and settings |
|
|
42
|
+
|
|
43
|
+
## Auth
|
|
44
|
+
|
|
45
|
+
Uses **Basic authentication** with email and API token. Obtain your API token from the Alegra settings page under "API" section.
|
|
46
|
+
|
|
47
|
+
## API Reference
|
|
48
|
+
|
|
49
|
+
- [Alegra API Docs](https://developer.alegra.com/)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
**Enterprise?** Contact us at [codespar.com](https://codespar.com) for dedicated support, custom integrations, and SLAs.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for Alegra — cloud accounting for LATAM (Colombian-founded).
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - create_invoice: Create an invoice
|
|
7
|
+
* - get_invoice: Get invoice by ID
|
|
8
|
+
* - list_invoices: List invoices
|
|
9
|
+
* - create_contact: Create a contact (customer/supplier)
|
|
10
|
+
* - list_contacts: List contacts
|
|
11
|
+
* - create_item: Create a product/service item
|
|
12
|
+
* - list_items: List items
|
|
13
|
+
* - list_payments: List payments
|
|
14
|
+
* - create_payment: Record a payment
|
|
15
|
+
* - get_company: Get company profile information
|
|
16
|
+
*
|
|
17
|
+
* Environment:
|
|
18
|
+
* ALEGRA_EMAIL — Account email
|
|
19
|
+
* ALEGRA_API_TOKEN — API token
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for Alegra — cloud accounting for LATAM (Colombian-founded).
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - create_invoice: Create an invoice
|
|
7
|
+
* - get_invoice: Get invoice by ID
|
|
8
|
+
* - list_invoices: List invoices
|
|
9
|
+
* - create_contact: Create a contact (customer/supplier)
|
|
10
|
+
* - list_contacts: List contacts
|
|
11
|
+
* - create_item: Create a product/service item
|
|
12
|
+
* - list_items: List items
|
|
13
|
+
* - list_payments: List payments
|
|
14
|
+
* - create_payment: Record a payment
|
|
15
|
+
* - get_company: Get company profile information
|
|
16
|
+
*
|
|
17
|
+
* Environment:
|
|
18
|
+
* ALEGRA_EMAIL — Account email
|
|
19
|
+
* ALEGRA_API_TOKEN — API token
|
|
20
|
+
*/
|
|
21
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
22
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
23
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
24
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
25
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
26
|
+
const EMAIL = process.env.ALEGRA_EMAIL || "";
|
|
27
|
+
const API_TOKEN = process.env.ALEGRA_API_TOKEN || "";
|
|
28
|
+
const BASE_URL = "https://api.alegra.com/api/v1";
|
|
29
|
+
async function alegraRequest(method, path, body) {
|
|
30
|
+
const headers = {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
"Accept": "application/json",
|
|
33
|
+
"Authorization": `Basic ${Buffer.from(EMAIL + ":" + API_TOKEN).toString("base64")}`,
|
|
34
|
+
};
|
|
35
|
+
const res = await fetch(`${BASE_URL}${path}`, {
|
|
36
|
+
method,
|
|
37
|
+
headers,
|
|
38
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
39
|
+
});
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
const err = await res.text();
|
|
42
|
+
throw new Error(`Alegra API ${res.status}: ${err}`);
|
|
43
|
+
}
|
|
44
|
+
return res.json();
|
|
45
|
+
}
|
|
46
|
+
const server = new Server({ name: "mcp-alegra", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
47
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
48
|
+
tools: [
|
|
49
|
+
{
|
|
50
|
+
name: "create_invoice",
|
|
51
|
+
description: "Create an invoice",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
date: { type: "string", description: "Invoice date (YYYY-MM-DD)" },
|
|
56
|
+
due_date: { type: "string", description: "Due date (YYYY-MM-DD)" },
|
|
57
|
+
client: { type: "number", description: "Client/contact ID" },
|
|
58
|
+
items: {
|
|
59
|
+
type: "array",
|
|
60
|
+
description: "Invoice items",
|
|
61
|
+
items: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
id: { type: "number", description: "Item ID" },
|
|
65
|
+
description: { type: "string", description: "Description" },
|
|
66
|
+
quantity: { type: "number", description: "Quantity" },
|
|
67
|
+
price: { type: "number", description: "Unit price" },
|
|
68
|
+
discount: { type: "number", description: "Discount percentage" },
|
|
69
|
+
tax: {
|
|
70
|
+
type: "array",
|
|
71
|
+
description: "Tax IDs",
|
|
72
|
+
items: { type: "number" },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ["quantity", "price"],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
observations: { type: "string", description: "Notes/observations" },
|
|
79
|
+
currency: { type: "string", description: "Currency code (COP, USD, MXN)" },
|
|
80
|
+
payment_method: { type: "string", description: "Payment method" },
|
|
81
|
+
stamp: {
|
|
82
|
+
type: "object",
|
|
83
|
+
description: "E-invoicing stamp (for DIAN)",
|
|
84
|
+
properties: {
|
|
85
|
+
generate_stamp: { type: "boolean", description: "Generate electronic stamp" },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ["date", "due_date", "client", "items"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "get_invoice",
|
|
94
|
+
description: "Get invoice details by ID",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: { invoiceId: { type: "number", description: "Invoice ID" } },
|
|
98
|
+
required: ["invoiceId"],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "list_invoices",
|
|
103
|
+
description: "List invoices",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
start: { type: "number", description: "Offset for pagination" },
|
|
108
|
+
limit: { type: "number", description: "Results limit (max 30)" },
|
|
109
|
+
date_from: { type: "string", description: "Start date (YYYY-MM-DD)" },
|
|
110
|
+
date_to: { type: "string", description: "End date (YYYY-MM-DD)" },
|
|
111
|
+
status: { type: "string", description: "Filter by status (open, closed, void)" },
|
|
112
|
+
client_id: { type: "number", description: "Filter by client ID" },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "create_contact",
|
|
118
|
+
description: "Create a contact (customer or supplier)",
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
name: { type: "string", description: "Contact name" },
|
|
123
|
+
identification: { type: "string", description: "Tax ID (NIT, CC, CE, RUT)" },
|
|
124
|
+
email: { type: "string", description: "Email address" },
|
|
125
|
+
phone_primary: { type: "string", description: "Primary phone" },
|
|
126
|
+
address: { type: "string", description: "Address" },
|
|
127
|
+
city: { type: "string", description: "City" },
|
|
128
|
+
department: { type: "string", description: "Department/state" },
|
|
129
|
+
type: { type: "string", description: "Contact type (client, provider)" },
|
|
130
|
+
regime: { type: "string", description: "Tax regime (simplified, common)" },
|
|
131
|
+
},
|
|
132
|
+
required: ["name"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "list_contacts",
|
|
137
|
+
description: "List contacts",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
start: { type: "number", description: "Offset for pagination" },
|
|
142
|
+
limit: { type: "number", description: "Results limit" },
|
|
143
|
+
type: { type: "string", description: "Filter by type (client, provider)" },
|
|
144
|
+
query: { type: "string", description: "Search query" },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "create_item",
|
|
150
|
+
description: "Create a product or service item",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
name: { type: "string", description: "Item name" },
|
|
155
|
+
description: { type: "string", description: "Description" },
|
|
156
|
+
reference: { type: "string", description: "Reference/SKU code" },
|
|
157
|
+
price: { type: "number", description: "Price" },
|
|
158
|
+
category: { type: "number", description: "Category ID" },
|
|
159
|
+
inventory: {
|
|
160
|
+
type: "object",
|
|
161
|
+
description: "Inventory settings",
|
|
162
|
+
properties: {
|
|
163
|
+
unit: { type: "string", description: "Unit of measure" },
|
|
164
|
+
initial_quantity: { type: "number", description: "Initial stock quantity" },
|
|
165
|
+
unit_cost: { type: "number", description: "Unit cost" },
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
tax: {
|
|
169
|
+
type: "array",
|
|
170
|
+
description: "Tax IDs",
|
|
171
|
+
items: { type: "number" },
|
|
172
|
+
},
|
|
173
|
+
type: { type: "string", description: "Item type (product, service, kit)" },
|
|
174
|
+
},
|
|
175
|
+
required: ["name", "price"],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "list_items",
|
|
180
|
+
description: "List products and services",
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: "object",
|
|
183
|
+
properties: {
|
|
184
|
+
start: { type: "number", description: "Offset for pagination" },
|
|
185
|
+
limit: { type: "number", description: "Results limit" },
|
|
186
|
+
type: { type: "string", description: "Filter by type (product, service)" },
|
|
187
|
+
query: { type: "string", description: "Search query" },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "list_payments",
|
|
193
|
+
description: "List payments",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: "object",
|
|
196
|
+
properties: {
|
|
197
|
+
start: { type: "number", description: "Offset for pagination" },
|
|
198
|
+
limit: { type: "number", description: "Results limit" },
|
|
199
|
+
date_from: { type: "string", description: "Start date (YYYY-MM-DD)" },
|
|
200
|
+
date_to: { type: "string", description: "End date (YYYY-MM-DD)" },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "create_payment",
|
|
206
|
+
description: "Record a payment",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
date: { type: "string", description: "Payment date (YYYY-MM-DD)" },
|
|
211
|
+
amount: { type: "number", description: "Payment amount" },
|
|
212
|
+
bank_account: { type: "number", description: "Bank account ID" },
|
|
213
|
+
payment_method: { type: "string", description: "Payment method" },
|
|
214
|
+
invoices: {
|
|
215
|
+
type: "array",
|
|
216
|
+
description: "Invoices to apply payment to",
|
|
217
|
+
items: {
|
|
218
|
+
type: "object",
|
|
219
|
+
properties: {
|
|
220
|
+
id: { type: "number", description: "Invoice ID" },
|
|
221
|
+
amount: { type: "number", description: "Amount applied" },
|
|
222
|
+
},
|
|
223
|
+
required: ["id", "amount"],
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
client: { type: "number", description: "Client ID" },
|
|
227
|
+
observations: { type: "string", description: "Notes" },
|
|
228
|
+
},
|
|
229
|
+
required: ["date", "amount"],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "get_company",
|
|
234
|
+
description: "Get company profile and settings",
|
|
235
|
+
inputSchema: { type: "object", properties: {} },
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
}));
|
|
239
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
240
|
+
const { name, arguments: args } = request.params;
|
|
241
|
+
try {
|
|
242
|
+
switch (name) {
|
|
243
|
+
case "create_invoice": {
|
|
244
|
+
const payload = {
|
|
245
|
+
date: args?.date,
|
|
246
|
+
dueDate: args?.due_date,
|
|
247
|
+
client: args?.client,
|
|
248
|
+
items: args?.items,
|
|
249
|
+
};
|
|
250
|
+
if (args?.observations)
|
|
251
|
+
payload.observations = args.observations;
|
|
252
|
+
if (args?.currency)
|
|
253
|
+
payload.currency = args.currency;
|
|
254
|
+
if (args?.payment_method)
|
|
255
|
+
payload.paymentMethod = args.payment_method;
|
|
256
|
+
if (args?.stamp)
|
|
257
|
+
payload.stamp = args.stamp;
|
|
258
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/invoices", payload), null, 2) }] };
|
|
259
|
+
}
|
|
260
|
+
case "get_invoice":
|
|
261
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/invoices/${args?.invoiceId}`), null, 2) }] };
|
|
262
|
+
case "list_invoices": {
|
|
263
|
+
const params = new URLSearchParams();
|
|
264
|
+
if (args?.start)
|
|
265
|
+
params.set("start", String(args.start));
|
|
266
|
+
if (args?.limit)
|
|
267
|
+
params.set("limit", String(args.limit));
|
|
268
|
+
if (args?.date_from)
|
|
269
|
+
params.set("date", args.date_from);
|
|
270
|
+
if (args?.date_to)
|
|
271
|
+
params.set("date_end", args.date_to);
|
|
272
|
+
if (args?.status)
|
|
273
|
+
params.set("status", args.status);
|
|
274
|
+
if (args?.client_id)
|
|
275
|
+
params.set("client", String(args.client_id));
|
|
276
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/invoices?${params}`), null, 2) }] };
|
|
277
|
+
}
|
|
278
|
+
case "create_contact": {
|
|
279
|
+
const payload = { name: args?.name };
|
|
280
|
+
if (args?.identification)
|
|
281
|
+
payload.identification = args.identification;
|
|
282
|
+
if (args?.email)
|
|
283
|
+
payload.email = args.email;
|
|
284
|
+
if (args?.phone_primary)
|
|
285
|
+
payload.phonePrimary = args.phone_primary;
|
|
286
|
+
if (args?.address)
|
|
287
|
+
payload.address = { address: args.address };
|
|
288
|
+
if (args?.city)
|
|
289
|
+
payload.address = { ...payload.address, city: args.city };
|
|
290
|
+
if (args?.department)
|
|
291
|
+
payload.address = { ...payload.address, department: args.department };
|
|
292
|
+
if (args?.type)
|
|
293
|
+
payload.type = args.type;
|
|
294
|
+
if (args?.regime)
|
|
295
|
+
payload.regime = args.regime;
|
|
296
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/contacts", payload), null, 2) }] };
|
|
297
|
+
}
|
|
298
|
+
case "list_contacts": {
|
|
299
|
+
const params = new URLSearchParams();
|
|
300
|
+
if (args?.start)
|
|
301
|
+
params.set("start", String(args.start));
|
|
302
|
+
if (args?.limit)
|
|
303
|
+
params.set("limit", String(args.limit));
|
|
304
|
+
if (args?.type)
|
|
305
|
+
params.set("type", args.type);
|
|
306
|
+
if (args?.query)
|
|
307
|
+
params.set("query", args.query);
|
|
308
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/contacts?${params}`), null, 2) }] };
|
|
309
|
+
}
|
|
310
|
+
case "create_item": {
|
|
311
|
+
const payload = {
|
|
312
|
+
name: args?.name,
|
|
313
|
+
price: [{ price: args?.price }],
|
|
314
|
+
};
|
|
315
|
+
if (args?.description)
|
|
316
|
+
payload.description = args.description;
|
|
317
|
+
if (args?.reference)
|
|
318
|
+
payload.reference = args.reference;
|
|
319
|
+
if (args?.category)
|
|
320
|
+
payload.category = { id: args.category };
|
|
321
|
+
if (args?.inventory)
|
|
322
|
+
payload.inventory = args.inventory;
|
|
323
|
+
if (args?.tax)
|
|
324
|
+
payload.tax = args.tax;
|
|
325
|
+
if (args?.type)
|
|
326
|
+
payload.type = args.type;
|
|
327
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/items", payload), null, 2) }] };
|
|
328
|
+
}
|
|
329
|
+
case "list_items": {
|
|
330
|
+
const params = new URLSearchParams();
|
|
331
|
+
if (args?.start)
|
|
332
|
+
params.set("start", String(args.start));
|
|
333
|
+
if (args?.limit)
|
|
334
|
+
params.set("limit", String(args.limit));
|
|
335
|
+
if (args?.type)
|
|
336
|
+
params.set("type", args.type);
|
|
337
|
+
if (args?.query)
|
|
338
|
+
params.set("query", args.query);
|
|
339
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/items?${params}`), null, 2) }] };
|
|
340
|
+
}
|
|
341
|
+
case "list_payments": {
|
|
342
|
+
const params = new URLSearchParams();
|
|
343
|
+
if (args?.start)
|
|
344
|
+
params.set("start", String(args.start));
|
|
345
|
+
if (args?.limit)
|
|
346
|
+
params.set("limit", String(args.limit));
|
|
347
|
+
if (args?.date_from)
|
|
348
|
+
params.set("date", args.date_from);
|
|
349
|
+
if (args?.date_to)
|
|
350
|
+
params.set("date_end", args.date_to);
|
|
351
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/payments?${params}`), null, 2) }] };
|
|
352
|
+
}
|
|
353
|
+
case "create_payment": {
|
|
354
|
+
const payload = {
|
|
355
|
+
date: args?.date,
|
|
356
|
+
amount: args?.amount,
|
|
357
|
+
};
|
|
358
|
+
if (args?.bank_account)
|
|
359
|
+
payload.bankAccount = args.bank_account;
|
|
360
|
+
if (args?.payment_method)
|
|
361
|
+
payload.paymentMethod = args.payment_method;
|
|
362
|
+
if (args?.invoices)
|
|
363
|
+
payload.invoices = args.invoices;
|
|
364
|
+
if (args?.client)
|
|
365
|
+
payload.client = args.client;
|
|
366
|
+
if (args?.observations)
|
|
367
|
+
payload.observations = args.observations;
|
|
368
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/payments", payload), null, 2) }] };
|
|
369
|
+
}
|
|
370
|
+
case "get_company":
|
|
371
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", "/company"), null, 2) }] };
|
|
372
|
+
default:
|
|
373
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
async function main() {
|
|
381
|
+
if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
|
|
382
|
+
const { default: express } = await import("express");
|
|
383
|
+
const { randomUUID } = await import("node:crypto");
|
|
384
|
+
const app = express();
|
|
385
|
+
app.use(express.json());
|
|
386
|
+
const transports = new Map();
|
|
387
|
+
app.get("/health", (_req, res) => res.json({ status: "ok", sessions: transports.size }));
|
|
388
|
+
app.post("/mcp", async (req, res) => {
|
|
389
|
+
const sid = req.headers["mcp-session-id"];
|
|
390
|
+
if (sid && transports.has(sid)) {
|
|
391
|
+
await transports.get(sid).handleRequest(req, res, req.body);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!sid && isInitializeRequest(req.body)) {
|
|
395
|
+
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
396
|
+
t.onclose = () => { if (t.sessionId)
|
|
397
|
+
transports.delete(t.sessionId); };
|
|
398
|
+
const s = new Server({ name: "mcp-alegra", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
399
|
+
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
400
|
+
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
401
|
+
await s.connect(t);
|
|
402
|
+
await t.handleRequest(req, res, req.body);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|
|
406
|
+
});
|
|
407
|
+
app.get("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
408
|
+
await transports.get(sid).handleRequest(req, res);
|
|
409
|
+
else
|
|
410
|
+
res.status(400).send("Invalid session"); });
|
|
411
|
+
app.delete("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
412
|
+
await transports.get(sid).handleRequest(req, res);
|
|
413
|
+
else
|
|
414
|
+
res.status(400).send("Invalid session"); });
|
|
415
|
+
const port = Number(process.env.MCP_PORT) || 3000;
|
|
416
|
+
app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
const transport = new StdioServerTransport();
|
|
420
|
+
await server.connect(transport);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codespar/mcp-alegra",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Alegra — cloud accounting for LATAM (Colombian-founded)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-alegra": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": ["dist"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^22.0.0",
|
|
20
|
+
"typescript": "^5.8.0"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"keywords": ["alegra", "accounting", "colombia", "latam", "invoicing", "mcp"],
|
|
24
|
+
"mcpName": "io.github.codespar/mcp-alegra"
|
|
25
|
+
}
|