본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 07. 20:36

Node.js를 사용하여 MCP 서버 구축하기

요약

Model Context Protocol(MCP)을 사용하여 Node.js 기반의 MCP 서버를 구축하는 방법을 설명합니다. SDK와 Zod를 활용해 AI 호스트가 사용할 수 있는 도구, 리소스, 프롬프트를 구현하는 과정을 다룹니다.

핵심 포인트

  • MCP는 AI 호스트와 외부 컨텍스트를 연결하는 개방형 표준 프로토콜입니다.
  • Node.js SDK를 사용하여 도구(Tools), 리소스(Resources), 프롬프트(Prompts)를 구현할 수 있습니다.
  • Zod 스키마를 통해 도구 호출을 위한 타입 안전한 입력을 정의합니다.
  • JSON-RPC를 기반으로 stdio 또는 HTTP 전송 계층을 통해 통신합니다.

Model Context Protocol (MCP)는 AI 호스트 (hosts) (Claude, ChatGPT, Cursor, VS Code 등)를 임시방편적인 플러그인 대신 구조화된 프로토콜을 통해 외부 컨텍스트 및 액션 (context and actions) 에 연결하기 위한 개방형 표준입니다.

호스트는 MCP 클라이언트 (MCP client) 를 실행합니다. 여러분의 애플리케이션은 MCP 서버 (MCP server) 가 되어, 도구 (tools) (모델이 호출할 수 있는 액션), 리소스 (resources) (읽기 전용 데이터), 그리고 선택적으로 프롬프트 (prompts) (재사용 가능한 메시지 템플릿)를 노출합니다. 통신에는 stdio 또는 HTTP와 같은 전송 계층(transport)을 통한 JSON-RPC를 사용합니다.

이 포스트에서는 공식 @modelcontextprotocol/sdk 패키지와 Zod 스키마를 사용하여 Node.js로 작은 할 일(todo) MCP 서버를 구축하는 방법을 보여줍니다.

아키텍처 개요

sequenceDiagram
  participant User
  participant Host as AI Host (Claude / ChatGPT)
...

클라이언트가 연결된 후, 서버는 초기화 핸드셰이크(initializeinitialized)를 완료합니다. 호스트는 tools/list, resources/list, prompts/list를 통해 기능을 발견한 다음, 런타임에 도구를 호출하거나 리소스를 읽습니다.

사전 요구 사항

  • Node.js 버전 26
  • npm i @modelcontextprotocol/sdk zod
  • 클라이언트 테스트를 위한 선택 사항: Claude Desktop 및/또는 ChatGPT (Connectors / Apps)

안정적인 v1 SDK는 @modelcontextprotocol/sdk입니다. v2 분할 버전(@modelcontextprotocol/server, @modelcontextprotocol/client)은 프리릴리스(pre-release) 상태입니다. 이 포스트는 현재 프로덕션 도구와 일치하는 v1을 사용합니다.

MCP 기능 - 도구, 리소스, 프롬프트

기능목적데모 예시
도구 (Tools)타입이 지정된 입력을 가진 모델 호출 액션add_todo, list_todos, mark_todo_done
...

도구는 주요 통합 접점입니다. 모델은 tools/call을 통해 도구를 호출합니다. 리소스는 resources/read로 가져오며 읽기 전용 상태를 유지해야 합니다. 프롬프트는 prompts/get을 통해 미리 구축된 메시지를 반환합니다.

도구 (Tool) 입력에는 클라이언트가 파라미터를 알 수 있도록 스키마 (Schema)가 필요합니다. TypeScript SDK를 사용하면 inputSchema에 Zod 필드를 전달하면 됩니다:

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

...

고정된 URI에 리소스 (Resource)를 등록합니다:

