@codespar/mcp-colppy 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 +51 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +302 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# MCP Colppy
|
|
2
|
+
|
|
3
|
+
MCP server for **Colppy** — Argentine cloud accounting platform with integrated AFIP electronic invoicing.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Set your credentials
|
|
9
|
+
export COLPPY_API_KEY="your-api-key"
|
|
10
|
+
export COLPPY_COMPANY_ID="your-company-id"
|
|
11
|
+
|
|
12
|
+
# Run via stdio
|
|
13
|
+
npx tsx packages/argentina/colppy/src/index.ts
|
|
14
|
+
|
|
15
|
+
# Run via HTTP
|
|
16
|
+
npx tsx packages/argentina/colppy/src/index.ts --http
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Environment Variables
|
|
20
|
+
|
|
21
|
+
| Variable | Required | Description |
|
|
22
|
+
|----------|----------|-------------|
|
|
23
|
+
| `COLPPY_API_KEY` | Yes | API key from Colppy |
|
|
24
|
+
| `COLPPY_COMPANY_ID` | Yes | Company identifier |
|
|
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
|
+
| `list_customers` | List customers |
|
|
33
|
+
| `create_customer` | Create a customer |
|
|
34
|
+
| `list_products` | List products and services |
|
|
35
|
+
| `create_invoice` | Create an invoice (integrates with AFIP) |
|
|
36
|
+
| `list_invoices` | List invoices |
|
|
37
|
+
| `get_balance` | Get account balance summary |
|
|
38
|
+
| `list_accounts` | List chart of accounts (plan de cuentas) |
|
|
39
|
+
| `create_payment` | Record a payment against an invoice |
|
|
40
|
+
|
|
41
|
+
## Auth
|
|
42
|
+
|
|
43
|
+
Uses **API key + session** authentication. The API key is sent with each request along with the company ID. Colppy uses a JSON-RPC style API where service and operation are specified in the request body.
|
|
44
|
+
|
|
45
|
+
## API Reference
|
|
46
|
+
|
|
47
|
+
- [Colppy API Documentation](https://www.colppy.com/api)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
**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,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for Colppy — Argentine cloud accounting + AFIP invoicing.
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - list_customers: List customers
|
|
7
|
+
* - create_customer: Create a customer
|
|
8
|
+
* - list_products: List products/services
|
|
9
|
+
* - create_invoice: Create an invoice (integrates with AFIP)
|
|
10
|
+
* - list_invoices: List invoices
|
|
11
|
+
* - get_balance: Get account balance
|
|
12
|
+
* - list_accounts: List chart of accounts
|
|
13
|
+
* - create_payment: Record a payment
|
|
14
|
+
*
|
|
15
|
+
* Environment:
|
|
16
|
+
* COLPPY_API_KEY — API key
|
|
17
|
+
* COLPPY_COMPANY_ID — Company identifier
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for Colppy — Argentine cloud accounting + AFIP invoicing.
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - list_customers: List customers
|
|
7
|
+
* - create_customer: Create a customer
|
|
8
|
+
* - list_products: List products/services
|
|
9
|
+
* - create_invoice: Create an invoice (integrates with AFIP)
|
|
10
|
+
* - list_invoices: List invoices
|
|
11
|
+
* - get_balance: Get account balance
|
|
12
|
+
* - list_accounts: List chart of accounts
|
|
13
|
+
* - create_payment: Record a payment
|
|
14
|
+
*
|
|
15
|
+
* Environment:
|
|
16
|
+
* COLPPY_API_KEY — API key
|
|
17
|
+
* COLPPY_COMPANY_ID — Company identifier
|
|
18
|
+
*/
|
|
19
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
20
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
21
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
22
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
23
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
24
|
+
const API_KEY = process.env.COLPPY_API_KEY || "";
|
|
25
|
+
const COMPANY_ID = process.env.COLPPY_COMPANY_ID || "";
|
|
26
|
+
const BASE_URL = "https://login.colppy.com/lib/frontera2";
|
|
27
|
+
async function colppyRequest(service, operation, params) {
|
|
28
|
+
const payload = {
|
|
29
|
+
service,
|
|
30
|
+
operation,
|
|
31
|
+
parameters: {
|
|
32
|
+
...params,
|
|
33
|
+
sespiKey: API_KEY,
|
|
34
|
+
idEmpresa: COMPANY_ID,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const res = await fetch(BASE_URL, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: { "Content-Type": "application/json" },
|
|
40
|
+
body: JSON.stringify(payload),
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
const err = await res.text();
|
|
44
|
+
throw new Error(`Colppy API ${res.status}: ${err}`);
|
|
45
|
+
}
|
|
46
|
+
return res.json();
|
|
47
|
+
}
|
|
48
|
+
const server = new Server({ name: "mcp-colppy", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
49
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
50
|
+
tools: [
|
|
51
|
+
{
|
|
52
|
+
name: "list_customers",
|
|
53
|
+
description: "List customers",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
filter: { type: "string", description: "Search filter (name or tax ID)" },
|
|
58
|
+
offset: { type: "number", description: "Pagination offset" },
|
|
59
|
+
limit: { type: "number", description: "Results limit" },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "create_customer",
|
|
65
|
+
description: "Create a customer",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
name: { type: "string", description: "Customer name or business name" },
|
|
70
|
+
tax_id: { type: "string", description: "CUIT/CUIL/DNI number" },
|
|
71
|
+
tax_id_type: { type: "string", description: "Document type (CUIT, CUIL, DNI)" },
|
|
72
|
+
tax_category: { type: "string", description: "Tax category (RI, Monotributo, Exento, ConsumidorFinal)" },
|
|
73
|
+
email: { type: "string", description: "Email address" },
|
|
74
|
+
phone: { type: "string", description: "Phone number" },
|
|
75
|
+
address: { type: "string", description: "Street address" },
|
|
76
|
+
},
|
|
77
|
+
required: ["name", "tax_id"],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "list_products",
|
|
82
|
+
description: "List products and services",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
filter: { type: "string", description: "Search filter" },
|
|
87
|
+
offset: { type: "number", description: "Pagination offset" },
|
|
88
|
+
limit: { type: "number", description: "Results limit" },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "create_invoice",
|
|
94
|
+
description: "Create an invoice (integrates with AFIP for electronic invoicing)",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
customer_id: { type: "string", description: "Customer ID" },
|
|
99
|
+
invoice_type: { type: "string", description: "Invoice type (A, B, C)" },
|
|
100
|
+
point_of_sale: { type: "number", description: "Punto de venta" },
|
|
101
|
+
items: {
|
|
102
|
+
type: "array",
|
|
103
|
+
description: "Invoice items",
|
|
104
|
+
items: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
product_id: { type: "string", description: "Product ID" },
|
|
108
|
+
description: { type: "string", description: "Description" },
|
|
109
|
+
quantity: { type: "number", description: "Quantity" },
|
|
110
|
+
unit_price: { type: "number", description: "Unit price" },
|
|
111
|
+
iva_rate: { type: "number", description: "IVA rate (21, 10.5, 27, 0)" },
|
|
112
|
+
},
|
|
113
|
+
required: ["description", "quantity", "unit_price"],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
currency: { type: "string", description: "Currency (ARS, USD)" },
|
|
117
|
+
},
|
|
118
|
+
required: ["customer_id", "invoice_type", "items"],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "list_invoices",
|
|
123
|
+
description: "List invoices",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
date_from: { type: "string", description: "Start date (YYYY-MM-DD)" },
|
|
128
|
+
date_to: { type: "string", description: "End date (YYYY-MM-DD)" },
|
|
129
|
+
customer_id: { type: "string", description: "Filter by customer" },
|
|
130
|
+
status: { type: "string", description: "Filter by status" },
|
|
131
|
+
offset: { type: "number", description: "Pagination offset" },
|
|
132
|
+
limit: { type: "number", description: "Results limit" },
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "get_balance",
|
|
138
|
+
description: "Get account balance summary",
|
|
139
|
+
inputSchema: {
|
|
140
|
+
type: "object",
|
|
141
|
+
properties: {
|
|
142
|
+
date: { type: "string", description: "Balance date (YYYY-MM-DD)" },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "list_accounts",
|
|
148
|
+
description: "List chart of accounts (plan de cuentas)",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
filter: { type: "string", description: "Search filter" },
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: "create_payment",
|
|
158
|
+
description: "Record a payment against an invoice",
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
invoice_id: { type: "string", description: "Invoice ID" },
|
|
163
|
+
amount: { type: "number", description: "Payment amount" },
|
|
164
|
+
payment_method: { type: "string", description: "Payment method (cash, bank_transfer, check, card)" },
|
|
165
|
+
date: { type: "string", description: "Payment date (YYYY-MM-DD)" },
|
|
166
|
+
reference: { type: "string", description: "Payment reference" },
|
|
167
|
+
},
|
|
168
|
+
required: ["invoice_id", "amount", "payment_method"],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
}));
|
|
173
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
174
|
+
const { name, arguments: args } = request.params;
|
|
175
|
+
try {
|
|
176
|
+
switch (name) {
|
|
177
|
+
case "list_customers": {
|
|
178
|
+
const params = {};
|
|
179
|
+
if (args?.filter)
|
|
180
|
+
params.filter = args.filter;
|
|
181
|
+
if (args?.offset)
|
|
182
|
+
params.offset = args.offset;
|
|
183
|
+
if (args?.limit)
|
|
184
|
+
params.limit = args.limit;
|
|
185
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("cliente", "listar", params), null, 2) }] };
|
|
186
|
+
}
|
|
187
|
+
case "create_customer":
|
|
188
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("cliente", "crear", {
|
|
189
|
+
razonSocial: args?.name,
|
|
190
|
+
cuit: args?.tax_id,
|
|
191
|
+
tipoDocumento: args?.tax_id_type,
|
|
192
|
+
categoriaFiscal: args?.tax_category,
|
|
193
|
+
email: args?.email,
|
|
194
|
+
telefono: args?.phone,
|
|
195
|
+
direccion: args?.address,
|
|
196
|
+
}), null, 2) }] };
|
|
197
|
+
case "list_products": {
|
|
198
|
+
const params = {};
|
|
199
|
+
if (args?.filter)
|
|
200
|
+
params.filter = args.filter;
|
|
201
|
+
if (args?.offset)
|
|
202
|
+
params.offset = args.offset;
|
|
203
|
+
if (args?.limit)
|
|
204
|
+
params.limit = args.limit;
|
|
205
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("producto", "listar", params), null, 2) }] };
|
|
206
|
+
}
|
|
207
|
+
case "create_invoice":
|
|
208
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("factura", "crear", {
|
|
209
|
+
idCliente: args?.customer_id,
|
|
210
|
+
tipoComprobante: args?.invoice_type,
|
|
211
|
+
puntoVenta: args?.point_of_sale,
|
|
212
|
+
items: args?.items,
|
|
213
|
+
moneda: args?.currency,
|
|
214
|
+
}), null, 2) }] };
|
|
215
|
+
case "list_invoices": {
|
|
216
|
+
const params = {};
|
|
217
|
+
if (args?.date_from)
|
|
218
|
+
params.fechaDesde = args.date_from;
|
|
219
|
+
if (args?.date_to)
|
|
220
|
+
params.fechaHasta = args.date_to;
|
|
221
|
+
if (args?.customer_id)
|
|
222
|
+
params.idCliente = args.customer_id;
|
|
223
|
+
if (args?.status)
|
|
224
|
+
params.estado = args.status;
|
|
225
|
+
if (args?.offset)
|
|
226
|
+
params.offset = args.offset;
|
|
227
|
+
if (args?.limit)
|
|
228
|
+
params.limit = args.limit;
|
|
229
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("factura", "listar", params), null, 2) }] };
|
|
230
|
+
}
|
|
231
|
+
case "get_balance": {
|
|
232
|
+
const params = {};
|
|
233
|
+
if (args?.date)
|
|
234
|
+
params.fecha = args.date;
|
|
235
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("balance", "obtener", params), null, 2) }] };
|
|
236
|
+
}
|
|
237
|
+
case "list_accounts": {
|
|
238
|
+
const params = {};
|
|
239
|
+
if (args?.filter)
|
|
240
|
+
params.filter = args.filter;
|
|
241
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("cuenta", "listar", params), null, 2) }] };
|
|
242
|
+
}
|
|
243
|
+
case "create_payment":
|
|
244
|
+
return { content: [{ type: "text", text: JSON.stringify(await colppyRequest("pago", "crear", {
|
|
245
|
+
idFactura: args?.invoice_id,
|
|
246
|
+
monto: args?.amount,
|
|
247
|
+
metodoPago: args?.payment_method,
|
|
248
|
+
fecha: args?.date,
|
|
249
|
+
referencia: args?.reference,
|
|
250
|
+
}), null, 2) }] };
|
|
251
|
+
default:
|
|
252
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
async function main() {
|
|
260
|
+
if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
|
|
261
|
+
const { default: express } = await import("express");
|
|
262
|
+
const { randomUUID } = await import("node:crypto");
|
|
263
|
+
const app = express();
|
|
264
|
+
app.use(express.json());
|
|
265
|
+
const transports = new Map();
|
|
266
|
+
app.get("/health", (_req, res) => res.json({ status: "ok", sessions: transports.size }));
|
|
267
|
+
app.post("/mcp", async (req, res) => {
|
|
268
|
+
const sid = req.headers["mcp-session-id"];
|
|
269
|
+
if (sid && transports.has(sid)) {
|
|
270
|
+
await transports.get(sid).handleRequest(req, res, req.body);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (!sid && isInitializeRequest(req.body)) {
|
|
274
|
+
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
275
|
+
t.onclose = () => { if (t.sessionId)
|
|
276
|
+
transports.delete(t.sessionId); };
|
|
277
|
+
const s = new Server({ name: "mcp-colppy", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
278
|
+
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
279
|
+
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
280
|
+
await s.connect(t);
|
|
281
|
+
await t.handleRequest(req, res, req.body);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|
|
285
|
+
});
|
|
286
|
+
app.get("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
287
|
+
await transports.get(sid).handleRequest(req, res);
|
|
288
|
+
else
|
|
289
|
+
res.status(400).send("Invalid session"); });
|
|
290
|
+
app.delete("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
291
|
+
await transports.get(sid).handleRequest(req, res);
|
|
292
|
+
else
|
|
293
|
+
res.status(400).send("Invalid session"); });
|
|
294
|
+
const port = Number(process.env.MCP_PORT) || 3000;
|
|
295
|
+
app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
const transport = new StdioServerTransport();
|
|
299
|
+
await server.connect(transport);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codespar/mcp-colppy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Colppy — Argentine cloud accounting + AFIP invoicing",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-colppy": "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": ["colppy", "accounting", "argentina", "afip", "invoicing", "mcp"],
|
|
24
|
+
"mcpName": "io.github.codespar/mcp-colppy"
|
|
25
|
+
}
|