본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 11:44

50줄로 나만의 MCP 서버 만들기: AI 에이전트를 위한 실제 도구

요약

AI 에이전트가 외부 데이터 및 API와 상호작용할 수 있도록 MCP(Model Context Protocol) 서버를 구축하는 방법을 소개합니다. 단 50줄의 코드로 구현 가능한 모듈형 도구 서버 아키텍처를 통해 에이전트의 실행 능력을 확장하는 실용적인 가이드를 제공합니다.

핵심 포인트

  • LLM의 한계를 극복하기 위한 도구(Tool) 메커니즘의 중요성
  • 단일 구조 에이전트 대비 모듈형 도구 서버의 확장성 및 보안 이점
  • MCP를 활용한 에이전트와 외부 시스템 간의 가교 역할 구현
  • 단순성, 보안, 관찰 가능성을 중심으로 한 서버 설계 철학

AI 에이전트(AI agents)는 최근 저를 가장 설레게 하는 분야 중 하나입니다. 에이전트들은 언어 모델(Language Models)을 통해 복잡한 추론 능력을 갖추게 되었지만, 종종 실제 세상과 상호작용하는 데 어려움을 겪습니다. 그들은 "생각"할 수는 있지만, "행동"할 수는 없습니다. 바로 이 지점에서 에이전트에게 실제 "도구(tools)"를 제공하는 것이 그들의 능력을 근본적으로 변화시킵니다.

저에게 이러한 필요성이 생긴 것은 제조 ERP 내의 AI 기반 계획 에이전트가 실제 재고 데이터를 읽거나 주문을 생성해야 했을 때, 또는 제 사이드 프로젝트의 금융 계산기가 복잡한 데이터를 분석해야 했을 때였습니다. 이 포스트에서는 제가 어떻게 단 50줄의 코드로 자신만의 MCP(Model Context Protocol) 도구 서버를 구현하여 AI 에이전트가 실제 세상의 도구와 연결될 수 있도록 했는지, 그리고 이 과정에서 직면했던 과제들에 대해 설명하겠습니다. 저의 목표는 여러분의 에이전트가 단순히 말만 하는 것이 아니라, 실제로 행동할 수 있게 만드는 실용적이고 직접적인 방법을 보여주는 것입니다.

AI 에이전트와 실제 세상 사이의 가교: 왜 도구 서버인가?

대규모 언어 모델(LLMs)은 믿을 수 없을 정도로 강력합니다. 텍스트 이해, 요약, 코드 생성과 같은 많은 분야에서 탁월한 성능을 발휘합니다. 하지만 본질적으로 이들은 고립된 시스템입니다. 인터넷 접속 권한이 없고, 데이터베이스에 연결할 수 없으며, 외부 API를 호출할 수 없습니다. 에이전트가 "재고 수준을 확인해줘"라고 말할 때, 그들은 이 요청을 어떻게 수행해야 하는지 알지 못합니다. 바로 이 지점에서 에이전트가 외부 세계와 상호작용할 수 있도록 허용하는 "도구(tool)" 메커니즘이 필요합니다.

제조 기업을 위한 ERP를 개발하던 중, 저희의 AI 계획 에이전트(planning agent)는 실시간 생산 라인 상태를 조회하거나 새로운 생산 주문을 생성해야 했습니다. 단순히 에이전트에게 "생산 주문 생성"이라는 명령을 내리는 것만으로는 충분하지 않았습니다. 이 명령 뒤에는 특정 파라미터(parameter)와 함께 호출될 수 있는 API 엔드포인트(endpoint)가 반드시 있어야 했습니다. 이것이 제 도구 서버(tool server) 아이디어의 첫 씨앗이 되었습니다. 저는 단일 구조의 에이전트(monolithic agent) 대신, 독립적이고 집중된 도구들을 수용하는 서버를 구축하기로 했습니다. 이러한 접근 방식은 도구의 확장성(scalability)을 높이는 동시에 보안 및 결함 격리(fault isolation) 측면에서 상당한 이점을 제공합니다.

