betrieb-mcp

betrieb-mcp

MCP server that gives AI agents controlled access to a building services company's daily operations, including customer records, scheduling, and offer calculations. All write operations are protected by a two-stage approval mechanism with time-limited tokens to prevent unauthorized changes.

Category
访问服务器

README

betrieb-mcp

MCP-Server, der einem KI-Agenten den Zugriff auf den Betriebsalltag eines Gebäudedienstleisters gibt: Kundenakte, Terminplanung, Angebotskalkulation.

Der eigentliche Gegenstand dieses Repositories ist nicht die Fachlogik, sondern die Frage, wie ein Agent schreiben darf. Lesen ist frei. Jeder schreibende Vorgang läuft zweistufig über eine Freigabe-Schranke.


Warum zweistufig

Ein Agent, der Termine buchen und Angebote anlegen kann, ist nützlich. Ein Agent, der das unbeaufsichtigt tut, ist ein Haftungsrisiko: Ein falsch verstandener Satz am Telefon, und ein Monteur steht am falschen Tag beim falschen Kunden.

Die übliche Antwort darauf ist ein Bestätigungsdialog im Frontend. Der reicht nicht, weil er außerhalb des Systems liegt — der Agent kann ihn umgehen, sobald er über eine andere Oberfläche angesprochen wird. Hier sitzt die Schranke deshalb im Server:

  1. betrieb_prepare_* rechnet, prüft auf Konflikte, erzeugt eine Vorschau in Klartext und ein Freigabe-Token. Geschrieben wird nichts.
  2. Ein Mensch liest die Vorschau und bestätigt.
  3. betrieb_commit_* löst das Token ein und schreibt.

Das Token ist an genau eine Nutzlast gebunden, fünf Minuten gültig, nicht zwischen Aktionen übertragbar und nach einmaliger Verwendung verbraucht. Ein Agent, der die Vorschau überspringen will, hat kein Token — und damit keinen Schreibzugriff. Die Regel ist nicht Konvention, sondern Mechanik.

Zusätzlich wird der Terminkonflikt unmittelbar vor dem Schreiben erneut geprüft. Zwischen Vorschau und Bestätigung können Minuten liegen; in denen kann jemand anders denselben Slot belegt haben. Ohne diese zweite Prüfung wäre die Schranke ein Papiertiger — sie würde nur den Zustand von vorhin bestätigen.


Werkzeuge

Werkzeug Art Zweck
betrieb_suche_kunden lesend Suche über Name, Ort, Kundennummer; mit Blättern
betrieb_hole_kunde lesend Stammdaten samt Terminen und Angeboten
betrieb_liste_termine lesend Termine im Zeitraum, optional je Monteur
betrieb_freie_slots lesend Freie Zeitfenster eines Monteurs an einem Tag
betrieb_preisliste lesend Leistungskatalog mit Einheiten und Nettopreisen
betrieb_kalkuliere lesend Positionen durchrechnen, ohne etwas anzulegen
betrieb_prepare_angebot Vorschau Kalkuliert und gibt Freigabe-Token aus
betrieb_prepare_termin Vorschau Prüft Konflikte und gibt Freigabe-Token aus
betrieb_commit_angebot schreibend Legt das Angebot an — nur mit Token
betrieb_commit_termin schreibend Bucht den Termin — nur mit Token

betrieb_freie_slots ist bewusst ein eigenes Werkzeug und kein Nebenprodukt der Terminliste: Ein Agent, der buchen soll, braucht die Lücken, nicht die Belegung. Aus einer Liste von Terminen die freien Fenster abzuleiten, ist eine Rechenaufgabe, die im Server besser aufgehoben ist als im Modell.


Ablauf in echt

Ungekürzte Ausgaben eines Laufs gegen die Demo-Datenbank.

Freie Fenster suchen — Kaya hat vormittags und nachmittags Termine:

$ betrieb_freie_slots  datum=2026-09-01  dauer_minuten=90  monteur=Kaya
{
  "datum": "2026-09-01",
  "monteur": "Kaya",
  "dauer_minuten": 90,
  "freie_fenster": [
    { "von": "11:00", "bis": "13:00" },
    { "von": "15:30", "bis": "17:00" }
  ]
}

Einen belegten Slot buchen wollen — der Fehler nennt den Grund und den nächsten Schritt:

$ betrieb_prepare_termin  kunde_id=K-1002  datum=2026-09-01  von=09:00 ...
{
  "fehler": "Kaya ist am 2026-09-01 von 08:00 bis 11:00 bereits gebunden
             (Treppenhausreinigung, 3 Aufgänge). Nutze 'betrieb_freie_slots',
             um ein passendes Fenster zu finden."
}

Angebot vorbereiten — Vorschau für den Menschen, Token für den Agenten:

$ betrieb_prepare_angebot  kunde_id=K-1001  positionen=[...]

