ohneben's Wafeq MCP

ohneben's Wafeq MCP: gestiona tus libros de Wafeq desde Claude, Cursor o cualquier cliente MCP: los 251 endpoints de la API como herramientas MCP categorizadas por seguridad, a través de stdio o Streamable HTTP, en Docker.

Documentación

MCP de Wafeq de ohneben

Buy Me A Coffee


Licencia y comprobaciones

CI License: MIT

Registros MCP

MCP Registry Listed on mcpservers.org Wafeq-MCP MCP server

Gestiona tus libros de Wafeq en lenguaje natural desde asistentes de IA como Claude, Cursor y cualquier otro cliente MCP.

Este servidor de Protocolo de Contexto de Modelo expone la API pública de Wafeq — los 251 endpoints completos, generados directamente desde la especificación OpenAPI como herramientas MCP, más dos escritas a mano. Cada herramienta lleva una categoría de seguridad (🟢 solo lectura / 🟡 escritura / 🟠 cambio de estado / 🔴 irreversible o destructiva) para que tu asistente sepa qué hace una acción antes de llamarla — incluyendo la diferencia entre guardar una factura y presentarla ante una autoridad fiscal, algo que ningún envoltorio tipo CRUD puede decirte. Funciona sobre stdio (Claude Desktop y otros lanzadores locales) o HTTP Streamable (alojado en Docker), e incluye reintentos, limitación de velocidad en el cliente, tiempos de espera de solicitud, claves de idempotencia, carga multiparte y manejo de PDF binarios para que resista un libro real.

Por qué lo querrás

Algunos servidores MCP solo reenvían una API. Este está construido para ser seguro de entregar a un LLM y fácil de ejecutar contra datos contables reales:

Lo que obtienesPor qué importa
Los 251 endpoints, impulsados por especificaciónCobertura completa de facturas, recibos, cotizaciones, notas de crédito y débito, pagos, banca, diarios, nómina, proyectos, inventario e informes — nada seleccionado a mano ni omitido.
Nueve categorías de seguridad, no cuatro 🟢 / 🟡 / 🟠 / 🔴Una docena de los POST de Wafeq no son creaciones. Las vistas previas no escriben nada; finalizar una amortización anticipadamente publica en el libro mayor sin deshacer; reportar una factura a una autoridad fiscal deja tu organización permanentemente. Cada una tiene su propio aviso en lugar de agruparse con "crear".
Instrucciones del servidor enviadas al conectarAl cliente se le indica cómo leer los avisos de seguridad y las pocas convenciones de Wafeq — formato de fecha, separador decimal, rangos de informes de período completo — de antemano, en lugar de descubrirlas cometiendo un error primero.
Anotaciones MCP legibles por máquina (readOnlyHint, destructiveHint)Los hosts que respetan anotaciones (incluido Claude) pueden confiar automáticamente en las 98 herramientas de solo lectura y exigir confirmación antes de cualquiera de las 44 que eliminan o no se pueden deshacer.
Parámetros de informe correctos, por informeCada uno de los cuatro informes tiene su propio esquema: el balance general toma date + period_count; pérdidas y ganancias y flujo de caja toman date_after + date_before; la balanza de comprobación toma from_date + to_date. Wafeq ignora silenciosamente los parámetros de consulta mal escritos, por lo que un nombre incorrecto parece una llamada que funciona.
Validación de período completo antes de enviarPérdidas y ganancias y flujo de caja rechazan rangos que no se alinean con meses o años completos. El servidor verifica localmente y responde con el rango válido más cercano en lugar de gastar un viaje de ida y vuelta en un HTTP 400.
Claves de idempotencia automáticasCada uno de los 146 endpoints de escritura que admiten X-Wafeq-Idempotency-Key recibe un UUID v4 automáticamente, reutilizado entre reintentos — para que un contratiempo de red nunca duplique una factura. Proporciona la tuya propia para que una re-ejecución deliberada también sea segura.
Cargas de archivos que realmente funcionanPOST /files/ es solo multiparte y POST /files/raw/ necesita un encabezado Content-Disposition. Ambos se manejan; pasas contenido base64 y un nombre de archivo.
PDF binarios manejados como bytesLos nueve endpoints de PDF se codifican en base64 dentro de un pequeño envoltorio con tamaño y tipo de contenido, en lugar de leerse como texto y corromperse.
Reintentos automáticos con retrocesoLas respuestas transitorias 429 / 5xx se reintentan con retroceso exponencial con jitter, respetando Retry-After — con la misma clave de idempotencia, exactamente como exige la guía de integración de Wafeq.
Limitación de velocidad integradaSe auto-limita para que una ráfaga de llamadas de herramientas no dispare un 429. Wafeq no publica un límite numérico, por lo que el predeterminado es deliberadamente conservador y configurable.
Inquilino verificado al inicioUna clave de API de Wafeq está limitada a la organización. El servidor llama a GET /organization/ antes de servir y publica el resultado en /health, para que una clave mal configurada aparezca como un nombre que puedes verificar en lugar de escrituras contra los libros de la empresa equivocada.
Dos transportes: stdio y HTTP StreamableÚsalo localmente en Claude Desktop, o ejecuta un servidor siempre activo al que cualquier número de clientes MCP llegue por HTTP.
Docker + docker-compose, verificación de salud, reinicio automáticodocker compose up y permanece activo, vinculado solo a localhost.
Autenticación opcional con token portador en el endpoint HTTPPon el servidor detrás de un secreto compartido en cuanto sea accesible más allá de localhost.
Tus secretos nunca llegan al modeloLas credenciales viven en el entorno del servidor y se inyectan en cada solicitud. La herramienta de paso directo no puede anular Authorization ni apuntar la credencial a otro host.
Actualizaciones de especificación sin fricción¿Wafeq publica una especificación más nueva? Reemplaza un archivo y reconstruye — los nuevos endpoints se convierten automáticamente en nuevas herramientas, sin cambios de código.

Cómo se compara