ℹ️ 단일 구조 에이전트(Monolithic Agent) vs. 모듈형 도구 서버(Modular Tool Server)

단일 구조 에이전트는 모든 도구 로직을 내부에 포함합니다. 초기에는 이것이 더 쉬워 보일 수 있지만, 도구의 수가 증가함에 따라 복잡성, 유지보수 및 보안 위험이 증가합니다. 반면, 모듈형 도구 서버는 각 도구를 별도의 API 엔드포인트로 제시하여 더 깔끔하고 확장 가능하며 관리하기 쉬운 아키텍처(architecture)를 제공합니다. 제 경험상, 모듈형 구조가 장기적으로 훨씬 더 지속 가능하다는 것을 알 수 있었습니다.

이 도구 서버는 에이전트와 현실 세계 사이의 가교 역할을 합니다. 에이전트가 작업을 완수하기 위해 어떤 도구가 필요한지 결정하면, 이 서버에 요청을 보냅니다. 서버는 요청을 처리하고, 관련 도구를 실행한 뒤, 그 결과를 에이전트에게 반환합니다. 이런 방식을 통해 저희의 에이전트들은 단순한 대화형 개체에서 행동을 취하는 개체로 변모합니다. 저에게 있어 이것은 AI를 실제 운영에 통합하는 데 있어 결정적인 단계였습니다.

도구 서버 아키텍처: 단순성과 보안에 집중한 나의 접근 방식

도구 서버를 설계할 때 저의 핵심 철학은 항상 단순성, 보안, 그리고 관찰 가능성(observability)이었습니다. 에이전트가 사용하는 도구들이 복잡한 인프라를 요구하지 않고, 빠르게 개발될 수 있으며, 쉽게 배포될 수 있는 것이 중요합니다. 그렇기 때문에 저는 처음에는 무거운 오케스트레이션(orchestration) 솔루션을 피하고 가벼운 웹 프레임워크(web framework)에 집중했습니다.

저는 보통 Python과 FastAPI를 선호합니다. FastAPI는 높은 성능을 제공하는 동시에 OpenAPI 스키마 (OpenAPI schemas)를 통해 자동화된 문서화를 제공하므로, 에이전트(agent)가 도구(tool)를 이해하기 더 쉽게 만들어 줍니다. 기본적인 아키텍처 (architecture)는 들어오는 요청을 처리하고, 어떤 도구를 호출할지 결정하며, 해당 도구를 실행한 뒤 결과를 반환하는 간단한 HTTP API (HTTP API)로 구성됩니다. 초기 MVP (Minimum Viable Product) 단계에서는 데이터베이스 (database)나 복잡한 메시지 큐 (message queue) 통합이 필요하지 않았습니다. 직접적인 API 호출 (API calls)만으로도 충분했습니다.

이러한 서버에서 보안 (security)은 결코 간과될 수 없습니다. 에이전트가 민감한 시스템에 접근하는 도구를 호출하는 경우가 많기 때문에, 각 호출을 누가 수행했는지, 권한 (authorization)이 있는지 확인하고 악의적인 사용을 방지하는 것이 매우 중요합니다. 저는 내부 도구용으로 JWT/OAuth2 토큰 패턴 (token patterns)을 사용했습니다. 에이전트에게 부여된 토큰이 사용할 수 있는 특정 도구에 대한 구체적인 권한을 포함하도록 보장했습니다. 또한, 도구 서버 자체에 기본적인 속도 제한 (rate limiting) 메커니즘을 설정하여, 에이전트가 오류 상태에 빠지거나 무한 루프 (infinite loop)에 진입하더라도 시스템이 과부하되는 것을 방지했습니다.

⚠️ 보안은 언제나 최우선 순위입니다

