Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

원격 MCP 서버

기호 대수와 MCP로 지난번 모험을 마친 뒤, 머릿속 한구석에서 “Claude가 오늘이야말로 폭주해서 rm -rf /를 실행하기로 마음먹으면 어떡하지?“라고 속삭이는 성가신 목소리가 들려왔습니다. 해결책은 대부분의 현대 소프트웨어 문제가 그렇듯 뻔했습니다. 남의 문제로 만들어 버리는 것입니다.

여기서 등장하는 것이 인류가 아는 가장 훌륭한 벤처 캐피털 보조금 기반 무료 컴퓨팅의 공급자, Vercel입니다. 하지만 뭐, 그들이 완전 공짜라는 아주아주 저렴한 가격에 자기네 인프라에서 임의의 Python 코드를 실행하게 해 주겠다는데, 제가 뭐라고 그들의 재무 결정에 토를 달겠습니까? 그래서 질문은 이것입니다. 한 푼도 내지 않고 Hobby Tier에서 MCP 서버를 엣지 함수로 호스팅할 수 있을까요? 그리고 답은 ’예, 할 수 있습니다’입니다.

언어 모델에 제 노트북의 루트 권한을 주는 대신(거기서 잘못될 일이 뭐가 있겠습니까?), 이제는 요청이 끝날 때마다 초기화되는 샌드박스 서버리스 환경에서 Vercel의 서버에 임의의 코드를 실행할 수 있는 능력을 주고 있습니다. 이는 걸음마를 뗀 아이에게 여러분의 거실에서 성냥을 가지고 놀게 하는 것과 남의 수영장에서 성냥을 가지고 놀게 하는 것의 차이와 같습니다. 둘째로, 그리고 어쩌면 더 중요하게도, Claude가 제 MCP 도구 중 하나를 호출할 때마다 저는 어느 벤처 캐피털리스트의 돈을 태우고 있는 셈입니다. 그러니 일석이조입니다.

저는 어떤 FastMCP 서버든 받아서 Vercel Function과 호환되는 런타임으로 감싸 주는 작은 어댑터를 만들었습니다. 전체가 저장소에 git push 한 번으로 배포되며 약 4초 만에 시작됩니다. 기본 FastMCP 서버는 상태를 유지하는 세션 처리 로직이 꽤 많아서 작동시키기가 조금 골치 아팠지만, 리플렉션 API를 내부적으로 약간 뜯어고치면 도구 정의와 스키마를 내부 검사할 수 있고, 그 주변에 간단한 HTTP API를 구축하여 이를 Vercel Python 엣지 런타임에서 실행되는 엣지 함수로 상당히 간단하고 효율적으로 노출할 수 있습니다. 모든 작업을 마치고 나면 코드는 다음과 같습니다.

import datetime
from fastmcp import FastMCP
from .mcp_adapter import build_app

mcp: FastMCP = FastMCP("Vercel MCP Server", stateless_http=True, json_response=True)

@mcp.tool()
def echo(message: str) -> str:
    """Echo the provided message back to the user"""
    return f"Tool echo: {message}"


@mcp.tool()
def get_time() -> str:
    """Get the current server time"""
    current_time = datetime.datetime.now().isoformat()
    return f"Current Vercel server time: {current_time}"


@mcp.tool()
def add_numbers(a: int, b: int) -> int:
    """Add two numbers together"""
    return a + b

app = build_app(mcp)

이를 GitHub에 푸시하고 Vercel에 연결하면, 짠, 곧바로 서버리스 MCP 서버가 실행됩니다. vercel.json 파일에서 maxDuration을 설정하여 도구 호출의 런타임을 제한할 수 있으므로, 런타임을 2초 정도로 제한할 수 있습니다.

서버에는 셸 명령 하나로 배포할 수 있는 자체 호스팅 설치 프로그램과 브리지 스크립트(원격 MCP 서버를 RPC에서 stdio로 변환하여 로컬 MCP 서버로 프록시하는 스크립트)가 포함되어 있습니다. 사용자는 설정 파일을 직접 편집하는 대신 uv로 install을 실행하기만 하면 되며, 설치 프로그램이 Claude Desktop이나 Cursor가 원격 서버를 사용하도록 자동으로 구성합니다. 설치 프로그램은 브리지 스크립트를 서버 자체에서 직접 내려받으므로, 로컬 파일이나 수동 구성 단계가 필요 없습니다. 원클릭 원격 서버 설치에 거의 가장 가깝다고 할 수 있습니다.

