feat: Add Multi-Agent Orchestrator MCP Server using Cursor Agent CLI

This commit is contained in:
2026-02-06 00:03:56 +09:00
committed by DDD1542
parent 43541a12c9
commit 04565eb480
11 changed files with 3464 additions and 0 deletions
+189
View File
@@ -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
File diff suppressed because it is too large Load Diff
+29
View File
@@ -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"
]
}
@@ -0,0 +1,6 @@
/**
* 에이전트 모듈 내보내기
*/
export * from "./types.js";
export * from "./prompts.js";
@@ -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: <if test="name != null">...</if>
- 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;
@@ -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;
}
+401
View File
@@ -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<string> {
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);
});
@@ -0,0 +1,55 @@
/**
* 간단한 로깅 유틸리티
*/
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
const LOG_LEVELS: Record<LogLevel, number> = {
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));
}
},
};
+19
View File
@@ -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"]
}