MCP(Model Context Protocol) 실전 가이드 — AI 에디터에 도구를 붙이는 방법

April 15, 2026

MCP 실전 가이드

생성형 AI가 코드를 작성하고 리팩터링하는 시대에, “우리 팀의 내부 API 문서”, “사내 지식 DB”, “운영 중인 대시보드”, “특정 CLI 도구”까지 한 번에 묶어 주는 것이 새로운 병목이 되었다. REST로 열어두면 보안이 걱정되고, 매번 프롬프트에 붙여 넣기면 컨텍스트 한도가 터진다. 이런 틈을 메우기 위해 등장한 표준 중 하나가 바로 Model Context Protocol(MCP)이다. 이 글은 MCP가 무엇인지, 어떻게 동작하는지, 그리고 실제로 서버를 만들어 에디터에 붙일 때 무엇을 조심해야 하는지를 실무에서 바로 쓸 수 있는 수준으로 정리한다.

MCP의 위치: 왜 또 다른 프로토콜인가

대부분의 LLM 애플리케이션은 “텍스트 입력 → 모델 → 텍스트 출력”이라는 단순한 파이프라인으로 시작한다. 하지만 실제 제품에서는 다음이 필요하다.

  • 최신 정보: 사내 위키나 티켓 시스템의 내용을 반영해야 한다.
  • 실행: 빌드, 테스트, 배포 스크립트를 안전한 범위에서 호출해야 한다.
  • 탐색: 거대한 저장소에서 특정 심볼만 찾아 요약해야 한다.

이미 “함수 호출(Function Calling)”이나 “Tool Use”라는 개념은 OpenAI, Anthropic 등 여러 API에서 제공한다. 하지만 호스트 애플리케이션(에디터, 데스크톱 앱, 자체 에이전트) 입장에서는, 도구를 어떻게 노출하고, 어떤 형식으로 주고받고, 어떻게 권한을 분리할지에 대한 공통 언어가 없으면 매 통합마다 제각각 구현해야 한다.

MCP는 이런 도구·리소스·프롬프트 힌트를 표준화하는 프로토콜이다. JSON-RPC 스타일의 메시지로 “어떤 도구가 있는지”, “어떤 인자를 받는지”, “실행 결과를 어떻게 돌려줄지”를 정의한다. 덕분에 한 번 MCP 서버를 만들면, 여러 MCP 호스트 클라이언트에서 재사용할 수 있다는 점이 기대 효과다.

기존 방식과의 차이

접근 장점 한계
긴 프롬프트에 문서 붙여넣기 구현 단순 토큰 낭비, 최신성 없음, 민감 정보 유출 위험
REST 래핑 API 익숙한 패턴 에디터별 통합 코드 반복, 세밀한 권한 모델 부족
MCP 서버 표준화, 재사용, 호스트·도구 분리 서버 설계·배포·보안 책임이 개발자에게 있음

MCP는 “은총알”이 아니다. 팀이 정리한 도구 경계와 보안 정책이 없으면, 아무리 표준화해도 난장판이 된다. 그래서 이 글 후반부에는 보안·운영 체크리스트를 비중 있게 두었다.

구성 요소: 호스트, 클라이언트, 서버

용어가 혼동되기 쉬우니, 역할을 먼저 고정한다.

  1. 호스트(Host)
    사용자가 상호작용하는 애플리케이션이다. 예를 들어 Cursor, Claude Desktop, 자체 제작한 AI 채팅 앱 등이 여기에 해당한다. 호스트는 MCP 클라이언트를 내장하고, 사용자 세션과 UI를 담당한다.
  2. MCP 클라이언트
    호스트 안에서 동작하며, MCP 서버와 통신한다. “어떤 도구 목록이 있는지 물어보기”, “특정 도구 실행 요청”, “리소스 읽기” 같은 작업을 프로토콜에 맞게 보낸다.
  3. MCP 서버
    실제로 도구(Tools), 리소스(Resources), 프롬프트(Prompts) 를 제공하는 프로세스다. Node, Python, Go 등으로 구현할 수 있고, stdio(표준 입출력)나 HTTP(S) 등 전송 방식을 선택할 수 있다(호스트·SDK에 따라 지원 범위가 다름).

직관적으로 말하면, 서버는 “능력을 노출하는 쪽”, 클라이언트는 “그 능력을 AI에게 연결하는 쪽”이다. 사용자가 보는 채팅 UI는 호스트가 책임진다.

