OpsBridge MCP
OpsBridge MCP
Ein Model Context Protocol (MCP)-Server, der einem KI-Clienten kontrollierten, prüfbaren Zugriff auf Kunden- und Support-Ticket-Daten eines Unternehmens gewährt – einschließlich einer echten Schreibaktion, die durch eine serverseitig erzwungene Genehmigungsprüfung abgesichert ist und nicht durch eine Prompt-Anweisung.
Dies ist eine fokussierte technische Demonstration, kein Produkt. Es ist ein Portfolio-Stück, das dazu gebaut wurde, eine Sache gut zu zeigen: einen korrekt implementierten MCP-Server in TypeScript, mit der spezifischen technischen Disziplin, die eine Demo, die lediglich funktioniert, von einer unterscheidet, die tatsächlich sicher für ein LLM ist – Schema-Validierung, parametrisiertes SQL, ein in Anwendungscode erzwungenes Genehmigungs-Gate und ein Audit-Trail, alles gegen das echte SDK und das echte Protokoll verifiziert statt nur angenommen. Es ist nicht irgendwo bereitgestellt, hat keine echten Kunden und erhebt keinen Anspruch auf Produktionsreife – siehe Einschränkungen und Was ich für die Produktion ändern würde für die genaue Abgrenzung.
Welches Problem das löst
Von KI-Clienten wird zunehmend erwartet, dass sie echte Aktionen in echten Systemen ausführen und nicht nur Fragen beantworten. Das schafft ein spezifisches technisches Problem: Wie lässt man ein Modell Live-Geschäftsdaten lesen und eine folgenreiche Aktion ausführen, ohne (a) uneingeschränkten Datenbankzugriff zu gewähren oder (b) darauf zu vertrauen, dass der Prompt das Einzige ist, was zwischen „das Modell hat das vorgeschlagen" und „das ist tatsächlich passiert" steht?
OpsBridge ist eine kleine, vollständige Antwort auf dieses Problem für einen konkreten Fall: ein Support-Ticket-System. Es legt genau die Daten offen, die ein KI-Assistent benötigt (Kunden, Tickets), und genau eine Möglichkeit, etwas zu ändern (ein Ticket erstellen) – und dieser eine Schreibpfad kann nur ausgeführt werden, wenn der Aufrufer explizit approved: true angibt, was in Servercode geprüft wird, der unabhängig davon läuft, was das Modell „entscheidet". Alles andere im Projekt – Schemas, Fehlerbehandlung, Audit-Logging – existiert, um genau diese eine Zusage tatsächlich vertrauenswürdig zu machen.
Related MCP server: SQLite MCP Server
Was MCP in dieser Architektur tut
Das Model Context Protocol ist die Schicht, die es einem KI-Clienten (Claude Code, Claude Desktop, dem MCP Inspector oder allem anderen, das MCP spricht) ermöglicht, zu erkennen, was dieser Server kann, und ihn aufzurufen, ohne dass pro Client benutzerdefinierter Integrationscode erforderlich ist. Konkret ist MCP in diesem Projekt verantwortlich für:
Tool-Erkennung – der Server bewirbt
search_customers,get_customer,list_customer_ticketsundcreate_support_ticket, jeweils mit einer per JSON-Schema beschriebenen Ein- und Ausgabe, die automatisch aus den Zod-Schemas dieses Projekts generiert wird.Ein strukturierter Anfrage/Antwort-Vertrag – Jeder Tool-Aufruf wird gegen sein Schema validiert, bevor der Code dieses Projekts überhaupt ausgeführt wird, und jede Antwort ist entweder ein normales Ergebnis oder ein wohlgeformtes
isError: true-Ergebnis – niemals eine rohe Ausnahme oder eine fehlerhafte Antwort.Transport – JSON-RPC 2.0 über stdio. Der Client startet
node dist/index.jsals Unterprozess und kommuniziert über stdin/stdout mit ihm; es gibt keinen Netzwerkport.
MCP erledigt nicht die eigentliche Arbeit – es ist der Grund, warum ein generischer KI-Client diesen Server überhaupt ohne maßgeschneiderten Klebecode nutzen kann. Die Geschäftslogik, Validierung und Sicherheitsgarantien stammen aus diesem Projekt selbst.
Architektur
flowchart TD
Client["Claude Code / MCP Client"]
Protocol["MCP Protocol<br/>(JSON-RPC over stdio)"]
Server["OpsBridge MCP Server<br/>src/server.ts · src/index.ts"]
Tools["Tool Layer<br/>src/tools/*.ts"]
Approval["Approval / Validation<br/>src/domain/*.ts"]
DB[("SQLite Database<br/>src/db/*.ts")]
Audit["Audit Log (stderr)<br/>src/lib/audit.ts"]
Client --> Protocol --> Server --> Tools --> Approval --> DB
Tools -.->|every call, success or failure| Auditsrc/
db/ SQLite schema, synthetic seed data, idempotent seeding
domain/ Repository functions (customers, tickets) — plain TS, no MCP knowledge
tools/ One file per MCP tool: Zod schema, audit-log wrapper, thin handler
lib/ Audit logging (lib/audit.ts) and typed error classes (lib/errors.ts)
server.ts Builds the McpServer and registers all tools
index.ts Entrypoint — opens/seeds the DB, connects stdio transportDie Schichtung ist bewusst und einseitig: Jede Schicht kennt nur die darunterliegende, und domain/ importiert nichts aus @modelcontextprotocol/sdk – es ist schlichtes TypeScript, das auf einer better-sqlite3-Datenbank arbeitet. Dadurch kann die Testsuite den echten End-to-End-Tool-Aufrufpfad ausführen (ein echter MCP-Client, der mit einem echten McpServer spricht), anstatt die Schichtgrenzen zu mocken. Ausführliche Beschreibung inklusive exakter Codepfade: docs/architecture.md.
Verfügbare Tools
Tool | Typ | Zweck |
| lesen | Kunden nach Name oder E-Mail finden (teilweise, Groß-/Kleinschreibung wird ignoriert) |
| lesen | Details eines Kunden anhand der ID abrufen |
| lesen | Tickets eines Kunden auflisten, optional nach Status gefiltert |
| schreiben | Ein neues Ticket erstellen – erfordert explizites |
Basiert auf SQLite mit synthetischen, fiktiven Daten: 10 Kunden, 18 vorbefüllte Support-Tickets.
Technologie-Stack
Schicht | Wahl | Warum |
Sprache | TypeScript, strict mode + | Fängt echte Fehler an den Schichtgrenzen ab, die dieses Projekt betreffen (optionale Felder, indizierter Zugriff) |
MCP SDK |
| Aktuelle veröffentlichte Hauptversion – es gibt zum Zeitpunkt der Erstellung kein v2; anhand der eigenen |
Schema-Validierung |
| Einzige Quelle der Wahrheit sowohl für die Laufzeitvalidierung als auch für das JSON-Schema, das an Clients gesendet wird |
Datenbank |
| Keine Async-Treiber-/Pool-Komplexität für einen lokalen Einzelprozess-Server; |
Laufzeit | Node.js 20+ | Angegebene Projekt-Basisversion |
Tests |
| Verbindet einen echten MCP- |
Lint |
|
|
Dev-Runner |
| Führt |
Genehmigungsmechanismus
create_support_ticket ist die eine folgenreiche Aktion im System, daher ist es die eine Stelle, an der dieses Projekt ein hartes Tor einbaut:
// src/domain/tickets.ts
export function createSupportTicket(db, input: CreateTicketInput): Ticket {
if (input.approved !== true) {
throw new ApprovalRequiredError(
"Ticket creation was not approved. Set approved=true to confirm this action before it is created.",
);
}
// ... only reaches the INSERT after this point
}Zwei Dinge machen dies zu einem echten Durchsetzungsmechanismus und nicht nur zu einem Vorschlag:
Es läuft in der Domänenschicht unterhalb der MCP-Tool-Schicht, bevor irgendein SQL ausgeführt wird – es gibt keinen Codepfad vom Tool-Handler zum Datenbank-
INSERT, der es überspringt.approvedist ein erforderlicher boolescher Wert im Eingabeschema des Tools, nicht optional. Wenn man es weglässt, schlägt die Schema-Validierung fehl, bevor dieser Code überhaupt ausgeführt wird; beifalsewird es hier abgelehnt.
Die Tool-Beschreibung fordert das Modell außerdem auf, zuerst mit dem Benutzer zu bestätigen – aber das ist ein Hinweistext für das Verhalten des Modells, nicht das, was das System sicher macht. Die Garantie gilt auch dann, wenn ein Modell die Beschreibung ignoriert und das Tool direkt aufruft; der Server und nicht der Prompt ist die letzte Verteidigungslinie.
Was das nicht garantiert: Dass tatsächlich ein Mensch das Flag gesetzt hat – approved: true ist nur ein weiteres Argument, das ein Modell aus eigenem Antrieb liefern könnte, ohne dass ein Mensch die Anfrage je zu Gesicht bekommt. Um diese Lücke vollständig zu schließen, müsste der Server einen interaktiven Bestätigungs-Roundtrip an einen Menschen erzwingen (MCP-Elicitation); dieses Projekt fügt das bewusst nicht hinzu, da es eine echte Änderung des Interaktionsmodells für eine Garantie wäre, die dieses Projekt nicht beansprucht. Siehe Einschränkungen.
Sicherheitsüberlegungen
Die Genehmigung wird im Anwendungscode erzwungen, nicht im Prompt – siehe oben.
Jeder Tool-Aufruf wird zur Prüfung protokolliert (Audit-Log) nach stderr (
src/lib/audit.ts, angewendet auf der Tool-Ebene über einenwithAudit()-Wrapper um alle vier Tools): Tool-Name, Zeitstempel, Erfolg/Fehler und eine nicht sensible Kennung (customer_id, sofern zutreffend);create_support_ticket-Zeilen erfassen außerdem, ob der Aufruf genehmigt wurde. Niemals der sensible Inhalt eines Aufrufs – keine Ticket-Betreffe/-Beschreibungen, kein roher Suchtext, keine E-Mail/Telefon/Name.Das gesamte SQL ist parametrisiert über vorbereitete Anweisungen von
better-sqlite3– keine String-Verkettung, es gibt also keine SQL-Injection-Angriffsfläche, obwohl die Eingabe letztlich von einem LLM stammt. DasLIKE-Muster vonsearch_customersmaskiert außerdem%/_, sodass Suchtext wörtlich und nicht als Platzhalter abgeglichen wird (andernfalls würde eine Abfrage nur mit"%"jede Zeile zurückgeben).Eingaben werden mit Zod validiert, bevor sie eine Geschäftslogik erreichen – Längenbegrenzungen, Enum-Einschränkungen bei
priority/status– und fehlerhafte Eingaben werden mit einer klaren Fehlermeldung abgelehnt, anstatt durchgereicht zu werden.Gespeicherter Tickettext wird als Daten und nicht als Anweisungen behandelt.
subject/descriptionsind Freitext, und ein jetzt erstelltes Ticket wird von einem späterenlist_customer_tickets-Aufruf wörtlich zurückgelesen – ein Vektor für Prompt-Injection zweiter Ordnung. Der Antworttext weist ausdrücklich darauf hin, dass es sich bei diesem Inhalt um gespeicherte Kundeneingaben handelt, nicht um Anweisungen. Das ist eine Abschwächung, keine Garantie.Keine Authentifizierung oder Autorisierung. Dies ist eine lokale Demo für einen einzelnen Benutzer – jeder, der den Prozess starten kann, hat vollen Zugriff auf alle Tools, einschließlich vollständiger personenbezogener Kundendaten. Hier bewusst ausgeklammert; müsste geändert werden, bevor dieses Muster echte Multi-Tenant-Daten berührt.
Keine Geheimnisse irgendwo im Projekt. Keine API-Schlüssel, Tokens oder Anmeldedaten; die einzige externe Abhängigkeit ist die lokale SQLite-Datei, die in der
.gitignoreausgeschlossen ist.
Beispielhafte Claude-Interaktionen
Lese-Pfad-Aufforderungen nach dem Verbinden:
„Suche nach einem Kunden namens Chen."
„Rufe die vollständigen Details für Kunde cust_004 ab."
„Welche offenen Tickets hat cust_005?"
Der interessante Teil ist der Schreibpfad:
Du: „Erstelle ein Support-Ticket mit hoher Priorität für cust_002, weil deren Sendungsnummern nicht synchronisiert werden – aber frag mich, bevor du es tatsächlich erstellst."
Erwartetes Verhalten: Das Modell ruft bei Bedarf
search_customers/get_customerauf und fragt dich dann entweder um Bestätigung, bevor escreate_support_ticketaufruft, oder ruft es einmal mitapprovedfalse/weggelassen auf, erhält eine Ablehnung und legt dir das vorgeschlagene Ticket erneut vor. In jedem Fall wird nichts geschrieben, bis du tatsächlich zugestimmt hast und das Modell es erneut mitapproved: trueaufruft.
Weitere skriptartige Durchläufe, einschließlich des direkten Erzwingens des Ablehnungspfads, um die rohe Durchsetzungsmeldung zu sehen: docs/demo-script.md.
Lokale Einrichtung
Erfordert Node.js 20+.
npm install
npm run db:seed # creates and seeds data/opsbridge.db (10 customers, 18 tickets)
npm run build # compiles TypeScript to dist/npm run dev # runs src/index.ts directly with tsx (auto-seeds on first run)
# or, after `npm run build`:
npm start # runs dist/index.jsDer Server kommuniziert über stdio – kein HTTP-Port, nichts, was man direkt im Browser aufrufen kann.
Verbinden mit Claude Code: Dieses Repo enthält eine projektspezifische .mcp.json (generiert über
claude mcp add opsbridge --scope project -- node dist/index.js, also exakt das, was die CLI
selbst erzeugt, nicht handgeschrieben). Zuerst bauen, dann einmalig genehmigen:
npm run build
claude # prompts to trust this project's .mcp.json server on first run — approve it
claude mcp list # should show: opsbridge: node dist/index.js - ✔ ConnectedVerbinden mit einem anderen MCP-Client (Claude Desktop usw.) – die meisten lesen eine JSON-Konfiguration mit einem
command/args-Paar:
{
"mcpServers": {
"opsbridge": {
"command": "node",
"args": ["/absolute/path/to/opsbridge-mcp/dist/index.js"]
}
}
}Manuelles Ausprobieren ohne vollständigen Client – der MCP Inspector, Version bewusst festgepinnt
(ein unversioniertes npx @modelcontextprotocol/inspector kann zu einem veralteten gecachten Build auflösen
anstatt zur aktuellen Version):
npx @modelcontextprotocol/inspector@2.3.0 node dist/index.js # web UI
npx @modelcontextprotocol/inspector@2.3.0 --cli node dist/index.js -- --method tools/list # headlessTesten
npm test # vitest — 33 tests across 6 files
npm run typecheck
npm run lintDie Tests verbinden einen echten MCP-Client mit einem echten McpServer über den InMemoryTransport des SDKs,
unterlegt mit einer frischen In-Memory-SQLite-Datenbank pro Test (tests/helpers.ts) – sie üben also den tatsächlichen
Request → Zod-Validierung → Tool-Handler → Response-Pfad aus, den ein echter Client durchläuft, nicht nur die
Domänenfunktionen isoliert. Die Abdeckung umfasst: erfolgreiche Suche und Suche mit leerem Ergebnis, Kunde nicht
gefunden, Ticket-Auflistung mit/ohne Statusfilter, ungültige Eingaben bei jedem Tool, Ticket-Erstellung abgelehnt
sowohl mit approved: false als auch mit vollständig weggelassenem approved, erfolgreiche Erstellung,
Sicherheit gegen doppelte Einreichungen, LIKE-Wildcard-Escaping, Prompt-Injection-Rahmentext und
Audit-Log-Inhalte (einschließlich der Tatsache, dass PII niemals in einer Log-Zeile erscheint) für jedes Tool.
Einschränkungen
Bewusste Scope-Begrenzungen für eine fokussierte Demo, keine Versehen:
Keine Authentifizierung, Autorisierung oder benutzerbezogene Dateneingrenzung – siehe Sicherheitsüberlegungen.
Das Genehmigungs-Flag ist kein verifiziertes menschliches Signal – es ist ein Boolean, den ein Modell aus eigener Initiative setzen könnte; siehe Genehmigungsmechanismus.
Keine Paginierung – die Suche ist auf 10 Ergebnisse begrenzt; Ticket-Listen sind unbegrenzt, aber der Datensatz ist winzig.
Keine Update- oder Delete-Tools – nur die Ticket-Erstellung ist eine Schreibaktion.
Nur stdio-Transport – kein HTTP/SSE, keine Remote-Deployment-Geschichte.
Kein Rate Limiting oder Idempotenz-Schlüssel bei
create_support_ticket– ein wiederholter Aufruf erstellt ein zweites, unabhängiges Ticket, anstatt dedupliziert zu werden.SQLite, einzelner Prozess – kein Connection Pooling, keine Migrationswerkzeuge über
CREATE TABLE IF NOT EXISTShinaus.Audit-Log ist ein lokaler stderr-Stream – wird nirgendwohin übertragen, nicht abfragbar, keine Aufbewahrungsrichtlinie.
Was ich für die Produktion ändern würde
Falls dieses Muster jemals auf echte Kunden statt auf synthetische Demodaten ausgerichtet würde:
Weg vom stdio hin zu Streamable HTTP mit OAuth-Bearer-Auth, pro Mandant/Kunde abgegrenzt – das SDK unterstützt diesen Transport bereits; das heutige stdio-Modell vertraut implizit jedem, der den Prozess starten kann, was für eine lokale Demo in Ordnung ist und sonst nirgendwo.
Echte Autorisierung hinzufügen, die den authentifizierten Aufrufer darauf abbildet, welche Kunden/Tickets er berühren darf – jedes Tool ist derzeit ohne Abgrenzung.
Genehmigung verifizierbar machen, nicht nur vorhanden – MCP-Elicitation verwenden, um eine echte Round-Trip- Bestätigung an einen Menschen zu erzwingen, oder ein kurzlebiges Token verlangen, das von einem separaten Bestätigungsschritt außerhalb der Kontrolle des Modells ausgestellt wird.
SQLite durch Postgres ersetzen mit gepoolten Verbindungen und einem echten Migrationswerkzeug.
Das Audit-Log an einen dauerhaften und abfragbaren Ort bringen (nicht stderr) mit Aufbewahrungs- und Zugriffs- kontrollen, die dem entsprechen, was es protokolliert.
Rate Limiting und einen Idempotenz-Schlüssel auf dem Schreibpfad hinzufügen.
Paginierung zu
search_customersundlist_customer_ticketshinzufügen.Observability hinzufügen – Latenz, Fehlerrate und Aufrufvolumen pro Tool.
Typecheck/Test/Lint in CI bei jeder Änderung ausführen, nicht nur lokal bei Bedarf.
Nichts davon ist hier implementiert – der Zweck dieses Projekts ist es, das Muster korrekt in kleinem Maßstab zu demonstrieren, nicht vorab Infrastruktur zu bauen, die eine echte Bereitstellung bräuchte, eine Demo aber nicht.
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
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to perform CRUD operations on a SQLite database, with all tools protected by Google OAuth 2.0 authentication.
- AlicenseNot gradedqualityCmaintenanceEnables AI models to execute SQL queries against a SQLite database and receive results as JSON.72MIT
- FlicenseNot gradedqualityCmaintenanceA secure MCP server that exposes a SQLite database to AI agents with Role-Based Access Control, supporting authentication, customer/order/user management, and audit logging.
- FlicenseAqualityCmaintenanceEnables AI assistants like Cursor to manage customer support tickets in SQLite through MCP tools, supporting creation, retrieval, search, and updates via natural language.4
Related MCP Connectors
Runtime permission, approval, and audit layer for AI agent tool execution.
Deterministic compliance and vertical knowledge bases for autonomous agents. Free 24hr trial.
Pre-action allow/deny for AI agents. 24 statutes, 13 jurisdictions: EU AI Act, GDPR, DPDP.
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/ivantagesam/opsbridge-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server