Skip to main content
Glama
ivantagesam

OpsBridge MCP

by ivantagesam

OpsBridge MCP

AI 클라이언트에게 비즈니스의 고객 및 지원 티켓 데이터에 대한 통제되고 감사 가능한 접근을 제공하는 Model Context Protocol(MCP) 서버입니다. 프롬프트 지시가 아닌 서버가 강제하는 승인 확인에 의해 게이트되는 실제 쓰기 작업 하나를 포함합니다.

이것은 집중된 기술 데모이지 제품이 아닙니다. 한 가지를 제대로 보여 주기 위해 만든 포트폴리오 작품입니다: TypeScript로 올바르게 구현된 MCP 서버, 그리고 단순히 작동하는 데모와 실제로 LLM을 안전하게 가리킬 수 있는 데모를 구분하는 구체적인 엔지니어링 규율 — 스키마 검증, 매개변수화된 SQL, 애플리케이션 코드에서 강제되는 승인 게이트, 감사 추적 등이 가정이 아니라 실제 SDK와 실제 프로토콜에 대해 검증되었습니다. 이 프로젝트는 어디에도 배포되어 있지 않고, 실제 고객이 없으며, 프로덕션 준비가 되었다고 주장하지 않습니다 — 정확한 경계는 LimitationsWhat I'd change for production를 참고하세요.

이 프로젝트가 해결하는 문제

AI 클라이언트는 점점 질문에 답하는 것뿐만 아니라 실제 시스템에서 실제 작업을 수행할 것으로 기대됩니다. 이는 특정한 엔지니어링 문제를 만들어냅니다: 모델이 실시간 비즈니스 데이터를 읽고 중요한 작업을 수행하도록 허용하면서도, (a) 제한 없는 데이터베이스 액세스를 제공하거나 (b) "모델이 제안했다"와 "실제로 발생했다" 사이를 막는 유일한 경계가 프롬프트라고 믿는 것을 피하는 방법.

OpsBridge는 지원 티켓 시스템이라는 하나의 구체적인 사례에 대한 그 문제에 대한 작은 완전한 답입니다. AI 어시스턴트에게 필요한 정확한 데이터(고객, 티켓)와 어떤 것을 변경할 수 있는 정확히 한 가지 방법(티켓 생성)을 노출하며, 그 하나의 쓰기 경로는 호출자가 명시적으로 approved: true를 제공하지 않으면 실행될 수 없습니다. 모델이 어떻게 "결정"하는지와 무관하게 실행되는 서버 코드에서 이를 검사합니다. 프로젝트의 다른 모든 것 — 스키마, 오류 처리, 감사 로깅 — 은 그 하나의 보장이 실제로 신뢰할 수 있도록 존재합니다.

Related MCP server: SQLite MCP Server

이 아키텍처에서 MCP가 수행하는 역할

Model Context Protocol은 AI 클라이언트(Claude Code, Claude Desktop, MCP Inspector, 또는 MCP를 말할 줄 아는 것무엇이든)가 클라이언트별 사용자 정의 통합 코드 없이 이 서버를 검색하고 호출할 수 있게 해 주는 계층입니다. 구체적으로 이 프로젝트에서 MCP는 다음을 담당합니다:

  • 도구 검색 — 서버는 search_customers, get_customer, list_customer_tickets, create_support_ticket을 각각 JSON-Schema로 기술된 입력과 출력과 함께 광고하며, 이 스키마는 이 프로젝트의 Zod 스키마에서 자동으로 생성됩니다.

  • 구조화된 요청/응답 계약 — 모든 도구 호출은 이 프로젝트의 코드가 실행되기 전에 해당 스키마에 대해 검증되며, 모든 응답은 일반 결과 또는 잘 구성된 isError: true 결과이며, 원시 예외나 잘못 구성된 답변은 없습니다.

  • 트랜스포트 — stdio를 통한 JSON-RPC 2.0. 클라이언트는 node dist/index.js를 하위 프로세스로 실행하고 stdin/stdout을 통해 통신환입니까; 네트워크 포트가 없습니다.

MCP는 실제 작업을 전혀 수행하지 않습니다 — 그것은 일반적인 AI 클라이언트가 맞춤 접착제 코드 없이 이 서버를 사용할 수 있는 이유입니다. 비즈니스 로직, 검증 및 안전 보장은 이 프로젝트 자체의 것입니다.

아키텍처

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

