Cursor나 Claude Desktop에 사내 DB·API를 연결하려 하지만 REST 래퍼만으로는 AI가 실행 시점에 도구를 발견하지 못합니다. 본문은 MCP Server를 처음부터 작성하는 실전 절차를 제공합니다.① 환경 구성부터 Hello World, Tools / Resources / Prompts 3요소 구현; ② HTTP 전송 전환과 디버깅; ③ 프로덕션 배포 및 지식베이스 RAG 연동까지 8단계로 재현 가능합니다. 프로토콜 전체 그림은MCP와 AI 시대의 HTTP, 호스트 설정은Agent Skill 가이드와 함께 읽으세요.
tools/list 동적 발견이 빠져 AI가 파라미터를 오해합니다.-32602(Invalid params)나 -32000(Server error)를 올바르게 반환하지 않으면 AI가 재시도 불가 상태에 빠집니다.MCP(Model Context Protocol)는 Host·Client·Server 3계층으로 구성됩니다. Host(Cursor, Claude Desktop 등)가 내장 Client를 실행하고 Server와 JSON-RPC 2.0으로 통신합니다. Server가 공개하는 3가지 프리미티브가 구현의 핵심입니다.
| 프리미티브 | 역할 | 대표 유스케이스 |
|---|---|---|
| Tools | AI가 능동 호출하는 작업(부수효과 있음) | DB 쿼리, Slack 게시, 파일 쓰기 |
| Resources | 읽기 전용 컨텍스트 제공 | 설정 YAML, API 명세, DB 스키마 |
| Prompts | 재사용 가능한 프롬프트 템플릿 | 코드 리뷰 절차, 인시던트 대응 플로 |
본문은 TypeScript 공식 SDK(@modelcontextprotocol/sdk)를 기준으로 합니다. Python 버전(mcp 패키지)도 동등한 API 면을 갖추며 개념은 동일합니다.
node -v로 확인)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
첫 Server는 「연결 확인」이 목적입니다. STDIO 전송이면 추가 포트 없이 Cursor에서 즉시 테스트할 수 있습니다.
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);
Cursor 설정: Settings → MCP → Add Server에서 command: npx tsx src/index.ts를 지정합니다. tools/list에 ping이 표시되면 성공입니다.
프로덕션 Tool은 Zod 스키마로 파라미터를 자기 기술하고, 부수효과(쓰기·삭제)를 description에 명시합니다. 아래는 PostgreSQL 읽기 전용 Tool 예시입니다.
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는 URI 스킴으로 식별됩니다. AI는 resources/list로 목록을 가져오고 resources/read로 내용을 읽습니다. DB 스키마나 사내 Wiki를 Resource로 공개하면 Tool 호출 전 컨텍스트 주입에 활용됩니다.
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는 인자가 있는 템플릿입니다. 코드 리뷰나 SQL 최적화 등 정형 플로를 Host에서 한 번에 호출할 수 있습니다.
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\`\`\``,
},
}],
})
);
팀 공유나 CI/CD 연동에서는 STDIO에서 Streamable HTTP(SSE)로 전환합니다. Client는 /mcp 엔드포인트에 POST하고 Server는 SSE로 이벤트를 푸시합니다.
| 전송 | 적용 | 제약 |
|---|---|---|
| STDIO | 로컬 IDE, 단일 사용자 | Host 종료 시 끊김, 원격 불가 |
| HTTP + SSE | 클라우드 상시 가동, 팀 공유 | TLS 필수, session affinity, 7×24 호스트 필요 |
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"));
tsc --init 실행.ping Tool 하나로 연결 확인.mcp.json에 command와 args 기록 후 tools/list 확인.npx @modelcontextprotocol/inspector)로 RPC 왕복 시각화tools/call 응답은 반드시 content 배열 형식으로 반환사내 지식베이스를 MCP화하는 대표 패턴은 다음과 같습니다.
kb://doc/{id})search_kb로 벡터 검색 실행 후 관련 청크 반환answer-from-kb로 검색 결과를 인용하며 답변하는 템플릿벡터 DB(pgvector, Qdrant 등)는 Server 내부에 두고 AI에는 Tool 인터페이스만 공개합니다. 권한 관리를 Server 계층에서 일원화할 수 있습니다.
2026년 현재 MCP 생태계는 10,000개 이상의 Server를 보유하며 OpenAI·Google·Microsoft가 Client 지원을 완료했습니다. Linux Foundation 산하 AAIF 거버넌스 이전으로 OAuth 2.1 인증 표준화가 로드맵에 포함되어 있습니다. Server를 하나 작성하면 Cursor·Claude Desktop·VS Code 전부에서 재사용 가능합니다. 이것이 투자의 핵심입니다.
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 계층에서 토큰 검증을 구현하세요.