Tool-Output-Bloat-Fix Plan

Overview

Sonnet 4.6 bricht in vf-sonnet (Open WebUI + Bedrock + mcp-vf-hosted) regelmaessig mit unvollstaendiger Antwort ab wenn die Aufgabe mehr als 2-3 Tool-Calls plus nennenswerte Datenmengen verarbeiten muss. Symptom-Bekaempfung mit max_tokens 4k → 16k → 32k skaliert nicht. Dieser Plan adressiert die Wurzel ueber vier Phasen — Quick-Wins, Server-Side-Aggregations-Tools, Tool-Output-Compaction-Defaults, Long-Term-Workflow-Tool-Coverage — und etabliert das Pattern fuer alle kuenftigen Multi-Tool-Use-Cases.

Problem Frame

Konkretes Beispiel (heute 14:30 in VF-Pilot): User fragt nach Event-Bilanz, Sonnet feuert 5 parallele ticketpay_list_*-Calls mit Default size=50. Tool-Output kombiniert: 20-30k Tokens Rohdaten. Sonnet beginnt zu antworten, bricht bei „Lass mich al” ab. Bedrock-Logs zeigen 200 OK fuer alle Calls — also kein API-Fehler, sondern Output-Budget aufgebraucht oder Stream gecuttet.

Wurzelanalyse:

  1. Tool-Output-Bloat (primaer) — 5 list-Calls × 50 Records mit allen Feldern fressen 20-30k Tokens, die Sonnet erst lesen + aggregieren muss bevor visible Output startet.
  2. Extended Thinking frisst unsichtbar 3-8k Output-Tokens — max_tokens deckt thinking + visible Output gemeinsam.
  3. Cloudflare-Tunnel 100s-Cutoff (Issue #16747) — bei langem Sonnet-Reasoning kommen keine Stream-Bytes durch, CF killt die Verbindung.
  4. Eskalierende Knappheit — bei jedem zusaetzlichen Tool-Call oder Folge-Schritt wird Output-Headroom enger, bis es kollabiert.

Wie Anthropic das bei claude.ai loest:

  • Task-Tools statt Raw-Data-Tools — Connectors haben get_invoice_summary(period), der Server aggregiert, das LLM bekommt 200 Tokens statt 30k.
  • Artifacts — lange Outputs gehen in separate Render-Komponente, nicht in den Chat-Stream.
  • Extended Thinking budget explizitthinking.budget_tokens getrennt von max_tokens.
  • Prompt-Caching auf Tool-Schemas — billig haeufiger zu callen statt einen Mega-Output zu verarbeiten.

Wir adaptieren #1, #3 strukturell; #2 via HTML-Output-Konvention (schon v2.5 live); #4 ist eh schon teilweise aktiv via LiteLLM cache_control_injection_points.

Requirements Trace

  • R1 — Aggregations-Aufgaben (Event-Bilanz, Offene Posten, Monatsabschluss) muessen ohne max_tokens-Cutoff vollstaendig durchlaufen, auch bei 5+ User-Schritten in derselben Konversation
  • R2 — Cloudflare-Tunnel-Stream darf nicht durch 100s-Timeout cuttet werden bei langem Reasoning
  • R3 — Extended Thinking muss explizit gebudget sein, damit visible Output deterministisch Platz hat
  • R4 — Token-Kosten pro Aggregations-Aufgabe sollen signifikant sinken (Ziel: ~Faktor 50-100 weniger Tool-Output-Tokens)
  • R5 — Pattern muss wiederverwendbar sein fuer kuenftige VF-Workflow-Tools (offene_posten, monatsabschluss, andere)
  • R6 — Bestehende Sub-MCPs (papierkram, ticketpay, m365) duerfen nicht breaking-changed werden — Aggregations-Tools sind additiv

Scope Boundaries

Explizit nicht in Scope:

  • v2.5 HTML-Section ist schon live, nicht erneut aufmachen
  • Open WebUI Filter-Functions (Audit, PII-Maskierung, Cost-Tracking) — separate Arbeit, eigener Plan
  • 2FA, RAG-Migration auf pgvector, Bedrock Titan Embeddings — separate Tier-3-Items
  • Stirling-PDF-Setup — separate Aufgabe, wartet auf Marvin’s CF-Setup
  • MCP-Pipeline-AWS-Phase 2 (Multi-Tenant) — anderes Projekt
  • M365-spezifische Aggregations-Tools — Bedarf noch nicht bewiesen, nicht jetzt
  • Open-WebUI-Engine-Wechsel von Pyodide zu Jupyter — separate Compute-/Security-Diskussion

Context & Research

Relevant Code and Patterns

  • ~/source/mcps/mcp-vf-hosted/src/mcp_vf_hosted/main.py — FastMCP v2 Wrapper, mounted 3 Sub-MCPs als Proxies via create_proxy(Client(StdioTransport(...))). Neue Tools werden direkt mit @app.tool registriert vor app.mount(proxy, namespace=...).
  • ~/source/mcps/mcp-vf-hosted/src/mcp_vf_hosted/prompts.py — Workflow-Prompts (offene_posten, event_bilanz, monatsabschluss). Aktueller Stand: Prompt-Texte mit Tool-Choreographie-Anweisungen. Wir migrieren event_bilanz von Prompt zu echtem Tool (P2).
  • ~/source/mcps/mcp-ticketpay/src/mcp_ticketpay/server.py — hat etablierten Output-Compaction-Mechanismus: _AUTO_COMPACT_BYTES = 50_000, _compact_item(), _select_fields(), count_only=True, fields=[...]. Vorbild fuer Aggregations-Tool-Output.
  • ~/source/mcps/mcp-vf-hosted/tests/ — pytest-Infrastruktur mit asyncio (conftest.py, test_health.py, test_prompts.py). Plan-Test-Files folgen diesem Pattern.
  • ~/source/mcps/mcp-vf-hosted/infra/lib/mcp-vf-hosted-stack.ts — Image-Digest gepinnt; nach Code-Aenderung Image-Bump-Pattern (dokumentiert direkt im Stack-File).
  • ~/source/apps/open-webui-vf/infra/lib/open-webui-vf-stack.ts — hat DEFAULT_MODEL_PARAMS als globale ENV-Var; thinking-Budget kann dort ergaenzt werden.
  • ~/source/apps/open-webui-vf/prompts/vf-sonnet.txt — System-Prompt v2.5 mit <werkzeug_prioritaet> und <fehler_resilienz>. P3 erweitert um Tool-Output-Compaction-Klausel.

Institutional Learnings

  • agentic-ventures/intern/wissen/prozesse/system-prompt-patterns.md — etabliertes Pattern-File mit Snippets fuer Werkzeug-Prioritaet, Fehler-Resilienz, Output-Format. P3 ergaenzt um „Aggregation-First”-Snippet.
  • agentic-ventures/intern/runs/2026-05-13-research-open-webui-docs/findings.md — hat schon die CF-Tunnel-Config-Empfehlung (connectTimeout: 600s, tcpKeepAlive: 30s) und das Aggregations-Tool-Pattern als wichtigste Production-Hebel identifiziert.
  • mcp-vf-hosted Lessons (capabilities/mcps/mcp-vf-hosted.md): Image-Digest-Pinning vermeidet Drift, ECR-Push-Pattern dokumentiert (linux/amd64 wegen Fargate-x86), DeletionPolicy:Retain fuer Secrets vor Stack-Delete.

External References

Key Technical Decisions

DecisionRationale
Aggregations-Tools im mcp-vf-hosted Wrapper, nicht in Sub-MCPsCross-MCP-Aggregation moeglich (monatsabschluss spannt papierkram + ticketpay), keine Code-Duplikation in Sub-MCPs, konsistent mit Workflow-Prompts-Pattern. Sub-MCPs bleiben „raw data layer”.
Wrapper-Tool ruft Sub-MCPs intern via FastMCP-ClientSub-MCPs sind eh schon als Proxies mounted; FastMCP-Client kann sie direkt aufrufen ohne Subprozess-Overhead. Tool-Output-Aggregation passiert in Python in-process.
Erstes Pilot-Tool: ticketpay_event_bilanzHochfrequenter Use-Case (jedes naechste Event), klare Aggregation-Formel (Brutto, Stornos, Gebuehren, Netto), gut testbar. Andere zwei Workflow-Tools (offene_posten, monatsabschluss) folgen als P4 nach Pilot-Lerneffekten.
thinking.budget_tokens=4096 als Global-DefaultMitte des Anthropic-Empfehlungs-Range. Bei Multi-Tool-Aufgaben reicht’s; bei Reasoning-schweren Tasks (Code-Review-Style) per-Modell-Override moeglich.
System-Prompt-Klausel statt Tool-Schema-Default-Aenderung in ticketpayBackward-Kompatibilitaet — andere User des ticketpay-MCP (z.B. Marvin’s Claude-Code-Setup) sollen nicht plötzlich anders defaulten. Verhalten beim VF-Sonnet steuern wir ueber Prompt, nicht ueber Tool-Schema.
Aggregations-Tool gibt strukturiertes JSON mit Aggregaten + Quellen-Counts, NICHT alle Detail-RecordsFaktor 50-100 weniger Tokens. Wenn User Detail braucht, callt er gezielt ticketpay_list_* mit Filter.
CF-Tunnel-Config: nur connectTimeout + tcpKeepAlive, kein Wechsel auf ALB/CF-WorkerMinimaler Eingriff; ALB-Wechsel wuerde mcp-hosting-fargate-tunnel-Pattern brechen, das wir gerade etabliert haben.

Open Questions

Resolved During Planning

  • Wo bauen wir das Aggregations-Tool?mcp-vf-hosted Wrapper. Cross-MCP, weniger Duplikation, gleiche Schicht wie Workflow-Prompts.
  • Erstes Pilot-Tool?ticketpay_event_bilanz. Andere zwei (offene_posten, monatsabschluss) in P4.
  • Thinking-Budget? → 4096 Tokens. Mitte des Anthropic-Range, anpassbar per-Modell.
  • Wo CF-Tunnel-Config aendern? → Cloudflare Zero Trust Dashboard → Networks → Tunnels → mcp-vf-hosted und vf-chat (beide Tunnels mit den gleichen Settings — Public Hostname → Additional application settings → Connection).
  • System-Prompt-Klausel oder Tool-Schema-Default? → Klausel. Hat VF-Scope, andere ticketpay-User bleiben unbeeintraechtigt.

Deferred to Implementation

  • Genaues JSON-Schema des Aggregations-Tool-Outputs — finalisiert beim Schreiben, abhaengig davon was TicketPAY-API beim Aggregieren genau zurueckliefert (z.B. ob paid_at pro Transaction da ist oder ueber Status abgeleitet werden muss).
  • Exact pytest-Fixtures fuer das Aggregations-Tool — abhaengig davon ob wir das Live-API oder Mocks nutzen. Wahrscheinlich Mocks da kein TicketPAY-Test-Account.
  • Cloudflare-Tunnel-Config-UI-Reihenfolge — UI-Layout kann sich aendern; die Setting-Namen sind stabil aber der Klick-Pfad pruefe ich erst im Browser.

High-Level Technical Design

Dieser Sketch zeigt die intendierte Struktur des neuen Aggregations-Tools im mcp-vf-hosted-Wrapper. Direktive Guidance fuer Review, nicht Implementation-Spec. Der implementierende Agent soll’s als Kontext nutzen, nicht 1:1 reproduzieren.

Vorher (heute, problematisch):

sequenceDiagram
    participant User
    participant Sonnet
    participant TicketPAY-MCP
    participant TicketPAY-API

    User->>Sonnet: "Event-Bilanz fuer naechstes Event"
    Sonnet->>TicketPAY-MCP: list_events()
    TicketPAY-MCP->>TicketPAY-API: GET /exports/events
    TicketPAY-API-->>TicketPAY-MCP: ~50 Events (paginiert)
    TicketPAY-MCP-->>Sonnet: 5k Tokens
    par Parallele Tool-Calls
        Sonnet->>TicketPAY-MCP: list_tickets(event_id=X)
        TicketPAY-MCP->>TicketPAY-API: GET /exports/tickets
        TicketPAY-MCP-->>Sonnet: 8k Tokens
    and
        Sonnet->>TicketPAY-MCP: list_transactions(event_id=X)
        TicketPAY-MCP-->>Sonnet: 6k Tokens
    and
        Sonnet->>TicketPAY-MCP: list_fees(event_id=X)
        TicketPAY-MCP-->>Sonnet: 4k Tokens
    and
        Sonnet->>TicketPAY-MCP: list_cancellations(event_id=X)
        TicketPAY-MCP-->>Sonnet: 3k Tokens
    end
    Note over Sonnet: 26k Tokens Tool-Output<br/>+ 3-8k thinking<br/>+ system prompt 3.5k<br/>= eng → Cutoff
    Sonnet-->>User: "Lass mich al..." [ABBRUCH]

Nachher (mit Aggregations-Tool):

sequenceDiagram
    participant User
    participant Sonnet
    participant vf-mono Tool
    participant Sub-MCPs

    User->>Sonnet: "Event-Bilanz fuer naechstes Event"
    Sonnet->>vf-mono Tool: ticketpay_event_bilanz(event_id="next")
    Note over vf-mono Tool: Intern parallel:<br/>5 Sub-MCP-Calls,<br/>Aggregation in Python
    vf-mono Tool->>Sub-MCPs: 5 Tool-Calls (intern)
    Sub-MCPs-->>vf-mono Tool: ~25k Tokens (in-process)
    Note over vf-mono Tool: Aggregation:<br/>summen + counts<br/>+ key metrics
    vf-mono Tool-->>Sonnet: ~200 Tokens<br/>{tickets_verkauft, brutto, stornos,<br/> gebuehren, netto, event_meta}
    Sonnet-->>User: Vollstaendige Bilanz-Antwort (kein Cutoff)

Tool-Output-Schema (Skizze, finalisiert in Implementation):

{
  "event": {
    "id": 123939,
    "name": "Sparkasse Hamm | Comedy",
    "date": "2026-05-27",
    "status": "soldout"
  },
  "tickets": {
    "verkauft_anzahl": 350,
    "brutto_eur": 12250.00
  },
  "stornos": {
    "anzahl": 12,
    "refund_eur": 420.00
  },
  "gebuehren": {
    "ticketpay_eur": 367.50,
    "zahlungsanbieter_eur": 122.50
  },
  "netto_auszahlung_eur": 11340.00,
  "transaktionen": {
    "anzahl_bezahlt": 338,
    "summe_eingegangen_eur": 11830.00
  },
  "_sources": {
    "list_tickets_total": 350,
    "list_transactions_total": 338,
    "list_fees_total": 4,
    "list_cancellations_total": 12
  }
}

Implementation Units

graph TB
    P1A[P1.1 CF-Tunnel-Config<br/>Browser-Action] --> Test1[Smoke-Test:<br/>lange Calls gehen durch]
    P1B[P1.2 Thinking-Budget<br/>Stack-ENV] --> Test1
    P2A[P2.1 event_bilanz Tool<br/>im mcp-vf-hosted] --> P2B[P2.2 Tests + Build + Deploy]
    P2B --> Test2[Smoke-Test:<br/>Bilanz-Frage komplett]
    P3A[P3.1 System-Prompt v2.6<br/>Aggregation-First-Klausel] --> Test3[Verifikation:<br/>Sonnet nutzt Aggregations-Tool]
    P2B --> P3A
    P1A -.optional.- P2A
    P1B -.optional.- P2A
    Test1 --> Test2
    Test2 --> Test3
    Test3 --> P4[P4 weitere Workflow-Tools<br/>offene_posten, monatsabschluss]

Phase 1: Quick-Wins (parallel, blockieren nichts)

  • Unit P1.1: Cloudflare-Tunnel-Config — connectTimeout + tcpKeepAlive (Marvin Browser-Action pending)

Goal: Cloudflare-Tunnel cuttet keine Streams mehr bei langem Sonnet-Reasoning (Issue 16747-Mitigation).

Requirements: R2

Dependencies: Keine. Pure Cloudflare-Dashboard-Konfiguration durch Marvin.

Files: keine — Cloudflare-Zero-Trust-Dashboard.

Approach:

  • In Cloudflare Zero Trust → Networks → Tunnels → fuer beide Tunnels (mcp-vf-hosted und vf-chat):
    • Public Hostname-Editor oeffnen → Additional application settings → Connection
    • connectTimeout: 600s (Default 30s)
    • tcpKeepAlive: 30s (Default 30s — pruefen)
    • keepAliveTimeout: 600s (Default 90s)
    • Optional: noTLSVerify: true falls Origin self-signed (bei uns http://localhost: nicht relevant)
  • Settings speichern, kein cloudflared-Restart noetig (CF pusht Config zu Tunnel-Connectors).

Patterns to follow: Keine bestehende Doku im Vault, dieses Setup ist neu. Nach erfolgreicher Konfiguration in mcp-hosting-fargate-tunnel als Sub-Section dokumentieren.

Test scenarios:

  • Happy path: Marvin oeffnet https://vf-chat.agenticventures.de, stellt eine Frage die >120s Bedrock-Reasoning braucht (z.B. „erklaer mir Schritt fuer Schritt wie Stripe-Webhooks funktionieren”) — Antwort kommt komplett durch ohne Cutoff bei 100s.
  • Edge case: 5+ Min langes Multi-Tool-Reasoning (z.B. Cross-Quartal-Buchhaltungs-Analyse mit vielen Papierkram-Calls) — kein 524 Origin-Error.

Verification: CloudWatch zeigt fuer einen >100s-Call keinen 524 Origin-Error mehr; Open-WebUI-Log zeigt continuous streaming bis stop_reason=end_turn.

Estimated effort: 5-10 min Browser-Klick.

  • Unit P1.2: Extended-Thinking-Budget global setzen (deployed 2026-05-14)

Goal: Reasoning-Tokens sind vom visible Output-Budget entkoppelt — Sonnet hat deterministischen Headroom.

Requirements: R3

Dependencies: Keine. Stack-ENV-Aenderung in open-webui-vf.

Files:

  • Modify: ~/source/apps/open-webui-vf/infra/lib/open-webui-vf-stack.tsDEFAULT_MODEL_PARAMS JSON-String erweitern um thinking Block

Approach:

  • Aktuelles DEFAULT_MODEL_PARAMS={"max_tokens":8192,"temperature":0.7,"top_p":0.9,"function_calling":"native"} erweitern um:
    "thinking": {"type":"enabled","budget_tokens":4096}
    
  • Vorsicht: LiteLLM muss thinking-Param fuer Bedrock Converse durchreichen — laut Anthropic-LiteLLM-Doku ja, aber bei Erst-Deploy CloudWatch checken ob Bedrock 400 wirft (z.B. bei aelteren Bedrock-Modellen die das nicht koennen).
  • Per-Modell-Override moeglich via Custom-Model-params.thinking falls einzelne Modelle anderes Budget brauchen.
  • CDK-Deploy: ~3-5 min (Make-before-break ist im Stack).

Patterns to follow:

  • DEFAULT_MODEL_PARAMS-Pattern existiert schon — Section folgt gleicher Konvention.
  • Stack-Comment-Stil: kurz erklaeren warum (verweist auf diesen Plan).

Test scenarios:

  • Happy path: vf-sonnet Chat mit komplexer Aufgabe (z.B. „erstelle einen Vergleichs-Report ueber die letzten 3 Events”) laeuft durch — Antwort ist sichtbar, kein „Lass mich al”-Cutoff.
  • Edge case: thinking_budget zu klein gewaehlt → Sonnet’s Reasoning ist abgeschnitten → Antwort wird oberflaechlich. Wenn das auftritt: Budget hochziehen (8192 oder 16384) per-Modell auf vf-sonnet.
  • Error path: Bedrock returnt 400 weil thinking-Param nicht akzeptiert → CloudWatch zeigt das klar, Rollback durch Entfernen des thinking-Blocks und Re-Deploy.

Verification: Bei einer komplexen Aufgabe (z.B. Multi-Event-Vergleich) ist die visible Output-Token-Anzahl in der LiteLLM-Response-Usage signifikant hoeher als ohne thinking_budget (Sonnet hat mehr Headroom).

Rollback: thinking-Block aus DEFAULT_MODEL_PARAMS entfernen + Stack-Deploy. ~5 min.

Estimated effort: 20-30 min (Stack-Edit + CDK-Deploy + Smoke-Test).

Phase 2: Server-Side Aggregations-Tool

  • Unit P2.1: ticketpay_event_bilanz Tool im mcp-vf-hosted (7/7 tests GREEN, registered in main.py)

Goal: Neues Wrapper-Tool das intern die 5 ticketpay_list_*-Calls macht, aggregiert und ~200 Tokens strukturiertes Ergebnis zurueckgibt. Pattern fuer alle kuenftigen VF-Workflow-Tools.

Requirements: R1, R4, R5, R6

Dependencies: Keine harten Blocker. Bevorzugt nach P1.1 (CF-Tunnel) deployen damit der erste Smoke-Test sauber laeuft.

Files:

  • Create: ~/source/mcps/mcp-vf-hosted/src/mcp_vf_hosted/aggregations/__init__.py
  • Create: ~/source/mcps/mcp-vf-hosted/src/mcp_vf_hosted/aggregations/ticketpay.py — Aggregations-Logik (event_bilanz Funktion)
  • Modify: ~/source/mcps/mcp-vf-hosted/src/mcp_vf_hosted/main.py — neue Tool-Registrierung @app.tool mit Aufruf in aggregations.ticketpay
  • Test: ~/source/mcps/mcp-vf-hosted/tests/test_aggregations_ticketpay.py

Approach:

Execution note: Test-first — Mock-basierter Unit-Test fuer die Aggregations-Logik schreiben, dann implementieren. Integration-Test gegen Live-API erst wenn Mocks gruen.

  • Aggregations-Modul aggregations/ticketpay.py exportiert async function event_bilanz(client: FastMCP-Client, event_id: int) -> dict.
  • In main.py: nach Sub-MCP-Mount, neuer @app.tool mit Name ticketpay_event_bilanz (Namespace-konsistent), Dokstring beschreibt Zweck + Output-Schema, Body ruft aggregations.ticketpay.event_bilanz(client, event_id).
  • Die client Referenz: FastMCP-v2 Wrapper hat Zugriff auf seine eigenen mounted Sub-MCPs — internally entweder via client.call_tool(...) oder ueber direkte Function-Refs der mounted Proxies. Welcher Pfad genau, ist Implementation-Detail (Deferred to Implementation).
  • Aggregations-Logik:
    1. Parallel: list_events() → finde Event mit ID, hol Name + Date + Status
    2. Parallel: list_tickets(event_id=X), list_transactions(event_id=X), list_fees(event_id=X), list_cancellations(event_id=X) — alle mit count_only=True zuerst, dann nochmal ohne wenn Detail-Aggregation noetig ist
    3. Aggregation in Python: Summen, Counts, Netto-Berechnung
    4. Output strukturiertes Dict (siehe Schema in High-Level Technical Design)
  • Error-Handling: wenn ein Sub-Call fehlschlaegt → strukturierter Error im Output (_errors: [{tool: "...", error: "..."}]), nicht hart-raise — der Aufrufer (Sonnet) kann dann entscheiden.
  • Caching: nicht in P2 (Pilot). Falls Performance-Issue: spaeter.

Patterns to follow:

  • Tool-Registrierung wie in main.py mit @app.tool (FastMCP v2 Decorator). Siehe Pattern bei den Workflow-Prompts in prompts.py.
  • Async/await ueberall (FastMCP v2 ist asyncio-nativ).
  • Compaction-Mechanismus aus mcp-ticketpay/src/mcp_ticketpay/server.py (_AUTO_COMPACT_BYTES) ist KEIN Vorbild — wir aggregieren komplett anders (keine Listen, sondern Aggregates).

Test scenarios:

  • Happy path: event_bilanz mit valider event_id (Mock-Sub-MCP returnt 2 Tickets, 2 Transactions, 1 Fee, 0 Cancellations) → Output enthaelt korrekte Summen, Netto = Brutto - Stornos - Gebuehren, _sources-Counts stimmen.
  • Edge case: kein Sold-Out — Event mit 0 Tickets verkauft → Aggregation funktioniert, alle Summen sind 0.00, _sources.list_tickets_total = 0.
  • Edge case: viele Stornos — Stornos > Brutto sollte nicht negatives Netto produzieren ohne expliziten Hinweis; in _warnings-Block hochsetzen wenn netto < 0.
  • Edge case: paginated Tool-Output — wenn ein Sub-Tool 3 Seiten returnt, alle paginieren (FastMCP-Client kann das oder wir loopen manuell).
  • Error path: invalid event_id (negativ, string statt int) → Tool returnt strukturierten Error, kein Crash.
  • Error path: TicketPAY-API down — Sub-MCP wirft Exception → Aggregation fangt das ab, returnt _errors-Block + so-viel-wie-moeglich aggregierte Daten.
  • Integration scenario: echter Mount im FastMCP-Wrapper — Tool ist via client.list_tools() sichtbar mit korrektem Schema, Docstring landet als Description (wichtig fuer Sonnet’s Tool-Choice).

Verification:

  • pytest gruen mit allen Test-Scenarios
  • Tool-Schema bei await client.list_tools() enthaelt ticketpay_event_bilanz mit kompletter Description
  • Lokaler Smoke-Test mit MCP-Inspector: Tool ruft alle Sub-MCPs, returnt korrekte Aggregate

Rollback: Stack-File auf vorherigen Image-Digest pinnen + CDK-Deploy. Aggregations-Code bleibt im Repo, ist nur nicht mehr im Container. ~5 min.

Estimated effort: 2-3h (Tests schreiben + implementieren + lokal verifizieren).

  • Unit P2.2: ECR-Build + ECS-Deploy mit neuem Aggregations-Tool (deployed 2026-05-14, digest ae60756b)

Goal: mcp-vf-hosted Container laeuft mit dem neuen Tool in eu-central-1, ist via Cloudflare-Tunnel erreichbar.

Requirements: R1, R4

Dependencies: P2.1 fertig

Files:

  • Modify: ~/source/mcps/mcp-vf-hosted/infra/lib/mcp-vf-hosted-stack.ts — Image-Digest in der vf-mono-Container-Definition

Approach:

  • docker buildx build --platform linux/amd64 -f mcp-vf-hosted/Dockerfile -t <ecr-uri>:latest --push . aus ~/source/mcps/
  • Neuen Manifest-Digest aus ECR holen: aws ecr describe-images --repository-name mcp-vf-hosted --image-ids imageTag=latest --query 'imageDetails[0].imageDigest' --output text
  • Stack-File mit neuem Digest patchen
  • CDK Deploy mit --profile av-prod --require-approval never
  • ECS macht Make-before-break, ~3 min Deployment-Zeit

Patterns to follow: Exakt das Bump-Pattern aus dem Dockerfile-Kommentar und mcp-vf-hosted-stack.ts (haben wir gestern 2026-05-13 genutzt fuer den ticketpay-Fix).

Test scenarios: Keine direkten Code-Tests — Smoke-Test gegen Live-Endpoint via MCP-Inspector oder vf-sonnet direkt.

Verification:

  • ECS describe-services zeigt runningCount: 1, rolloutState: COMPLETED, taskDefinition: <neue Revision>
  • vf-sonnet Chat: „Mach mir die Bilanz fuer das naechste Event” → Tool wird aufgerufen, Antwort kommt komplett ohne Cutoff
  • CloudWatch-Logs zeigen Sub-MCP-Calls bei ticketpay_event_bilanz-Invocation (interne Calls sichtbar)

Rollback: Vorherigen Digest in Stack-File + Re-Deploy. ~5 min.

Estimated effort: 15-20 min Build + Push + Deploy.

Phase 3: System-Prompt Aggregation-First-Klausel (v2.6)

  • Unit P3.1: vf-sonnet System-Prompt v2.6 — Aggregation-First (live 2026-05-14, 15008 chars)

Goal: Sonnet ruft automatisch das neue Aggregations-Tool statt 5 list_*-Tools wenn die Frage nach einer Bilanz/Aggregation aussieht.

Requirements: R1, R5

Dependencies: P2.2 muss deployed sein (sonst kennt Sonnet das Tool nicht im Schema-Discovery).

Files:

  • Modify: ~/source/apps/open-webui-vf/prompts/vf-sonnet.txt — neue Section <aggregation_first> oder Erweiterung von <werkzeug_prioritaet>
  • Modify: ~/source/apps/open-webui-vf/prompts/vf-sonnet.md — Changelog-Eintrag v2.6
  • API-Patch via curl auf https://vf-chat.agenticventures.de/api/v1/models/model/update?id=vf-sonnet (keine Stack-Deploy noetig)

Approach:

Neue Section (oder Ergaenzung) mit klarem Pattern:

<aggregation_first>
Wenn die Aufgabe nach einer Aggregation aussieht (Bilanz, Summe, 
Uebersicht, Vergleich, Monatsabschluss): pruefe zuerst ob es einen
spezifischen Workflow-Tool fuer diese Aggregation gibt. Beispiele:

- "Event-Bilanz" / "Was hat das Event eingebracht" / "Tickets + Netto fuer Event X"
  -> ticketpay_event_bilanz(event_id), NICHT 5 parallele list_*-Calls
- "Offene Posten" / "Unbezahlte Rechnungen" -> (kuenftig: papierkram_offene_posten)
- "Monatsabschluss Mai" -> (kuenftig: vf_monatsabschluss(monat))

Wenn es einen passenden Aggregations-Tool gibt: IMMER den nutzen statt
mehrere raw list_*-Tools zu chainen. Der Aggregations-Tool macht
serverseitig die Berechnung und liefert ~200 Tokens statt 30k Tokens
Rohdaten — schneller, billiger, kein Output-Cutoff-Risiko.

Wenn KEIN passender Aggregations-Tool existiert UND die Aufgabe trotzdem
viele list_*-Calls braucht: nutze `count_only=True` und `fields=[...]`
aktiv um Tool-Output klein zu halten. Bei ticketpay-Tools z.B.:
list_tickets(from_=..., to=..., count_only=True) gibt nur Total zurueck,
keine 50 Detail-Records.
</aggregation_first>

Patterns to follow: XML-Section-Schema des v2.5-Prompts; etabliertes system-prompt-patterns-Snippet-Format.

Test scenarios:

  • Happy path: Marvin fragt „Wie lief das naechste Event finanziell?” → Sonnet ruft ticketpay_event_bilanz als ersten und einzigen Tool, gibt strukturierte Bilanz zurueck.
  • Edge case: Frage ist NICHT-Aggregations „Liste mir die 5 letzten verkauften Tickets” → Sonnet ruft ticketpay_list_tickets(size=5) und NICHT event_bilanz. Klausel darf nicht overreach-en.
  • Edge case: Aggregation, aber kein dediziertes Tool (noch nicht vorhanden) „Erstell mir den Monatsabschluss” → Sonnet erkennt dass es kein vf_monatsabschluss-Tool gibt, faellt zurueck auf manuelle Aggregation mit count_only=True als erstem Schritt.

Verification: vf-sonnet’s Custom-Model-API-GET zeigt system_len ~12k+ Zeichen, updated_at aktuell; Smoke-Test im neuen Chat zeigt einzigen Tool-Call statt 5.

Rollback: vorherige Prompt-Version (v2.5) per API zurueckspielen. ~30 Sekunden.

Estimated effort: 30 min (Section schreiben + API-Patch + Smoke-Test).

Phase 4: Long-Term — Weitere Workflow-Tools nach Bedarf

  • Unit P4.1 (deferred): papierkram_offene_posten Aggregations-Tool

Goal: Server-side Aggregation aller unbezahlten Rechnungen mit Tagen-offen und Mahnstufen.

Triggered durch: Wenn Marvin’s regelmaessige Buchhaltungs-Reviews den Workflow-Prompt /offene_posten haeufig nutzen und dort Cutoffs / langsam laeuft. Konkrete Schwelle: 2+ Vorfaelle in 2 Wochen → Tool bauen.

Pattern: Identisch zu P2.1, anderer Aggregations-Code.

Estimated effort: 1-2h.

  • Unit P4.2 (deferred): vf_monatsabschluss(monat) Cross-MCP-Aggregation

Goal: Kombiniert Papierkram-Rechnungen + TicketPAY-Auszahlungen + Bank-Bewegungen fuer einen Monat in ein konsolidiertes Ergebnis.

Triggered durch: Wenn das monatliche Reporting fuer VF tatsaechlich ueber vf-sonnet ablaeuft (nicht nur Marvin’s Console-Sessions). Plus: Pattern-Validation aus P2.1 muss durch sein.

Estimated effort: 2-3h (Cross-MCP-Logik komplexer).

Beide P4-Units: Status deferred, nicht in Scope dieses Plans. Hier nur erwaehnt damit das Pattern-Investment in P2 klar kontextualisiert ist.

System-Wide Impact

  • Interaction graph:

    • vf-sonnet (Custom-Model) → mcp-vf-hosted (Wrapper) → neue ticketpay_event_bilanz Tool → intern: mcp-ticketpay (Sub-MCP) → TicketPAY-API
    • LiteLLM-Layer wird thinking-Param durchreichen — neue API-Surface gegen Bedrock Converse
    • Cloudflare-Tunnel-Config aendert sich fuer beide Tunnels (mcp-vf-hosted UND vf-chat) — Symmetry pruefen
  • Error propagation:

    • Sub-MCP-Fehler innerhalb des Aggregations-Tools werden gefangen, in _errors strukturiert, Tool returnt aber so-viel-wie-moeglich. Sonnet entscheidet ob das nutzbar ist.
    • Wenn LiteLLM thinking-Param nicht akzeptiert → 400 Bedrock-Error → CloudWatch zeigt das, Rollback durch Entfernen.
    • Cloudflare-Tunnel-Config-Fehler (z.B. ungueltige Timeout-Werte) → CF Dashboard validiert vor Save.
  • State lifecycle risks:

    • Aggregations-Tool ist stateless — keine DB-Writes, keine Sessions. Sicher.
    • Cloudflare-Tunnel-Config ist persistiert pro Hostname. Vor Aenderung dokumentieren was vorher war (Rollback-Pfad).
  • API surface parity:

    • mcp-vf-hosted ist Single-Tenant (VF-only). Andere Kunden die das Pattern adoptieren wollen, wuerden eigenen Wrapper mit eigenen Aggregations-Tools bekommen.
    • Sub-MCPs (papierkram, ticketpay, m365) bleiben unbeeintraechtigt — kein Breaking Change in deren API.
  • Integration coverage: Tests mit gemockten Sub-MCP-Tools decken die Aggregations-Logik. Real-World-Smoke-Test im VF-Chat ist der eigentliche Integration-Test der nicht durch Mocks ersetzt werden kann.

  • Unchanged invariants:

    • Sub-MCPs: API-Vertrag unveraendert
    • Scalekit-OAuth-Layer: unveraendert
    • Cloudflared-Sidecar-Pattern: unveraendert (nur Config-Param-Tweak im CF-Dashboard)
    • Open-WebUI-LiteLLM-Bedrock-Pfad: nur additive Param-Erweiterung

Risks & Dependencies

RiskMitigation
LiteLLM reicht thinking-Param nicht an Bedrock durchSmoke-Test direkt nach P1.2-Deploy; Rollback ist 1 Zeile + CDK-Deploy
Aggregations-Tool macht selbst zu viele Sub-Calls und cuttet interncount_only=True als ersten Pass, gezielt Details holen nur wenn noetig; max. 3-5 Sub-Calls pro Aggregation
Sonnet ignoriert die neue <aggregation_first>-Klausel und ruft trotzdem raw list_*-ToolsTool-Description sehr klar schreiben (Sonnet folgt Tool-Descriptions mehr als Prompt-Klauseln); A/B im Test pruefen
Cloudflare-Tunnel-Config-Aenderung bricht laufende ConnectionsCF macht Hot-Reload; im worst case Tunnel-Restart ueber Dashboard (cloudflared in ECS bleibt running)
Image-Digest-Bump im mcp-vf-hosted-Stack vergisst CDK-Diff zu pruefen → falsche Stack-AenderungVor cdk deploy IMMER cdk diff pruefen — Pattern aus mcp-vf-hosted-Lessons schon etabliert
Aggregations-Tool-Output-Schema aendert sich spaeter → breaking change fuer SonnetErst beim 2. Workflow-Tool (P4) das Output-Schema generalisieren; bis dahin per-Tool eigenes Schema OK
Tests sind alle gegen Mocks, Real-API hat Schema-DriftSmoke-Test gegen Live-TicketPAY mit echtem Event vor Deploy (manuell). VF-Pilot-Phase erlaubt das.

Cost-Implikation

P1.1 (CF-Tunnel): Keine Cost-Aenderung. Verhindert User-Frustration und Re-Runs.

P1.2 (Thinking-Budget): Output-Tokens steigen leicht (thinking_budget zaehlt zu Output-Kosten), aber Erfolgsrate komplexer Aufgaben deutlich hoeher → weniger Retry-Calls. Sonnet 4.6 Output @ 0.061 pro Call. Bei aktuell ~50 komplexen Calls/Monat = ~3 EUR/Monat Mehrkosten. Marginal.

P2 (Aggregations-Tool): Hauptsparhebel. Pro Bilanz-Aufruf:

  • Vorher: 5 list_*-Tool-Outputs zu ~25k Tokens Input fuer Sonnet’s Output-Generation → ca. 0.08 EUR Input + 0.30 EUR Output = ~0.38 EUR
  • Nachher: 1 aggregation-Tool-Output zu ~200 Tokens + kuerzere Output-Antwort → ca. 0.001 EUR Input + 0.05 EUR Output = ~0.05 EUR
  • Ersparnis pro Aufruf: ~0.33 EUR (~85% weniger)
  • Bei 30 Bilanz-Aufrufen/Monat: 10 EUR/Monat Ersparnis. Nicht riesig, aber kompounded mit anderen Workflow-Tools (P4) skaliert das.

P3 (System-Prompt v2.6): Token-Cost +~150 Tokens System-Prompt pro Call. Bei prompt-caching (eh aktiv): ~zero Marginalkosten.

Netto-Cost-Bilanz: Klar positiv. Plus: groesserer Wert ist „User-Experience” (keine abgebrochenen Antworten) und „Skalierbarkeit” (Pattern fuer kuenftige Tools).

Sources & References

  • Origin: Marvin’s Brief aus /ce:plan Aufruf 2026-05-14, plus aktuelle Inzidenz-Logs in CloudWatch (open-webui-vf log group)
  • Recherche-Run: _index (System-Prompt-Patterns + 8 Adaptions-Snippets)
  • Recherche-Run: _index (Open-WebUI-ENV-Vars, CF-Timeout, Tool-Use-Pattern)
  • Code-Vorbild: ~/source/mcps/mcp-ticketpay/src/mcp_ticketpay/server.py (_AUTO_COMPACT_BYTES, count_only, fields Pattern)
  • Workflow-Prompts: ~/source/mcps/mcp-vf-hosted/src/mcp_vf_hosted/prompts.py (event_bilanz Prompt-Text als Logik-Vorlage)
  • Anthropic Extended Thinking
  • AWS Bedrock Converse + Reasoning
  • Open WebUI Issue #16747
  • LiteLLM Anthropic-Provider-Doku