계층화는 의도적이고 단방향적입니다: 각 계층은 아래에 있는 하나의 계층만 알고, domain/@modelcontextprotocol/sdk에서 아무것도 가져오지 않습니다 — better-sqlite3 데이터베이스를 대상으로 하는 구조적인 TypeScript입니다. 그렇기 때문에 테스트 스위트가 계층 경계를 모킹하는 대신 실제 도구 호출 경로(실제 MCP Client가 실제 McpServer와 통신)를 실행할 수 있는 것입니다. 정확한 코드 경로를 포함한 전체 설명: docs/architecture.md.

노출된 도구

도구

유형

목적

search_customers

읽기(read)

이름 또는 이메일로 고객 검색(부분 일치, 대소문자 무시)

get_customer

읽기(read)

id로 한 고객의 세부 정보 가져오기

list_customer_tickets

읽기(read)

고객의 티켓 목록, 선택적으로 상태로 필터링

create_support_ticket

쓰기(write)

새 티켓 생성 — 명시적인 approved: true 필요

SQLite와 합성된 가상 데이터로 지원됩니다: 고객 10명, 시드된 지원 티켓 18개.

기술 스택

계층

선택

언어

TypeScript, strict mode + noUncheckedIndexedAccess / exactOptionalPropertyTypes

이 프로젝트가 중시하는 계층 경계(선택적 필드, 인덱스 접근)에서 실제 버그를 잡아냅니다

MCP SDK

@modelcontextprotocol/sdk 1.30.0

현재 게시된 주요 버전 — 이 글을 쓰는 시점에 v2는 없습니다. 튜토리얼이 아닌 설치된 패키지의 자체 .d.ts 파일로 검증했습니다

스키마 검증

zod ^4

런타임 검증과 클라이언트에게 전송되는 JSON Schema의 단일 소스

데이터베이스

better-sqlite3 ^12(동기식)

단일 프로세스 로컬 서버에 대한 비동기 드라이버/풀 복잡성 없음; ^12가 아닌 13.x는 Node 22+가 필요하고 이 프로젝트는 Node 20+를 타겟팅하기 때문

런타임

Node.js 20+

프로젝트의 명시된 기본 기준

테스트

vitest ^4

실제 MCP ClientInMemoryTransport를 통해 실제 McpServer에 연결합니다 — 테스트 참조

린트

eslint ^10 + typescript-eslint ^8

typescript-eslint는 아직 TypeScript 7(새로운 Go 기반 컴파일러)을 지원하지 않으므로 TypeScript는 5.9.x 라인으로 고정됨 — 실수가 아니라 의도된 호환성 선택

개발 러너

tsx

개발 중에 빌드 단계 없이 src/index.ts를 직접 실행합니다

승인 메커니즘

create_support_ticket은 시스템에서 하나의 중요한 작업이기 때문에 이 프로젝트가 하드 게이트를 추가하는 유일한 곳입니다:

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

이것이 실제 강제 메커니즘이며 제안이 아닌 이유는 두 가지입니다:

  1. MCP 도구 계층 아래의 도메인 계층에서, SQL이 실행되기 전에 실행됩니다 — 도구 핸들러에서 데이터베이스 INSERT로 이어지는 경로로서 이를 건너뛰는 코드 경로가 없습니다.

  2. approved는 도구의 입력 스키마에서 필수 boolean이며 선택 사항이 아닙니다. 이를 빠뜨리면 이 코드가 실행조차 되기 전에 호출이 스키마 검증에 실패합니다. false를 전달하면 여기에서 거부됩니다.

도구 설명은 또한 모델에게 먼저 사용자와 확인할 것을 요청합니다 — 그러나 그것은 모델의 행동에 대한 권고사항일 뿐이며 시스템을 안전하게 만드는 것이 아닙니다. 모델이 설명을 무시하고 도구를 직접 호출해도 보장은 유지됩니다. main def line은 프롬프트가 아니라 서버입니다.

이것이 보장하지 않는 것: 실제 인간이 그 플래그를 설정했다는 것 — approved: true는 단지 모델이 스스로 제공할 수 있는 또 하나의 인수일 뿐이며, 어떤 인간이 요청을 보지도 않을 수 있습니다. 이 격차를 완전히 닫으려면 서버가 인간에게 대화형 확인 왕복 요청을 강제해야 합니다(MCP elicitation). 이 메커니즘은 이 프로젝트가 제공한다고 주장하지 않는 보장을 위한 실제 상호작용 모델 변경이므로 이 프로젝트는 의도적으로 추가하지 않습니다. Limitations를 참조하세요.

