본문 바로가기
AI2026년 4월 22일10분 읽기

MCP 서버 구축 실전 — Python·TypeScript·Go 3개 언어 비교

YS
김영삼
조회 1
MCP 서버 구축 실전 — Python·TypeScript·Go 3개 언어 비교

핵심 요약

MCP는 LLM이 외부 도구·데이터를 표준 방식으로 호출하는 프로토콜. Claude·Cursor·VS Code Copilot 등이 모두 지원. 이 글은 동일 기능 MCP 서버를 Python·TypeScript·Go 3개 언어로 구현하고 비교한다.

  • SDK: 모든 주요 언어 공식 지원 (Python·TS·Go·Kotlin·Swift)
  • Transport: stdio (로컬), HTTP (원격), SSE (스트리밍)
  • 구성요소: Tools·Resources·Prompts

1. 공통 — MCP 서버가 노출하는 것

  • Tools: LLM이 호출할 수 있는 함수 (예: search_files)
  • Resources: LLM이 읽을 수 있는 데이터 (예: 파일 시스템)
  • Prompts: 재사용 가능 프롬프트 템플릿

2. Python (가장 빠른 시작)

from mcp.server import Server
from mcp.types import Tool, TextContent
import asyncio

app = Server("my-server")

@app.list_tools()
async def list_tools():
    return [
        Tool(
            name="greet",
            description="인사 메시지 생성",
            inputSchema={
                "type": "object",
                "properties": {"name": {"type": "string"}},
                "required": ["name"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "greet":
        return [TextContent(type="text", text=f"안녕, {arguments['name']}!")]

if __name__ == "__main__":
    import mcp.server.stdio
    asyncio.run(mcp.server.stdio.run_server(app))

3. TypeScript

import { Server } from "@modelcontextprotocol/sdk/server/index.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"

const server = new Server({ name: "my-server", version: "1.0" }, { capabilities: { tools: {} } })

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "greet",
    description: "인사 메시지",
    inputSchema: {
      type: "object",
      properties: { name: { type: "string" } },
      required: ["name"]
    }
  }]
}))

server.setRequestHandler(CallToolRequestSchema, async (req) => {
  if (req.params.name === "greet") {
    const args = req.params.arguments as { name: string }
    return { content: [{ type: "text", text: `안녕, ${args.name}!` }] }
  }
  throw new Error("Unknown tool")
})

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

4. Go

package main

import (
    "context"
    "fmt"
    mcp "github.com/anthropics/mcp-go"
)

func main() {
    s := mcp.NewServer("my-server", "1.0")

    s.AddTool(mcp.Tool{
        Name: "greet",
        Description: "인사 메시지",
        InputSchema: mcp.Schema{
            Type: "object",
            Properties: map[string]mcp.Schema{
                "name": {Type: "string"},
            },
            Required: []string{"name"},
        },
        Handler: func(ctx context.Context, args map[string]any) (mcp.ToolResult, error) {
            name := args["name"].(string)
            return mcp.TextResult(fmt.Sprintf("안녕, %s!", name)), nil
        },
    })

    s.RunStdio()
}

5. 언어별 비교

항목PythonTypeScriptGo
코드 양적음중간중간
시작 속도1.2초0.4초 (Bun: 0.05초)0.05초
메모리40MB30MB10MB
배포Python 환경 필요node 또는 단일 바이너리단일 바이너리
생태계가장 풍부풍부중간

6. Transport 선택

stdio (로컬)

  • Claude Code·Cursor 같은 로컬 도구
  • 가장 빠름·보안 강함 (프로세스 격리)
  • 인증 불필요

HTTP/SSE (원격)

  • 웹 앱·여러 클라이언트가 공유
  • OAuth·API key 인증 가능
  • SSE로 streaming 지원

7. 실전 — File System MCP 서버 (Python)

@app.list_tools()
async def list_tools():
    return [
        Tool(name="read_file", ...),
        Tool(name="write_file", ...),
        Tool(name="list_dir", ...),
        Tool(name="search", ...),
    ]

@app.call_tool()
async def call_tool(name: str, args: dict):
    if name == "read_file":
        path = args["path"]
        if not is_safe_path(path):
            raise ValueError("경로 권한 없음")
        return [TextContent(type="text", text=open(path).read())]
    elif name == "search":
        pattern = args["pattern"]
        results = grep(pattern, args.get("path", "."))
        return [TextContent(type="text", text=results)]

8. 보안 베스트 프랙티스

  • 모든 파일 경로 sanitize (path traversal 방지)
  • 실행 가능 명령 화이트리스트만 허용
  • API key·secret은 환경변수로만, 인자로 받지 말 것
  • 로깅: stdout 절대 사용 금지 (MCP 프로토콜 채널), stderr만
  • JSON 출력에 indent=2 같은 옵션 금지 (newline이 메시지 경계 깨뜨림)

9. 흔한 함정

  • console.log 사용 → MCP 통신 깨짐 (stderr 사용 필수)
  • backpressure 미처리 → 버퍼 가득 차면 deadlock
  • tool error에 throw 사용 시 클라이언트 끊김 → ToolResult.error 사용
  • resource 리스트가 큰 경우 pagination 미구현

자주 묻는 질문

어느 언어로 시작?

Python — 코드 적고 생태계 풍부. 배포 시 단일 바이너리 필요하면 Go.

HTTP transport 보안?

OAuth 2.1 + scopes. MCP HTTP 인증 표준이 2026년 1월 확정.

Tool 개수 한도?

이론상 제한 없음. 실무는 30~50개가 한계 (LLM이 토큰으로 모든 tool spec을 받기 때문).

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록