AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP unreviewed MIT Self-run

Hacienda Cr

mcp-dojocodinglabs-hacienda-cr · by DojoCodingLabs

TypeScript SDK, CLI & MCP Server for Costa Rica electronic invoicing (Hacienda API)

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add mcp-dojocodinglabs-hacienda-cr

Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Reads credentials/environment and may exfiltrate them.

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Reliability & compatibility

Not yet reviewed
0 installs to date
no reviews yet
26d ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Hacienda Cr? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

hacienda-cr — Facturación Electrónica Costa Rica

El toolkit open-source más completo para facturación electrónica en Costa Rica.\ SDK + CLI + Servidor MCP para emitir comprobantes electrónicos contra la API v4.4 del Ministerio de Hacienda.

[](https://www.npmjs.com/package/@dojocoding/hacienda-sdk) [](https://github.com/DojoCodingLabs/hacienda-cr/actions/workflows/ci.yml) [](LICENSE) [](https://nodejs.org) [](https://www.typescriptlang.org)


¿Por qué hacienda-cr?

Emitir facturas electrónicas en Costa Rica no debería ser un dolor de cabeza. Entre la autenticación OAuth2, la generación de XML con namespaces específicos, la firma digital XAdES-EPES, la clave numérica de 50 dígitos y el polling del estado... hay demasiada complejidad accidental.

hacienda-cr resuelve todo eso en un solo toolkit:

  • SDK — Librería TypeScript con tipado estricto: auth, XML, firma digital, cálculo de IVA, envío y consulta.
  • CLI — Herramienta de línea de comandos hacienda para emitir, firmar, validar y consultar desde la terminal.
  • MCP Server — Servidor de Model Context Protocol para que asistentes de IA (Claude, etc.) emitan facturas por vos.

> Funciona con los 7 tipos de comprobante + Mensaje Receptor. Compatible con sandbox y producción.


Empezá en 2 minutos

Opción 1: SDK (para desarrolladores)

npm install @dojocoding/hacienda-sdk
import { HaciendaClient, DocumentType, Situation } from "@dojocoding/hacienda-sdk";

// 1. Crear el cliente
const client = new HaciendaClient({
  environment: "sandbox",
  credentials: {
    idType: "02", // Cédula Jurídica
    idNumber: "3101234567",
    password: process.env.HACIENDA_PASSWORD!,
  },
});

// 2. Autenticarse
await client.authenticate();

// 3. Generar la clave numérica
const clave = client.buildClave({
  date: new Date(),
  taxpayerId: "3101234567",
  documentType: DocumentType.FACTURA_ELECTRONICA,
  sequence: 1,
  situation: Situation.NORMAL,
});

// 4. Construir XML, firmar y enviar (ver ejemplo completo abajo)

Opción 2: CLI (para facturar desde la terminal)

npm install -g @dojocoding/hacienda-cli

# Autenticarse
hacienda auth login --cedula-type 02 --cedula 3101234567

# Crear borrador interactivo
hacienda draft --interactive

# Validar antes de enviar
hacienda validate factura.json

# Enviar (vista previa primero)
hacienda submit factura.json --dry-run

# Consultar contribuyente
hacienda lookup 3101234567

Opción 3: MCP Server (para asistentes de IA)

npm install -g @dojocoding/hacienda-mcp
hacienda-mcp

Le podés decir a Claude: "Creá una factura de Mi Empresa S.A. (cédula 3101234567) a Cliente S.R.L. (cédula 3109876543) por 2 horas de consultoría a ₡50.000 cada una con IVA del 13%."


Tipos de comprobante soportados

| Código | Tipo de comprobante | Builder del SDK | | ------ | ------------------------------------- | ------------------------------ | | 01 | Factura Electrónica | buildFacturaXml() | | 02 | Nota de Débito Electrónica | buildNotaDebitoXml() | | 03 | Nota de Crédito Electrónica | buildNotaCreditoXml() | | 04 | Tiquete Electrónico | buildTiqueteXml() | | 05 | Factura Electrónica de Compra | buildFacturaCompraXml() | | 06 | Factura Electrónica de Exportación | buildFacturaExportacionXml() | | 07 | Recibo Electrónico de Pago | buildReciboPagoXml() | | — | Mensaje Receptor (aceptación/rechazo) | buildMensajeReceptorXml() |


Tabla de contenidos

  • [SDK — Documentación completa](#sdk--documentación-completa)
  • [HaciendaClient](#haciendaclient)
  • [Autenticación OAuth2](#autenticación-oauth2)
  • [Creación de documentos](#creación-de-documentos)
  • [Cálculo de IVA](#cálculo-de-iva)
  • [Clave numérica](#clave-numérica)
  • [Firma digital XAdES-EPES](#firma-digital-xades-epes)
  • [Envío y consulta de estado](#envío-y-consulta-de-estado)
  • [Consulta de contribuyentes](#consulta-de-contribuyentes)
  • [Gestión de configuración](#gestión-de-configuración)
  • [Logging estructurado](#logging-estructurado)
  • [Manejo de errores](#manejo-de-errores)
  • [CLI — Referencia de comandos](#cli--referencia-de-comandos)
  • [MCP Server — Integración con IA](#mcp-server--integración-con-ia)
  • [Desarrollo](#desarrollo)
  • [Licencia](#licencia)

SDK — Documentación completa

HaciendaClient

El punto de entrada principal. Orquesta autenticación, generación de claves y operaciones con la API.

import { HaciendaClient } from "@dojocoding/hacienda-sdk";

const client = new HaciendaClient({
  // Requerido
  environment: "sandbox", // "sandbox" | "production"
  credentials: {
    idType: "02", // "01"=Física, "02"=Jurídica, "03"=DIMEX, "04"=NITE
    idNumber: "3101234567", // Cédula de 9-12 dígitos
    password: process.env.HACIENDA_PASSWORD!,
  },

  // Opcional
  p12Path: "/ruta/al/certificado.p12", // Para firma digital
  p12Pin: process.env.HACIENDA_P12_PIN, // PIN del .p12
  fetchFn: customFetch, // Implementación fetch personalizada
});

Las opciones se validan al instanciar con Zod. Si algo está mal, lanza ValidationError con detalles claros.

Autenticación OAuth2

Hacienda usa OAuth2 ROPC (Resource Owner Password Credentials). El SDK maneja todo el ciclo de vida del token automáticamente.

// Autenticarse (obtiene access + refresh token)
await client.authenticate();

// Verificar estado
console.log(client.isAuthenticated); // true

// Obtener token válido (refresca automáticamente si expiró)
const token = await client.getAccessToken();

// Forzar re-autenticación
client.invalidate();
await client.authenticate();

Ciclo de vida del token:

  • Access token expira en ~5 minutos (se cachea en memoria, se refresca 30s antes)
  • Refresh token dura ~10 horas
  • getAccessToken() maneja el refresh de forma transparente

Ambientes de Hacienda:

| Ambiente | URL base de la API | IDP Realm | Client ID | | ------------ | ---------------------------------------------------------- | ---------- | ---------- | | sandbox | api.comprobanteselectronicos.go.cr/recepcion-sandbox/v1/ | rut-stag | api-stag | | production | api.comprobanteselectronicos.go.cr/recepcion/v1/ | rut | api-prod |

Creación de documentos

Ejemplo completo de una Factura Electrónica — el flujo es igual para los demás tipos:

import {
  buildFacturaXml,
  calculateLineItemTotals,
  calculateInvoiceSummary,
  buildClave,
  DocumentType,
  Situation,
} from "@dojocoding/hacienda-sdk";
import type { LineItemInput } from "@dojocoding/hacienda-sdk";

// 1. Definir las líneas de detalle
const lineas: LineItemInput[] = [
  {
    numeroLinea: 1,
    codigoCabys: "8310100000000", // Código CABYS (13 dígitos)
    cantidad: 2,
    unidadMedida: "Unid",
    detalle: "Servicios de desarrollo web",
    precioUnitario: 50000,
    esServicio: true,
    impuesto: [
      {
        codigo: "01", // IVA
        codigoTarifaIVA: "08", // Tarifa general 13%
        tarifa: 13,
      },
    ],
  },
  {
    numeroLinea: 2,
    codigoCabys: "4321000000000",
    cantidad: 1,
    unidadMedida: "Unid",
    detalle: "Laptop",
    precioUnitario: 500000,
    esServicio: false,
    impuesto: [
      {
        codigo: "01",
        codigoTarifaIVA: "08",
        tarifa: 13,
      },
    ],
    descuento: [
      {
        montoDescuento: 25000,
        codigoDescuento: "01",
        naturalezaDescuento: "Descuento por volumen",
      },
    ],
  },
];

// 2. Calcular totales por línea (agrega montoTotal, subTotal, impuestoNeto, etc.)
const lineasCalculadas = lineas.map(calculateLineItemTotals);

// 3. Calcular resumen de factura (ResumenFactura)
const resumen = calculateInvoiceSummary(lineasCalculadas);

// 4. Generar la clave numérica
const clave = buildClave({
  date: new Date(),
  taxpayerId: "3101234567",
  documentType: DocumentType.FACTURA_ELECTRONICA,
  sequence: 1,
  situation: Situation.NORMAL,
});

// 5. Consecutivo
const numeroConsecutivo = "00100001010000000001";

// 6. Armar la factura y generar XML
const factura = {
  clave,
  proveedorSistemas: "3101234567", // Cédula del proveedor de sistemas (v4.4)
  codigoActividadEmisor: "620100",
  numeroConsecutivo,
  fechaEmision: new Date().toISOString(),
  emisor: {
    nombre: "Mi Empresa S.A.",
    identificacion: { tipo: "02", numero: "3101234567" },
    ubicacion: {
      provincia: "1",
      canton: "01",
      distrito: "01",
      otrasSenas: "100m norte del parque central",
    },
    correoElectronico: "facturacion@miempresa.co.cr",
  },
  receptor: {
    nombre: "Cliente S.R.L.",
    identificacion: { tipo: "02", numero: "3109876543" },
    correoElectronico: "pagos@cliente.co.cr",
  },
  condicionVenta: "01", // Contado
  detalleServicio: lineasCalculadas,
  resumenFactura: {
    ...resumen,
    // v4.4: los medios de pago van dentro del ResumenFactura, con monto
    medioPago: [{ tipoMedioPago: "01", totalMedioPago: resumen.totalComprobante }],
  },
};

const xml = buildFacturaXml(factura);

Validación de XML:

import { validateFacturaInput } from "@dojocoding/hacienda-sdk";

const resultado = validateFacturaInput(datosFactura);
if (!resultado.valid) {
  for (const err of resultado.errors) {
    console.error(`${err.path}: ${err.message}`);
  }
}

Cálculo de IVA

Utilidades para calcular impuestos, totales por línea y resúmenes según la normativa de Hacienda. Todos los montos se redondean a 5 decimales.

import { round5, calculateLineItemTotals, calculateInvoiceSummary } from "@dojocoding/hacienda-sdk";
import type { LineItemInput, CalculatedLineItem, InvoiceSummary } from "@dojocoding/hacienda-sdk";

const item: LineItemInput = {
  numeroLinea: 1,
  codigoCabys: "8310100000000",
  cantidad: 3,
  unidadMedida: "Sp",
  detalle: "Horas de consultoría",
  precioUnitario: 75000,
  esServicio: true,
  impuesto: [{ codigo: "01", codigoTarifaIVA: "08", tarifa: 13 }],
};

const calculado: CalculatedLineItem = calculateLineItemTotals(item);
// calculado.montoTotal      = 225000       (3 × ₡75.000)
// calculado.subTotal        = 225000       (sin descuentos)
// calculado.impuestoNeto    = 29250        (₡225.000 × 13%)
// calculado.montoTotalLinea = 254250       (₡225.000 + ₡29.250)

const resumen: InvoiceSummary = calculateInvoiceSummary([calculado]);
// resumen.totalServGravados  = 225000
// resumen.totalImpuesto      = 29250
// resumen.totalComprobante   = 254250

Exoneraciones de IVA:

const itemExonerado: LineItemInput = {
  // ...campos base
  impuesto: [
    {
      codigo: "01",
      codigoTarifaIVA: "08",
      tarifa: 13,
      exoneracion: {
        tipoDocumento: "01",
        numeroDocumento: "AL-001-2025",
        nombreInstitucion: "99", // código de institución (Nota v4.4)
        fechaEmision: "2025-01-01T00:00:00",
        tarifaExonerada: 13, // puntos de tarifa exonerados
      },
    },
  ],
};

Tarifas de IVA soportadas: 0%, 0.5%, 1%, 2%, 4%, 8%, 13% (códigos 01-11 de la v4.4)

Clave numérica

Cada comprobante electrónico requiere una clave numérica única de 50 dígitos. El SDK la genera y parsea automáticamente.

Estructura: [506][DDMMYY][cédula 12 dígitos][sucursal 3][terminal 5][tipo doc 2][consecutivo 10][situación 1][código seguridad 8]

import { buildClave, parseClave, DocumentType, Situation } from "@dojocoding/hacienda-sdk";

// Generar clave
const clave = buildClave({
  date: new Date("2025-07-15"),
  taxpayerId: "3101234567",
  documentType: DocumentType.FACTURA_ELECTRONICA,
  sequence: 42,
  situation: Situation.NORMAL,
  branch: "001", // Opcional, default "001"
  pos: "00001", // Opcional, default "00001"
});
// => "50615072500310123456700100001010000000042112345678"

// Parsear clave existente
const parsed = parseClave(clave);
// parsed.countryCode   => "506"
// parsed.date          => Date(2025-07-15)
// parsed.taxpayerId    => "003101234567"
// parsed.documentType  => "01"
// parsed.sequence      => 42
// parsed.situation     => "1"
// parsed.securityCode  => "12345678"

Códigos de situación:

  • 1 Normal (envío estándar en línea)
  • 2 Contingencia (fallo del sistema de Hacienda)
  • 3 Sin Internet (fuera de línea)

Firma digital XAdES-EPES

Todo XML enviado a Hacienda debe estar firmado con XAdES-EPES usando el certificado .p12 del contribuyente (RSA 2048 + SHA-256). El SDK maneja todo el proceso de firma.

import { readFileSync } from "node:fs";
import { signXml, signAndEncode, loadP12 } from "@dojocoding/hacienda-sdk";

const p12Buffer = readFileSync("/ruta/al/certificado.p12");
const pin = process.env.HACIENDA_P12_PIN!;

// Firmar XML (retorna XML firmado como string)
const xmlFirmado = await signXml(xml, p12Buffer, pin);

// Firmar y codificar en Base64 (listo para enviar a la API)
const xmlBase64 = await signAndEncode(xml, p12Buffer, pin);

// Cargar .p12 para inspeccionar el certificado
const credenciales = await loadP12(p12Buffer, pin);
// credenciales.privateKey      — CryptoKey para firma
// credenciales.certificateDer  — Certificado codificado en DER

Envío y consulta de estado

Opción simplificada — submitAndWait (recomendada):

Envía el documento y espera a que Hacienda lo procese. Maneja el polling automáticamente.

import { submitAndWait, HttpClient } from "@dojocoding/hacienda-sdk";

const httpClient = new HttpClient({
  baseUrl: "https://api.comprobanteselectronicos.go.cr/recepcion-sandbox/v1",
  getToken: () => client.getAccessToken(),
});

const resultado = await submitAndWait(
  httpClient,
  {
    clave: "50601...",
    fecha: new Date().toISOString(),
    emisor: {
      tipoIdentificacion: "02",
      numeroIdentificacion: "3101234567",
    },
    comprobanteXml: xmlBase64Firmado,
  },
  {
    pollIntervalMs: 3000, // Consultar cada 3 segundos (default)
    timeoutMs: 60000, // Timeout a 60 segundos (default)
    onPoll: (status, intento) => {
      console.log(`Intento ${intento}: ${status.status}`);
    },
  },
);

if (resultado.accepted) {
  console.log("¡Comprobante aceptado por Hacienda!");
} else {
  console.log("Rechazado:", resultado.rejectionReason);
}

Opción granular — control total:

import { submitDocument, getStatus, isTerminalStatus } from "@dojocoding/hacienda-sdk";

// Enviar
const response = await submitDocument(httpClient, solicitud);

// Consultar estado
const status = await getStatus(httpClient, "50601...");
if (isTerminalStatus(status.status)) {
  console.log("Estado final:", status.status);
}

Listar y consultar comprobantes:

import { listComprobantes, getComprobante } from "@dojocoding/hacienda-sdk";

const lista = await listComprobantes(httpClient, {
  offset: 0,
  limit: 10,
  fechaEmisionDesde: "2025-01-01",
  fechaEmisionHasta: "2025-12-31",
});

const detalle = await getComprobante(httpClient, "50601...");

Reintentos con backoff exponencial:

import { withRetry } from "@dojocoding/hacienda-sdk";

const resultado = await withRetry(() => submitDocument(httpClient, solicitud), {
  maxAttempts: 3,
  delayMs: 1000,
  backoff: "exponential",
});

Consulta de contribuyentes

Buscá información de cualquier contribuyente usando la API pública de actividades económicas de Hacienda (no requiere autenticación):

import { lookupTaxpayer } from "@dojocoding/hacienda-sdk";

const info = await lookupTaxpayer("3101234567");
console.log(info.nombre); // "MI EMPRESA S.A."
console.log(info.tipoIdentificacion); // "02"
for (const actividad of info.actividades) {
  console.log(`${actividad.codigo}: ${actividad.descripcion} (${actividad.estado})`);
}

Gestión de configuración

La configuración se almacena en ~/.hacienda-cr/config.toml con soporte para múltiples perfiles (ej: sandbox, producci

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.