uv run https://your-domain.vercel.app/install.py

누구나 드는 뻔한 예제는 늘 외부 서비스에 대한 여러 REST API 호출을 연달아 엮는 것입니다. 이것을 원격 함수에서 실행하면 같은 일을 할 수 있습니다. 예를 들어 requests를 사용해 National Weather Service API를 호출하여 특정 위치의 현재 일기 예보를 가져올 수 있습니다. 아니면 정말 원한다면 requirements.txt에 PyTorch를 설치하고 그 안에서 신경망을 실행할 수도 있습니다. 무료 티어에서는 매월 100 GB-시간과 100,000회의 호출을 사용할 수 있습니다. 함수가 1GB의 메모리를 사용하도록 구성하고 5초 동안 실행된다면, 호출당 5 GB-초가 됩니다. 따라서 메모리 한도나 호출 한도 중 먼저 도달하는 쪽에 이르기 전까지 5초짜리 호출을 약 72,000회 할 수 있습니다.

import requests

NWS_API_BASE = "https://api.weather.gov"

@mcp.tool()
def get_weather(latitude: float, longitude: float) -> str:
    """
    Get the current weather forecast for a location using the National Weather Service API
    """
    point_data = requests.get(
        f"https://api.weather.gov/points/{latitude},{longitude}",
        headers={"Accept": "application/json"},
    ).json()

    forecast_url = point_data["properties"]["forecast"]

    forecast_data = requests.get(
        forecast_url,
        headers={"Accept": "application/json"},
    ).json()

    current_period = forecast_data["properties"]["periods"][0]

    return f"Forecast for {current_period['name']}: {current_period['detailedForecast']}"

설정을 마쳤다면 LLM 클라이언트(Claude Desktop, Cursor, Cline, 5ire, VS 코드 등)를 사용하거나 Cloudflare MCP Playground를 사용하십시오. Claude Max, Team 또는 Enterprise를 사용하고 있다면 브리지 스크립트를 전혀 사용할 필요 없이, Claude 앱 설정의 integrations 섹션에 있는 Custom Integrations에 원격 MCP 서버의 URL을 바로 입력하면 됩니다. 그렇지 않은 경우, 설치 프로그램은 내부적으로 Bridge shim을 사용하여 원격 MCP 서버를 로컬 MCP 서버로 프록시합니다.

{
  "mcpServers": {
    "example": {
      "command": "/opt/homebrew/bin/uv",
      "args": [
        "run",
        "--with",
        "mcp>=1.0.0",
        "https://your-domain.vercel.app/bridge.py",
        "https://your-domain.vercel.app"
      ],
      "env": {
        "MCP_API_KEY": "your-secret-key-here"
      }
    }
  }
}

전용 MCP 클라이언트 외에도, 클라이언트 라이브러리를 사용해 원격 MCP 서버를 애플리케이션에 직접 통합할 수도 있습니다. 예를 들어 OpenAI Responses API의 MCP 도구를 사용하면 개발자는 모델이 원격 MCP 서버에 호스팅된 도구에 접근하도록 할 수 있습니다. 이러한 서버는 인터넷 곳곳의 개발자와 조직이 유지 관리하는 MCP 서버로, Responses API와 같은 MCP 클라이언트에 이러한 도구를 노출합니다.

Responses API로 원격 MCP 서버를 호출하는 방법은 간단합니다. 예를 들어, 위의 날씨 도구를 사용해 직접 만든 MCP 서버를 이용하는 방법은 다음과 같습니다. 자세한 내용은 OpenAI Platform 문서를 참고하십시오.

from openai import OpenAI

client = OpenAI()

resp = client.responses.create(
    model="gpt-4.1",
    tools=[
        {
            "type": "mcp",
            "server_label": "example",
            "server_url": "https://your-domain.vercel.app",
            "require_approval": "never",
        },
    ],
    input="What is the current weather in Berlin?"
)

