Skip to main content
Glama
ivantagesam

OpsBridge MCP

by ivantagesam

OpsBridge MCP

Un servidor del Model Context Protocol (MCP) que proporciona a un cliente de IA un acceso controlado y auditable a los datos de clientes y tickets de soporte de un negocio — incluyendo una única acción de escritura real, protegida por una comprobación de aprobación impuesta por el servidor y no por una instrucción en el prompt.

Esto es una demostración técnica centrada, no un producto. Es una pieza de portafolio construida para mostrar una cosa bien hecha: un servidor MCP implementado correctamente en TypeScript, con la disciplina de ingeniería específica que separa una demo que meramente funciona de una que en realidad es segura para apuntarle un LLM — validación de esquemas, SQL parametrizado, una puerta de aprobación impuesta en código de aplicación, y un registro de auditoría, todo verificado contra el SDK real y el protocolo real en lugar de asumido. No está desplegado en ningún sitio, no tiene clientes reales, y no pretende estar listo para producción — ver Limitaciones y Qué cambiaría para producción para ver exactamente dónde está esa línea.

Qué problema resuelve esto

Cada vez se espera más que los clientes de IA realicen acciones reales sobre sistemas reales, no solo que respondan preguntas. Eso crea un problema de ingeniería concreto: ¿cómo dejas que un modelo lea datos empresariales en vivo y realice una acción de consecuencias, sin (a) darle acceso ilimitado a la base de datos, o (b) confiar en que el prompt sea lo único que se interpone entre "el modelo sugirió esto" y "esto realmente ocurrió"?

OpsBridge es una respuesta pequeña y completa a ese problema para un caso concreto: un sistema de tickets de soporte. Expone exactamente los datos que necesita un asistente de IA (clientes, tickets), y exactamente una forma de cambiar algo (crear un ticket) — y esa única ruta de escritura no puede ejecutarse a menos que el llamador suministre explícitamente approved: true, comprobado en código de servidor que se ejecuta independientemente de lo que el modelo "decida". Todo lo demás en el proyecto — esquemas, manejo de errores, registro de auditoría — existe para hacer que esa única garantía sea realmente digna de confianza.

Related MCP server: SQLite MCP Server

Qué está haciendo MCP en esta arquitectura

El Model Context Protocol es la capa que permite que un cliente de IA (Claude Code, Claude Desktop, el MCP Inspector, o cualquier otra cosa que hable MCP) descubra qué puede hacer este servidor y lo invoque, sin ningún código de integración personalizado por cliente. Concretamente, en este proyecto MCP es responsable de:

  • Descubrimiento de herramientas — el servidor anuncia search_customers, get_customer, list_customer_tickets y create_support_ticket, cada una con una entrada y salida descritas mediante JSON-Schema, generadas automáticamente a partir de los esquemas Zod de este proyecto.

  • Un contrato estructurado de petición/respuesta — cada llamada a una herramienta se valida contra su esquema antes de que el código de este proyecto se ejecute, y cada respuesta es o bien un resultado normal o bien un resultado bien formado con isError: true — nunca una excepción cruda ni una respuesta malformada.

  • Transporte — JSON-RPC 2.0 sobre stdio. El cliente lanza node dist/index.js como subproceso y se comunica con él a través de stdin/stdout; no hay puerto de red.

MCP no hace nada del trabajo real — es la razón por la que un cliente de IA genérico puede usar este servidor sin código de pegamento a medida. La lógica de negocio, la validación y las garantías de seguridad son propias de este proyecto.

Arquitectura

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

La estratificación es deliberada y unidireccional: cada capa solo conoce a la que tiene debajo, y domain/ no importa nada de @modelcontextprotocol/sdk — es TypeScript puro operando sobre una base de datos better-sqlite3. Eso es lo que permite que la suite de pruebas ejercite la ruta real de llamada a herramientas de extremo a extremo (un Client MCP real hablando con un McpServer real) en lugar de simular los límites entre capas. Documentación completa, incluyendo rutas de código exactas: docs/architecture.md.

Herramientas expuestas

Herramienta

Tipo

Propósito

search_customers

lectura

Buscar clientes por nombre o email (parcial, sin distinguir mayúsculas)

get_customer

lectura

Obtener los detalles de un cliente por id

list_customer_tickets

lectura

Listar los tickets de un cliente, opcionalmente filtrados por estado

create_support_ticket

escritura

Crear un nuevo ticket — requiere approved: true explícito

Respaldado por SQLite con datos sintéticos y ficticios: 10 clientes, 18 tickets de soporte sembrados.

Pila tecnológica

Capa

Elección

Por qué

Lenguaje

TypeScript, modo estricto + noUncheckedIndexedAccess / exactOptionalPropertyTypes

Detecta errores reales en los límites entre capas que le importan a este proyecto (campos opcionales, acceso indexado)

SDK de MCP

@modelcontextprotocol/sdk 1.30.0

Versión mayor publicada actual — no hay v2 al momento de escribir esto; verificado contra los archivos .d.ts propios del paquete instalado en lugar de tutoriales

Validación de esquemas

zod ^4

