Actual Budget MCP

Servidor MCP Docker que conecta Claude Desktop/LibreChat/LobeChat con Actual Budget para gestión de presupuestos en lenguaje natural, administración de transacciones y análisis financieros.

Documentación

Actual MCP Server icon

Servidor Actual MCP

npm version npm downloads License: MIT Node.js Version TypeScript MCP Protocol Docker Pulls Docker Image Size Unraid Community Apps GitHub Actions CI GitHub stars

Habla con tu presupuesto. Ejecútalo en cualquier lugar. Confía en él en producción.

Actual MCP Server es un servidor de Model Context Protocol que conecta cualquier asistente de IA compatible con MCP (como LibreChat, LobeChat, Claude Desktop y más) directamente a tu instancia autoalojada de Actual Budget. Haz preguntas en lenguaje natural, crea transacciones, analiza gastos y gestiona todo tu presupuesto sin abrir nunca la interfaz de Actual Budget.

┌─────────────┐   MCP/HTTP    ┌──────────────────┐   Actual API   ┌──────────────┐
│  LibreChat  │ ◄───────────► │  Actual MCP      │ ◄───────────► │   Actual     │
│  LobeChat   │               │  Server          │               │   Budget     │
│  (remote)   │               │  (84 tools)      │               │   Server     │
└─────────────┘               └──────────────────┘               └──────────────┘

┌─────────────┐   MCP/stdio   ┌──────────────────┐   Actual API   ┌──────────────┐
│  Claude     │ ◄───────────► │  Actual MCP      │ ◄───────────► │   Actual     │
│  Desktop    │               │  Server          │               │   Budget     │
│  (local)    │               │  (84 tools)      │               │   Server     │
└─────────────┘               └──────────────────┘               └──────────────┘

¿Por qué este proyecto?

La mayoría de las implementaciones de MCP para Actual Budget son simples puentes stdio diseñados para uso local de un solo usuario con Claude Desktop. Este proyecto va más allá:

  • 84 herramientas, la cobertura más completa disponible. Cuentas, transacciones, categorías, beneficiarios, etiquetas, notas, reglas, presupuestos, operaciones por lotes, sincronización bancaria y más. Cubre la API de Actual Budget alcanzable sin lagunas reales.
  • Transporte HTTP y stdio. Se ejecuta como un servidor remoto real para LibreChat/LobeChat (--http), o como un proceso local directo para Claude Desktop (--stdio). No se necesita Docker ni servidor HTTP para uso local.
  • 6 herramientas exclusivas impulsadas por ActualQL. Busca y resume transacciones por mes, monto, categoría o beneficiario usando el motor de consultas nativo de Actual Budget. Resultados agregados, sin datos sin procesar volcados en la ventana de contexto de la IA.
  • Cambio de múltiples presupuestos en tiempo de ejecución. Configura múltiples archivos de presupuesto y permite que la IA cambie entre ellos a mitad de conversación con actual_budgets_switch. Funciona en ambos transportes: HTTP vincula el presupuesto activo a la sesión MCP, y stdio (Claude Desktop, Claude Code, Cursor) obtiene una sesión sintética por proceso, de modo que un cambio se limita a ese proceso en lugar de compartirse globalmente (#348).
  • Listo para múltiples usuarios con OIDC. Asegura cada sesión con JWT validados por JWKS y ACL de presupuesto por usuario. No se requieren tokens compartidos.
  • Fiabilidad de nivel de producción en ambos transportes. Agrupación de conexiones HTTP (hasta 15 sesiones concurrentes) y un proceso stdio de larga duración que inicia sesión UNA VEZ y reutiliza esa conexión para cada llamada de herramienta en lugar de reautenticarse por llamada, de modo que Claude Desktop y Claude Code se mantengan rápidos y una ráfaga de llamadas ya no arriesgue una tormenta de inicios de sesión por llamada contra el limitador ascendente. Reintento automático con retroceso exponencial y un conjunto completo de pruebas (unitarias + E2E + integración).

Verificado funcionando con LibreChat, LobeChat y Claude Desktop. Las 84 herramientas probadas de extremo a extremo. Cualquier cliente compatible con MCP debería funcionar.


Tabla de Contenidos


Inicio Rápido

Requisitos Previos

  • Servidor de Actual Budget en ejecución (local o remoto)
  • Tu ID de Sincronización de Presupuesto: Actual → Configuración → Mostrar Configuración Avanzada → ID de Sincronización
  • Node.js 22+ (método npm) o Docker

Opción A: Docker (recomendado)

docker run -d \
  --name actual-mcp-server-backend \
  -p 3600:3600 \
  # Use the same URL you type in your browser to open Actual Budget:
  #   http://localhost:5006          (if Actual Budget runs on the same machine)
  #   http://192.168.1.50:5006       (if it runs on another machine on your network)
  #   https://actual.yourdomain.com  (if you use a domain name)
  #   http://actual:5006             (if both containers share a Docker network; use container name)
  -e ACTUAL_SERVER_URL=http://localhost:5006 \
  -e ACTUAL_PASSWORD=your_password \
  -e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
  -e MCP_SSE_AUTHORIZATION=your_secret_token \
  -v actual-mcp-data:/app/data \        # required, see note below
  -v actual-mcp-logs:/app/logs \
  ghcr.io/agigante80/actual-mcp-server:latest

Por qué se requiere el volumen /app/data: Actual Budget no expone una API REST. La biblioteca oficial @actual-app/api (utilizada internamente por este servidor) funciona descargando una copia local de tus datos de presupuesto, ejecutando todas las consultas en esa copia local y luego sincronizando los cambios de vuelta. El volumen /app/data le da al contenedor un lugar persistente y escribible para almacenar esa copia local (es el directorio que la imagen crea y posee como usuario de ejecución). Sin él, el contenedor no tiene dónde escribir y fallará al iniciar. Consulta los docs de la API de Actual para más detalles.

actual-mcp no necesita ejecutarse en la misma máquina que Actual Budget. Puedes tener Actual Budget en un servidor y actual-mcp en otro, siempre que ACTUAL_SERVER_URL apunte a tu instancia de Actual Budget, todo funciona.

Verifica que está en ejecución:

# Quick health check
curl http://localhost:3600/health
# Expected: {"status":"ok","transport":"http","version":"..."}

# Full MCP handshake (also verifies your token)
curl -s -X POST http://localhost:3600/http \
  -H "Authorization: Bearer your_secret_token" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"cli-test","version":"1.0"}}}' \
  | python3 -m json.tool
# Success: JSON response with "protocolVersion" and "serverInfo"
# Wrong token: {"error": "Unauthorized"}
# Server not running: curl: (7) Failed to connect

También disponible en Docker Hub: agigante80/actual-mcp-server:latest

Opción B: Docker Compose

git clone https://github.com/agigante80/actual-mcp-server.git
cd actual-mcp-server
cp .env.example .env        # fill in ACTUAL_SERVER_URL, ACTUAL_PASSWORD, ACTUAL_BUDGET_SYNC_ID

docker compose --profile production up -d   # production: MCP server listens on :3600
# or
docker compose --profile dev up -d          # dev mode with hot-reload

El archivo compose define solo los perfiles dev y production. El servidor MCP escucha en :3600 directamente: no hay proxy inverso incluido ni servidor de Actual Budget incluido, así que apunta ACTUAL_SERVER_URL a tu propia instancia de Actual. Para TLS, habilita HTTPS nativo con MCP_ENABLE_HTTPS=true (además de MCP_HTTPS_CERT y MCP_HTTPS_KEY), o coloca el servidor detrás de tu propio proxy inverso.