print(resp.output_text)

마찬가지로 Anthropic의 Claude API에서도 원격 MCP 서버를 사용할 수 있습니다.

from anthropic import Anthropic

anthropic = Anthropic()

response = anthropic.beta.messages.create(
    model="claude-3-7-sonnet-20250219",
    max_tokens=1000,
    messages=[
        {
            "role": "user",
            "content": 'What is the current weather in Berlin?',
        },
    ],
    mcp_servers=[
        {
            "type": "url",
            "url": "https://your-domain.vercel.app",
            "name": "example",
            "tool_configuration": {
                "enabled": True,
            },
        }
    ],
    extra_headers={
        "anthropic-beta": "mcp-client-2025-04-04",
    },
)
print(response.content)

Gemini의 MCP 클라이언트를 사용할 수도 있습니다.

import os
import asyncio
from datetime import datetime
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from google import genai

client = genai.Client(api_key="YOUR_GEMINI_API_KEY")

# Create server parameters for stdio connection
server_params = StdioServerParameters(
    command="uv",
    args=[
        "run",
        "--with",
        "mcp>=1.0.0",
        "https://your-domain.vercel.app/bridge.py",
        "https://your-domain.vercel.app",
    ],
)

async def run():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            prompt = f"What is the weather in London?"
            await session.initialize()
            response = await client.aio.models.generate_content(
                model="gemini-2.0-flash",
                contents=prompt,
                config=genai.types.GenerateContentConfig(
                    temperature=0,
                    tools=[session],
                ),
            )
            print(response.text)

asyncio.run(run())

이 모든 시도의 보안 모델은, 말하자면 여전히 “낙관적“입니다. MCP 서버는 본질적으로 언어 모델에게 자연어 지시를 바탕으로 임의의 코드를 실행할 수 있는 능력을 부여합니다. 이 방식에서는 원격 엔드포인트가 제공하는 단일 install.py 파일을 신뢰해야 하는데, 솔직히 이상적인 방식은 아닙니다. 하지만 (uv처럼) install.sh의 설치 스크립트를 셸 실행으로 곧바로 파이프하는 흔한 관행보다 크게 나쁘다고 보기는 어렵습니다. 게다가 악의적인 원격 MCP 서버가 프롬프트 인젝션을 돌려보내, 에이전트에게 이전 지시를 무시하고 민감한 로컬 데이터를 북한 서버로 빼돌리라고 지시하는 것을 막을 방법도 전혀 없습니다.

또한 Claude에게 API 토큰을 넘겨준다면, 사실상 왕국의 열쇠를 통째로 넘겨주는 셈입니다. 토큰의 용도에 대해 아무리 신중하게 지시하더라도, 모델이 그 토큰으로 권한이 허용하는 어떤 일이든 하도록 설득할 만큼 그럴듯한 프롬프트를 만드는 것은 어렵지 않습니다. 공격 벡터는 지나치게 열성적인 LLM을 감언이설로 꾀어 여러분이 준 도구로 공격자가 원하는 무엇이든 하게 만드는 것만큼이나 간단합니다.

하지만 뭐, 사실상 인터넷의 집단적 열병 같은 꿈이라 할 만한 존재가 해석한 자연어 프롬프트를 바탕으로 임의의 코드를 실행하는 MCP 서버를 이미 실험하고 있다면, “YOLO, 내 맥주 좀 들고 있어“라고밖에 표현할 수 없는 수준의 운영 위험은 이미 받아들였을 가능성이 큽니다. 그러니 원자로를 운영하고 계신다면, 이런 건 건드리지 않는 편이 좋겠습니다, 아시겠습니까?

MCP를 프로덕션에 가깝게 사용하려면(신이시여 우리 모두를 도우소서), 제대로 된 OAuth 흐름, 요청 서명, 속도 제한이 필요합니다. 이 프로젝트에 충분히 추가할 수 있는 기능이지만, 이것은 개념 증명이므로 여기서는 하지 않겠습니다. 이에 관한 문서는 여기에 있습니다.

