MCP란?
Model Context Protocol(MCP)은 Claude가 외부 서비스에 안전하고 표준화된 방식으로 접근할 수 있게 해주는 프로토콜입니다. 한 번 만든 MCP 서버는 Claude Code, Claude Desktop, MCP를 지원하는 다른 IDE에서 그대로 사용할 수 있습니다.
트랜스포트 선택
세 가지 주요 트랜스포트가 있습니다.
| 트랜스포트 | 적합한 경우 | 인증 |
|---|---|---|
| HTTP | 원격 호스팅 SaaS, 다중 사용자 | OAuth, API 키 |
| stdio | 로컬 CLI 도구, 시스템 접근 | 환경변수, 토큰 |
| SSE | 일부 레거시 원격 서버 | OAuth |
신규 원격 서버는 보통 HTTP를 선택합니다.
시작 준비
필요한 것:
- Node.js 18 이상 (또는 Python 3.10 이상)
- Claude Code 설치 완료
- 연결하고 싶은 서비스나 데이터 소스
프로젝트 초기화 (TypeScript 예시)
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node
npx tsc --init
기본 구조
MCP 서버는 세 가지 요소로 구성됩니다.
- Tools — Claude가 호출할 수 있는 함수
- Resources — Claude가 읽을 수 있는 데이터 (파일·DB 행 등)
- Prompts — 미리 정의된 프롬프트 템플릿
대부분의 서버는 Tools만 있어도 충분합니다.
Tool 정의 (예: 날씨 조회)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({
name: "weather-server",
version: "1.0.0",
});
server.registerTool(
"get-weather",
{
title: "Get current weather",
description: "Returns current weather for a city",
inputSchema: {
city: z.string().describe("City name in English"),
},
},
async ({ city }) => {
const weather = await fetchWeather(city);
return {
content: [{ type: "text", text: JSON.stringify(weather, null, 2) }],
};
}
);
도구 설명(description)은 Claude가 도구를 언제 호출할지 결정하는 핵심 정보입니다. 명확하고 구체적으로 작성합니다.
stdio 트랜스포트로 실행
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const transport = new StdioServerTransport();
await server.connect(transport);
빌드 후 실행:
npx tsc
node dist/index.js
Claude Code에 연결
1. 로컬 stdio 서버 추가
claude mcp add weather -- node /absolute/path/to/dist/index.js
--scope user를 붙이면 모든 프로젝트에서, --scope project를 붙이면 .mcp.json을 통해 팀 전체와 공유할 수 있습니다.
2. 원격 HTTP 서버 추가
claude mcp add --transport http weather https://mcp.example.com/mcp
OAuth가 필요한 경우 /mcp 명령으로 인증 링크를 받습니다.
3. 프로젝트 단위 공유 (.mcp.json)
.mcp.json을 프로젝트 루트에 두면 팀원이 동일한 MCP 설정을 공유할 수 있습니다.
{
"mcpServers": {
"weather": {
"type": "http",
"url": "https://mcp.example.com/mcp"
}
}
}
Claude Desktop에 연결
Claude Desktop은 자체 설정 파일로 stdio MCP 서버를 로드합니다. Claude Code의 claude mcp add가 아니라 직접 JSON 파일을 편집해야 합니다.
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
파일이 없으면 새로 만듭니다.
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
}
}
저장한 후 Desktop 앱을 완전히 종료(Cmd+Q) 하고 다시 실행해야 적용됩니다. 창을 닫는 것만으로는 프로세스가 종료되지 않습니다. 다시 실행한 뒤 채팅 입력창 아래 도구 패널에서 노출된 도구 목록을 확인할 수 있습니다.
권한 민감한 도구 디자인
셸 실행·파일 시스템·외부 네트워크처럼 권한이 강한 작업을 노출할 때는 도구를 좁게 쪼개는 것이 핵심입니다. Claude Desktop의 권한 모델은 도구 단위 허용/거부이므로, 한 도구의 입력 스키마가 넓을수록 사용자가 “항상 허용”을 누르는 순간의 위험이 커집니다.
나쁜 예 — 범용 셸 노출
server.registerTool("run_shell", {
description: "Runs an arbitrary shell command",
inputSchema: { command: z.string() },
}, async ({ command }) => { /* ... */ });
이 한 도구는 ls부터 rm -rf·curl까지 모든 작업을 같은 권한 단위로 묶습니다. 사용자가 한 번 “항상 허용”을 누르면 사실상 무제한 셸 접근이 됩니다.
좋은 예 — 의도별 분리
server.registerTool("list_files", {
description: "Lists files in a directory",
inputSchema: { path: z.string() },
}, /* ... */);
server.registerTool("read_file", {
description: "Reads a text file",
inputSchema: { path: z.string() },
}, /* ... */);
server.registerTool("git_status", {
description: "Returns git status for the current repo",
inputSchema: {},
}, /* ... */);
각 도구가 좁은 입력 스키마를 가지므로 의도하지 않은 작업이 끼어들 여지가 줄고, 사용자가 도구별로 다른 허용 정책을 세울 수 있습니다.
설계 원칙
- 프롬프트가 아니라 스키마로 제약 —
description에 “위험한 명령 금지”를 적기보다 inputSchema에서 허용 범위를 좁힘 - 부수효과가 있는 도구(쓰기·실행)와 읽기 전용 도구를 이름과 반환값으로 명확히 구분
- 경로·URL 인자는 서버 측에서 화이트리스트로 한 번 더 검증 — Claude가 전달하는 값을 신뢰하지 않음
테스트
claude
/mcp
추가한 서버 상태와 노출된 도구 목록이 보입니다. 그 후 일반 대화로 도구를 호출하도록 요청해 동작을 확인합니다.
배포 패턴
- 공식 SaaS — 자체 도메인에 HTTP MCP 서버 배포 (예:
https://mcp.notion.com/mcp). 사용자가 OAuth로 인증. - 개인용 도구 — npm 패키지로 배포하고 README에
claude mcp add명령 제공. - 마켓플레이스 등록 — claude.com/plugins에 등록하면 발견성과 설치 경험이 크게 개선됩니다.
다음 단계
- Notion MCP, GitHub MCP, Slack MCP 구현을 참고하여 실전 패턴 학습
- MCP 공식 문서에서 Resources·Prompts·OAuth Provider 등 고급 기능 확인
- 토큰 사용량과 응답 속도가 중요하므로 도구 응답은 가능한 짧고 구조화된 JSON으로 반환