Opción C: npm (servidor HTTP)

Requiere Node.js 22+. npm y npx no lo aplican, así que verifica con node --version primero. En un Node más antiguo, el servidor se niega a iniciar y te lo indica. Ten en cuenta que npx ejecuta el node que esté primero en tu PATH, que no siempre es el que instalaste más recientemente.

# Quick start via npx (no clone needed):
ACTUAL_SERVER_URL=http://localhost:5006 \
ACTUAL_PASSWORD=your_password \
ACTUAL_BUDGET_SYNC_ID=your-sync-id \
MCP_SSE_AUTHORIZATION=your_token \
npx actual-mcp-server --http

# Or clone for development / custom config:
git clone https://github.com/agigante80/actual-mcp-server.git
cd actual-mcp-server
npm install
cp .env.example .env        # fill in required values
npm run build
npm run dev -- --http

El servidor inicia en http://localhost:3600/http por defecto (el puerto de escucha es MCP_BRIDGE_PORT, por defecto 3600).

Opción D: stdio (Claude Desktop nativo, sin necesidad de Docker ni servidor HTTP)

El transporte stdio ejecuta el servidor MCP como un proceso hijo. Claude Desktop lo inicia directamente y se comunica a través de stdin/stdout. Sin puerto de red, sin token de autenticación, sin Docker requerido. No se necesita clonar: npx descarga y almacena en caché el paquete automáticamente.

Añade a claude_desktop_config.json (consulta docs/guides/MCP_CLIENTS_SETUP.md para la ubicación del archivo de configuración y todas las opciones de cliente):

{
  "mcpServers": {
    "actual-budget": {
      "command": "npx",
      "args": ["-y", "actual-mcp-server", "--stdio"],
      "env": {
        "ACTUAL_SERVER_URL": "http://localhost:5006",
        "ACTUAL_PASSWORD": "your_actual_password",
        "ACTUAL_BUDGET_SYNC_ID": "your-sync-id-here",
        "MCP_BRIDGE_DATA_DIR": "/absolute/path/to/data-dir"
      }
    }
  }
}

No se necesita token. stdio se ejecuta como un proceso local propiedad de tu usuario. El transporte en sí es el límite de seguridad. Las 84 herramientas están disponibles.

MCP_BRIDGE_DATA_DIR debe ser una ruta absoluta. Sin una, el directorio de datos se resuelve relativo a donde el cliente inicia el proceso, lo que puede ser impredecible. El directorio se crea automáticamente en el primer inicio.

Opción E: Unraid (Aplicaciones de la Comunidad)

Unraid Community Applications

Actual MCP Server está publicado en la tienda de Aplicaciones de la Comunidad de Unraid: ca.unraid.net/apps/actual-mcp-server. Esto ejecuta el transporte HTTP, la opción correcta para LibreChat, LobeChat y otros clientes MCP remotos.

Instálalo desde la pestaña Apps (Aplicaciones de la Comunidad):

  1. Abre la pestaña Apps y busca actual-mcp-server, luego haz clic en Instalar.
  2. Completa URL del servidor Actual, Contraseña del servidor Actual e ID de Sincronización del servidor Actual (el ID de Sincronización está en Actual Budget: abre el presupuesto, Configuración, Mostrar configuración avanzada, ID de Sincronización).
  3. Establece un token de autenticación MCP fuerte. Genera uno con openssl rand -hex 32. Un token en blanco desactiva toda la autenticación HTTP y expone tus datos financieros sin autenticación en la LAN, por lo que esto es obligatorio (consulta Transporte y Autenticación).
  4. Deja PUID=99 y PGID=100 (nobody:users) para que el contenedor pueda escribir en los directorios de Datos y Registros de appdata, luego inícialo.
  5. Accede al endpoint de salud a través del enlace WebUI del contenedor (puerto 3600); apunta tu cliente MCP a http://[server-ip]:3600/http con el token Bearer.

La plantilla de Unraid vive en unraid/actual-mcp-server.xml. Para el flujo de publicación consulta docs/UNRAID_CA_PUBLISHING.md.

Conectar un cliente de IA

LibreChat / LobeChat: añade a librechat.yaml (o a la configuración del plugin MCP de LobeChat):

mcpServers:
  actual-mcp:
    type: "streamable-http"
    url: "http://actual-mcp-server-backend:3600/http"
    headers:
      Authorization: "Bearer YOUR_TOKEN_HERE"
    serverInstructions: true
    timeout: 600000

Consulta docs/guides/AI_CLIENT_SETUP.md para la configuración completa de LibreChat, LobeChat, red y proxy HTTPS/TLS.

Claude Desktop vía HTTP (cuando el servidor ya se está ejecutando como contenedor Docker):

{
  "mcpServers": {
    "actual-budget": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "http://localhost:3600/http",
        "--header",
        "Authorization: Bearer YOUR_TOKEN_HERE"
      ]
    }
  }
}

Claude Desktop vía stdio (nativo, sin necesidad de servidor HTTP; consulta la Opción D arriba):

{
  "mcpServers": {
    "actual-budget": {
      "command": "node",
      "args": ["/absolute/path/to/actual-mcp-server/dist/src/index.js", "--stdio"],
      "env": {
        "ACTUAL_SERVER_URL": "http://localhost:5006",
        "ACTUAL_PASSWORD": "your_password",
        "ACTUAL_BUDGET_SYNC_ID": "your-sync-id",
        "MCP_BRIDGE_DATA_DIR": "/absolute/path/to/actual-mcp-server/actual-data"
      }
    }
  }
}

Consulta docs/guides/MCP_CLIENTS_SETUP.md para todas las opciones (incluyendo Cursor, VS Code, Gemini CLI), correcciones de rutas Linux/NVM y solución de problemas.


Actualización

Docker (Opción A)

docker pull ghcr.io/agigante80/actual-mcp-server:latest
docker stop actual-mcp-server-backend
docker rm actual-mcp-server-backend
# Re-run the original docker run command with the same flags and volumes

También disponible en Docker Hub: docker pull agigante80/actual-mcp-server:latest

Docker Compose (Opción B)

docker compose pull
docker compose --profile production up -d

npm / repositorio clonado (Opción C)

git pull
npm install
npm run build
# Then restart the server

npx / stdio (Opciones C y D)

Si ejecutas npx actual-mcp-server sin una versión instalada globalmente, npx obtiene la última del registro automáticamente. Pero si previamente la instalaste globalmente (npm install -g actual-mcp-server), la instalación global tiene prioridad, por lo que debes actualizarla explícitamente:

# Upgrade the global install
npm install -g actual-mcp-server

# Or force the registry version without touching your global install
npx actual-mcp-server@latest --http

Para Claude Desktop (stdio), reinicia Claude después de actualizar.


Herramientas Disponibles

84 herramientas en todas las categorías. Todas las herramientas usan la convención de nomenclatura actual_<category>_<action>.

Cuentas (12)

