@codespar/mcp-alegra 0.1.0 → 0.2.1
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 +15 -5
- package/dist/index.d.ts +10 -0
- package/dist/index.js +242 -2
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -25,19 +25,29 @@ npx tsx packages/colombia/alegra/src/index.ts --http
|
|
|
25
25
|
| `MCP_HTTP` | No | Set to `"true"` to enable HTTP transport |
|
|
26
26
|
| `MCP_PORT` | No | HTTP port (default: 3000) |
|
|
27
27
|
|
|
28
|
-
## Tools
|
|
28
|
+
## Tools (20)
|
|
29
29
|
|
|
30
|
-
| Tool |
|
|
31
|
-
|
|
30
|
+
| Tool | Purpose |
|
|
31
|
+
|---|---|
|
|
32
32
|
| `create_invoice` | Create an invoice |
|
|
33
33
|
| `get_invoice` | Get invoice details by ID |
|
|
34
34
|
| `list_invoices` | List invoices |
|
|
35
|
-
| `
|
|
35
|
+
| `void_invoice` | Void/cancel an invoice |
|
|
36
|
+
| `get_invoice_pdf` | Get invoice PDF download URL |
|
|
37
|
+
| `send_invoice` | Email an invoice to one or more recipients |
|
|
38
|
+
| `create_contact` | Create a contact (customer or supplier) |
|
|
39
|
+
| `update_contact` | Update an existing contact |
|
|
40
|
+
| `delete_contact` | Delete a contact |
|
|
36
41
|
| `list_contacts` | List contacts |
|
|
37
|
-
| `create_item` | Create a product
|
|
42
|
+
| `create_item` | Create a product or service item |
|
|
43
|
+
| `update_item` | Update an existing item |
|
|
38
44
|
| `list_items` | List products and services |
|
|
39
45
|
| `list_payments` | List payments |
|
|
46
|
+
| `get_payment` | Get payment by ID |
|
|
40
47
|
| `create_payment` | Record a payment |
|
|
48
|
+
| `void_payment` | Void/annul a payment |
|
|
49
|
+
| `list_categories` | List item categories (chart of accounts) |
|
|
50
|
+
| `list_bank_accounts` | List bank accounts |
|
|
41
51
|
| `get_company` | Get company profile and settings |
|
|
42
52
|
|
|
43
53
|
## Auth
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,22 @@
|
|
|
6
6
|
* - create_invoice: Create an invoice
|
|
7
7
|
* - get_invoice: Get invoice by ID
|
|
8
8
|
* - list_invoices: List invoices
|
|
9
|
+
* - void_invoice: Void/cancel an invoice
|
|
10
|
+
* - get_invoice_pdf: Get invoice PDF URL
|
|
11
|
+
* - send_invoice: Email an invoice to a contact
|
|
9
12
|
* - create_contact: Create a contact (customer/supplier)
|
|
13
|
+
* - update_contact: Update an existing contact
|
|
14
|
+
* - delete_contact: Delete a contact
|
|
10
15
|
* - list_contacts: List contacts
|
|
11
16
|
* - create_item: Create a product/service item
|
|
17
|
+
* - update_item: Update an existing item
|
|
12
18
|
* - list_items: List items
|
|
13
19
|
* - list_payments: List payments
|
|
20
|
+
* - get_payment: Get payment by ID
|
|
14
21
|
* - create_payment: Record a payment
|
|
22
|
+
* - void_payment: Void a payment
|
|
23
|
+
* - list_categories: List item categories (chart of accounts)
|
|
24
|
+
* - list_bank_accounts: List bank accounts
|
|
15
25
|
* - get_company: Get company profile information
|
|
16
26
|
*
|
|
17
27
|
* Environment:
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,22 @@
|
|
|
6
6
|
* - create_invoice: Create an invoice
|
|
7
7
|
* - get_invoice: Get invoice by ID
|
|
8
8
|
* - list_invoices: List invoices
|
|
9
|
+
* - void_invoice: Void/cancel an invoice
|
|
10
|
+
* - get_invoice_pdf: Get invoice PDF URL
|
|
11
|
+
* - send_invoice: Email an invoice to a contact
|
|
9
12
|
* - create_contact: Create a contact (customer/supplier)
|
|
13
|
+
* - update_contact: Update an existing contact
|
|
14
|
+
* - delete_contact: Delete a contact
|
|
10
15
|
* - list_contacts: List contacts
|
|
11
16
|
* - create_item: Create a product/service item
|
|
17
|
+
* - update_item: Update an existing item
|
|
12
18
|
* - list_items: List items
|
|
13
19
|
* - list_payments: List payments
|
|
20
|
+
* - get_payment: Get payment by ID
|
|
14
21
|
* - create_payment: Record a payment
|
|
22
|
+
* - void_payment: Void a payment
|
|
23
|
+
* - list_categories: List item categories (chart of accounts)
|
|
24
|
+
* - list_bank_accounts: List bank accounts
|
|
15
25
|
* - get_company: Get company profile information
|
|
16
26
|
*
|
|
17
27
|
* Environment:
|
|
@@ -43,7 +53,7 @@ async function alegraRequest(method, path, body) {
|
|
|
43
53
|
}
|
|
44
54
|
return res.json();
|
|
45
55
|
}
|
|
46
|
-
const server = new Server({ name: "mcp-alegra", version: "0.1
|
|
56
|
+
const server = new Server({ name: "mcp-alegra", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
47
57
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
48
58
|
tools: [
|
|
49
59
|
{
|
|
@@ -113,6 +123,53 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
113
123
|
},
|
|
114
124
|
},
|
|
115
125
|
},
|
|
126
|
+
{
|
|
127
|
+
name: "void_invoice",
|
|
128
|
+
description: "Void/cancel an invoice",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
invoiceId: { type: "number", description: "Invoice ID" },
|
|
133
|
+
reason: { type: "string", description: "Reason for void/annulment" },
|
|
134
|
+
},
|
|
135
|
+
required: ["invoiceId"],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "get_invoice_pdf",
|
|
140
|
+
description: "Get invoice PDF download URL",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: "object",
|
|
143
|
+
properties: {
|
|
144
|
+
invoiceId: { type: "number", description: "Invoice ID" },
|
|
145
|
+
},
|
|
146
|
+
required: ["invoiceId"],
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "send_invoice",
|
|
151
|
+
description: "Email an invoice to one or more recipients",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
invoiceId: { type: "number", description: "Invoice ID" },
|
|
156
|
+
emails: {
|
|
157
|
+
type: "array",
|
|
158
|
+
description: "Recipient email addresses",
|
|
159
|
+
items: { type: "string" },
|
|
160
|
+
},
|
|
161
|
+
cc: {
|
|
162
|
+
type: "array",
|
|
163
|
+
description: "CC recipients",
|
|
164
|
+
items: { type: "string" },
|
|
165
|
+
},
|
|
166
|
+
subject: { type: "string", description: "Email subject" },
|
|
167
|
+
body: { type: "string", description: "Email body/message" },
|
|
168
|
+
send_copy_to_user: { type: "boolean", description: "Send a copy to the account user" },
|
|
169
|
+
},
|
|
170
|
+
required: ["invoiceId", "emails"],
|
|
171
|
+
},
|
|
172
|
+
},
|
|
116
173
|
{
|
|
117
174
|
name: "create_contact",
|
|
118
175
|
description: "Create a contact (customer or supplier)",
|
|
@@ -132,6 +189,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
132
189
|
required: ["name"],
|
|
133
190
|
},
|
|
134
191
|
},
|
|
192
|
+
{
|
|
193
|
+
name: "update_contact",
|
|
194
|
+
description: "Update an existing contact",
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: "object",
|
|
197
|
+
properties: {
|
|
198
|
+
contactId: { type: "number", description: "Contact ID" },
|
|
199
|
+
name: { type: "string", description: "Contact name" },
|
|
200
|
+
identification: { type: "string", description: "Tax ID" },
|
|
201
|
+
email: { type: "string", description: "Email address" },
|
|
202
|
+
phone_primary: { type: "string", description: "Primary phone" },
|
|
203
|
+
address: { type: "string", description: "Address" },
|
|
204
|
+
city: { type: "string", description: "City" },
|
|
205
|
+
department: { type: "string", description: "Department/state" },
|
|
206
|
+
type: { type: "string", description: "Contact type (client, provider)" },
|
|
207
|
+
regime: { type: "string", description: "Tax regime" },
|
|
208
|
+
},
|
|
209
|
+
required: ["contactId"],
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: "delete_contact",
|
|
214
|
+
description: "Delete a contact",
|
|
215
|
+
inputSchema: {
|
|
216
|
+
type: "object",
|
|
217
|
+
properties: {
|
|
218
|
+
contactId: { type: "number", description: "Contact ID" },
|
|
219
|
+
},
|
|
220
|
+
required: ["contactId"],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
135
223
|
{
|
|
136
224
|
name: "list_contacts",
|
|
137
225
|
description: "List contacts",
|
|
@@ -175,6 +263,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
175
263
|
required: ["name", "price"],
|
|
176
264
|
},
|
|
177
265
|
},
|
|
266
|
+
{
|
|
267
|
+
name: "update_item",
|
|
268
|
+
description: "Update an existing item",
|
|
269
|
+
inputSchema: {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
itemId: { type: "number", description: "Item ID" },
|
|
273
|
+
name: { type: "string", description: "Item name" },
|
|
274
|
+
description: { type: "string", description: "Description" },
|
|
275
|
+
reference: { type: "string", description: "Reference/SKU code" },
|
|
276
|
+
price: { type: "number", description: "Price" },
|
|
277
|
+
category: { type: "number", description: "Category ID" },
|
|
278
|
+
tax: {
|
|
279
|
+
type: "array",
|
|
280
|
+
description: "Tax IDs",
|
|
281
|
+
items: { type: "number" },
|
|
282
|
+
},
|
|
283
|
+
type: { type: "string", description: "Item type (product, service, kit)" },
|
|
284
|
+
},
|
|
285
|
+
required: ["itemId"],
|
|
286
|
+
},
|
|
287
|
+
},
|
|
178
288
|
{
|
|
179
289
|
name: "list_items",
|
|
180
290
|
description: "List products and services",
|
|
@@ -201,6 +311,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
201
311
|
},
|
|
202
312
|
},
|
|
203
313
|
},
|
|
314
|
+
{
|
|
315
|
+
name: "get_payment",
|
|
316
|
+
description: "Get payment by ID",
|
|
317
|
+
inputSchema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: { paymentId: { type: "number", description: "Payment ID" } },
|
|
320
|
+
required: ["paymentId"],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
204
323
|
{
|
|
205
324
|
name: "create_payment",
|
|
206
325
|
description: "Record a payment",
|
|
@@ -229,6 +348,41 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
229
348
|
required: ["date", "amount"],
|
|
230
349
|
},
|
|
231
350
|
},
|
|
351
|
+
{
|
|
352
|
+
name: "void_payment",
|
|
353
|
+
description: "Void/annul a payment",
|
|
354
|
+
inputSchema: {
|
|
355
|
+
type: "object",
|
|
356
|
+
properties: {
|
|
357
|
+
paymentId: { type: "number", description: "Payment ID" },
|
|
358
|
+
},
|
|
359
|
+
required: ["paymentId"],
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: "list_categories",
|
|
364
|
+
description: "List item categories (chart of accounts)",
|
|
365
|
+
inputSchema: {
|
|
366
|
+
type: "object",
|
|
367
|
+
properties: {
|
|
368
|
+
start: { type: "number", description: "Offset for pagination" },
|
|
369
|
+
limit: { type: "number", description: "Results limit" },
|
|
370
|
+
query: { type: "string", description: "Search query" },
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "list_bank_accounts",
|
|
376
|
+
description: "List bank accounts",
|
|
377
|
+
inputSchema: {
|
|
378
|
+
type: "object",
|
|
379
|
+
properties: {
|
|
380
|
+
start: { type: "number", description: "Offset for pagination" },
|
|
381
|
+
limit: { type: "number", description: "Results limit" },
|
|
382
|
+
type: { type: "string", description: "Filter by account type" },
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
},
|
|
232
386
|
{
|
|
233
387
|
name: "get_company",
|
|
234
388
|
description: "Get company profile and settings",
|
|
@@ -275,6 +429,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
275
429
|
params.set("client", String(args.client_id));
|
|
276
430
|
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/invoices?${params}`), null, 2) }] };
|
|
277
431
|
}
|
|
432
|
+
case "void_invoice": {
|
|
433
|
+
const payload = { status: "void" };
|
|
434
|
+
if (args?.reason)
|
|
435
|
+
payload.reason = args.reason;
|
|
436
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("PUT", `/invoices/${args?.invoiceId}/void`, payload), null, 2) }] };
|
|
437
|
+
}
|
|
438
|
+
case "get_invoice_pdf":
|
|
439
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/invoices/${args?.invoiceId}/pdf`), null, 2) }] };
|
|
440
|
+
case "send_invoice": {
|
|
441
|
+
const payload = { emails: args?.emails };
|
|
442
|
+
if (args?.cc)
|
|
443
|
+
payload.copyTo = args.cc;
|
|
444
|
+
if (args?.subject)
|
|
445
|
+
payload.subject = args.subject;
|
|
446
|
+
if (args?.body)
|
|
447
|
+
payload.body = args.body;
|
|
448
|
+
if (args?.send_copy_to_user !== undefined)
|
|
449
|
+
payload.sendCopyToUser = args.send_copy_to_user;
|
|
450
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", `/invoices/${args?.invoiceId}/email`, payload), null, 2) }] };
|
|
451
|
+
}
|
|
278
452
|
case "create_contact": {
|
|
279
453
|
const payload = { name: args?.name };
|
|
280
454
|
if (args?.identification)
|
|
@@ -295,6 +469,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
295
469
|
payload.regime = args.regime;
|
|
296
470
|
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/contacts", payload), null, 2) }] };
|
|
297
471
|
}
|
|
472
|
+
case "update_contact": {
|
|
473
|
+
const payload = {};
|
|
474
|
+
if (args?.name)
|
|
475
|
+
payload.name = args.name;
|
|
476
|
+
if (args?.identification)
|
|
477
|
+
payload.identification = args.identification;
|
|
478
|
+
if (args?.email)
|
|
479
|
+
payload.email = args.email;
|
|
480
|
+
if (args?.phone_primary)
|
|
481
|
+
payload.phonePrimary = args.phone_primary;
|
|
482
|
+
if (args?.address)
|
|
483
|
+
payload.address = { address: args.address };
|
|
484
|
+
if (args?.city)
|
|
485
|
+
payload.address = { ...payload.address, city: args.city };
|
|
486
|
+
if (args?.department)
|
|
487
|
+
payload.address = { ...payload.address, department: args.department };
|
|
488
|
+
if (args?.type)
|
|
489
|
+
payload.type = args.type;
|
|
490
|
+
if (args?.regime)
|
|
491
|
+
payload.regime = args.regime;
|
|
492
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("PUT", `/contacts/${args?.contactId}`, payload), null, 2) }] };
|
|
493
|
+
}
|
|
494
|
+
case "delete_contact":
|
|
495
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("DELETE", `/contacts/${args?.contactId}`), null, 2) }] };
|
|
298
496
|
case "list_contacts": {
|
|
299
497
|
const params = new URLSearchParams();
|
|
300
498
|
if (args?.start)
|
|
@@ -326,6 +524,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
326
524
|
payload.type = args.type;
|
|
327
525
|
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/items", payload), null, 2) }] };
|
|
328
526
|
}
|
|
527
|
+
case "update_item": {
|
|
528
|
+
const payload = {};
|
|
529
|
+
if (args?.name)
|
|
530
|
+
payload.name = args.name;
|
|
531
|
+
if (args?.description)
|
|
532
|
+
payload.description = args.description;
|
|
533
|
+
if (args?.reference)
|
|
534
|
+
payload.reference = args.reference;
|
|
535
|
+
if (args?.price !== undefined)
|
|
536
|
+
payload.price = [{ price: args.price }];
|
|
537
|
+
if (args?.category)
|
|
538
|
+
payload.category = { id: args.category };
|
|
539
|
+
if (args?.tax)
|
|
540
|
+
payload.tax = args.tax;
|
|
541
|
+
if (args?.type)
|
|
542
|
+
payload.type = args.type;
|
|
543
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("PUT", `/items/${args?.itemId}`, payload), null, 2) }] };
|
|
544
|
+
}
|
|
329
545
|
case "list_items": {
|
|
330
546
|
const params = new URLSearchParams();
|
|
331
547
|
if (args?.start)
|
|
@@ -350,6 +566,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
350
566
|
params.set("date_end", args.date_to);
|
|
351
567
|
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/payments?${params}`), null, 2) }] };
|
|
352
568
|
}
|
|
569
|
+
case "get_payment":
|
|
570
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/payments/${args?.paymentId}`), null, 2) }] };
|
|
353
571
|
case "create_payment": {
|
|
354
572
|
const payload = {
|
|
355
573
|
date: args?.date,
|
|
@@ -367,6 +585,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
367
585
|
payload.observations = args.observations;
|
|
368
586
|
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("POST", "/payments", payload), null, 2) }] };
|
|
369
587
|
}
|
|
588
|
+
case "void_payment":
|
|
589
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("PUT", `/payments/${args?.paymentId}/void`, { status: "void" }), null, 2) }] };
|
|
590
|
+
case "list_categories": {
|
|
591
|
+
const params = new URLSearchParams();
|
|
592
|
+
if (args?.start)
|
|
593
|
+
params.set("start", String(args.start));
|
|
594
|
+
if (args?.limit)
|
|
595
|
+
params.set("limit", String(args.limit));
|
|
596
|
+
if (args?.query)
|
|
597
|
+
params.set("query", args.query);
|
|
598
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/categories?${params}`), null, 2) }] };
|
|
599
|
+
}
|
|
600
|
+
case "list_bank_accounts": {
|
|
601
|
+
const params = new URLSearchParams();
|
|
602
|
+
if (args?.start)
|
|
603
|
+
params.set("start", String(args.start));
|
|
604
|
+
if (args?.limit)
|
|
605
|
+
params.set("limit", String(args.limit));
|
|
606
|
+
if (args?.type)
|
|
607
|
+
params.set("type", args.type);
|
|
608
|
+
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", `/bank-accounts?${params}`), null, 2) }] };
|
|
609
|
+
}
|
|
370
610
|
case "get_company":
|
|
371
611
|
return { content: [{ type: "text", text: JSON.stringify(await alegraRequest("GET", "/company"), null, 2) }] };
|
|
372
612
|
default:
|
|
@@ -395,7 +635,7 @@ async function main() {
|
|
|
395
635
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
396
636
|
t.onclose = () => { if (t.sessionId)
|
|
397
637
|
transports.delete(t.sessionId); };
|
|
398
|
-
const s = new Server({ name: "mcp-alegra", version: "0.1
|
|
638
|
+
const s = new Server({ name: "mcp-alegra", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
399
639
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
400
640
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
401
641
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-alegra",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "MCP server for Alegra
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server for Alegra \u2014 cloud accounting for LATAM (Colombian-founded)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"mcp-alegra": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
-
"files": [
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
11
13
|
"scripts": {
|
|
12
14
|
"build": "tsc",
|
|
13
15
|
"start": "node dist/index.js"
|
|
@@ -20,6 +22,13 @@
|
|
|
20
22
|
"typescript": "^5.8.0"
|
|
21
23
|
},
|
|
22
24
|
"license": "MIT",
|
|
23
|
-
"keywords": [
|
|
25
|
+
"keywords": [
|
|
26
|
+
"alegra",
|
|
27
|
+
"accounting",
|
|
28
|
+
"colombia",
|
|
29
|
+
"latam",
|
|
30
|
+
"invoicing",
|
|
31
|
+
"mcp"
|
|
32
|
+
],
|
|
24
33
|
"mcpName": "io.github.codespar/mcp-alegra"
|
|
25
34
|
}
|