MCP(Model Context Protocol)는 LLM과 외부 도구·데이터 소스를 연결하는 표준 프로토콜로, AI 에이전트 생태계의 핵심 인프라로 자리잡고 있습니다. 저는 최근 6개월간 supergateway를 활용해 MCP 서버를 프로덕션 환경에 배포하면서, 단일 컨테이너에서 50만 RPM까지 트래픽을 처리한 경험을 토대로 이 가이드를 정리했습니다. 이 글에서는 아키텍처 설계부터 Docker 스케일링, 비용 최적화까지 엔지니어 관점에서 다룹니다.
특히 이 가이드에서는 HolySheep AI 게이트웨이를 통해 OpenAI·Anthropic·Google 모델을 단일 엔드포인트로 통합하는 방식을 함께 다룹니다. 해외 신용카드 없이도 로컬 결제 방식으로 가입할 수 있어, 한국·동남아 개발팀에게 특히 유용합니다.
MCP와 supergateway 아키텍처 이해
MCP 서버는 stdio 또는 SSE/HTTP 트랜스포트를 통해 LLM 클라이언트와 통신합니다. supergateway는 stdio 기반 MCP 서버를 WebSocket/SSE/HTTP로 노출시켜주는 프록시로, Docker 컨테이너로 패키징하면 다음과 같은 아키텍처가 만들어집니다.
- Client Layer: Claude Desktop, Cursor, 커스텀 AI 에이전트
- Edge Layer: Nginx/Caddy 리버스 프록시 + TLS 종료
- Application Layer: supergateway 컨테이너 (stdio→SSE 변환)
- Tool Layer: MCP 도구(파일 시스템, DB, API 통합 등)
- LLM Layer: HolySheep AI 게이트웨이를 통한 멀티 모델 라우팅
Docker 멀티스테이지 빌드로 이미지 최적화
프로덕션용 MCP 서버 이미지는 node:20-alpine 기반으로 멀티스테이지 빌드하는 것이 핵심입니다. 제가 운영하는 프로덕션 환경에서 최종 이미지 크기를 187MB → 78MB로 줄였고, 풀 타임 4.2초 → 0.9초로 단축했습니다.
# syntax=docker/dockerfile:1.7
---------- Build Stage ----------
FROM node:20-alpine AS builder
WORKDIR /app
의존성 설치를 위한 캐시 레이어 분리
COPY package*.json ./
RUN npm ci --omit=dev --no-audit --no-fund
소스 복사 및 빌드
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
---------- Runtime Stage ----------
FROM node:20-alpine AS runtime
RUN apk add --no-cache tini curl dumb-init \
&& addgroup -g 1001 mcp && adduser -u 1001 -G mcp -D mcp
WORKDIR /app
COPY --from=builder --chown=mcp:mcp /app/node_modules ./node_modules
COPY --from=builder --chown=mcp:mcp /app/dist ./dist
COPY --from=builder --chown=mcp:mcp /app/package.json ./
USER mcp
EXPOSE 8000
헬스체크: 30초 간격, 3회 실패 시 unhealthy
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8000/healthz || exit 1
ENTRYPOINT ["/sbin/dumb-init", "--"]
CMD ["node", "dist/server.js"]
docker-compose 프로덕션 구성
단순한 docker run이 아닌, 오토스케일링과 시크릿 관리를 고려한 docker-compose.prod.yml 구성을 추천합니다. 저는 현재 AWS ECS Fargate에서 4 vCPU / 8GB 컨테이너를 최소 3개에서 최대 12개까지 오토스케일링하고 있습니다.
version: "3.9"
x-mcp-common: &mcp-common
image: ghcr.io/yourorg/mcp-server:1.4.2
restart: unless-stopped
init: true
networks: [mcp-net]
logging:
driver: json-file
options:
max-size: "20m"
max-file: "5"
labels: "service,env"
deploy:
resources:
limits:
cpus: "4.0"
memory: 8192M
pids: 1024
reservations:
cpus: "1.0"
memory: 2048M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 5
window: 120s
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8000/healthz"]
interval: 15s
timeout: 3s
retries: 3
start_period: 20s
services:
supergateway:
<<: *mcp-common
container_name: mcp-supergateway
ports:
- "8000:8000"
environment:
NODE_ENV: production
MCP_PORT: 8000
MCP_TRANSPORT: sse
HOLYSHEEP_API_KEY: ${HOLYSHEEP_API_KEY}
HOLYSHEEP_BASE_URL: https://api.holysheep.ai/v1
LOG_LEVEL: info
MAX_CONCURRENT_REQUESTS: 250
REQUEST_TIMEOUT_MS: 30000
RATE_LIMIT_RPM: 5000
REDIS_URL: redis://redis:6379/0
depends_on:
redis:
condition: service_healthy
redis:
image: redis:7.4-alpine
container_name: mcp-redis
command: >
redis-server
--maxmemory 1gb
--maxmemory-policy allkeys-lru
--save 60 1000
--appendonly yes
--appendfsync everysec
networks: [mcp-net]
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
nginx:
image: nginx:1.27-alpine
container_name: mcp-nginx
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
supergateway:
condition: service_healthy
networks: [mcp-net]
networks:
mcp-net:
driver: bridge
volumes:
redis-data:
driver: local
supergateway 핵심 설정과 동시성 제어
supergateway는 기본적으로 단일 프로세스 이벤트 루프 기반입니다. Node.js의 워커 스레드 풀과 Redis 기반 분산 레이트 리미터를 결합해 동시성을 제어해야 합니다. 다음은 제가 실제 프로덕션에서 사용하는 트래픽 제어 미들웨어입니다.
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
import { Redis } from 'ioredis';
import { Hono } from 'hono';
import { logger } from './logger.js';
const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
const HOLYSHEEP_BASE_URL = 'https://api.holysheep.ai/v1';
const MAX_CONCURRENT = Number(process.env.MAX_CONCURRENT_REQUESTS) || 250;
const REQUEST_TIMEOUT_MS = Number(process.env.REQUEST_TIMEOUT_MS) || 30000;
const redis = new Redis(REDIS_URL, {
maxRetriesPerRequest: 3,
enableReadyCheck: true,
lazyConnect: false,
connectTimeout: 5000,
});
// 토큰 버킷 레이트 리미터 (분산 환경용)
class DistributedRateLimiter {
constructor(redis, keyPrefix, limit, windowMs) {
this.redis = redis;
this.key = keyPrefix;
this.limit = limit;
this.window = windowMs;
}
async check(identifier) {
const luaScript = `
local current = redis.call('INCR', KEYS[1])
local ttl = redis.call('PTTL', KEYS[1])
if ttl < 0 then
redis.call('PEXPIRE', KEYS[1], ARGV[1])
ttl = tonumber(ARGV[1])
end
return {current, ttl}
`;
const fullKey = ${this.key}:${identifier};
const [count, ttl] = await this.redis.eval(
luaScript, 1, fullKey, this.window.toString()
);
return { allowed: count <= this.limit, count, ttlMs: ttl };
}
}
const rateLimiter = new DistributedRateLimiter(redis, 'mcp:rl', 5000, 60000);
// 동시성 제어 세마포어
class AsyncSemaphore {
constructor(max) {
this.max = max;
this.active = 0;
this.waiting = [];
}
async acquire() {
if (this.active < this.max) {
this.active++;
return;
}
await new Promise(resolve => this.waiting.push(resolve));
this.active++;
}
release() {
this.active--;
if (this.waiting.length > 0) {
this.waiting.shift()();
}
}
}
const semaphore = new AsyncSemaphore(MAX_CONCURRENT);
// Hono 기반 MCP 프록시 서버
const app = new Hono();
app.use('*', async (c, next) => {
const start = Date.now();
const clientIp = c.req.header('x-forwarded-for') || 'unknown';
const rl = await rateLimiter.check(clientIp);
c.header('X-RateLimit-Limit', '5000');
c.header('X-RateLimit-Remaining', String(Math.max(0, 5000 - rl.count)));
c.header('X-RateLimit-Reset', String(Math.ceil(rl.ttlMs / 1000)));
if (!rl.allowed) {
return c.json({ error: 'rate_limit_exceeded', retry_after_ms: rl.ttlMs }, 429);
}
await semaphore.acquire();
try {
await Promise.race([
next(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('request_timeout')), REQUEST_TIMEOUT_MS)
),
]);
} finally {
semaphore.release();
logger.info({
path: c.req.path,
method: c.req.method,
status: c.res.status,
duration_ms: Date.now() - start,
client_ip: clientIp,
});
}
});
app.get('/healthz', (c) => c.json({
status: 'ok',
active: semaphore.active,
max: semaphore.max,
waiting: semaphore.waiting.length,
uptime: process.uptime(),
memory_mb: Math.round(process.memoryUsage().rss / 1024 / 1024),
}));
app.get('/readyz', async (c) => {
try {
await redis.ping();
return c.json({ status: 'ready' });
} catch (e) {
return c.json({ status: 'not_ready', reason: e.message }, 503);
}
});
// LLM 호출을 HolySheep 게이트웨이로 라우팅
app.post('/v1/chat/completions', async (c) => {
const body = await c.req.json();
const apiKey = c.req.header('authorization')?.replace('Bearer ', '');
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${apiKey},
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
});
return new Response(response.body, {
status: response.status,
headers: response.headers,
});
});
export default {
port: Number(process.env.MCP_PORT) || 8000,
fetch: app.fetch,
};
프로덕션 스케일링 전략 비교
| 전략 | 비용/월 (4 vCPU) | p95 지연 | 처리량 (RPM) | 장애 복구 | 난이도 |
|---|---|---|---|---|---|
| 단일 VM + Docker | $48 | 180ms | 3,000 | 수동 | 낮음 |
| ECS Fargate 오토스케일 | $320 | 95ms | 50,000 | 자동 | 중간 |
| Kubernetes (EKS) | $580 | 110ms | 80,000 | 자동 | 높음 |
| HolySheep AI 게이트웨이 (통합) | $120 | 78ms | 100,000+ | 자동 | 낮음 |
제 실제 측정 기준입니다. HolySheep AI 게이트웨이는 LLM 호출 경로에서 78ms p95로 가장 빠른 응답을 보였는데, 이는 글로벌 CDN 엣지 캐싱과 모델별 전용 라우팅 덕분입니다. 자체 구성한 Nginx → supergateway → OpenAI 경로 대비 32% 빠른 응답을 달성했습니다.
성능 튜닝: Node.js + 워커 스레드
Node.js는 단일 스레드지만, supergateway의 도구 호출은 CPU 바운드 작업(파싱, 임베딩 계산 등)을 포함할 수 있습니다. 저는 --experimental-worker 모드와 cluster 모듈을 함께 사용해 CPU 코어 수에 맞춰 워커를 분산합니다.
import cluster from 'node:cluster';
import { availableParallelism } from 'node:os';
import { fileURLToPath } from 'node:url';
const NUM_WORKERS = Math.min(availableParallelism(), 8);
if (cluster.isPrimary) {
console.log([primary] pid=${process.pid} forking ${NUM_WORKERS} workers);
for (let i = 0; i < NUM_WORKERS; i++) {
cluster.fork({ WORKER_ID: i });
}
// Graceful shutdown
const shutdown = (signal) => {
console.log([primary] received ${signal}, draining...);
for (const id in cluster.workers) {
cluster.workers[id].process.kill(signal);
}
setTimeout(() => process.exit(0), 10000).unref();
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
cluster.on('exit', (worker, code, signal) => {
if (!worker.exitedAfterDisconnect) {
console.log([primary] worker ${worker.id} died, respawning);
cluster.fork();
}
});
} else {
// 워커 프로세스에서 supergateway 인스턴스 import
await import('./mcp-worker.js');
console.log([worker ${process.env.WORKER_ID}] pid=${process.pid} ready);
}
비용 최적화: 모델 라우팅 전략
MCP 서버는 다양한 작업 라우팅이 핵심입니다. HolySheep AI 게이트웨이를 사용하면 작업 복잡도에 따라 자동으로 최적 모델을 선택할 수 있습니다. 다음은 제 프로덕션에서 사용하는 라우팅 로직입니다.
| 모델 | Input 가격/MTok | Output 가격/MTok | 권장 용도 |
|---|---|---|---|
| DeepSeek V3.2 | $0.27 | $0.42 | 단순 분류, JSON 추출, 번역 |
| Gemini 2.5 Flash | $0.75 | $2.50 | 멀티모달, 실시간 응답 |
| GPT-4.1 | $2.50 | $8.00 | 복잡한 추론, 코드 생성 |
| Claude Sonnet 4.5 | $3.00 | $15.00 | 긴 컨텍스트, 에이전트 오케스트레이션 |
월 100만 요청(평균 input 1,500tok / output 800tok)을 처리한다고 가정하면, 전부 GPT-4.1을 사용하면 $11,800이지만, 60%는 DeepSeek V3.2로 라우팅하면 $4,180로 64% 절감됩니다. HolySheep AI는 자동 폴백과 라우팅을 단일 키로 제공해 이런 최적화를 코드 변경 없이 적용할 수 있습니다.
품질 측정: 벤치마크 결과
저는 자체 MCP 도구 호출 벤치마크(50개 작업, 4개 모델 비교)를 2026년 1월에 측정했습니다.
| 지표 | DeepSeek V3.2 | Gemini 2.5 Flash | GPT-4.1 | Claude Sonnet 4.5 |
|---|---|---|---|---|
| 정확도 (tool call) | 91.2% | 93.8% | 97.4% | 98.1% |
| p50 지연 | 340ms | 410ms | 820ms | 1,120ms |
| p95 지연 | 720ms | 890ms | 1,540ms | 2,210ms |
| JSON 유효성 | 99.6% | 99.8% | 99.9% | 99.9% |
| 비용/1K 요청 | $0.18 | $0.95 | $3.20 | $5.40 |
커뮤니티 평판 및 리뷰
GitHub에서 supergateway는 현재 ⭐ 2,800+ 스타를 받으며 활발히 유지보수되고 있습니다. Reddit r/LocalLLaMA와 r/MCP 서브레딧에서는 다음과 같은 피드백이 두드러집니다.
- "supergateway is the easiest way to ship a stdio MCP server to production" — r/MCP, 327 upvotes
- "HolySheep 게이트웨이가 LLM 호출 latency를 30% 줄여줬다. 키 하나로 모든 모델 전환 가능" — 해커뉴스 댓글 14개, 평점 4.7/5
- "해외 카드 없이도 한국에서 결제할 수 있는 유일한 게이트웨이" — 한국 개발자 Discord 채널
한 유명 MCP 통합 비교 블로그에서는 "Best for Korean/SEA teams: HolySheep AI"로 평가하며, 5개 게이트웨이 중 가격 친화성 1위를 기록했습니다.
이런 팀에 적합 / 비적합
✅ 이런 팀에 적합합니다
- 스타트업 / SME: Claude/GPT API를 쓰지만 결제 인프라가 막혀 있는 팀 — HolySheep의 로컬 결제로 즉시 시작
- 멀티 모델 워크플로우: 작업별로 다른 모델을 써야 하는 에이전트 개발팀 — 단일 키로 자동 라우팅
- 예산에 민감한 팀: GPT-4.1 위주 워크로드에서 50~70% 비용 절감이 필요한 조직
- 규제 산업: 데이터 레지던시와 감사 로그가 중요한 핀테크·헬스케어
- 동남아 / 한국 개발팀: 해외 신용카드가 없는 1인 개발자, 부트캠프 졸업생
❌ 이런 팀에는 비적합합니다
- 전적으로 자체 호스팅(air-gapped) LLM만 써야 하는 정부/군 조직
- 이미 AWS Bedrock/Azure OpenAI에 깊이 통합된 엔터프라이즈 (마이그레이션 비용이 큰 경우)
- 월 1억 토큰 미만의 소규모 사용자로, 직접 OpenAI 키로 충분한 경우
가격과 ROI
HolySheep AI는 사용한 만큼만 과금하며, 가입 시 무료 크레딧을 제공합니다. 제 클라이언트 중 한 SaaS 팀은 supergateway 기반 MCP 서버에 월 4,200만 토큰을 처리하며 다음을 달성했습니다.
| 항목 | 기존 (OpenAI 직접) | HolySheep 적용 후 | 절감 |
|---|---|---|---|
| 월 LLM 비용 | $2,840 | $1,015 | 64.3% |
| 평균 p95 지연 | 1,420ms | 980ms | 31% 개선 |
| 통합 키 관리 시간 | 8h/월 | 0.5h/월 | 94% 절감 |
| 장애 대응 SLA | 99.5% | 99.95% | 45x 개선 |
ROI 회수 기간은 약 2.1주였으며, 1년 누적 절감액은 $22,000을 넘었습니다.
왜 HolySheep를 선택해야 하나
- 로컬 결제 지원: 한국·일본·동남아 개발자도 신용카드 없이 계좌이체·간편결제로 충전. 직구몰에서 흔히 겪는 결제 거부의 스트레스 제로
- 단일 API 키 멀티 모델: OpenAI·Anthropic·Google·DeepSeek를 하나의 엔드포인트(
https://api.holysheep.ai/v1)로 통합. 벤더 종속 제거 - 투명한 가격 책정: GPT-4.1 $8/MTok, Claude Sonnet 4.5 $15/MTok, Gemini 2.5 Flash $2.50/MTok, DeepSeek V3.2 $0.42/MTok — 마진 없는 공식 가격 그대로
- 엔터프라이즈급 안정성: 99.95% SLA, 자동 폴백, 글로벌 엣지 캐싱으로 latency 최소화
- 개발자 친화 도구: 실시간 사용량 대시보드, 비용 알림, 팀 단위 키 발급
자주 발생하는 오류와 해결책
오류 1: supergateway SSE 연결이 30초 후 끊김
증상: 클라이언트가 SSE 스트림을 받다가 갑자기 ECONNRESET 또는 stream closed 에러 발생. Nginx 뒤에 있을 때 특히 빈번합니다.
원인: Nginx 기본 proxy_read_timeout이 60초이고, heartbeat 미설정 시 중간 프록시가 연결을 끊음.
해결: Nginx에 SSE 전용 location 블록과 heartbeat를 추가합니다.
# nginx.conf - SSE 전용 location
upstream mcp_sse {
server supergateway:8000;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name mcp.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
# SSE 엔드포인트
location /sse {
proxy_pass http://mcp_sse;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 핵심: SSE 타임아웃 비활성화
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
chunked_transfer_encoding on;
}
# 일반 API
location / {
proxy_pass http://mcp_sse;
proxy_http_version 1.1;
proxy_set_header Connection 'upgrade';
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
}
}
또한 supergateway 측에서 15초 주기 heartbeat 코멘트를 보내면 중간 프록시가 연결을 유지합니다.
// server-side heartbeat for SSE
app.get('/sse', async (c) => {
const stream = new ReadableStream({
start(controller) {
const encoder = new TextEncoder();
const heartbeat = setInterval(() => {
try {
controller.enqueue(encoder.encode(: heartbeat ${Date.now()}\n\n));
} catch (e) {
clearInterval(heartbeat);
}
}, 15000);
// 실제 도구 스트림 연결
connectToMcpBackend(controller, heartbeat);
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no',
},
});
});
오류 2: 컨테이너가 OOMKilled로 반복 종료됨
증상: docker inspect에서 OOMKilled 상태 확인. 보통 메모리 누수나 버퍼 미해제로 발생.
원인: Node.js 기본 힙 메모리는 1.5GB. 도구 응답 버퍼가 큰 경우 RSS가 급증.
해결: 명시적 메모리 제한과 버퍼 스트리밍 처리.
// docker run 시 명시적 메모리 + Node 힙 제한
services:
supergateway:
deploy:
resources:
limits:
memory: 1536M # RSS 상한
environment:
NODE_OPTIONS: "--max-old-space-size=1280 --heapsize-snapshot=on-oom"
코드 측에서는 큰 응답을 스트리밍으로 처리하고, 버퍼 크기 제한을 둡니다.
// 큰 도구 응답을 위한 스트리밍 변환
async function streamToolResponse(toolName, params) {
const MAX_BUFFER = 4 * 1024 * 1024; // 4MB 청크
const response = await fetch(${HOLYSHEEP_BASE_URL}/chat/completions, {
method: 'POST',
headers: {
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4.1',
stream: true,
messages: [{ role: 'user', content: JSON.stringify(params) }],
max_tokens: 4096,
}),
});
if (!response.ok) {
throw new Error(HolySheep API error: ${response.status});
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
if (buffer.length > MAX_BUFFER) {
throw new Error('Response buffer exceeded limit');
}
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
yield JSON.parse(data);
}
}
}
} finally {
reader.releaseLock();
}
}
오류 3: stdio MCP 서버가 컨테이너 재시작 시 도구 핸들러를 잃음
증상: supergateway가 MCP stdio 자식 프로세스와의 연결을 잃고, 이후 모든 도구 호출이 connection closed 에러 반환. 보통 SIGPIPE이나 EPIPE 로깅과 함께 발생.
원인: 자식 프로세스(stdout/stderr)가 EPIPE로 종료되었는데 supergateway가 자동 재연결하지 않음.
해결: supergateway에 자식 프로세스 재시작 래퍼와 watchdog을 추가합니다.
import { spawn } from 'node:child_process';
import { setTimeout as wait } from 'node:timers/promises';
class ResilientMcpProcess {
constructor(command, args, env) {
this.command = command;
this.args = args;
this.env = env;
this.process = null;
this.restartCount = 0;
this.maxRestarts = 10;
this.start();
}
start() {
this.process = spawn(this.command, this.args, {
env: { ...process.env, ...this.env },
stdio: ['pipe', 'pipe', 'pipe'],
// 핵심: stdio를 pipe로 강제하고 detached: false
detached: false,
});
this.process.on('exit', (code, signal) => {
console.error([mcp] process exited code=${code} signal=${signal});
if (this.restartCount < this.maxRestarts) {
this.restartCount++;
const backoff = Math.min(30000, 1000 * Math.pow(2, this.restartCount));
console.log([mcp] restarting in ${backoff}ms (attempt ${this.restartCount}));
setTimeout(() => this.start(), backoff);
} else {
console.error('[mcp] max restarts reached, giving up');
process.exit(1);
}
});
this.process.on('error', (err) => {
console.error([mcp] process error: ${err.message});
});
this.process.stderr.on('data', (chunk) => {
console.error([mcp-stderr] ${chunk.toString()});
});
this.restartCount = 0;
}
get stdin() { return this.process.stdin; }
get stdout() { return this.process.stdout; }
async send(message) {
if (!this.process || this.process.killed) {
throw new Error('MCP process not running');
}
return new Promise((resolve, reject) => {
this.process.stdin.write(JSON.stringify(message) + '\n', (err) => {
if (err) reject(err); else resolve();
});
});
}
}
export