도구를 호출하는 AI 에이전트는 잠재적인 보안 리스크 (security risks)를 동반합니다. 각 도구 엔드포인트 (endpoint)를 적절히 인증하고, 들어오는 요청을 검증하며, 불필요한 권한을 피하는 것이 필수적입니다. 저는 각 도구가 최소 권한 원칙 (principle of least privilege)에 따라 작동하도록 했습니다. 예를 들어, 주식 조회 도구는 절대로 주식을 업데이트할 수 있는 권한을 가져서는 안 됩니다.

나아가, 도구가 오래 걸리는 작업 (long-running operations)을 수행할 가능성이 있는 경우, 저는 비동기 실행 (asynchronous execution)을 선택했습니다. 도구를 호출하는 에이전트가 무한정 기다리게 하고 싶지는 않기 때문입니다. 이러한 경우, 도구 서버는 도구를 비동기적으로 시작하고 에이전트에게 작업 ID (operation ID)를 반환할 수 있으며, 에이전트는 해당 ID로 작업 상태를 조회할 수 있습니다. 이는 사용자 경험을 개선하는 동시에 서버 리소스를 더 효율적으로 사용할 수 있게 해줍니다.

FastAPI를 이용한 기본적인 도구 서버 설정: 50줄로 시작하기

이제 가장 실용적인 부분인 50줄로 기본적인 FastAPI 도구 서버를 작성하는 방법을 알아보겠습니다. 저의 목표는 에이전트가 외부 세계와 상호작용할 수 있는 능력을 부여하는 최소한의 구조를 만드는 것입니다. 이 예제에서는 간단한 "재고 조회 (stock query)" 도구를 만들 것입니다. 에이전트는 이 도구를 호출하여 특정 제품의 현재 재고 수량을 확인할 수 있게 됩니다.

먼저, 필요한 라이브러리를 설치합니다: pip install fastapi uvicorn. 그런 다음, 다음 Python 코드를 main.py로 저장할 수 있습니다:

# main.py
from fastapi import FastAPI, HTTPException, Depends, status
from pydantic import BaseModel
...

이 코드 스니펫은 토큰 검증 (verify_token) 및 기본적인 재고 조회 (query_stock_tool) 도구를 갖춘 FastAPI 서버를 생성합니다. mock_stock_data 부분은 실제 데이터베이스나 외부 ERP API 호출을 시뮬레이션합니다. 저는 AGENT_API_KEY 환경 변수를 설정하여 간단한 인증 메커니즘을 추가했습니다. 이 코드를 실행하려면 터미널에 python main.py를 입력하기만 하면 됩니다. 약 40~50줄의 코드로 여러분의 AI 에이전트를 위한 보안이 적용된 도구 엔드포인트(endpoint)가 준비됩니다! 저는 이 코드를 실행하고 에이전트에 연결하는 데 2분이 채 걸리지 않았습니다.

에이전트 측면에서는, 일반적으로 함수 호출 (function_calling) 메커니즘이나 수동으로 생성된 OpenAPI 스키마(schema)를 사용하여 이 도구를 정의합니다. 예를 들어, 에이전트에게 다음과 같은 정의를 제공할 수 있습니다:

