JavaScript로 작은 MCP 서버 구축하기 - Claude, Codex 친화적
요약
Model Context Protocol(MCP)을 사용하여 LLM에 실시간 도구를 제공하는 작은 JavaScript 서버를 구축하는 방법을 설명합니다. ESM 설정, Stdio 전송 방식, 그리고 다양한 모델을 위한 어댑터 패턴 등 실무적인 구현 가이드를 제공합니다.
핵심 포인트
- MCP를 통해 모델이 환각 대신 실제 도구를 호출하도록 구현 가능
- Node.js 환경에서 @modelcontextprotocol/sdk를 활용한 서버 구축
- Stdio 및 HTTP 전송 방식을 통한 로컬/원격 실행 지원
- ESM 모듈 설정 및 올바른 패키지 임포트 방법 안내
TL;DR — Model Context Protocol (MCP)를 사용하여 LLM에 도구(tools)를 노출하는 작은 서버를 배포합니다. 이 가이드는 최소한의 실행 가능한 예제를 보여주고, 각 부분을 설명하며, 흔히 발생하는 문제점(ESM imports, transports, adapters)을 다룹니다.
이것이 중요한 이유
- MCP를 사용하면 모델이 환각(hallucinating)을 일으키는 대신 실제 도구(데이터 가져오기, 명령 실행 등)를 호출할 수 있습니다.
- 알림, GitHub 조회, 데이터베이스 쿼리 등 실시간 데이터로 모델을 확장하는 아주 좋은 방법입니다.
얻게 될 내용
- 단일 도구인
get-repo-stats를 노출하는 작동하는 서버 (index.js) - 로컬(stdio) 또는 원격(HTTP)에서 실행하기 위한 빠른 지침
- OpenAI (Codex/GPT), Google Gemini, Anthropic Claude를 위한 짧은 어댑터 패턴
빠른 체크리스트
- Node 18+
- npm install (
@modelcontextprotocol/sdk및zod에 이미 의존성이 있습니다) - ESM을 위해
package.json에 "type": "module" 추가
빠른 시작 (복사-붙여넣기)
1) 의존성 설치 또는 기존 의존성 확인:
npm install
2) 로컬에서 실행:
node index.js
MODULE_TYPELESS_PACKAGE_JSON 경고가 나타나면 package.json에 "type": "module"을 추가하세요 (아래 예시 참고).
최소한의 index.js (리포지토리에 복사하세요)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
...
코드 설명
-
McpServer: 도구를 등록하고 프로토콜을 처리하는 상위 수준 API (high-level API)
-
server.tool(name, description, schema, handler): 호출 가능한 도구를 등록합니다. 입력 유효성 검사(input validation)를 위해zod를 사용하세요. -
Handler는 다음과 같은 형태의 결과를 반환합니다:
{ content: [ { type: 'text', text: '...' } ] }
-
StdioServerTransport: 가장 쉬운 로컬 전송 방식(local transport) — stdin/stdout을 통해 읽기/쓰기를 수행합니다.
-
도구를 등록한 후
server.connect(transport)를 호출하여 초기화 시 사용 가능한 도구들이 광고(advertise)되도록 하세요.
ESM / import 주의사항
-
Node.js가 파일을 ESM (ECMAScript Modules)으로 처리하도록
package.json에"type": "module"을 추가하여MODULE_TYPELESS_PACKAGE_JSON경고를 방지하세요. -
패키지 export 엔트리포인트 (entrypoints)를 사용하고
.js확장자를 포함하세요:- 좋은 예:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; - 나쁜 예:
dist/esm/…로의 딥 임포트 (deep imports) — Node가 경로를 해석하지 못하거나 경로 세그먼트가 중복될 수 있습니다.
- 좋은 예:
package.json 예시 스니펫 (snippet)
{
"name": "mcp",
"version": "1.0.0",
...
전송 방식 (Transports) 한눈에 보기
- stdio: 로컬 어댑터 (Claude Desktop, 로컬 테스트용). 실행이 더 간단합니다.
- StreamableHTTP: 모델 호스트가 HTTP 엔드포인트 (endpoint)를 호출합니다 (클라우드 배포 시 권장됨).
- WebSocket: 고급 워크플로우를 위한 장기 지속 양방향 연결 (long-lived bidirectional connections).
LLM 연결 방법
OpenAI / Codex (함수 호출 (function-calling) 흐름)
Codex에 로컬 MCP 서버 추가하기
Codex는 STDIO를 통해 로컬 MCP 서버를 사용할 수 있습니다. MCP 설정은 기본적으로 ~/.codex/config.toml에 저장되거나, 신뢰할 수 있는 프로젝트의 경우 프로젝트 레벨의 .codex/config.toml에 저장됩니다.
옵션 1: Codex CLI로 추가하기
codex mcp add my-local-git-stat-server -- node /path/to/your/project/index.js
서버에 환경 변수 (environment variables)가 필요한 경우:
codex mcp add my-local-server --env -- node /path/to/your/project/index.js
옵션 2: config.toml에서 수동으로 추가하기
~/.codex/config.toml을 열고 다음을 추가하세요:
[mcp_servers.my-local-server]
command = "node"
args = ["/path/to/your/project/index.js"]
...
서버 검증
Codex를 재시작한 다음, Codex TUI에서 활성화된 MCP 서버를 확인하세요:
/mcp
다음 명령어로 사용 가능한 MCP 명령어를 검사할 수도 있습니다:
codex mcp --help
Google Gemini
- 호스팅 환경이 HTTP 콜백 (callbacks)을 지원하는 경우,
StreamableHTTPClientTransport를 사용하세요. 그렇지 않은 경우, Gemini 함수 호출을 MCP 클라이언트 호출로 변환하는 브리지 프로세스 (bridge process)를 실행하세요.
Anthropic Claude
- 로컬 Claude Desktop의 경우: 데스크톱 앱이 서버 프로세스를 생성(spawn)하고 stdio를 사용할 수 있습니다. 데스크톱 앱이
node /path/index.js를 실행하도록 설정하세요. - 호스팅된 Claude 통합(integrations)의 경우, HTTP 전송(transport) 방식을 권장합니다.
간단한 어댑터 스케치 (의사 코드)
// 서버를 생성한 후 클라이언트 대면 로직에 연결
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
...
OpenAI와 함께 사용하기 (Codex / GPT function-calling)
권장 패턴
- 로컬/테스트: stdio 전송 (transport) + StdioClientTransport 사용 (어댑터에서 서버를 자식 프로세스로 생성).
- 호스팅: StreamableHTTPTransport를 사용하여 MCP 서버를 노출하고, 어댑터가 StreamableHTTPClientTransport를 통해 이를 호출하도록 합니다.
최소한의 어댑터 스케치 (의사 코드)
// adapter-openai.js — 상위 수준 스케치
import OpenAI from 'openai'; // 또는 사용 중인 HTTP 래퍼(wrapper)
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
...
참고 사항
- 모델의 함수 이름(function names)에서 MCP 도구 이름(tool names)으로의 매핑을 명시적으로 유지하고 문서화하세요.
- API 키를 커밋하지 마세요. 환경 변수(env vars)를 사용하고, 프로세스를 생성할 때 데이터를 정화(sanitize)하세요.
Anthropic Claude와 함께 사용하기 (Claude Desktop)
로컬 데스크톱 (stdio) — 가장 쉬운 방법
1. Claude Desktop 설정에 서버 추가 (index.js의 절대 경로 사용):
{
"mcpServers": {
"github-stats": {
...
2. 프로젝트의 package.json에 "type": "module"이 포함되어 있는지, 그리고 import 문에서 .js 확장자를 사용하는지 확인하세요.
3. Claude Desktop을 시작하거나 재시작하세요. 데스크톱 앱이 서버를 생성하고 stdin/stdout을 통해 통신합니다. Claude에게 "What are the stats for facebook/react?"라고 물어보면 get-repo-stats 도구를 호출해야 합니다.
호스팅된 Claude / 원격
- Claude 호스팅이 HTTP 콜백(callbacks)을 지원하는 경우, HTTP 래퍼(wrapper) 뒤에 MCP 서버를 호스팅하고 모델 측에서 StreamableHTTPClientTransport를 사용하세요.
테스트 팁
- import/ESM 문제를 디버깅할 때는
node --trace-warnings index.js명령어로 서버를 실행하세요. - 연결 후
client.listTools()를 호출하는 간단한 스크립트를 사용하여 제공되는 도구(tools)들을 확인하세요.
디버깅 체크리스트 (Debugging checklist)
- 깊은 경로(deep path)에서
ERR_MODULE_NOT_FOUND가 발생하나요?@modelcontextprotocol/sdk/server/mcp.js임포트(import) 방식으로 전환하세요. MODULE_TYPELESS_PACKAGE_JSON오류가 발생하나요?package.json에"type": "module"을 추가하세요.- 해결 경로 추적(resolution traces)을 위해
node --trace-warnings index.js를 실행하세요.
보안 및 배포 (Security & deployment (brief))
- HTTP를 노출하는 경우, 인증(API key, OAuth)을 요구하고 TLS를 사용하세요.
- 자식 프로세스(child processes)를 생성할 때는 환경 변수를 정제(sanitize)하고 비밀 정보(secrets)가 유출되지 않도록 주의하세요.
- 외부 서비스를 호출하는 도구(tools)에 대해서는 리소스 사용량(타임아웃, 동시성)을 제한하세요.
직접 커스텀하기 (Make it yours (next steps))
- 더 많은 도구 추가 (이슈(issues), 릴리스(releases), 검색(search))
- 프라이빗 GitHub 저장소를 위한 인증 기능 추가
- npm CLI로 래핑(wrap)하고 로컬 설치를 쉽게 할 수 있도록 설정 파일(config file) 추가
참고 자료 및 확인할 곳 (References & where to look)
- SDK 예제:
node_modules/@modelcontextprotocol/sdk/dist/esm/examples - 공식 문서: https://modelcontextprotocol.io
- 예제 코드: https://github.com/gaurav101/ai-experiment
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기