도구(Tools), 리소스(Resources), 프롬프트(Prompts)

MCP에서 자주 쓰이는 세 가지 추상화를 정리한다.

도구(Tools)

모델이 호출할 수 있는 함수에 가깝다. 이름, 설명, JSON Schema 형태의 입력 스키마를 등록한다. 예를 들어 search_internal_wiki 도구는 query 문자열을 받아 관련 문서 요약을 반환할 수 있다.

도구 설계 시 실무 팁은 다음과 같다.

  • 입력 스키마를 좁게: “문자열 하나”로 때우기 쉽지만, 모델이 헷갈리면 잘못된 인자를 넣는다. 열거형(enum), 기본값, 최대 길이 등을 명시한다.
  • 멱등성: 같은 입력이면 같은 결과가 나오게 할 수 있는지 검토한다. 외부 부작용이 크면 “dry-run” 도구를 별도로 둔다.
  • 에러 메시지: 모델이 다음 행동을 정하도록, 사람이 읽기 좋은 한국어/영문 메시지와 함께 재시도 가능 여부를 적어 준다.

리소스(Resources)

파일, 문서, URL 같은 참조 가능한 데이터를 노출한다. “이 경로의 스펙 문서를 읽어라”처럼, 모델이 필요할 때 내용을 가져가는 용도다. 도구와의 차이는, 리소스는 읽기 중심인 경우가 많다는 점이다(구현에 따라 쓰기도 가능하지만 정책을 엄격히 할 것).

프롬프트(Prompts)

재사용 가능한 프롬프트 템플릿을 등록할 수 있다. 팀에서 자주 쓰는 “코드 리뷰 체크리스트”, “장애 보고서 초안” 같은 것을 표준화할 때 유용하다.

전송 방식: stdio와 원격

초기에 많이 쓰이는 방식은 stdio다. 호스트가 MCP 서버 프로세스를 띄우고, 표준 입출력으로 JSON-RPC 메시지를 주고받는다. 장점은 방화벽 안에서도 동작하기 쉽고, 로컬 개발이 간단하다는 것이다. 단점은 프로세스 생명주기 관리동시 접속이 호스트 구현에 의존한다는 점이다.

원격(HTTP/SSE 등) 전송은 팀 단위로 중앙 MCP 서버를 두고 싶을 때 고려한다. 이때는 인증·TLS·레이트 리밋이 필수이고, 서버를 인터넷에 노출할지, VPN 뒤에 둘지를 아키텍처 차원에서 결정해야 한다.

TypeScript로 최소 MCP 서버 만들기 (개념 예제)

아래는 개념을 잡기 위한 의사 코드에 가까운 예시다. 실제 SDK 버전에 따라 import 경로와 클래스 이름이 다를 수 있으므로, 공식 문서와 사용 중인 호스트의 예제를 함께 보라. 중요한 것은 “무엇을 export하는가”이다.

// 개념 예시: 실제 프로젝트에서는 공식 @modelcontextprotocol/sdk 등을 사용한다.
// 1) 서버 인스턴스 생성
// 2) list_tools 로 도구 목록 노출
// 3) call_tool 로 분기 처리
// 4) stdio transport 로 호스트에 연결

// 의사 코드
/*
server.registerTool({
  name: "add_internal_comment",
  description: "내부 이슈에 코멘트를 남긴다. dryRun이 true면 실제 기록하지 않는다.",
  inputSchema: {
    type: "object",
    properties: {
      issueId: { type: "string" },
      body: { type: "string", maxLength: 4000 },
      dryRun: { type: "boolean", default: true },
    },
    required: ["issueId", "body"],
  },
  handler: async ({ issueId, body, dryRun }) => {
    if (dryRun) {
      return { content: [{ type: "text", text: `[dry-run] Would comment on ${issueId}` }] };
    }
    // await issueTracker.postComment(issueId, body);
    return { content: [{ type: "text", text: "ok" }] };
  },
});
*/

실무에서는 다음을 추가한다.

  • 로깅: 어떤 도구가 어떤 인자로 호출되었는지(PII 마스킹 후)
  • 타임아웃: 외부 API가 느릴 때 무한 대기 방지
  • 레이트 리밋: 동일 사용자·동일 IP 기준 호출 제한

Cursor 등 에디터와의 연동에서 생기는 실제 문제