CapacidadEste proyectoEnvoltorio genérico OpenAPI→MCP*
Los 251 endpoints de Wafeq como herramientas
Categoría de seguridad + aviso por herramienta
Presentación ante autoridad fiscal marcada como irreversible, no "crear"
Anotaciones MCP readOnlyHint / destructiveHint
Campos de solo lectura eliminados de los cuerpos de crear/actualizar
Prosa de enumeraciones duplicada compactada fuera de los esquemas
Parámetros de fecha correctos, por informe
Rango de período completo validado antes de enviar
X-Wafeq-Idempotency-Key automático, estable entre reintentos
Carga de archivos multiparte + binario crudo
Respuestas PDF binarias codificadas en base64, no dañadas
Fechas de transacción recuperadas para partidas de diario
Reintentos automáticos en 429 / 5xx (respeta Retry-After)
Limitación de velocidad en el cliente
Identidad de la organización verificada al inicio
Transporte stdio
Transporte HTTP Streamable
Docker + docker-compose, verificación de salud, reinicio automático
Autenticación opcional con token portador en el endpoint
LicenciaMITvaría

*Los envoltorios genéricos OpenAPI→MCP convierten cualquier especificación en herramientas MCP. Pueden alcanzar los mismos endpoints, pero tratan cada operación de manera idéntica — y contra la especificación de Wafeq específicamente heredan el problema de campos obligatorios de solo lectura descrito en MIGRATION.md. "➖" = varía según la herramienta / no garantizado.

Lo que puedes hacer

Una vez conectado, pregúntale a tu asistente cosas como:

  • "¿Cuál fue nuestra pérdida y ganancia para la primera mitad de este año?"
  • "Muéstrame todas las facturas impagas de más de 30 días, con el nombre del cliente."
  • "Crea un borrador de factura para Acme Ltd por 3 días de consultoría a €800/día."
  • "Descarga la factura INV-2026-014 como PDF."
  • "¿En qué cuenta se registró la transferencia de €7,000 de enero?"
  • "Adjunta este recibo al gasto EXP-118."
  • "Concilia las líneas del extracto bancario de marzo contra el libro mayor."
  • "Convierte la cotización QUO-31 en una factura y registra el pago."

Cómo funciona

Claude / Cursor / any MCP client  ──MCP──►  this server  ──HTTPS──►  Wafeq API (your organization)

Al inicio, el servidor analiza la especificación OpenAPI incluida en herramientas MCP — resolviendo $refs, protegiéndose contra esquemas recursivos y eliminando campos asignados por el servidor (readOnly) de los cuerpos de solicitud — etiqueta cada herramienta con su categoría de seguridad, verifica a qué organización de Wafeq pertenecen las credenciales y luego inyecta tu credencial en cada solicitud saliente. Tu clave permanece en el entorno del servidor; el modelo nunca la ve ni la maneja.

Requisitos

  • Una organización de Wafeq con acceso a API — ya sea una clave de API privada (Wafeq → Configuración → Desarrollador → Claves de API) o un token de acceso OAuth2. Consulta Obtén tus credenciales de API.
  • Docker (Docker Desktop en macOS/Windows) para el inicio rápido a continuación — o Node.js ≥ 20 para ejecutar desde el código fuente.

Inicio rápido (Docker)

1. Agrega tus credenciales. Copia la configuración de ejemplo y complétala:

cp .env.example .env

Luego edita .env y establece WAFEQ_API_KEY. Si el servidor será accesible más allá de localhost, establece también MCP_SHARED_TOKEN con una cadena aleatoria larga.

2. Inicia el servidor:

docker compose up -d --build

docker-compose.yml se vincula solo a 127.0.0.1:8765, por lo que el servidor es accesible desde tu máquina pero no desde la red.

3. Confirma que está funcionando — y que apunta a los libros correctos:

curl -s http://localhost:8765/health
{
  "status": "ok",
  "server": "wafeq-mcp",
  "version": "2.0.0",
  "tools": 253,
  "organization": {
    "status": "ok",
    "id": "org_...",
    "name": "Your Company FZCO",
    "base_currency": "EUR",
    "country": "AE"
  },
  "auth_required": false
}

Revisa el campo name. Esa es la organización a la que tu clave escribe. Si no es la empresa que esperabas, detente y corrige la clave antes de hacer cualquier otra cosa. /health responde 503 y "status": "degraded" cuando las credenciales no se pueden verificar.

4. Apunta tu cliente MCP hacia él: http://localhost:8765/mcp (HTTP Streamable).

Los endpoints remotos se agregan a Claude como un conector personalizado (Configuración → Conectores), o se puentean localmente con mcp-remote. Para el puente, agrega esto bajo mcpServers en la configuración de tu cliente y reinicia la aplicación por completo:

{
  "mcpServers": {
    "wafeq": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://localhost:8765/mcp",
        "--header", "Authorization: Bearer YOUR_MCP_SHARED_TOKEN"
      ]
    }
  }
}

(Elimina la línea --header si dejaste MCP_SHARED_TOKEN vacío.)

¿Prefieres una imagen lista?

Cada versión publica una imagen lista para ejecutar en el Registro de Contenedores de GitHub, por lo que puedes omitir la compilación local por completo:

docker run -d --name wafeq-mcp -p 127.0.0.1:8765:8765 --env-file .env \
  ghcr.io/ohneben/wafeq-mcp:latest

Fija una versión (:2.0.0) en lugar de latest si quieres que las versiones sean algo a lo que te adhieres deliberadamente.

Instalar desde el Registro MCP

El servidor está publicado en el Registro MCP como io.github.ohneben/wafeq-mcp, por lo que los clientes que reconocen el registro pueden instalarlo por nombre. La entrada del registro lanza la imagen sobre stdio — consulta Ejecutar el contenedor sobre stdio para la configuración escrita a mano equivalente.

curl -s "https://registry.modelcontextprotocol.io/v0.1/servers/io.github.ohneben%2Fwafeq-mcp/versions/latest"

Obtén tus credenciales de API

Clave de API privada (la mayoría de las personas): en Wafeq, ve a Configuración → Desarrollador → Claves de API y crea una clave. Está limitada a una organización. Ponla en .env como WAFEQ_API_KEY; el servidor la envía como Authorization: Api-Key <key>.