Fuente única de verdad tanto para la validación en tiempo de ejecución como para el JSON Schema enviado a los clientes

Base de datos

better-sqlite3 ^12 (síncrono)

Sin complejidad de driver/pool asíncrono para un servidor local de un solo proceso; ^12, no el más nuevo 13.x, porque 13.x requiere Node 22+ y este proyecto apunta a Node 20+

Entorno de ejecución

Node.js 20+

Línea base declarada del proyecto

Pruebas

vitest ^4

Conecta un Client MCP real a un McpServer real a través de InMemoryTransport — ver Pruebas

Lint

eslint ^10 + typescript-eslint ^8

typescript-eslint aún no soporta TypeScript 7 (el nuevo compilador basado en Go), así que TypeScript está fijado a la línea 5.9.x — una elección deliberada de compatibilidad, no un descuido

Ejecutor de desarrollo

tsx

Ejecuta src/index.ts directamente sin paso de compilación durante el desarrollo

Mecanismo de aprobación

create_support_ticket es la única acción de consecuencias en el sistema, así que es el único lugar donde este proyecto añade una puerta dura:

// 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
}

Dos cosas hacen que esto sea un mecanismo de imposición real en lugar de una sugerencia:

  1. Se ejecuta en la capa de dominio, por debajo de la capa de herramientas MCP, antes de que se ejecute cualquier SQL — no hay ninguna ruta de código desde el manejador de la herramienta hasta el INSERT en la base de datos que lo omita.

  2. approved es un booleano obligatorio en el esquema de entrada de la herramienta, no opcional. Si se omite, la llamada falla la validación del esquema antes de que este código siquiera se ejecute; si se pasa false, se rechaza aquí.

La descripción de la herramienta también pide al modelo que confirme primero con el usuario — pero eso es texto de asesoramiento para el comportamiento del modelo, no lo que hace seguro al sistema. La garantía se mantiene incluso si un modelo ignora la descripción y llama a la herramienta directamente; el servidor, no el prompt, es la última línea de defensa.

Lo que esto no garantiza: que un humano haya establecido realmente el flag — approved: true es solo otro argumento que un modelo podría suministrar por iniciativa propia, sin que ningún humano vea jamás la petición. Cerrar esa brecha por completo requeriría que el servidor forzara un ida y vuelta de confirmación interactiva hacia un humano (elicitation de MCP); este proyecto deliberadamente no añade eso, ya que es un cambio real en el modelo de interacción para una garantía que este proyecto no afirma proporcionar. Ver Limitaciones.

Consideraciones de seguridad

  • La aprobación se impone en código de aplicación, no en el prompt — ver arriba.

  • Cada llamada a una herramienta se registra en el registro de auditoría en stderr (src/lib/audit.ts, aplicado en la capa de herramientas mediante un envoltorio withAudit() alrededor de las cuatro herramientas): nombre de la herramienta, marca de tiempo, éxito/fallo, y un identificador no sensible (customer_id cuando corresponde); las líneas de create_support_ticket también registran si la llamada fue aprobada. Nunca el contenido sensible de una llamada — sin asuntos/descripciones de tickets, sin texto crudo de búsqueda, sin email/teléfono/nombre.

  • Todo el SQL está parametrizado mediante sentencias preparadas de better-sqlite3 — sin concatenación de cadenas, así que no hay superficie de inyección SQL aunque la entrada se origine en última instancia en un LLM. El patrón LIKE de search_customers también escapa %/_ para que el texto de búsqueda se compare literalmente, no como comodín (de lo contrario, una consulta de solo "%" devolvería todas las filas).

  • La entrada se valida con Zod antes de que llegue a cualquier lógica de negocio — límites de longitud, restricciones de enum en priority/status — rechazando entrada malformada con un error claro en lugar de dejarla pasar.

  • El texto de ticket almacenado se enmarca como datos, no como instrucciones. subject/description son texto libre, y un ticket creado ahora se lee de vuelta verbatim por una llamada posterior a list_customer_tickets — un vector de inyección de prompt de segundo orden. El texto de respuesta señala explícitamente que este contenido es entrada de cliente almacenada, no directivas. Esto es una mitigación, no una garantía.

  • Sin autenticación ni autorización. Esta es una demo local de un solo usuario — cualquiera que pueda lanzar el proceso tiene acceso completo a todas las herramientas, incluyendo PII completa de clientes. Explícitamente fuera de alcance aquí; tendría que cambiar antes de que este patrón tocara datos reales de múltiples inquilinos.

  • Sin secretos en ningún lugar del proyecto. Sin claves de API, tokens ni credenciales; la única dependencia externa es el archivo SQLite local, que está en gitignore.

Ejemplos de interacciones con Claude

Prompts de la ruta de lectura, una vez conectado:

  • "Busca un cliente llamado Chen."

  • "Obtén los detalles completos del cliente cust_004."

  • "¿Qué tickets abiertos tiene cust_005?"

El interesante es la ruta de escritura:

Tú: "Crea un ticket de soporte de prioridad alta para cust_002 sobre sus números de seguimiento que no se sincronizan — pero consúltame antes de crearlo realmente."

