@iflow-mcp/dojocodinglabs-hacienda-cr 0.2.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,151 @@
1
+ # @dojocoding/hacienda-mcp
2
+
3
+ MCP (Model Context Protocol) Server for Costa Rica electronic invoicing (Hacienda API v4.4).
4
+
5
+ Exposes the `@dojocoding/hacienda-sdk` as AI-accessible tools and resources via the Model Context Protocol, allowing AI assistants like Claude Desktop to create invoices, check document status, and look up taxpayer information.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install -g @dojocoding/hacienda-mcp
11
+ ```
12
+
13
+ Requires **Node.js 22+**.
14
+
15
+ ## Setup
16
+
17
+ ### Claude Desktop
18
+
19
+ Add this to your Claude Desktop configuration file:
20
+
21
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
22
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "hacienda-cr": {
28
+ "command": "npx",
29
+ "args": ["-y", "@dojocoding/hacienda-mcp"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ Or if installed globally:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "hacienda-cr": {
41
+ "command": "hacienda-mcp"
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Other MCP Clients
48
+
49
+ The server uses stdio transport. Start it with:
50
+
51
+ ```bash
52
+ npx @dojocoding/hacienda-mcp
53
+ # or
54
+ hacienda-mcp
55
+ ```
56
+
57
+ ### Programmatic Usage
58
+
59
+ ```ts
60
+ import { createServer } from "@dojocoding/hacienda-mcp";
61
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
62
+
63
+ const server = createServer();
64
+ const transport = new StdioServerTransport();
65
+ await server.connect(transport);
66
+ ```
67
+
68
+ ## Tools
69
+
70
+ ### `create_invoice`
71
+
72
+ Create a Factura Electronica (electronic invoice). Accepts emisor, receptor, and line items. Automatically computes taxes, totals, generates the clave numerica, and builds the XML.
73
+
74
+ **Parameters:**
75
+
76
+ - `emisor` -- Issuer information (name, ID, email)
77
+ - `receptor` -- Receiver information (name, optional ID, optional email)
78
+ - `codigoActividad` -- CABYS activity code (6 digits)
79
+ - `condicionVenta` -- Sale condition code (default: `"01"` = cash)
80
+ - `medioPago` -- Payment methods array (default: `["01"]` = cash)
81
+ - `lineItems` -- Array of line items with CABYS code, quantity, unit, description, price, and optional tax/discount
82
+ - `plazoCredito` -- Credit term in days (optional)
83
+
84
+ ### `check_status`
85
+
86
+ Check the processing status of a document by its 50-digit clave numerica.
87
+
88
+ **Parameters:**
89
+
90
+ - `clave` -- The 50-digit clave numerica
91
+
92
+ ### `list_documents`
93
+
94
+ List recent electronic documents with optional filters.
95
+
96
+ **Parameters:**
97
+
98
+ - `limit` -- Max results (1-100, default: 10)
99
+ - `offset` -- Pagination offset (default: 0)
100
+ - `emisorIdentificacion` -- Filter by issuer ID (optional)
101
+ - `receptorIdentificacion` -- Filter by receiver ID (optional)
102
+ - `fechaDesde` -- Start date filter, ISO 8601 (optional)
103
+ - `fechaHasta` -- End date filter, ISO 8601 (optional)
104
+
105
+ ### `get_document`
106
+
107
+ Get full details of an electronic document by its 50-digit clave numerica.
108
+
109
+ **Parameters:**
110
+
111
+ - `clave` -- The 50-digit clave numerica
112
+
113
+ ### `lookup_taxpayer`
114
+
115
+ Look up a Costa Rica taxpayer by identification number (cedula). Returns name, ID type, and registered economic activities.
116
+
117
+ **Parameters:**
118
+
119
+ - `identificacion` -- Taxpayer ID number (9-12 digits)
120
+
121
+ ### `draft_invoice`
122
+
123
+ Generate a draft invoice template with sensible defaults. Returns JSON that can be passed to `create_invoice`.
124
+
125
+ **Parameters:**
126
+
127
+ - `emisorNombre` -- Issuer name
128
+ - `emisorIdTipo` -- Issuer ID type (default: `"02"`)
129
+ - `emisorIdNumero` -- Issuer ID number
130
+ - `emisorEmail` -- Issuer email
131
+ - `receptorNombre` -- Receiver name
132
+ - `receptorIdTipo` -- Receiver ID type (optional)
133
+ - `receptorIdNumero` -- Receiver ID number (optional)
134
+ - `receptorEmail` -- Receiver email (optional)
135
+ - `codigoActividad` -- Activity code (default: `"620100"`)
136
+ - `description` -- Default line item description (default: `"Servicio profesional"`)
137
+ - `amount` -- Default line item amount (default: `0`)
138
+ - `includeIva` -- Include 13% IVA (default: `true`)
139
+
140
+ ## Resources
141
+
142
+ | URI | Description |
143
+ | ------------------------------------- | ------------------------------------------ |
144
+ | `hacienda://schemas/factura` | JSON schema for invoice creation input |
145
+ | `hacienda://reference/document-types` | Document types, codes, and descriptions |
146
+ | `hacienda://reference/tax-codes` | Tax codes, IVA rates, and units of measure |
147
+ | `hacienda://reference/id-types` | Identification types and validation rules |
148
+
149
+ ## Full Documentation
150
+
151
+ See the [root README](../../README.md) for comprehensive documentation with examples.
@@ -0,0 +1,1080 @@
1
+ // src/server.ts
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+
4
+ // src/tools/create-invoice.ts
5
+ import { z } from "zod";
6
+ import {
7
+ buildFacturaXml,
8
+ buildClave,
9
+ DocumentType,
10
+ Situation,
11
+ calculateLineItemTotals,
12
+ calculateInvoiceSummary,
13
+ getNextSequence,
14
+ DEFAULT_BRANCH,
15
+ DEFAULT_POS
16
+ } from "@dojocoding/hacienda-sdk";
17
+ var ImpuestoInputSchema = z.object({
18
+ codigo: z.string().describe('Tax type code (e.g. "01" for IVA)'),
19
+ codigoTarifa: z.string().optional().describe('IVA rate code (e.g. "08" for 13%)'),
20
+ tarifa: z.number().describe("Tax rate percentage (e.g. 13 for 13%)")
21
+ });
22
+ var DescuentoInputSchema = z.object({
23
+ montoDescuento: z.number().positive().describe("Discount amount"),
24
+ naturalezaDescuento: z.string().describe("Reason for discount")
25
+ });
26
+ var LineItemInputSchema = z.object({
27
+ codigoCabys: z.string().describe("CABYS code (13 digits)"),
28
+ cantidad: z.number().positive().describe("Quantity"),
29
+ unidadMedida: z.string().describe('Unit of measure (e.g. "Unid", "Sp", "kg")'),
30
+ detalle: z.string().describe("Item description (max 200 chars)"),
31
+ precioUnitario: z.number().min(0).describe("Unit price before taxes"),
32
+ esServicio: z.boolean().optional().describe("Whether this is a service (true) or merchandise (false). Defaults to false"),
33
+ impuesto: z.array(ImpuestoInputSchema).optional().describe("Taxes to apply to this line item"),
34
+ descuento: z.array(DescuentoInputSchema).optional().describe("Discounts for this line item")
35
+ });
36
+ var IdentificacionInputSchema = z.object({
37
+ tipo: z.string().describe('ID type: "01"=Fisica, "02"=Juridica, "03"=DIMEX, "04"=NITE'),
38
+ numero: z.string().describe("ID number (digits only)")
39
+ });
40
+ var EmisorInputSchema = z.object({
41
+ nombre: z.string().describe("Issuer name (max 100 chars)"),
42
+ identificacion: IdentificacionInputSchema.describe("Taxpayer identification"),
43
+ nombreComercial: z.string().optional().describe("Commercial name"),
44
+ correoElectronico: z.string().describe("Email address")
45
+ });
46
+ var ReceptorInputSchema = z.object({
47
+ nombre: z.string().describe("Receiver name (max 100 chars)"),
48
+ identificacion: IdentificacionInputSchema.optional().describe("Taxpayer identification"),
49
+ correoElectronico: z.string().optional().describe("Email address")
50
+ });
51
+ var CreateInvoiceInputSchema = z.object({
52
+ emisor: EmisorInputSchema.describe("Invoice issuer (emisor)"),
53
+ receptor: ReceptorInputSchema.describe("Invoice receiver (receptor)"),
54
+ codigoActividad: z.string().describe("CABYS activity code (6 digits)"),
55
+ condicionVenta: z.string().default("01").describe('Sale condition code: "01"=Cash, "02"=Credit, etc. Defaults to "01"'),
56
+ medioPago: z.array(z.string()).default(["01"]).describe('Payment methods: ["01"]=Cash, ["02"]=Card, etc. Defaults to ["01"]'),
57
+ lineItems: z.array(LineItemInputSchema).min(1).describe("Invoice line items (at least one required)"),
58
+ plazoCredito: z.string().optional().describe("Credit term in days (required when condicionVenta is 02)")
59
+ });
60
+ function registerCreateInvoiceTool(server) {
61
+ server.tool(
62
+ "create_invoice",
63
+ "Create a Factura Electronica (electronic invoice) for Costa Rica. Accepts emisor, receptor, and line items. Automatically computes taxes, totals, generates the clave numerica, and builds the XML. Returns the unsigned XML string.",
64
+ {
65
+ emisor: EmisorInputSchema,
66
+ receptor: ReceptorInputSchema,
67
+ codigoActividad: CreateInvoiceInputSchema.shape.codigoActividad,
68
+ condicionVenta: CreateInvoiceInputSchema.shape.condicionVenta,
69
+ medioPago: CreateInvoiceInputSchema.shape.medioPago,
70
+ lineItems: CreateInvoiceInputSchema.shape.lineItems,
71
+ plazoCredito: CreateInvoiceInputSchema.shape.plazoCredito
72
+ },
73
+ async (args) => {
74
+ try {
75
+ const lineItemInputs = args.lineItems.map((item, index) => ({
76
+ numeroLinea: index + 1,
77
+ codigoCabys: item.codigoCabys,
78
+ cantidad: item.cantidad,
79
+ unidadMedida: item.unidadMedida,
80
+ detalle: item.detalle,
81
+ precioUnitario: item.precioUnitario,
82
+ esServicio: item.esServicio ?? false,
83
+ impuesto: item.impuesto,
84
+ descuento: item.descuento
85
+ }));
86
+ const calculatedItems = lineItemInputs.map(calculateLineItemTotals);
87
+ const summary = calculateInvoiceSummary(calculatedItems);
88
+ const now = /* @__PURE__ */ new Date();
89
+ const taxpayerId = args.emisor.identificacion.numero;
90
+ const docTypeCode = "01";
91
+ const branch = DEFAULT_BRANCH;
92
+ const pos = DEFAULT_POS;
93
+ const sequence = await getNextSequence(docTypeCode, branch, pos);
94
+ const clave = buildClave({
95
+ date: now,
96
+ taxpayerId,
97
+ documentType: DocumentType.FACTURA_ELECTRONICA,
98
+ sequence,
99
+ situation: Situation.NORMAL
100
+ });
101
+ const seq = String(sequence).padStart(10, "0");
102
+ const numeroConsecutivo = `${branch}${pos}${docTypeCode}${seq}`;
103
+ const fechaEmision = now.toISOString();
104
+ const detalleServicio = calculatedItems.map((item) => ({
105
+ ...item,
106
+ unidadMedida: item.unidadMedida
107
+ }));
108
+ const factura = {
109
+ clave,
110
+ codigoActividad: args.codigoActividad,
111
+ numeroConsecutivo,
112
+ fechaEmision,
113
+ emisor: {
114
+ nombre: args.emisor.nombre,
115
+ identificacion: {
116
+ tipo: args.emisor.identificacion.tipo,
117
+ numero: args.emisor.identificacion.numero
118
+ },
119
+ ...args.emisor.nombreComercial ? { nombreComercial: args.emisor.nombreComercial } : {},
120
+ correoElectronico: args.emisor.correoElectronico
121
+ },
122
+ receptor: {
123
+ nombre: args.receptor.nombre,
124
+ ...args.receptor.identificacion ? {
125
+ identificacion: {
126
+ tipo: args.receptor.identificacion.tipo,
127
+ numero: args.receptor.identificacion.numero
128
+ }
129
+ } : {},
130
+ ...args.receptor.correoElectronico ? { correoElectronico: args.receptor.correoElectronico } : {}
131
+ },
132
+ condicionVenta: args.condicionVenta,
133
+ ...args.plazoCredito ? { plazoCredito: args.plazoCredito } : {},
134
+ medioPago: args.medioPago,
135
+ detalleServicio,
136
+ resumenFactura: {
137
+ totalServGravados: summary.totalServGravados,
138
+ totalServExentos: summary.totalServExentos,
139
+ ...summary.totalServExonerado > 0 ? { totalServExonerado: summary.totalServExonerado } : {},
140
+ totalMercanciasGravadas: summary.totalMercanciasGravadas,
141
+ totalMercanciasExentas: summary.totalMercanciasExentas,
142
+ ...summary.totalMercExonerada > 0 ? { totalMercExonerada: summary.totalMercExonerada } : {},
143
+ totalGravado: summary.totalGravado,
144
+ totalExento: summary.totalExento,
145
+ ...summary.totalExonerado > 0 ? { totalExonerado: summary.totalExonerado } : {},
146
+ totalVenta: summary.totalVenta,
147
+ totalDescuentos: summary.totalDescuentos,
148
+ totalVentaNeta: summary.totalVentaNeta,
149
+ totalImpuesto: summary.totalImpuesto,
150
+ totalComprobante: summary.totalComprobante
151
+ }
152
+ };
153
+ const xml = buildFacturaXml(factura);
154
+ return {
155
+ content: [
156
+ {
157
+ type: "text",
158
+ text: [
159
+ `Factura Electronica created successfully.`,
160
+ ``,
161
+ `Clave: ${clave}`,
162
+ `Consecutivo: ${numeroConsecutivo}`,
163
+ `Total: ${summary.totalComprobante}`,
164
+ `Tax: ${summary.totalImpuesto}`,
165
+ ``,
166
+ `--- XML (unsigned) ---`,
167
+ xml
168
+ ].join("\n")
169
+ }
170
+ ]
171
+ };
172
+ } catch (error) {
173
+ const message = error instanceof Error ? error.message : String(error);
174
+ return {
175
+ content: [
176
+ {
177
+ type: "text",
178
+ text: `Error creating invoice: ${message}`
179
+ }
180
+ ],
181
+ isError: true
182
+ };
183
+ }
184
+ }
185
+ );
186
+ }
187
+
188
+ // src/tools/document-tools.ts
189
+ import { z as z2 } from "zod";
190
+ import {
191
+ parseClave,
192
+ getStatus,
193
+ extractRejectionReason,
194
+ listComprobantes,
195
+ getComprobante
196
+ } from "@dojocoding/hacienda-sdk";
197
+ import { DOCUMENT_TYPE_NAMES } from "@dojocoding/hacienda-shared";
198
+
199
+ // src/tools/api-client.ts
200
+ import { bootstrapClient } from "@dojocoding/hacienda-sdk";
201
+ var clientCache = /* @__PURE__ */ new Map();
202
+ async function createMcpApiClient(profileName) {
203
+ const key = profileName ?? "default";
204
+ const cached = clientCache.get(key);
205
+ if (cached) {
206
+ return cached;
207
+ }
208
+ const { httpClient } = await bootstrapClient({ profileName: key });
209
+ clientCache.set(key, httpClient);
210
+ return httpClient;
211
+ }
212
+
213
+ // src/tools/document-tools.ts
214
+ function registerCheckStatusTool(server) {
215
+ server.tool(
216
+ "check_status",
217
+ "Check the processing status of an electronic document by its 50-digit clave numerica. Returns the current status from Hacienda (recibido, procesando, aceptado, rechazado). Requires a configured profile (run `hacienda auth login` first).",
218
+ {
219
+ clave: z2.string().length(50).describe("The 50-digit clave numerica of the document to check"),
220
+ profile: z2.string().default("default").describe('Config profile name (default: "default")')
221
+ },
222
+ async (args) => {
223
+ try {
224
+ const parsed = parseClave(args.clave);
225
+ const docTypeName = DOCUMENT_TYPE_NAMES[parsed.documentType] ?? `Unknown (${parsed.documentType})`;
226
+ const httpClient = await createMcpApiClient(args.profile);
227
+ const status = await getStatus(httpClient, args.clave);
228
+ let rejectionReason;
229
+ if (status.responseXml) {
230
+ rejectionReason = extractRejectionReason(status.responseXml);
231
+ }
232
+ return {
233
+ content: [
234
+ {
235
+ type: "text",
236
+ text: [
237
+ `Document Status`,
238
+ ``,
239
+ `Clave: ${args.clave}`,
240
+ `Document Type: ${docTypeName}`,
241
+ `Taxpayer ID: ${parsed.taxpayerId}`,
242
+ `Date: ${parsed.dateRaw}`,
243
+ `Sequence: ${parsed.sequence}`,
244
+ ``,
245
+ `Status: ${status.status}`,
246
+ status.date ? `Response Date: ${status.date}` : null,
247
+ rejectionReason ? `Rejection Reason: ${rejectionReason}` : null
248
+ ].filter(Boolean).join("\n")
249
+ }
250
+ ]
251
+ };
252
+ } catch (error) {
253
+ const message = error instanceof Error ? error.message : String(error);
254
+ return {
255
+ content: [
256
+ {
257
+ type: "text",
258
+ text: `Error checking status: ${message}`
259
+ }
260
+ ],
261
+ isError: true
262
+ };
263
+ }
264
+ }
265
+ );
266
+ }
267
+ function registerListDocumentsTool(server) {
268
+ server.tool(
269
+ "list_documents",
270
+ "List recent electronic documents. Optionally filter by date range, emisor, or receptor. Returns a summary list with clave, date, type, and status. Requires a configured profile (run `hacienda auth login` first).",
271
+ {
272
+ limit: z2.number().int().min(1).max(100).default(10).describe("Maximum number of documents to return (1-100, default 10)"),
273
+ offset: z2.number().int().min(0).default(0).describe("Pagination offset (default 0)"),
274
+ emisorIdentificacion: z2.string().optional().describe("Filter by issuer identification number"),
275
+ receptorIdentificacion: z2.string().optional().describe("Filter by receiver identification number"),
276
+ fechaDesde: z2.string().optional().describe("Filter by start date (ISO 8601)"),
277
+ fechaHasta: z2.string().optional().describe("Filter by end date (ISO 8601)"),
278
+ profile: z2.string().default("default").describe('Config profile name (default: "default")')
279
+ },
280
+ async (args) => {
281
+ try {
282
+ const httpClient = await createMcpApiClient(args.profile);
283
+ const result = await listComprobantes(httpClient, {
284
+ offset: args.offset,
285
+ limit: args.limit,
286
+ emisorIdentificacion: args.emisorIdentificacion,
287
+ receptorIdentificacion: args.receptorIdentificacion,
288
+ fechaEmisionDesde: args.fechaDesde,
289
+ fechaEmisionHasta: args.fechaHasta
290
+ });
291
+ const lines = [
292
+ `Document List`,
293
+ ``,
294
+ `Total: ${result.totalRegistros} documents`,
295
+ `Showing: ${result.comprobantes.length} (offset ${result.offset})`,
296
+ ``
297
+ ];
298
+ if (result.comprobantes.length === 0) {
299
+ lines.push("No documents found matching the criteria.");
300
+ } else {
301
+ for (const doc of result.comprobantes) {
302
+ lines.push(
303
+ `- ${doc.clave}`,
304
+ ` Date: ${doc.fechaEmision} | Status: ${doc.estado}`,
305
+ ` Emisor: ${doc.emisor.numeroIdentificacion}`,
306
+ ``
307
+ );
308
+ }
309
+ }
310
+ return {
311
+ content: [
312
+ {
313
+ type: "text",
314
+ text: lines.join("\n")
315
+ }
316
+ ]
317
+ };
318
+ } catch (error) {
319
+ const message = error instanceof Error ? error.message : String(error);
320
+ return {
321
+ content: [
322
+ {
323
+ type: "text",
324
+ text: `Error listing documents: ${message}`
325
+ }
326
+ ],
327
+ isError: true
328
+ };
329
+ }
330
+ }
331
+ );
332
+ }
333
+ function registerGetDocumentTool(server) {
334
+ server.tool(
335
+ "get_document",
336
+ "Get full details of an electronic document by its 50-digit clave numerica. Returns the document metadata, status, and XML content. Requires a configured profile (run `hacienda auth login` first).",
337
+ {
338
+ clave: z2.string().length(50).describe("The 50-digit clave numerica of the document to retrieve"),
339
+ profile: z2.string().default("default").describe('Config profile name (default: "default")')
340
+ },
341
+ async (args) => {
342
+ try {
343
+ const parsed = parseClave(args.clave);
344
+ const docTypeName = DOCUMENT_TYPE_NAMES[parsed.documentType] ?? `Unknown (${parsed.documentType})`;
345
+ const httpClient = await createMcpApiClient(args.profile);
346
+ const doc = await getComprobante(httpClient, args.clave);
347
+ let xmlContent;
348
+ if (doc.comprobanteXml) {
349
+ try {
350
+ xmlContent = Buffer.from(doc.comprobanteXml, "base64").toString("utf-8");
351
+ } catch {
352
+ xmlContent = "(unable to decode XML)";
353
+ }
354
+ }
355
+ return {
356
+ content: [
357
+ {
358
+ type: "text",
359
+ text: [
360
+ `Document Details`,
361
+ ``,
362
+ `Clave: ${doc.clave}`,
363
+ `Document Type: ${docTypeName}`,
364
+ `Taxpayer ID: ${parsed.taxpayerId}`,
365
+ `Emission Date: ${doc.fechaEmision}`,
366
+ `Status: ${doc.estado}`,
367
+ doc.fechaRespuesta ? `Response Date: ${doc.fechaRespuesta}` : null,
368
+ ``,
369
+ `Emisor: ${doc.emisor.tipoIdentificacion} ${doc.emisor.numeroIdentificacion}`,
370
+ doc.receptor ? `Receptor: ${doc.receptor.tipoIdentificacion} ${doc.receptor.numeroIdentificacion}` : null,
371
+ ``,
372
+ `Branch: ${parsed.branch}`,
373
+ `POS: ${parsed.pos}`,
374
+ `Sequence: ${parsed.sequence}`,
375
+ `Situation: ${parsed.situation}`,
376
+ `Security Code: ${parsed.securityCode}`,
377
+ xmlContent ? `
378
+ --- XML ---
379
+ ${xmlContent}` : null
380
+ ].filter(Boolean).join("\n")
381
+ }
382
+ ]
383
+ };
384
+ } catch (error) {
385
+ const message = error instanceof Error ? error.message : String(error);
386
+ return {
387
+ content: [
388
+ {
389
+ type: "text",
390
+ text: `Error getting document: ${message}`
391
+ }
392
+ ],
393
+ isError: true
394
+ };
395
+ }
396
+ }
397
+ );
398
+ }
399
+
400
+ // src/tools/lookup-tools.ts
401
+ import { z as z3 } from "zod";
402
+ import { lookupTaxpayer } from "@dojocoding/hacienda-sdk";
403
+ import { IDENTIFICATION_TYPE_NAMES } from "@dojocoding/hacienda-shared";
404
+ function registerLookupTaxpayerTool(server) {
405
+ server.tool(
406
+ "lookup_taxpayer",
407
+ "Look up a Costa Rica taxpayer by their identification number (cedula). Returns the taxpayer name, identification type, and registered economic activities. Useful for validating a receptor before creating an invoice.",
408
+ {
409
+ identificacion: z3.string().min(9).max(12).describe("Taxpayer identification number (9-12 digits)")
410
+ },
411
+ async (args) => {
412
+ try {
413
+ const info = await lookupTaxpayer(args.identificacion);
414
+ const activities = info.actividades.length > 0 ? info.actividades.map((a) => ` - [${a.codigo}] ${a.descripcion} (${a.estado})`) : [" (no activities registered)"];
415
+ return {
416
+ content: [
417
+ {
418
+ type: "text",
419
+ text: [
420
+ `Taxpayer Information`,
421
+ ``,
422
+ `Name: ${info.nombre}`,
423
+ `Identification: ${args.identificacion}`,
424
+ `Type: ${info.tipoIdentificacion}`,
425
+ ``,
426
+ `Economic Activities:`,
427
+ ...activities
428
+ ].join("\n")
429
+ }
430
+ ]
431
+ };
432
+ } catch (error) {
433
+ const message = error instanceof Error ? error.message : String(error);
434
+ return {
435
+ content: [
436
+ {
437
+ type: "text",
438
+ text: `Error looking up taxpayer: ${message}`
439
+ }
440
+ ],
441
+ isError: true
442
+ };
443
+ }
444
+ }
445
+ );
446
+ }
447
+ function registerDraftInvoiceTool(server) {
448
+ server.tool(
449
+ "draft_invoice",
450
+ "Generate a draft invoice template with sensible defaults for Costa Rica electronic invoicing. Use this to quickly scaffold an invoice that can then be reviewed and submitted. Returns a JSON template that can be passed to create_invoice.",
451
+ {
452
+ emisorNombre: z3.string().describe("Issuer (emisor) name"),
453
+ emisorIdTipo: z3.string().default("02").describe(
454
+ 'Issuer ID type: "01"=Fisica, "02"=Juridica, "03"=DIMEX, "04"=NITE. Defaults to "02"'
455
+ ),
456
+ emisorIdNumero: z3.string().describe("Issuer identification number"),
457
+ emisorEmail: z3.string().describe("Issuer email address"),
458
+ receptorNombre: z3.string().describe("Receiver (receptor) name"),
459
+ receptorIdTipo: z3.string().optional().describe('Receiver ID type (optional): "01"=Fisica, "02"=Juridica, "03"=DIMEX, "04"=NITE'),
460
+ receptorIdNumero: z3.string().optional().describe("Receiver identification number (optional)"),
461
+ receptorEmail: z3.string().optional().describe("Receiver email address (optional)"),
462
+ codigoActividad: z3.string().default("620100").describe("CABYS activity code (6 digits). Defaults to 620100 (IT services)"),
463
+ description: z3.string().default("Servicio profesional").describe("Default line item description"),
464
+ amount: z3.number().default(0).describe("Default line item amount (price). Set to 0 to leave as placeholder"),
465
+ includeIva: z3.boolean().default(true).describe("Whether to include 13% IVA tax. Defaults to true")
466
+ },
467
+ async (args) => {
468
+ const lineItem = {
469
+ codigoCabys: "8310100000000",
470
+ cantidad: 1,
471
+ unidadMedida: "Sp",
472
+ detalle: args.description,
473
+ precioUnitario: args.amount,
474
+ esServicio: true
475
+ };
476
+ if (args.includeIva) {
477
+ lineItem.impuesto = [
478
+ {
479
+ codigo: "01",
480
+ codigoTarifa: "08",
481
+ tarifa: 13
482
+ }
483
+ ];
484
+ }
485
+ const draft = {
486
+ emisor: {
487
+ nombre: args.emisorNombre,
488
+ identificacion: {
489
+ tipo: args.emisorIdTipo,
490
+ numero: args.emisorIdNumero
491
+ },
492
+ correoElectronico: args.emisorEmail
493
+ },
494
+ receptor: {
495
+ nombre: args.receptorNombre,
496
+ ...args.receptorIdTipo && args.receptorIdNumero ? {
497
+ identificacion: {
498
+ tipo: args.receptorIdTipo,
499
+ numero: args.receptorIdNumero
500
+ }
501
+ } : {},
502
+ ...args.receptorEmail ? { correoElectronico: args.receptorEmail } : {}
503
+ },
504
+ codigoActividad: args.codigoActividad,
505
+ condicionVenta: "01",
506
+ medioPago: ["01"],
507
+ lineItems: [lineItem]
508
+ };
509
+ const idTypeName = IDENTIFICATION_TYPE_NAMES[args.emisorIdTipo] ?? args.emisorIdTipo;
510
+ return {
511
+ content: [
512
+ {
513
+ type: "text",
514
+ text: [
515
+ `Invoice Draft Template`,
516
+ ``,
517
+ `Emisor: ${args.emisorNombre} (${idTypeName}: ${args.emisorIdNumero})`,
518
+ `Receptor: ${args.receptorNombre}`,
519
+ `Activity: ${args.codigoActividad}`,
520
+ `Sale Condition: Contado (cash)`,
521
+ `Payment: Efectivo (cash)`,
522
+ args.includeIva ? `IVA: 13% included` : `IVA: not included`,
523
+ ``,
524
+ `You can pass this JSON to the create_invoice tool:`,
525
+ ``,
526
+ "```json",
527
+ JSON.stringify(draft, null, 2),
528
+ "```",
529
+ ``,
530
+ `Modify the lineItems array to add more items, change quantities,`,
531
+ `prices, descriptions, and tax configuration as needed.`
532
+ ].join("\n")
533
+ }
534
+ ]
535
+ };
536
+ }
537
+ );
538
+ }
539
+
540
+ // src/tools/index.ts
541
+ function registerTools(server) {
542
+ registerCreateInvoiceTool(server);
543
+ registerCheckStatusTool(server);
544
+ registerListDocumentsTool(server);
545
+ registerGetDocumentTool(server);
546
+ registerLookupTaxpayerTool(server);
547
+ registerDraftInvoiceTool(server);
548
+ }
549
+
550
+ // src/resources/factura-schema.ts
551
+ var FACTURA_SCHEMA = {
552
+ $schema: "https://json-schema.org/draft/2020-12/schema",
553
+ title: "Factura Electronica (Costa Rica)",
554
+ description: "Schema for creating a Factura Electronica via the create_invoice tool. This describes the input format accepted by the tool.",
555
+ type: "object",
556
+ required: ["emisor", "receptor", "codigoActividad", "lineItems"],
557
+ properties: {
558
+ emisor: {
559
+ type: "object",
560
+ description: "Invoice issuer (emisor) information",
561
+ required: ["nombre", "identificacion", "correoElectronico"],
562
+ properties: {
563
+ nombre: {
564
+ type: "string",
565
+ maxLength: 100,
566
+ description: "Issuer name (Nombre o Razon Social)"
567
+ },
568
+ identificacion: {
569
+ type: "object",
570
+ required: ["tipo", "numero"],
571
+ properties: {
572
+ tipo: {
573
+ type: "string",
574
+ enum: ["01", "02", "03", "04"],
575
+ description: "01=Cedula Fisica, 02=Cedula Juridica, 03=DIMEX, 04=NITE"
576
+ },
577
+ numero: {
578
+ type: "string",
579
+ pattern: "^\\d{9,12}$",
580
+ description: "Identification number (9-12 digits)"
581
+ }
582
+ }
583
+ },
584
+ nombreComercial: {
585
+ type: "string",
586
+ maxLength: 80,
587
+ description: "Commercial name (optional)"
588
+ },
589
+ correoElectronico: {
590
+ type: "string",
591
+ format: "email",
592
+ description: "Email address"
593
+ }
594
+ }
595
+ },
596
+ receptor: {
597
+ type: "object",
598
+ description: "Invoice receiver (receptor) information",
599
+ required: ["nombre"],
600
+ properties: {
601
+ nombre: {
602
+ type: "string",
603
+ maxLength: 100,
604
+ description: "Receiver name"
605
+ },
606
+ identificacion: {
607
+ type: "object",
608
+ properties: {
609
+ tipo: {
610
+ type: "string",
611
+ enum: ["01", "02", "03", "04"],
612
+ description: "Identification type code"
613
+ },
614
+ numero: {
615
+ type: "string",
616
+ description: "Identification number"
617
+ }
618
+ }
619
+ },
620
+ correoElectronico: {
621
+ type: "string",
622
+ format: "email",
623
+ description: "Email address (optional)"
624
+ }
625
+ }
626
+ },
627
+ codigoActividad: {
628
+ type: "string",
629
+ pattern: "^\\d{6}$",
630
+ description: "CABYS economic activity code (6 digits)"
631
+ },
632
+ condicionVenta: {
633
+ type: "string",
634
+ enum: ["01", "02", "03", "04", "05", "06", "07", "08", "09", "99"],
635
+ default: "01",
636
+ description: "Sale condition: 01=Cash, 02=Credit, 03=Consignment, 04=Layaway, 99=Other"
637
+ },
638
+ medioPago: {
639
+ type: "array",
640
+ items: {
641
+ type: "string",
642
+ enum: ["01", "02", "03", "04", "05", "99"]
643
+ },
644
+ default: ["01"],
645
+ description: "Payment methods: 01=Cash, 02=Card, 03=Check, 04=Transfer, 05=Third-party, 99=Other"
646
+ },
647
+ plazoCredito: {
648
+ type: "string",
649
+ description: "Credit term in days (required when condicionVenta=02)"
650
+ },
651
+ lineItems: {
652
+ type: "array",
653
+ minItems: 1,
654
+ description: "Invoice line items",
655
+ items: {
656
+ type: "object",
657
+ required: ["codigoCabys", "cantidad", "unidadMedida", "detalle", "precioUnitario"],
658
+ properties: {
659
+ codigoCabys: {
660
+ type: "string",
661
+ pattern: "^\\d{13}$",
662
+ description: "CABYS product/service code (13 digits)"
663
+ },
664
+ cantidad: {
665
+ type: "number",
666
+ minimum: 0,
667
+ exclusiveMinimum: true,
668
+ description: "Quantity"
669
+ },
670
+ unidadMedida: {
671
+ type: "string",
672
+ description: 'Unit of measure: "Sp"=Service, "Unid"=Unit, "kg"=Kilogram, "m"=Meter, etc.'
673
+ },
674
+ detalle: {
675
+ type: "string",
676
+ maxLength: 200,
677
+ description: "Item description"
678
+ },
679
+ precioUnitario: {
680
+ type: "number",
681
+ minimum: 0,
682
+ description: "Unit price before taxes"
683
+ },
684
+ esServicio: {
685
+ type: "boolean",
686
+ default: false,
687
+ description: "true=service, false=merchandise"
688
+ },
689
+ impuesto: {
690
+ type: "array",
691
+ description: "Taxes to apply",
692
+ items: {
693
+ type: "object",
694
+ required: ["codigo", "tarifa"],
695
+ properties: {
696
+ codigo: {
697
+ type: "string",
698
+ description: "Tax code: 01=IVA, 02=Selective, etc."
699
+ },
700
+ codigoTarifa: {
701
+ type: "string",
702
+ description: "IVA rate code: 01=0%, 02=1%, 03=2%, 04=4%, 08=13%"
703
+ },
704
+ tarifa: {
705
+ type: "number",
706
+ description: "Tax rate percentage (e.g. 13 for 13%)"
707
+ }
708
+ }
709
+ }
710
+ },
711
+ descuento: {
712
+ type: "array",
713
+ description: "Discounts",
714
+ items: {
715
+ type: "object",
716
+ required: ["montoDescuento", "naturalezaDescuento"],
717
+ properties: {
718
+ montoDescuento: {
719
+ type: "number",
720
+ description: "Discount amount"
721
+ },
722
+ naturalezaDescuento: {
723
+ type: "string",
724
+ description: "Reason for discount"
725
+ }
726
+ }
727
+ }
728
+ }
729
+ }
730
+ }
731
+ }
732
+ }
733
+ };
734
+ function registerFacturaSchemaResource(server) {
735
+ server.resource(
736
+ "factura-schema",
737
+ "hacienda://schemas/factura",
738
+ {
739
+ description: "JSON Schema for the Factura Electronica (electronic invoice) input format. Use this to understand the required fields and structure for creating invoices.",
740
+ mimeType: "application/json"
741
+ },
742
+ async () => ({
743
+ contents: [
744
+ {
745
+ uri: "hacienda://schemas/factura",
746
+ mimeType: "application/json",
747
+ text: JSON.stringify(FACTURA_SCHEMA, null, 2)
748
+ }
749
+ ]
750
+ })
751
+ );
752
+ }
753
+
754
+ // src/resources/document-types.ts
755
+ import {
756
+ DocumentTypeCode,
757
+ DOCUMENT_TYPE_NAMES as DOCUMENT_TYPE_NAMES2,
758
+ SituationCode,
759
+ MensajeReceptorCode,
760
+ SALE_CONDITION_NAMES,
761
+ PAYMENT_METHOD_NAMES
762
+ } from "@dojocoding/hacienda-shared";
763
+ var REFERENCE_DATA = {
764
+ documentTypes: Object.entries(DOCUMENT_TYPE_NAMES2).map(([code, name]) => ({
765
+ code,
766
+ name,
767
+ description: getDocTypeDescription(code)
768
+ })),
769
+ situationCodes: [
770
+ { code: SituationCode.NORMAL, name: "Normal", description: "Standard submission" },
771
+ {
772
+ code: SituationCode.CONTINGENCIA,
773
+ name: "Contingencia",
774
+ description: "Contingency mode (system issues)"
775
+ },
776
+ {
777
+ code: SituationCode.SIN_INTERNET,
778
+ name: "Sin Internet",
779
+ description: "No internet connection at time of sale"
780
+ }
781
+ ],
782
+ mensajeReceptorCodes: [
783
+ {
784
+ code: MensajeReceptorCode.ACEPTADO,
785
+ name: "Aceptado totalmente",
786
+ description: "Fully accepted"
787
+ },
788
+ {
789
+ code: MensajeReceptorCode.ACEPTADO_PARCIALMENTE,
790
+ name: "Aceptado parcialmente",
791
+ description: "Partially accepted"
792
+ },
793
+ { code: MensajeReceptorCode.RECHAZADO, name: "Rechazado", description: "Rejected" }
794
+ ],
795
+ saleConditions: Object.entries(SALE_CONDITION_NAMES).map(([code, name]) => ({
796
+ code,
797
+ name
798
+ })),
799
+ paymentMethods: Object.entries(PAYMENT_METHOD_NAMES).map(([code, name]) => ({
800
+ code,
801
+ name
802
+ }))
803
+ };
804
+ function getDocTypeDescription(code) {
805
+ switch (code) {
806
+ case DocumentTypeCode.FACTURA_ELECTRONICA:
807
+ return "Standard electronic invoice. Requires receptor. Most common document type.";
808
+ case DocumentTypeCode.NOTA_DEBITO_ELECTRONICA:
809
+ return "Electronic debit note. References an existing invoice to add charges.";
810
+ case DocumentTypeCode.NOTA_CREDITO_ELECTRONICA:
811
+ return "Electronic credit note. References an existing invoice to apply credits/returns.";
812
+ case DocumentTypeCode.TIQUETE_ELECTRONICO:
813
+ return "Simplified electronic receipt. Receptor is optional. For consumers (B2C).";
814
+ case DocumentTypeCode.FACTURA_ELECTRONICA_COMPRA:
815
+ return "Purchase invoice. Issued when buying from unregistered suppliers.";
816
+ case DocumentTypeCode.FACTURA_ELECTRONICA_EXPORTACION:
817
+ return "Export invoice. For international sales. Receptor may use foreign ID.";
818
+ case DocumentTypeCode.RECIBO_ELECTRONICO_PAGO:
819
+ return "Electronic payment receipt. New in v4.4.";
820
+ default:
821
+ return "Document type.";
822
+ }
823
+ }
824
+ function registerDocumentTypesResource(server) {
825
+ server.resource(
826
+ "document-types",
827
+ "hacienda://reference/document-types",
828
+ {
829
+ description: "Reference data for Costa Rica electronic document types, situation codes, receiver message codes, sale conditions, and payment methods.",
830
+ mimeType: "application/json"
831
+ },
832
+ async () => ({
833
+ contents: [
834
+ {
835
+ uri: "hacienda://reference/document-types",
836
+ mimeType: "application/json",
837
+ text: JSON.stringify(REFERENCE_DATA, null, 2)
838
+ }
839
+ ]
840
+ })
841
+ );
842
+ }
843
+
844
+ // src/resources/tax-codes.ts
845
+ import {
846
+ TaxCode,
847
+ IvaRateCode,
848
+ IVA_RATE_PERCENTAGES,
849
+ ExonerationType,
850
+ UnitOfMeasure
851
+ } from "@dojocoding/hacienda-shared";
852
+ var TAX_REFERENCE = {
853
+ taxCodes: [
854
+ { code: TaxCode.IVA, name: "IVA", description: "Impuesto al Valor Agregado (Value Added Tax)" },
855
+ {
856
+ code: TaxCode.IMPUESTO_SELECTIVO_CONSUMO,
857
+ name: "Impuesto Selectivo de Consumo",
858
+ description: "Selective consumption tax"
859
+ },
860
+ {
861
+ code: TaxCode.IMPUESTO_UNICO_COMBUSTIBLES,
862
+ name: "Impuesto Unico a los Combustibles",
863
+ description: "Fuel tax"
864
+ },
865
+ {
866
+ code: TaxCode.IMPUESTO_BEBIDAS_ALCOHOLICAS,
867
+ name: "Impuesto Bebidas Alcoholicas",
868
+ description: "Alcoholic beverages tax"
869
+ },
870
+ {
871
+ code: TaxCode.IMPUESTO_BEBIDAS_SIN_ALCOHOL,
872
+ name: "Impuesto Bebidas sin Alcohol",
873
+ description: "Non-alcoholic beverages and toiletries tax"
874
+ },
875
+ { code: TaxCode.IMPUESTO_TABACO, name: "Impuesto al Tabaco", description: "Tobacco tax" },
876
+ {
877
+ code: TaxCode.IVA_CALCULO_ESPECIAL,
878
+ name: "IVA (calculo especial)",
879
+ description: "IVA with special calculation"
880
+ },
881
+ {
882
+ code: TaxCode.IVA_BIENES_USADOS,
883
+ name: "IVA Bienes Usados",
884
+ description: "IVA for used goods regime (factor method)"
885
+ },
886
+ {
887
+ code: TaxCode.IMPUESTO_CEMENTO,
888
+ name: "Impuesto al Cemento",
889
+ description: "Cement tax"
890
+ },
891
+ { code: TaxCode.OTROS, name: "Otros", description: "Other taxes" }
892
+ ],
893
+ ivaRateCodes: Object.entries(IVA_RATE_PERCENTAGES).map(([code, rate]) => ({
894
+ code,
895
+ rate,
896
+ name: getIvaRateName(code)
897
+ })),
898
+ exonerationTypes: [
899
+ { code: ExonerationType.COMPRAS_AUTORIZADAS, name: "Compras autorizadas" },
900
+ {
901
+ code: ExonerationType.VENTAS_EXENTAS_DIPLOMATICOS,
902
+ name: "Ventas exentas a diplomaticos"
903
+ },
904
+ {
905
+ code: ExonerationType.AUTORIZADO_LEY_ESPECIAL,
906
+ name: "Autorizado por ley especial"
907
+ },
908
+ {
909
+ code: ExonerationType.EXENCIONES_DGH,
910
+ name: "Exenciones de la Direccion General de Hacienda"
911
+ },
912
+ { code: ExonerationType.TRANSITORIO_V, name: "Transitorio V" },
913
+ { code: ExonerationType.TRANSITORIO_IX, name: "Transitorio IX" },
914
+ { code: ExonerationType.TRANSITORIO_XVII, name: "Transitorio XVII" },
915
+ { code: ExonerationType.OTROS, name: "Otros" }
916
+ ],
917
+ commonUnitsOfMeasure: [
918
+ { code: UnitOfMeasure.UNIDAD, name: "Unidad", description: "Unit (general merchandise)" },
919
+ {
920
+ code: UnitOfMeasure.SERVICIOS_PROFESIONALES,
921
+ name: "Servicios Profesionales",
922
+ description: "Professional services"
923
+ },
924
+ { code: UnitOfMeasure.HORAS, name: "Horas", description: "Hours" },
925
+ { code: UnitOfMeasure.KILOGRAMOS, name: "Kilogramos", description: "Kilograms" },
926
+ { code: UnitOfMeasure.LITROS, name: "Litros", description: "Liters" },
927
+ { code: UnitOfMeasure.METROS, name: "Metros", description: "Meters" },
928
+ {
929
+ code: UnitOfMeasure.METROS_CUADRADOS,
930
+ name: "Metros cuadrados",
931
+ description: "Square meters"
932
+ },
933
+ { code: UnitOfMeasure.METROS_CUBICOS, name: "Metros cubicos", description: "Cubic meters" },
934
+ { code: UnitOfMeasure.CAJAS, name: "Cajas", description: "Boxes" },
935
+ { code: UnitOfMeasure.PAQUETES, name: "Paquetes", description: "Packages" },
936
+ { code: UnitOfMeasure.GALONES, name: "Galones", description: "Gallons" },
937
+ { code: UnitOfMeasure.OTROS, name: "Otros", description: "Other (specify)" }
938
+ ],
939
+ notes: {
940
+ commonUsage: "For most services, use tax code '01' (IVA) with rate code '08' (13% general rate). Unit of measure for services is typically 'Sp' (Servicios Profesionales). For merchandise, use 'Unid' (Unidad).",
941
+ ivaExempt: "Some goods and services are exempt from IVA (rate code '01', 0%). The reduced rates (1%, 2%, 4%) apply to specific goods like basic foodstuffs."
942
+ }
943
+ };
944
+ function getIvaRateName(code) {
945
+ switch (code) {
946
+ case IvaRateCode.EXENTO:
947
+ return "Exento (0%)";
948
+ case IvaRateCode.REDUCIDA_1:
949
+ return "Tarifa reducida 1%";
950
+ case IvaRateCode.REDUCIDA_2:
951
+ return "Tarifa reducida 2%";
952
+ case IvaRateCode.REDUCIDA_4:
953
+ return "Tarifa reducida 4%";
954
+ case IvaRateCode.TRANSITORIO_0:
955
+ return "Transitorio 0%";
956
+ case IvaRateCode.TRANSITORIO_4:
957
+ return "Transitorio 4%";
958
+ case IvaRateCode.TRANSITORIO_8:
959
+ return "Transitorio 8%";
960
+ case IvaRateCode.GENERAL_13:
961
+ return "Tarifa general 13%";
962
+ default:
963
+ return `Code ${code}`;
964
+ }
965
+ }
966
+ function registerTaxCodesResource(server) {
967
+ server.resource(
968
+ "tax-codes",
969
+ "hacienda://reference/tax-codes",
970
+ {
971
+ description: "Reference data for Costa Rica tax codes, IVA rates, exoneration types, and units of measure used in electronic invoicing.",
972
+ mimeType: "application/json"
973
+ },
974
+ async () => ({
975
+ contents: [
976
+ {
977
+ uri: "hacienda://reference/tax-codes",
978
+ mimeType: "application/json",
979
+ text: JSON.stringify(TAX_REFERENCE, null, 2)
980
+ }
981
+ ]
982
+ })
983
+ );
984
+ }
985
+
986
+ // src/resources/id-types.ts
987
+ import {
988
+ IdentificationType,
989
+ IDENTIFICATION_TYPE_NAMES as IDENTIFICATION_TYPE_NAMES2,
990
+ IDENTIFICATION_LENGTHS
991
+ } from "@dojocoding/hacienda-shared";
992
+ var ID_TYPES_REFERENCE = {
993
+ identificationTypes: [
994
+ {
995
+ code: IdentificationType.CEDULA_FISICA,
996
+ name: IDENTIFICATION_TYPE_NAMES2[IdentificationType.CEDULA_FISICA],
997
+ lengths: IDENTIFICATION_LENGTHS[IdentificationType.CEDULA_FISICA],
998
+ description: "Physical person identification card. 9-digit number. Used for individual taxpayers (personas fisicas).",
999
+ example: "101230456"
1000
+ },
1001
+ {
1002
+ code: IdentificationType.CEDULA_JURIDICA,
1003
+ name: IDENTIFICATION_TYPE_NAMES2[IdentificationType.CEDULA_JURIDICA],
1004
+ lengths: IDENTIFICATION_LENGTHS[IdentificationType.CEDULA_JURIDICA],
1005
+ description: "Legal entity identification. 10-digit number. Used for companies and organizations (sociedades anonimas, etc.).",
1006
+ example: "3101234567"
1007
+ },
1008
+ {
1009
+ code: IdentificationType.DIMEX,
1010
+ name: IDENTIFICATION_TYPE_NAMES2[IdentificationType.DIMEX],
1011
+ lengths: IDENTIFICATION_LENGTHS[IdentificationType.DIMEX],
1012
+ description: "Foreign resident identification (Documento de Identidad Migratoria para Extranjeros). 11 or 12 digit number. Used for foreign residents in Costa Rica.",
1013
+ example: "12345678901"
1014
+ },
1015
+ {
1016
+ code: IdentificationType.NITE,
1017
+ name: IDENTIFICATION_TYPE_NAMES2[IdentificationType.NITE],
1018
+ lengths: IDENTIFICATION_LENGTHS[IdentificationType.NITE],
1019
+ description: "Tax identification for foreigners without DIMEX (Numero de Identificacion Tributaria Especial). 10-digit number.",
1020
+ example: "1234567890"
1021
+ }
1022
+ ],
1023
+ notes: {
1024
+ formatting: "Identification numbers must contain only digits, no dashes or spaces. The number must match the expected length for the given type.",
1025
+ usage: "The emisor (issuer) always requires identification. The receptor (receiver) identification is required for Factura Electronica but optional for Tiquete Electronico."
1026
+ }
1027
+ };
1028
+ function registerIdTypesResource(server) {
1029
+ server.resource(
1030
+ "id-types",
1031
+ "hacienda://reference/id-types",
1032
+ {
1033
+ description: "Reference data for Costa Rica identification types used in electronic invoicing. Includes type codes, expected lengths, descriptions, and examples.",
1034
+ mimeType: "application/json"
1035
+ },
1036
+ async () => ({
1037
+ contents: [
1038
+ {
1039
+ uri: "hacienda://reference/id-types",
1040
+ mimeType: "application/json",
1041
+ text: JSON.stringify(ID_TYPES_REFERENCE, null, 2)
1042
+ }
1043
+ ]
1044
+ })
1045
+ );
1046
+ }
1047
+
1048
+ // src/resources/index.ts
1049
+ function registerResources(server) {
1050
+ registerFacturaSchemaResource(server);
1051
+ registerDocumentTypesResource(server);
1052
+ registerTaxCodesResource(server);
1053
+ registerIdTypesResource(server);
1054
+ }
1055
+
1056
+ // src/server.ts
1057
+ var SERVER_NAME = "hacienda-cr";
1058
+ var SERVER_VERSION = "0.0.1";
1059
+ function createServer() {
1060
+ const server = new McpServer(
1061
+ {
1062
+ name: SERVER_NAME,
1063
+ version: SERVER_VERSION
1064
+ },
1065
+ {
1066
+ capabilities: {
1067
+ tools: {},
1068
+ resources: {}
1069
+ },
1070
+ instructions: "Hacienda CR MCP Server \u2014 tools and reference data for Costa Rica electronic invoicing (comprobantes electronicos). Use the tools to create invoices, check document status, and look up reference data. Resources provide schemas and reference tables for document types, tax codes, and identification types."
1071
+ }
1072
+ );
1073
+ registerTools(server);
1074
+ registerResources(server);
1075
+ return server;
1076
+ }
1077
+
1078
+ export {
1079
+ createServer
1080
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createServer
4
+ } from "./chunk-YJYSROUZ.js";
5
+
6
+ // src/cli.ts
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ async function main() {
9
+ const server = createServer();
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ }
13
+ main().catch((error) => {
14
+ console.error("Fatal error starting MCP server:", error);
15
+ process.exit(1);
16
+ });
@@ -0,0 +1,43 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+
3
+ /**
4
+ * MCP Server for Costa Rica electronic invoicing.
5
+ *
6
+ * Sets up an McpServer instance with all tools and resources registered.
7
+ * This module is the primary export — consumers can either use the
8
+ * `createServer()` factory (e.g. for testing) or run the stdio transport
9
+ * via the CLI entry point.
10
+ *
11
+ * @module server
12
+ */
13
+
14
+ /**
15
+ * Creates and configures the MCP server with all tools and resources.
16
+ *
17
+ * @returns A fully configured McpServer ready to be connected to a transport.
18
+ */
19
+ declare function createServer(): McpServer;
20
+
21
+ /**
22
+ * @dojocoding/hacienda-mcp
23
+ *
24
+ * MCP Server for Costa Rica electronic invoicing.
25
+ * Exposes SDK functionality as AI-accessible tools via the Model Context Protocol.
26
+ *
27
+ * Tools:
28
+ * - create_invoice — Build a Factura Electronica XML from structured input
29
+ * - check_status — Check document processing status by clave
30
+ * - list_documents — List recent electronic documents
31
+ * - get_document — Get full document details by clave
32
+ * - lookup_taxpayer — Look up taxpayer info by identification number
33
+ * - draft_invoice — Generate a draft invoice template with sensible defaults
34
+ *
35
+ * Resources:
36
+ * - hacienda://schemas/factura — JSON schema for invoice creation
37
+ * - hacienda://reference/document-types — Document types, codes, and descriptions
38
+ * - hacienda://reference/tax-codes — Tax codes, IVA rates, and units of measure
39
+ * - hacienda://reference/id-types — Identification types and validation rules
40
+ */
41
+ declare const PACKAGE_NAME: "@dojocoding/hacienda-mcp";
42
+
43
+ export { PACKAGE_NAME, createServer };
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-YJYSROUZ.js";
4
+
5
+ // src/index.ts
6
+ var PACKAGE_NAME = "@dojocoding/hacienda-mcp";
7
+ export {
8
+ PACKAGE_NAME,
9
+ createServer
10
+ };
package/package.json ADDED
@@ -0,0 +1 @@
1
+ {"name": "@iflow-mcp/dojocodinglabs-hacienda-cr", "version": "0.2.0", "description": "MCP Server for Costa Rica electronic invoicing (AI-accessible tools)", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "bin": {"iflow-mcp-dojocodinglabs-hacienda-cr": "./dist/cli.js"}, "exports": {".": {"import": "./dist/index.js", "types": "./dist/index.d.ts"}}, "files": ["dist"], "keywords": ["costa-rica", "hacienda", "electronic-invoicing", "factura-electronica", "comprobantes-electronicos", "mcp", "model-context-protocol"], "scripts": {"build": "tsup", "prepublishOnly": "pnpm run build", "test": "vitest run", "lint": "eslint src/", "typecheck": "tsc --noEmit", "clean": "rm -rf dist"}, "dependencies": {"@dojocoding/hacienda-sdk": "workspace:*", "@dojocoding/hacienda-shared": "workspace:*", "@modelcontextprotocol/sdk": "^1.26.0", "zod": "^4.3.6"}, "license": "MIT", "author": "Dojo Coding Labs <dev@dojocoding.io> (https://dojocoding.io)"}