Aplicación OAuth2: si tienes un token de acceso de una aplicación OAuth2 de Wafeq, ponlo en .env como WAFEQ_ACCESS_TOKEN en su lugar. El servidor cambia a Authorization: Bearer <token> automáticamente. Establece WAFEQ_AUTH_SCHEME solo si necesitas forzar un esquema mientras ambas variables están presentes.

Configuración

Toda la configuración es mediante variables de entorno. Todo excepto la credencial tiene un valor predeterminado funcional.

VariableDefaultWhat it does
WAFEQ_API_KEYClave API privada de la organización. Se envía como Api-Key <key>. Se requiere una credencial.
WAFEQ_ACCESS_TOKENToken de acceso OAuth2. Se envía como Bearer <token>. Tiene prioridad sobre WAFEQ_API_KEY.
WAFEQ_AUTH_SCHEMEautoForzar api-key o bearer. Normalmente déjalo sin configurar.
WAFEQ_API_BASE_URLhttps://api.wafeq.com/v1URL base de la API de Wafeq.
WAFEQ_OPENAPI_PATHspec incluidaUsar un documento OpenAPI diferente (JSON o YAML).
MCP_TRANSPORTstdiostdio o http. Docker establece http.
PORT8765Puerto de escucha HTTP.
HOST0.0.0.0Dirección de enlace HTTP.
MCP_HTTP_PATH/mcpRuta donde se sirve el endpoint de MCP.
MCP_SHARED_TOKENToken Bearer requerido en /mcp. Vacío = sin autenticación. Configúralo si el puerto es accesible más allá de localhost.
WAFEQ_TOOL_GROUPSGrupos de recursos separados por comas para exponer, p. ej. invoices,bills,reports. Vacío = los 251. Ejecuta npm run list-tools para la lista.
WAFEQ_MAX_REQUESTS20Límite de tasa del lado del cliente: solicitudes por ventana. 0 desactiva la limitación.
WAFEQ_RATE_WINDOW_MS10000Ventana del límite de tasa en milisegundos.
WAFEQ_MAX_RETRIES3Reintentos en 429 / 5xx / errores de red.
WAFEQ_TIMEOUT_MS30000Tiempo de espera de solicitud por intento.
WAFEQ_ALLOW_LOCAL_FILE_UPLOADfalsePermitir que las herramientas de carga lean el sistema de archivos de esta máquina mediante file_path. Consulta Seguridad.
WAFEQ_MAX_UPLOAD_BYTES26214400Tamaño máximo de carga decodificada (25 MiB).

¿Demasiadas herramientas?

251 herramientas es mucho. El catálogo completo tiene aproximadamente 0.5 MB de JSON (~133k tokens) en tools/list, y algunos hosts se vuelven más lentos o menos precisos con tantas. Dos cosas ayudan.

Los esquemas ya están compactados. La especificación de Wafeq renderiza los valores de cada enum en su descripción además de en enum — la lista de monedas sola tiene ~4 KB, insertada en 203 lugares. El generador colapsa esos envoltorios de allOf de un solo miembro y elimina las listas con viñetas duplicadas, lo que reduce ~29% la carga útil sin eliminar un solo valor permitido.

Reduce el catálogo si aún lo quieres más pequeño — sin cambios de código:

WAFEQ_TOOL_GROUPS=invoices,bills,contacts,payments,reports,accounts,items,tax-rates

Las dos herramientas escritas a mano están siempre disponibles, así que nada queda inalcanzable — cualquier cosa que filtres aún se puede llamar mediante wafeq_request.

Categorías de seguridad de herramientas

La descripción de cada herramienta comienza con un banner, y cada herramienta lleva las anotaciones MCP correspondientes. Los conteos son para la especificación incluida (251 generadas + 2 escritas a mano = 253).

BannerHerramientasreadOnlyHintdestructiveHintQué cubre
🟢 READ-ONLY85Cada GET, más la herramienta de conveniencia del libro mayor de cuentas.
🟢 READ-ONLY · returns a PDF9Las descargas PDF: factura, factura simplificada, nota de crédito, nota de débito, factura de compra, cotización, orden de compra, pago, recibo de nómina. Devueltas codificadas en base64.
🟢 READ-ONLY · preview / simulation4Vistas previas de amortización y reconocimiento de ingresos. POST, pero documentadas como que no escriben nada.
🟡 WRITE · creates data39Creaciones de colecciones, ambas cargas de archivos y las dos conversiones (cotización→factura, orden de compra→factura de compra). No idempotentes por naturaleza — de ahí la clave de idempotencia automática.
🟡 WRITE · updates data70Cada PUT y PATCH.
🟠 STATE CHANGE · moves a document in or out of the ledger2Marcar gasto como publicado / borrador. Reversible — cada uno deshace al otro.
🔴 IRREVERSIBLE · files the document with an external tax authority3Reportar factura / nota de crédito / factura simplificada a la autoridad fiscal. Deja tu organización y no se puede recuperar.
🔴 IRREVERSIBLE · posts the remaining balance to the ledger2Terminar amortización / reconocimiento de ingresos antes de tiempo. Sin deshacer por API — ejecuta la vista previa correspondiente primero.
🔴 DESTRUCTIVE · deletes39Cada DELETE, más el paso directo de wafeq_request (su efecto no se puede conocer de antemano).
2539844

Los tres grupos 🔴 todos establecen destructiveHint: true, así que un host que respeta las anotaciones se detiene y pregunta antes de cualquiera de ellos — no solo antes de eliminaciones. Presentar una factura ante una autoridad fiscal es al menos tan importante como eliminar una, y a diferencia de una eliminación, alcanza fuera de tu organización.

Imprime el catálogo en vivo en cualquier momento, sin credenciales:

