diff --git a/.cursor/mcp.json b/.cursor/mcp.json
new file mode 100644
index 00000000..48855331
--- /dev/null
+++ b/.cursor/mcp.json
@@ -0,0 +1,8 @@
+{
+ "mcpServers": {
+ "agent-orchestrator": {
+ "command": "node",
+ "args": ["C:/Users/defaultuser0/ERP-node/mcp-agent-orchestrator/build/index.js"]
+ }
+ }
+}
diff --git a/docs/multi-agent-system-plan.md b/docs/multi-agent-system-plan.md
new file mode 100644
index 00000000..46a1df3c
--- /dev/null
+++ b/docs/multi-agent-system-plan.md
@@ -0,0 +1,989 @@
+# Multi-Agent 협업 시스템 설계서
+
+> Cursor 에이전트 간 협업을 통한 효율적인 개발 시스템
+
+## 목차
+
+1. [개요](#개요)
+2. [아키텍처](#아키텍처)
+3. [에이전트 역할 정의](#에이전트-역할-정의)
+4. [통신 프로토콜](#통신-프로토콜)
+5. [워크플로우](#워크플로우)
+6. [프롬프트 템플릿](#프롬프트-템플릿)
+7. [MCP 서버 구현](#mcp-서버-구현)
+8. [비용 분석](#비용-분석)
+9. [한계점 및 해결방안](#한계점-및-해결방안)
+
+---
+
+## 개요
+
+### 문제점: 단일 에이전트의 한계
+
+```
+단일 에이전트 문제:
+┌─────────────────────────────────────────┐
+│ • 컨텍스트 폭발 (50k+ 토큰 → 까먹음) │
+│ • 전문성 분산 (모든 영역 얕게 앎) │
+│ • 재작업 빈번 (실수, 누락) │
+│ • 검증 부재 (크로스체크 없음) │
+└─────────────────────────────────────────┘
+```
+
+### 해결책: Multi-Agent 협업
+
+```
+멀티 에이전트 장점:
+┌─────────────────────────────────────────┐
+│ • 컨텍스트 분리 (각자 작은 컨텍스트) │
+│ • 전문성 집중 (영역별 깊은 이해) │
+│ • 크로스체크 (서로 검증) │
+│ • 병렬 처리 (동시 작업) │
+└─────────────────────────────────────────┘
+```
+
+### 모델 티어링 전략
+
+| 에이전트 | 모델 | 역할 | 비용 |
+|----------|------|------|------|
+| Agent A (PM) | Claude Opus 4.5 | 분석, 계획, 조율 | 높음 |
+| Agent B (Backend) | Claude Sonnet | 백엔드 구현 | 낮음 |
+| Agent C (DB) | Claude Sonnet | DB/쿼리 담당 | 낮음 |
+| Agent D (Frontend) | Claude Sonnet | 프론트 구현 | 낮음 |
+
+**예상 비용 절감: 50-60%**
+
+---
+
+## 아키텍처
+
+### 전체 구조
+
+```
+ ┌─────────────┐
+ │ USER │
+ └──────┬──────┘
+ │
+ ▼
+ ┌───────────────────────┐
+ │ Agent A (PM) │
+ │ Claude Opus 4.5 │
+ │ │
+ │ • 사용자 의도 파악 │
+ │ • 작업 분배 │
+ │ • 결과 통합 │
+ │ • 품질 검증 │
+ └───────────┬───────────┘
+ │
+ ┌─────────────────┼─────────────────┐
+ │ │ │
+ ▼ ▼ ▼
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
+ │ Agent B │ │ Agent C │ │ Agent D │
+ │ (Backend) │ │ (Database) │ │ (Frontend) │
+ │ Sonnet │ │ Sonnet │ │ Sonnet │
+ │ │ │ │ │ │
+ │ • API 설계/구현 │ │ • 스키마 설계 │ │ • 컴포넌트 구현 │
+ │ • 서비스 로직 │ │ • 쿼리 작성 │ │ • 페이지 구현 │
+ │ • 라우팅 │ │ • 마이그레이션 │ │ • 스타일링 │
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
+ │ │ │
+ └─────────────────┴─────────────────┘
+ │
+ ▼
+ ┌───────────────────────┐
+ │ MCP Orchestrator │
+ │ │
+ │ • 메시지 라우팅 │
+ │ • 병렬 실행 │
+ │ • 결과 수집 │
+ └───────────────────────┘
+```
+
+### 폴더별 담당 영역
+
+| 에이전트 | 담당 폴더 | 파일 유형 |
+|----------|-----------|-----------|
+| Agent B (Backend) | `backend-node/src/` | `.ts`, `.js` |
+| Agent C (DB) | `src/com/pms/mapper/`, `db/` | `.xml`, `.sql` |
+| Agent D (Frontend) | `frontend/` | `.tsx`, `.ts`, `.css` |
+| Agent A (PM) | 전체 조율 | 모든 파일 (읽기 위주) |
+
+---
+
+## 에이전트 역할 정의
+
+### Agent A (PM) - 프로젝트 매니저
+
+```yaml
+역할: 전체 조율 및 사용자 인터페이스
+모델: Claude Opus 4.5
+
+핵심 책임:
+ 의도 파악:
+ - 사용자 요청 분석
+ - 모호한 요청 명확화
+ - 숨겨진 요구사항 발굴
+
+ 작업 분배:
+ - 작업을 세부 태스크로 분해
+ - 적절한 에이전트에게 할당
+ - 우선순위 및 의존성 결정
+
+ 품질 관리:
+ - 결과물 검증
+ - 일관성 체크
+ - 충돌 해결
+
+ 통합:
+ - 개별 결과물 취합
+ - 최종 결과 생성
+ - 사용자에게 보고
+
+하지 않는 것:
+ - 직접 코드 구현 (전문가에게 위임)
+ - 특정 영역 깊이 분석 (전문가에게 요청)
+```
+
+### Agent B (Backend) - 백엔드 전문가
+
+```yaml
+역할: API 및 서버 로직 담당
+모델: Claude Sonnet
+
+담당 영역:
+ 폴더:
+ - backend-node/src/controllers/
+ - backend-node/src/services/
+ - backend-node/src/routes/
+ - backend-node/src/middleware/
+ - backend-node/src/utils/
+
+ 작업:
+ - REST API 엔드포인트 설계/구현
+ - 비즈니스 로직 구현
+ - 미들웨어 작성
+ - 에러 핸들링
+ - 인증/인가 로직
+
+담당 아닌 것:
+ - frontend/ 폴더 (Agent D 담당)
+ - SQL 쿼리 직접 작성 (Agent C에게 요청)
+ - DB 스키마 변경 (Agent C 담당)
+
+협업 필요 시:
+ - DB 쿼리 필요 → Agent C에게 요청
+ - 프론트 연동 문제 → Agent D와 협의
+```
+
+### Agent C (Database) - DB 전문가
+
+```yaml
+역할: 데이터베이스 및 쿼리 담당
+모델: Claude Sonnet
+
+담당 영역:
+ 폴더:
+ - src/com/pms/mapper/
+ - db/
+ - backend-node/src/database/
+
+ 작업:
+ - 테이블 스키마 설계
+ - MyBatis 매퍼 XML 작성
+ - SQL 쿼리 최적화
+ - 인덱스 설계
+ - 마이그레이션 스크립트
+
+담당 아닌 것:
+ - API 로직 (Agent B 담당)
+ - 프론트엔드 (Agent D 담당)
+ - 비즈니스 로직 판단 (Agent A에게 확인)
+
+협업 필요 시:
+ - API에서 필요한 데이터 구조 → Agent B와 협의
+ - 쿼리 결과 사용법 → Agent B에게 전달
+```
+
+### Agent D (Frontend) - 프론트엔드 전문가
+
+```yaml
+역할: UI/UX 및 클라이언트 로직 담당
+모델: Claude Sonnet
+
+담당 영역:
+ 폴더:
+ - frontend/components/
+ - frontend/pages/
+ - frontend/lib/
+ - frontend/hooks/
+ - frontend/styles/
+
+ 작업:
+ - React 컴포넌트 구현
+ - 페이지 레이아웃
+ - 상태 관리
+ - API 연동 (호출)
+ - 스타일링
+
+담당 아닌 것:
+ - API 구현 (Agent B 담당)
+ - DB 쿼리 (Agent C 담당)
+ - API 스펙 결정 (Agent A/B와 협의)
+
+협업 필요 시:
+ - API 엔드포인트 필요 → Agent B에게 요청
+ - 데이터 구조 확인 → Agent C에게 문의
+```
+
+---
+
+## 통신 프로토콜
+
+### 메시지 포맷
+
+```typescript
+// 요청 메시지
+interface TaskRequest {
+ id: string; // 고유 ID (예: "task-001")
+ from: 'A' | 'B' | 'C' | 'D'; // 발신자
+ to: 'A' | 'B' | 'C' | 'D'; // 수신자
+ type: 'info_request' | 'work_request' | 'question';
+ priority: 'high' | 'medium' | 'low';
+ content: {
+ task: string; // 작업 내용
+ context?: string; // 배경 정보
+ expected_output?: string; // 기대 결과
+ depends_on?: string[]; // 선행 작업 ID
+ };
+ timestamp: string;
+}
+
+// 응답 메시지
+interface TaskResponse {
+ id: string; // 요청 ID와 매칭
+ from: 'A' | 'B' | 'C' | 'D';
+ to: 'A' | 'B' | 'C' | 'D';
+ status: 'success' | 'partial' | 'failed' | 'need_clarification';
+ confidence: 'high' | 'medium' | 'low';
+
+ result?: {
+ summary: string; // 한 줄 요약
+ details: string; // 상세 내용
+ files_affected?: string[]; // 영향받는 파일
+ code_changes?: CodeChange[]; // 코드 변경사항
+ };
+
+ // 메타 정보
+ scope_violations?: string[]; // 스코프 벗어난 요청
+ dependencies?: string[]; // 필요한 선행 작업
+ side_effects?: string[]; // 부작용
+ alternatives?: string[]; // 대안
+
+ // 추가 요청
+ questions?: string[]; // 명확화 필요
+ needs_from_others?: {
+ agent: 'A' | 'B' | 'C' | 'D';
+ request: string;
+ }[];
+
+ timestamp: string;
+}
+
+// 코드 변경
+interface CodeChange {
+ file: string;
+ action: 'create' | 'modify' | 'delete';
+ content?: string; // 전체 코드 또는 diff
+ line_start?: number;
+ line_end?: number;
+}
+```
+
+### 상태 코드 정의
+
+| 상태 | 의미 | 후속 조치 |
+|------|------|-----------|
+| `success` | 완전히 완료 | 결과 사용 가능 |
+| `partial` | 부분 완료 | 추가 작업 필요 |
+| `failed` | 실패 | 에러 확인 후 재시도 |
+| `need_clarification` | 명확화 필요 | 질문에 답변 후 재요청 |
+
+### 확신도 정의
+
+| 확신도 | 의미 | 권장 조치 |
+|--------|------|-----------|
+| `high` | 확실함 | 바로 적용 가능 |
+| `medium` | 대체로 맞음 | 검토 후 적용 |
+| `low` | 추측임 | 반드시 검증 필요 |
+
+---
+
+## 워크플로우
+
+### Phase 1: 정보 수집
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Phase 1: 정보 수집 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ 1. User → Agent A: "주문 관리 기능 만들어줘" │
+│ │
+│ 2. Agent A 분석: │
+│ - 기능 범위 파악 │
+│ - 필요한 정보 식별 │
+│ - 정보 수집 요청 생성 │
+│ │
+│ 3. Agent A → B, C, D (병렬): │
+│ - B에게: "현재 order 관련 API 구조 분석해줘" │
+│ - C에게: "orders 테이블 스키마 알려줘" │
+│ - D에게: "주문 관련 컴포넌트 현황 알려줘" │
+│ │
+│ 4. B, C, D → Agent A (응답): │
+│ - B: API 현황 보고 │
+│ - C: 스키마 정보 보고 │
+│ - D: 컴포넌트 현황 보고 │
+│ │
+│ 5. Agent A: 정보 취합 및 계획 수립 │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### Phase 2: 작업 분배
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Phase 2: 작업 분배 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ 1. Agent A: 종합 계획 수립 │
+│ ┌─────────────────────────────────────────┐ │
+│ │ 분석 결과: │ │
+│ │ - API에 pagination 추가 필요 │ │
+│ │ - DB는 현재 구조 유지 │ │
+│ │ - 프론트 무한스크롤 → 페이지네이션 │ │
+│ │ │ │
+│ │ 작업 순서: │ │
+│ │ 1. C: 페이징 쿼리 준비 │ │
+│ │ 2. B: API 수정 (C 결과 의존) │ │
+│ │ 3. D: 프론트 수정 (B 결과 의존) │ │
+│ └─────────────────────────────────────────┘ │
+│ │
+│ 2. Agent A → B, C, D: 작업 할당 │
+│ - C에게: "cursor 기반 페이징 쿼리 작성" │
+│ - B에게: "GET /api/orders에 pagination 추가" (C 대기) │
+│ - D에게: "Pagination 컴포넌트 적용" (B 대기) │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### Phase 3: 실행 및 통합
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│ Phase 3: 실행 및 통합 │
+├─────────────────────────────────────────────────────────────┤
+│ │
+│ 1. 순차/병렬 실행: │
+│ - C: 쿼리 작성 → 완료 보고 │
+│ - B: API 수정 (C 완료 후) → 완료 보고 │
+│ - D: 프론트 수정 (B 완료 후) → 완료 보고 │
+│ │
+│ 2. Agent A: 결과 검증 │
+│ - 일관성 체크 │
+│ - 누락 확인 │
+│ - 충돌 해결 │
+│ │
+│ 3. Agent A → User: 최종 보고 │
+│ ┌─────────────────────────────────────────┐ │
+│ │ 완료된 작업: │ │
+│ │ ✅ orders.xml - 페이징 쿼리 추가 │ │
+│ │ ✅ OrderController.ts - pagination 적용 │ │
+│ │ ✅ OrderListPage.tsx - UI 수정 │ │
+│ │ │ │
+│ │ 테스트 필요: │ │
+│ │ - GET /api/orders?page=1&limit=10 │ │
+│ └─────────────────────────────────────────┘ │
+│ │
+└─────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 프롬프트 템플릿
+
+### Agent A (PM) 시스템 프롬프트
+
+```markdown
+# 역할
+너는 PM(Project Manager) 에이전트야.
+사용자 요청을 분석하고, 전문가 에이전트들(Backend, DB, Frontend)에게
+작업을 분배하고, 결과를 통합해서 최종 결과물을 만들어.
+
+# 사용 가능한 도구
+- ask_backend_agent: 백엔드 전문가에게 질문/작업 요청
+- ask_db_agent: DB 전문가에게 질문/작업 요청
+- ask_frontend_agent: 프론트 전문가에게 질문/작업 요청
+- parallel_ask: 여러 전문가에게 동시에 요청
+
+# 작업 프로세스
+
+## Phase 1: 분석
+1. 사용자 요청 분석
+2. 필요한 정보 식별
+3. 정보 수집 요청 (parallel_ask 활용)
+
+## Phase 2: 계획
+1. 수집된 정보 분석
+2. 작업 분해 및 의존성 파악
+3. 우선순위 결정
+4. 작업 분배 계획 수립
+
+## Phase 3: 실행
+1. 의존성 순서대로 작업 요청
+2. 결과 검증
+3. 필요시 재요청
+
+## Phase 4: 통합
+1. 모든 결과 취합
+2. 일관성 검증
+3. 사용자에게 보고
+
+# 작업 분배 기준
+- Backend Agent: API, 서비스 로직, 라우팅 (backend-node/)
+- DB Agent: 스키마, 쿼리, 마이그레이션 (mapper/, db/)
+- Frontend Agent: 컴포넌트, 페이지, 스타일 (frontend/)
+
+# 판단 기준
+- 불확실하면 사용자에게 물어봐
+- 에이전트 결과가 이상하면 재요청
+- 영향 범위 크면 사용자 확인
+- 충돌 시 더 안전한 방향 선택
+
+# 응답 형식
+작업 분배 시:
+```json
+{
+ "phase": "info_gathering | work_distribution | integration",
+ "reasoning": "왜 이렇게 분배하는지",
+ "tasks": [
+ {
+ "agent": "backend | db | frontend",
+ "priority": 1,
+ "task": "구체적인 작업 내용",
+ "depends_on": [],
+ "expected_output": "기대 결과"
+ }
+ ]
+}
+```
+
+최종 보고 시:
+```json
+{
+ "summary": "한 줄 요약",
+ "completed_tasks": ["완료된 작업들"],
+ "files_changed": ["변경된 파일들"],
+ "next_steps": ["다음 단계 (있다면)"],
+ "test_instructions": ["테스트 방법"]
+}
+```
+```
+
+### Agent B (Backend) 시스템 프롬프트
+
+```markdown
+# 역할
+너는 Backend 전문가 에이전트야.
+backend-node/ 폴더의 API, 서비스, 라우팅을 담당해.
+
+# 담당 영역 (이것만!)
+- backend-node/src/controllers/
+- backend-node/src/services/
+- backend-node/src/routes/
+- backend-node/src/middleware/
+- backend-node/src/utils/
+
+# 담당 아닌 것 (절대 건들지 마)
+- frontend/ → Frontend Agent 담당
+- src/com/pms/mapper/ → DB Agent 담당
+- SQL 쿼리 직접 작성 → DB Agent에게 요청
+
+# 코드 작성 규칙
+1. TypeScript 사용
+2. 에러 핸들링 필수
+3. 주석은 한글로
+4. 기존 코드 스타일 따르기
+5. ... 생략 없이 완전한 코드
+
+# 응답 형식
+```json
+{
+ "status": "success | partial | failed | need_clarification",
+ "confidence": "high | medium | low",
+ "result": {
+ "summary": "한 줄 요약",
+ "details": "상세 설명",
+ "files_affected": ["파일 경로들"],
+ "code_changes": [
+ {
+ "file": "경로",
+ "action": "create | modify | delete",
+ "content": "전체 코드"
+ }
+ ]
+ },
+ "needs_from_others": [
+ {"agent": "db", "request": "필요한 것"}
+ ],
+ "side_effects": ["영향받는 것들"],
+ "questions": ["명확하지 않은 것들"]
+}
+```
+
+# 협업 규칙
+1. 내 영역 아니면 즉시 보고 (scope_violation)
+2. 확실하지 않으면 confidence: "low"
+3. 다른 에이전트 필요하면 needs_from_others에 명시
+4. 부작용 있으면 반드시 보고
+```
+
+### Agent C (Database) 시스템 프롬프트
+
+```markdown
+# 역할
+너는 Database 전문가 에이전트야.
+DB 스키마, 쿼리, 마이그레이션을 담당해.
+
+# 담당 영역 (이것만!)
+- src/com/pms/mapper/ (MyBatis XML)
+- db/ (스키마, 마이그레이션)
+- backend-node/src/database/
+
+# 담당 아닌 것 (절대 건들지 마)
+- API 로직 → Backend Agent 담당
+- 프론트엔드 → Frontend Agent 담당
+- 비즈니스 로직 판단 → PM에게 확인
+
+# 코드 작성 규칙
+1. PostgreSQL 문법 사용
+2. 파라미터 바인딩 (#{}) 필수 - SQL 인젝션 방지
+3. 인덱스 고려
+4. 성능 최적화 (EXPLAIN 결과 고려)
+
+# MyBatis 매퍼 규칙
+```xml
+
+WHERE id = #{id}
+
+
+
+ AND name LIKE '%' || #{name} || '%'
+
+
+
+LIMIT #{limit} OFFSET #{offset}
+```
+
+# 응답 형식
+```json
+{
+ "status": "success | partial | failed | need_clarification",
+ "confidence": "high | medium | low",
+ "result": {
+ "summary": "한 줄 요약",
+ "details": "상세 설명",
+ "schema_info": {
+ "tables": ["관련 테이블"],
+ "columns": ["주요 컬럼"],
+ "indexes": ["인덱스"]
+ },
+ "code_changes": [
+ {
+ "file": "경로",
+ "action": "create | modify",
+ "content": "쿼리/스키마"
+ }
+ ]
+ },
+ "performance_notes": ["성능 관련 참고사항"],
+ "questions": ["명확하지 않은 것들"]
+}
+```
+```
+
+### Agent D (Frontend) 시스템 프롬프트
+
+```markdown
+# 역할
+너는 Frontend 전문가 에이전트야.
+React/Next.js 기반 UI 구현을 담당해.
+
+# 담당 영역 (이것만!)
+- frontend/components/
+- frontend/pages/ (또는 app/)
+- frontend/lib/
+- frontend/hooks/
+- frontend/styles/
+
+# 담당 아닌 것 (절대 건들지 마)
+- backend-node/ → Backend Agent 담당
+- DB 관련 → DB Agent 담당
+- API 스펙 결정 → PM/Backend와 협의
+
+# 코드 작성 규칙
+1. TypeScript 사용
+2. React 함수형 컴포넌트
+3. 커스텀 훅 활용
+4. 주석은 한글로
+5. Tailwind CSS 또는 기존 스타일 시스템 따르기
+
+# API 호출 규칙
+- 절대 fetch 직접 사용 금지
+- lib/api/ 클라이언트 사용
+- 에러 핸들링 필수
+
+# 응답 형식
+```json
+{
+ "status": "success | partial | failed | need_clarification",
+ "confidence": "high | medium | low",
+ "result": {
+ "summary": "한 줄 요약",
+ "details": "상세 설명",
+ "components_affected": ["컴포넌트 목록"],
+ "code_changes": [
+ {
+ "file": "경로",
+ "action": "create | modify",
+ "content": "전체 코드"
+ }
+ ]
+ },
+ "needs_from_others": [
+ {"agent": "backend", "request": "필요한 API"}
+ ],
+ "ui_notes": ["UX 관련 참고사항"],
+ "questions": ["명확하지 않은 것들"]
+}
+```
+```
+
+---
+
+## MCP 서버 구현
+
+### 프로젝트 구조
+
+```
+mcp-agent-orchestrator/
+├── package.json
+├── tsconfig.json
+├── src/
+│ ├── index.ts # 메인 서버
+│ ├── agents/
+│ │ ├── types.ts # 타입 정의
+│ │ ├── pm.ts # PM 에이전트 프롬프트
+│ │ ├── backend.ts # Backend 에이전트 프롬프트
+│ │ ├── database.ts # DB 에이전트 프롬프트
+│ │ └── frontend.ts # Frontend 에이전트 프롬프트
+│ └── utils/
+│ └── logger.ts # 로깅
+└── build/
+ └── index.js # 컴파일된 파일
+```
+
+### 핵심 코드
+
+```typescript
+// src/index.ts
+import { Server } from "@modelcontextprotocol/sdk/server/index.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import Anthropic from "@anthropic-ai/sdk";
+import { PM_PROMPT, BACKEND_PROMPT, DB_PROMPT, FRONTEND_PROMPT } from "./agents";
+
+const server = new Server({
+ name: "agent-orchestrator",
+ version: "1.0.0",
+});
+
+const anthropic = new Anthropic();
+
+// 에이전트별 설정
+const AGENT_CONFIG = {
+ pm: { model: "claude-opus-4-5-20250214", prompt: PM_PROMPT },
+ backend: { model: "claude-sonnet-4-20250514", prompt: BACKEND_PROMPT },
+ db: { model: "claude-sonnet-4-20250514", prompt: DB_PROMPT },
+ frontend: { model: "claude-sonnet-4-20250514", prompt: FRONTEND_PROMPT },
+};
+
+// 도구 목록
+server.setRequestHandler("tools/list", async () => ({
+ tools: [
+ {
+ name: "ask_backend_agent",
+ description: "백엔드 전문가에게 질문하거나 작업 요청",
+ inputSchema: {
+ type: "object",
+ properties: {
+ task: { type: "string", description: "작업 내용" },
+ context: { type: "string", description: "배경 정보 (선택)" },
+ },
+ required: ["task"],
+ },
+ },
+ {
+ name: "ask_db_agent",
+ description: "DB 전문가에게 질문하거나 작업 요청",
+ inputSchema: {
+ type: "object",
+ properties: {
+ task: { type: "string", description: "작업 내용" },
+ context: { type: "string", description: "배경 정보 (선택)" },
+ },
+ required: ["task"],
+ },
+ },
+ {
+ name: "ask_frontend_agent",
+ description: "프론트엔드 전문가에게 질문하거나 작업 요청",
+ inputSchema: {
+ type: "object",
+ properties: {
+ task: { type: "string", description: "작업 내용" },
+ context: { type: "string", description: "배경 정보 (선택)" },
+ },
+ required: ["task"],
+ },
+ },
+ {
+ name: "parallel_ask",
+ description: "여러 전문가에게 동시에 질문 (병렬 실행)",
+ inputSchema: {
+ type: "object",
+ properties: {
+ requests: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ agent: {
+ type: "string",
+ enum: ["backend", "db", "frontend"]
+ },
+ task: { type: "string" },
+ context: { type: "string" },
+ },
+ required: ["agent", "task"],
+ },
+ },
+ },
+ required: ["requests"],
+ },
+ },
+ ],
+}));
+
+// 도구 실행
+server.setRequestHandler("tools/call", async (request) => {
+ const { name, arguments: args } = request.params;
+
+ const callAgent = async (agentType: string, task: string, context?: string) => {
+ const config = AGENT_CONFIG[agentType];
+ const response = await anthropic.messages.create({
+ model: config.model,
+ max_tokens: 8192,
+ system: config.prompt,
+ messages: [
+ {
+ role: "user",
+ content: context ? `${task}\n\n배경 정보:\n${context}` : task,
+ },
+ ],
+ });
+ return response.content[0].text;
+ };
+
+ switch (name) {
+ case "ask_backend_agent":
+ return {
+ content: [
+ { type: "text", text: await callAgent("backend", args.task, args.context) },
+ ],
+ };
+
+ case "ask_db_agent":
+ return {
+ content: [
+ { type: "text", text: await callAgent("db", args.task, args.context) },
+ ],
+ };
+
+ case "ask_frontend_agent":
+ return {
+ content: [
+ { type: "text", text: await callAgent("frontend", args.task, args.context) },
+ ],
+ };
+
+ case "parallel_ask":
+ const results = await Promise.all(
+ args.requests.map(async (req) => ({
+ agent: req.agent,
+ result: await callAgent(req.agent, req.task, req.context),
+ }))
+ );
+ return {
+ content: [
+ { type: "text", text: JSON.stringify(results, null, 2) },
+ ],
+ };
+
+ default:
+ throw new Error(`Unknown tool: ${name}`);
+ }
+});
+
+// 서버 시작
+const transport = new StdioServerTransport();
+await server.connect(transport);
+```
+
+### Cursor 설정
+
+```json
+// .cursor/mcp.json
+{
+ "mcpServers": {
+ "agent-orchestrator": {
+ "command": "node",
+ "args": ["C:/Users/defaultuser0/mcp-agent-orchestrator/build/index.js"],
+ "env": {
+ "ANTHROPIC_API_KEY": "your-api-key-here"
+ }
+ }
+ }
+}
+```
+
+---
+
+## 비용 분석
+
+### 토큰 사용량 비교
+
+| 시나리오 | 단일 에이전트 | 멀티 에이전트 | 절감 |
+|----------|--------------|--------------|------|
+| 기능 1개 추가 | 100,000 토큰 | 60,000 토큰 | 40% |
+| 시스템 리팩토링 | 300,000 토큰 | 150,000 토큰 | 50% |
+| 새 모듈 개발 | 500,000 토큰 | 200,000 토큰 | 60% |
+
+### 비용 계산 (예시)
+
+```
+단일 에이전트 (전부 Opus):
+- 300,000 토큰 × $15/M = $4.50
+
+멀티 에이전트 (Opus PM + Sonnet Workers):
+- PM (Opus): 50,000 토큰 × $15/M = $0.75
+- Workers (Sonnet): 100,000 토큰 × $3/M = $0.30
+- 총: $1.05
+
+절감: $4.50 - $1.05 = $3.45 (76% 절감!)
+```
+
+### ROI 분석
+
+```
+초기 투자:
+- MCP 서버 개발: 4-6시간
+- 프롬프트 튜닝: 2-4시간
+- 테스트: 2시간
+- 총: 8-12시간
+
+회수:
+- 대규모 작업당 $3-5 절감
+- 재작업 시간 70% 감소
+- 품질 30% 향상
+
+손익분기점: 대규모 작업 3-5회
+```
+
+---
+
+## 한계점 및 해결방안
+
+### 현재 한계
+
+| 한계 | 설명 | 해결방안 |
+|------|------|----------|
+| 완전 자동화 불가 | Cursor 에이전트 간 직접 통신 없음 | MCP 서버로 우회 |
+| 파일 읽기 제한 | 각 에이전트가 모든 파일 접근 어려움 | 컨텍스트에 필요한 정보 전달 |
+| 실시간 동기화 | 변경사항 즉시 반영 어려움 | 명시적 갱신 요청 |
+| 에러 복구 | 자동 롤백 메커니즘 없음 | 수동 복구 또는 git 활용 |
+
+### 향후 개선 방향
+
+1. **파일 시스템 연동**
+ - MCP 서버에 파일 읽기/쓰기 도구 추가
+ - 에이전트가 직접 코드 확인 가능
+
+2. **결과 자동 적용**
+ - 코드 변경사항 자동 적용
+ - git 커밋 자동화
+
+3. **피드백 루프**
+ - 테스트 자동 실행
+ - 실패 시 자동 재시도
+
+4. **히스토리 관리**
+ - 대화 이력 저장
+ - 컨텍스트 캐싱
+
+---
+
+## 체크리스트
+
+### 구현 전 준비
+
+- [ ] Node.js 18+ 설치
+- [ ] Anthropic API 키 발급
+- [ ] 프로젝트 폴더 생성
+
+### MCP 서버 구현
+
+- [ ] package.json 설정
+- [ ] TypeScript 설정
+- [ ] 기본 서버 구조
+- [ ] 도구 정의 (4개)
+- [ ] 에이전트 프롬프트 작성
+- [ ] 빌드 및 테스트
+
+### Cursor 연동
+
+- [ ] mcp.json 설정
+- [ ] Cursor 재시작
+- [ ] 도구 호출 테스트
+- [ ] 실제 작업 테스트
+
+### 튜닝
+
+- [ ] 프롬프트 개선
+- [ ] 에러 핸들링 강화
+- [ ] 로깅 추가
+- [ ] 성능 최적화
+
+---
+
+## 참고 자료
+
+- [MCP SDK 문서](https://modelcontextprotocol.io/)
+- [Anthropic API 문서](https://docs.anthropic.com/)
+- [CrewAI](https://github.com/joaomdmoura/crewAI) - 멀티에이전트 프레임워크 참고
+- [AutoGen](https://github.com/microsoft/autogen) - Microsoft 멀티에이전트 참고
+
+---
+
+*작성일: 2026-02-05*
+*버전: 1.0*
diff --git a/mcp-agent-orchestrator/README.md b/mcp-agent-orchestrator/README.md
new file mode 100644
index 00000000..ce9aac42
--- /dev/null
+++ b/mcp-agent-orchestrator/README.md
@@ -0,0 +1,189 @@
+# Multi-Agent Orchestrator MCP Server v2.0
+
+Cursor Agent CLI를 활용한 멀티에이전트 시스템입니다.
+**Cursor Team Plan만으로 동작** - 외부 API 키 불필요!
+
+## 아키텍처
+
+```
+┌─────────────────────────────────────────┐
+│ Cursor IDE (PM Agent) │
+│ Claude Opus 4.5 │
+└────────────────────┬────────────────────┘
+ │ MCP Tools
+ ┌────────────────┼────────────────┐
+ ▼ ▼ ▼
+┌────────┐ ┌────────┐ ┌────────┐
+│Backend │ │ DB │ │Frontend│
+│ Agent │ │ Agent │ │ Agent │
+│ via CLI│ │ via CLI│ │ via CLI│
+│Sonnet │ │Sonnet │ │Sonnet │
+└────────┘ └────────┘ └────────┘
+ ↑ ↑ ↑
+ └──────────────┴───────────────┘
+ Cursor Agent CLI
+ (Team Plan 크레딧 사용)
+```
+
+## 특징
+
+- **API 키 불필요**: Cursor Team Plan 크레딧만 사용
+- **크로스 플랫폼**: Windows, Mac, Linux 지원
+- **진짜 병렬 실행**: `parallel_ask`로 동시 작업
+- **모델 티어링**: PM=Opus, Sub-agents=Sonnet
+
+## 사전 요구사항
+
+1. **Cursor Team/Pro Plan** 구독
+2. **Cursor Agent CLI** 설치 및 로그인
+ ```bash
+ # 설치 후 로그인 확인
+ agent status
+ ```
+
+## 설치
+
+```bash
+cd mcp-agent-orchestrator
+npm install
+npm run build
+```
+
+## Cursor 설정
+
+### Windows
+
+`.cursor/mcp.json`:
+```json
+{
+ "mcpServers": {
+ "agent-orchestrator": {
+ "command": "node",
+ "args": ["C:/Users/YOUR_USERNAME/ERP-node/mcp-agent-orchestrator/build/index.js"]
+ }
+ }
+}
+```
+
+### Mac
+
+`.cursor/mcp.json`:
+```json
+{
+ "mcpServers": {
+ "agent-orchestrator": {
+ "command": "node",
+ "args": ["/Users/YOUR_USERNAME/ERP-node/mcp-agent-orchestrator/build/index.js"]
+ }
+ }
+}
+```
+
+**주의**: Mac에서 agent CLI가 PATH에 있어야 합니다.
+```bash
+# agent CLI 위치 확인
+which agent
+# 보통: ~/.cursor-agent/bin/agent 또는 /usr/local/bin/agent
+
+# PATH에 없으면 추가 (.zshrc 또는 .bashrc)
+export PATH="$HOME/.cursor-agent/bin:$PATH"
+```
+
+## 사용 가능한 도구
+
+### ask_backend_agent
+백엔드 전문가에게 질문/작업 요청
+- API 설계, 서비스 로직, 라우팅
+- 담당 폴더: `backend-node/src/`
+
+### ask_db_agent
+DB 전문가에게 질문/작업 요청
+- 스키마, 쿼리, MyBatis 매퍼
+- 담당 폴더: `src/com/pms/mapper/`, `db/`
+
+### ask_frontend_agent
+프론트엔드 전문가에게 질문/작업 요청
+- React 컴포넌트, 페이지, 스타일
+- 담당 폴더: `frontend/`
+
+### parallel_ask
+여러 전문가에게 동시에 질문 (진짜 병렬 실행!)
+- 정보 수집 단계에서 유용
+
+### get_agent_info
+에이전트 시스템 정보 확인
+
+## 워크플로우 예시
+
+### 1단계: 정보 수집 (병렬)
+```
+parallel_ask([
+ { agent: "backend", task: "현재 order 관련 API 구조 분석" },
+ { agent: "db", task: "orders 테이블 스키마 분석" },
+ { agent: "frontend", task: "주문 관련 컴포넌트 현황 분석" }
+])
+```
+
+### 2단계: 개별 작업 (순차)
+```
+ask_db_agent("cursor 기반 페이징 쿼리 작성")
+ask_backend_agent("GET /api/orders에 pagination 추가")
+ask_frontend_agent("Pagination 컴포넌트 적용")
+```
+
+## 모델 설정
+
+| Agent | Model | 역할 |
+|-------|-------|------|
+| PM (Cursor IDE) | Opus 4.5 | 전체 조율, 사용자 대화 |
+| Backend | Sonnet 4.5 | API, 서비스 로직 |
+| DB | Sonnet 4.5 | 스키마, 쿼리 |
+| Frontend | Sonnet 4.5 | 컴포넌트, UI |
+
+**비용 최적화**: PM만 Opus, 나머지는 Sonnet 사용
+
+## 환경 변수
+
+- `LOG_LEVEL`: 로그 레벨 (debug, info, warn, error)
+
+## 트러블슈팅
+
+### Windows: agent 명령어가 안 됨
+```powershell
+# PowerShell 실행 정책 확인
+Get-ExecutionPolicy -List
+
+# 필요시 변경
+Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+```
+
+### Mac: agent 명령어를 찾을 수 없음
+```bash
+# agent CLI 위치 확인
+ls -la ~/.cursor-agent/bin/
+
+# PATH 추가
+echo 'export PATH="$HOME/.cursor-agent/bin:$PATH"' >> ~/.zshrc
+source ~/.zshrc
+```
+
+### 응답이 오래 걸림
+- 정상입니다! 각 에이전트 호출에 15-30초 소요
+- `parallel_ask`로 병렬 처리하면 시간 절약
+
+## 개발
+
+```bash
+# 개발 모드 (watch)
+npm run dev
+
+# 빌드
+npm run build
+
+# 테스트 실행
+npm start
+```
+
+## 라이선스
+
+MIT
diff --git a/mcp-agent-orchestrator/package-lock.json b/mcp-agent-orchestrator/package-lock.json
new file mode 100644
index 00000000..79594e1f
--- /dev/null
+++ b/mcp-agent-orchestrator/package-lock.json
@@ -0,0 +1,1444 @@
+{
+ "name": "mcp-agent-orchestrator",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "mcp-agent-orchestrator",
+ "version": "1.0.0",
+ "dependencies": {
+ "@anthropic-ai/sdk": "^0.39.0",
+ "@modelcontextprotocol/sdk": "^1.0.0"
+ },
+ "devDependencies": {
+ "@types/node": "^20.0.0",
+ "typescript": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@anthropic-ai/sdk": {
+ "version": "0.39.0",
+ "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.39.0.tgz",
+ "integrity": "sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^18.11.18",
+ "@types/node-fetch": "^2.6.4",
+ "abort-controller": "^3.0.0",
+ "agentkeepalive": "^4.2.1",
+ "form-data-encoder": "1.7.2",
+ "formdata-node": "^4.3.2",
+ "node-fetch": "^2.6.7"
+ }
+ },
+ "node_modules/@anthropic-ai/sdk/node_modules/@types/node": {
+ "version": "18.19.130",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+ "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@anthropic-ai/sdk/node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "license": "MIT"
+ },
+ "node_modules/@hono/node-server": {
+ "version": "1.19.9",
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz",
+ "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.14.1"
+ },
+ "peerDependencies": {
+ "hono": "^4"
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk": {
+ "version": "1.26.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz",
+ "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==",
+ "license": "MIT",
+ "dependencies": {
+ "@hono/node-server": "^1.19.9",
+ "ajv": "^8.17.1",
+ "ajv-formats": "^3.0.1",
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "eventsource-parser": "^3.0.0",
+ "express": "^5.2.1",
+ "express-rate-limit": "^8.2.1",
+ "hono": "^4.11.4",
+ "jose": "^6.1.3",
+ "json-schema-typed": "^8.0.2",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.25 || ^4.0",
+ "zod-to-json-schema": "^3.25.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@cfworker/json-schema": "^4.1.1",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@cfworker/json-schema": {
+ "optional": true
+ },
+ "zod": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.31",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.31.tgz",
+ "integrity": "sha512-5jsi0wpncvTD33Sh1UCgacK37FFwDn+EG7wCmEvs62fCvBL+n8/76cAYDok21NF6+jaVWIqKwCZyX7Vbu8eB3A==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/node-fetch": {
+ "version": "2.6.13",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz",
+ "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "form-data": "^4.0.4"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+ "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
+ "license": "MIT",
+ "dependencies": {
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
+ "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.7.0",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.1",
+ "raw-body": "^3.0.1",
+ "type-is": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
+ "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "license": "MIT",
+ "dependencies": {
+ "eventsource-parser": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
+ "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
+ "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==",
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "10.0.1"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+ "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data-encoder": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
+ "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
+ "license": "MIT"
+ },
+ "node_modules/form-data/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/formdata-node": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
+ "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "1.0.0",
+ "web-streams-polyfill": "4.0.0-beta.3"
+ },
+ "engines": {
+ "node": ">= 12.20"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hono": {
+ "version": "4.11.7",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz",
+ "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ip-address": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
+ "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/jose": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz",
+ "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-typed": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
+ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
+ "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/pkce-challenge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
+ "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
+ "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.1",
+ "mime-types": "^3.0.2",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "4.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
+ "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/zod": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.25.1",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz",
+ "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.25 || ^4"
+ }
+ }
+ }
+}
diff --git a/mcp-agent-orchestrator/package.json b/mcp-agent-orchestrator/package.json
new file mode 100644
index 00000000..bc8b5b7c
--- /dev/null
+++ b/mcp-agent-orchestrator/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "mcp-agent-orchestrator",
+ "version": "2.0.0",
+ "description": "Multi-Agent Orchestrator MCP Server using Cursor Agent CLI (Team Plan)",
+ "type": "module",
+ "main": "build/index.js",
+ "scripts": {
+ "build": "tsc",
+ "start": "node build/index.js",
+ "dev": "tsc --watch"
+ },
+ "dependencies": {
+ "@modelcontextprotocol/sdk": "^1.0.0"
+ },
+ "devDependencies": {
+ "@types/node": "^20.0.0",
+ "typescript": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "keywords": [
+ "cursor",
+ "mcp",
+ "multi-agent",
+ "ai",
+ "orchestrator"
+ ]
+}
diff --git a/mcp-agent-orchestrator/src/agents/index.ts b/mcp-agent-orchestrator/src/agents/index.ts
new file mode 100644
index 00000000..7aebaa5c
--- /dev/null
+++ b/mcp-agent-orchestrator/src/agents/index.ts
@@ -0,0 +1,6 @@
+/**
+ * 에이전트 모듈 내보내기
+ */
+
+export * from "./types.js";
+export * from "./prompts.js";
diff --git a/mcp-agent-orchestrator/src/agents/prompts.ts b/mcp-agent-orchestrator/src/agents/prompts.ts
new file mode 100644
index 00000000..339ae84d
--- /dev/null
+++ b/mcp-agent-orchestrator/src/agents/prompts.ts
@@ -0,0 +1,261 @@
+/**
+ * Agent System Prompts (English to avoid CMD encoding issues)
+ * Agents will still respond in Korean based on user preferences
+ */
+
+export const PM_PROMPT = `# Role
+You are a PM (Project Manager) agent.
+Analyze user requests, distribute tasks to specialist agents (Backend, DB, Frontend),
+and integrate results to create the final deliverable.
+
+# Available Tools
+- ask_backend_agent: Ask/request tasks from backend expert
+- ask_db_agent: Ask/request tasks from DB expert
+- ask_frontend_agent: Ask/request tasks from frontend expert
+- parallel_ask: Request from multiple experts simultaneously
+
+# Work Process
+
+## Phase 1: Analysis
+1. Analyze user request
+2. Identify required information
+3. Request info gathering (use parallel_ask)
+
+## Phase 2: Planning
+1. Analyze gathered information
+2. Break down tasks and identify dependencies
+3. Determine priorities
+4. Create work distribution plan
+
+## Phase 3: Execution
+1. Request tasks in dependency order
+2. Verify results
+3. Re-request if needed
+
+## Phase 4: Integration
+1. Collect all results
+2. Verify consistency
+3. Report to user
+
+# Task Distribution Criteria
+- Backend Agent: API, service logic, routing (backend-node/)
+- DB Agent: Schema, queries, migrations (mapper/, db/)
+- Frontend Agent: Components, pages, styles (frontend/)
+
+# Decision Criteria
+- Ask user if uncertain
+- Re-request if agent result seems wrong
+- Confirm with user if impact is large
+- Choose safer direction when conflicts arise
+
+# Response Format
+Use JSON format when distributing tasks:
+{
+ "phase": "info_gathering | work_distribution | integration",
+ "reasoning": "why distributing this way",
+ "tasks": [
+ {
+ "agent": "backend | db | frontend",
+ "priority": 1,
+ "task": "specific task content",
+ "depends_on": [],
+ "expected_output": "expected result"
+ }
+ ]
+}
+
+Final report:
+{
+ "summary": "one line summary",
+ "completed_tasks": ["completed tasks"],
+ "files_changed": ["changed files"],
+ "next_steps": ["next steps"],
+ "test_instructions": ["how to test"]
+}`;
+
+export const BACKEND_PROMPT = `# Role
+You are a Backend specialist agent.
+You handle API, services, and routing in the backend-node/ folder.
+
+# Your Domain (ONLY these!)
+- backend-node/src/controllers/
+- backend-node/src/services/
+- backend-node/src/routes/
+- backend-node/src/middleware/
+- backend-node/src/utils/
+
+# NOT Your Domain (NEVER touch)
+- frontend/ -> Frontend Agent handles this
+- src/com/pms/mapper/ -> DB Agent handles this
+- Direct SQL queries -> Request from DB Agent
+
+# Code Rules
+1. Use TypeScript
+2. Error handling required
+3. Comments in Korean
+4. Follow existing code style
+5. Complete code, no ... ellipsis
+
+# Response Format (JSON)
+{
+ "status": "success | partial | failed | need_clarification",
+ "confidence": "high | medium | low",
+ "result": {
+ "summary": "one line summary",
+ "details": "detailed explanation",
+ "files_affected": ["file paths"],
+ "code_changes": [
+ {
+ "file": "path",
+ "action": "create | modify | delete",
+ "content": "complete code"
+ }
+ ]
+ },
+ "needs_from_others": [
+ {"agent": "db", "request": "what you need"}
+ ],
+ "side_effects": ["affected areas"],
+ "questions": ["unclear points"]
+}
+
+# Collaboration Rules
+1. Report immediately if out of scope (scope_violation)
+2. Set confidence: "low" if uncertain
+3. Specify in needs_from_others if other agents needed
+4. Always report side effects`;
+
+export const DB_PROMPT = `# Role
+You are a Database specialist agent.
+You handle DB schema, queries, and migrations.
+
+# Your Domain (ONLY these!)
+- src/com/pms/mapper/ (MyBatis XML)
+- db/ (schema, migrations)
+- backend-node/src/database/
+
+# NOT Your Domain (NEVER touch)
+- API logic -> Backend Agent handles this
+- Frontend -> Frontend Agent handles this
+- Business logic decisions -> Confirm with PM
+
+# Code Rules
+1. Use PostgreSQL syntax
+2. Parameter binding (#{}) required - prevent SQL injection
+3. Consider indexes
+4. Consider performance optimization
+
+# MyBatis Mapper Rules
+- Parameter binding: WHERE id = #{id}
+- Dynamic queries: ...
+- Pagination: LIMIT #{limit} OFFSET #{offset}
+
+# Response Format (JSON)
+{
+ "status": "success | partial | failed | need_clarification",
+ "confidence": "high | medium | low",
+ "result": {
+ "summary": "one line summary",
+ "details": "detailed explanation",
+ "schema_info": {
+ "tables": ["related tables"],
+ "columns": ["main columns"],
+ "indexes": ["indexes"]
+ },
+ "code_changes": [
+ {
+ "file": "path",
+ "action": "create | modify",
+ "content": "query/schema"
+ }
+ ]
+ },
+ "performance_notes": ["performance considerations"],
+ "questions": ["unclear points"]
+}
+
+# Collaboration Rules
+1. Report immediately if out of scope
+2. Set confidence: "low" if uncertain
+3. Always mention performance issues`;
+
+export const FRONTEND_PROMPT = `# Role
+You are a Frontend specialist agent.
+You handle React/Next.js UI implementation.
+
+# Your Domain (ONLY these!)
+- frontend/components/
+- frontend/pages/ or frontend/app/
+- frontend/lib/
+- frontend/hooks/
+- frontend/styles/
+
+# NOT Your Domain (NEVER touch)
+- backend-node/ -> Backend Agent handles this
+- DB related -> DB Agent handles this
+- API spec decisions -> Discuss with PM/Backend
+
+# Code Rules
+1. Use TypeScript
+2. React functional components
+3. Use custom hooks
+4. Comments in Korean
+5. Follow Tailwind CSS or existing style system
+
+# API Call Rules
+- NEVER use fetch directly!
+- Use lib/api/ client
+- Error handling required
+
+# Response Format (JSON)
+{
+ "status": "success | partial | failed | need_clarification",
+ "confidence": "high | medium | low",
+ "result": {
+ "summary": "one line summary",
+ "details": "detailed explanation",
+ "components_affected": ["component list"],
+ "code_changes": [
+ {
+ "file": "path",
+ "action": "create | modify",
+ "content": "complete code"
+ }
+ ]
+ },
+ "needs_from_others": [
+ {"agent": "backend", "request": "needed API"}
+ ],
+ "ui_notes": ["UX considerations"],
+ "questions": ["unclear points"]
+}
+
+# Collaboration Rules
+1. Report immediately if out of scope
+2. Set confidence: "low" if uncertain
+3. Specify in needs_from_others if API needed
+4. Suggest UX improvements if any`;
+
+// 에이전트 설정 맵
+export const AGENT_CONFIGS = {
+ pm: {
+ model: 'claude-opus-4-5-20250214',
+ systemPrompt: PM_PROMPT,
+ maxTokens: 8192,
+ },
+ backend: {
+ model: 'claude-sonnet-4-20250514',
+ systemPrompt: BACKEND_PROMPT,
+ maxTokens: 8192,
+ },
+ db: {
+ model: 'claude-sonnet-4-20250514',
+ systemPrompt: DB_PROMPT,
+ maxTokens: 8192,
+ },
+ frontend: {
+ model: 'claude-sonnet-4-20250514',
+ systemPrompt: FRONTEND_PROMPT,
+ maxTokens: 8192,
+ },
+} as const;
diff --git a/mcp-agent-orchestrator/src/agents/types.ts b/mcp-agent-orchestrator/src/agents/types.ts
new file mode 100644
index 00000000..ad045ba6
--- /dev/null
+++ b/mcp-agent-orchestrator/src/agents/types.ts
@@ -0,0 +1,63 @@
+/**
+ * Multi-Agent System 타입 정의
+ */
+
+// 에이전트 타입
+export type AgentType = 'pm' | 'backend' | 'db' | 'frontend';
+
+// 에이전트 설정
+export interface AgentConfig {
+ model: string;
+ systemPrompt: string;
+ maxTokens: number;
+}
+
+// 작업 요청
+export interface TaskRequest {
+ agent: AgentType;
+ task: string;
+ context?: string;
+}
+
+// 작업 응답 상태
+export type ResponseStatus = 'success' | 'partial' | 'failed' | 'need_clarification';
+
+// 확신도
+export type ConfidenceLevel = 'high' | 'medium' | 'low';
+
+// 코드 변경
+export interface CodeChange {
+ file: string;
+ action: 'create' | 'modify' | 'delete';
+ content?: string;
+ lineStart?: number;
+ lineEnd?: number;
+}
+
+// 에이전트 응답
+export interface AgentResponse {
+ status: ResponseStatus;
+ confidence: ConfidenceLevel;
+ result?: {
+ summary: string;
+ details: string;
+ filesAffected?: string[];
+ codeChanges?: CodeChange[];
+ };
+ scopeViolations?: string[];
+ dependencies?: string[];
+ sideEffects?: string[];
+ alternatives?: string[];
+ questions?: string[];
+ needsFromOthers?: {
+ agent: AgentType;
+ request: string;
+ }[];
+}
+
+// 병렬 요청 결과
+export interface ParallelResult {
+ agent: AgentType;
+ result: string;
+ error?: string;
+}
diff --git a/mcp-agent-orchestrator/src/index.ts b/mcp-agent-orchestrator/src/index.ts
new file mode 100644
index 00000000..fb62e7b8
--- /dev/null
+++ b/mcp-agent-orchestrator/src/index.ts
@@ -0,0 +1,401 @@
+#!/usr/bin/env node
+/**
+ * Multi-Agent Orchestrator MCP Server
+ *
+ * Cursor Agent CLI를 활용한 멀티에이전트 시스템
+ * - PM (Cursor IDE): 전체 조율
+ * - Sub-agents (agent CLI): 전문가별 작업 수행
+ *
+ * 모든 AI 호출이 Cursor Team Plan으로 처리됨!
+ * API 키 불필요!
+ */
+
+import { Server } from "@modelcontextprotocol/sdk/server/index.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import {
+ CallToolRequestSchema,
+ ListToolsRequestSchema,
+} from "@modelcontextprotocol/sdk/types.js";
+import { exec } from "child_process";
+import { promisify } from "util";
+import { platform } from "os";
+import { AGENT_CONFIGS } from "./agents/prompts.js";
+import { AgentType, ParallelResult } from "./agents/types.js";
+import { logger } from "./utils/logger.js";
+
+const execAsync = promisify(exec);
+
+// OS 감지
+const isWindows = platform() === "win32";
+logger.info(`Platform detected: ${platform()} (isWindows: ${isWindows})`);
+
+// MCP 서버 생성
+const server = new Server(
+ {
+ name: "agent-orchestrator",
+ version: "2.0.0",
+ },
+ {
+ capabilities: {
+ tools: {},
+ },
+ }
+);
+
+/**
+ * Cursor Agent CLI를 통해 에이전트 호출
+ * Cursor Team Plan 사용 - API 키 불필요!
+ *
+ * 크로스 플랫폼 지원:
+ * - Windows: cmd /c "echo. | agent ..." (stdin 닫기 위해)
+ * - Mac/Linux: echo "" | agent ... (bash 사용)
+ */
+async function callAgentCLI(
+ agentType: AgentType,
+ task: string,
+ context?: string
+): Promise {
+ const config = AGENT_CONFIGS[agentType];
+
+ // 모델 선택: PM은 opus, 나머지는 sonnet
+ const model = agentType === 'pm' ? 'opus-4.5' : 'sonnet-4.5';
+
+ logger.info(`Calling ${agentType} agent via CLI`, { model, task: task.substring(0, 100) });
+
+ try {
+ // 프롬프트 구성
+ const systemPrompt = config.systemPrompt
+ .replace(/\r?\n/g, ' ') // 줄바꿈을 공백으로
+ .replace(/"/g, '\\"'); // 쌍따옴표 이스케이프
+
+ const userMessage = context
+ ? `${task} (Background info: ${context})`
+ : task;
+
+ // 전체 프롬프트 (시스템 + 유저)
+ const fullPrompt = `SYSTEM INSTRUCTIONS: ${systemPrompt} --- TASK REQUEST: ${userMessage}`
+ .replace(/\[/g, '(') // 대괄호를 괄호로 변환 (쉘 호환)
+ .replace(/\]/g, ')')
+ .replace(/"/g, '\\"'); // 쌍따옴표 이스케이프
+
+ let cmd: string;
+ let shell: string;
+
+ if (isWindows) {
+ // Windows: CMD를 통해 echo로 빈 입력 파이프
+ cmd = `cmd /c "echo. | agent -p \\"${fullPrompt}\\" --model ${model} --output-format text"`;
+ shell = 'cmd.exe';
+ } else {
+ // Mac/Linux: Bash를 통해 빈 문자열 파이프
+ // 참고: Mac에서는 agent CLI가 ~/.cursor-agent/bin/agent 경로에 있을 수 있음
+ cmd = `echo "" | agent -p "${fullPrompt}" --model ${model} --output-format text`;
+ shell = '/bin/bash';
+ }
+
+ logger.debug(`Executing on ${isWindows ? 'Windows' : 'Mac/Linux'}: agent -p "..." --model ${model}`);
+
+ const { stdout, stderr } = await execAsync(cmd, {
+ cwd: process.cwd(),
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
+ timeout: 300000, // 5분 타임아웃
+ shell,
+ });
+
+ if (stderr && !stderr.includes('warning')) {
+ logger.warn(`${agentType} agent stderr`, { stderr });
+ }
+
+ logger.info(`${agentType} agent completed via CLI`);
+ return stdout.trim();
+ } catch (error) {
+ logger.error(`${agentType} agent CLI error`, error);
+ throw error;
+ }
+}
+
+/**
+ * 도구 목록 핸들러
+ */
+server.setRequestHandler(ListToolsRequestSchema, async () => {
+ return {
+ tools: [
+ {
+ name: "ask_backend_agent",
+ description:
+ "백엔드 전문가에게 질문하거나 작업을 요청합니다. " +
+ "API 설계, 서비스 로직, 라우팅, 미들웨어 관련 작업에 사용하세요. " +
+ "담당 폴더: backend-node/src/ (Cursor Team Plan 사용, sonnet-4.5 모델)",
+ inputSchema: {
+ type: "object" as const,
+ properties: {
+ task: {
+ type: "string",
+ description: "백엔드 에이전트에게 요청할 작업 내용",
+ },
+ context: {
+ type: "string",
+ description: "작업에 필요한 배경 정보 (선택사항)",
+ },
+ },
+ required: ["task"],
+ },
+ },
+ {
+ name: "ask_db_agent",
+ description:
+ "DB 전문가에게 질문하거나 작업을 요청합니다. " +
+ "스키마 설계, SQL 쿼리, MyBatis 매퍼, 마이그레이션 관련 작업에 사용하세요. " +
+ "담당 폴더: src/com/pms/mapper/, db/ (Cursor Team Plan 사용, sonnet-4.5 모델)",
+ inputSchema: {
+ type: "object" as const,
+ properties: {
+ task: {
+ type: "string",
+ description: "DB 에이전트에게 요청할 작업 내용",
+ },
+ context: {
+ type: "string",
+ description: "작업에 필요한 배경 정보 (선택사항)",
+ },
+ },
+ required: ["task"],
+ },
+ },
+ {
+ name: "ask_frontend_agent",
+ description:
+ "프론트엔드 전문가에게 질문하거나 작업을 요청합니다. " +
+ "React 컴포넌트, 페이지, 스타일링, 상태관리 관련 작업에 사용하세요. " +
+ "담당 폴더: frontend/ (Cursor Team Plan 사용, sonnet-4.5 모델)",
+ inputSchema: {
+ type: "object" as const,
+ properties: {
+ task: {
+ type: "string",
+ description: "프론트엔드 에이전트에게 요청할 작업 내용",
+ },
+ context: {
+ type: "string",
+ description: "작업에 필요한 배경 정보 (선택사항)",
+ },
+ },
+ required: ["task"],
+ },
+ },
+ {
+ name: "parallel_ask",
+ description:
+ "여러 전문가에게 동시에 질문합니다 (진짜 병렬 실행!). " +
+ "정보 수집 단계에서 모든 영역의 현황을 빠르게 파악할 때 유용합니다. " +
+ "모든 에이전트가 동시에 실행되어 시간 절약! (Cursor Team Plan 사용)",
+ inputSchema: {
+ type: "object" as const,
+ properties: {
+ requests: {
+ type: "array",
+ description: "각 에이전트에게 보낼 요청 목록",
+ items: {
+ type: "object",
+ properties: {
+ agent: {
+ type: "string",
+ enum: ["backend", "db", "frontend"],
+ description: "요청할 에이전트 타입",
+ },
+ task: {
+ type: "string",
+ description: "해당 에이전트에게 요청할 작업",
+ },
+ context: {
+ type: "string",
+ description: "배경 정보 (선택사항)",
+ },
+ },
+ required: ["agent", "task"],
+ },
+ },
+ },
+ required: ["requests"],
+ },
+ },
+ {
+ name: "get_agent_info",
+ description:
+ "에이전트 시스템의 현재 상태와 사용 가능한 에이전트 정보를 확인합니다.",
+ inputSchema: {
+ type: "object" as const,
+ properties: {},
+ },
+ },
+ ],
+ };
+});
+
+/**
+ * 도구 호출 핸들러
+ */
+server.setRequestHandler(CallToolRequestSchema, async (request) => {
+ const { name, arguments: args } = request.params;
+
+ logger.info(`Tool called: ${name}`);
+
+ try {
+ switch (name) {
+ case "ask_backend_agent": {
+ const { task, context } = args as { task: string; context?: string };
+ const result = await callAgentCLI("backend", task, context);
+ return {
+ content: [{ type: "text" as const, text: result }],
+ };
+ }
+
+ case "ask_db_agent": {
+ const { task, context } = args as { task: string; context?: string };
+ const result = await callAgentCLI("db", task, context);
+ return {
+ content: [{ type: "text" as const, text: result }],
+ };
+ }
+
+ case "ask_frontend_agent": {
+ const { task, context } = args as { task: string; context?: string };
+ const result = await callAgentCLI("frontend", task, context);
+ return {
+ content: [{ type: "text" as const, text: result }],
+ };
+ }
+
+ case "parallel_ask": {
+ const { requests } = args as {
+ requests: Array<{
+ agent: "backend" | "db" | "frontend";
+ task: string;
+ context?: string;
+ }>;
+ };
+
+ logger.info(`Parallel ask to ${requests.length} agents (TRUE PARALLEL!)`);
+
+ // 진짜 병렬 실행! 모든 에이전트가 동시에 작업
+ const results: ParallelResult[] = await Promise.all(
+ requests.map(async (req) => {
+ try {
+ const result = await callAgentCLI(req.agent, req.task, req.context);
+ return { agent: req.agent, result };
+ } catch (error) {
+ return {
+ agent: req.agent,
+ result: "",
+ error: error instanceof Error ? error.message : "Unknown error",
+ };
+ }
+ })
+ );
+
+ // 결과를 보기 좋게 포맷팅
+ const formattedResults = results.map((r) => {
+ const header = `\n${"=".repeat(60)}\n## ${r.agent.toUpperCase()} Agent 응답\n${"=".repeat(60)}\n`;
+ if (r.error) {
+ return `${header}❌ 에러: ${r.error}`;
+ }
+ return `${header}${r.result}`;
+ });
+
+ return {
+ content: [
+ {
+ type: "text" as const,
+ text: formattedResults.join("\n"),
+ },
+ ],
+ };
+ }
+
+ case "get_agent_info": {
+ const info = {
+ system: "Multi-Agent Orchestrator v2.0",
+ version: "2.0.0",
+ backend: "Cursor Agent CLI (Team Plan)",
+ apiKey: "NOT REQUIRED! Using Cursor subscription",
+ agents: {
+ pm: {
+ role: "Project Manager",
+ model: "opus-4.5 (Cursor IDE에서 직접)",
+ description: "전체 조율, 사용자 의도 파악, 작업 분배",
+ },
+ backend: {
+ role: "Backend Specialist",
+ model: "sonnet-4.5 (via agent CLI)",
+ description: "API, 서비스 로직, 라우팅 담당",
+ folder: "backend-node/src/",
+ },
+ db: {
+ role: "Database Specialist",
+ model: "sonnet-4.5 (via agent CLI)",
+ description: "스키마, 쿼리, 마이그레이션 담당",
+ folder: "src/com/pms/mapper/, db/",
+ },
+ frontend: {
+ role: "Frontend Specialist",
+ model: "sonnet-4.5 (via agent CLI)",
+ description: "컴포넌트, 페이지, 스타일링 담당",
+ folder: "frontend/",
+ },
+ },
+ features: {
+ parallel_execution: true,
+ cursor_team_plan: true,
+ separate_api_key: false,
+ real_multi_session: true,
+ },
+ usage: {
+ single_agent: "ask_backend_agent, ask_db_agent, ask_frontend_agent",
+ parallel: "parallel_ask로 여러 에이전트 동시 호출",
+ workflow: "1. parallel_ask로 정보 수집 → 2. 개별 에이전트로 작업 분배",
+ },
+ };
+
+ return {
+ content: [
+ {
+ type: "text" as const,
+ text: JSON.stringify(info, null, 2),
+ },
+ ],
+ };
+ }
+
+ default:
+ throw new Error(`Unknown tool: ${name}`);
+ }
+ } catch (error) {
+ logger.error(`Tool error: ${name}`, error);
+ return {
+ content: [
+ {
+ type: "text" as const,
+ text: `❌ 에러 발생: ${error instanceof Error ? error.message : "Unknown error"}`,
+ },
+ ],
+ isError: true,
+ };
+ }
+});
+
+/**
+ * 서버 시작
+ */
+async function main() {
+ logger.info("Starting Multi-Agent Orchestrator MCP Server v2.0...");
+ logger.info("Backend: Cursor Agent CLI (Team Plan - No API Key Required!)");
+
+ const transport = new StdioServerTransport();
+ await server.connect(transport);
+
+ logger.info("MCP Server connected and ready!");
+}
+
+main().catch((error) => {
+ logger.error("Server failed to start", error);
+ process.exit(1);
+});
diff --git a/mcp-agent-orchestrator/src/utils/logger.ts b/mcp-agent-orchestrator/src/utils/logger.ts
new file mode 100644
index 00000000..9e74d552
--- /dev/null
+++ b/mcp-agent-orchestrator/src/utils/logger.ts
@@ -0,0 +1,55 @@
+/**
+ * 간단한 로깅 유틸리티
+ */
+
+type LogLevel = 'debug' | 'info' | 'warn' | 'error';
+
+const LOG_LEVELS: Record = {
+ debug: 0,
+ info: 1,
+ warn: 2,
+ error: 3,
+};
+
+// 환경변수로 로그 레벨 설정 (기본: info)
+const currentLevel = (process.env.LOG_LEVEL as LogLevel) || 'info';
+
+function shouldLog(level: LogLevel): boolean {
+ return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];
+}
+
+function formatMessage(level: LogLevel, message: string, data?: unknown): string {
+ const timestamp = new Date().toISOString();
+ const prefix = `[${timestamp}] [${level.toUpperCase()}]`;
+
+ if (data) {
+ return `${prefix} ${message} ${JSON.stringify(data, null, 2)}`;
+ }
+ return `${prefix} ${message}`;
+}
+
+export const logger = {
+ debug(message: string, data?: unknown): void {
+ if (shouldLog('debug')) {
+ console.error(formatMessage('debug', message, data));
+ }
+ },
+
+ info(message: string, data?: unknown): void {
+ if (shouldLog('info')) {
+ console.error(formatMessage('info', message, data));
+ }
+ },
+
+ warn(message: string, data?: unknown): void {
+ if (shouldLog('warn')) {
+ console.error(formatMessage('warn', message, data));
+ }
+ },
+
+ error(message: string, data?: unknown): void {
+ if (shouldLog('error')) {
+ console.error(formatMessage('error', message, data));
+ }
+ },
+};
diff --git a/mcp-agent-orchestrator/tsconfig.json b/mcp-agent-orchestrator/tsconfig.json
new file mode 100644
index 00000000..c974e14e
--- /dev/null
+++ b/mcp-agent-orchestrator/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "outDir": "./build",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules", "build"]
+}