HerramientaDescripción
actual_accounts_listListar todas las cuentas
actual_accounts_createCrear nueva cuenta
actual_accounts_updateActualizar detalles de la cuenta. Un id que no existe se rechaza en lugar de crear un registro parcial
actual_accounts_deleteEliminar cuenta permanentemente
actual_accounts_closeCerrar cuenta. Una cuenta SIN transacciones es ELIMINADA por Actual, no cerrada
actual_accounts_reopenReabrir cuenta cerrada. Un id que no es una cuenta se rechaza en lugar de crear una
actual_accounts_get_balanceObtener saldo de cuenta en una fecha
actual_account_flow_summaryExplicar el cambio exacto de saldo en un conjunto de cuentas durante un rango de fechas: ingresos externos, salida de gastos, créditos, entradas sin categorizar y transferencias hacia, desde o dentro de la selección, con una conciliación exacta
actual_account_groups_listListar los grupos de cuentas personalizados utilizados para organizar cuentas en la barra lateral (Actual 26.9.0+)
actual_account_groups_createCrear un grupo de cuentas. Asignar cuentas a él es una llamada separada de actual_accounts_update
actual_account_groups_updateRenombrar un grupo de cuentas o cambiar su posición en la barra lateral. Un grupo que no existe se rechaza, sin escribir nada
actual_account_groups_deleteEliminar un grupo de cuentas. Sus cuentas NO se eliminan, quedan sin agrupar

Transacciones (17)

Estándar (7)

HerramientaDescripción
actual_transactions_getObtener transacciones de una cuenta
actual_transactions_filterFiltrar con criterios avanzados
actual_transactions_createCrear nueva(s) transacción(es), incluyendo divisiones (pasa un array subtransactions)
actual_transactions_importImportar y conciliar transacciones
actual_transactions_updateActualizar una transacción, dividir una simple en su lugar, o editar los hijos de una división existente vía subtransactions
actual_transactions_deleteEliminar una transacción
actual_transactions_mergeFusionar dos transacciones de la misma cuenta y mismo monto (por ejemplo, un duplicado importado del banco y una entrada manual). El upstream conserva una (la importada primero) y ELIMINA la otra, llenando solo los campos vacíos de beneficiario, categoría, notas y programación de la fila conservada, así como sus banderas falsas de conciliado y compensado; irreversible

Transacciones divididas: pasa un array subtransactions (cada hijo necesita un amount en centavos enteros; category y notes son opcionales). Los montos de los hijos deben sumar el amount del padre (el servidor no lo aplica, por lo que la herramienta lo hace). Un padre dividido no lleva categoría propia: pon las categorías en los hijos. actual_transactions_update con subtransactions edita los hijos de una transacción que ya es dividida, o divide una transacción simple EN EL MISMO LUGAR: conserva su id, imported_id y estado de conciliación, por lo que una transacción sincronizada con el banco puede dividirse sin romper la coincidencia de importación. Su propia categoría se limpia, y un hijo que no nombra categoría la hereda. Un hijo dividido no puede dividirse a su vez. Nota: actual_transactions_import reenvía un array subtransactions a la API pero NO aplica la verificación de suma, así que usa actual_transactions_create para divisiones con saldo garantizado.

Utilidad (2)

HerramientaDescripción
actual_transactions_uncategorizedResumen de transacciones sin categorizar (totalCount, totalAmount, desglose por cuenta); pasa includeTransactions:true para filas paginadas
actual_transactions_update_batchAplica muchas actualizaciones de transacciones en UNA llamada (updates: [{ id, fields }]); devuelve conteos de éxito y fallo por elemento

Exclusivas con ActualQL (8), únicas de este servidor MCP

HerramientaDescripción
actual_transactions_aggregateTotales deterministas en centavos enteros agrupados por mes, categoría, grupo de categorías, beneficiario o cuenta; las transferencias se excluyen mediante transfer_id y los hijos divididos se cuentan una vez
actual_transactions_search_by_monthBusca por mes usando la transformación $month
actual_transactions_search_by_amountEncuentra por rango de montos
actual_transactions_search_by_categoryBusca por nombre de categoría
actual_transactions_search_by_payeeEncuentra por beneficiario/proveedor
actual_transactions_summary_by_categoryResumen de gastos agrupado por categoría
actual_transactions_summary_by_payeePrincipales proveedores con totales y conteos
actual_recurring_expenses_summaryDetecta cargos recurrentes (suscripciones, facturas) del historial publicado: cadencia (con tolerancia de desfase de fecha y fin de mes), último monto, cambios de precio, ocurrencias, costo anualizado y estado activo o inactivo

Transferencias (1)

HerramientaDescripción
actual_transfers_createCrea una transferencia emparejada entre dos cuentas (débito + crédito vinculados por transfer_id, idéntico a "Hacer transferencia" de la interfaz)

Nota: Usa actual_transfers_create para cualquier movimiento entre cuentas, no actual_transactions_create. La herramienta dedicada crea ambos lados (débito y crédito) atómicamente para que los libros se mantengan equilibrados. Limitaciones: ambas cuentas deben existir y estar abiertas, y from_account debe diferir de to_account.

Categorías (4)

actual_categories_get · actual_categories_create · actual_categories_update · actual_categories_delete

Grupos de categorías (4)

actual_category_groups_get · actual_category_groups_create · actual_category_groups_update · actual_category_groups_delete

Beneficiarios (7)

actual_payees_get · actual_payees_common_list · actual_payees_create · actual_payees_update · actual_payees_delete · actual_payees_merge · actual_payee_rules_get

Etiquetas (4)

actual_tags_list · actual_tags_create · actual_tags_update · actual_tags_delete

HerramientaDescripción
actual_tags_listLista todas las etiquetas (id, palabra de etiqueta, color y descripción opcionales)
actual_tags_createCrea o actualiza una etiqueta por nombre; devuelve el UUID de la etiqueta
actual_tags_updateActualiza nombre, color o descripción de la etiqueta por UUID
actual_tags_deleteEliminación suave de una etiqueta por UUID

Notas (2)

actual_notes_get · actual_notes_update

HerramientaDescripción
actual_notes_getObtiene la nota de cualquier entidad (UUID de cuenta/categoría/grupo de categorías/beneficiario, o presupuesto-AAAA-MM)
actual_notes_updateEstablece o limpia la nota de cualquier entidad; valida que la entidad exista o coincida con el patrón presupuesto-AAAA-MM

Preferencias (1)

HerramientaDescripción
actual_preferences_getLee las preferencias de visualización sincronizadas del presupuesto (formato de número, formato de fecha, moneda, primer día de la semana). Solo visualización: todos los montos permanecen en centavos enteros

Presupuestos (12)

HerramientaDescripción
actual_budgets_list_availableLista todos los archivos de presupuesto configurados
actual_budgets_switchCambia el presupuesto activo (multi-presupuesto)
actual_budgets_get_allLista los archivos de presupuesto disponibles
actual_budgets_getMonthsLista los meses del presupuesto
actual_budgets_getMonthObtiene el presupuesto de un mes específico
actual_budgets_setAmountEstablece el monto del presupuesto de una categoría. El mes debe ser uno que tenga el presupuesto (ver actual_budgets_getMonths)
actual_budgets_transferTransfiere montos entre categorías
actual_budgets_setCarryoverHabilita/deshabilita el arrastre
actual_budgets_holdForNextMonthRetiene fondos para el próximo mes. Actual limita la retención a lo que queda por presupuestar, por lo que la respuesta informa el monto realmente retenido
actual_budgets_resetHoldRestablece el estado de retención
actual_budgets_exportExporta el presupuesto activo como .zip en ACTUAL_EXPORT_DIR; devuelve ruta, tamaño en bytes y sha256, nunca el contenido del archivo
actual_budgets_importRestaura un presupuesto desde un .zip de Actual o una exportación de YNAB4/YNAB5. Destructivo: el id del presupuesto proviene del archivo, por lo que reimportar una exportación reemplaza los datos de ese presupuesto en lugar de hacer una copia. También carga el presupuesto importado, cambiando el presupuesto activo de la sesión

