@codespar/mcp-afip 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 +54 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +235 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# MCP AFIP
|
|
2
|
+
|
|
3
|
+
MCP server for **AFIP** — Argentine tax authority (Administracion Federal de Ingresos Publicos) for electronic invoicing (Factura Electronica) with CAE authorization.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Set your credentials
|
|
9
|
+
export AFIP_CERT_PATH="/path/to/cert.crt"
|
|
10
|
+
export AFIP_KEY_PATH="/path/to/key.key"
|
|
11
|
+
export AFIP_CUIT="20123456789"
|
|
12
|
+
|
|
13
|
+
# Run via stdio
|
|
14
|
+
npx tsx packages/argentina/afip/src/index.ts
|
|
15
|
+
|
|
16
|
+
# Run via HTTP
|
|
17
|
+
npx tsx packages/argentina/afip/src/index.ts --http
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Environment Variables
|
|
21
|
+
|
|
22
|
+
| Variable | Required | Description |
|
|
23
|
+
|----------|----------|-------------|
|
|
24
|
+
| `AFIP_CERT_PATH` | Yes | Path to AFIP certificate (.crt) |
|
|
25
|
+
| `AFIP_KEY_PATH` | Yes | Path to AFIP private key (.key) |
|
|
26
|
+
| `AFIP_CUIT` | Yes | CUIT number of the taxpayer |
|
|
27
|
+
| `AFIP_ENV` | No | `"production"` or `"homologation"` (default: homologation) |
|
|
28
|
+
| `MCP_HTTP` | No | Set to `"true"` to enable HTTP transport |
|
|
29
|
+
| `MCP_PORT` | No | HTTP port (default: 3000) |
|
|
30
|
+
|
|
31
|
+
## Tools
|
|
32
|
+
|
|
33
|
+
| Tool | Description |
|
|
34
|
+
|------|-------------|
|
|
35
|
+
| `create_invoice` | Create an electronic invoice (Factura Electronica) |
|
|
36
|
+
| `get_invoice` | Get invoice details by type, POS, and number |
|
|
37
|
+
| `get_last_invoice_number` | Get last authorized invoice number for a POS |
|
|
38
|
+
| `get_cae_status` | Check CAE authorization status |
|
|
39
|
+
| `list_invoice_types` | List available invoice types (A, B, C, etc.) |
|
|
40
|
+
| `list_tax_types` | List available tax types (IVA, percepciones, etc.) |
|
|
41
|
+
| `get_server_status` | Check AFIP web-service availability |
|
|
42
|
+
| `get_authorized_points_of_sale` | List authorized puntos de venta |
|
|
43
|
+
|
|
44
|
+
## Auth
|
|
45
|
+
|
|
46
|
+
Uses **certificate-based WSAA authentication** (SOAP). The MCP server wraps AFIP's SOAP services (WSFE) internally using a REST approach. You need a valid certificate issued by AFIP. For testing, use the homologation environment.
|
|
47
|
+
|
|
48
|
+
## API Reference
|
|
49
|
+
|
|
50
|
+
- [AFIP Web Services Documentation](https://www.afip.gob.ar/ws/)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
**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 AFIP — Argentine tax authority, electronic invoicing (Factura Electrónica).
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - create_invoice: Create an electronic invoice (Factura Electrónica)
|
|
7
|
+
* - get_invoice: Get invoice details by type and number
|
|
8
|
+
* - get_last_invoice_number: Get last authorized invoice number for a POS
|
|
9
|
+
* - get_cae_status: Check CAE authorization status
|
|
10
|
+
* - list_invoice_types: List available invoice types (A, B, C, etc.)
|
|
11
|
+
* - list_tax_types: List available tax types (IVA, percepciones, etc.)
|
|
12
|
+
* - get_server_status: Check AFIP web-service availability
|
|
13
|
+
* - get_authorized_points_of_sale: List authorized puntos de venta
|
|
14
|
+
*
|
|
15
|
+
* Environment:
|
|
16
|
+
* AFIP_CERT_PATH — Path to AFIP certificate (.crt)
|
|
17
|
+
* AFIP_KEY_PATH — Path to AFIP private key (.key)
|
|
18
|
+
* AFIP_CUIT — CUIT number
|
|
19
|
+
* AFIP_ENV — "production" or "homologation" (default: homologation)
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for AFIP — Argentine tax authority, electronic invoicing (Factura Electrónica).
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - create_invoice: Create an electronic invoice (Factura Electrónica)
|
|
7
|
+
* - get_invoice: Get invoice details by type and number
|
|
8
|
+
* - get_last_invoice_number: Get last authorized invoice number for a POS
|
|
9
|
+
* - get_cae_status: Check CAE authorization status
|
|
10
|
+
* - list_invoice_types: List available invoice types (A, B, C, etc.)
|
|
11
|
+
* - list_tax_types: List available tax types (IVA, percepciones, etc.)
|
|
12
|
+
* - get_server_status: Check AFIP web-service availability
|
|
13
|
+
* - get_authorized_points_of_sale: List authorized puntos de venta
|
|
14
|
+
*
|
|
15
|
+
* Environment:
|
|
16
|
+
* AFIP_CERT_PATH — Path to AFIP certificate (.crt)
|
|
17
|
+
* AFIP_KEY_PATH — Path to AFIP private key (.key)
|
|
18
|
+
* AFIP_CUIT — CUIT number
|
|
19
|
+
* AFIP_ENV — "production" or "homologation" (default: homologation)
|
|
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 CERT_PATH = process.env.AFIP_CERT_PATH || "";
|
|
27
|
+
const KEY_PATH = process.env.AFIP_KEY_PATH || "";
|
|
28
|
+
const CUIT = process.env.AFIP_CUIT || "";
|
|
29
|
+
const AFIP_ENV = process.env.AFIP_ENV || "homologation";
|
|
30
|
+
const BASE_URL = AFIP_ENV === "production"
|
|
31
|
+
? "https://servicios1.afip.gov.ar"
|
|
32
|
+
: "https://wswhomo.afip.gov.ar";
|
|
33
|
+
async function afipRequest(method, path, body) {
|
|
34
|
+
const headers = {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
"Accept": "application/json",
|
|
37
|
+
};
|
|
38
|
+
if (CUIT)
|
|
39
|
+
headers["X-AFIP-CUIT"] = CUIT;
|
|
40
|
+
const res = await fetch(`${BASE_URL}${path}`, {
|
|
41
|
+
method,
|
|
42
|
+
headers,
|
|
43
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const err = await res.text();
|
|
47
|
+
throw new Error(`AFIP API ${res.status}: ${err}`);
|
|
48
|
+
}
|
|
49
|
+
return res.json();
|
|
50
|
+
}
|
|
51
|
+
const server = new Server({ name: "mcp-afip", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
52
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
53
|
+
tools: [
|
|
54
|
+
{
|
|
55
|
+
name: "create_invoice",
|
|
56
|
+
description: "Create an electronic invoice (Factura Electrónica) via AFIP",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: {
|
|
60
|
+
point_of_sale: { type: "number", description: "Punto de venta number" },
|
|
61
|
+
invoice_type: { type: "number", description: "Invoice type code (1=A, 6=B, 11=C, etc.)" },
|
|
62
|
+
concept: { type: "number", description: "Concept (1=Products, 2=Services, 3=Both)" },
|
|
63
|
+
doc_type: { type: "number", description: "Document type (80=CUIT, 86=CUIL, 96=DNI, 99=Consumer Final)" },
|
|
64
|
+
doc_number: { type: "number", description: "Document number of the customer" },
|
|
65
|
+
amount_total: { type: "number", description: "Total invoice amount" },
|
|
66
|
+
amount_net: { type: "number", description: "Net taxable amount" },
|
|
67
|
+
amount_iva: { type: "number", description: "IVA tax amount" },
|
|
68
|
+
currency: { type: "string", description: "Currency code (PES=ARS, DOL=USD)" },
|
|
69
|
+
items: {
|
|
70
|
+
type: "array",
|
|
71
|
+
description: "Invoice line items",
|
|
72
|
+
items: {
|
|
73
|
+
type: "object",
|
|
74
|
+
properties: {
|
|
75
|
+
description: { type: "string", description: "Item description" },
|
|
76
|
+
quantity: { type: "number", description: "Quantity" },
|
|
77
|
+
unit_price: { type: "number", description: "Unit price" },
|
|
78
|
+
iva_rate: { type: "number", description: "IVA rate (21, 10.5, 27, 0)" },
|
|
79
|
+
},
|
|
80
|
+
required: ["description", "quantity", "unit_price"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: ["point_of_sale", "invoice_type", "concept", "doc_type", "doc_number", "amount_total"],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "get_invoice",
|
|
89
|
+
description: "Get invoice details by type, point of sale, and number",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
point_of_sale: { type: "number", description: "Punto de venta number" },
|
|
94
|
+
invoice_type: { type: "number", description: "Invoice type code" },
|
|
95
|
+
invoice_number: { type: "number", description: "Invoice number" },
|
|
96
|
+
},
|
|
97
|
+
required: ["point_of_sale", "invoice_type", "invoice_number"],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "get_last_invoice_number",
|
|
102
|
+
description: "Get last authorized invoice number for a point of sale and type",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: {
|
|
106
|
+
point_of_sale: { type: "number", description: "Punto de venta number" },
|
|
107
|
+
invoice_type: { type: "number", description: "Invoice type code" },
|
|
108
|
+
},
|
|
109
|
+
required: ["point_of_sale", "invoice_type"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "get_cae_status",
|
|
114
|
+
description: "Check CAE authorization status for an invoice",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
point_of_sale: { type: "number", description: "Punto de venta number" },
|
|
119
|
+
invoice_type: { type: "number", description: "Invoice type code" },
|
|
120
|
+
invoice_number: { type: "number", description: "Invoice number" },
|
|
121
|
+
},
|
|
122
|
+
required: ["point_of_sale", "invoice_type", "invoice_number"],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "list_invoice_types",
|
|
127
|
+
description: "List available invoice types (Factura A, B, C, etc.)",
|
|
128
|
+
inputSchema: { type: "object", properties: {} },
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "list_tax_types",
|
|
132
|
+
description: "List available tax types (IVA, percepciones, retenciones, etc.)",
|
|
133
|
+
inputSchema: { type: "object", properties: {} },
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "get_server_status",
|
|
137
|
+
description: "Check AFIP web-service availability (WSFE status)",
|
|
138
|
+
inputSchema: { type: "object", properties: {} },
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "get_authorized_points_of_sale",
|
|
142
|
+
description: "List authorized puntos de venta for the CUIT",
|
|
143
|
+
inputSchema: { type: "object", properties: {} },
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
}));
|
|
147
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
148
|
+
const { name, arguments: args } = request.params;
|
|
149
|
+
try {
|
|
150
|
+
switch (name) {
|
|
151
|
+
case "create_invoice": {
|
|
152
|
+
const payload = {
|
|
153
|
+
point_of_sale: args?.point_of_sale,
|
|
154
|
+
invoice_type: args?.invoice_type,
|
|
155
|
+
concept: args?.concept,
|
|
156
|
+
doc_type: args?.doc_type,
|
|
157
|
+
doc_number: args?.doc_number,
|
|
158
|
+
amount_total: args?.amount_total,
|
|
159
|
+
};
|
|
160
|
+
if (args?.amount_net)
|
|
161
|
+
payload.amount_net = args.amount_net;
|
|
162
|
+
if (args?.amount_iva)
|
|
163
|
+
payload.amount_iva = args.amount_iva;
|
|
164
|
+
if (args?.currency)
|
|
165
|
+
payload.currency = args.currency;
|
|
166
|
+
if (args?.items)
|
|
167
|
+
payload.items = args.items;
|
|
168
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("POST", "/wsfe/invoices", payload), null, 2) }] };
|
|
169
|
+
}
|
|
170
|
+
case "get_invoice":
|
|
171
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", `/wsfe/invoices/${args?.invoice_type}/${args?.point_of_sale}/${args?.invoice_number}`), null, 2) }] };
|
|
172
|
+
case "get_last_invoice_number":
|
|
173
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", `/wsfe/last-invoice/${args?.invoice_type}/${args?.point_of_sale}`), null, 2) }] };
|
|
174
|
+
case "get_cae_status":
|
|
175
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", `/wsfe/cae-status/${args?.invoice_type}/${args?.point_of_sale}/${args?.invoice_number}`), null, 2) }] };
|
|
176
|
+
case "list_invoice_types":
|
|
177
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", "/wsfe/invoice-types"), null, 2) }] };
|
|
178
|
+
case "list_tax_types":
|
|
179
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", "/wsfe/tax-types"), null, 2) }] };
|
|
180
|
+
case "get_server_status":
|
|
181
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", "/wsfe/status"), null, 2) }] };
|
|
182
|
+
case "get_authorized_points_of_sale":
|
|
183
|
+
return { content: [{ type: "text", text: JSON.stringify(await afipRequest("GET", "/wsfe/points-of-sale"), null, 2) }] };
|
|
184
|
+
default:
|
|
185
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
async function main() {
|
|
193
|
+
if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
|
|
194
|
+
const { default: express } = await import("express");
|
|
195
|
+
const { randomUUID } = await import("node:crypto");
|
|
196
|
+
const app = express();
|
|
197
|
+
app.use(express.json());
|
|
198
|
+
const transports = new Map();
|
|
199
|
+
app.get("/health", (_req, res) => res.json({ status: "ok", sessions: transports.size }));
|
|
200
|
+
app.post("/mcp", async (req, res) => {
|
|
201
|
+
const sid = req.headers["mcp-session-id"];
|
|
202
|
+
if (sid && transports.has(sid)) {
|
|
203
|
+
await transports.get(sid).handleRequest(req, res, req.body);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (!sid && isInitializeRequest(req.body)) {
|
|
207
|
+
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
208
|
+
t.onclose = () => { if (t.sessionId)
|
|
209
|
+
transports.delete(t.sessionId); };
|
|
210
|
+
const s = new Server({ name: "mcp-afip", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
211
|
+
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
212
|
+
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
213
|
+
await s.connect(t);
|
|
214
|
+
await t.handleRequest(req, res, req.body);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|
|
218
|
+
});
|
|
219
|
+
app.get("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
220
|
+
await transports.get(sid).handleRequest(req, res);
|
|
221
|
+
else
|
|
222
|
+
res.status(400).send("Invalid session"); });
|
|
223
|
+
app.delete("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
224
|
+
await transports.get(sid).handleRequest(req, res);
|
|
225
|
+
else
|
|
226
|
+
res.status(400).send("Invalid session"); });
|
|
227
|
+
const port = Number(process.env.MCP_PORT) || 3000;
|
|
228
|
+
app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
const transport = new StdioServerTransport();
|
|
232
|
+
await server.connect(transport);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codespar/mcp-afip",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for AFIP — Argentine tax authority, electronic invoicing (Factura Electrónica)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-afip": "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": ["afip", "tax", "argentina", "factura-electronica", "cae", "mcp"],
|
|
24
|
+
"mcpName": "io.github.codespar/mcp-afip"
|
|
25
|
+
}
|