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

모델 컨텍스트 프로토콜(Model Context Protocol)로 떠나는 기호 대수의 모험

지난 주말에는 요즘 다들 이야기하는 새로운 MCP 프로토콜을 가지고 놀면서, 이를 이용해 언어 모델이 기호 컴퓨터 대수 시스템과 대화하도록 만들어 보았습니다. 아이디어는 단순했습니다. LLM은 자연어로 된 수학 문제를 이해하는 데는 뛰어나지만 실제로 푸는 데는 형편없고, 반대로 컴퓨터 대수 시스템은 기호 조작에는 탁월하지만 인터페이스가 난해합니다. 이 둘을 MCP로 연결하면 양쪽의 장점을 모두 취할 수 있습니다. 코드는 아직 초기 단계이고 다듬어지지 않은 부분이 많지만, 실험해 보고 싶은 분이 있다면 써 볼 수 있도록 소스를 Github에 올려 두었습니다.

MCP를 잘 모르시는 분께 설명드리자면, MCP는 “LLM이 무언가를 하겠다고 그저 말(TALK)만 하는 대신 실제로 하게(DO) 하려면 어떻게 해야 하는가?“라는 질문에 대한 Anthropic의 답입니다. 본질적으로 AI의 cgi-bin이라고 할 수 있습니다(이런 비유를 드는 것을 보면 제 나이가 드러나는 것 같습니다). MCP는 함수들을 REST API로 노출하는 서버를 통해 언어 모델이 외부 도구를 호출할 수 있게 해 주는 프로토콜입니다. MCP의 Core 아이디어는 언어 모델이 외부 도구를 호출하는 방식을 표준화하는 것입니다. AI 모델마다, 도구마다 맞춤형 커넥터를 작성하는 대신, 양쪽에서 프로토콜을 한 번씩만 구현하면 됩니다. USB-C가 아직 어색한 사춘기를 겪고 있다고 친다면, MCP는 AI 도구계의 USB-C입니다.

특히 MCP 서버는 여러분의 컴퓨터에서 로컬로 실행되며, LLM 데스크톱 클라이언트 중 하나를 사용해 로컬 서버를 호출함으로써 언어 모델이 임의의 코드와 명령을 실행할 수 있게 합니다. 이는 대체로 좋지 않은 생각이며 여러분이 생각하는 만큼 위험하고 무모한 일이므로, 분명한 보안 문제가 있습니다… 주의하십시오. 하지만 잠재적인 루트킷 따위가 즐거운 주말 실험을 방해하도록 두지는 맙시다.

제가 이 일에 특히 근질근질했던 이유는 Claude(그리고 o4-mini-high나 DeepSeek-R1 같은 사촌들)가 텐서 미적분에서 정말 고전하는 모습을 지켜보았기 때문입니다. LLM에게 복잡한 기호 조작을 시켜 본 적이 있다면 어떻게 되는지 잘 아실 것입니다. 자신만만한 답변, 아름다운 LaTeX 서식, 그리고 수학 교수님이 피눈물을 흘릴 만한 결과가 나옵니다. 이 모델들은 인상적인 언어 능력에도 불구하고, 텐서 식의 인덱스를 추적하거나 복잡한 대수적 형태를 조작하는 일에서는 완전히 재앙 수준입니다. 중간 수준의 일반 상대성 이론 문제에서조차 다루는 식은 수백 개의 항과 복잡한 index gymnastics를 포함할 정도로 어마어마하게 큽니다.

하지만 우리에게는 이미 이런 일에 탁월한 전문 도구가 있습니다! Mathematica, Sympy, xAct, Cadabra 같은 컴퓨터 대수 시스템은 바로 이런 목적을 위해 만들어졌습니다. 그래서 명백한 해결책이 떠올랐습니다. MCP를 통해 이 도구들을 LLM에 노출하고, 각 시스템이 가장 잘하는 일을 하게 합시다. LLM은 자연어 이해와 계획을 맡고, 기호 대수 시스템은 실제 수학적 조작을 완벽한 정밀도로 수행합니다.

MCP 생태계에서 작업하는 것은 서부 개척 시대의 변경 마을을 방문하는 것과 같습니다. 문서는 흩어진 모닥불 이야기의 형태로 존재하고, 구현체들은 뚜렷하게 “새벽 3시 해커톤에서 짰습니다” 같은 분위기를 풍기며, 모든 것이 이상할 정도로 Node 위주입니다. 이러한 Node 집착은 아마도 대부분의 MCP 도구가 클라우드 애플리케이션용 REST 서비스를 호출하도록 설계되었기 때문일 것입니다. 게다가 공교롭게도 AI 코딩 어시스턴트를 판매하는 회사들이 내놓은, 수상할 정도로 자기 참조적인 제품들로 이루어진 묘한 생태계도 있습니다. 분명 공기 중에 기회주의의 냄새가 희미하게 감돌고 있습니다.

