@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 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.
@@ -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
+ }