보안 고려 사항

  • 승인은 프롬프트가 아닌 애플리케이션 코드에서 강제됩니다 — 위를 참조하세요.

  • 모든 도구 호출은 감사 로그로 기록됩니다(stderr, src/lib/audit.ts, 네 도구 모두를 감싸는 withAudit() 래퍼로 도구 계층에서 적용): 도구 이름, 타임스탬프, 성공/실패, 비민감 식별자(해당하는 경우 customer_id). create_support_ticket 줄은 호출이 승인되었는지 여부도 기록합니다. 호출의 민감한 내용은 절대 기록하지 않습니다 — 티켓 제목/설명, 원시 검색 쿼리 텍스트, 이메일/전화/이름은 기록하지 않습니다.

  • 모든 SQL은 매개변수화됩니다 better-sqlite3의 준비 구문을 통해 — 문자열 결합이 없으므로 입력이 궁극적으로 LLM에서 유래해도 SQL 주입 표면이 없다습니다. search_customersLIKE 패턴도 %/_를 이스케이프하여 검색 텍스트가 리터럴로 일치하며 와일드카드로 일치하지 않습니다(그렇지 않으면 "%"만 쿼리하는 것만 줄의 모든 행을 반환했을 것).

  • 입력은 어떤 비즈니스 로직에 도달하기 전에 Zod로 검증됩니다. 길이 제한, priority/status에 대한 enum 제약 등 — 잘못된 형식의 입력을 통과시키는 대신 명확한 오류로 거부됩니다.

  • 저장된 티켓 텍스트는 지시가 아닌 데이터로 취급됩니다. subject/description은 자유 텍스트이며, 지금 만든 티켓은 나중에 list_customer_tickets 호출에 의해 그대로 읽힐 수 있으므로 2차적 prompt-injection 경로가 되는. 응답 텍스트는 이 내용이 지시가 아닌 저장된 고객 입력임을 명시적으로 지적합니다. 이 것은 완화책이지 보장이 아닙니다.

  • 인증 또는 권한 부여 없음. 이것은 로컬의 단일 사용자 데모입니다 — 프로세스를 시작할 수 있는 사람은 모든 도구에 대해 전체 액세스를 가지며 완전한 고객 개인 식별 정보(PII)를 포함합니다. 여기서 명시적으로 범위 밖이며 이 패턴이 실제 다중 테넌트 데이터를 다루기 전에 변해야 합니다.

  • 프로젝트 어디에서나 비밀 정보 없음. API 키, 토큰 또는 자격 증명이 없습니다. 유일한 외부 종속성은 로컬 SQLite 파일이며 gitignored입니다.

예시 Claude 상호작용

읽기 경로 프롬프트(연결 후):

  • "Chen이라는 고객을 검색해."

  • "고객 cust_004의 전체 정보를 가져와."

  • "cust_005의 열린 티켓이 무엇입니까?"

흥미로운 것은 쓰기 경로입니다:

하시는 분: "cust_002에 대해 추적 번호가 동기화되지 않는 경우에 대한 high-priority 지원 티켓을 생성해 — 하지만 실제로 생성하기 전에 나와 확인해줘."

기대하는 동작: 먹데이가 필요에 따라 search_customers/get_customer을 호출한 다음 create_support_ticket를 호출하기 전에 확인을 요청하거나, approved false/생략한 상태로 한 번 호출하여 거절을 받고는 제안된 티켓을 사용자에게 다시 나타냅니다. 어느 경우든 실제로 동의하고 모델이 approved: true로 다시 호출하기 전까지 아무것도 쓰여지지 않습니다.

더 많은 시나리오 워크스루 및 거부 경로를 직접 발동하여 실제 강제 메시지를 확인하는 방법: docs/demo-script.md.

로컬 설정

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

서버는 stdio를 통해 통신합니다 — HTTP 포트가 없으며 직접 브라우징할 것이 없습니다.

Claude Code 연결: 이 저장소에는 프로젝트 범위의 .mcp.json이 포함되어 있습니다(claude mcp add opsbridge --scope project -- node dist/index.js로 생성됨). 따라서 CLI가 직접 생성한 것과 정확히 동일하며, 수작업으로 작성된 것이 아닙니다. 먼저 빌드한 후 한 번 승인하세요:

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

다른 MCP 클라이언트 연결(Claude Desktop 등) — 대부분 command/args 쌍이 있는 JSON 구성을 읽습니다:

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