npm run list-tools
🟢 SOLO LECTURA (85)
HerramientaEndpoint
wafeq_account_ledgerescrita a mano
wafeq_accounts_listGET /accounts/
wafeq_accounts_retrieveGET /accounts/{id}/
wafeq_amortizations_listGET /amortizations/
wafeq_amortizations_retrieveGET /amortizations/{id}/
wafeq_bank_accounts_ledger_transactions_listGET /bank-accounts/{bank_account_id}/ledger-transactions/
wafeq_bank_accounts_ledger_transactions_retrieveGET /bank-accounts/{bank_account_id}/ledger-transactions/{id}/
wafeq_bank_accounts_listGET /bank-accounts/
wafeq_bank_accounts_retrieveGET /bank-accounts/{id}/
wafeq_bank_accounts_statement_transactions_listGET /bank-accounts/{bank_account_id}/statement-transactions/
wafeq_bank_accounts_statement_transactions_retrieveGET /bank-accounts/{bank_account_id}/statement-transactions/{id}/
wafeq_beneficiaries_listGET /beneficiaries/
wafeq_beneficiaries_retrieveGET /beneficiaries/{id}/
wafeq_bills_line_items_listGET /bills/{bill_id}/line-items/
wafeq_bills_line_items_retrieveGET /bills/{bill_id}/line-items/{id}/
wafeq_bills_listGET /bills/
wafeq_bills_retrieveGET /bills/{id}/
wafeq_branches_listGET /branches/
wafeq_branches_retrieveGET /branches/{id}/
wafeq_contacts_listGET /contacts/
wafeq_contacts_retrieveGET /contacts/{id}/
wafeq_cost_centers_listGET /cost-centers/
wafeq_cost_centers_retrieveGET /cost-centers/{id}/
wafeq_credit_notes_line_items_listGET /credit-notes/{credit_note_id}/line-items/
wafeq_credit_notes_line_items_retrieveGET /credit-notes/{credit_note_id}/line-items/{id}/
wafeq_credit_notes_listGET /credit-notes/
wafeq_credit_notes_retrieveGET /credit-notes/{id}/
wafeq_custom_fields_listGET /custom-fields/
wafeq_custom_fields_retrieveGET /custom-fields/{id}/
wafeq_debit_notes_line_items_listGET /debit-notes/{debit_note_id}/line-items/
wafeq_debit_notes_line_items_retrieveGET /debit-notes/{debit_note_id}/line-items/{id}/
wafeq_debit_notes_listGET /debit-notes/
wafeq_debit_notes_retrieveGET /debit-notes/{id}/
wafeq_employees_listGET /employees/
wafeq_employees_retrieveGET /employees/{id}/
wafeq_expenses_listGET /expenses/
wafeq_expenses_retrieveGET /expenses/{id}/
wafeq_files_listGET /files/
wafeq_files_retrieveGET /files/{id}/
wafeq_invoices_line_items_listGET /invoices/{invoice_id}/line-items/
wafeq_invoices_line_items_retrieveGET /invoices/{invoice_id}/line-items/{id}/
wafeq_invoices_listGET /invoices/
wafeq_invoices_retrieveGET /invoices/{id}/
wafeq_item_units_of_measure_listGET /item-units-of-measure/
wafeq_item_units_of_measure_retrieveGET /item-units-of-measure/{id}/
wafeq_items_listGET /items/
wafeq_items_retrieveGET /items/{id}/
wafeq_journal_line_items_listGET /journal-line-items/
wafeq_journal_line_items_retrieveGET /journal-line-items/{id}/
wafeq_manual_journals_listGET /manual-journals/
wafeq_manual_journals_retrieveGET /manual-journals/{id}/
wafeq_organization_retrieveGET /organization/
wafeq_payment_requests_listGET /payment_requests/
wafeq_payment_requests_retrieveGET /payment_requests/{id}/
wafeq_payments_listGET /payments/
wafeq_payments_retrieveGET /payments/{id}/
wafeq_payslips_listGET /payslips/
wafeq_payslips_pay_items_listGET /payslips/{payslip_id}/pay-items/
wafeq_payslips_pay_items_retrieveGET /payslips/{payslip_id}/pay-items/{id}/
wafeq_payslips_retrieveGET /payslips/{id}/
wafeq_projects_listGET /projects/
wafeq_projects_retrieveGET /projects/{id}/
wafeq_purchase_orders_line_items_listGET /purchase-orders/{purchase_order_id}/line-items/
wafeq_purchase_orders_line_items_retrieveGET /purchase-orders/{purchase_order_id}/line-items/{id}/
wafeq_purchase_orders_listGET /purchase-orders/
wafeq_purchase_orders_retrieveGET /purchase-orders/{id}/
wafeq_quotes_line_items_listGET /quotes/{quote_id}/line-items/
wafeq_quotes_line_items_retrieveGET /quotes/{quote_id}/line-items/{id}/
wafeq_quotes_listGET /quotes/
wafeq_quotes_retrieveGET /quotes/{id}/
wafeq_reports_balance_sheet_listGET /reports/balance-sheet/
wafeq_reports_cash_flow_listGET /reports/cash-flow/
wafeq_reports_profit_and_loss_listGET /reports/profit-and-loss/
wafeq_reports_trial_balance_listGET /reports/trial-balance/
wafeq_revenue_recognitions_listGET /revenue-recognitions/
wafeq_revenue_recognitions_retrieveGET /revenue-recognitions/{id}/
wafeq_simplified_invoices_line_items_listGET /simplified-invoices/{invoice_id}/line-items/
wafeq_simplified_invoices_line_items_retrieveGET /simplified-invoices/{invoice_id}/line-items/{id}/
wafeq_simplified_invoices_listGET /simplified-invoices/
wafeq_simplified_invoices_retrieveGET /simplified-invoices/{id}/
wafeq_tax_rates_listGET /tax-rates/
wafeq_units_of_measure_listGET /units-of-measure/
wafeq_units_of_measure_retrieveGET /units-of-measure/{id}/
wafeq_warehouses_listGET /warehouses/
wafeq_warehouses_retrieveGET /warehouses/{id}/
🟢 SOLO LECTURA (PDF) (9)
HerramientaEndpoint
wafeq_bills_download_retrieveGET /bills/{id}/download/
wafeq_credit_notes_download_retrieveGET /credit-notes/{id}/download/
wafeq_debit_notes_download_retrieveGET /debit-notes/{id}/download/
wafeq_invoices_download_retrieveGET /invoices/{id}/download/
wafeq_payments_download_retrieveGET /payments/{id}/download/
wafeq_payslips_download_retrieveGET /payslips/{id}/download/
wafeq_purchase_orders_download_retrieveGET /purchase-orders/{id}/download/
wafeq_quotes_download_retrieveGET /quotes/{id}/download/
wafeq_simplified_invoices_download_retrieveGET /simplified-invoices/{id}/download/
🟢 SOLO LECTURA (VISTA PREVIA) (4)
HerramientaEndpoint
wafeq_amortizations_preview_createPOST /amortizations/preview/
wafeq_amortizations_preview_end_early_createPOST /amortizations/{id}/preview-end-early/
wafeq_revenue_recognitions_preview_createPOST /revenue-recognitions/preview/
wafeq_revenue_recognitions_preview_end_early_createPOST /revenue-recognitions/{id}/preview-end-early/
🟡 ESCRITURA · CREACIONES (39)
HerramientaEndpoint
wafeq_accounts_createPOST /accounts/
wafeq_bank_accounts_createPOST /bank-accounts/
wafeq_bank_accounts_ledger_transactions_createPOST /bank-accounts/{bank_account_id}/ledger-transactions/
wafeq_bank_accounts_statement_transactions_createPOST /bank-accounts/{bank_account_id}/statement-transactions/
wafeq_beneficiaries_createPOST /beneficiaries/
wafeq_bills_createPOST /bills/
wafeq_bills_line_items_createPOST /bills/{bill_id}/line-items/
wafeq_branches_createPOST /branches/
wafeq_contacts_createPOST /contacts/
wafeq_cost_centers_createPOST /cost-centers/
wafeq_credit_notes_createPOST /credit-notes/
wafeq_credit_notes_line_items_createPOST /credit-notes/{credit_note_id}/line-items/
wafeq_custom_fields_createPOST /custom-fields/
wafeq_debit_notes_createPOST /debit-notes/
wafeq_debit_notes_line_items_createPOST /debit-notes/{debit_note_id}/line-items/
wafeq_employees_createPOST /employees/
wafeq_expenses_createPOST /expenses/
wafeq_invoices_createPOST /invoices/
wafeq_invoices_line_items_createPOST /invoices/{invoice_id}/line-items/
wafeq_item_units_of_measure_createPOST /item-units-of-measure/
wafeq_items_createPOST /items/
wafeq_manual_journals_createPOST /manual-journals/
wafeq_payment_requests_createPOST /payment_requests/
wafeq_payments_createPOST /payments/
wafeq_payslips_createPOST /payslips/
wafeq_payslips_pay_items_createPOST /payslips/{payslip_id}/pay-items/
wafeq_projects_createPOST /projects/
wafeq_purchase_orders_bill_createPOST /purchase-orders/{id}/bill/
wafeq_purchase_orders_createPOST /purchase-orders/
wafeq_purchase_orders_line_items_createPOST /purchase-orders/{purchase_order_id}/line-items/
wafeq_quotes_createPOST /quotes/
wafeq_quotes_invoice_createPOST /quotes/{id}/invoice/
wafeq_quotes_line_items_createPOST /quotes/{quote_id}/line-items/
wafeq_simplified_invoices_createPOST /simplified-invoices/
wafeq_simplified_invoices_line_items_createPOST /simplified-invoices/{invoice_id}/line-items/
wafeq_units_of_measure_createPOST /units-of-measure/
wafeq_upload_filePOST /files/
wafeq_upload_file_rawPOST /files/raw/
wafeq_warehouses_createPOST /warehouses/
🟡 ESCRITURA · ACTUALIZACIONES (70) | Herramienta | Endpoint | |---|---| | `wafeq_accounts_partial_update` | `PATCH /accounts/{id}/` | | `wafeq_accounts_update` | `PUT /accounts/{id}/` | | `wafeq_bank_accounts_ledger_transactions_partial_update` | `PATCH /bank-accounts/{bank_account_id}/ledger-transactions/{id}/` | | `wafeq_bank_accounts_ledger_transactions_update` | `PUT /bank-accounts/{bank_account_id}/ledger-transactions/{id}/` | | `wafeq_bank_accounts_partial_update` | `PATCH /bank-accounts/{id}/` | | `wafeq_bank_accounts_statement_transactions_partial_update` | `PATCH /bank-accounts/{bank_account_id}/statement-transactions/{id}/` | | `wafeq_bank_accounts_statement_transactions_update` | `PUT /bank-accounts/{bank_account_id}/statement-transactions/{id}/` | | `wafeq_bank_accounts_update` | `PUT /bank-accounts/{id}/` | | `wafeq_beneficiaries_partial_update` | `PATCH /beneficiaries/{id}/` | | `wafeq_beneficiaries_update` | `PUT /beneficiaries/{id}/` | | `wafeq_bills_line_items_partial_update` | `PATCH /bills/{bill_id}/line-items/{id}/` | | `wafeq_bills_line_items_update` | `PUT /bills/{bill_id}/line-items/{id}/` | | `wafeq_bills_partial_update` | `PATCH /bills/{id}/` | | `wafeq_bills_update` | `PUT /bills/{id}/` | | `wafeq_branches_partial_update` | `PATCH /branches/{id}/` | | `wafeq_branches_update` | `PUT /branches/{id}/` | | `wafeq_contacts_partial_update` | `PATCH /contacts/{id}/` | | `wafeq_contacts_update` | `PUT /contacts/{id}/` | | `wafeq_cost_centers_partial_update` | `PATCH /cost-centers/{id}/` | | `wafeq_cost_centers_update` | `PUT /cost-centers/{id}/` | | `wafeq_credit_notes_line_items_partial_update` | `PATCH /credit-notes/{credit_note_id}/line-items/{id}/` | | `wafeq_credit_notes_line_items_update` | `PUT /credit-notes/{credit_note_id}/line-items/{id}/` | | `wafeq_credit_notes_partial_update` | `PATCH /credit-notes/{id}/` | | `wafeq_credit_notes_update` | `PUT /credit-notes/{id}/` | | `wafeq_custom_fields_partial_update` | `PATCH /custom-fields/{id}/` | | `wafeq_custom_fields_update` | `PUT /custom-fields/{id}/` | | `wafeq_debit_notes_line_items_partial_update` | `PATCH /debit-notes/{debit_note_id}/line-items/{id}/` | | `wafeq_debit_notes_line_items_update` | `PUT /debit-notes/{debit_note_id}/line-items/{id}/` | | `wafeq_debit_notes_partial_update` | `PATCH /debit-notes/{id}/` | | `wafeq_debit_notes_update` | `PUT /debit-notes/{id}/` | | `wafeq_employees_partial_update` | `PATCH /employees/{id}/` | | `wafeq_employees_update` | `PUT /employees/{id}/` | | `wafeq_expenses_partial_update` | `PATCH /expenses/{id}/` | | `wafeq_expenses_update` | `PUT /expenses/{id}/` | | `wafeq_invoices_line_items_partial_update` | `PATCH /invoices/{invoice_id}/line-items/{id}/` | | `wafeq_invoices_line_items_update` | `PUT /invoices/{invoice_id}/line-items/{id}/` | | `wafeq_invoices_partial_update` | `PATCH /invoices/{id}/` | | `wafeq_invoices_update` | `PUT /invoices/{id}/` | | `wafeq_item_units_of_measure_partial_update` | `PATCH /item-units-of-measure/{id}/` | | `wafeq_item_units_of_measure_update` | `PUT /item-units-of-measure/{id}/` | | `wafeq_items_partial_update` | `PATCH /items/{id}/` | | `wafeq_items_update` | `PUT /items/{id}/` | | `wafeq_manual_journals_partial_update` | `PATCH /manual-journals/{id}/` | | `wafeq_manual_journals_update` | `PUT /manual-journals/{id}/` | | `wafeq_payment_requests_partial_update` | `PATCH /payment_requests/{id}/` | | `wafeq_payment_requests_update` | `PUT /payment_requests/{id}/` | | `wafeq_payments_partial_update` | `PATCH /payments/{id}/` | | `wafeq_payments_update` | `PUT /payments/{id}/` | | `wafeq_payslips_partial_update` | `PATCH /payslips/{id}/` | | `wafeq_payslips_pay_items_partial_update` | `PATCH /payslips/{payslip_id}/pay-items/{id}/` | | `wafeq_payslips_pay_items_update` | `PUT /payslips/{payslip_id}/pay-items/{id}/` | | `wafeq_payslips_update` | `PUT /payslips/{id}/` | | `wafeq_projects_partial_update` | `PATCH /projects/{id}/` | | `wafeq_projects_update` | `PUT /projects/{id}/` | | `wafeq_purchase_orders_line_items_partial_update` | `PATCH /purchase-orders/{purchase_order_id}/line-items/{id}/` | | `wafeq_purchase_orders_line_items_update` | `PUT /purchase-orders/{purchase_order_id}/line-items/{id}/` | | `wafeq_purchase_orders_partial_update` | `PATCH /purchase-orders/{id}/` | | `wafeq_purchase_orders_update` | `PUT /purchase-orders/{id}/` | | `wafeq_quotes_line_items_partial_update` | `PATCH /quotes/{quote_id}/line-items/{id}/` | | `wafeq_quotes_line_items_update` | `PUT /quotes/{quote_id}/line-items/{id}/` | | `wafeq_quotes_partial_update` | `PATCH /quotes/{id}/` | | `wafeq_quotes_update` | `PUT /quotes/{id}/` | | `wafeq_simplified_invoices_line_items_partial_update` | `PATCH /simplified-invoices/{invoice_id}/line-items/{id}/` | | `wafeq_simplified_invoices_line_items_update` | `PUT /simplified-invoices/{invoice_id}/line-items/{id}/` | | `wafeq_simplified_invoices_partial_update` | `PATCH /simplified-invoices/{id}/` | | `wafeq_simplified_invoices_update` | `PUT /simplified-invoices/{id}/` | | `wafeq_units_of_measure_partial_update` | `PATCH /units-of-measure/{id}/` | | `wafeq_units_of_measure_update` | `PUT /units-of-measure/{id}/` | | `wafeq_warehouses_partial_update` | `PATCH /warehouses/{id}/` | | `wafeq_warehouses_update` | `PUT /warehouses/{id}/` |
🟠 CAMBIO DE ESTADO (2)
HerramientaEndpoint
wafeq_expenses_mark_as_draft_createPOST /expenses/{id}/mark-as-draft/
wafeq_expenses_mark_as_posted_createPOST /expenses/{id}/mark-as-posted/
🔴 IRREVERSIBLE · PRESENTACIÓN EXTERNA (3)
HerramientaEndpoint
wafeq_credit_notes_tax_authority_report_createPOST /credit-notes/{id}/tax-authority/report/
wafeq_invoices_tax_authority_report_createPOST /invoices/{id}/tax-authority/report/
wafeq_simplified_invoices_tax_authority_report_createPOST /simplified-invoices/{id}/tax-authority/report/
🔴 IRREVERSIBLE · LIBRO MAYOR (2)
HerramientaEndpoint
wafeq_amortizations_end_early_createPOST /amortizations/{id}/end-early/
wafeq_revenue_recognitions_end_early_createPOST /revenue-recognitions/{id}/end-early/
🔴 DESTRUCTIVO · ELIMINA (39)
HerramientaEndpoint
wafeq_accounts_destroyDELETE /accounts/{id}/
wafeq_amortizations_destroyDELETE /amortizations/{id}/
wafeq_bank_accounts_destroyDELETE /bank-accounts/{id}/
wafeq_bank_accounts_ledger_transactions_destroyDELETE /bank-accounts/{bank_account_id}/ledger-transactions/{id}/
wafeq_bank_accounts_statement_transactions_destroyDELETE /bank-accounts/{bank_account_id}/statement-transactions/{id}/
wafeq_beneficiaries_destroyDELETE /beneficiaries/{id}/
wafeq_bills_destroyDELETE /bills/{id}/
wafeq_bills_line_items_destroyDELETE /bills/{bill_id}/line-items/{id}/
wafeq_branches_destroyDELETE /branches/{id}/
wafeq_contacts_destroyDELETE /contacts/{id}/
wafeq_cost_centers_destroyDELETE /cost-centers/{id}/
wafeq_credit_notes_destroyDELETE /credit-notes/{id}/
wafeq_credit_notes_line_items_destroyDELETE /credit-notes/{credit_note_id}/line-items/{id}/
wafeq_custom_fields_destroyDELETE /custom-fields/{id}/
wafeq_debit_notes_destroyDELETE /debit-notes/{id}/
wafeq_debit_notes_line_items_destroyDELETE /debit-notes/{debit_note_id}/line-items/{id}/
wafeq_employees_destroyDELETE /employees/{id}/
wafeq_expenses_destroyDELETE /expenses/{id}/
wafeq_files_destroyDELETE /files/{id}/
wafeq_invoices_destroyDELETE /invoices/{id}/
wafeq_invoices_line_items_destroyDELETE /invoices/{invoice_id}/line-items/{id}/
wafeq_item_units_of_measure_destroyDELETE /item-units-of-measure/{id}/
wafeq_items_destroyDELETE /items/{id}/
wafeq_manual_journals_destroyDELETE /manual-journals/{id}/
wafeq_payment_requests_destroyDELETE /payment_requests/{id}/
wafeq_payments_destroyDELETE /payments/{id}/
wafeq_payslips_destroyDELETE /payslips/{id}/
wafeq_payslips_pay_items_destroyDELETE /payslips/{payslip_id}/pay-items/{id}/
wafeq_projects_destroyDELETE /projects/{id}/
wafeq_purchase_orders_destroyDELETE /purchase-orders/{id}/
wafeq_purchase_orders_line_items_destroyDELETE /purchase-orders/{purchase_order_id}/line-items/{id}/
wafeq_quotes_destroyDELETE /quotes/{id}/
wafeq_quotes_line_items_destroyDELETE /quotes/{quote_id}/line-items/{id}/
wafeq_requestescrito a mano
wafeq_revenue_recognitions_destroyDELETE /revenue-recognitions/{id}/
wafeq_simplified_invoices_destroyDELETE /simplified-invoices/{id}/
wafeq_simplified_invoices_line_items_destroyDELETE /simplified-invoices/{invoice_id}/line-items/{id}/
wafeq_units_of_measure_destroyDELETE /units-of-measure/{id}/
wafeq_warehouses_destroyDELETE /warehouses/{id}/