{
  "name": "query_stock",
  "description": "제품의 현재 재고 수량 및 위치를 조회합니다.",
...

에이전트는 이 정의를 사용하여 언제 어떤 파라미터(parameter)로 query_stock 도구를 호출할지 학습합니다. 그런 다음, 토큰을 포함하여 도구 서버로 HTTP POST 요청을 보냅니다. 이러한 단순한 구조가 우리 AI 에이전트에게 놀라운 능력을 부여합니다.

보안 및 관찰 가능성 (Security and Observability): 도구 서버의 필수 요소

도구 서버는 기능적일 뿐만 아니라 보안(Security)과 관찰 가능성(Observability)을 갖추어야 합니다. 제가 개발하는 모든 시스템과 마찬가지로, 이 두 가지 원칙은 이번 작업에서도 매우 중요했습니다. 특히 AI 에이전트와 같은 반자율 시스템(semi-autonomous systems)이 실제 세계의 리소스에 접근하는 상황에서는 보안 위험이 배가될 수 있습니다.

인증(Authentication)과 인가(Authorization)는 가장 첫 번째이자 가장 중요한 단계입니다. 위의 예시에서는 간단한 환경 변수(environment variable)를 사용했지만, 실제 운영 환경(production environment)이라면 반드시 JWT/OAuth2 토큰을 사용할 것입니다. 에이전트가 받는 토큰은 특정 파라미터를 가진 특정 도구만을 호출할 수 있도록 범위(scoped)가 지정되어야 합니다. 예를 들어, 영업 에이전트는 재고를 조회할 수 있어야 하는 반면, 생산 에이전트는 생산 주문을 생성할 권한을 가져야 합니다. 잘못된 인가는 에이전트가 예상치 못하거나 바람직하지 않은 행동을 수행하게 만들 수 있습니다. 이는 제가 이전에 DDoS 완화 계층(mitigation layer)을 설계할 때 겪었던 경험을 떠올리게 했습니다. 모든 들어오는 요청의 출처와 의도를 검증하는 것은 필수적이었습니다.

속도 제한(Rate limiting)은 폭주하는 에이전트나 악의적인 사용으로부터 도구 서버를 보호하기 위해 필수적입니다. 한 번은 에이전트가 무한 루프에 빠져 초당 500번씩 동일한 도구를 호출하는 것을 본 적이 있습니다. 이로 인해 제 백엔드 시스템에 불필요한 부하가 발생했고 심지어 서비스 중단까지 일어났습니다. 저는 즉시 fail2ban과 유사한 메커니즘을 구축해야 했습니다. 특정 시간 내에 너무 많은 요청을 보내는 에이전트의 IP나 토큰을 일시적으로 차단하는 방식이었습니다. 이는 Nginx 리버스 프록시(reverse proxy) 계층에서 구현하거나, FastAPI 내에서 미들웨어(middleware)로 직접 구현할 수 있습니다.

💡 Fail2ban 방식의 보호 조치

AI 에이전트가 예상치 못하게 너무 많은 요청을 보낼 경우를 대비해 속도 제한(rate limiting)이나 fail2ban과 유사한 도구를 사용하여 시스템을 보호하세요. 이는 악의적인 공격뿐만 아니라 에이전트의 오류 동작도 방지합니다. 저의 도구 서버에서는 FastAPI의 Limiter 미들웨어를 사용하여 초당 요청 수를 일정 수준으로 제한했습니다.

반면, 관측성 (Observability)은 에이전트가 무엇을 하고 있는지, 어떤 도구를 얼마나 자주 사용하는지, 그리고 그 도구들이 얼마나 성공적인지를 이해하는 데 필수적입니다. 저는 journald 통합을 통해 도구 서버 로그를 중앙 위치에 수집합니다. 각 도구 호출(tool call)에 대해 어떤 에이전트가 호출했는지, 어떤 도구가 호출되었는지, 입력 파라미터(input parameters)는 무엇인지, 그리고 반환된 결과는 무엇인지를 기록합니다. 또한 각 도구가 실행되는 데 걸리는 시간과 오류 발생 여부를 모니터링하기 위해 Prometheus 메트릭 (metrics)을 수집합니다. 이를 통해 에이전트 행동의 이상 징후나 도구 성능의 퇴보를 빠르게 감지할 수 있습니다. 예를 들어, 4월 28일에 한 도구의 평균 응답 시간이 50ms에서 500ms로 증가한 것을 발견했을 때, 저는 즉시 로그를 확인하여 문제의 근본 원인을 찾아냈습니다.

실제 통합 사례 및 직면한 과제

도구 서버를 구축하고 실행하는 것이 한 단계였다면, 진짜 도전 과제는 이러한 도구들을 실제 시스템과 통합하는 것이었습니다. 도구는 단순히 Python 함수가 아닙니다. 데이터베이스에 연결하거나, 외부 API를 호출하거나, 운영 체제 명령을 실행해야 할 수도 있습니다. 이러한 통합은 많은 기술적 과제를 동반합니다.

데이터베이스 통합은 제가 마주한 흔한 시나리오 중 하나였습니다. 제조 ERP 시스템에서 AI 계획 에이전트(AI planning agent)는 생산 주문을 승인해야 했습니다. 이 승인 프로세스에는 PostgreSQL의 여러 테이블에 데이터를 쓰는 작업이 포함되었습니다. 하지만 네트워크 지연으로 인해 승인이 때때로 실패하거나, 에이전트가 승인이 실패했다고 판단하여 다시 호출하는 경우가 발생했습니다. 이는 중복 레코드 생성이나 데이터 불일치 문제로 이어졌습니다. 이 시나리오에서 저는 도구의 멱등성 (idempotency)을 보장해야 했습니다. 즉, 동일한 요청이 여러 번 전송되더라도 시스템의 상태가 변경되지 않아야 합니다. 저는 API에 idempotency_key를 추가하고 데이터베이스에서 이 키를 확인하는 방식으로 이 문제를 해결했으며, 이 작업에는 2일이 소요되었습니다.

🔥 멱등성 오류(Idempotency Error) 및 데이터 불일치(Data Inconsistency)

도구를 여러 번 호출할 위험이 있는 모든 시나리오에서 멱등성(Idempotency)을 보장하는 것은 매우 중요합니다. 특히 금융 거래, 주문 생성 또는 생산 주문 배치와 같은 상황에서 멱등성이 보장되지 않는 도구는 심각한 데이터 불일치와 운영 문제를 초래할 수 있습니다.

트랜잭션 관리(Transaction management) 또한 또 다른 중요한 문제였습니다. 만약 도구가 여러 서로 다른 시스템에 데이터를 기록한다면, 이러한 작업들은 원자적(Atomic, 전부 성공하거나 전부 실패하거나)이어야 했습니다. 예를 들어, 주문 생성 도구가 ERP의 주문 테이블과 재고 시스템의 재고 테이블 모두에 항목을 기록한다면, 이 두 작업은 함께 성공하거나 함께 롤백(Rollback)되어야 했습니다. 분산 트랜잭션(Distributed transactions) 대신, 저는 일반적으로 transaction outbox 또는 이벤트 소싱 (Event-sourcing) 패턴을 사용하는 최종 일관성(Eventual consistency) 모델을 채택했습니다. 이는 시스템을 더 유연하게 만들었지만, 데이터 일관성을 보장하기 위해 추가적인 노력이 필요했습니다.

마지막으로, 도구들은 장시간 실행되는 작업(Long-running operations)을 수행할 가능성이 있었습니다. 예를 들어, 대규모 보고서를 생성하거나 복잡한 시뮬레이션을 실행하는 데는 몇 분이 걸릴 수 있습니다. 에이전트가 이렇게 오래 기다리는 것은 비실용적이었습니다. 이러한 경우, 저는 Redis와 같은 메시지 큐(Message queue)를 사용하는 비동기 처리(Asynchronous processing) 모델로 전환했습니다. 도구 서버는 요청을 큐에 넣고 즉시 작업 ID (Operation ID)를 반환합니다. 그러면 에이전트는 이 ID를 사용하여 주기적으로 작업 상태를 조회할 수 있습니다. 이 접근 방식은 에이전트가 차단(Blocked)되는 것을 방지하고 도구 서버가 더 많은 요청을 동시에 처리할 수 있게 해주었습니다. Redis의 OOM(Out of Memory) 축출 정책(Eviction policy) 선택과 커넥션 풀(Connection pool) 튜닝 또한 이 과정에서 제가 주의를 기울여야 했던 중요한 세부 사항들이었습니다.

다음 단계 및 에이전트 아키텍처에 대한 생각

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0