AI를 쓰다 보면 답답한 순간이 있다. "내 컴퓨터에 있는 파일 좀 읽어줘", "DB에서 데이터 좀 가져와줘" 하면 못한다. AI는 머리는 좋은데 손발이 없는 상태다.
그래서 나온 게 function calling이다. AI한테 "이런 함수들 쓸 수 있어"라고 알려주면, AI가 필요할 때 그 함수를 호출한다. 근데 문제는 각 AI 플랫폼마다 방식이 다르다는 거다. OpenAI는 OpenAI 방식, Anthropic은 Anthropic 방식. 같은 도구를 여러 AI에 연결하려면 매번 다시 만들어야 한다.
USB가 나오기 전을 생각해보면 이해가 쉽다. 그때는 프린터, 키보드, 마우스가 전부 다른 포트를 썼다. PS/2, 시리얼, 패러럴... 노트북 뒤에 포트가 엄청 많았다. USB가 나온 후에야 "아, 하나의 표준으로 다 연결할 수 있구나" 싶었다.
MCP가 바로 그거다. AI와 외부 도구를 연결하는 표준 프로토콜. 한 번 만들면 Claude든 다른 AI든 똑같이 쓸 수 있다. 이게 왜 중요한지, 어떻게 작동하는지 정리해봤다.
MCP는 무엇인가
MCP(Model Context Protocol)는 Anthropic이 만든 오픈 프로토콜이다. AI 애플리케이션(Host)과 외부 도구(Server) 사이의 통신 규약을 정의한다.
핵심은 표준화다. 예전에는 각 AI 앱이 자기만의 방식으로 도구를 연결했다. 이제는 MCP 프로토콜만 따르면 어떤 AI 앱이든 똑같이 연결된다.
구조는 이렇다:
Host (Claude Desktop)
↓
Client (MCP Client)
↓ JSON-RPC
Server (File System MCP Server)
- Host: 실제 AI 애플리케이션. Claude Desktop, VS Code, 커스텀 앱 등
- Client: Host 안에서 돌아가는 MCP 클라이언트. 프로토콜을 구현함
- Server: 실제 도구를 제공하는 서버. 파일 시스템, DB, API 등
비유하자면 전기 콘센트 같다. 콘센트(프로토콜)만 맞으면 어떤 전자제품(도구)이든 꽂을 수 있다. Host는 벽, Client는 콘센트, Server는 전자제품이다.
세 가지 기본 요소
MCP는 세 가지 primitive를 제공한다:
1. Tools (도구)
AI가 직접 호출할 수 있는 함수다. "파일 읽기", "DB 쿼리", "API 호출" 같은 것들.
// MCP Server에서 tool 정의
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "read_file",
description: "Read contents of a file",
inputSchema: {
type: "object",
properties: {
path: { type: "string" }
}
}
}]
};
});
// Tool 실행 핸들러
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "read_file") {
const content = await fs.readFile(request.params.arguments.path, 'utf-8');
return { content: [{ type: "text", text: content }] };
}
});
이게 function calling과 비슷해 보이지만, 차이는 표준화에 있다. 같은 tool을 OpenAI든 Claude든 그대로 쓸 수 있다.
2. Resources (리소스)
AI가 읽을 수 있는 데이터 소스다. 파일, DB 레코드, API 응답 등. Tool이 "동작"이라면 Resource는 "데이터"다.
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [{
uri: "file:///project/README.md",
name: "Project README",
mimeType: "text/markdown"
}]
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const content = await fetchResource(request.params.uri);
return { contents: [{ uri: request.params.uri, text: content }] };
});
생각해보면 AI는 context가 필요하다. "이 프로젝트에 대해"라고 하면 프로젝트 정보를 어디선가 가져와야 한다. Resource가 그걸 표준화한 거다.
3. Prompts (프롬프트)
재사용 가능한 프롬프트 템플릿이다. 자주 쓰는 작업을 미리 정의해둘 수 있다.
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return {
prompts: [{
name: "review_code",
description: "Review code for best practices",
arguments: [{
name: "file_path",
description: "Path to the code file",
required: true
}]
}]
};
});
이건 솔직히 나는 아직 잘 안 쓴다. Tool과 Resource가 더 직관적이다. 하지만 팀에서 공통 프롬프트를 공유할 때는 유용할 것 같다.
JSON-RPC로 통신한다
MCP는 JSON-RPC 2.0을 쓴다. 메시지 포맷이 간단하다:
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": { "path": "/project/README.md" }
}
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "# Project Title\n..." }]
}
}
전송 방식은 두 가지:
- stdio: 표준 입출력으로 통신. 로컬 프로세스 간 통신에 적합
- SSE (Server-Sent Events): HTTP 위에서 동작. 원격 서버 연결에 적합
대부분의 경우 stdio를 쓴다. Claude Desktop도 stdio로 MCP 서버를 띄운다. 간단하고 빠르다.
실제로 MCP 서버 만들기
간단한 파일 시스템 MCP 서버를 만들어봤다:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import fs from "fs/promises";
import path from "path";
const server = new Server({
name: "filesystem-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "read_file",
description: "Read contents of a file",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "File path to read" }
},
required: ["path"]
}
},
{
name: "list_directory",
description: "List files in a directory",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "Directory path" }
},
required: ["path"]
}
}
]
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "read_file") {
const content = await fs.readFile(args.path, "utf-8");
return {
content: [{ type: "text", text: content }]
};
}
if (name === "list_directory") {
const files = await fs.readdir(args.path);
return {
content: [{ type: "text", text: files.join("\n") }]
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
이걸 npm package로 만들고, Claude Desktop 설정에 추가하면 된다:
{
"mcpServers": {
"filesystem": {
"command": "node",
"args": ["/path/to/filesystem-server/index.js"]
}
}
}
이제 Claude에게 "내 프로젝트의 README.md 읽어줘"라고 하면 실제로 읽는다. 마법 같다.
MCP vs Function Calling vs Plugin
헷갈리는 부분이다. 셋 다 비슷해 보이는데 뭐가 다른가?
Function Calling은 개념이다. AI가 함수를 호출할 수 있다는 능력 자체를 말한다. OpenAI, Anthropic 모두 function calling을 지원한다. 하지만 구현 방식이 제각각이다.
Plugin은 플랫폼 종속적인 확장이다. ChatGPT Plugin은 OpenAI에서만 작동한다. 독점적이다.
MCP는 프로토콜이다. 표준이다. 한 번 만들면 어디서든 쓸 수 있다. HTTP가 웹 서버의 표준이듯, MCP는 AI 도구 연결의 표준을 목표로 한다.
비유하자면:
- Function calling = "전화를 걸 수 있다"는 개념
- Plugin = 특정 통신사의 전용 앱
- MCP = 전화 프로토콜 표준 (누구든 따르면 통화 가능)
실제 사용 사례
내가 쓰고 있는 MCP 서버들:
1. Filesystem MCP
로컬 파일을 읽고 쓴다. Claude가 내 프로젝트 파일을 직접 볼 수 있다.
2. PostgreSQL MCP
DB를 쿼리한다. "지난 주 가입한 유저 수" 같은 질문에 바로 답한다.
3. GitHub MCP
GitHub API를 호출한다. "최근 이슈 목록 보여줘", "PR 만들어줘" 같은 게 된다.
4. Serena MCP
코드베이스를 시맨틱하게 탐색한다. "이 함수를 호출하는 곳 찾아줘" 같은 질문이 정확해진다.
이게 다 표준 프로토콜이라서, 한 번 설정하면 모든 MCP Host에서 쓸 수 있다. 실제로 Claude Desktop에서 설정한 MCP 서버를 VS Code Extension에서도 그대로 쓴다.
생태계와 미래
Anthropic이 MCP를 오픈 소스로 공개했다. 이미 커뮤니티가 활발하다:
- 공식 SDK: TypeScript, Python
- Host 구현: Claude Desktop, VS Code (via extensions), 커스텀 앱
- Server 생태계: 50+ 공식 서버, 100+ 커뮤니티 서버
생각해보면 이게 AI 에이전트의 핵심이다. AI가 똑똑해지는 것도 중요하지만, 현실 세계와 연결되는 게 더 중요하다. 데이터를 읽고, 도구를 쓰고, 액션을 취할 수 있어야 한다.
MCP가 그걸 표준화한다. USB가 주변기기를 표준화했듯, MCP가 AI 도구를 표준화한다. 앞으로 모든 AI 앱이 MCP를 지원할 것 같다. 아니면 자기만의 폐쇄적인 시스템으로 남든가.
표준이 이기는 이유는 간단하다. 네트워크 효과다. MCP 서버가 많아지면 MCP를 지원하는 Host가 유리해진다. Host가 많아지면 서버를 만드는 게 매력적이다. 선순환이다.
정리
MCP를 이해하고 나니 AI 개발이 다르게 보인다. AI는 이제 고립된 모델이 아니다. 도구와 연결된 에이전트다.
핵심 정리:
- MCP는 AI와 도구를 연결하는 표준 프로토콜
- Tools, Resources, Prompts 세 가지 primitive 제공
- JSON-RPC로 통신, stdio 또는 SSE 전송
- 한 번 만들면 모든 Host에서 재사용 가능
- 생태계가 빠르게 성장 중
실제로 느낀 건, MCP 서버 만드는 게 생각보다 쉽다는 거다. 기존에 쓰던 라이브러리를 MCP로 감싸기만 하면 된다. 그러면 AI가 그 라이브러리를 쓸 수 있게 된다.
앞으로는 "AI와 통합" 기능이 기본이 될 것 같다. 모든 SaaS, 모든 도구가 MCP 서버를 제공할 거다. 그러면 AI는 진짜로 우리 업무 환경 전체와 연결된다.
생각해보면 이게 우리가 원했던 AI 에이전트의 모습이다. 대화만 하는 챗봇이 아니라, 실제로 일을 하는 동료. MCP가 그 길을 열어주고 있다.