Cobertura

ÁreaHerramientas🟢 Lectura🟡 Escritura🔴 Irreversible🔴 Eliminación
Ventas y cuentas por cobrar692531310
Compras y cuentas por pagar54192708
Banca186903
Libro mayor e informes3119624
Nómina197903
Datos maestros y dimensiones54182709
Archivos y organización63201
Vía de escape y conveniencia21001
Total25398111539

"Escritura" incluye las dos herramientas de 🟠 cambio de estado. Las áreas se asignan a los recursos de Wafeq de la siguiente manera — Ventas: facturas, facturas simplificadas, cotizaciones, notas de crédito, pagos, solicitudes de pago · Compras: facturas de compra, órdenes de compra, notas de débito, gastos, beneficiarios · Banca: cuentas bancarias con su libro mayor y transacciones de estado de cuenta · Libro mayor e informes: cuentas, asientos manuales, partidas de asiento, los cuatro informes, tasas impositivas, amortizaciones, reconocimientos de ingresos · Nómina: recibos de pago, empleados · Datos maestros: contactos, artículos, unidades de medida, almacenes, proyectos, centros de costo, sucursales, campos personalizados.

Dos herramientas escritas a mano

Todo lo anterior es generado. Dos herramientas están escritas a mano:

  • wafeq_account_ledger (🟢) — partidas de asiento con su fecha de transacción real. Las filas de /journal-line-items/ de Wafeq llevan created_ts (cuando la fila llegó a Wafeq), que suele ser un mes diferente al de la transacción, y no tienen ningún campo de fecha. Esta herramienta recupera la fecha usando los filtros de date_after/date_before del propio endpoint, que operan sobre la fecha de transacción. Sondea un mes a la vez y solo divide en consultas por día donde existen filas, por lo que los períodos tranquilos cuestan una solicitud cada uno; el resultado informa requests_made.
  • wafeq_request (🔴) — la vía de escape: cualquier método, cualquier ruta, más query, body y headers. Es el respaldo para cualquier cosa que el spec incluido no cubra, no la interfaz principal. Categorizado como destructivo porque su efecto no se puede conocer de antemano.