Angebot für Hausverwaltung Keller GmbH (K-1001), Pforzheim

  420 m²           grundreinigung_qm             1008.00 EUR
  24 Element       fensterreinigung_element       165.60 EUR
  1 pauschal       anfahrt                         45.00 EUR

  Netto                                       1218.60 EUR
  zzgl. 19 % MwSt.                             231.53 EUR
  Brutto                                      1450.13 EUR

freigabe_token: frg_uG6AsbNllcRtJnaS
hinweis: Es wurde nichts gespeichert. Lege die Vorschau dem Menschen vor und
         rufe erst nach ausdrücklicher Bestätigung 'betrieb_commit_angebot' auf.

Bestätigen — und ein zweites Mal bestätigen:

$ betrieb_commit_angebot  freigabe_token=frg_uG6AsbNllcRtJnaS
{ "angebot_id": "ANG-2026-5044", "status": "entwurf", "brutto": "1450.13" }

$ betrieb_commit_angebot  freigabe_token=frg_uG6AsbNllcRtJnaS
{ "fehler": "Unbekanntes oder bereits eingelöstes Freigabe-Token. ..." }

Der zweite Aufruf ist der interessante: Ein wiederholter Werkzeugaufruf — durch Neuversuch, Kontextverlust oder schlicht Verwirrung des Modells — erzeugt kein zweites Angebot.


Entscheidungen, die bewusst so getroffen sind

Fachlogik ohne MCP-Abhängigkeit. core.py kennt weder Protokoll noch Tools. server.py ist eine dünne Schicht aus Schemas, Freigaben und Fehlerübersetzung. Deshalb laufen die Tests ohne Server, und dieselbe Logik wäre hinter einer REST- API oder einem CLI wiederverwendbar.

Geld als Decimal, nie als float. 0.1 + 0.2 ist in einem Angebot kein akademisches Problem. Gerundet wird kaufmännisch (ROUND_HALF_UP) und nur an einer Stelle.

Fehler sind Antworten, keine Exceptions. Jedes Werkzeug gibt bei fachlichen Problemen {"fehler": "..."} zurück, und zwar mit dem konkreten nächsten Schritt („Nutze betrieb_freie_slots…"). Ein Stacktrace beendet den Agentenlauf, ohne dass jemand klüger geworden ist.

Annotationen ehrlich gesetzt. prepare ist readOnlyHint: true, aber idempotentHint: false — es schreibt nichts, erzeugt aber bei jedem Aufruf ein neues Token. commit ist readOnlyHint: false. Wer diese Hinweise falsch setzt, nimmt Clients die Möglichkeit, sinnvoll nachzufragen.

SQLite ohne ORM. Das Repository soll ohne Fremdabhängigkeit und ohne Migrationsschritt starten. Für fünf Tabellen ist eine ORM-Schicht Ballast.


Start

git clone https://github.com/LucaT02/betrieb-mcp
cd betrieb-mcp

python3 -m unittest discover -s tests     # 22 Tests, keine Abhängigkeiten nötig

pip install -e .                          # nur für den Serverbetrieb
python -m betrieb_mcp.seed                # Demo-Datenbank anlegen
python -m betrieb_mcp.server              # Server über stdio starten

In Claude Desktop oder Claude Code eintragen:

{
  "mcpServers": {
    "betrieb": {
      "command": "python",
      "args": ["-m", "betrieb_mcp.server"],
      "env": { "BETRIEB_DB": "/absoluter/pfad/betrieb.db" }
    }
  }
}

Tests

22 Tests, Schwerpunkt auf den Stellen, an denen es weh tut:

  • Kalkulation — Summen, kaufmännische Rundung, unbekannte Leistung, Menge 0
  • Freie Fenster — Lücke zwischen Terminen, Randfenster vor Feierabend, kein Platz
  • Freigabe-Schranke — Vorschau schreibt nichts; Token schreibt genau einmal; Token nicht zwischen Aktionen übertragbar; abgelaufenes und erfundenes Token
  • Terminkonflikt — Überlappung, Ende nach Feierabend, und der Fall, für den die zweite Prüfung existiert: ein Slot, der zwischen Vorschau und Bestätigung belegt wurde

Was fehlt

Ehrlich benannt, statt so zu tun, als wäre das produktionsreif:

  • Kein Mehrbenutzerbetrieb. Der Freigabespeicher liegt im Prozess. Für mehrere Server-Instanzen müsste er nach Redis oder in die Datenbank wandern.
  • Keine Authentifizierung. stdio-Transport, ein Betrieb, ein Prozess. Für einen entfernten Server bräuchte es Streamable HTTP samt Auth.
  • Kein Audit-Log. Wer wann was freigegeben hat, wird nicht protokolliert — für einen echten Einsatz die erste Ergänzung, die ich bauen würde.
  • Die Preisliste ist hart verdrahtet. In einer echten Installation käme sie aus der Warenwirtschaft.

Lizenz

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选