Comportamiento esperado: el modelo llama a search_customers/get_customer según sea necesario, y luego o bien te pide que confirmes antes de llamar a create_support_ticket, o bien lo llama una vez con approved en false/omitido, recibe el rechazo, y te devuelve el ticket propuesto. En cualquier caso, no se escribe nada hasta que realmente hayas aceptado y el modelo lo llame de nuevo con approved: true.

Más recorridos guionizados, incluyendo forzar la ruta de rechazo directamente para ver el mensaje crudo de imposición: docs/demo-script.md.

Configuración local

Requiere 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

El servidor se comunica a través de stdio — sin puerto HTTP, nada a lo que navegar directamente.

Conexión con Claude Code: este repositorio incluye un .mcp.json con alcance de proyecto (generado mediante claude mcp add opsbridge --scope project -- node dist/index.js, así que es exactamente lo que el propio CLI produce, no algo escrito a mano). Compila primero y luego aprueba una vez:

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

Conexión con cualquier otro cliente MCP (Claude Desktop, etc.) — la mayoría lee una configuración JSON con un par command/args:

{
  "mcpServers": {
    "opsbridge": {
      "command": "node",
      "args": ["/absolute/path/to/opsbridge-mcp/dist/index.js"]
    }
  }
}

Probarlo manualmente sin un cliente completo — el MCP Inspector, con la versión fijada deliberadamente (un npx @modelcontextprotocol/inspector sin versionar puede resolverse a una caché de compilación obsoleta en lugar de la versión actual):

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

Pruebas

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

Las pruebas conectan un Client MCP real a un McpServer real a través del InMemoryTransport del SDK, respaldado por una base de datos SQLite en memoria nueva por prueba (tests/helpers.ts) — ejercitando la ruta real de solicitud → validación Zod → manejador de herramientas → respuesta por la que pasa un cliente real, no solo las funciones de dominio de forma aislada. La cobertura incluye: búsqueda con resultados y con resultado vacío, cliente no encontrado, listado de tickets con y sin filtro de estado, entrada no válida en todas las herramientas, creación de tickets rechazada tanto con approved: false como con approved omitido por completo, creación exitosa, seguridad contra envíos duplicados, escape de comodines LIKE, texto de encuadre de inyección de prompts y contenido del registro de auditoría (incluyendo que la PII nunca aparece en una línea de registro) para cada herramienta.

Limitaciones

Recortes de alcance deliberados para una demo enfocada, no descuidos:

  • Sin autenticación, autorización ni delimitación de datos por usuario — ver consideraciones de Seguridad.

  • El indicador de aprobación no es una señal humana verificada — es un booleano que un modelo podría establecer por iniciativa propia; ver Mecanismo de aprobación.

  • Sin paginación — la búsqueda está limitada a 10 resultados; las listas de tickets no tienen límite, pero el conjunto de datos es pequeño.

  • Sin herramientas de actualización ni eliminación — solo la creación de tickets es una acción de escritura.

  • Solo transporte stdio — sin HTTP/SSE, sin historia de despliegue remoto.

  • Sin límite de velocidad ni clave de idempotencia en create_support_ticket — una llamada reintentada crea un segundo ticket independiente en lugar de deduplicarse.

  • SQLite, proceso único — sin agrupación de conexiones, sin herramientas de migración más allá de CREATE TABLE IF NOT EXISTS.

  • El registro de auditoría es un flujo stderr local — no se envía a ningún sitio, no es consultable, sin política de retención.

Qué cambiaría para producción

Si este patrón se aplicara alguna vez a clientes reales en lugar de datos de demostración sintéticos:

  • Pasar de stdio a HTTP Streamable con autenticación OAuth bearer, con alcance por inquilino/cliente — el SDK ya admite este transporte; el modelo actual de stdio confía implícitamente en quien pueda iniciar el proceso, lo cual está bien para una demo local y en ningún otro lugar.

  • Añadir autorización real que mapee al llamante autenticado con qué clientes/tickets puede tocar — actualmente todas las herramientas no tienen alcance.

  • Hacer que la aprobación sea verificable, no solo presente — usar la elicitación de MCP para forzar una confirmación real de ida y vuelta a un humano, o exigir un token de corta duración emitido por un paso de confirmación separado fuera del control del modelo.

  • Sustituir SQLite por Postgres con conexiones agrupadas y una herramienta de migración real.

  • Enviar el registro de auditoría a algún lugar duradero y consultable (no stderr) con retención y controles de acceso adecuados para lo que audita.

  • Añadir límite de velocidad y una clave de idempotencia en la ruta de escritura.

  • Añadir paginación a search_customers y list_customer_tickets.

  • Añadir observabilidad — latencia, tasa de errores y volumen de llamadas por herramienta.

  • Ejecutar typecheck/test/lint en CI en cada cambio, no solo localmente bajo demanda.

Nada de esto está implementado aquí — el objetivo de este proyecto es demostrar el patrón correctamente a pequeña escala, no preconstruir infraestructura que un despliegue real necesitaría pero una demo no.

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