Ejecutar desde el código fuente (stdio, sin Docker)

npm ci
npm run build

Luego regístralo con tu cliente MCP. Para Claude Desktop, agrega a claude_desktop_config.json:

{
  "mcpServers": {
    "wafeq": {
      "command": "node",
      "args": ["/absolute/path/to/Wafeq MCP/dist/index.js"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "WAFEQ_API_KEY": "your-key-here"
      }
    }
  }
}

Para Claude Code:

claude mcp add wafeq --env WAFEQ_API_KEY=your-key-here -- node /absolute/path/to/dist/index.js

Ejecutar el contenedor sobre stdio

También puedes dejar que tu cliente inicie la imagen publicada directamente, sin servidor HTTP y sin compilación local:

{
  "mcpServers": {
    "wafeq": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT=stdio",
        "-e", "WAFEQ_API_KEY",
        "ghcr.io/ohneben/wafeq-mcp:latest"
      ],
      "env": {
        "WAFEQ_API_KEY": "your-key-here"
      }
    }
  }
}

MCP_TRANSPORT=stdio es requerido aquí: la imagen usa por defecto el transporte HTTP.

Scripts útiles:

ComandoQué hace
npm run buildCompila TypeScript a dist/.
npm testEjecuta la suite de Vitest.
npm run list-toolsImprime el catálogo categorizado. No necesita credenciales.
npm run start:stdioEjecuta sobre stdio.
npm run start:httpEjecuta el servidor HTTP Streamable.

