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
Servidor Actual MCP
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
- Actualización
- Herramientas Disponibles
- Configuración
- Cambio de Múltiples Presupuestos
- Transporte y Autenticación
- Pruebas
- Documentación
- Contribución
- Arquitectura
- Licencia
- Agradecimientos
- Aviso Legal
- Soporte
- Patrocinio
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/datale 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_URLapunte 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
devyproduction. El servidor MCP escucha en:3600directamente: no hay proxy inverso incluido ni servidor de Actual Budget incluido, así que apuntaACTUAL_SERVER_URLa tu propia instancia de Actual. Para TLS, habilita HTTPS nativo conMCP_ENABLE_HTTPS=true(además deMCP_HTTPS_CERTyMCP_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 --versionprimero. En un Node más antiguo, el servidor se niega a iniciar y te lo indica. Ten en cuenta quenpxejecuta elnodeque esté primero en tuPATH, 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_DIRdebe 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)
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):
- Abre la pestaña Apps y busca
actual-mcp-server, luego haz clic en Instalar. - 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).
- 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). - 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. - Accede al endpoint de salud a través del enlace WebUI del contenedor (puerto
3600); apunta tu cliente MCP ahttp://[server-ip]:3600/httpcon 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)
| Herramienta | Descripción |
|---|---|
actual_accounts_list | Listar todas las cuentas |
actual_accounts_create | Crear nueva cuenta |
actual_accounts_update | Actualizar detalles de la cuenta. Un id que no existe se rechaza en lugar de crear un registro parcial |
actual_accounts_delete | Eliminar cuenta permanentemente |
actual_accounts_close | Cerrar cuenta. Una cuenta SIN transacciones es ELIMINADA por Actual, no cerrada |
actual_accounts_reopen | Reabrir cuenta cerrada. Un id que no es una cuenta se rechaza en lugar de crear una |
actual_accounts_get_balance | Obtener saldo de cuenta en una fecha |
actual_account_flow_summary | Explicar 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_list | Listar los grupos de cuentas personalizados utilizados para organizar cuentas en la barra lateral (Actual 26.9.0+) |
actual_account_groups_create | Crear un grupo de cuentas. Asignar cuentas a él es una llamada separada de actual_accounts_update |
actual_account_groups_update | Renombrar 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_delete | Eliminar un grupo de cuentas. Sus cuentas NO se eliminan, quedan sin agrupar |
Transacciones (17)
Estándar (7)
| Herramienta | Descripción |
|---|---|
actual_transactions_get | Obtener transacciones de una cuenta |
actual_transactions_filter | Filtrar con criterios avanzados |
actual_transactions_create | Crear nueva(s) transacción(es), incluyendo divisiones (pasa un array subtransactions) |
actual_transactions_import | Importar y conciliar transacciones |
actual_transactions_update | Actualizar una transacción, dividir una simple en su lugar, o editar los hijos de una división existente vía subtransactions |
actual_transactions_delete | Eliminar una transacción |
actual_transactions_merge | Fusionar 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 unamounten centavos enteros;categoryynotesson opcionales). Los montos de los hijos deben sumar elamountdel 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_updateconsubtransactionsedita los hijos de una transacción que ya es dividida, o divide una transacción simple EN EL MISMO LUGAR: conserva su id,imported_idy 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_importreenvía un arraysubtransactionsa la API pero NO aplica la verificación de suma, así que usaactual_transactions_createpara divisiones con saldo garantizado.
Utilidad (2)
| Herramienta | Descripción |
|---|---|
actual_transactions_uncategorized | Resumen de transacciones sin categorizar (totalCount, totalAmount, desglose por cuenta); pasa includeTransactions:true para filas paginadas |
actual_transactions_update_batch | Aplica 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
| Herramienta | Descripción |
|---|---|
actual_transactions_aggregate | Totales 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_month | Busca por mes usando la transformación $month |
actual_transactions_search_by_amount | Encuentra por rango de montos |
actual_transactions_search_by_category | Busca por nombre de categoría |
actual_transactions_search_by_payee | Encuentra por beneficiario/proveedor |
actual_transactions_summary_by_category | Resumen de gastos agrupado por categoría |
actual_transactions_summary_by_payee | Principales proveedores con totales y conteos |
actual_recurring_expenses_summary | Detecta 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)
| Herramienta | Descripción |
|---|---|
actual_transfers_create | Crea 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_createpara cualquier movimiento entre cuentas, noactual_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, yfrom_accountdebe diferir deto_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
| Herramienta | Descripción |
|---|---|
actual_tags_list | Lista todas las etiquetas (id, palabra de etiqueta, color y descripción opcionales) |
actual_tags_create | Crea o actualiza una etiqueta por nombre; devuelve el UUID de la etiqueta |
actual_tags_update | Actualiza nombre, color o descripción de la etiqueta por UUID |
actual_tags_delete | Eliminación suave de una etiqueta por UUID |
Notas (2)
actual_notes_get · actual_notes_update
| Herramienta | Descripción |
|---|---|
actual_notes_get | Obtiene la nota de cualquier entidad (UUID de cuenta/categoría/grupo de categorías/beneficiario, o presupuesto-AAAA-MM) |
actual_notes_update | Establece o limpia la nota de cualquier entidad; valida que la entidad exista o coincida con el patrón presupuesto-AAAA-MM |
Preferencias (1)
| Herramienta | Descripción |
|---|---|
actual_preferences_get | Lee 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)
| Herramienta | Descripción |
|---|---|
actual_budgets_list_available | Lista todos los archivos de presupuesto configurados |
actual_budgets_switch | Cambia el presupuesto activo (multi-presupuesto) |
actual_budgets_get_all | Lista los archivos de presupuesto disponibles |
actual_budgets_getMonths | Lista los meses del presupuesto |
actual_budgets_getMonth | Obtiene el presupuesto de un mes específico |
actual_budgets_setAmount | Establece el monto del presupuesto de una categoría. El mes debe ser uno que tenga el presupuesto (ver actual_budgets_getMonths) |
actual_budgets_transfer | Transfiere montos entre categorías |
actual_budgets_setCarryover | Habilita/deshabilita el arrastre |
actual_budgets_holdForNextMonth | Retiene 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_resetHold | Restablece el estado de retención |
actual_budgets_export | Exporta el presupuesto activo como .zip en ACTUAL_EXPORT_DIR; devuelve ruta, tamaño en bytes y sha256, nunca el contenido del archivo |
actual_budgets_import | Restaura 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
| Herramienta | Descripción |
|---|---|
actual_rules_create_or_update | Upsert 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_batch | Crea 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)
| Herramienta | Descripción |
|---|---|
actual_schedules_get | Lista transacciones programadas, con su próxima fecha de ocurrencia |
actual_schedules_create | Crea una programación (única o recurrente); la recurrencia es una configuración tipada, ver src/lib/schemas/recur.ts |
actual_schedules_update | Actualiza el monto, beneficiario, cuenta o recurrencia de una programación existente |
actual_schedules_delete | Elimina una programación |
Consulta avanzada y sincronización (2)
| Herramienta | Descripción |
|---|---|
actual_query_run | Ejecuta una consulta ActualQL personalizada |
actual_bank_sync | Activa 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 conactual_transactions_getantes 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 conactual_budgets_getMonth, luego envía un lote de montos absolutos. Cada operación necesita al menos uno deamount(centavos enteros) ocarryover. 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 conactual_budgets_getMonthantes de reintentar.actual_rules_create_batch(contado en Reglas): crea de 1 a 50 reglas, cada una exactamente la entrada deactual_rules_create. Resultado:{ succeeded: [{index, id}], failed: [{index, error}], total, successCount, failureCount }. Los elementos marcados como "no intentados" nunca se enviaron. Vuelve a leer conactual_rules_getantes de reintentar cualquier elemento.
Información del servidor y búsqueda (5)
| Herramienta | Descripción |
|---|---|
actual_server_info | Estado del servidor, versión, información de compilación |
actual_server_get_version | Versión del servidor de Actual Budget |
actual_get_context | Recupera 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_name | Resuelve un nombre exacto → UUID para cuentas, categorías, beneficiarios |
actual_entities_search | Encuentra 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
| Variable | Default | Requerido | Descripció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) | No | Contraseña de cifrado opcional para presupuestos cifrados |
ALLOW_INSECURE_UPSTREAM | false | No | Permitir 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_MS | 30000 | No | Tiempo 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_MS | 600000 | No | Tiempo 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_PORT | 3600 | No | Puerto en el que el servidor MCP escuchará |
MCP_BRIDGE_BIND_HOST | 0.0.0.0 | No | Dirección de host para vincular el servidor (0.0.0.0 = todas las interfaces) |
MCP_BRIDGE_DATA_DIR | ./actual-data | No | Directorio 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) | No | Directorio 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_HOST | auto-detectado | No | Nombre de host/IP público para el servidor (mostrado en los registros) |
MCP_BRIDGE_PUBLIC_SCHEME | auto-detectado | No | Esquema público (http o https) |
MCP_BRIDGE_USE_TLS | false | No | Configú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 | --http | No | Modo de transporte. Solo --http es un valor válido; stdio se selecciona mediante el indicador CLI --stdio, no esta variable |
MCP_HTTP_PATH | /http | No | Ruta de enrutamiento del endpoint HTTP |
MCP_BRIDGE_HTTP_PATH | igual que MCP_HTTP_PATH | No | Ruta HTTP anunciada mostrada a los clientes (configurada cuando un proxy inverso reescribe la ruta) |
MCP_HTTP_BODY_LIMIT | 512kb | No | Tamañ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_TOOLSETS | all | No | Grupos 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) | No | Herramientas 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_ONLY | false | No | true 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_POOL | true | No | Habilitar agrupación de conexiones basada en sesiones |
MAX_CONCURRENT_SESSIONS | 15 | No | Máximo de sesiones MCP concurrentes permitidas |
SESSION_IDLE_TIMEOUT_MINUTES | 5 | No | Minutos antes de la limpieza de sesiones inactivas |
| Seguridad y autenticación | |||
AUTH_PROVIDER | none | No | Modo de autenticación: none (Bearer estático) o oidc (JWT validado por JWKS) |
MCP_SSE_AUTHORIZATION | (ninguno) | No | Token Bearer estático (AUTH_PROVIDER=none; altamente recomendado en producción) |
MCP_ALLOW_UNAUTHENTICATED | false | No | Opció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 OIDC | URL del emisor OIDC (p. ej., https://sso.example.com) |
OIDC_ALLOW_INSECURE_ISSUER | false | No | Permitir 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 OIDC | URL 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) | No | Valores 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) | No | Hosts 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) | No | URL 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_SOURCE | authorization | No | Dó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) | No | ACL 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_SOURCE | static | No | static 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_CLAIM | auto | No | Qué 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) | No | Enlaces <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_SOURCE | token | No | Dó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_MS | 5000 | No | Tiempo 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_HTTPS | false | No | Habilitar TLS nativo. Requiere MCP_HTTPS_CERT y MCP_HTTPS_KEY |
MCP_HTTPS_CERT | (ninguno) | No | Ruta al archivo de certificado PEM (requerido cuando MCP_ENABLE_HTTPS=true) |
MCP_HTTPS_KEY | (ninguno) | No | Ruta al archivo de clave privada PEM (requerido cuando MCP_ENABLE_HTTPS=true) |
| Configuración de registro | |||
MCP_BRIDGE_STORE_LOGS | false | No | Habilitar registro en archivo (vs solo consola) |
MCP_BRIDGE_LOG_DIR | app/logs (junto a la instalación) | No | Directorio para archivos de registro (si STORE_LOGS=true). .env.example y Docker lo configuran explícitamente (p. ej., ./logs, /app/logs) |
MCP_BRIDGE_LOG_LEVEL | debug (dev) / info (prod) | No | Nivel de registro: error, warn, info, debug |
LOG_FORMAT | auto | No | Formato 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_NAME | actual-mcp-server | No | Nombre del servicio estampado en cada registro estructurado (json) |
Rotación de registros (cuando MCP_BRIDGE_STORE_LOGS=true) | |||
MCP_BRIDGE_MAX_FILES | 14d | No | Conservar registros rotados por N días (p. ej., 14d, 30d) |
MCP_BRIDGE_MAX_LOG_SIZE | 20m | No | Rotar cuando el archivo alcanza el tamaño (p. ej., 20m, 100m) |
MCP_BRIDGE_ROTATE_DATEPATTERN | YYYY-MM-DD | No | Patrón de fecha para nombres de archivos de registro rotados |
| Desarrollo y depuración | |||
DEBUG | (ninguno) | No | Habilitar modo de depuración (registro detallado) cuando se configura con cualquier valor verdadero |
LOG_LEVEL | (ninguno) | No | Interruptor 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_TRANSPORT | false | No | Habilitar registro de depuración a nivel de transporte |
| Avanzado/Interno | |||
ACTUAL_API_CONCURRENCY | 5 | No | Máximo de operaciones concurrentes de la API de Actual |
NODE_ENV | (ninguno) / production | No | Entorno 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 |
VERSION | auto-detectado | No | Versión del servidor (configurada automáticamente por build/Docker) |
TZ | UTC | No | Zona 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 herramientas | Herramientas | Contenido |
|---|---|---|
context | 14 | Orientación, consultas e información del servidor |
transactions | 13 | Lecturas de transacciones, crear, actualizar, eliminar, importar y transferencias |
analysis | 7 | Resúmenes, agregados y saldos |
budget | 10 | Meses de presupuesto, montos, arrastre, retenciones, transferencias, actualizaciones por lotes y notas |
rules | 7 | Reglas y reglas de beneficiarios, incluyendo actual_rules_create_batch |
schedules | 4 | Transacciones programadas |
structure | 21 | Cuentas, grupos de cuentas, categorías, grupos de categorías, beneficiarios y etiquetas |
query | 1 | actual_query_run (solo SELECT) |
admin | 6 | Cambio 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.
| Variable | Requerida | Recurso alternativo |
|---|---|---|
BUDGET_DEFAULT_NAME | No | "Default" |
BUDGET_N_NAME | Sí (habilita el grupo) | (ninguno) |
BUDGET_N_SYNC_ID | Sí | (ninguno) |
BUDGET_N_SERVER_URL | No | ACTUAL_SERVER_URL |
BUDGET_N_PASSWORD | No | ACTUAL_PASSWORD |
BUDGET_N_ENCRYPTION_PASSWORD | No | (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:
| Modo | Indicador | Caso de uso | Autenticación |
|---|---|---|---|
| HTTP | --http | LibreChat, LobeChat, Docker, implementaciones multiusuario | Token Bearer u OIDC |
| stdio | --stdio | Claude Desktop, Cursor, uso local de un solo usuario | Ninguna (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
PATHdel shell, por lo quenodetambié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 suOIDC_ISSUERcomo servidor de autorización. La ruta sigue aOIDC_RESOURCE: conhttps://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-resourcepara 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 (suauthorization_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 cuandoAUTH_PROVIDERno esoidc.
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/apise 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
subes único y nunca se reasigna;preferred_usernameyemailno 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 supreferred_usernameal suyo puede hacerse pasar por usted en el inicio de sesión de Actual también. Si eso le importa, fijeAUTH_BUDGET_ACL_CLAIMa 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 eluser_namede 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
| Comando | Qué prueba | Requiere servidor en vivo |
|---|---|---|
npm run build | Compilación de TypeScript | No |
npm run test:unit-js | Prueba de humo de 84 herramientas, validación de esquema, ACL de autenticación | No |
npm run test:adapter | Adaptador, lógica de reintento, concurrencia | No |
npm run test:e2e | Cumplimiento del protocolo MCP (Playwright) | No |
npm run test:e2e:docker:full | Integración de pila completa | Sí (Docker) |
npm run test:integration | Verificaciones de sanidad del servidor en vivo | Sí |
npm run test:integration:full | Suite completa de integración en vivo | Sí |
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
| Documento | Contenido |
|---|---|
| docs/guides/MCP_CLIENTS_SETUP.md | Empiece aquí para conectar Claude Desktop, Cursor, VS Code (Copilot), Gemini CLI o Claude Code |
| docs/guides/AI_CLIENT_SETUP.md | Configuración de LibreChat y LobeChat, redes Docker, proxy HTTPS/TLS, OIDC |
| docs/guides/DEPLOYMENT.md | Docker, perfiles de Docker Compose, configuración de producción, Kubernetes |
| docs/ARCHITECTURE.md | Capas de componentes, flujo de datos, protocolos de transporte |
| docs/SECURITY_AND_PRIVACY.md | Modelos de autenticación, modelo de amenazas, endurecimiento |
| docs/TESTING_AND_RELIABILITY.md | Estrategia de pruebas, cobertura, patrones de confiabilidad |
| docs/NEW_TOOL_CHECKLIST.md | Guía paso a paso para agregar una nueva herramienta MCP |
| CONTRIBUTING.md | Configuración de desarrollo, estándares de código, proceso de PR |
| .env.example | Referencia 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:
- Fork →
git checkout -b feature/my-feature - Haga cambios + agregue pruebas
npm run build && npm run test:unit-jsdebe pasar- 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()ensrc/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
- Actual Budget: software de presupuestos de código abierto
- Model Context Protocol: integración estandarizada de aplicaciones con IA
- LibreChat: alternativa de código abierto a ChatGPT
- s-stefanov/actual-mcp: patrón de adaptador original
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.