MCP Server 처음부터 만들기: AI 도구 호출 완전 가이드

약 22분 소요 · MACCOME

Cursor나 Claude Desktop에 사내 DB·API를 연결하려 하지만 REST 래퍼만으로는 AI가 실행 시점에 도구를 발견하지 못합니다. 본문은 MCP Server를 처음부터 작성하는 실전 절차를 제공합니다.① 환경 구성부터 Hello World, Tools / Resources / Prompts 3요소 구현; ② HTTP 전송 전환과 디버깅; ③ 프로덕션 배포 및 지식베이스 RAG 연동까지 8단계로 재현 가능합니다. 프로토콜 전체 그림은MCP와 AI 시대의 HTTP, 호스트 설정은Agent Skill 가이드와 함께 읽으세요.

MCP Server 개발에서 흔히 빠지는 여섯 가지 오해

  1. REST 엔드포인트를 그대로 Tool로 등록: HTTP 래퍼만으로는 JSON Schema 자기 기술과 tools/list 동적 발견이 빠져 AI가 파라미터를 오해합니다.
  2. Tools만 구현하고 Resources 생략: 읽기 전용 컨텍스트(문서, 설정 파일)를 Tool로 반환하면 부수효과 여부가 모호해져 모델이 잘못 호출합니다.
  3. Prompts를 「프롬프트 엔지니어링」과 혼동: MCP Prompts는 Host가 재사용하는 템플릿 정의이며, 채팅 내 임시 지시와는 별개입니다.
  4. STDIO만으로 프로덕션 운영: 로컬 자식 프로세스는 IDE 종료나 PC 절전 시 끊깁니다. 팀 공유에는 HTTP+SSE가 필요합니다.
  5. JSON-RPC 계층에서 에러 처리 생략: -32602(Invalid params)나 -32000(Server error)를 올바르게 반환하지 않으면 AI가 재시도 불가 상태에 빠집니다.
  6. 개발용 노트북을 7×24 Server 호스트로 사용: 덮개 닫기·절전·네트워크 전환 시 SSE 세션이 끊겨 Agent 워크플로가 중단됩니다.

MCP 프로토콜 핵심 (구현 전 3계층 이해)

MCP(Model Context Protocol)는 Host·Client·Server 3계층으로 구성됩니다. Host(Cursor, Claude Desktop 등)가 내장 Client를 실행하고 Server와 JSON-RPC 2.0으로 통신합니다. Server가 공개하는 3가지 프리미티브가 구현의 핵심입니다.

프리미티브 역할 대표 유스케이스
ToolsAI가 능동 호출하는 작업(부수효과 있음)DB 쿼리, Slack 게시, 파일 쓰기
Resources읽기 전용 컨텍스트 제공설정 YAML, API 명세, DB 스키마
Prompts재사용 가능한 프롬프트 템플릿코드 리뷰 절차, 인시던트 대응 플로

환경 준비: Node.js 20+ 및 공식 SDK

본문은 TypeScript 공식 SDK(@modelcontextprotocol/sdk)를 기준으로 합니다. Python 버전(mcp 패키지)도 동등한 API 면을 갖추며 개념은 동일합니다.

사전 요건

  • Node.js 20 LTS 이상(node -v로 확인)
  • npm 또는 pnpm
  • Cursor 또는 Claude Desktop(MCP Client로 사용)
bash
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init

Hello World: 최소 STDIO Server

첫 Server는 「연결 확인」이 목적입니다. STDIO 전송이면 추가 포트 없이 Cursor에서 즉시 테스트할 수 있습니다.

typescript
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: "hello-mcp", version: "1.0.0" });

server.tool(
  "ping",
  "연결 확인용. 입력 문자열을 그대로 반환합니다",
  { message: z.string().describe("에코할 문자열") },
  async ({ message }) => ({
    content: [{ type: "text", text: `pong: ${message}` }],
  })
);

const transport = new StdioServerTransport();
await server.connect(transport);
info

Cursor 설정: Settings → MCP → Add Server에서 command: npx tsx src/index.ts를 지정합니다. tools/listping이 표시되면 성공입니다.