Mantener el spec actualizado

Las herramientas se generan desde spec/wafeq-public-api.json al inicio — no hay paso de generación de código ni lista de herramientas escrita a mano. Coloca un documento OpenAPI más nuevo (JSON o YAML), recompila, y los nuevos endpoints se convierten en nuevas herramientas. Consulta spec/README.md para ver de dónde proviene la copia incluida y qué revisar después de una actualización.

Las correcciones de comportamiento observado viven en src/overrides.ts, con clave por operationId y fechadas, de modo que una entrada cuya operación desaparezca simplemente deja de aplicarse.

Notas y convenciones

  • Fechas son YYYY-MM-DD. Montos usan un punto como separador decimal.
  • Paginación: las herramientas de listado toman limit y offset, e informan el recuento total.
  • Informes toman parámetros de fecha específicos del informe — balance general date + period_count; pérdidas y ganancias y flujo de caja date_after + date_before; balance de comprobación from_date + to_date. Wafeq ignora silenciosamente un parámetro de query mal escrito, por lo que un nombre incorrecto parece una llamada exitosa; los esquemas por informe existen para hacer eso imposible.
  • Períodos completos: los rangos de pérdidas y ganancias y flujo de caja deben cubrir meses o años completos. El servidor verifica localmente y responde con el rango válido más cercano en lugar de gastar un viaje de ida y vuelta en un HTTP 400.
  • Subidas de archivos (wafeq_files_*): pasa contenido base64 más un nombre de archivo. POST /files/ es solo multipart y POST /files/raw/ necesita un header Content-Disposition — ambos se manejan por ti.
  • Descargas de PDF regresan codificadas en base64 en un pequeño sobre que lleva el tamaño y el tipo de contenido, no como texto distorsionado.
  • Idempotencia: cada endpoint de escritura que soporta X-Wafeq-Idempotency-Key recibe un UUID v4 automáticamente, reutilizado entre reintentos. Pasa el tuyo propio para hacer una re-ejecución deliberada segura.
  • Reintentos: las respuestas transitorias de 429 / 5xx se reintentan con retroceso exponencial con jitter, respetando Retry-After, bajo la misma clave de idempotencia.
  • Límite de tasa: Wafeq no publica un límite numérico, por lo que el valor predeterminado del lado del cliente (WAFEQ_MAX_REQUESTS=20 por WAFEQ_RATE_WINDOW_MS=10000) es deliberadamente conservador. Auméntalo si conoces tu asignación.
  • Una organización por credencial. Una clave de API de Wafeq está limitada a la organización; cada llamada de herramienta actúa sobre esa organización, y /health la nombra.

