본문 바로가기

MCP

MCP 서버 팀 배포 가이드 — 로컬에서 서버로 올려서 팀 전체가 쓰기

반응형

 

지금까지 만든 MCP 서버는 다 로컬이에요. 내 맥북 꺼지면 팀원은 못 써요. 로컬 MCP는 나만 쓸 수 있고 내 맥북이 꺼지면 그대로 끝나버리는 구조인데, 서버에 올려서 운영하는 MCP는 팀원 A, B, C가 전부 같은 서버에 연결해서 쓸 수 있어요. 토큰도 한 곳에서 관리할 수 있고 내 맥북 상태와 상관없이 24시간 돌아간다는 게 가장 큰 차이예요.

1단계 — MCP 서버를 HTTP로 변환

기존 stdio 방식 MCP를 HTTP 서버로 바꿔요. 원래 로컬 방식은 stdio_server를 통해 표준 입출력으로만 통신하기 때문에 같은 머신 안에서만 동작해요.

# 로컬에서만 동작
async with stdio_server() as (read, write):
    await app.run(read, write, ...)

이 방식은 프로세스가 같은 머신에 떠 있어야만 동작하기 때문에 팀 단위 배포에는 쓸 수 없어요. 이걸 HTTP로 바꾸려면 SSE(Server-Sent Events) 전송 방식을 쓰는 Starlette 기반 서버로 다시 짜야 해요.

# server.py
from mcp.server import Server
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route, Mount
from starlette.responses import JSONResponse
import uvicorn
import os

app = Server("team-mcp")

# 툴 정의 (기존과 동일)
@app.list_tools()
async def list_tools():
    return [...]

@app.call_tool()
async def call_tool(name, arguments):
    ...

# HTTP + SSE 서버로 변환
sse = SseServerTransport("/messages")

async def handle_sse(request):
    # 인증 체크
    api_key = request.headers.get("X-API-Key")
    if api_key != os.environ["MCP_API_KEY"]:
        return JSONResponse({"error": "Unauthorized"}, status_code=401)

    async with sse.connect_sse(
        request.scope, request.receive, request._send
    ) as streams:
        await app.run(streams[0], streams[1], app.create_initialization_options())

starlette_app = Starlette(
    routes=[
        Route("/sse", endpoint=handle_sse),
        Mount("/messages", app=sse.handle_post_message),
        Route("/health", endpoint=lambda r: JSONResponse({"status": "ok"}))
    ]
)

if __name__ == "__main__":
    uvicorn.run(starlette_app, host="0.0.0.0", port=8000)

list_tools와 call_tool 함수는 기존 stdio 버전 그대로 가져오면 되고, 새로 추가된 부분은 SSE 연결과 API 키 인증, 헬스체크 엔드포인트예요. 특히 handle_sse 안에서 X-API-Key 헤더를 검증하는 부분이 핵심인데, 이게 없으면 URL만 아는 누구나 서버에 접근할 수 있게 돼요.

2단계 — Docker로 컨테이너화

서버 코드가 준비됐으면 이제 어디서든 똑같이 띄울 수 있도록 컨테이너로 묶어야 해요. Dockerfile은 Python 슬림 이미지 위에 의존성을 설치하고 코드를 복사하는 단순한 구조예요.

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["python", "server.py"]

requirements.txt에는 서버 실행에 필요한 패키지를 정리해두면 돼요.

# requirements.txt
mcp
uvicorn
starlette
asyncpg
httpx
python-dotenv

이렇게 두 파일만 준비되면 로컬에서 바로 빌드하고 테스트할 수 있어요.

# 로컬 테스트
docker build -t team-mcp .
docker run -p 8000:8000 \
  --env-file .env \
  team-mcp

# 헬스 체크
curl http://localhost:8000/health
# {"status": "ok"}

