Payment Delay MCP
Payment Delay MCP – Auslieferung eines Produktions-ML-Modells an beliebige LLMs über MCP
Ein scikit-learn-Klassifikator, der hinter einem FastAPI-Mikroservice bereitgestellt und Sprachmodellen als Model Context Protocol-Tools zur Verfügung gestellt wird – sodass ein handelsüblicher Chat-Client das Modell korrekt entdeckt und aufruft, ohne dass dafür auch nur eine Zeile Integrationscode geschrieben wurde.

Die These
Das Modell ist die Fracht, nicht der Zweck.
Die meisten „KI-gestützten“ Demos verdrahten einen Modellaufruf fest in eine maßgeschneiderte Anwendung. Dieses Projekt kehrt das um: Der Klassifikator wird als Protokoll veröffentlicht, sodass der LLM-Client austauschbar ist. Derselbe Server treibt OpenWebUI in Docker, OpenCode auf der CLI und Claude Desktop an – ohne Codeänderung und ohne client-spezifischen Adapter.
Überblick
Ein Telekommunikationsanbieter möchte wissen, welche Kunden ihre Rechnung zu spät bezahlen. Ein trainierter Klassifikator beantwortet das, aber eine .pkl-Datei ist kein Produkt – jemand muss trotzdem Klebecode schreiben, um sie aufzurufen, und dieser Klebecode wird für jeden neuen Konsumenten neu geschrieben.
Dieses Repository ist der Klebecode, einmal geschrieben als Protokoll. Vier Schichten, jede unabhängig bereitstellbar:
flowchart TB
subgraph reasoning["Reasoning path"]
UI["OpenWebUI<br/>:3000"] -->|OpenAI protocol| LL["LiteLLM<br/>:4000"]
LL -->|bedrock_mantle| BR["AWS Bedrock<br/>gpt-oss-120b"]
end
subgraph tools["Tool path"]
UI -->|OpenAPI| MCPO["mcpo<br/>:8001"]
MCPO -->|MCP over stdio| FM["FastMCP server<br/>5 tools · 2 resources · 1 prompt"]
FM -->|HTTP| API["FastAPI service<br/>:8000"]
API --> PRED["inference.predictor<br/>the only code that<br/>opens the pickle"]
PRED --> PKL[("models/*.pkl<br/>RandomForest +<br/>RandomOverSampler")]
end
style reasoning fill:#1f2a3710,stroke:#8884
style tools fill:#1f372a10,stroke:#8884Die beiden Pfade sind bewusst getrennt. Das LLM führt niemals etwas aus. Es sendet eine tool_calls-Nachricht, die ein Tool und dessen Argumente benennt; der Client führt sie aus und spielt das Ergebnis zurück. Diese Unterscheidung ist es, die das Modell austauschbar macht – und sie ist der Grund, warum dieser Stack identisch funktioniert, ob die Reasoning-Ebene nun Bedrock, ein lokales Ollama oder Claude ist.
Related MCP server: Company API MCP Server
Die Kernidee: Tool-Auswahl ist ein Dokumentationsproblem
Ein LLM wählt ein Tool anhand von Name, Signatur und Docstring aus – sonst nichts. Kein Fine-Tuning, keine Beispiele, keine Routing-Logik. Die Docstrings sind also die Schnittstelle, und das Schreiben derselben ist Ingenieursarbeit, kein Kommentar.
Zwei Tools hier überschneiden sich stark. Beide sagen Zahlungsverzug voraus. Damit das Modell ohne Aufforderung richtig wählt, mussten die operativen Randbedingungen direkt in die Beschreibung kodiert werden:
Tool | Wann das Modell es wählen soll | Das unterscheidende Signal |
| Der Nutzer hat eine CSV, als Pfad oder eingefügten Text | Der Docstring warnt, dass |
| Der Nutzer beschreibt einen einzelnen Kunden in Prosa | Der Docstring sagt „für natürlichsprachliche Fälle, in denen das LLM einen einzelnen Kunden in strukturierte Features extrahiert“ |
Verifiziertes Ergebnis: Bei einem in einfachem Englisch beschriebenen Kunden wählte gpt-oss-120b ohne Unterstützung predict_single_customer statt predict_payment_delay, füllte das Feature-Wörterbuch aus der Prosa und lieferte eine fundierte Antwort. Bestätigt in den Logs beider Hops – POST /predict_single_customer 200 bei mcpo, dann POST /predict 200 beim Modellservice.
Das ist die gesamte Behauptung des Projekts, und sie ist falsifizierbar: Deaktiviert man das Tool, beantwortet dasselbe Modell dieselbe Frage selbstbewusst und falsch – bei beiden leeren Log-Bereichen.
Ein Request, Ende zu Ende
Der Teil, den die meisten Tool-Use-Diagramme auslassen: Eine einzige Nutzerfrage kostet zwei Roundtrips zum Modell, und die dazwischenliegende Assistant-Nachricht muss wörtlich wiedergegeben werden, sonst bleibt die tool_call_id in der Luft hängen:
sequenceDiagram
participant U as User
participant W as OpenWebUI
participant L as LiteLLM
participant M as Bedrock model
participant O as mcpo
participant S as FastMCP
participant A as FastAPI + model
U->>W: "Will customer X pay late?"
W->>L: messages[] + tools[]
L->>M: translated to Bedrock
M-->>W: finish_reason: tool_calls
Note over W: the client executes,<br/>not the model
W->>O: POST /predict_single_customer
O->>S: MCP call over stdio
S->>A: POST /predict
A-->>S: {prediction, probability_yes}
S-->>O: result
O-->>W: 200 OK
W->>L: messages[] + assistant(tool_calls) + tool(result)
L->>M: second round trip
M-->>U: grounded natural-language answerDas tools[]-Array wird bei jedem Request erneut gesendet – das Modell ist zustandslos und entdeckt das Toolset in jeder Runde neu.
Was verifiziert ist
Vier Prüfpunkte, jeder gegen Logs bestätigt statt gegen Annahmen:
# | Schicht | Beleg |
1 | Modellservice |
|
2 | mcpo-Brücke | 5 Tools gerendert unter |
3 | LiteLLM zu Bedrock |
|
4 | Vollständige autonome Schleife |
|
Prüfpunkt 3 ist wichtiger, als es aussieht: finish_reason: tool_calls ist der einzige Weg, um „das Modell hat die Tool-Nutzung abgelehnt“ von „das Tool wurde ihm nie angeboten“ zu unterscheiden. Diese Fehler sehen im Chat-Fenster identisch aus.
Das Modell
Offenlegung der Daten. Die Trainingsdaten sind ein öffentlicher Telekom-Churn-Benchmark, dessen Zielspalte für diese Übung in
payment_delayumbenannt wurde. Die Features sind Anrufprotokoll- und Kontofelder, keine Abrechnungshistorie. Die Modellierung ist real und die Pipeline ist real; der geschäftliche Rahmen ist synthetisch. Behandeln Sie die Zahlen als funktionierendes Beispiel, nicht als validiertes Kreditrisikomodell.
Eigenschaft | Wert |
Zeilen / Spalten | 3.000 / 20 |
Klassenbalance |
|
Pipeline |
|
Aufteilung | 80/20 stratifiziert |
Features bei der Inferenz | 36 – 19 rohe plus 17 abgeleitete |
Entscheidungsschwelle | 0,35, als Artefakt persistiert |
Die Schwelle ist nicht 0,5 und nicht fest verdrahtet. Sie wird als models/threshold.pkl ausgeliefert und ist pro Request überschreibbar, denn bei einem Ziel, das zu 13,77 % positiv ist, optimiert der Standard-Cutoff auf das Falsche. Eine niedrigere Schwelle erfasst mehr Zahlungsverzögerer, um den Preis von mehr False Positives – und welcher Kompromiss richtig ist, ist eine Geschäftsentscheidung, keine Modellierungsentscheidung – deshalb stellt die API sie als Parameter bereit.
Nichts im Codebase verdrahtet einen Spaltennamen fest. Die Feature-Reihenfolge stammt aus feature_columns.pkl, die Ausreißergrenzen aus outlier_bounds.pkl, sodass ein Retraining keine Codeänderung erfordert.
Technische Entscheidungen, die eine Verteidigung wert sind
Der MCP-Server importiert das Modell nie. Er ruft die API über HTTP auf. Das hält den MCP-Prozess klein – kein sklearn, kein 9-MB-Pickle im Speicher – und erlaubt es dem Modellservice, wie jeder andere Mikroservice skaliert, bereitgestellt und überwacht zu werden. Ein Protokolladapter sollte keine Geschäftslogik enthalten.
Die Vorhersage läuft außerhalb der Event-Loop. Der Inferenzaufruf wird mit run_in_threadpool ausgeführt, sodass CPU-gebundenes Scoring FastAPIs asynchrone Loop unter gleichzeitigen Requests nie blockiert.
stdio-Disziplin. MCP über stdio erfordert, dass stdout JSON-RPC-Frames und sonst nichts trägt – ein verirrtes print() korrumpiert den Stream und beendet die Sitzung. Folglich wird das gesamte Logging auf stderr umgeleitet, httpx und httpcore werden stummgeschaltet, und launcher.py leitet uvicorns Ausgabe in eine Logdatei um, wartet auf /health und übergibt dem Client erst dann sauberes stdio.
Zwei Einstiegspunkte für zwei Topologien. server.py ist der Container-Einstiegspunkt, bei dem die API ein separater Service ist. launcher.py ist der lokale Einstiegspunkt, der die API selbst startet und auf sie wartet – die richtige Form für einen Desktop-MCP-Client, der erwartet, dass ein Prozess seine Abhängigkeiten besitzt.
Ein Pin, der einen realen Vorfall dokumentiert. mcp>=1.2.0,<2.0: mcp 2.x hat streamablehttp_client umbenannt, und mcpo 0.0.20 importiert noch den alten Namen, sodass mcpo gegen 2.x in eine Crash-Loop gerät. Die Obergrenze ist in requirements.txt mit dem Grund kommentiert, denn ein Versions-Pin ohne Grund wird von der nächsten Person, die ihn liest, gelöscht.
Repository-Struktur
mcp-payment-delay/
├── src/payment_delay/
│ ├── config.py # single source of truth for paths + endpoints, all env-overridable
│ ├── inference/predictor.py # the only code that opens the pickle; imports no web framework
│ ├── api/main.py # thin FastAPI adapter over the predictor
│ └── mcp_server/
│ ├── server.py # FastMCP tools, resources, prompt (container entrypoint)
│ ├── api_client.py # HTTP calls into the model service
│ └── launcher.py # starts the API, then serves MCP on clean stdio (local entrypoint)
├── models/ # model, threshold, outlier bounds, feature order
├── data/telecomunicatii.csv # sample dataset
├── deploy/litellm_config.yaml # Bedrock routing
├── scripts/bedrock_smoke_test.py # asserts a tool call comes back, not merely a 200
├── docs/ # architecture + Docker runbook
├── Dockerfile # one image, serves both the API and the mcpo bridge
└── docker-compose.yml # API + mcpo + LiteLLM + OpenWebUIErste Schritte
Nur den Modellservice ausführen – keine Cloud-Anmeldedaten nötig
python3 -m venv .venv && source .venv/bin/activate
make install # pip install -e ".[dev]"
make api # http://localhost:8000/docsEndpoint | Zweck |
| Service läuft, Modell geladen |
| Modelltyp, Klassen, Features, Schwelle |
| Erforderliche CSV-Spalten |
| eine Zeile (JSON-Objekt oder einzeilige CSV) -> ein ja/nein |
| mehrzeilige CSV -> ein ja/nein pro Zeile |
| mehrzeilige CSV -> ein ja/nein für die gesamte Datei |
curl -F "file=@data/telecomunicatii.csv" \
"http://localhost:8000/predict/summary?threshold=0.35"Einen eigenen MCP-Client anbinden
python3 -m payment_delay.mcp_server.launcherStellt die Tools über stdio bereit und startet die API, falls sie noch nicht gesund ist. opencode.json verdrahtet dies in OpenCode; Claude Desktop und jeder andere stdio-MCP-Client binden sich auf dieselbe Weise an.
Den vollständigen Stack ausführen
cp .env.example .env # add your Bedrock key
python3 scripts/bedrock_smoke_test.py
make stack # http://localhost:3000Vollständiges Runbook, einschließlich Einrichtung der Anmeldedaten und Fehlerbehebung: docs/docker-stack.md.
Die MCP-Oberfläche
Fünf Tools, zwei Ressourcen, eine Prompt-Vorlage:
get_api_health service + model status
get_model_info model metadata, classes, features, endpoints
get_input_schema expected CSV columns
predict_payment_delay CSV in (path or text), per-row or aggregate, threshold configurable
predict_single_customer one customer as a JSON object
payment-delay://context business + modelling context, injected as a resource
payment-delay://api-contract the HTTP contract these tools call
interpret_payment_delay_result prompt template for business-language explanationRessourcen und Prompts sind die untergenutzte Hälfte von MCP. Die Kontextressource bedeutet, dass dem Client nicht gesagt werden muss, wofür das Modell da ist – er kann es nachlesen.
Technologie-Stack
FastAPI · FastMCP · mcpo · scikit-learn · imbalanced-learn · pandas · LiteLLM · AWS Bedrock · OpenWebUI · Docker Compose · uvicorn · httpx
Dokumentation
Architektur – die Schichten, die Vorhersage-Pipeline und warum die Trennung genau dort liegt
Docker-Stack-Runbook – Anmeldedaten, Start, Health Checks, Fehlerbehebung
docs/assignment/ – das ursprüngliche Briefing
Eduard-Gabriel Tudoran, 2026.
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 Connectors
Unified MCP Server is a remote MCP connector for AI agents and vertical AI products that provides access to 22,000+ authorized SaaS tools across 400+ integrations and 24 categories directly inside LLMs (Claude, GPT, Gemini, Cohere). Tools operate only on explicitly authorized customer connections, enabling agents to safely read and write against live third-party systems.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Connect MCP clients to 2,000+ AI models without managing provider API keys.
Discover and call 10,000+ production APIs from one MCP server. Pay-per-call billing for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceExposes enterprise KPIs, health scores, forecasting, and anomaly detection as MCP tools, resources, and prompts for use by any MCP-compatible agent.2AGPL 3.0
- FlicenseNot gradedqualityCmaintenanceExposes internal company services as LLM-callable MCP tools, enabling AI agents to perform business operations like customer management, order processing, and support ticketing through natural language.
- FlicenseNot gradedqualityCmaintenanceExposes a governed lending portfolio (loans, customers, risk-tier history) to any MCP-compatible AI client via read-only tools, schema resources, and analysis prompts, wrapping an existing API gateway instead of connecting directly to the database.
- FlicenseAqualityBmaintenanceMCP server exposing a fictional payment domain as tools, resources, and prompts, enabling reasoning over transactions, payment hubs, services, and system health.8
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/eddii1/mcp-payment-delay'
If you have feedback or need assistance with the MCP directory API, please join our Discord server