CI y lanzamientos

Cada push y pull request se compila y prueba en Node 20 y 22, y el catálogo de herramientas se genera sin credenciales presentes — que es lo que detecta un nombre de herramienta duplicado o ilegal según el esquema antes de que se publique. CI también falla si .env alguna vez se vuelve rastreado.

Un lanzamiento es una etiqueta vX.Y.Z y nada más. No se mantiene ningún número de versión a mano. Empujar la etiqueta ejecuta toda la cadena:

  1. La versión se deriva una sola vez, a partir de la etiqueta.
  2. La imagen se compila y se publica en ghcr.io/ohneben/wafeq-mcp — etiquetada con la versión, MAJOR.MINOR, el SHA corto y latest en main.
  3. La entrada se publica en el Registro MCP con server.json fijado a esa etiqueta de imagen exacta. La propiedad se demuestra mediante la etiqueta io.modelcontextprotocol.server.name en la imagen, que debe coincidir con el name de server.json — una prueba garantiza que así sea.
  4. El número publicado se escribe de vuelta en package.json y server.json en main, y la etiqueta se mueve a ese commit. Así, el repositorio siempre indica la última versión publicada, y el servidor la reporta a través de MCP y en /health sin necesidad de editar código.
npm version 2.0.1 --no-git-tag-version   # optional; CI stamps it either way
git tag v2.0.1 && git push origin v2.0.1

workflow_dispatch vuelve a publicar una versión dada sin crear una nueva etiqueta. Los envíos a main compilan una imagen -dev.g<sha> y se detienen ahí — nunca tocan el registro.

Seguridad

  • Las credenciales permanecen en el servidor. Se leen del entorno y se inyectan por solicitud. El modelo ve las entradas de las herramientas y las respuestas de la API, nunca la clave. La herramienta de paso directo no puede sobrescribir el encabezado Authorization, y se niega a enviar la credencial a cualquier host que no sea la base de API configurada.
  • Nunca confirmes .env. Está ignorado por git, y la CI falla si alguna vez se rastrea. .env.example solo contiene marcadores de posición.
  • Vincula a localhost, o establece un token. docker-compose.yml publica en 127.0.0.1 únicamente. Si expones el puerto más allá, establece MCP_SHARED_TOKEN primero; se compara en tiempo constante.
  • Las cargas de archivos locales están desactivadas por defecto. WAFEQ_ALLOW_LOCAL_FILE_UPLOAD=false significa que el servidor no leerá archivos de su propio sistema de archivos. Activarlo permite que cualquier cosa que pueda llamar al servidor le pida leer una ruta local — déjalo desactivado a menos que lo necesites y confíes en cada cliente. Las cargas en Base64 funcionan de cualquier manera.
  • Verifica la organización en /health antes de la primera escritura. Una clave de API está limitada a una organización, y una clave incorrecta falla escribiendo en la empresa equivocada en lugar de generar un error.
  • Las herramientas 🔴 significan lo que dicen. Las eliminaciones son permanentes, terminar un horario antes de tiempo no tiene deshacer en la API, y una declaración ante la autoridad fiscal no se puede recuperar. Mantén las confirmaciones del host activadas para cualquier cosa que lleve destructiveHint.

Consulta SECURITY.md para reportar una vulnerabilidad.

Créditos y licencia

MIT — consulta LICENSE.md. Construido sobre el SDK de TypeScript del Protocolo de Contexto de Modelo, siguiendo la misma arquitectura que ohneben's LearnWorlds MCP. No está afiliado ni respaldado por Wafeq.