본문 바로가기

MCP

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

반응형

지금까지 만든 MCP 서버는 다 로컬이에요. 내 맥북 꺼지면 팀원은 못 써요.

로컬 MCP:
나만 씀, 내 맥북 꺼지면 끝

서버 MCP:
팀원 A, B, C 모두 같은 서버 연결
→ 토큰 한 곳에서 관리
→ 내 맥북 상태 상관없음

 


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

기존 stdio 방식 MCP를 HTTP 서버로 바꿔요.

기존 로컬 방식 (stdio):

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

HTTP 방식으로 변환:

# 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)

2단계 — Docker로 컨테이너화

# 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
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"}

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

Railway는 GitHub 연동하면 자동 배포돼요. 설정 거의 없어요.

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

# 로그인
railway login

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

Railway 대시보드에서 환경변수 설정:

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

배포되면 URL 발급돼요.

https://team-mcp-production.up.railway.app

4단계 — 팀원 Claude Code 연결

팀원들은 이렇게 연결해요.

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 조합하기

 

여러 MCP 조합하기 — DB + Slack + GitHub 연동으로 자동화 파이프라인 만들기

MCP 서버 하나씩 쓰면 편해요. 근데 여러 개를 조합하면 차원이 달라져요.혼자: "DB 조회해줘" → 결과 반환조합: "DB에서 결제 실패 급증하면 GitHub 이슈 자동 생성하고 Slack으로 팀에 알림 보내줘"

cell-devlog.tistory.com

MCP 서버 직접 만들기

 

MCP 서버 직접 만들기 — 내 서비스를 Claude에 연동하는 법

MCP(Model Context Protocol)는 Claude가 외부 서비스랑 직접 대화하게 해주는 표준 프로토콜이에요.기존에는 이랬어요.나: "우리 DB에서 오늘 주문 건수 알려줘"Claude: "저는 DB에 접근할 수 없어요. 직접 확

cell-devlog.tistory.com

 

반응형