헬스 체크 응답으로 {"status": "ok"}가 떨어지면 컨테이너가 정상적으로 떠 있다는 뜻이고, 이 상태에서 바로 배포 단계로 넘어가면 돼요.

3단계 — Railway 배포 (제일 간단)

Railway는 GitHub 연동하면 자동 배포돼요. 설정 거의 없어요. CLI로 직접 배포하고 싶다면 아래처럼 설치하고 로그인한 다음 init과 up만 실행하면 끝이에요.

# Railway CLI 설치
npm install -g @railway/cli

# 로그인
railway login

# 프로젝트 생성 및 배포
railway init
railway up

명령어 네 줄이면 배포가 끝나는데, 이렇게 배포한 다음에는 Railway 대시보드에서 환경변수를 따로 설정해줘야 해요. DB 접속 정보나 외부 서비스 토큰처럼 민감한 값들을 여기에 넣어두면 코드에는 전혀 노출되지 않아요.

CLAUDE_DATABASE_URL = postgresql://claude_readonly:...
GITHUB_TOKEN = ghp_...
SLACK_BOT_TOKEN = xoxb-...
MCP_API_KEY = team-secret-key-here  ← 팀 공유용 키

이 중에서 MCP_API_KEY만 팀원들에게 공유하면 되고, 나머지 값들은 서버에만 남아 있어요. 배포가 끝나면 https://team-mcp-production.up.railway.app 형태의 URL이 발급되는데, 이 주소가 곧 팀원들이 연결할 엔드포인트가 돼요.

4단계 — 팀원 Claude Code 연결

팀원들은 발급받은 URL과 API 키만 있으면 바로 연결할 수 있어요. claude mcp add 명령어로 등록하는 방법이 가장 간단해요.

claude mcp add team-mcp \
  --transport http \
  --header "X-API-Key: team-secret-key-here" \
  https://team-mcp-production.up.railway.app/sse

매번 같은 명령어를 치기 번거롭다면 프로젝트 안에 설정 파일로 넣어두는 방법도 있어요. .claude/mcp.json에 아래처럼 작성해두면 프로젝트를 열 때마다 자동으로 연결돼요.

{
  "mcpServers": {
    "team-mcp": {
      "type": "http",
      "url": "https://team-mcp-production.up.railway.app/sse",
      "headers": {
        "X-API-Key": "${MCP_API_KEY}"
      }
    }
  }
}

팀원은 .env에 MCP_API_KEY만 넣으면 돼요. DB 비밀번호, GitHub 토큰 같은 민감한 정보는 서버에만 있고 팀원한테 공유 안 해도 돼요.

로컬 vs 서버 배포 비교

항목 로컬 MCP 서버 배포 MCP

설정 개인 .env 서버 환경변수
팀 공유 각자 토큰 발급 MCP_API_KEY만 공유
가용성 내 맥북 켜져 있을 때만 24/7
비용 무료 Railway 약 $5/월
관리 개인 관리 중앙 관리

소규모 팀(3인 이하)이면 로컬로 충분해요. 5인 이상이면 서버 배포가 편해요.

마무리

결국 MCP 서버를 로컬에 둘지 서버로 올릴지는 팀 규모와 가용성 요구사항에 따라 갈리는 선택이에요. 인원이 적고 가볍게 쓰는 단계라면 로컬 stdio 방식으로도 충분히 버틸 수 있지만, 팀이 커지면서 누군가의 맥북 상태에 의존하는 구조는 금방 한계에 부딫혀요. HTTP로 전환하고 Docker로 묶어서 Railway 같은 플랫폼에 올려두면 토큰 관리와 가용성 문제를 한 번에 해결할 수 있고, 팀원은 API 키 하나만 받아서 바로 연결하면 돼요. 보안 측면에서는 X-API-Key 인증과 민감 정보의 서버 측 보관이 핵심이니, 배포 전에 이 부분만큼은 꼭 한 번 더 점검해보는 게 좋아요.


 

반응형