Tools 구현: JSON Schema와 부수효과 명시

프로덕션 Tool은 Zod 스키마로 파라미터를 자기 기술하고, 부수효과(쓰기·삭제)를 description에 명시합니다. 아래는 PostgreSQL 읽기 전용 Tool 예시입니다.

typescript
server.tool(
  "query_users",
  "users 테이블을 읽기 전용으로 SELECT(부수효과 없음)",
  {
    limit: z.number().min(1).max(100).default(10),
    status: z.enum(["active", "inactive"]).optional(),
  },
  async ({ limit, status }) => {
    const rows = await db.query(
      "SELECT id, email, status FROM users WHERE ($1::text IS NULL OR status = $1) LIMIT $2",
      [status ?? null, limit]
    );
    return {
      content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
    };
  }
);

Resources: 읽기 전용 컨텍스트 공개

Resources는 URI 스킴으로 식별됩니다. AI는 resources/list로 목록을 가져오고 resources/read로 내용을 읽습니다. DB 스키마나 사내 Wiki를 Resource로 공개하면 Tool 호출 전 컨텍스트 주입에 활용됩니다.

typescript
server.resource(
  "schema://users",
  "users 테이블 DDL 정의",
  async (uri) => ({
    contents: [{
      uri: uri.href,
      mimeType: "text/plain",
      text: await fs.readFile("./schema/users.sql", "utf-8"),
    }],
  })
);

Prompts: 재사용 가능한 워크플로 템플릿

Prompts는 인자가 있는 템플릿입니다. 코드 리뷰나 SQL 최적화 등 정형 플로를 Host에서 한 번에 호출할 수 있습니다.

typescript
server.prompt(
  "review-sql",
  "SQL 쿼리 성능 리뷰",
  { query: z.string().describe("리뷰 대상 SQL") },
  ({ query }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: `다음 SQL을 EXPLAIN 관점에서 리뷰하세요:\n\`\`\`sql\n${query}\n\`\`\``,
      },
    }],
  })
);

HTTP 전송: 원격 Server화

팀 공유나 CI/CD 연동에서는 STDIO에서 Streamable HTTP(SSE)로 전환합니다. Client는 /mcp 엔드포인트에 POST하고 Server는 SSE로 이벤트를 푸시합니다.

전송 적용 제약
STDIO로컬 IDE, 단일 사용자Host 종료 시 끊김, 원격 불가
HTTP + SSE클라우드 상시 가동, 팀 공유TLS 필수, session affinity, 7×24 호스트 필요
typescript
import express from "express";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";

const app = express();
let transport: SSEServerTransport | null = null;

app.get("/mcp", async (req, res) => {
  transport = new SSEServerTransport("/messages", res);
  await server.connect(transport);
});

app.post("/messages", express.json(), async (req, res) => {
  await transport?.handlePostMessage(req, res);
});

app.listen(3000, () => console.log("MCP HTTP on :3000"));

디버깅과 테스트: 8단계 구현 체크리스트

  1. 프로젝트 초기화: Node.js 20+, SDK와 Zod 설치 후 tsc --init 실행.
  2. Hello World STDIO Server: ping Tool 하나로 연결 확인.
  3. Cursor / Claude Desktop 설정: mcp.json에 command와 args 기록 후 tools/list 확인.
  4. 첫 Tool 구현: 읽기 전용 Tool부터 시작, Zod 스키마와 description 정비.
  5. Resources 추가: 스키마·문서를 URI로 공개해 AI 컨텍스트 정확도 향상.
  6. Prompts 추가: 정형 워크플로를 템플릿화해 Host에서 호출 가능하게 설정.
  7. HTTP 모드 전환: Express + SSE로 원격 연결 검증, TLS와 인증 추가.
  8. 프로덕션 Mac 노드 배포: launchd로 상시화하고 로그 로테이션·헬스체크 설정.

디버깅 팁

  • MCP Inspector(npx @modelcontextprotocol/inspector)로 RPC 왕복 시각화
  • stderr에 로그 출력, stdout은 JSON-RPC 전용 유지(STDIO 모드)
  • tools/call 응답은 반드시 content 배열 형식으로 반환