많은 개발자가 MCP를 처음 쓸 때 “로컬에서는 되는데 동료는 안 된다”는 상황을 겪는다. 흔한 원인은 다음과 같다.

  1. 실행 파일 경로: Node 버전 매니저(nvm, fnm)를 쓰면, GUI 앱이 로그인 셸 환경변수를 못 읽어 node를 찾지 못하는 경우가 있다. 이때는 절대 경로로 node를 지정하거나, 래퍼 스크립트를 둔다.
  2. 권한: 사내 인증서, VPN, AWS SSO 토큰이 터미널 세션에만 있는 경우가 있다. MCP 서버 프로세스가 같은 권한을 갖지 못하면 API 호출이 실패한다.
  3. 민감 정보: 도구 응답에 토큰, 개인정보가 섞이면 모델 컨텍스트에 남는다. 마스킹·필터링 레이어를 서버 측에 둔다.

팀 규칙 예시

  • 프로덕션 DB에 직접 쓰기 도구는 금지하고, 읽기 전용 복제본에만 연결한다.
  • 삭제·배포 류 도구는 별도 프로파일에서만 활성화한다.
  • 모든 도구 호출은 감사 로그를 남긴다(누가, 언제, 무엇을).

보안: MCP는 “새로운 공격면”이다

MCP를 도입하면 편해지는 만큼, 에이전트가 할 수 있는 일의 범위가 넓어진다. 고려해야 할 위협은 다음과 같다.

  1. 프롬프트 인젝션과 도구 남용
    사용자 입력에 악의적인 지시가 섞이면, 모델이 의도하지 않은 도구를 호출할 수 있다. 완화책으로는 도구별 허용 목록, 2단계 확인(사용자 승인), 읽기 전용 모드가 있다.
  2. 과도한 데이터 유출
    “내부 문서 검색” 도구가 너무 많은 컨텍스트를 한 번에 반환하면, 모델 제공자 측 로그나 다른 세션으로 이어질 위험을 평가해야 한다(특히 클라우드 모델 사용 시).
  3. 서플라이 체인
    npm 패키지로 배포된 MCP 서버를 그대로 믿지 말고, 소스 검토, 고정 버전, 내부 레지스트리 미러를 검토한다.
  4. 권한 최소화
    서버 프로세스 OS 사용자, 클라우드 IAM 역할, DB 계정 모두 최소 권한 원칙을 적용한다.

운영 관점: 관측 가능성과 장애 대응

MCP 서버도 결국 하나의 마이크로서비스처럼 다루는 것이 안전하다.

  • 헬스체크: 프로세스가 살아 있는지, 의존 API가 응답하는지
  • 메트릭: 도구 호출 수, 지연, 에러율, 타임아웃 비율
  • 알림: 특정 도구의 에러율이 급증하면 페이지

특히 외부 API 의존이 큰 도구는, 장애 시 우아한 실패(graceful degradation) 메시지를 반환하도록 설계한다. “지금 이슈 트래커가 응답하지 않습니다. 5분 후 다시 시도하세요” 같은 문장은 모델이 사용자에게 전달하기 좋다.

설계 패턴: 도구를 작게 쪼개기

초보자가 자주 하는 실패는 하나의 거대한 도구에 모든 것을 넣는 것이다. 예를 들어 do_everything_for_deploy 같은 도구는 모델이 언제 어떤 단계에서 실패했는지 알기 어렵다. 대신 다음처럼 나눈다.

  • get_latest_commit_sha
  • run_ci_status_check
  • create_release_tag (승인 후에만)

이렇게 하면 디버깅, 권한 분리, 감사가 모두 쉬워진다.

팀 도입 로드맵 제안

  1. 1주차: 읽기 전용 도구만(문서 검색, 스키마 조회). 로그 수집.
  2. 2주차: 팀원 피드백으로 스키마·설명문 개선. 잘못된 호출 패턴 분석.
  3. 3주차: 제한된 쓰기 도구(티켓 코멘트 dry-run → 실제).
  4. 4주차: 배포·운영 도구는 별도 프로파일과 승인 플로우.

무리하게 처음부터 “모든 자동화”를 넣으면, 보안 사고나 운영 부담만 커진다.

