이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- MCP는 모델 기능인가, 애플리케이션 통합 프로토콜인가?
- host, client, server는 각각 어떤 역할을 하는가?
- tool, resource, prompt는 어떻게 구분되는가?
- function calling과 MCP는 어떤 관계인가?
개요
MCP는 Model Context Protocol의 약자다. 공식 문서는 MCP를 AI 애플리케이션이 외부 도구와 데이터 소스에 연결되는 표준 방식으로 설명한다. 모델 자체를 바꾸는 기술이라기보다, 모델을 사용하는 host 애플리케이션이 MCP server를 통해 도구와 리소스를 발견하고 사용하는 프로토콜이다.
백엔드 개발자 관점에서 MCP는 “AI에게 사내 API를 어떻게 안전하게 연결할 것인가”라는 문제다.
왜 백엔드 개발자에게 중요한가
LLM은 기본적으로 외부 시스템을 직접 알지 못한다. 주문 상태, 사내 문서, Jira 티켓, DB 레코드, 배포 상태를 알려면 애플리케이션이 정보를 가져와 context로 넣거나 tool을 통해 행동하게 해야 한다.
기능이 작을 때는 provider의 function calling만으로 충분할 수 있다. 그러나 도구가 많아지고, 여러 host가 같은 도구를 공유하고, 사내 시스템을 표준 방식으로 연결하고 싶어지면 MCP 같은 프로토콜이 중요해진다.
핵심 원리
MCP 구조는 크게 다음으로 이해한다.
- host: 사용자가 만나는 AI 애플리케이션이다. 예를 들어 IDE, 데스크톱 앱, 내부 챗봇이 될 수 있다.
- client: host 안에서 MCP server와 통신하는 구성 요소다.
- server: tool, resource, prompt를 제공하는 프로세스나 서비스다.
- tool: 실행 가능한 함수나 작업이다.
- resource: 읽을 수 있는 데이터다.
- prompt: 재사용 가능한 프롬프트 템플릿이다.
중요한 점은 MCP server가 모델과 직접 같은 것이 아니라는 점이다. MCP server는 외부 능력을 제공하고, host/client가 이를 모델 호출 흐름과 연결한다.
Function Calling과의 차이
function calling은 모델 provider가 제공하는 모델 호출 기능이다. 모델에게 “이런 함수들을 호출할 수 있다”고 알려주면, 모델이 어떤 함수를 어떤 인자로 호출할지 구조화해 반환한다.
MCP는 도구와 리소스를 제공하는 외부 프로토콜이다. host는 MCP server에서 tool 목록을 받고, 모델에게 도구 schema를 알려주고, 모델이 호출을 선택하면 실제 MCP server에 요청을 보낼 수 있다.
정리하면 다음과 같다.
| 구분 | function calling | MCP |
|---|---|---|
| 위치 | 모델 provider API 기능 | host와 tool server 사이 프로토콜 |
| 관심사 | 모델이 어떤 도구를 호출할지 표현 | 도구와 리소스를 표준 방식으로 제공 |
| 종속성 | provider별 API 차이 | MCP 구현과 server 계약 |
| 백엔드 관심 | schema와 응답 검증 | 인증, 권한, 배포, 감사, tool 안정성 |
코드로 이해하기
다음은 TypeScript MCP server에서 간단한 read-only tool을 노출하는 형태의 예시다. 실제 SDK API는 버전에 따라 달라질 수 있으므로 공식 SDK 문서를 확인해야 한다.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "internal-docs",
version: "1.0.0",
});
server.tool(
"search_policy",
{
query: z.string().min(2),
tenantId: z.string(),
},
async ({ query, tenantId }) => {
const results = await searchPolicies({ query, tenantId });
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "ok",
results: results.map((item) => ({
sourceId: item.sourceId,
title: item.title,
excerpt: item.excerpt,
})),
}),
},
],
};
},
);
await server.connect(new StdioServerTransport());이 예시에서 중요한 것은 tool 이름과 입력 schema가 모델과 host가 이해하는 계약이 된다는 점이다. 실제 기업 환경에서는 tenantId를 클라이언트 입력으로 그대로 믿지 않고 인증 토큰에서 확인해야 한다.
LLM agent가 MCP tool을 호출하는 흐름은 다음처럼 나뉜다.
async function runAgentTurn(userQuestion: string) {
const tools = await mcpClient.listTools();
const modelDecision = await llm.complete({
messages: [{ role: "user", content: userQuestion }],
tools: tools.map(toModelToolSchema),
});
if (modelDecision.type !== "tool_call") {
return modelDecision.content;
}
const tool = tools.find((item) => item.name === modelDecision.toolName);
if (!tool) {
throw new Error("unknown tool requested by model");
}
await authorization.requireToolScope({
actorId: currentUser.id,
toolName: tool.name,
});
const result = await mcpClient.callTool({
name: tool.name,
arguments: modelDecision.arguments,
});
auditLogger.info({
event: "mcp.tool.called",
actorId: currentUser.id,
toolName: tool.name,
status: result.status,
});
return llm.complete({
messages: [
{ role: "user", content: userQuestion },
{ role: "tool", content: JSON.stringify(result) },
],
});
}핵심은 모델의 tool call 결정을 그대로 실행하지 않는 것이다. host는 tool 존재 여부, 사용자 권한, 감사 로그, timeout, 결과 크기 제한을 확인한 뒤 MCP server를 호출한다.
백엔드 설계 판단
MCP server를 만들 때 먼저 정할 것은 노출 범위다.
- 이 server는 로컬 개발용인가, 사내 서비스용인가?
- read-only인가, write tool도 포함하는가?
- 사용자별 권한을 어떻게 확인하는가?
- tool 결과에 민감정보가 들어갈 수 있는가?
- tool 호출 로그는 어디에 남기는가?
- provider function calling과 직접 연결할 것인가, host에서 중간 검증을 할 것인가?
처음부터 모든 업무 API를 MCP로 노출하지 않는다. 안전한 read-only tool부터 작게 시작하는 편이 좋다.
장애 상황과 대응
MCP 관련 장애는 다음처럼 나타난다.
- tool discovery가 실패해 모델이 도구를 못 쓴다.
- tool schema 변경 후 host와 server가 맞지 않는다.
- tool timeout으로 agent loop가 멈춘다.
- 권한 없는 resource가 조회된다.
- tool 결과에 prompt injection 텍스트가 포함된다.
대응은 server version, tool schema version, trace id, actor, tool result status를 남기는 것이다.
인프라 협업 포인트
MCP server는 배포 단위가 될 수 있다. 인프라팀과 다음을 합의해야 한다.
- local stdio server인지 remote server인지
- network inbound/outbound 정책
- secret 관리
- server autoscaling 필요 여부
- tool별 rate limit
- 감사 로그 저장 위치
- host와 server 사이 인증 방식
MCP server가 늘어나면 사내 시스템 접근 경로도 늘어난다. 인프라팀 입장에서는 관리 대상이 증가한다는 점을 이해해야 한다.
개인 프로젝트 최소 기준
- read-only tool부터 만든다.
- tool 이름과 설명을 명확히 쓴다.
- 입력 schema를 둔다.
- API key를 코드에 넣지 않는다.
- write tool은 수동 확인 단계를 둔다.
기업 운영 수준 기준
- MCP server별 owner를 둔다.
- tool schema version을 관리한다.
- 인증과 권한을 서버에서 강제한다.
- 모든 tool 호출을 audit log로 남긴다.
- 보안팀과 prompt injection, data exfiltration 위험을 리뷰한다.
실전 팁
- tool 이름은 짧지만 구체적으로 짓는다.
run보다search_policy가 낫다. - tool description에는 언제 쓰면 안 되는지도 적는다.
- resource는 가능한 source id와 version을 함께 반환한다.
- write tool은 처음부터 승인 흐름과 idempotency key를 받게 설계한다.
위험 신호!
- MCP server가 내부 관리자 API를 거의 그대로 노출한다.
- tool 호출 actor를 기록하지 않는다.
- tool timeout이 없다.
- read-only와 write tool 권한이 같다.
- tool 결과를 검증 없이 모델 지시처럼 넣는다.
확인 질문
확인 질문
- MCP는 어떤 문제를 해결하는가?
- AI 애플리케이션이 외부 도구와 데이터 소스를 표준 방식으로 발견하고 호출하는 문제를 해결한다.
- MCP server와 모델은 같은 것인가?
- 아니다. MCP server는 tool, resource, prompt를 제공하는 외부 서버이고 모델 호출은 host가 연결한다.
- function calling과 MCP의 가장 큰 차이는 무엇인가?
- function calling은 provider API의 도구 호출 표현이고, MCP는 도구와 리소스를 제공하는 프로토콜이다.
Spring AI 적용 연결
- Spring AI에서 MCP는 외부 도구를
ChatClient호출 흐름에 연결하는 표준 통합 경계로 볼 수 있다. client/server 구성은 08. MCP Client Server 통합에서 이어진다. - MCP를 RAG, tool, observability와 묶는 예시는 13. 고객지원 RAG Capstone을 함께 본다.