나는 MCP를 통해 Notion에서 Incident War Rooms를 조립하는 에이전트를 만들었다
Source: Dev.to
Overview
Incident Runbook은 Notion을 살아있는 사고 대응 시스템으로 바꾸는 에이전트입니다. Incidents 데이터베이스를 감시하고, 새로운 SEV1 또는 SEV2가 나타나면 다른 세 개의 데이터베이스(Services, Runbooks, On‑Call)와 교차 참조하여 대응자가 필요한 모든 정보를 담은 War Room 페이지를 조립하고 Notion에 다시 씁니다. 사고가 해결되면 Gemini 2.5 Flash를 사용해 AI 사후 보고서를 생성합니다. 전체 과정은 Notion의 Model Context Protocol (MCP) 서버를 통해 이루어지며—REST 호출도, 웹훅도, 미들웨어 레이어도 없습니다.
The Problem
SEV1이 새벽 2시에 발생하면 엔지니어들은 다음과 같이 급하게 움직입니다:
- “Runbook은 어디에 있지?”
- “On‑call 담당자는 누구지?”
- “이 서비스에 의존하는 것이 뭐가 있지?”
그들은 흩어져 있는 문서에서 복사‑붙여넣기하고, Slack에 ping을 보내며 소중한 시간을 잃게 됩니다. 사고 대응은 자동화보다 부족한 트라이벌 지식에 의존하는 경우가 많습니다.
The Solution
Incident Runbook은 Notion 워크스페이스를 살아있는 사고 대응 시스템으로 전환합니다:
- 사고를 기록 → 몇 초 만에 전체 War Room이 자동으로 조립됩니다.
- 모든 상호작용은 MCP를 통해 이루어지며—데이터베이스 읽기, 페이지 조립, 사후 보고서 작성까지 직접 API 호출 없이 수행됩니다.
How It Works
Detect
Incidents 데이터베이스에서 새로운 SEV1/SEV2/SEV3 항목을 스캔합니다.
Lookup
- “Affected Service” 관계를 따라 Services 데이터베이스를 찾습니다.
- 연관된 Runbook을 Runbooks 데이터베이스에서 가져옵니다.
- 역할별로 필터링된 On‑Call 데이터베이스의 연락처를 추출합니다.
Assemble
Notion이 네이티브하게 렌더링하는 단일 Markdown 문자열 형태의 War Room 페이지를 생성합니다. 페이지에는 다음이 포함됩니다:
- 사고 상세 테이블
- On‑call 연락처 목록
- 전체 Runbook 단계
- 의존 서비스 목록
- 타임라인
- 체크박스가 있는 액션 체크리스트
페이지는 notion-create-pages 호출 하나로, 사고 페이지를 부모로 하여 생성됩니다.
Post‑mortem
사고가 해결 상태로 표시되면:
- 생성 시점부터 해결 시점까지의 MTTR을 계산합니다.
- 사고 상세와 타임라인을 Gemini 2.5 Flash에 전송합니다.
- AI가 생성한 사후 보고서를 새로운 Notion 페이지에 기록합니다.
MCP Call Pattern
에이전트는 한 번의 스캔으로 네 개의 Notion 데이터베이스를 조정합니다:
- Incidents 데이터베이스에서 새로운 항목이나 해결된 항목을 찾기 위해
notion-search를 실행합니다. - 각 사고에 대해 관계를 따라 Services, Runbooks, On‑Call을 조회합니다.
- 각 페이지에서 속성과 내용을 가져오기 위해
notion-fetch를 사용합니다.
예시: 1개의 사고, 3개의 서비스, 2개의 Runbook, 4개의 on‑call 연락처가 있을 경우 → 약 20개의 MCP 호출이 발생합니다. 각 호출이 빠르게 처리되므로 전체 스캔은 몇 초 안에 끝납니다.
Relation Handling
Notion MCP는 관계 속성을 페이지 URL 배열(예: ["https://www.notion.so/abc123def456"]) 형태로 반환하고, 단순 ID가 아닙니다. 에이전트는 URL을 파싱하고 대시(-)를 제거해 대시 없는 16진수 문자열로 정규화하여 데이터베이스 ID와 일치시킵니다.
Challenges & Debugging
- ID 불일치 – Services는 한 형식의 ID를 사용하고 사고 관계는 다른 형식을 사용했습니다. 모든 ID를 대시 없는 16진수 문자열로 정규화하면 문제가 해결되었습니다.
- 속성 파싱 – 속성이 XML 블록 안에 JSON 형태로 들어오며, 대소문자를 구분하는 키(
"status"vs."Status")가 달라질 수 있습니다. 해커톤에서는 정규식과 대소문자 체크로 충분했지만, 보다 견고한 해결책은 읽을 때 키를 정규화하는 것입니다.
Watch Mode
에이전트는 30초마다 폴링하는 watch mode로 동작합니다. 초기 버전에서는 매 폴링 주기마다 새로운 mcp-remote 프로세스를 생성해 시스템 자원을 급격히 소모했습니다. 해결책은 스캔 로직을 기존 MCP 클라이언트를 받아 재사용 가능한 함수로 옮겨, watch mode가 사이클 간에 단일 연결을 유지하도록 만든 것입니다.
Repository
Notion MCP Hackathon을 위해 제작 — 직접 API 호출 0회, MCP 100 % 활용.