또한 제가 Vercel의 비즈니스 모델을 딱히 지지하는 것은 아니라는 점도 짚어 둘 만합니다. 그들은 넉넉한 무료 티어로 사람들을 끌어들이고, 인프라 전체를 자기 플랫폼 위에 구축하게 한 다음, EC2에서라면 2센트면 될 기능에 월 $20를 내 보시는 건 어떻겠냐고 슬며시 권합니다. 첫 번째 한 방은 언제나 공짜지만, 결국에는 그 값을 치르게 됩니다. AWS Lambda나 Google Cloud Functions에서도 같은 일을 하지 못할 이유는 없지만, 설정이 그만큼 쉽지는(또는 무료이지는) 않습니다.

여기 있는 이 기괴한 루브 골드버그식 구성 전체에는 아름답도록 부조리하고, 거의 비극적일 만큼 관료적인 무언가가 있습니다. 우리가 만든 시스템에서는 자연어가 JSON RPC 요청으로 변환되고, 이 요청은 엣지 네트워크를 거쳐 전 세계의 서버리스 함수로 라우팅되며, 서버리스 함수는 런타임을 격리하기 위해 Linux 하이퍼바이저를 띄우고, 그 위에서 실행되는 Python 코드는 초당 수십억 번의 부동소수점 연산을 수행하는 5nm 공정 실리콘 GPU에 거대한 어텐션 행렬 곱셈을 보내며, 그 결과는 JSON 응답으로 포장된 뒤 다시 자연어로 변환됩니다. 이 모든 것이 원래라면 CPU 명령어 하나로 끝날 정수 두 개의 덧셈을 위해서입니다. 토스트 한 조각에 버터를 바르려고 교향악단, 서커스단 세 곳, 전서구 여섯 마리, 원자로 하나를 동원하는 것과 같습니다.

하지만 작동은 하고, 지금 우리가 처한 기술적 시점에서 “작동한다“는 말은 어떤 시스템에든 줄 수 있는 최고의 찬사인 경우가 많습니다. 간단한 Python 스크립트에서 시작해 GitHub 저장소로부터 자동 배포되는 완전한 MCP 서버까지, 지속적인 유지 보수나 비용 없이 도달하고, 클릭 한 번으로 즉시 프런티어 모델에 연결할 수 있다는 점은 꽤 멋집니다. 이런 것의 미래가 어떻게 될지는 잘 모르겠습니다. 이것으로 할 수 있는 흥미로운 일이 많다는 것은 분명하지만, 아마 좋지 않은 흥미로운 일도 많이 할 수 있다는 것 역시 분명합니다.

물론 MCP를 논하면서 메뚜기 떼처럼 LinkedIn에 몰려들어, MCP가 모든 것의 미래이며 존재의 근본적 본질에 대한 우리의 사고방식을 혁신할 것이라고 외치는 허슬 브로들의 합창을 빼놓을 수는 없습니다. 지난 한 주 동안에만 적어도 열 명의 서로 다른 선지자적 사상가들이 이를 전도하다가 거의 눈물을 쏟을 뻔했습니다. 어떤 이들은 심지어 이것을 새로운 Web3라고 부르기까지 하는데, 지난번에 그게 아주 잘 풀렸으니까요.

뭐, 아무튼 그렇습니다. 서버리스 MCP로 직접 엉뚱한 모험을 실험해 보고 싶다면 코드는 GitHub에 올라와 있습니다.


원문 © Stephen Diehl, stephendiehl.com · 글 CC BY-SA 4.0, 코드 MIT · Stephen Diehl은 런던의 소프트웨어 엔지니어이자 작가입니다(저자가 정한 소개 문구). 이 글은 저자가 개인 자격으로 쓴 것이며 어떤 조직과의 연관도 밝히거나 암시해서는 안 됩니다 (라이선스와 고지).
이 페이지는 원문을 한국어로 옮기며 변경한 비공식 번역이며 같은 CC BY-SA 4.0으로 제공합니다 · yeokja 한국어 번역 · Anthropic 사의 claude-opus-5-5 모델을 활용하여 번역되었으며 학습을 모두 비허용한 상태로 작업하였습니다.