전체 클라이언트 없이 수동으로 확인 — MCP Inspector는 의도적으로 버전을 고정했습니다(버전이 지정되지 않은 npx @modelcontextprotocol/inspector는 현재 릴리스 대신 오래된 캐시된 빌드로 해석될 수 있음):

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

테스트

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

테스트는 실제 MCP Client를 SDK의 InMemoryTransport를 통해 실제 McpServer에 연결하며, 테스트별로 새로운 인메모리 SQLite 데이터베이스를 사용합니다(tests/helpers.ts) — 실제 클라이언트가 거치는 요청 → Zod 검증 → 도구 핸들러 → 응답 경로를 그대로 실행하며, 도메인 함수만 단독으로 테스트하는 것이 아닙니다. 적용 범위에는 다음이 포함됩니다: 성공 및 빈 결과 검색, 고객을 찾을 수 없음, 상태 필터 유무에 따른 티켓 목록, 모든 도구에 대한 잘못된 입력, approved: falseapproved를 완전히 생략한 경우 모두에서 거부되는 티켓 생성, 성공적인 생성, 중복 제출 안전성, LIKE 와일드카드 이스케이프, 프롬프트 인젝션 프레이밍 텍스트, 그리고 모든 도구에 대한 감사 로그 내용(PII가 로그 줄에 절대 나타나지 않는 것 포함).

제한 사항

집중된 데모를 위한 의도적인 범위 축소이며, 누락이 아닙니다:

  • 인증, 권한 부여, 사용자별 데이터 범위 지정 없음 — 보안 고려 사항 참조.

  • 승인 플래그는 검증된 인간 신호가 아님 — 모델이 스스로 설정할 수 있는 부울 값입니다. 승인 메커니즘 참조.

  • 페이지네이션 없음 — 검색은 10개 결과로 제한되며, 티켓 목록은 무제한이지만 데이터셋이 매우 작습니다.

  • 업데이트 또는 삭제 도구 없음 — 티켓 생성만 쓰기 작업입니다.

  • stdio 전송만 지원 — HTTP/SSE 없음, 원격 배포 방안 없음.

  • create_support_ticket에 속도 제한 또는 멱등성 키 없음 — 재시도된 호출은 중복 제거되지 않고 두 번째 독립 티켓을 생성합니다.

  • SQLite, 단일 프로세스 — 연결 풀링 없음, CREATE TABLE IF NOT EXISTS 외에 마이그레이션 도구 없음.

  • 감사 로그는 로컬 stderr 스트림 — 어디에도 전송되지 않으며, 쿼리 불가능하고, 보존 정책도 없습니다.

프로덕션을 위해 변경할 사항

이 패턴이 합성 데모 데이터 대신 실제 고객을 대상으로 사용된다면:

  • stdio에서 OAuth 베어러 인증이 있는 Streamable HTTP로 전환, 테넌트/고객별로 범위 지정 — SDK는 이미 이 전송을 지원합니다. 현재의 stdio 모델은 프로세스를 실행할 수 있는 사람을 암시적으로 신뢰하며, 이는 로컬 데모에서는 괜찮지만 그 외에는 적합하지 않습니다.

  • 실제 권한 부여 추가 — 인증된 호출자를 접근 가능한 고객/티켓에 매핑. 현재 모든 도구는 범위가 지정되지 않았습니다.

  • 승인을 단순 존재가 아닌 검증 가능하게 만들기 — MCP 일루시테이션(elicitation)을 사용하여 인간에게 실제 왕복 확인을 강제하거나, 모델의 통제 밖에 있는 별도의 확인 단계에서 발급된 단기 토큰을 요구합니다.

  • SQLite를 Postgres로 교체 — 풀링된 연결과 실제 마이그레이션 도구 사용.

  • 감사 로그를 내구성 있고 쿼리 가능한 곳으로 전송(stderr 아님) — 감사 대상에 적합한 보존 및 접근 제어 적용.

  • 쓰기 경로에 속도 제한 및 멱등성 키 추가.

  • search_customerslist_customer_tickets에 페이지네이션 추가.

  • 관측 가능성 추가 — 도구별 지연 시간, 오류율, 호출 볼륨.

  • 모든 변경 사항에 대해 CI에서 타입체크/테스트/린트 실행 — 로컬에서 필요할 때만이 아니라.

이 중 어느 것도 여기서 구현되지 않았습니다 — 이 프로젝트의 목적은 실제 배포에 필요하지만 데모에는 필요하지 않은 인프라를 미리 구축하는 것이 아니라, 패턴을 소규모로 올바르게 시연하는 것입니다.

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