Reglas (6)

actual_rules_get · actual_rules_create · actual_rules_update · actual_rules_delete

HerramientaDescripción
actual_rules_create_or_updateUpsert idempotente: crea una regla, o actualiza la existente que coincida con las mismas condiciones, en una sola llamada. Úsalo en lugar de obtener-y-luego-crear al reejecutar una configuración de categorización, para que las ejecuciones repetidas no acumulen reglas duplicadas
actual_rules_create_batchCrea de 1 a 50 reglas en una llamada, cada una exactamente la entrada de actual_rules_create. De mejor esfuerzo, no atómico (ver Operaciones por lotes): una regla que falla sus propias verificaciones, nombra una categoría, beneficiario o cuenta que no existe, o es rechazada por Actual se informa con su índice mientras el resto continúa

Programaciones (4)

HerramientaDescripción
actual_schedules_getLista transacciones programadas, con su próxima fecha de ocurrencia
actual_schedules_createCrea una programación (única o recurrente); la recurrencia es una configuración tipada, ver src/lib/schemas/recur.ts
actual_schedules_updateActualiza el monto, beneficiario, cuenta o recurrencia de una programación existente
actual_schedules_deleteElimina una programación

Consulta avanzada y sincronización (2)

HerramientaDescripción
actual_query_runEjecuta una consulta ActualQL personalizada
actual_bank_syncActiva la sincronización bancaria (GoCardless/SimpleFIN)

Operaciones por lotes (1)

Cuatro herramientas aceptan muchos elementos en una llamada, para clientes que permiten solo unas pocas llamadas por turno. Comparten UN contrato: cada elemento se aplica de forma independiente y no atómica. No hay reversión, por lo que los elementos aplicados antes de un fallo permanecen aplicados; un elemento defectuoso se informa por su índice y el resto continúa; un fallo de infraestructura (conexión perdida, tiempo de espera) falla toda la llamada, y entonces debes leer los datos nuevamente antes de reintentar cualquier cosa. Una ruta masiva nunca reintenta una creación por sí sola.

  • actual_transactions_update_batch (contado en Transacciones): actualiza hasta 50 transacciones, {id, fields} cada una. Resultado: { succeeded: [{id}], failed: [{id, error}], total, successCount, failureCount }. Los elementos marcados como "no intentados" nunca se enviaron. Vuelve a leer con actual_transactions_get antes de reintentar cualquier elemento.
  • actual_transactions_import (contado en Transacciones): creación masiva con semántica de importación (deduplicación y conciliación contra transacciones existentes, reglas aplicadas opcionalmente). Úsalo para agregar muchas transacciones a la vez.
  • actual_budget_updates_batch: establece montos de presupuesto y/o banderas de arrastre para 1 a 100 pares mes/categoría, y la forma de reequilibrar categorías: lee el mes con actual_budgets_getMonth, luego envía un lote de montos absolutos. Cada operación necesita al menos uno de amount (centavos enteros) o carryover. Resultado: { succeeded: [{index, month, categoryId}], failed: [{index, month, categoryId, error}], total, successCount, failureCount }. Una categoría desconocida, un mes fuera de rango o un arrastre en una categoría de ingresos falla solo ese elemento. Después de un tiempo de espera, lee los meses nuevamente con actual_budgets_getMonth antes de reintentar.
  • actual_rules_create_batch (contado en Reglas): crea de 1 a 50 reglas, cada una exactamente la entrada de actual_rules_create. Resultado: { succeeded: [{index, id}], failed: [{index, error}], total, successCount, failureCount }. Los elementos marcados como "no intentados" nunca se enviaron. Vuelve a leer con actual_rules_get antes de reintentar cualquier elemento.

Información del servidor y búsqueda (5)

HerramientaDescripción
actual_server_infoEstado del servidor, versión, información de compilación
actual_server_get_versionVersión del servidor de Actual Budget
actual_get_contextRecupera cuentas abiertas, grupos de categorías con categorías anidadas y beneficiarios (ordenados por nombre, limitados por payeeLimit) en una sola llamada
actual_get_id_by_nameResuelve un nombre exacto → UUID para cuentas, categorías, beneficiarios
actual_entities_searchEncuentra cuentas/categorías/beneficiarios por un patrón de nombre (contiene/empiezaCon/terminaCon/exacto/difuso). Corrige "beneficiario no encontrado" por un nombre parcial o mal escrito

Gestión de sesión (2)

actual_session_list · actual_session_close


Configuración

Toda la configuración es mediante variables de entorno. Copia .env.example a .env para comenzar.

Referencia completa de variables de entorno