MCP만으로 해결되지 않는 것

  • 나쁜 데이터 품질: 위키가 엉망이면 검색 도구도 엉망이다.
  • 조직 절차: 코드 리뷰, 변경 관리 프로세스가 없으면 도구만 늘어난다.
  • 모델 한계: 도구가 완벽해도 추론 실패는 남는다. 평가 세트를 준비할 것.

정리

MCP는 AI 에디터와 사내 시스템 사이의 접착제로 이해하면 쉽다. 표준 프로토콜이 있기 때문에, 호스트가 바뀌어도 서버를 재사용할 여지가 생긴다. 하지만 도구 경계 설계, 권한, 로깅, 팀 규칙 없이 도입하면 “편의를 위한 새로운 단일 장애점”이 될 수 있다. 이 글이 MCP 서버를 처음 설계하거나, Cursor에 사내 도구를 안전하게 붙이려는 팀에게 실전 체크리스트 역할을 하길 바란다.

참고로 알아두면 좋은 것들

  • 사용 중인 호스트가 지원하는 전송 방식(stdio / remote)인증 모델
  • 공식 SDK 릴리스 노트(브레이킹 체인지)
  • 사내 보안팀의 클라우드 LLM 사용 정책

MCP는 도구다. 어떤 일을 자동화할지, 어디까지 허용할지를 팀이 먼저 합의하는 것이, 기술보다 앞선다.

프로토콜 메시지 흐름을 한 번 더 짚기

실제 구현을 읽을 때 헷갈리는 부분이 “언제 무엇이 오가는가”다. 대략적인 순서는 다음과 같다(호스트·SDK에 따라 세부는 다를 수 있다).

  1. 클라이언트 초기화: 클라이언트가 서버에 연결하고, 서버가 지원하는 기능(capabilities)을 서로 교환한다.
  2. 도구 목록 조회: tools/list 또는 이에 상응하는 요청으로 도구 이름·설명·입력 스키마를 받는다.
  3. 도구 호출: 모델이 특정 도구를 선택하면 tools/call 형태로 인자가 전달되고, 서버는 구조화된 결과를 돌려준다.
  4. 리소스·프롬프트: 필요 시 리소스 URI 목록, 프롬프트 템플릿을 추가로 조회한다.

여기서 중요한 점은, 모델이 “도구를 호출했다”는 사실은 호스트 쪽에서도 관찰 가능해야 한다는 것이다. 그래서 운영 팀은 호스트 로그와 MCP 서버 로그를 상관관계(correlation id) 로 묶는 것을 권장한다. 예를 들어 사용자 세션 ID나 요청 ID를 도구 핸들러 인자에 넣지는 않더라도, 로그 한 줄에 같은 ID를 남겨 추적 가능하게 한다.

JSON-RPC 스타일 사고방식

MCP는 내부적으로 JSON 메시지를 주고받는다. 직접 패킷을 만질 일은 SDK가 줄여 주지만, 장애 분석할 때는 “요청 ID”, “에러 객체”, “부분 실패”를 이해해야 한다.

  • 요청 ID: 비동기 환경에서 응답 매칭에 쓰인다. 로그에 요청 ID를 남기면 재현이 쉽다.
  • 에러 객체: HTTP가 아니라도, 애플리케이션 수준에서 code, message, data를 구조화해 돌려줄 수 있다. 사용자에게 보여 줄 문구와, 개발자가 디버깅할 상세는 분리하는 것이 좋다.
  • 스트리밍: 일부 호스트는 긴 응답을 스트리밍한다. 도구 구현 시 “중간 청크”와 “최종 요약”을 어떻게 나눌지 미리 정한다.

Cursor에서의 설정 개략 (개념)

Cursor를 예로 들면, 사용자는 설정 파일에 MCP 서버 실행 커맨드를 등록한다. 정확한 키 이름과 스키마는 제품 업데이트로 바뀔 수 있으므로 공식 문서를 항상 우선하라. 여기서는 “무엇을 채워야 하는지” 수준만 적는다.

  • command: node 혹은 npx, uvx 등 실행 파일
  • args: 엔트리 스크립트 경로, --port 같은 인자
  • env: API 키, NODE_ENV, 사내 프록시 URL
  • cwd: 모노레포 루트를 지정해야 상대 경로가 맞는 경우

자주 발생하는 실수는 상대 경로를 앱이 실행되는 cwd 기준으로 해석한다는 점이다. 팀 저장소 루트에 mcp-servers/wiki가 있다면, cwd를 명시하거나 절대 경로를 쓴다.