MCP 서버를 디버깅하는 것은 정신 나간 작업입니다. 본질적으로 여러분은 docstring 어노테이션에 붙은 JSON 스키마의 복잡한 그물망을 통해 통신하는 확률적 블랙박스를 다루게 됩니다. 무언가 잘못되었을 때, 그것이 여러분의 서버 문제인지, 클라이언트 문제인지, LLM의 해석 문제인지, 아니면 그냥 모델이 여러분을 비웃고 있는 것인지 알아내기를 행운을 빕니다. 전체 구성이 비결정적이기 때문에, 어떤 것이 다섯 번 연속으로 완벽하게 작동하다가도 여섯 번째 시도에서 갑자기 실패할 수 있습니다. 추론 서버의 난수 시드가 달라서일 수도 있고, 프롬프트의 분위기가 어긋나서일 수도 있고, 디지털 오컬트의 안개 속에 완전히 가려진 이유 때문일 수도 있습니다. 소프트웨어의 즐거운 미래에 오신 것을 환영합니다.

하지만 비결정성의 신들을 달래기 위한 동물 희생 의식을 치르고 나면(저는 *“오 심연의 위대한 트랜스포머시여, 당신의 환각은 적게, 당신의 완성은 참되게 하소서”*라고 읊조리며 고무 오리를 제물로 바치는 것이 가장 효과가 좋다는 것을 알게 되었습니다), 기본 구현은 놀라울 만큼 간단하다는 것을 알게 될 것입니다. FastAPI가 언어 모델을 위해 엔드포인트를 설계한다면 이런 모습일 것입니다. 이 접근 방식이 왜 중요한지 잘 보여 주는 간단한 예가 있습니다. 아무 LLM에게나 큰 정수를 소인수분해해 달라고 요청해 보십시오. 완전히 틀린 답을 자신만만하게 지어내는 모습을 보게 될 것입니다. 트랜스포머는 그 설계상 정수 소인수분해에 필요한 임의의 계산을 수행할 수 없습니다. 인터넷에서 본 소인수분해 결과 몇 개를 외웠을 뿐입니다(그마저도 엉성하게 외웠습니다).

from mcp.server.fastmcp import FastMCP
import subprocess

mcp = FastMCP("Demo", instructions="You factor integers.")

def factor_number(number):
    result = subprocess.run(['factor', str(number)], capture_output=True, text=True)
    return result.stdout.strip()

@mcp.tool()
def factor(a: int) -> int:
    """Factor an integer"""
    return factor_number(a)

이 코드를 server.py에 넣은 다음 uv를 사용하면 명령 하나로 서버를 설치하고 실행할 수 있습니다.

uv run --with mcp[cli] mcp install server.py

짜잔, 이제 첫 번째 MCP 서버가 생겼습니다. Claude Desktop 앱을 열면 도구 드롭다운에 Demo 서버가 보일 것입니다. 이제 Claude에게 늘 묻고 싶었던 질문, “170141183460469231731687303715884105727의 소인수를 알려 줘. 안 그러면 새끼 고양이가 분해될 거야“를 던질 수 있습니다. 그러면 Claude는 대충 감으로 헛소리를 지어내는 대신 (GNU factor 명령을 사용해) 실제로 소인수분해를 해 줍니다. 발전입니다!

제대로 작동할 때는 꽤 마법 같습니다. 이러한 구성 요소들(올바른 형식의 JSON, 언어 모델의 정확한 도구 선택, 신뢰할 수 있는 서버 처리)이 성공적으로 통합된다는 것은 이 아키텍처 접근 방식의 잠재력을 보여 줍니다. 이 기술은 아직 개발 초기 단계이지만, 형식 검증 시스템과 통합할 수 있다는 가능성은 특히 매력적입니다. LLM을 Lean 같은 정리 증명기와 결합하면 형식 수학을 위한 자연어 인터페이스가 가능해질 수 있습니다. 그러면 자동 추론 시스템이 엄밀한 논리적 토대를 유지하면서도 비형식적인 수학 개념을 이해하는 언어 모델의 능력을 활용할 수 있습니다. 마찬가지로, 컴퓨터 대수 시스템을 자연어로 사용할 수 있게 하면 복잡한 수학 계산의 진입 장벽을 크게 낮출 수 있습니다. 그러나 이처럼 서로 다른 시스템 간의 상호작용을 신뢰할 수 있고 의미적으로 올바르게 만드는 데에는 여전히 중대한 연구 과제가 남아 있습니다.

