Skip to main content
Glama
ivantagesam

OpsBridge MCP

by ivantagesam

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_tickets und create_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.js als 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| Audit
src/
  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 transport

Die 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

search_customers

lesen

Kunden nach Name oder E-Mail finden (teilweise, Groß-/Kleinschreibung wird ignoriert)

get_customer

lesen

Details eines Kunden anhand der ID abrufen

list_customer_tickets

lesen

Tickets eines Kunden auflisten, optional nach Status gefiltert

create_support_ticket

schreiben

Ein neues Ticket erstellen – erfordert explizites approved: true

Basiert auf SQLite mit synthetischen, fiktiven Daten: 10 Kunden, 18 vorbefüllte Support-Tickets.

Technologie-Stack

Schicht

Wahl

Warum

Sprache

TypeScript, strict mode + noUncheckedIndexedAccess / exactOptionalPropertyTypes

Fängt echte Fehler an den Schichtgrenzen ab, die dieses Projekt betreffen (optionale Felder, indizierter Zugriff)

MCP SDK

@modelcontextprotocol/sdk 1.30.0

Aktuelle veröffentlichte Hauptversion – es gibt zum Zeitpunkt der Erstellung kein v2; anhand der eigenen .d.ts-Dateien des installierten Pakets verifiziert, nicht anhand von Tutorials

Schema-Validierung

zod ^4

Einzige Quelle der Wahrheit sowohl für die Laufzeitvalidierung als auch für das JSON-Schema, das an Clients gesendet wird

Datenbank

better-sqlite3 ^12 (synchron)

Keine Async-Treiber-/Pool-Komplexität für einen lokalen Einzelprozess-Server; ^12, nicht das neuere 13.x, weil 13.x Node 22+ erfordert und dieses Projekt auf Node 20+ abzielt

Laufzeit

Node.js 20+

Angegebene Projekt-Basisversion

Tests

vitest ^4

Verbindet einen echten MCP-Client über InMemoryTransport mit einem echten McpServer – siehe Testen

Lint

eslint ^10 + typescript-eslint ^8

typescript-eslint unterstützt TypeScript 7 (den neuen Go-basierten Compiler) noch nicht, daher ist TypeScript auf die 5.9.x-Linie festgelegt – eine bewusste Kompatibilitätsentscheidung, kein Versehen

Dev-Runner

tsx

Führt src/index.ts während der Entwicklung direkt ohne Build-Schritt aus

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:

  1. 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.

  2. approved ist 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; bei false wird 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 einen withAudit()-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. Das LIKE-Muster von search_customers maskiert 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/description sind Freitext, und ein jetzt erstelltes Ticket wird von einem späteren list_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 .gitignore ausgeschlossen 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_customer auf und fragt dich dann entweder um Bestätigung, bevor es create_support_ticket aufruft, oder ruft es einmal mit approved false/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 mit approved: true aufruft.

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.js

Der 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 - ✔ Connected

Verbinden 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   # headless

Testen

npm test        # vitest — 33 tests across 6 files
npm run typecheck
npm run lint

Die 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 EXISTS hinaus.

  • 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_customers und list_customer_tickets hinzufü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.

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all related MCP servers

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.

View all MCP Connectors

Latest Blog Posts

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