Vaani-Pay MCP Server
Vaani Pay Assistant
Un chatbot de asistencia para pagos multiusuario, bilingüe (inglés + hindi), seguro y en tiempo real: los usuarios se registran/inician sesión con su propia cuenta y después preguntan por sus propios pagos, pedidos, reembolsos, transacciones, riesgo de fraude y estadísticas — con un estricto aislamiento de datos por usuario aplicado en la capa de herramientas MCP, una base de datos SQLite real y un flujo de estado WebSocket en vivo que muestra lo que el agente está haciendo.
Empezó como una construcción de hackatón de demostración estática (usuarios codificados, tokens fijos, solo inglés) y se ha actualizado a una plataforma basada en bases de datos, multiusuario, segura y bilingüe sin cambiar las partes funcionales que ya funcionaban: el protocolo WebSocket, la arquitectura de herramientas MCP y la lógica central del agente tienen la misma forma que antes — solo han cambiado la fuente de datos y el modelo de autenticación subyacentes.
Arquitectura
Browser (chat UI + login/signup/profile)
│ REST (/auth, /users/me, /transactions) │ WebSocket (/ws)
▼ ▼
FastAPI — auth endpoints, profile endpoints FastAPI — WebSocket handler
│ │
▼ ▼
app/auth.py (register / login / sessions) AI Agent (app/agent.py)
│ NLU (Grok API) → intent + entities
│ Tool selection → MCP tool
▼ │
app/db.py — SQLite │ MCP (stdio transport)
users, sessions, chat_history, ▼
payments, orders, refunds, transactions MCP Server (mcp_server/server.py)
▲ get_payment_status │ get_order_details
│ get_refund_status │ get_customer_details
└───────────── same DB, same ownership ──── get_transaction_history │ check_fraud_risk
checks on every query get_payment_statistics
│
▼
mcp_server/data_layer.py
— ownership check on every lookup,
now backed by SQLite instead of JSONRelated MCP server: nexi-xpay-mcp-server
1. Cuentas y privacidad de datos
Cuentas reales.
POST /auth/registercrea una fila de usuario en SQLite con una contraseña hash segura (PBKDF2-HMAC-SHA256, sal aleatoria por contraseña, 260k iteraciones — verapp/security.py). Las contraseñas en texto plano nunca se almacenan ni se registran en ningún sitio.Sesiones de inicio de sesión reales.
POST /auth/loginverifica las credenciales y emite un token de sesión opaco e imposible de adivinar (generate_token()deapp/security.py, 256 bits de entropía) almacenado en la tablasessionscon una caducidad (SESSION_TTL_HOURSen.env, por defecto 24h). Los tokens caducados o desconocidos se rechazan en todos los puntos donde se comprueban.Cada herramienta MCP que busca un recurso específico (
get_payment_status,get_order_details,get_refund_status,check_fraud_risk) requiere un parámetrorequesting_user_idy verifica, enmcp_server/data_layer.py, que el recurso pertenece realmente a ese usuario — ahora mediante una cláusula SQL parametrizadaWHERE ... AND user_id = ?— antes de devolver nada.Si el recurso pertenece a otra persona o no existe en absoluto, se devuelve la misma respuesta genérica en ambos casos:
"Access denied. You are not authorized to access this information."Devolver mensajes diferentes para «no encontrado» frente a «datos de otra persona» permitiría a un usuario enumerar ID válidos observando qué error recibe — esto cierra ese canal auxiliar.requesting_user_ides siempre la identidad autenticada de quien llama (resuelta una sola vez en el momento de la autenticación WebSocket / de la petición REST — verapp/auth.py), nunca un valor extraído de un mensaje de chat, un parámetro de URL o el cuerpo de la petición. El esquema de extracción deapp/nlu.pyno tiene ningún campouser_id, por lo que no hay manera de que un mensaje (ni siquiera uno malintencionado) cuele una identidad diferente en una llamada de herramienta.get_customer_details,get_transaction_historyyget_payment_statisticsno reciben ningún ID de recurso — siempre devuelven los datos propios de quien llama, por lo que no hay ninguna superficie de manipulación de ID para estas tres herramientas.Eliminación de la cuenta (
DELETE /users/me) requiere volver a introducir la contraseña actual como confirmación y, a continuación, elimina la fila del usuario: las claves foráneas conON DELETE CASCADEeliminan también todas las sesiones, el historial de chat, los pagos, los pedidos, los reembolsos y las transacciones de ese usuario.
Verifica el aislamiento de datos directamente:
python3 test_offline.py2. Registro, inicio de sesión y gestión de cuentas
POST /auth/register— nombre, correo electrónico, contraseña, teléfono opcional y preferencia de idioma. Las contraseñas deben tener al menos 8 caracteres y contener una combinación de letras y números (app/security.py).POST /auth/login— devuelve un token de sesión + perfil de usuario.POST /auth/logout— revoca el token de sesión actual en el servidor.GET /users/me/PUT /users/me— ver/actualizar el perfil (nombre, teléfono).POST /users/me/change-password— requiere la contraseña actual; cambiarla invalida todas las sesiones existentes (obliga a iniciar sesión de nuevo en todas partes), de modo que un token antiguo filtrado deja de funcionar.GET /users/me/preferences/PUT /users/me/preferences— leer/actualizar la preferencia de idioma (en/hi), persistida en la base de datos para que sobreviva al cierre/inicio de sesión.DELETE /users/me— eliminación permanente de la cuenta (requiere contraseña +confirm: trueexplícito).
Todo esto también es accesible desde la propia interfaz de chat mediante el botón ⚙️ de la cabecera (ver/editar perfil, selector de idioma, cambiar contraseña, cerrar sesión, eliminar cuenta).
3. Interfaz de chat
static/index.html — una aplicación de una sola página:
Pestañas de Iniciar sesión / Registrarse que se muestran antes de que sea posible chatear.
Panel de Perfil y Configuración (edición de nombre/teléfono, cambio de contraseña, selector de idioma, cierre de sesión, eliminación de cuenta con un paso de confirmación).
Menú de sugerencias plegable sobre el campo de entrada, generado a partir del mismo diccionario de cadenas traducidas que el resto de la interfaz.
Burbujas de chat, línea de estado en vivo e indicador de estado en la cabecera — sin cambios respecto al diseño original.
4. Comunicación en tiempo real
El chat sigue realizándose a través de un único WebSocket (/ws) — la forma del protocolo no ha cambiado; solo el token de autenticación es ahora un token de sesión real respaldado por la base de datos en lugar de un valor estático:
{"type": "auth", "token": "<session token from /auth/login>"}
↓
{"type": "auth_success", "user_id": "...", "name": "...", "language": "en"}Para cada mensaje de chat, el servidor transmite eventos de estado en este orden y, a continuación, la respuesta final (localizada):
🔍 Understanding your request...
🔧 Checking payment information...
✓ Payment information retrieved
🤖 Generating response...
<final answer, in the user's selected language>Cada turno de chat (tanto los mensajes del usuario como los del asistente) también se persiste en la tabla chat_history (_persist_chat_turn de app/main.py), limitado al usuario autenticado.
5. Arquitectura basada en MCP
mcp_server/server.py expone exactamente estas 7 herramientas, divididas en módulos de dominio en mcp_server/tools/:
Herramienta | Archivo |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Todo respaldado por la base de datos SQLite (mcp_server/data_layer.py → app/db.py) en lugar de JSON estático. Las firmas de las herramientas, el agente y el frontend no han cambiado con respecto al diseño original; solo ha cambiado la fuente de datos subyacente a data_layer.py, exactamente como la arquitectura original fue diseñada para permitir.
6. Soporte bilingüe (inglés + hindi)
Cadenas de la interfaz: el diccionario
UI_STRINGSdeapp/i18n.py, servido a través deGET /i18n/{lang}. El frontend lo obtiene una vez al cargar y en cada cambio de idioma, y lo aplica mediante los atributosdata-i18n/data-i18n-placeholder— ninguna cadena traducida está codificada de forma fija en el HTML/JS.Respuestas del asistente de IA:
AGENT_STRINGSdeapp/i18n.py(mensajes fijos como saludos) yREPLY_TEMPLATES(mensajes interpolados como el estado del pago).app/agent.pygenera cada respuesta a través de estos — nada en el agente codifica texto en inglés directamente.NLU: el prompt de
app/nlu.pypide explícitamente al modelo Grok que procese entrada en hindi/inglés/mixta y que traduzca siempre internamente al inglés para la extracción de intenciones/entidades, de modo que el asistente entienda una pregunta en cualquier caso y responda en el idioma preferido del usuario.Persistencia: la preferencia de idioma reside en
users.languageen la base de datos (se establece al registrarse y se puede cambiar en cualquier momento mediantePUT /users/me/preferences), de modo que sobrevive al cierre/inicio de sesión.Cambio dinámico: cambiar el idioma en Configuración actualiza la interfaz inmediatamente y reconecta el WebSocket para que la siguiente respuesta de chat llegue ya en el nuevo idioma — sin necesidad de recargar la página.
7. Requisitos de seguridad
Requisito | Dónde se implementa |
Autenticación |
|
Autorización |
|
Aislamiento de usuario/sesión |
|
Comprobaciones de permisos a nivel de MCP | Se aplican dentro de las propias herramientas MCP ( |
Validación de entrada |
|
Protección contra inyección SQL | Cada consulta en |
Límite de peticiones |
|
CORS seguro |
|
Hash seguro de contraseñas |
|
Caducidad de tokens |
|
Mensajes de error de autenticación genéricos |
|
Gestión segura de errores | El |
Protección contra la manipulación de IDs | Un usuario puede escribir cualquier |
8. Esquema de la base de datos
users id, name, email, phone, password_hash, language, created_at, updated_at, last_login
sessions token, user_id, created_at, expires_at
chat_history id, user_id, conversation_id, role, message, timestamp
payments payment_id, user_id, status, amount, method, failure_reason, date
orders order_id, user_id, status, total, items (JSON), date
refunds refund_id, user_id, payment_id, amount, status, date
transactions txn_id, user_id, type, amount, status, date
-- Wallet: the real money-movement system (see section 9 below)
payment_accounts id, user_id, payment_id, account_number, ifsc, balance, currency, status, created_at
wallet_transactions id, transaction_id, sender_account_id, receiver_account_id, amount, transaction_type,
status, description, sender_name, receiver_name, recipient_account_number,
recipient_ifsc, failure_reason, created_at, updated_at
beneficiaries id, user_id, recipient_name, account_number, ifsc, created_atVéase SCHEMA en app/db.py para el DDL completo con claves foráneas e índices.
9. Monedero: cuentas de pago, añadir dinero y enviar dinero
Cada usuario registrado recibe un monedero real y utilizable, no solo un visor de historial de pagos. Es, con diferencia, la mayor incorporación sobre la actualización de base de datos/autenticación, y está construido como módulo propio (app/wallet.py) al que llaman tanto la API REST como las herramientas de IA/MCP, de modo que hay exactamente un lugar donde se aplican las reglas de movimiento de dinero.
Creación automática de cuenta. POST /auth/register crea la fila del usuario Y una fila de payment_accounts en la misma transacción de base de datos (véase register() en app/auth.py al llamar a insert_account_row() en app/wallet.py): un usuario nunca puede existir sin monedero, y un monedero nunca se crea como un paso separado que pueda fallar de forma independiente. Cada cuenta recibe:
un ID de pago único (
PAY..., identificador interno),un número de cuenta único de 12 dígitos,
un IFSC fijo (
VPAY0000001— Vaani Pay es un monedero virtual de una sola sucursal, por lo que todas las cuentas comparten un mismo IFSC, igual que suelen hacer las cuentas virtuales de un neobanco real),un saldo inicial de ₹0.
La respuesta del registro incluye un mensaje: «Tu cuenta de pago se ha creado correctamente», que sirve de confirmación, además de los detalles de la nueva cuenta, mostrados al usuario de inmediato (tanto en la respuesta de la API como en el mensaje de confirmación de la pantalla de registro).
Añadir dinero. POST /wallet/add-money (o el botón «Añadir dinero» en la pantalla del monedero, o pedir al asistente de IA «añade ₹5,000 a mi cuenta») valida el importe (> ₹0, ≤ ₹2,00,000 por transacción — MAX_ADD_MONEY en app/wallet.py), y después actualiza el saldo de forma atómica y añade una fila CREDIT a wallet_transactions. No hay ninguna pasarela de pago real conectada en la versión del hackathon: se trata de una recarga explícitamente simulada, que cumple el requisito del brief de «flujo de fondos simulado seguro».
Enviar dinero: siempre una confirmación en dos pasos. Ni la API REST ni el asistente de IA mueven dinero nunca en una sola llamada:
POST /wallet/transfers(initiate_transferenapp/wallet.py) valida el destinatario y el saldo del remitente, y crea una filaPENDINGenwallet_transactions— aún no hay cambios de saldo. Devuelve una vista previa de confirmación (destinatario, número de cuenta enmascarado, IFSC, importe, comisión, débito total), que es lo que muestra la pantalla «Confirmar transferencia».POST /wallet/transfers/{id}/confirm(confirm_transfer) es la única llamada que mueve dinero de verdad. Vuelve a validar el saldo del remitente y el estado de la cuenta en el momento de confirmar (no solo en el momento de iniciar, por si algo ha cambiado entre medias — p. ej., dos transferencias iniciadas una detrás de otra), y después debita al remitente y, si el destinatario es una cuenta real de Vaani Pay, lo acredita, dentro de una única transacción atómica de SQLite protegida por un bloqueo a nivel de proceso. Si algo falla a mitad de camino, todo se revierte: una transferencia nunca puede terminar debitada pero no acreditada.POST /wallet/transfers/{id}/cancelcancela una transferencia aúnPENDINGsin tocar ningún saldo.
Enviar a un número de cuenta que no está en nuestro sistema también funciona (como transferencia externa simulada: se debita al remitente, pero no hay ninguna cuenta de Vaani Pay a la que acreditar), lo que cumple el requisito del brief de «acreditar el saldo del destinatario si el destinatario existe en el sistema simulado».
Validación del destinatario. POST /wallet/validate-recipient (validate_recipient) comprueba: el formato del número de cuenta (9–18 dígitos), el formato del IFSC (^[A-Z]{4}0[A-Z0-9]{6}$), que el IFSC coincida con el número de cuenta si es una cuenta interna y — algo crucial — que el remitente no se esté enviando dinero a su propio número de cuenta. Si solo se proporciona el nombre del destinatario (sin número de cuenta), busca ese nombre entre los beneficiarios guardados del propio solicitante y lo resuelve automáticamente si hay exactamente una coincidencia.
Beneficiarios guardados. Tras una transferencia correcta, la interfaz ofrece «¿Guardar este destinatario?» — POST /beneficiaries lo guarda solo para el usuario autenticado (nunca global ni compartido), de modo que la próxima vez el usuario (o el asistente de IA, cuando se le pida «envía ₹2,000 a Rahul») pueda resolver una transferencia solo con el nombre.
Historial de transacciones y filtros. GET /wallet/transactions?filter=... (all / add_money / sent / received / failed / pending): todo se calcula en vivo desde wallet_transactions, nunca está fijo en el código. La pestaña Historial de la pantalla del monedero y las consultas al asistente de IA «muestra mis transacciones del monedero» / «cuánto he gastado este mes» leen exactamente de la misma función (get_wallet_transactions / get_spending_summary en app/wallet.py).
El saldo siempre se deriva, nunca se establece directamente. A propósito, no existe ninguna función set_balance() en todo el código: las únicas formas de que cambie un saldo son como efecto secundario de add_money() o confirm_transfer(), que además añaden una fila inmutable a wallet_transactions en el mismo paso atómico. El frontend solo muestra lo que devuelve GET /wallet/account; no puede influir en ello.
Seguridad específica del monedero
Regla | Cómo se aplica |
Un usuario nunca puede modificar su propio saldo directamente | Ninguna función pública establece el saldo excepto como efecto secundario de Add Money / confirm_transfer, ambas validan el importe y generan una fila de auditoría |
Un usuario nunca puede modificar el saldo de otro usuario | Toda función de la cartera toma el |
Un usuario nunca puede confirmar/cancelar la transferencia de otra persona |
|
Las autotransferencias están bloqueadas |
|
Los importes no pueden manipularse durante el proceso | El importe utilizado para debitar/acreditar realmente en el momento de |
La IA no puede mover dinero sin confirmación explícita | Las herramientas MCP |
Atomicidad |
|
10. Flujo de pago bilingüe
La cartera es totalmente bilingüe, usando el mismo mecanismo app/i18n.py que el resto de la aplicación — Add Money, Send Money (los tres pasos), la pantalla de confirmación, los estados de las transacciones y cada respuesta de la IA sobre saldos/transferencias se renderizan a través de t()/tpl() — sin inglés hardcodeado en ningún lugar de app/wallet.py, app/agent.py o la interfaz de cartera de static/index.html. Por ejemplo, pedirle a la IA "Rahul ko ₹2,000 bhejo" (Hindi/Hinglish) recorre exactamente el mismo flujo de resolver → confirmar → ejecutar que la versión en inglés, con cada mensaje — incluida la pantalla de confirmación — renderizado en hindi.
Configuración
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Add your Grok API key (get one at https://console.x.ai)La base de datos se crea automáticamente en la primera ejecución (y, solo si está vacía, se precarga con dos usuarios de demostración — ver más abajo); no se requiere ningún paso de migración separado para el desarrollo local. Para crearla explícitamente con antelación:
python3 -m app.dbVerifique antes de tocar el navegador:
python3 diagnose_setup.pyEjecución
uvicorn app.main:app --reload --port 8000Abra http://localhost:8000. Regístrese para crear una cuenta nueva, o inicie sesión con una de las cuentas de demostración precargadas:
Contraseña | |
|
|
|
|
Luego pruebe el menú de sugerencias, haga preguntas como "check payment status pay_1001" o "मेरा भुगतान pay_1001 का स्टेटस क्या है?", cambie de idioma desde Settings, o intente iniciar sesión como un usuario y preguntar por los IDs de pago/pedido/reembolso del otro usuario (pay_1003, ord_2002, rfnd_3002 pertenecen a Priya) para ver la respuesta de acceso denegado.
Para probar la cartera: abra el botón 💰 Wallet en el encabezado. Ambas cuentas de demostración comienzan con un saldo (₹8.500 para Ramesh, ₹8.000 para Priya) y una transferencia de demostración ya en su historial. Pruebe "Add Money", o "Send Money" al número de cuenta de la otra cuenta de demostración (visible en su propia pantalla de Wallet), o pregúntele directamente al asistente de IA: "what's my balance?", "add ₹5,000 to my account", "send ₹2,000 to Priya Stores" (la primera vez pedirá su número de cuenta + IFSC, y luego ofrecerá guardarla como beneficiaria después de una transferencia exitosa — después de eso, basta con su nombre), o "Mera current balance kitna hai?" en hindi.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
AlicenseNot gradedqualityAmaintenanceEnables AI agents to interact with Juspay's payment processing APIs and merchant dashboard for managing orders, transactions, refunds, customers, gateways, and reporting through natural language.21Apache 2.0- AlicenseAqualityDmaintenanceEnables AI assistants to query orders, transaction details, warnings/anomalies, and payment methods from your Nexi XPay merchant account.4MIT

AlipayPlus MCP Serverofficial
AlicenseAqualityDmaintenanceIntegrates Ant International's AlipayPlus payment APIs, enabling AI assistants to handle payment and refund operations seamlessly.68MIT- FlicenseNot gradedqualityCmaintenanceEnables AI to query a business database for customers, orders, and revenue using natural language through safe, well-defined tools.
Related MCP Connectors
Taiwan payments (ECPay 綠界 + NewebPay 藍新) & e-invoices for AI agents. Stateless, never holds funds.
Korea payments for AI agents — card, KakaoPay/NaverPay, 가상계좌 via Toss Payments. Never holds funds.
Let AI agents add Yolfi crypto checkout, paylinks, webhooks, and status checks.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/divyaupadhyay56/Vaani-Pay'
If you have feedback or need assistance with the MCP directory API, please join our Discord server