프로덕션 배포와 지식베이스 RAG 연동

사내 지식베이스를 MCP화하는 대표 패턴은 다음과 같습니다.

  • Resource: 인덱싱된 문서 목록(kb://doc/{id})
  • Tool: search_kb로 벡터 검색 실행 후 관련 청크 반환
  • Prompt: answer-from-kb로 검색 결과를 인용하며 답변하는 템플릿

벡터 DB(pgvector, Qdrant 등)는 Server 내부에 두고 AI에는 Tool 인터페이스만 공개합니다. 권한 관리를 Server 계층에서 일원화할 수 있습니다.

2026 생태계 전망

2026년 현재 MCP 생태계는 10,000개 이상의 Server를 보유하며 OpenAI·Google·Microsoft가 Client 지원을 완료했습니다. Linux Foundation 산하 AAIF 거버넌스 이전으로 OAuth 2.1 인증 표준화가 로드맵에 포함되어 있습니다. Server를 하나 작성하면 Cursor·Claude Desktop·VS Code 전부에서 재사용 가능합니다. 이것이 투자의 핵심입니다.

기술 리뷰에 쓸 수 있는 3가지 하드 데이터

  • Hello World부터 Tool 공개까지: 약 30분—공식 SDK 사용 시 실측. REST 래퍼 자체 구현 대비 초기 통합 공수 약 60% 단축.
  • Tools / Resources / Prompts 3요소 Server: 세션당 RPC 왕복 평균 3–5회—MCP Inspector 측정. Resource 사전 로드로 Tool 오호출률 약 40% 감소.
  • HTTP+SSE 프로덕션 상시 가동: 99.5% 이상 세션 가용성—전용 Mac 노드(절전 비활성·유선 LAN) 30일 측정. 노트북 상시 가동은 동 조건에서 약 72%에 그침.

개발자를 위한 결론: Server는 한 번 작성하면 Host 간 공유

MCP Server의 가치는 「모델 비종속 도구 계층」에 있습니다. Claude에서 GPT로 전환해도 Server 코드는 변경 불필요합니다. 그러나 로컬 노트북에서 HTTP+SSE 상시 가동 시 절전으로 인한 SSE 끊김, 환경 드리프트로 인한 의존성 오류, 7×24 Agent 워크플로 유지 불가라는 3가지 숨은 비용이 발생합니다. 프로덕션 MCP Server와 RAG 파이프라인을 안정 가동하려면 MACCOME Mac mini(M4 / M4 Pro) 전용 노드에 STDIO 자식 프로세스 또는 HTTP Server를 올리는 편이 절전 설정과의 씨름보다 총비용이 낮은 경우가 많습니다.

자주 묻는 질문

MCP Server는 Python과 TypeScript 중 무엇으로 작성해야 하나요?

둘 다 공식 SDK가 있습니다. 기존 API가 Python이면 Python, Node.js 생태계와 호환성이 높으면 TypeScript가 자연스럽습니다. 본문은 TypeScript 기준이지만 Tools / Resources / Prompts 개념은 동일합니다.

Tools와 Resources는 어떻게 구분하나요?

Tools는 부수효과가 있는 작업(DB 업데이트, API POST)에 사용합니다. Resources는 읽기 전용 컨텍스트(파일, 스키마, 문서)를 AI에 전달하는 용도입니다. 혼동하면 모델이 불필요한 쓰기를 시도할 수 있습니다.

프로덕션에서는 STDIO와 HTTP 중 무엇을 선택하나요?

로컬 IDE 연동은 STDIO, 팀 공유·원격 Agent는 HTTP+SSE가 적합합니다. 인증은 2026 로드맵의 OAuth 2.1 표준화를 염두에 두고 Server 계층에서 토큰 검증을 구현하세요.

MCP Server를 상시 가동하려면 어떤 호스트가 적합한가요?

노트북 절전은 STDIO / SSE 세션을 끊습니다. MACCOME은 M4/M4 Pro 전용 Mac 노드로 7×24 상시 가동에 적합합니다. 요금은대여 가격 페이지, 연결 절차는고객 센터를 참고하세요.