VariableDefaultRequeridoDescripción
Conexión de Actual Budget
ACTUAL_SERVER_URL(ninguno)SíURL de tu servidor de Actual Budget. Usa la misma URL que escribes en tu navegador: http://localhost:5006 (local), http://192.168.1.x:5006 (red), https://actual.yourdomain.com (dominio), o http://actual:5006 (nombre del contenedor si está en la misma red Docker)
ACTUAL_PASSWORD(ninguno)SíContraseña de inicio de sesión para tu servidor de Actual Budget, la que escribes en su propia pantalla de inicio de sesión. No es la misma que ACTUAL_BUDGET_PASSWORD (que descifra un presupuesto cifrado E2E) y no está relacionada con AUTH_PROVIDER=oidc (que controla cómo los clientes MCP se autentican en este servidor, no cómo este servidor se autentica en Actual). Este servidor siempre inicia sesión en Actual con una contraseña, por lo que el inicio de sesión con contraseña ascendente debe permanecer habilitado
ACTUAL_BUDGET_SYNC_ID(ninguno)SíID de sincronización del presupuesto desde Actual (Configuración y luego ID de sincronización)
ACTUAL_BUDGET_PASSWORD(ninguno)NoContraseña de cifrado opcional para presupuestos cifrados
ALLOW_INSECURE_UPSTREAMfalseNoPermitir un upstream http:// incluso cuando ACTUAL_BUDGET_PASSWORD está configurado (#161). Desactivado por defecto para que se rechace un upstream en texto plano más una contraseña de cifrado
ACTUAL_OP_TIMEOUT_MS30000NoTiempo de espera por operación (ms) para cada llamada a la API upstream de Actual (inicialización, descarga del presupuesto, sincronización y cada operación de herramienta). Una llamada estancada se rechaza después de este límite para que no pueda mantener el mutex global de la API para siempre y bloquear llamadas posteriores de herramientas (#270). Configúralo en 0 para desactivarlo
ACTUAL_IMPORT_TIMEOUT_MS600000NoTiempo de espera separado, más largo (ms) para una importación de presupuesto, que es legítimamente larga en lugar de estancada. Una importación es una carga rastreada, por lo que cada otra sesión espera en ella; limitarla al tiempo de espera general de operación convirtió la importación grande de un inquilino en un bloqueo de todo el proceso (#407). Configúralo en 0 para desactivarlo
Configuración del servidor MCP
MCP_BRIDGE_PORT3600NoPuerto en el que el servidor MCP escuchará
MCP_BRIDGE_BIND_HOST0.0.0.0NoDirección de host para vincular el servidor (0.0.0.0 = todas las interfaces)
MCP_BRIDGE_DATA_DIR./actual-dataNoDirectorio para almacenar datos locales de Actual Budget (SQLite). Se requiere que sea una ruta persistente. La biblioteca @actual-app/api descarga una copia local de tu presupuesto aquí para ejecutar consultas; usa un volumen montado en Docker para persistirlo entre reinicios
ACTUAL_EXPORT_DIR(sin configurar)NoDirectorio donde actual_budgets_export escribe los archivos de presupuesto .zip. Sin configurar significa <MCP_BRIDGE_DATA_DIR>/exports, que es escribible y persistido en la imagen Docker. Debe ser escribible por el usuario de ejecución
MCP_BRIDGE_PUBLIC_HOSTauto-detectadoNoNombre de host/IP público para el servidor (mostrado en los registros)
MCP_BRIDGE_PUBLIC_SCHEMEauto-detectadoNoEsquema público (http o https)
MCP_BRIDGE_USE_TLSfalseNoConfigúralo en true para anunciar https:// en la URL del servidor (para configuraciones de proxy inverso donde TLS se termina upstream)
Configuración de transporte
MCP_TRANSPORT_MODE--httpNoModo de transporte. Solo --http es un valor válido; stdio se selecciona mediante el indicador CLI --stdio, no esta variable
MCP_HTTP_PATH/httpNoRuta de enrutamiento del endpoint HTTP
MCP_BRIDGE_HTTP_PATHigual que MCP_HTTP_PATHNoRuta HTTP anunciada mostrada a los clientes (configurada cuando un proxy inverso reescribe la ruta)
MCP_HTTP_BODY_LIMIT512kbNoTamaño máximo aceptado del cuerpo de solicitud JSON-RPC (p. ej., 512kb, 1mb, 1048576). Validado al inicio: un tamaño inválido, cero o desbordado se niega a iniciar nombrando la variable (#466)
Superficie de herramientas
MCP_TOOLSETSallNoGrupos de conjuntos de herramientas separados por comas (context, transactions, analysis, budget, rules, schedules, structure, query, admin) y ajustes preestablecidos (chat) para publicar; all publica cada herramienta. Los nombres desconocidos se niegan a iniciar. Consulta Reduciendo la superficie de herramientas
MCP_TOOLS(vacío)NoHerramientas adicionales para agregar además de MCP_TOOLSETS, por nombre registrado completo (p. ej., actual_accounts_list). Los nombres desconocidos se niegan a iniciar
MCP_READ_ONLYfalseNotrue oculta y rechaza las 47 herramientas capaces de escribir. Solo se aceptan true o false (sin distinción de mayúsculas/minúsculas); cualquier otro valor se niega a iniciar
Gestión de sesiones
USE_CONNECTION_POOLtrueNoHabilitar agrupación de conexiones basada en sesiones
MAX_CONCURRENT_SESSIONS15NoMáximo de sesiones MCP concurrentes permitidas
SESSION_IDLE_TIMEOUT_MINUTES5NoMinutos antes de la limpieza de sesiones inactivas
Seguridad y autenticación
AUTH_PROVIDERnoneNoModo de autenticación: none (Bearer estático) o oidc (JWT validado por JWKS)
MCP_SSE_AUTHORIZATION(ninguno)NoToken Bearer estático (AUTH_PROVIDER=none; altamente recomendado en producción)
MCP_ALLOW_UNAUTHENTICATEDfalseNoOpción de exclusión para la autenticación HTTP requerida por defecto (#242). En un enlace no loopback sin token y sin OIDC, el servidor se niega a iniciar; configúralo en true para ejecutarlo abiertamente deliberadamente (p. ej., detrás de tu propio proxy)
OIDC_ISSUER(ninguno)Si OIDCURL del emisor OIDC (p. ej., https://sso.example.com)
OIDC_ALLOW_INSECURE_ISSUERfalseNoPermitir un emisor OIDC en texto plano (http) en una red confiable (#244). Desactivado por defecto (emisor http rechazado al inicio); configura true solo para pruebas locales/LAN
OIDC_RESOURCE(ninguno)Si OIDCURL canónica de este servidor, el identificador de recurso RFC 9728 y el aud esperado (#461). Usa la URL completa del endpoint MCP público, p. ej., https://actual-mcp.example.com/http, sin barra diagonal final. Debe ser una URL http(s) absoluta (un ID de cliente desnudo se niega a iniciar); coloca un aud de ID de cliente en OIDC_ACCEPTED_AUDIENCES en su lugar
OIDC_ACCEPTED_AUDIENCES(ninguno)NoValores aud adicionales aceptados más allá de OIDC_RESOURCE, separados por comas (#245). Para IdPs que colocan el ID de cliente en aud (p. ej., Authentik) o la URI de redirección del cliente OAuth (Cloudflare Access para SaaS). Lista de permitidos estricta, nunca un comodín
OIDC_JWKS_TRUSTED_HOSTS(ninguno)NoHosts JWKS de origen cruzado opt-in, host o host:port separados por comas (#254). Para IdPs cuyo jwks_uri vive en otro host, p. ej., Google necesita www.googleapis.com. Coincidencia exacta, sin comodines; el valor vacío por defecto mantiene solo el mismo origen
OIDC_JWKS_URI(ninguno)NoURL JWKS directa que omite el descubrimiento OpenID (#462), para emisores sin documento de descubrimiento (Cloudflare Access: https://<team>.cloudflareaccess.com/cdn-cgi/access/certs). Mismas reglas de https, credenciales y origen o host confiable que un jwks_uri descubierto, más sin consulta ni fragmento. Cuando se configura, /.well-known/oauth-authorization-server no se sirve y AUTH_BUDGET_ACL_IDENTITY_SOURCE=userinfo se rechaza
OIDC_TOKEN_SOURCEauthorizationNoDónde se lee el JWT Bearer (#462): authorization (por defecto) o cf-access-jwt-assertion para un servidor MCP detrás de Cloudflare Access con OAuth administrado (el borde reenvía el JWT de Access firmado en ese encabezado; Authorization nunca se consulta entonces, OIDC_SCOPES debe estar vacío, OIDC_SCOPES_SUPPORTED está permitido, y el origen debe ser alcanzable solo a través de Cloudflare). Consulta la receta de Cloudflare en docs/guides/AI_CLIENT_SETUP.md
OIDC_SCOPES(ninguno)NoÁmbitos requeridos (aplicados) separados por comas, también anunciados. Déjalo vacío cuando los JWT del IdP no llevan reclamo scope (Cloudflare Access para SaaS, o Casdoor cuando el cliente no solicita ámbito)
OIDC_SCOPES_SUPPORTED(ninguno)NoÁmbitos separados por comas anunciados en scopes_supported pero no aplicados. Úsalo cuando los JWT del IdP no llevan reclamo scope pero el cliente necesita un ámbito como offline_access anunciado (p. ej., Gemini con Cloudflare Access para SaaS). Los ámbitos requeridos de OIDC_SCOPES siempre se anuncian también. Anunciar offline_access desbloquea un token de actualización que este servidor no puede revocar: consulta Limitando un token de actualización filtrado
AUTH_BUDGET_ACL(ninguno)NoACL de presupuesto por usuario; consulta Configuración de clientes de IA. Validado al inicio: un valor que no es un objeto JSON de matrices de cadenas se niega a iniciar (#551). Sin configurar o {} significa sin restricciones
AUTH_BUDGET_ACL_SOURCEstaticNostatic usa el mapa AUTH_BUDGET_ACL anterior. actual deriva la ACL de la propia lista de acceso por archivo del servidor de Actual, por lo que revocar a alguien en Actual tiene efecto aquí sin editar la configuración. Requiere un servidor de Actual multiusuario (OpenID) que se haya inicializado primero con contraseña
AUTH_BUDGET_ACL_CLAIMautoNoQué reclamo de token identifica al principal cuando la fuente es actual. auto refleja la propia precedencia de Actual (preferred_username, login, email, id, sub) y compara el resultado contra el userName de Actual. Configura un solo nombre de reclamo para fijar uno en su lugar
AUTH_BUDGET_ACL_IDENTITY_MAP(vacío)NoEnlaces <sub>=<Actual userName> explícitos, separados por comas, usados cuando la fuente es actual. Se consultan antes de la precedencia de reclamos y son autoritativos: un sub vinculado cuyo objetivo no coincide con ningún archivo de presupuesto se niega en lugar de recurrir a un reclamo. Úsalo cuando tu token de acceso no lleva el reclamo que Actual almacenó, o después de un cambio de nombre del lado del IdP
AUTH_BUDGET_ACL_IDENTITY_SOURCEtokenNoDónde la ACL lee los reclamos de identidad cuando la fuente es actual. token usa el token de acceso verificado (sin llamada de red). userinfo llama al endpoint UserInfo del IdP, que es el mismo documento del que Actual deriva userName, y corrige implementaciones donde el token de acceso carece del reclamo que Actual almacenó. Opt-in porque hace que el IdP sea una dependencia dura de la autorización
AUTH_BUDGET_ACL_USERINFO_TIMEOUT_MS5000NoTiempo de espera para la solicitud UserInfo, limitado de 250 ms a 60 s. Sin valor de desactivación: una llamada sin límite en la ruta de autenticación convierte un IdP lento en una interrupción
MCP_ENABLE_HTTPSfalseNoHabilitar TLS nativo. Requiere MCP_HTTPS_CERT y MCP_HTTPS_KEY
MCP_HTTPS_CERT(ninguno)NoRuta al archivo de certificado PEM (requerido cuando MCP_ENABLE_HTTPS=true)
MCP_HTTPS_KEY(ninguno)NoRuta al archivo de clave privada PEM (requerido cuando MCP_ENABLE_HTTPS=true)
Configuración de registro
MCP_BRIDGE_STORE_LOGSfalseNoHabilitar registro en archivo (vs solo consola)
MCP_BRIDGE_LOG_DIRapp/logs (junto a la instalación)NoDirectorio para archivos de registro (si STORE_LOGS=true). .env.example y Docker lo configuran explícitamente (p. ej., ./logs, /app/logs)
MCP_BRIDGE_LOG_LEVELdebug (dev) / info (prod)NoNivel de registro: error, warn, info, debug
LOG_FORMATautoNoFormato de salida de registro: json o pretty. Precedencia: LOG_FORMAT explícito gana, si no NODE_ENV=production selecciona json, si no pretty
MCP_SERVICE_NAMEactual-mcp-serverNoNombre del servicio estampado en cada registro estructurado (json)
Rotación de registros (cuando MCP_BRIDGE_STORE_LOGS=true)
MCP_BRIDGE_MAX_FILES14dNoConservar registros rotados por N días (p. ej., 14d, 30d)
MCP_BRIDGE_MAX_LOG_SIZE20mNoRotar cuando el archivo alcanza el tamaño (p. ej., 20m, 100m)
MCP_BRIDGE_ROTATE_DATEPATTERNYYYY-MM-DDNoPatrón de fecha para nombres de archivos de registro rotados
Desarrollo y depuración
DEBUG(ninguno)NoHabilitar modo de depuración (registro detallado) cuando se configura con cualquier valor verdadero
LOG_LEVEL(ninguno)NoInterruptor de detección de depuración: configúrelo en debug para habilitar la salida de depuración adicional del transporte. Es distinto de MCP_BRIDGE_LOG_LEVEL (el nivel de winston); no tiene valor predeterminado y no es en sí mismo un nivel de registro
MCP_BRIDGE_DEBUG_TRANSPORTfalseNoHabilitar registro de depuración a nivel de transporte
Avanzado/Interno
ACTUAL_API_CONCURRENCY5NoMáximo de operaciones concurrentes de la API de Actual
NODE_ENV(ninguno) / productionNoEntorno de Node. Sin valor predeterminado de la aplicación; la imagen de Docker establece production, que selecciona registros JSON y oculta los rastreos de pila en las respuestas de error
VERSIONauto-detectadoNoVersión del servidor (configurada automáticamente por build/Docker)
TZUTCNoZona horaria para marcas de tiempo (p. ej., America/New_York)

Reducción de la superficie de herramientas

Las 84 herramientas se publican por defecto. Los clientes de chat con un presupuesto de herramientas pequeño (Gemini, LibreChat) funcionan mejor con menos, por lo que la superficie se puede reducir al inicio. Una herramienta que no está publicada también se rechaza si un cliente la llama por su nombre, y el rechazo menciona el ajuste responsable.

Conjunto de herramientasHerramientasContenido
context14Orientación, consultas e información del servidor
transactions13Lecturas de transacciones, crear, actualizar, eliminar, importar y transferencias
analysis7Resúmenes, agregados y saldos
budget10Meses de presupuesto, montos, arrastre, retenciones, transferencias, actualizaciones por lotes y notas
rules7Reglas y reglas de beneficiarios, incluyendo actual_rules_create_batch
schedules4Transacciones programadas
structure21Cuentas, grupos de cuentas, categorías, grupos de categorías, beneficiarios y etiquetas
query1actual_query_run (solo SELECT)
admin6Cambio de presupuesto, importación, exportación, sincronización bancaria y gestión de sesiones

Cada herramienta está exactamente en un grupo. El ajuste predefinido chat los atraviesa y publica 12 herramientas: actual_get_context, actual_query_run, actual_transactions_create, actual_transactions_update, actual_transactions_delete, actual_transactions_update_batch, actual_rules_create, actual_rules_create_batch, actual_budget_updates_batch, actual_budgets_transfer, actual_budgets_setAmount y actual_budgets_getMonth. Medido en el payload de tools/list, las 84 herramientas son 108,164 bytes y el ajuste predefinido chat es 25,949 bytes (aproximadamente 6.5k tokens ahorrados, una reducción del 76%).

MCP_READ_ONLY=true además oculta y rechaza toda herramienta con capacidad de escritura (47 de las 83, incluyendo actual_bank_sync, actual_budgets_export, actual_budgets_switch y actual_session_close), dejando 36. actual_session_list permanece disponible en modo solo lectura. Ver Seguridad y Privacidad.

Ejemplo: un asistente de chat.

MCP_TOOLSETS=chat
MCP_TOOLS=actual_accounts_list,actual_payees_get

Ejemplo: un asistente doméstico de solo lectura.

MCP_TOOLSETS=context,analysis,query
MCP_READ_ONLY=true

Una configuración que resuelve a cero herramientas se niega a iniciar.


Cambio de múltiples presupuestos

Configure múltiples archivos de Actual Budget para que la IA pueda cambiar entre ellos en tiempo de ejecución usando actual_budgets_list_available y actual_budgets_switch.

BUDGET_N_SERVER_URL y BUDGET_N_PASSWORD recurren a ACTUAL_SERVER_URL / ACTUAL_PASSWORD cuando se omiten.

VariableRequeridaRecurso alternativo
BUDGET_DEFAULT_NAMENo"Default"
BUDGET_N_NAMESí (habilita el grupo)(ninguno)
BUDGET_N_SYNC_IDSí(ninguno)
BUDGET_N_SERVER_URLNoACTUAL_SERVER_URL
BUDGET_N_PASSWORDNoACTUAL_PASSWORD
BUDGET_N_ENCRYPTION_PASSWORDNo(ninguno)
# Default budget
ACTUAL_SERVER_URL=http://actual:5006
ACTUAL_PASSWORD=my-password
ACTUAL_BUDGET_SYNC_ID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
BUDGET_DEFAULT_NAME=Personal

# Budget 1 (same server, same password)
BUDGET_1_NAME=Family
BUDGET_1_SYNC_ID=bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb

# Budget 2 (different server)
BUDGET_2_NAME=Business
BUDGET_2_SERVER_URL=https://actual-office.example.com
BUDGET_2_PASSWORD=office-password
BUDGET_2_SYNC_ID=cccccccc-cccc-cccc-cccc-cccccccccccc

Transporte y autenticación

El servidor admite dos modos de transporte:

ModoIndicadorCaso de usoAutenticación
HTTP--httpLibreChat, LobeChat, Docker, implementaciones multiusuarioToken Bearer u OIDC
stdio--stdioClaude Desktop, Cursor, uso local de un solo usuarioNinguna (aislamiento de procesos del sistema operativo)

Los dos modos son mutuamente excluyentes. Pase exactamente un indicador al iniciar el servidor.

Transporte stdio

stdio es la forma más sencilla de conectar Claude Desktop directamente a Actual Budget. El servidor MCP se ejecuta como un proceso hijo; Claude Desktop lo genera, se comunica a través de stdin/stdout usando NDJSON (el formato de cable MCP), y el proceso sale limpiamente cuando Claude Desktop se cierra.

Propiedades clave del modo stdio:

  • Sin puerto de red. El transporte es una tubería, no un socket.
  • Sin token de autenticación. La propiedad del proceso es el límite de seguridad.
  • Todos los registros van a stderr para que nunca corrompan el marco JSON-RPC en stdout.
  • El proceso sale cuando stdin se cierra (apagado de Claude Desktop).
  • Las 84 herramientas están disponibles, idénticas al modo HTTP.

Iniciar manualmente para verificar:

cd /path/to/actual-mcp-server
ACTUAL_SERVER_URL=http://localhost:5006 \
ACTUAL_PASSWORD=your_password \
ACTUAL_BUDGET_SYNC_ID=your-sync-id \
node dist/src/index.js --stdio

Envíe una solicitud de prueba (mantenga stdin abierto con sleep):

{ echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'; sleep 5; } \
| ACTUAL_SERVER_URL=http://localhost:5006 ACTUAL_PASSWORD=your_password ACTUAL_BUDGET_SYNC_ID=your-sync-id \
  node dist/src/index.js --stdio 2>/dev/null

Configuración de Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "actual-budget": {
      "command": "node",
      "args": ["/absolute/path/to/actual-mcp-server/dist/src/index.js", "--stdio"],
      "env": {
        "ACTUAL_SERVER_URL": "http://localhost:5006",
        "ACTUAL_PASSWORD": "your_actual_password",
        "ACTUAL_BUDGET_SYNC_ID": "your-sync-id-here",
        "MCP_BRIDGE_DATA_DIR": "/absolute/path/to/actual-mcp-server/actual-data"
      }
    }
  }
}

La ruta debe ser absoluta. Claude Desktop no hereda PATH del shell, por lo que node también debe ser absoluta si usa NVM o una instalación no estándar: /home/youruser/.nvm/versions/node/v22.x.x/bin/node.

Consulte docs/guides/MCP_CLIENTS_SETUP.md para todas las opciones de conexión (stdio nativo, mcp-remote vía HTTP/HTTPS), otros clientes (Cursor, VS Code, Gemini CLI, Claude Code), correcciones de rutas en Linux y solución de problemas.

Transporte HTTP

El transporte HTTP usa el endpoint /http (StreamableHTTP) con autenticación opcional de token Bearer u OIDC.

Token Bearer estático (un solo usuario)

# Generate a token
openssl rand -hex 32

# Add to .env
MCP_SSE_AUTHORIZATION=your_token_here

Los clientes envían: Authorization: Bearer your_token_here

OIDC (multiusuario)

AUTH_PROVIDER=oidc
OIDC_ISSUER=https://sso.yourdomain.com
OIDC_RESOURCE=https://actual-mcp.yourdomain.com/http   # this server's public MCP URL (the expected 'aud')
OIDC_ACCEPTED_AUDIENCES=your-client-id                  # only if your IdP puts the client id in 'aud' (#245)
OIDC_SCOPES=                                            # leave empty when the tokens carry no scope claim
# OIDC_SCOPES_SUPPORTED=openid,offline_access           # extra scopes advertised in discovery without enforcement (#472)

OIDC_RESOURCE es la URL canónica de este servidor MCP (el "URI canónico del servidor" de la especificación de autorización MCP y el identificador de recurso RFC 9728), no un ID de cliente IdP: la ruta de metadatos del recurso protegido y la audiencia esperada del token se derivan de ella, y un valor que no sea una URL http(s) absoluta se niega a iniciar con un mensaje que nombra la variable (#461). Use la URL completa del endpoint público (https://host/http, o lo que MCP_BRIDGE_HTTP_PATH / MCP_HTTP_PATH anuncie) en lugar del origen desnudo: con la forma de endpoint, el servidor publica tanto el documento específico de la ruta (canónico) como una copia raíz para clientes que solo miran la raíz (#473), y advierte al inicio cuando la ruta del recurso y la ruta anunciada no coinciden.

Endpoints de descubrimiento OAuth (automáticos en modo OIDC). Cuando AUTH_PROVIDER=oidc, el servidor publica los dos documentos de metadatos que un cliente OAuth necesita para iniciar un inicio de sesión, por lo que mcp-remote y el conector nativo de Claude.ai pueden descubrir el flujo sin configuración manual de endpoints:

  • GET /.well-known/oauth-protected-resource[<path>] (RFC 9728): identifica este servidor como un recurso protegido y apunta a su OIDC_ISSUER como servidor de autorización. La ruta sigue a OIDC_RESOURCE: con https://host/http, el documento canónico se sirve en /.well-known/oauth-protected-resource/http (RFC 9728 sección 3.1) y también se sirve una copia raíz en /.well-known/oauth-protected-resource para clientes que omiten la URL específica de la ruta (por ejemplo, Google Gemini, #473); el origen desnudo sirve solo la ruta raíz. El SDK oficial de MCP intenta la URL específica de la ruta primero y recurre a la raíz.
  • GET /.well-known/oauth-authorization-server (RFC 8414, #285): los metadatos del servidor de autorización (su authorization_endpoint / token_endpoint / registration_endpoint), re-servidos desde el documento de descubrimiento OpenID de su propio IdP. Esto está aquí porque varios clientes lo buscan en el origen del servidor de recursos, y algunos IdP (por ejemplo, Authentik) no lo exponen donde esos clientes lo buscan. Se obtiene una vez al inicio y se sirve textualmente, expone solo endpoints que su IdP ya publica públicamente y no requiere autenticación (un cliente lo lee antes de tener un token). No se necesita configuración adicional; está ausente cuando AUTH_PROVIDER no es oidc.

De dónde proviene la ACL de presupuesto por usuario. Por defecto, la ACL es el mapa AUTH_BUDGET_ACL que mantiene manualmente. Desde v0.10.x se puede derivar de la propia lista de acceso por archivo del servidor Actual, por lo que otorgar o revocar a alguien en Actual tiene efecto aquí sin editar la configuración y reiniciar:

AUTH_BUDGET_ACL_SOURCE=actual   # default: static (the AUTH_BUDGET_ACL map)
AUTH_BUDGET_ACL_CLAIM=auto      # default: mirrors Actual's own claim precedence

Cómo funciona la coincidencia. Actual no almacena el sub de su IdP. Cuando un usuario inicia sesión, Actual resuelve su identidad como el primer valor no vacío de preferred_username, login, email, id, sub, guarda esa cadena como el userName del usuario y genera su propio UUID no relacionado como userId. Por lo tanto, la ACL compara su token contra userName, usando esa misma precedencia. AUTH_BUDGET_ACL_CLAIM=auto (el predeterminado) hace exactamente esto; configúrelo a un solo nombre de reclamo para fijar uno en su lugar.

Tres cosas que debe saber antes de habilitarlo:

  • Requiere un servidor Actual multiusuario, es decir, uno configurado con ACTUAL_OPENID_*. En un servidor solo con contraseña no existen usuarios nombrados y cada principal se resuelve a nada.
  • Su servidor Actual debe haber sido inicializado con una CONTRASEÑA antes de que se agregara OpenID. Este es un requisito previo estricto de este servidor MCP en general, no solo de esta característica: @actual-app/api se autentica con una contraseña, y un servidor Actual configurado con OpenID desde cero rechaza el inicio de sesión con contraseña, por lo que este servidor no puede conectarse a él en absoluto. Una contraseña no se puede agregar después.
  • La identidad es tan confiable como el reclamo detrás de ella. OIDC garantiza que sub es único y nunca se reasigna; preferred_username y email no tienen tal garantía y a menudo son editables por el usuario. Esta exposición se hereda de Actual, que clavea sus propias cuentas de la misma manera, no se introduce aquí: cualquiera que pueda establecer su preferred_username al suyo puede hacerse pasar por usted en el inicio de sesión de Actual también. Si eso le importa, fije AUTH_BUDGET_ACL_CLAIM a un reclamo que su IdP no permita editar a los usuarios, y asegúrese de que ese sea el reclamo que su IdP usa para poblar el user_name de Actual.

Es opcional y falla cerrado: si la lista de presupuestos no se puede leer, o el principal no coincide con ningún archivo, el acceso se deniega en lugar de otorgarse.

Consulte Configuración de clientes de IA, OIDC para el formato AUTH_BUDGET_ACL, notas de Casdoor y la receta de Cloudflare Access para SaaS + Gemini.


Pruebas

ComandoQué pruebaRequiere servidor en vivo
npm run buildCompilación de TypeScriptNo
npm run test:unit-jsPrueba de humo de 84 herramientas, validación de esquema, ACL de autenticaciónNo
npm run test:adapterAdaptador, lógica de reintento, concurrenciaNo
npm run test:e2eCumplimiento del protocolo MCP (Playwright)No
npm run test:e2e:docker:fullIntegración de pila completaSí (Docker)
npm run test:integrationVerificaciones de sanidad del servidor en vivoSí
npm run test:integration:fullSuite completa de integración en vivoSí

Niveles de prueba de integración (tests/manual/): sanity → smoke → normal → extended → full → cleanup

Consulte tests/manual/README.md y tests/e2e/README.md para más detalles.


Documentación

DocumentoContenido
docs/guides/MCP_CLIENTS_SETUP.mdEmpiece aquí para conectar Claude Desktop, Cursor, VS Code (Copilot), Gemini CLI o Claude Code
docs/guides/AI_CLIENT_SETUP.mdConfiguración de LibreChat y LobeChat, redes Docker, proxy HTTPS/TLS, OIDC
docs/guides/DEPLOYMENT.mdDocker, perfiles de Docker Compose, configuración de producción, Kubernetes
docs/ARCHITECTURE.mdCapas de componentes, flujo de datos, protocolos de transporte
docs/SECURITY_AND_PRIVACY.mdModelos de autenticación, modelo de amenazas, endurecimiento
docs/TESTING_AND_RELIABILITY.mdEstrategia de pruebas, cobertura, patrones de confiabilidad
docs/NEW_TOOL_CHECKLIST.mdGuía paso a paso para agregar una nueva herramienta MCP
CONTRIBUTING.mdConfiguración de desarrollo, estándares de código, proceso de PR
.env.exampleReferencia de variables de entorno completamente anotada

Contribuciones

¡Las contribuciones son bienvenidas! Consulte CONTRIBUTING.md para la configuración de desarrollo, estándares de código y el proceso de PR.

Flujo rápido:

  1. Fork → git checkout -b feature/my-feature
  2. Haga cambios + agregue pruebas
  3. npm run build && npm run test:unit-js debe pasar
  4. Abra una Solicitud de extracción

Arquitectura

  • Tiempo de ejecución: Node.js 22 (Alpine Linux en Docker)
  • Lenguaje: TypeScript (ESM, resolución de módulos NodeNext)
  • SDK MCP: @modelcontextprotocol/sdk
  • API de Actual: @actual-app/api
  • Validación: Zod (tipos de tiempo de ejecución + JSON Schema para entradas de herramientas)
  • Transportes: Express + StreamableHTTP (--http) · StdioServerTransport (--stdio)
  • Registro: Winston con rotación diaria (todo el resultado se enruta a stderr en modo stdio) Todas las llamadas a la API de Actual pasan por el wrapper withActualApi() en src/lib/actual-adapter.ts, que gestiona el ciclo de vida de init/shutdown, reintentos (3 intentos, retroceso exponencial) y limitación de concurrencia. Consulta docs/ARCHITECTURE.md para la documentación completa del diseño.

Licencia

MIT. Consulta LICENSE para más detalles.


Agradecimientos


Aviso legal

Este proyecto comenzó como un ejercicio de aprendizaje personal para explorar la tecnología Model Context Protocol. Es un proyecto independiente de código abierto, no afiliado, respaldado ni apoyado por Actual Budget ni por ninguna otra organización.

El software se proporciona tal cual, sin garantía de ningún tipo. El autor no acepta responsabilidad alguna por su uso, ni por pérdida de datos, errores financieros u otras consecuencias derivadas de su utilización. Si lo conectas a datos financieros reales, lo haces bajo tu propio riesgo.


Soporte

  • GitHub Issues: informes de errores, solicitudes de funciones, preguntas e ideas

Versión: 0.22.46 | Número de herramientas: 84 (verificado compatible con LibreChat)

Patrocinador

Construyo y mantengo esto en mi tiempo libre. Es gratuito, seguirá siendo gratuito y se mantendrá de todos modos.

Si te ha ahorrado tiempo y quieres dar las gracias, puedes hacerlo en github.com/sponsors/agigante80. Totalmente opcional, y nada del proyecto cambia en ningún caso.