개발자를 위한 첫 MCP 서버 구축 방법: 단계별 가이드 (2026)
요약
본 가이드는 2026년 최신 SDK를 사용하여 MCP(Model Communication Protocol) 서버를 처음부터 구축하는 단계별 개발자 가이드입니다. MCP 서버는 AI 애플리케이션이 표준 인터페이스를 통해 도구 및 데이터에 접근하게 하며, 클라이언트-서버 간의 통신 흐름을 설명합니다. 최근 프로토콜 개정(2026년 7월)으로 인해 구형 튜토리얼은 작동하지 않으며, 상태 비저장(stateless) 방식이 핵심 변경 사항입니다.
핵심 포인트
- MCP 서버는 AI 앱의 도구 접근을 위한 표준 인터페이스 역할을 합니다.
- 클라이언트가 모델에게 사용 가능한 도구를 전달하고, 요청 시 서버가 작업을 수행합니다.
- 2026년 프로토콜은 상태 비저장(stateless) 방식으로 변경되어 구형 가이드와 다릅니다.
- MCP Inspector 및 Claude Code 연결을 통해 테스트 환경을 구축할 수 있습니다.
READ HERE:
How to Build Your First MCP Server: Step-by-Step Guide for Developers (2026) — TopBlogs
현재 2026 SDK를 사용하여 작동하는 MCP 서버를 구축하고, MCP Inspector에서 테스트하며, Claude Code에 연결합니다. 다른 개발자들이 흔히 저지르는 실수도 포함되어 있습니다.
최종 업데이트: 2026년 10월
만약 작년에 MCP 튜토리얼을 따라 했고 코드가 공식 문서와 더 이상 일치하지 않는다면, 설정이 제대로 되어 있지 않을 가능성이 높습니다. 프로토콜은 2026년 7월 28일에 가장 큰 개정판을 배포했으며, 서버 작성 방식에 변화를 주었습니다. 많은 오래된 가이드들이 여전히 이전 버전을 가르치고 있습니다.
이 가이드는 현재 SDK를 사용하여 MCP 서버를 처음부터 구축합니다. 이 과정을 거치면 MCP Inspector에서 테스트하고 Claude Code에 연결할 수 있는 두 가지 도구를 갖게 됩니다. Node.js가 이미 설치되어 있다면, 기본적인 서버는 약 15분 만에 실행될 수 있습니다. 환경 설정이나 Claude Code 설정을 처음 하는 경우에는 더 많은 시간을 할애해야 합니다.
이 가이드 작성 과정: 구현은 현재 MCP 문서와 SDK 예제를 따릅니다. 문제 해결 섹션 역시 실제로 MCP 서버를 구축한 개발자들의 보고서를 바탕으로 작성되었습니다. 해당 보고서는 등장하는 곳에 출처가 명시되어 있으며 마지막에 목록화되어 있습니다.
MCP 서버가 실제로 하는 일
MCP 서버는 AI 애플리케이션이 표준 인터페이스를 통해 도구(tools), 그리고 선택적으로 데이터 및 프롬프트 템플릿에 접근할 수 있도록 하는 프로그램입니다. AI 애플리케이션이 클라이언트 역할을 합니다. 이 클라이언트는 사용 가능한 도구 목록을 읽어 모델에게 전달합니다. 모델이 특정 도구가 필요하다고 결정하면, 클라이언트가 tools/call 요청을 보내고, 서버가 작업을 수행하며, 그 결과가 다시 돌아옵니다. 모델은 절대로 서버와 직접 대화하지 않습니다.
모든 도구는 이름(name), 설명(description), 그리고 입력 스키마(input schema)를 가집니다. 모델은 설명을 사용하여 사용자의 도구가 해당 작업에 적합한지 결정합니다. 모호한 설명은 부실한 도구 선택으로 이어지므로, 여기에 시간을 충분히 할애해야 합니다.
2026년에 변경된 사항과 구형 튜토리얼이 작동하지 않는 이유
현재 프로토콜 개정 버전은 2026-07-28입니다. 주요 변경 사항은 상태 비저장(stateless) 프로토콜 코어입니다. 공식 출시 발표에 따르면, initialize 핸드셰이크와 Mcp-Session-Id 헤더가 폐지되었으며, 이제 각 요청은 자체 프로토콜 버전과 클라이언트 세부 정보를 포함합니다.
이는 여러분에게 두 가지 영향을 미칩니다.
SDK 패키지 레이아웃이 변경되었습니다. TypeScript SDK에서 v2는 단일 @modelcontextprotocol/sdk 패키지를 @modelcontextprotocol/server와 @modelcontextprotocol/client를 포함한 별도의 패키지로 대체했습니다. Python SDK에서는 v2가 FastMCP의 이름을 MCPServer로 변경했습니다. 만약 튜토리얼이 @modelcontextprotocol/sdk나 mcp.server.fastmcp에서 가져온다면, 구형 SDK API를 사용하고 있는 것입니다. 따라 하기 전에 해당 튜토리얼이 어떤 프로토콜 개정 버전을 지원하는지 확인하세요.
상태가 도구 내부로 이동했습니다. 상태 비저장 프로토콜 코어라는 것이 여러분의 애플리케이션이 상태 비저장이 되어야 한다는 의미는 아닙니다. 이는 최신 프로토콜이 더 이상 전송 계층(transport-level) 세션에 의존하여 요청 간에 상태를 전달하지 않는다는 것을 의미합니다. 만약 서버가 호출을 거쳐 무언가를 유지해야 한다면, 관리자들은 작업 ID와 같은 명시적인 핸들(handle)을 도구에서 반환하고 모델이 이를 인수로 다시 전달하도록 하는 것을 권장합니다. 아래 원격 호스팅 섹션에 예시를 보여드립니다.
우리가 구축할 것
blog-helper라는 서버를 만들고 여기에 두 개의 도구를 추가합니다. reading-time은 텍스트가 읽는 데 걸리는 시간을 추정합니다. make-slug은 게시물 제목을 URL 슬러그로 변환합니다.
이들은 의도적으로 장난감(toy) 도구입니다. API 키나 네트워크 접근이 필요하지 않으며, 모든 도구가 수행하는 두 가지 작업, 즉 구조화된 입력을 받아 텍스트를 반환하는 것을 다룹니다. 무언가 실패한다면, 그 원인은 제3자 서비스가 아니라 여러분의 MCP 설정에 있습니다.
1단계: 프로젝트 설정
Node.js 22.19 이상이 필요합니다. TypeScript SDK는 Node.js 20 이상을 지원하지만, 3단계에서 사용되는 MCP Inspector는 Node.js 22.19.0 이상을 요구하므로 해당 버전을 한 번 설치하면 가이드 전체를 커버할 수 있습니다. SDK는 ES 모듈만 배포하므로 프로젝트가 type: module로 설정되어야 합니다. tsx 패키지는 TypeScript를 직접 실행하므로 빌드 단계가 없습니다.
mkdir blog-helper && cd blog-helper
npm init -y
npm pkg set type=module
...
2단계: 서버 작성하기
src/index.ts를 생성하고 다음 내용을 붙여넣습니다.
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
...
세 가지 세부 사항에 주목할 필요가 있습니다.
inputSchema의 Zod 스키마는 사용자가 작성하는 유일한 스키마입니다. SDK는 이를 사용하여 도구의 입력을 클라이언트에게 설명하고 핸들러가 실행되기 전에 잘못된 인수를 거부하므로, 어떤 핸들러에도 수동 검증이 포함되어 있지 않습니다.
서버는 createServer 함수 내부에 구축됩니다. 왜냐하면 serveStdio가 이 연결을 제공하는 인스턴스를 생성하기 위해 이를 호출하기 때문입니다. 해당 함수를 가볍게 유지하세요. 서버를 구성하고 도구를 등록한 후, 연결이 시작되기 전에 느린 네트워크 호출이나 값비싼 설정은 피해야 합니다.
마지막 줄에서 console.error로 로그하는 것은 의도적입니다. stdio 서버에서는 stdout이 프로토콜 메시지를 전송하므로, 진단 출력은 대신 stderr로 보내야 합니다. 임의의 console.log는 비(非)프로토콜 데이터를 stdout에 작성하여 JSON-RPC 스트림을 손상시킬 수 있습니다. 여러 개발자가 이 문제에 직면했다고 보고했으며, 그들의 계정은 기사의 후반부에 나옵니다.
3단계: 실행 및 Inspector에서 테스트하기
서버를 시작합니다:
npx tsx src/index.ts
배너가 출력된 후 유휴 상태로 대기합니다. 이는 예상되는 동작입니다. 왜냐하면 stdio 서버는 클라이언트가 통신을 시작할 때까지 stdin에서 기다리기 때문입니다.
도구를 호출하려면 MCP Inspector를 사용하세요. 이것은 명령어를 실행하고 stdio를 통해 연결하는 로컬 웹 앱입니다.
명령어는 Inspector의 웹 인터페이스를 열기 위한 일회성 토큰이 포함된 URL을 출력합니다. 이 토큰은 이전 프로토콜 개정판에서 사용했던 Mcp-Session-Id가 아니라, Inspector에 대한 접근 토큰입니다. 브라우저에서 해당 URL을 열고 Connect를 클릭한 다음, Tools 탭을 열어 make-slug를 선택하고 제목을 입력합니다. 그런 다음 빈 문자열로 reading-time을 호출합니다. 스키마는 최소 한 문자를 요구하므로, 핸들러가 실행되기 전에 이 호출은 거부되어야 합니다. 이 거부는 MCP 입력 유효성 검사가 작동하는 것을 빠르게 확인할 수 있는 방법입니다.
참고로, 위의 코드는 유효한 입력을 사용했을 때 다음과 같은 결과를 생성해야 합니다:
make-slug "2026년 나만의 첫 MCP 서버 만들기" -> my-first-mcp-server-in-2026
reading-time "안녕하세요 세상" -> 2 words, about 1 min read
DEV 커뮤니티 게시물 "How I Built My First MCP Server for Claude Code" (yureki_lab)의 개발자는 Claude에게 질문하고 답변이 올바른지 판단하는 과정을 통해 처음 몇 시간을 디버깅했다고 언급했습니다. Inspector로 이동하면서 모델을 거치지 않고 서버가 반환하는 내용을 직접 볼 수 있게 되었습니다. 클라이언트를 연결하기 전에 서버 자체를 테스트해 보는 것이 좋습니다.
4단계: Claude Code에 연결하기
로컬 stdio 서버를 등록하는 것은 단 하나의 명령어로 이루어집니다. 이중 대시(-) 뒤의 모든 내용은 Claude Code가 서버를 시작하는 데 사용하는 명령어입니다. 어느 폴더에서든 작동하도록 진입 파일에 절대 경로를 사용하세요.
claude mcp add blog-helper -- npx tsx /full/path/to/blog-helper/src/index.ts
그런 다음 claude mcp list를 실행하여 서버가 연결된 것으로 표시되는지 확인합니다. 실패하면 먼저 경로를 확인하세요. Windows에서는 셸이 예상하는 형식으로 경로를 작성해야 합니다.
서버가 연결되면, 예를 들어
Python으로 작업하는 경우, v2 SDK는 데코레이터가 적용된 함수로부터 서버를 구축합니다. Python 3.10 이상이 필요합니다. uv로 프로젝트를 설정하세요:
uv init blog-helper && cd blog-helper
uv add "mcp[cli]"
그런 다음 server.py를 생성하세요:
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("blog-helper")
...
타입 힌트(Type hints)가 입력 스키마(input schema)가 되고, 독스트링(docstring)이 도구 설명(tool description)이 됩니다. Python v2는 SDK의 대대적인 재작업(major rework)이므로, 여기에 있는 예제가 사용자의 버전에서 다르게 작동하는 경우 마이그레이션 가이드(migration guide)를 확인하세요. pip install mcp는 현재 2.x 라인을 설치하므로, 이전 코드를 유지하고 마이그레이션할 준비가 되지 않았다면 요구 사항에 <2 상한선을 유지하세요.
다른 개발자들이 직면했던 문제들
공식 튜토리얼은 순조로운 경로(happy path)만을 보여줍니다. 아래 보고서들은 MCP 서버를 구축, 테스트 또는 배포한 개발자들의 경험에서 나온 것이며, 각각의 내용은 작성자에게 공로가 돌아갑니다.
stdout이 stdio 서버를 망가뜨리는 이유
여러 개발자들이 같은 문제를 보고했습니다. Claude Code가 내부 서비스 카탈로그를 쿼리할 수 있도록 서버를 구축한 yureki_lab은 사소한 console.log 하나가 약 40분을 소비하고 암호 같은 구문 분석 오류(cryptic parse error)를 발생시켰다고 작성했습니다. 주말 프로젝트에서 약 2,300 npm 다운로드로 성장한 서버를 가진 brianmello는 stdout을 첫 번째 놀라움이라고 부르며, 사소한 출력은 자신의 코드뿐만 아니라 트리의 깊숙한 곳에 있는 의존성(dependency)에서도 나올 수 있다고 덧붙였습니다. 두 사람 모두 모든 진단 메시지를 stderr로 보내게 되었습니다.
공식 MCP 디버깅 문서에서는 로컬 서버에 대해 같은 규칙을 제시하며, 호스트 애플리케이션이 stderr로 작성하는 내용을 포착한다고 언급합니다. Windows의 경우, Claude Desktop은 로그를 %APPDATA%\Claude\logs 아래에 작성하므로, 연결 실패 시 찾아보기 좋은 첫 번째 장소입니다.
상대 경로가 혼란스러운 오류를 유발하는 이유
Jaypee의 MCP 연결 오류 문제 해결 가이드는 서버 설정 파일 내 상대 경로가 클라이언트의 작업 디렉터리(working directory)를 기준으로 해석되며, 서버 자체의 디렉터리를 기준으로 하지 않는다고 지적합니다. 따라서 터미널에서 작동하는 서버라도 Claude Desktop 내부에서는 실패할 수 있으며, 해결책은 절대 경로(absolute paths)를 사용하는 것입니다.
같은 가이드에는 몇 가지 다른 함정들도 나열되어 있습니다. 입력 스키마(input schemas)가 잘못된 경우, 서버는 성공적으로 연결되더라도 도구(tools)를 하나도 노출하지 못할 수 있습니다. API 키가 필요한 서버는 연결 시점이 아니라 첫 번째 도구 호출(tool call)에서 실패하는 경우가 많습니다. 그리고 npx 다운로드 실패는 다른 어떤 연결 오류와도 유사하게 보이므로, 클라이언트를 탓하기 전에 터미널에서 직접 서버 명령을 실행해 봐야 합니다.
사용자 의도를 중심으로 도구 설계하기
thunderbit-mcp의 개발팀(작성자 handle ethan_thunderbit)은 기존 API의 모든 엔드포인트(endpoint)를 도구로 감싸는 것이 첫 번째 유혹이라고 언급했습니다. 그들의 필드 가이드는 더 적고 예측 가능한 도구를 주장하며, 설명에 부정적 지침(negative guidance)을 추가할 것을 권장합니다. 이는 모델에게 언제 도구를 사용하지 말아야 하는지 알려주는 문장을 의미합니다. 또한 사용자들은 패키지를 설치하고 설정 항목을 추가하기 때문에 stdio를 가장 안정적인 첫 번째 전송 방식(transport)으로 설명합니다.
brianmello는 관련 조언을 한 줄로 요약했습니다: 모델이 이를 프롬프트처럼 읽기 때문에 도구 설명을 프롬프트처럼 작성하세요.
에러를 에이전트에게 유용하게 만들기
parkerrrrr는 도구가 오류를 발생시킬 때, 에이전트는 종종 일반적인 내부 오류만 보고 인증 실패인지, 잘못된 인자(bad argument) 때문인지, 아니면 상위 시스템 장애(upstream outage)인지를 구분할 수 없다는 점을 관찰했습니다. 핸들러에서 오류를 포착하고 isError: true와 함께 명확한 메시지를 반환하면 모델이 무엇이 잘못되었는지 보고 적절하게 반응할 수 있게 됩니다.
원격 서버에서의 인증
같은 작성자는 인증 기능이 꺼진 HTTP MCP 서버가 발견되어 악용되기 쉬운 기본 설정의 전형적인 예시라고 경고했습니다. stdio를 넘어선다면, 베어러 토큰(bearer token)이나 OAuth를 요구하고, 토큰이 구성되지 않았을 때 요청을 거부하도록 서버를 만들어야 합니다.
2026-07-28 사양으로 마이그레이션
krlz가 DEV Community에 새로운 개정판에 대해 작성하면서, 가장 큰 변경 사항은 'elicitation'에서 올 것으로 예상하고 있습니다. 이는 서버가 통화 중간에 사용자에게 질문을 하는 것을 의미합니다. 백채널(back-channel)이 사라졌기 때문에, 이 기능에 의존했던 코드는 새로운 다중 왕복 패턴(multi round-trip pattern)을 중심으로 다시 작성되어야 합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기