장애 분석 체크리스트

현장에서 바로 쓸 수 있는 순서다.

  1. 프로세스가 뜨는가: 호스트가 서버를 기동했는지, 즉시 크래시하는지 확인한다.
  2. stdio 버퍼링: 로그가 안 보인다고 해서 없는 것이 아닐 수 있다. 표준 에러로 로깅하고, 버퍼링 이슈를 의심한다.
  3. 네트워크: 원격 MCP라면 TLS 핸드셰이크, 프록시, DNS를 순서대로 점검한다.
  4. 권한 토큰: OAuth 토큰 만료, AWS 자격 증명 갱신 실패.
  5. 의존 서비스 레이트 리밋: 외부 API가 429를 반환하면 도구는 실패하고, 모델은 다른 전략을 시도한다. 백오프(backoff)캐시를 도입한다.

팀에서 자주 묻는 질문

Q. REST로 다 할 수 있는데 MCP가 꼭 필요한가?
A. 필수는 아니다. 다만 에디터·에이전트 생태계에서 재사용과 표준화를 노린다면 MCP가 유리한 경우가 많다. 이미 REST 게이트웨이가 잘 갖춰져 있고, 통합 대상이 한두 개라면 굳이 옮기지 않아도 된다.

Q. 사내 규정상 모델은 온프레미스만 쓴다. MCP는 괜찮은가?
A. MCP는 프로토콜이지 특정 클라우드가 아니다. 다만 호스트가 외부로 데이터를 보내는지, MCP 서버 응답이 로그에 남는지는 데이터 분류 정책과 함께 검토해야 한다.

Q. 도구가 너무 많으면 모델이 헷갈리지 않나?
A. 그렇다. 그래서 프로파일별로 서버를 나누거나, 세션 시작 시 필요한 도구만 활성화하는 패턴이 나온다. “전부 켜기”는 편해 보여도 품질을 떨어뜨린다.

Q. 테스트는 어떻게 하나?
A. 도구 핸들러는 순수 함수에 가깝게 분리하고, 단위 테스트로 입력 스키마 검증·외부 API 모킹을 한다. 통합 테스트는 실제 MCP 클라이언트를 띄우기보다, 서버를 직접 호출하는 스크립트로 스모크 테스트하는 팀도 많다.

Q. 버전 업으로 도구 스키마가 바뀌면?
A. 시맨틱 버저닝을 도구 이름에 반영하거나(search_wiki_v2), 마이그레이션 기간 동안 구버전을 병행 제공한다. 모델이 예전 스키마로 호출하는 혼선을 줄이려면, 설명문(description)에 “deprecated, 대신 X 사용”을 명확히 쓴다.

사례 연구: “문서 검색” 도구를 도입할 때의 의사결정

가장 흔한 첫 도구는 내부 문서 검색이다. 이때 팀은 보통 다음 갈림길에 선다.

  1. 전체 본문을 그대로 반환할 것인가, 요약과 링크만 줄 것인가. 전자는 구현은 쉽지만 토큰 비용과 유출 면적이 커진다.
  2. 검색 엔진을 무엇으로 둘 것인가. Elasticsearch, OpenSearch, 사내 Solr, 혹은 벡터 DB와의 하이브리드.
  3. 접근 제어를 어디서 할 것인가. MCP 서버가 사용자 신원을 어떻게 알 것인지(호스트가 헤더로 넘겨주는지, SSO 토큰을 검증하는지)를 정하지 않으면, “내부용”이라는 말이 공허해진다.

이 세 가지를 문서화한 뒤에야, 검색 품질 튜닝과 프롬프트 개선이 의미 있는 반복이 된다. 기술만으로는 부족하고, 정보 설계와 권한 모델이 함께 가야 한다.

마무리: “표준”은 책임을 줄여 주지 않는다

MCP는 통합 비용을 낮출 수 있는 좋은 방향이다. 하지만 무엇을 노출할지는 여전히 우리의 몫이다. 작은 도구부터, 읽기부터, 로그부터 시작해 점진적으로 확장하라. 그리고 항상 한 가지를 기억하라. 가장 위험한 것은 강력한 도구가 아니라, 감사 없는 도구다.


Written by Jeon Byung Hun 개발을 즐기는 bottlehs - Engineer, MS, AI, FE, BE, OS, IOT, Blockchain, 설계, 테스트