반응형

Claude Desktop, Cursor, Claude Code, Antigravity 등 최신 AI 에이전트 환경에서 외부 데이터베이스(PostgreSQL, Supabase), GitHub, 브라우저 제어, 사내 API를 연결할 때 MCP(Model Context Protocol)는 사실상 표준 인터페이스로 자리 잡았습니다. 하지만 처음 MCP 서버를 등록하거나 Node.js 버전 관리자(nvm, fnm, volta)를 사용하는 macOS·Linux·Windows 환경에서는 spawn npx ENOENT, Server disconnected, 혹은 JSON-RPC 파싱 에러로 인해 연결이 즉시 끊어지는 문제가 빈번하게 발생합니다.

이번 글에서는 1) 로컬(stdio) vs 원격(SSE / Streamable HTTP) MCP 아키텍처 및 운영 비용·보안 비교, 2) 가장 많이 발생하는 3대 연결 오류의 정확한 원인과 해결 명령어, 3) 실무 환경에서 API 키 유출과 프롬프트 인젝션을 막는 보안 설정 원칙을 정리합니다.


1. MCP 전송 방식(Transport) 비교: 로컬 stdio vs 원격 HTTP/SSE

MCP 서버는 구동 위치와 통신 채널에 따라 크게 로컬 프로세스(stdio) 방식과 클라우드·사내 서버에 배포하는 원격(SSE / Streamable HTTP) 방식으로 나뉩니다. 팀 단위 도입이나 클라우드 배포 전 아래 기준표를 먼저 확인하세요.

비교 항목 로컬 stdio 방식 원격 SSE / Streamable HTTP 권장 도입 환경
구동 원리 클라이언트 앱이 자식 프로세스(npx, uvx, docker)를 직접 실행 외부 클라우드 서버·컨테이너와 HTTPS/SSE 엔드포인트로 통신 개인 개발 PC는 stdio, 다수 팀원 공유는 원격 HTTP
인증 및 보안 로컬 설정 파일(JSON) 내 환경변수(env)로 토큰 전달 OAuth 2.1, Bearer Token, Cloudflare Access 등 API 게이트웨이 연동 사내 DB·클라우드 인프라는 중앙 인증 가능한 원격 권장
주요 장애 원인 GUI 앱의 PATH 환경변수 누락, stdout 일반 로그 출력 오염 리버스 프록시(Nginx/ALB) 타임아웃, CORS 및 버퍼링 설정 오류 방화벽·프록시 환경에서는 Keep-Alive 튜닝 필수
리소스 및 비용 서버 호스팅 비용 없음 (로컬 RAM/CPU 점유) 컨테이너/서버리스(Cloud Run, AWS Fargate, Workers) 호스팅 비용 발생 트래픽에 맞춰 오토스케일링 서버리스 활용 시 비용 최소화
반응형

2. MCP 서버 연결 실패 3대 오류 원인과 해결법

① Error: spawn npx ENOENT / spawn uvx ENOENT 해결

원인: macOS나 Linux에서 터미널(zsh/bash)을 열 때는 .zshrc가 로드되어 nvm이나 homebrew 경로가 잡히지만, GUI 애플리케이션(Claude Desktop, Cursor 등)을 아이콘으로 실행하면 기본 시스템 경로(/usr/bin:/bin)만 참조하기 때문에 npx나 node 실행 파일을 찾지 못해 발생합니다.

해결 방법: 터미널에서 which npx와 which node로 절대 경로를 확인한 뒤, 설정 JSON의 command에 절대 경로를 적고 env.PATH를 명시적으로 주입하면 100% 해결됩니다.

{
  "mcpServers": {
    "postgres-db": {
      "command": "/opt/homebrew/bin/npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-postgres",
        "postgresql://readonly_user:password@localhost:5432/prod_db"
      ],
      "env": {
        "PATH": "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
      }
    }
  }
}

Windows 환경에서 동일한 오류가 난다면 "command": "cmd", "args": ["/c", "npx", "-y", "@modelcontextprotocol/..."] 형태로 래핑하면 정상 구동됩니다.

② SyntaxError: Unexpected token / JSON-RPC 파싱 오류로 즉시 종료될 때

원인: stdio 기반 MCP 서버는 표준 출력(stdout)을 오직 JSON-RPC 2.0 메시지 통신 채널로만 사용합니다. 만약 직접 작성한 MCP 서버 코드나 서드파티 라이브러리에서 console.log("DB connected")나 dotenv 배너를 stdout에 출력하면 클라이언트가 이를 깨진 JSON으로 인식해 즉시 연결을 끊습니다.

해결 방법: 모든 디버그 로그는 반드시 표준 에러(stderr)로 출력해야 합니다.

// 잘못된 예시 (stdio JSON-RPC 스트림을 깨뜨림)
console.log("Starting MCP Server...");

// 올바른 예시 (stderr로 출력하여 프로토콜 간섭 방지)
console.error("[MCP] Starting server on stderr...");

③ 원격 SSE / Streamable HTTP 연결이 60초마다 끊기는 현상

원인: Nginx, Cloudflare, AWS ALB 뒤에 원격 MCP 서버를 배치했을 때, 프록시 버퍼링(proxy_buffering)이 켜져 있거나 기본 유휴 타임아웃(60초)이 적용되어 스트리밍 연결이 강제 종료되는 경우입니다.

해결 방법: 리버스 프록시 설정에서 SSE 엔드포인트의 버퍼링을 끄고 타임아웃을 확장합니다.

location /mcp/ {
    proxy_pass http://127.0.0.1:8080/mcp/;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 3600s;
    chunked_transfer_encoding off;
}
반응형

3. 실무 도입 시 반드시 지켜야 할 MCP 보안 및 비용 최적화 체크리스트

  • 최소 권한 계정(Read-Only) 분리: 데이터베이스나 클라우드 인프라 MCP 서버를 연결할 때는 절대 superuser나 루트 API 키를 넣지 마세요. 조회 전용(SELECT 권한만 부여된 DB 계정)으로 연결해야 에이전트의 오작동이나 프롬프트 인젝션으로 인한 데이터 유실을 원천 차단할 수 있습니다.
  • Docker 컨테이너 샌드박싱: 검증되지 않은 외부 오픈소스 MCP 패키지를 로컬에서 바로 npx -y로 실행하면 호스트 파일시스템(`~/.ssh`, `~/.aws` 등)에 접근할 수 있습니다. 민감한 환경에서는 docker run -i --rm --network none 또는 필요한 디렉토리만 마운트(`-v`)하여 격리 실행하는 것이 안전합니다.
  • 불필요한 MCP 서버 비활성화로 토큰 절약: 활성화된 MCP 서버가 많을수록 매 대화 턴마다 수십 개의 도구 스키마(Tool Definitions)가 시스템 프롬프트에 포함되어 수천 토큰이 기본 차감됩니다. 현재 프로젝트에 쓰지 않는 MCP 서버는 비활성화해 두어야 응답 속도와 API 비용을 동시에 아낄 수 있습니다.
반응형
Posted by Real_G