server.registerResource(
  'all-todos',
  'todo://all',
...

선택적 프롬프트 (Prompt)를 등록합니다:

server.registerPrompt(
  'summarize-open-todos',
  {
...

서버 구축하기

동일한 서버 정의가 여러 전송 방식 (Transport)에서 작동할 수 있도록 팩토리 (Factory)를 사용하세요:

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

...

도구 핸들러 (Tool handler)는 { content: [...] }를 반환합니다. 도구가 실패할 경우 호스트가 에러를 표시할 수 있도록 isError: true를 설정하세요. 리소스 핸들러 (Resource handler)는 { contents: [...] }를 반환합니다. 프롬프트 핸들러 (Prompt handler)는 { messages: [...] }를 반환합니다.

데모는 인메모리 저장소 (In-memory store)를 사용하므로 API 키나 데이터베이스 없이도 실행할 수 있습니다.

전송 방식 (Transports) - stdio vs SSE / Streamable HTTP

MCP는 프로토콜 (Protocol) (JSON-RPC 메시지)과 전송 방식 (Transport) (클라이언트와 서버 간에 바이트가 이동하는 방식)을 분리합니다.

Stdio (StdioServerTransport)

클라이언트는 귀하의 서버를 자식 프로세스 (Child process)로 생성합니다. JSON-RPC는 stdin/stdout을 통해 전달됩니다.

  • 사용 사례: Claude Desktop, Cursor, VS Code, Claude Code, 로컬 CLI 에이전트
  • 장점: 가장 간단한 설정, 포트나 방화벽 규칙 불필요, OAuth 불필요
  • 단점: 프로세스당 하나의 클라이언트만 연결 가능; 클라우드 호스트는 로컬 바이너리를 생성할 수 없음
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { createMcpServer } from './create-server.js';

...

로그는 반드시 stderr에만 작성하세요. stdout은 프로토콜 채널입니다.

원격 HTTP - SSE (레거시) vs Streamable HTTP (현재)

초기 MCP 원격 서버는 HTTP + SSE를 사용했습니다. 클라이언트에서 서버로의 요청에는 POST를 사용하고, 서버에서 클라이언트로의 스트리밍에는 Server-Sent Events를 사용했습니다. 해당 전송 방식은 현재 지원 중단(Deprecated)되었습니다.

새로운 서버는 Streamable HTTP (StreamableHTTPServerTransport)를 사용해야 합니다. 이는 POST 요청/응답, 알림을 위한 선택적 SSE (Server-Sent Events), 그리고 세션 관리 (Session management)를 지원합니다. v2 SDK에서는 서버 측 SSE를 완전히 제거하였으며, 레거시 (Legacy) 서버를 위한 클라이언트 측 SSE는 유지됩니다.

시나리오전송 방식 (Transport)
Claude Desktop, 로컬 개발stdio
...
Streamable HTTP 엔트리 (Stateless):
import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { createMcpServer } from './create-server.js';
...

createMcpExpressApp()은 localhost에 바인딩할 때 DNS 리바인딩 보호 (DNS rebinding protection) 기능을 활성화하며, 이는 로컬 HTTP 서버에 권장되는 방식입니다.

Streamable HTTP를 클라우드 클라이언트에게 노출하기 전에 반드시 HTTPS 뒤에 배포하십시오. ChatGPT 커넥터 (Connectors) 또한 프로덕션 (Production) 환경에서 사용하려면 OAuth 2.1이 필요합니다.

MCP 클라이언트 연결하기

Claude Desktop (stdio - 주요 데모 경로)

Windows의 설정 파일 경로: %APPDATA%\Claude\claude_desktop_config.json. Settings → Developer → Edit Config를 통해 파일을 열 수 있습니다.

{
  "mcpServers": {
    "todo-mcp": {
...

저장 후 Claude Desktop을 재시작하십시오. Claude는 쓰기 작업 (Write operations)을 실행하기 전에 도구 승인 UI를 표시합니다.

Claude Desktop (원격 HTTP)

claude_desktop_config.jsonstdio 서버만 검증합니다. 작동할 것을 기대하며 단순히 url 필드만 넣지 마십시오. 공개 HTTPS MCP 서버의 경우, Settings → Connectors → Add custom connector를 사용하십시오. 개발 중인 로컬 HTTP의 경우, mcp-remote를 사용하여 stdio 방식으로 실행되는 프록시 (Proxy)로 브릿지(Bridge)를 구성하십시오.

ChatGPT (Connectors / Apps)

ChatGPT에는 로컬 MCP 설정 파일이 없습니다. Settings → Apps (또는 Connectors)에서 이름, 설명, MCP 서버 URL과 함께 서버를 등록하십시오.

요구 사항:

  • 공개 HTTPS 엔드포인트 (Streamable HTTP)
  • 프로덕션 커넥터를 위한 OAuth 2.1
  • stdio 전용 서버는 ChatGPT가 접근할 수 있도록 HTTP 래퍼 (Wrapper) 또는 터널 (Tunnel)이 필요합니다.

ChatGPT는 쓰기/수정 (write/modify) 도구 호출 전에 확인 모달 (confirmation modals)을 표시합니다.

Cursor

Cursor는 Claude Desktop과 동일한 stdio 구조(command + args)를 가진 .cursor/mcp.json을 사용합니다.

그 외 중요한 사항

  • 보안 (Security) - 도구 입력을 검증하고, 도구의 범위를 좁게 설정하며, 리소스에 비밀 정보 (secrets)를 절대 노출하지 마세요.
  • 로깅 (Logging) - stdio 서버의 경우 stderr만 사용하세요. stdout에 console.log를 사용하는 것은 피해야 합니다.
  • 테스트 (Testing) - 스모크 테스트 (smoke tests)를 위해 StdioClientTransport와 함께 @modelcontextprotocol/sdk 클라이언트 헬퍼를 사용하세요.
  • 사양 진화 (Spec evolution) - 레거시 SSE 대신 Streamable HTTP를 선호하세요. SDK를 업그레이드할 때 v2 SDK 분할 (split) 사항을 주의 깊게 살펴보세요.

데모

이 포스트를 위한 실행 가능한 스크립트는 프라이빗 데모 리포지토리의 mcp-server-nodejs-demo 폴더에 있습니다. code demos를 통해 접근 권한을 얻으세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0