Vaani-Pay MCP Server
Vaani Pay Assistant
Ein sicherer, in Echtzeit arbeitender, mehrbenutzerfähiger, zweisprachiger (Englisch + Hindi) Zahlungs-Support-Chatbot: Benutzer registrieren sich bzw. melden sich mit ihrem eigenen Konto an und fragen dann nach ihren eigenen Zahlungen, Bestellungen, Erstattungen, Transaktionen, Betrugsrisiken und Statistiken – mit strenger, pro Benutzer durchgesetzter Datenisolierung auf der MCP-Tool-Ebene, einer echten SQLite-Datenbank und einem Live-WebSocket-Statusstream, der zeigt, was der Agent gerade tut.
Das Ganze begann als statischer Hackathon-Demo-Build (hartkodierte Benutzer, feste Token, nur Englisch) und wurde zu einer datenbankgestützten, mehrbenutzerfähigen, sicheren, zweisprachigen Plattform erweitert, ohne die funktionierenden Teile zu ändern, die bereits funktioniert haben: das WebSocket-Protokoll, die MCP-Tool-Architektur und die zentrale Agentenlogik haben dieselbe Form wie zuvor – nur die Datenquelle und das darunterliegende Authentifizierungsmodell haben sich geändert.
Architektur
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. Konten & Datenschutz
Echte Konten.
POST /auth/registererzeugt eine Benutzerzeile in SQLite mit einem sicher gehashten Passwort (PBKDF2-HMAC-SHA256, pro Passwort zufälliges Salt, 260.000 Iterationen – sieheapp/security.py). Klartext-Passwörter werden nirgendwo gespeichert oder protokolliert.Echte Anmeldesitzungen.
POST /auth/loginüberprüft die Anmeldedaten und stellt ein undurchschaubares, nicht erratbares Sitzungstoken aus (generate_token()inapp/security.py, 256 Bit Entropie), das in der Tabellesessionsmit einer Ablaufzeit gespeichert wird (SESSION_TTL_HOURSin.env, Standard 24h). Abgelaufene oder unbekannte Token werden überall dort abgelehnt, wo sie geprüft werden.Jedes MCP-Tool, das eine bestimmte Ressource nachschlägt (
get_payment_status,get_order_details,get_refund_status,check_fraud_risk) erfordert einen Parameterrequesting_user_idund prüft inmcp_server/data_layer.py, dass die Ressource tatsächlich diesem Benutzer gehört – jetzt über eine parametrisierte SQL-KlauselWHERE ... AND user_id = ?–, bevor irgendetwas zurückgegeben wird.Wenn die Ressource einer anderen Person gehört oder überhaupt nicht existiert, wird in beiden Fällen dieselbe generische Antwort zurückgegeben:
"Access denied. You are not authorized to access this information."Würden unterschiedliche Meldungen für „nicht gefunden" gegenüber „Daten einer anderen Person" zurückgegeben, könnte ein Benutzer gültige IDs enumerieren, indem er beobachtet, welchen Fehler er erhält – das schließt diesen Seitenkanal.requesting_user_idist immer die authentifizierte Identität des Aufrufers (einmalig zum Zeitpunkt der WebSocket-Authentifizierung bzw. der REST-Anfrage aufgelöst – sieheapp/auth.py), niemals ein Wert, der aus einer Chat-Nachricht, einem URL-Parameter oder einem Anforderungstext geparst wird. Das Extraktionsschema vonapp/nlu.pyenthält überhaupt kein Felduser_id, sodass keine Nachricht (auch keine böswillige) eine andere Identität in einen Tool-Aufruf einschleusen kann.get_customer_details,get_transaction_historyundget_payment_statisticsakzeptieren überhaupt keine Ressourcen-ID – sie geben immer die eigenen Daten des Aufrufers zurück, sodass es für diese drei Tools keinerlei Angriffsfläche zur ID-Manipulation gibt.Kontolöschung (
DELETE /users/me) erfordert die erneute Eingabe des aktuellen Passworts als Bestätigung und löscht dann die Benutzerzeile – Fremdschlüssel mitON DELETE CASCADEentfernen zusammen mit ihr alle Sitzungen, den Chatverlauf, Zahlungen, Bestellungen, Erstattungen und Transaktionen dieses Benutzers.
Verifizieren Sie die Datenisolierung direkt:
python3 test_offline.py2. Registrierung, Anmeldung & Kontoverwaltung
POST /auth/register– Name, E-Mail, Passwort, optional Telefon und eine Spracheinstellung. Passwörter müssen mindestens 8 Zeichen lang sein und eine Mischung aus Buchstaben und Zahlen enthalten (app/security.py).POST /auth/login– gibt ein Sitzungstoken und das Benutzerprofil zurück.POST /auth/logout– widerruft das aktuelle Sitzungstoken serverseitig.GET /users/me/PUT /users/me– Profil anzeigen/aktualisieren (Name, Telefon).POST /users/me/change-password– erfordert das aktuelle Passwort; eine Änderung invalidiert alle bestehenden Sitzungen (erzwingt überall eine erneute Anmeldung), sodass ein geleaktes altes Token nicht mehr funktioniert.GET /users/me/preferences/PUT /users/me/preferences– Spracheinstellung lesen/aktualisieren (en/hi), in der Datenbank gespeichert, sodass sie eine Abmeldung/Anmeldung übersteht.DELETE /users/me– dauerhafte Kontolöschung (Passwort + explizitesconfirm: trueerforderlich).
All dies ist auch direkt über die Chat-Oberfläche erreichbar, über die Schaltfläche ⚙️ in der Kopfzeile (Profil anzeigen/bearbeiten, Sprachumschalter, Passwort ändern, Abmelden, Konto löschen).
3. Chat-UI
static/index.html – eine Single-Page-App:
Bevor ein Chat möglich ist, werden Tabs für Anmeldung/Registrierung angezeigt.
Bereich „Profil & Einstellungen" (Bearbeiten von Name/Telefon, Passwortänderung, Sprachumschalter, Abmelden, Kontolöschung mit Bestätigungsschritt).
Einklappbares Vorschlagsmenü über dem Eingabefeld, gerendert aus demselben Wörterbuch übersetzter Zeichenfolgen wie der Rest der Benutzeroberfläche.
Chatblasen, Live-Statuszeile und Statusanzeige in der Kopfzeile – unverändert gegenüber dem ursprünglichen Design.
4. Echtzeitkommunikation
Der Chat läuft weiterhin über einen einzigen WebSocket (/ws) – die Protokollstruktur ist unverändert, nur das Authentifizierungstoken ist jetzt ein echtes, datenbankgestütztes Sitzungstoken anstelle eines statischen Werts:
{"type": "auth", "token": "<session token from /auth/login>"}
↓
{"type": "auth_success", "user_id": "...", "name": "...", "language": "en"}Für jede Chat-Nachricht streamt der Server Statusevents in dieser Reihenfolge und dann die endgültige (lokalisierte) Antwort:
🔍 Understanding your request...
🔧 Checking payment information...
✓ Payment information retrieved
🤖 Generating response...
<final answer, in the user's selected language>Jede Chat-Runde (sowohl Benutzer- als auch Assistentennachrichten) wird außerdem in der Tabelle chat_history gespeichert (_persist_chat_turn in app/main.py) und dem authentifizierten Benutzer zugeordnet.
5. MCP-basierte Architektur
mcp_server/server.py stellt genau diese 7 Tools bereit, aufgeteilt in Domänenmodule unter mcp_server/tools/:
Tool | Datei |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alle werden von der SQLite-Datenbank (mcp_server/data_layer.py → app/db.py) anstelle von statischem JSON unterstützt. Toolsignaturen, Agent und Frontend sind gegenüber dem ursprünglichen Design unverändert – nur die Datenquelle unter data_layer.py hat sich geändert, genau wie es die ursprüngliche Architektur ermöglichen sollte.
6. Zweisprachige Unterstützung (Englisch + Hindi)
UI-Strings: das Wörterbuch
UI_STRINGSinapp/i18n.py, bereitgestellt überGET /i18n/{lang}. Das Frontend ruft diese Datei einmal beim Laden und bei jedem Sprachwechsel ab und wendet sie über die Attributedata-i18n/data-i18n-placeholderan – keine übersetzte Zeichenfolge ist im HTML/JS hartkodiert.Antworten des KI-Assistenten:
AGENT_STRINGSinapp/i18n.py(feste Meldungen wie Begrüßungen) undREPLY_TEMPLATES(interpolierte Meldungen wie Zahlungsstatus).app/agent.pyrendert jede Antwort mithilfe dieser – nichts im Agenten enthält direkt hartkodierten englischen Text.NLU: Der Prompt in
app/nlu.pyfordert das Grok-Modell ausdrücklich auf, Hindi-/Englisch-/gemischte Eingaben zu verarbeiten und für die Absichts-/Entitätsextraktion intern immer ins Englische zu übersetzen, damit der Assistent eine Frage in jedem Fall versteht und in der bevorzugten Sprache des Benutzers antwortet.Persistenz: Die Spracheinstellung ist in der Datenbank unter
users.languagegespeichert (bei der Registrierung gesetzt, jederzeit änderbar überPUT /users/me/preferences), sodass sie eine Abmeldung/Anmeldung übersteht.Dynamischer Wechsel: Ein Sprachwechsel in den Einstellungen aktualisiert die Benutzeroberfläche sofort und verbindet den WebSocket neu, sodass die nächste Chat-Antwort bereits in der neuen Sprache zurückkommt – kein Neuladen der Seite erforderlich.
7. Sicherheitsanforderungen
Anforderung | Wo umgesetzt |
Authentifizierung |
|
Autorisierung |
|
Benutzer-/Sitzungsisolation |
|
MCP-Berechtigungsprüfungen | Werden innerhalb der MCP-Tools selbst durchgesetzt ( |
Eingabevalidierung |
|
Schutz vor SQL-Injection | Jede Abfrage in |
Ratenbegrenzung |
|
Sicheres CORS |
|
Sicheres Passwort-Hashing |
|
Token-Ablauf |
|
Allgemeine Authentifizierungsfehlermeldungen |
|
Sichere Fehlerbehandlung |
|
Schutz vor ID-Manipulation | Ein Benutzer kann beliebige |
8. Datenbankschema
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_atSiehe SCHEMA in app/db.py für das vollständige DDL mit Fremdschlüsseln und Indizes.
9. Wallet: Zahlungskonten, Geld hinzufügen & Geld senden
Jeder registrierte Benutzer erhält eine echte, nutzbare Wallet — nicht nur eine Zahlungsverlauf-Anzeige. Das ist die mit Abstand größte Ergänzung zu dem Datenbank-/Auth-Upgrade, und sie ist als eigenes Modul (app/wallet.py) umgesetzt, das sowohl die REST-API als auch die KI-/MCP-Tools aufrufen. So gibt es genau eine Stelle, die die Regeln für Geldbewegungen durchsetzt.
Automatische Kontoerstellung. POST /auth/register erstellt die Benutzerzeile UND eine payment_accounts-Zeile in derselben Datenbanktransaktion (siehe: register() in app/auth.py ruft insert_account_row() aus app/wallet.py auf) — ein Benutzer kann niemals ohne Wallet existieren, und eine Wallet wird niemals als separater, eigenständig fehlschlagbarer Schritt erstellt. Jedes Konto erhält:
eine eindeutige Zahlungs-ID (
PAY..., interner Bezeichner),eine eindeutige 12-stellige Kontonummer,
eine feste IFSC (
VPAY0000001— Vaani Pay ist eine virtuelle Wallet mit einer einzigen Filiale, daher teilen sich alle Konten eine IFSC, so wie es bei virtuellen Konten echter Neobanken oft der Fall ist),ein Startguthaben von ₹0.
Die Registrierungsantwort enthält eine Bestätigung (message: "Your payment account has been successfully created.") sowie die neuen Kontodaten, die dem Benutzer sofort angezeigt werden (sowohl in der API-Antwort als auch in der Bestätigungsmeldung des Registrierungsbildschirms).
Geld hinzufügen. POST /wallet/add-money (oder der Button „Geld hinzufügen“ im Wallet-Bildschirm, oder die Aufforderung an den KI-Assistenten „Füge ₹5,000 zu meinem Konto hinzu“) — validiert den Betrag (> ₹0, ≤ ₹2,00,000 pro Transaktion — MAX_ADD_MONEY in app/wallet.py), aktualisiert anschließend atomar das Guthaben und hängt eine CREDIT-Zeile an wallet_transactions an. Für den Hackathon-Build ist kein echtes Zahlungs-Gateway angebunden — es handelt sich ausdrücklich um eine simulierte Aufladung, die der Anforderung „sicherer simulierter Geldfluss“ aus dem Briefing entspricht.
Geld senden — immer eine zweistufige Bestätigung. Weder die REST-API noch der KI-Assistent bewegen Geld jemals in einem einzigen Aufruf:
POST /wallet/transfers(initiate_transferinapp/wallet.py) prüft den Empfänger und das Guthaben des Absenders und erstellt einePENDING-Zeile inwallet_transactions— noch keine Guthabenänderungen. Es wird eine Bestätigungsvorschau zurückgegeben (Empfänger, maskierte Kontonummer, IFSC, Betrag, Gebühr, Gesamtbelastung) — das ist es, was den Bildschirm „Transfer bestätigen“ erzeugt.POST /wallet/transfers/{id}/confirm(confirm_transfer) ist der einzige Aufruf, der tatsächlich Geld bewegt. Er validiert das Guthaben des Absenders und den Kontostatus zum Bestätigungszeitpunkt erneut (nicht nur beim Initiieren, falls sich in der Zwischenzeit etwas geändert hat — z. B. wenn zwei Überweisungen direkt hintereinander initiiert wurden), belastet dann den Absender und schreibt dem Empfänger den Betrag gut, falls dieser ein echtes Konto bei Vaani Pay ist — und das innerhalb einer einzigen atomaren SQLite-Transaktion, die durch eine prozessweite Sperre geschützt ist. Wenn irgendetwas auf halbem Weg fehlschlägt, wird das Ganze zurückgerollt — eine Überweisung kann niemals belastet, aber nicht gutgeschrieben sein.POST /wallet/transfers/{id}/cancelbricht eine nochPENDING-Überweisung ab, ohne ein Guthaben anzufassen.
Eine Überweisung an eine Kontonummer, die nicht in unserem System ist, gelingt weiterhin (als simulierte externe Überweisung — der Absender wird belastet, nur gibt es kein Konto bei Vaani Pay für die Gutschrift), was der Anforderung aus dem Briefing entspricht: „dem Empfänger den Betrag gutschreiben, wenn der Empfänger im simulierten System existiert“.
Empfängervalidierung. POST /wallet/validate-recipient (validate_recipient) prüft: das Kontonummernformat (9–18 Ziffern), das IFSC-Format (^[A-Z]{4}0[A-Z0-9]{6}$), dass die IFSC bei einem internen Konto zur Kontonummer passt, und — entscheidend — dass der Absender nicht auf die eigene Kontonummer überweist. Wenn nur ein Empfänger-Name angegeben ist (keine Kontonummer), sucht die Funktion den Namen in den gespeicherten Begünstigten des Aufrufers und löst ihn automatisch auf, wenn es genau eine Übereinstimmung gibt.
Gespeicherte Begünstigte. Nach einer erfolgreichen Überweisung fragt die UI „Diesen Empfänger speichern?“ — POST /beneficiaries speichert ihn nur für den authentifizierten Benutzer (niemals global/geteilt), sodass der Benutzer (oder der KI-Assistent, wenn er gebeten wird: „Sende ₹2,000 an Rahul“) beim nächsten Mal eine Überweisung allein anhand des Namens auflösen kann.
Transaktionshistorie & Filter. GET /wallet/transactions?filter=... (all / add_money / sent / received / failed / pending) — alles wird live aus wallet_transactions berechnet, niemals hartcodiert. Der Tab „Verlauf“ im Wallet-Bildschirm und die KI-Abfragen „Zeig mir meine Wallet-Transaktionen“ / „Wie viel habe ich diesen Monat ausgegeben“ lesen beide aus exakt derselben Funktion (get_wallet_transactions / get_spending_summary in app/wallet.py).
Guthaben wird immer abgeleitet, nie direkt gesetzt. Es gibt bewusst keine set_balance()-Funktion in der gesamten Codebasis — Guthaben ändern sich ausschließlich als Nebeneffekt von add_money() oder confirm_transfer(), die beide in demselben atomaren Schritt auch eine unveränderliche wallet_transactions-Zeile anhängen. Das Frontend zeigt immer nur das an, was GET /wallet/account zurückgibt; es kann es nicht beeinflussen.
Wallet-Sicherheit im Besonderen
Regel | Wie sie durchgesetzt wird |
Ein Benutzer kann sein eigenes Guthaben niemals direkt ändern | Keine öffentliche Funktion setzt das Guthaben, außer als Nebeneffekt von Add Money / confirm_transfer; beide sind betragsvalidiert und erzeugen eine Audit-Zeile |
Ein Benutzer kann niemals das Guthaben eines anderen Benutzers ändern | Jede Wallet-Funktion verwendet die authentifizierte |
Ein Benutzer kann niemals die Überweisung eines anderen bestätigen/stornieren |
|
Selbstüberweisungen sind blockiert |
|
Beträge können während der Ausführung nicht manipuliert werden | Der Betrag, der zum Zeitpunkt von |
Die KI kann ohne ausdrückliche Bestätigung kein Geld bewegen | Die MCP-Tools |
Atomarität |
|
10. Zweisprachiger Zahlungsablauf
Die Wallet ist vollständig zweisprachig und verwendet denselben app/i18n.py-Mechanismus wie der Rest der App — Add Money, Send Money (alle drei Schritte), der Bestätigungsbildschirm, Transaktionsstatus und jede KI-Antwort zu Guthaben/Überweisungen werden über t()/tpl() gerendert, wobei sich nirgendwo in app/wallet.py, app/agent.py oder der Wallet-Oberfläche von static/index.html hartkodiertes Englisch befindet. Fragt man die KI zum Beispiel „Rahul ko ₹2,000 bhejo“, durchläuft man exakt denselben resolve → confirm → execute-Ablauf wie in der englischen Version, wobei jede Nachricht — einschließlich des Bestätigungsbildschirms — auf Hindi gerendert wird.
Einrichtung
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)Die Datenbank wird beim ersten Start automatisch erstellt (und, nur wenn sie leer ist, mit zwei Demo-Benutzern befüllt — siehe unten); für die lokale Entwicklung ist kein separater Migrationsschritt erforderlich. Um sie explizit im Voraus zu erstellen:
python3 -m app.dbPrüfen Sie vor dem Öffnen des Browsers:
python3 diagnose_setup.pyAusführen
uvicorn app.main:app --reload --port 8000Öffnen Sie http://localhost:8000. Registrieren Sie sich für ein neues Konto, oder melden Sie sich mit einem der vorbefüllten Demo-Konten an:
Passwort | |
|
|
|
|
Probieren Sie dann das Vorschlagsmenü aus, stellen Sie Fragen wie „check payment status pay_1001“ oder „मेरा भुगतान pay_1001 का स्टेटस क्या है?“, wechseln Sie die Sprache unter Settings, oder versuchen Sie, sich als ein Benutzer anzumelden und nach den Zahlungs-/Bestell-/Erstattungs-IDs des anderen Benutzers zu fragen (pay_1003, ord_2002, rfnd_3002 gehören zu Priya), um die Antwort „Zugriff verweigert“ zu sehen.
Um die Wallet auszuprobieren: Öffnen Sie die Schaltfläche 💰 Wallet in der Kopfzeile. Beide Demo-Konten starten mit einem Guthaben (₹8,500 für Ramesh, ₹8,000 für Priya) und haben bereits eine Demo-Überweisung in ihrem Verlauf. Probieren Sie „Add Money“ aus, oder senden Sie mit „Send Money“ Geld an die Kontonummer des anderen Demo-Kontos (sichtbar in dessen eigenem Wallet-Bildschirm), oder fragen Sie den KI-Assistenten direkt: „what's my balance?“, „add ₹5,000 to my account“, „send ₹2,000 to Priya Stores“ (er fragt beim ersten Mal nach ihrer Kontonummer + IFSC und bietet dann an, sie nach einer erfolgreichen Überweisung als Zahlungsempfängerin zu speichern — danach reicht nur ihr Name), oder „Mera current balance kitna hai?“ auf 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