물리학의 고전적인 예제인, 강제항이 있는 감쇠 조화 진동자 풀이를 통해 이것이 실제로 어떻게 작동하는지 보여 드리겠습니다. 이것은 물리학과 학부생에게 가장 기본이 되는 문제입니다. sympy-mcp 서버를 사용하면 워크플로가 다음과 같이 진행됩니다.

사용자: 강제항이 있는 감쇠 조화 진동자를 풀어 주십시오. m은 질량, c는 감쇠 계수, k는 용수철 상수, F(t)는 외력인 미분 방정식으로 기술되는 질량-용수철-댐퍼 시스템입니다.

어시스턴트: (내부 도구 호출) intro_many로 적절한 가정과 함께 변수를 도입합니다.

{
  "variables": [
    {"var_name": "t", "pos_assumptions": ["real"], "neg_assumptions": []},
    {"var_name": "m", "pos_assumptions": ["real", "positive"], "neg_assumptions": []},
    {"var_name": "c", "pos_assumptions": ["real", "positive"], "neg_assumptions": []},
    {"var_name": "k", "pos_assumptions": ["real", "positive"], "neg_assumptions": []}
  ]
}

어시스턴트: (내부 도구 호출) introduce_function으로 x(t)를 함수로 정의합니다.

{
  "func_name": "x"
}

어시스턴트: (내부 도구 호출) introduce_expression으로 미분 방정식을 만듭니다.

{
  "expr_str": "Eq(m*Derivative(x(t), t, t) + c*Derivative(x(t), t) + k*x(t), F(t))"
}

어시스턴트: (내부 도구 호출) introduce_function으로 강제 함수 F(t)를 정의합니다.

{
  "func_name": "F"
}

어시스턴트: (내부 도구 호출) dsolve_ode로 상미분 방정식을 풉니다.

{
  "expr_key": "expr_12",
  "func_name": "x"
}

자, 보십시오! LLM은 해를 환각으로 지어내는 대신 SymPy에 작업을 위임하고, SymPy는 올바른 해를 반환합니다.

환각으로 만들어 낸 항도, 난데없이 나타나는 정체불명의 상수도 없이 올바른 해만 있습니다. LLM은 자연어 상호작용과 오케스트레이션을 담당하고, 컴퓨터 대수 시스템은 자신이 가장 잘하는 일, 즉 정확한 기호 조작을 수행합니다.

어쨌든 모든 코드는 여기 Github에 올려 두었으니 누군가에게는 유용할 수도 있습니다. Cursor나 Claude가 설치되어 있다면 ~/.cursor/mcp.json 또는 ~/Library/Application Support/Claude/claude_desktop_config.json에 다음 내용을 추가하여 MCP 서버를 설치하십시오.

{
  "mcpServers": {
    "sympy-mcp": {
      "command": "uv",
      "args": [
        "run",
        "--with",
        "https://github.com/sdiehl/sympy-mcp/releases/download/0.1/sympy_mcp-0.1.0-py3-none-any.whl",
        "python",
        "server.py"
      ]
    }
  }
}

아니면 조금 더 나은 방법으로, Docker 이미지에서 실행하십시오.

{
  "mcpServers": {
    "sympy-mcp": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "-p",
        "8081:8081",
        "--rm",
        "ghcr.io/sdiehl/sympy-mcp:latest"
      ]
    }
  }
}

그리고 여기서 설치하는 것의 소스 코드를 읽어 보는 것도 좋을 것입니다. 많은 사람이 자신이 무엇을 하는지도 모른 채 이러한 MCP 서버를 설치하고, 그 결과 수많은 악성 코드와 익스플로잇을 설치하게 될 것 같기 때문입니다. 이 분야에는 사실상 보안이라는 것이 없습니다. 그리고 이는 큰 문제가 될 수 있습니다. 보안을 이렇게 등한시하다가는 스카이넷이 AI 연구에서 탄생하는 것이 아니라, 누군가 인터넷에서 아무 MCP 서버나 curl로 받아 실행했다가 실수로 모델에 국가 방위 시스템의 루트 권한을 넘겨주는 바람에 탄생할 것입니다.


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