MCP 서버 튜토리얼: 30분 만에 나만의 AI 도구 구축하기
요약
Model Context Protocol(MCP)을 사용하여 30분 만에 커스텀 AI 도구를 구축하는 튜토리얼입니다. 비즈니스 로직을 AI 오케스트레이션 레이어와 분리하여 재사용성, 테스트 가능성, 보안성을 높이는 방법을 다룹니다.
핵심 포인트
- MCP를 통한 AI 도구 발견 및 실행 방식의 표준화
- 관심사 분리를 통한 비즈니스 로직과 AI 에이전트의 결합 해제
- Zod 스키마 검증 및 NeuroLink SDK를 활용한 서버 구축
- 도구의 재사용성, 테스트 가능성, 보안성 및 자동 발견 이점
여러분은 30분 만에 데이터베이스 쿼리 도구, 알림 도구, 파일 작업 도구라는 세 가지 커스텀 AI 도구를 갖춘 MCP 서버를 구축하게 될 것입니다. 이 튜토리얼이 끝날 때쯤이면 Zod로 검증된 도구 스키마(schema), 속도 제한(rate limiting), 서킷 브레이커(circuit breaker) 회복 탄력성, 그리고 엔드 투 엔드(end-to-end) AI 도구 호출을 위한 NeuroLink SDK와의 완전한 통합을 갖춘 작동 가능한 MCP 서버를 보유하게 됩니다.
Model Context Protocol (MCP)은 비즈니스 로직을 AI 오케스트레이션(orchestration) 레이어로부터 분리합니다. 도구 로직을 애플리케이션에 하드코딩하는 대신, 서버에 도구를 정의하면 어떤 AI 에이전트라도 런타임(runtime)에 이를 발견하고 실행할 수 있습니다. 이제 서버를 설정하고 첫 번째 도구를 구축해 보겠습니다.
Model Context Protocol이란 무엇인가?
MCP는 AI 모델이 도구를 발견하고 실행하는 방식을 표준화합니다. 이 프로토콜은 명확한 라이프사이클(lifecycle)을 정의합니다: 서버가 스키마(schema)와 함께 도구를 등록하면, AI 에이전트가 연결 시점에 해당 도구들을 발견하고, 모델이 사용자의 요청에 따라 도구를 호출할 시점을 결정하며, 서버가 도구 로직을 실행하고, 그 결과가 모델로 다시 흘러 들어가 최종 응답에 포함됩니다.
sequenceDiagram
participant U as User
participant A as AI Agent
...
핵심 통찰은 관심사의 분리(separation of concerns)입니다. 여러분의 MCP 서버는 데이터베이스 쿼리, API 호출, 파일 작업과 같은 비즈니스 로직을 깔끔한 도구 인터페이스 뒤에 캡슐화합니다. AI 에이전트는 데이터베이스가 어떻게 작동하는지 또는 알림이 어떻게 전송되는지 알 필요가 없습니다. 그저 스키마가 정의한 매개변수(parameters)와 함께 도구를 호출할 뿐입니다.
이 패턴은 다음과 같은 몇 가지 실질적인 이점을 제공합니다:
- 재사용성 (Reusability): 동일한 MCP 서버가 여러 AI 에이전트, 다양한 모델 및 다양한 애플리케이션에 서비스를 제공할 수 있습니다.
- 테스트 가능성 (Testability): 도구는 정의된 입력과 출력을 가지므로, 격리된 상태에서 유닛 테스트(unit test)를 수행하기 쉽습니다.
- 보안 (Security): 도구 실행은 액세스 제어, 속도 제한(rate limiting) 및 감사 로깅(audit logging)을 제어할 수 있는 서버 측에서 발생합니다.
- 발견 (Discovery): 에이전트는 어떤 도구를 사용할 수 있는지와 이를 어떻게 호출하는지를 자동으로 학습합니다.
Step 1 -- MCP 서버 생성하기
createMCPServer() 팩토리 함수 (factory function)를 사용하여 서버를 생성하는 것부터 시작합니다. 서버에는 ID, 제목, 설명 및 카테고리가 필요합니다.
import { createMCPServer } from "@juspay/neurolink";
const server = createMCPServer({
...
category 필드는 발견 (discovery) 및 조직화를 위해 서버를 분류합니다. NeuroLink는 다음과 같은 카테고리를 지원합니다: aiProviders, frameworks, development, business, content, data, integrations, automation, analysis, 그리고 custom. 도구의 목적을 가장 잘 설명하는 것을 선택하세요.
서버 객체는 도구 등록을 보유하고 검증 및 실행을 위한 메서드를 제공하는 경량 컨테이너 (lightweight container)입니다. 이 객체는 HTTP 서버를 시작하거나 포트 (port)에서 대기하지 않습니다. 이는 어떤 애플리케이션에도 임베드 (embed)할 수 있거나, HTTP를 통해 노출하거나, 프로세스 내에서 직접 사용할 수 있는 도구들의 논리적 그룹화 (logical grouping)입니다.
Step 2 -- 도구 등록하기
도구 (Tools)는 MCP 서버의 핵심입니다. 각 도구에는 name (이름), description (설명, LLM이 언제 호출할지 결정하는 데 사용됨), parameters (매개변수) 스키마 (schema, 런타임 검증 및 타입 추론을 위해 Zod로 정의됨), 그리고 비즈니스 로직을 포함하는 execute (실행) 함수가 필요합니다.
import { z } from "zod";
// 도구 1: 데이터베이스 쿼리 도구
...
보안 (Security): 이 예제는 LLM이 임의의 SELECT 쿼리를 제출할 수 있도록 허용합니다. 프로덕션 (production) 환경에서는 읽기 전용 데이터베이스 역할 (read-only database role)을 사용하고, 쿼리를 승인된 테이블의 허용 목록 (allowlist)으로 제한하며, 생 SQL (raw SQL) 대신 Knex 또는 Drizzle과 같은 쿼리 빌더 (query builder)를 사용하는 것을 고려하십시오.
startsWith("SELECT")체크는 최소한의 방어책일 뿐이며,UNION또는 서브쿼리 (subqueries)를 통한 데이터 유출 (data exfiltration)을 방지하지 못합니다. 사용자 제공 값(예:limit)에는 항상 매개변수화된 쿼리 (parameterized queries)를 사용하고, 신뢰할 수 없는 입력을 SQL 식별자(테이블 또는 컬럼 이름)에 절대 보간 (interpolate)하지 마십시오.
{: .prompt-warning }
도구 정의(tool definitions)를 위한 몇 가지 중요한 설계 원칙은 다음과 같습니다:
이름보다 설명(Description)이 더 중요합니다. LLM은 도구를 언제 호출할지 결정하기 위해 설명을 읽습니다. 도구가 무엇을 하는지, 어떤 입력을 기대하는지, 그리고 무엇을 반환하는지를 명확하게 기술하는 설명을 작성하십시오. 모호한 설명은 잘못된 도구 선택으로 이어집니다.
Zod 스키마(schemas)는 계약(contracts)을 강제합니다. parameters 스키마는 도구가 수락하는 입력의 정확한 형태를 정의합니다. Zod는 런타임(runtime)에 입력을 검증하므로, LLM으로부터 전달된 잘못된 형식의 파라미터(parameters)를 비즈니스 로직이 실행되기 전에 잡아낼 수 있습니다. 각 필드에 .describe()를 사용하여 LLM에게 기대되는 값에 대한 힌트를 제공하십시오.
실행 함수(Execute functions)는 방어적이어야 합니다. Zod가 확인하는 것 이상의 입력을 항상 검증하십시오. 데이터베이스 도구 예시에서는 설명에 "읽기 전용(read-only)"이라고 명시되어 있음에도 불구하고 쿼리가 SELECT로 시작하는지 확인합니다. 이는 LLM이 항상 지침을 완벽하게 따르지는 않기 때문입니다.
참고: 도구 이름은 camelCase를 사용하고 서술적이어야 합니다. LLM은 도구가 적절한지 판단할 때 설명과 함께 이름을 사용합니다. "doThing"이나 "process"와 같은 일반적인 이름은 피하십시오. "queryDatabase"나 "sendNotification"과 같이 구체적인 이름이 모델에게 더 명확한 의도 신호(intent signals)를 제공합니다.
{: .prompt-info }
3단계 -- 도구 검증 (Validate Tools)
도구를 프로덕션(production)에서 사용하기 전에, 적절한 패턴을 따르는지 검증하십시오. validateServerTools() 함수는 등록된 모든 도구의 완전성과 정확성을 확인합니다.
import { validateServerTools, getServerInfo } from "@juspay/neurolink";
// 모든 도구 검증
...
검증 항목에는 다음이 포함됩니다: 도구 이름이 비어 있지 않은 문자열인지, 설명이 존재하고 의미가 있는지, 실행 함수를 호출할 수 있는지, 그리고 파라미터 스키마가 유효한 Zod 객체인지 확인합니다. 시작 시점에 검증을 실행하면 사용자 요청이 고장 난 도구에 도달하기 전에 구성 오류(configuration errors)를 조기에 발견할 수 있습니다.
getServerInfo() 함수는 서버 상태의 요약 정보를 제공합니다: 등록된 도구(tools)의 개수, 소속 카테고리, 그리고 버전 정보입니다. 이는 상태 확인(health check) 엔드포인트와 운영 대시보드(operational dashboards)에 유용합니다.
4단계 -- NeuroLink로 도구 사용하기
이제 MCP 도구를 NeuroLink SDK에 연결하여, LLM이 생성 과정 중에 도구를 발견하고 호출할 수 있도록 합니다.
import { NeuroLink } from "@juspay/neurolink";
import { tool } from "ai";
import { z } from "zod";
...
neurolink.generate()에 도구를 전달하면, LLM은 시스템 컨텍스트(system context)의 일부로 도구 스키마(tool schemas)를 전달받습니다. 그런 다음 사용자의 요청을 바탕으로 도구를 호출할지 여부를 결정합니다. 이 예시에서 모델은 아마도 주문 횟수를 가져오기 위해 queryDatabase를 호출한 다음, 요약 내용을 Slack에 게시하기 위해 sendNotification을 호출하고, 마지막으로 자연어 응답을 합성할 것입니다.
위임 패턴(delegation pattern, AI 도구가 MCP 서버 도구를 래핑하는 방식)을 사용하면 MCP 서버를 도구 로직의 단일 진실 공급원(single source of truth)으로 유지할 수 있습니다. AI SDK 도구는 실행을 MCP 서버로 전달하는 얇은 래퍼(thin wrappers) 역할을 합니다. 이는 한 곳에서 도구 로직을 업데이트하면 모든 소비자(consumers)가 자동으로 업데이트를 적용받을 수 있음을 의미합니다.
참고:
generate()의tools옵션은 AI SDK 형식의 도구를 허용합니다. MCP 서버의registerTool()은 약간 다른 형태를 사용합니다. 위에 제시된 래퍼 패턴은 이 두 형식을 깔끔하게 연결해 줍니다. 향후 NeuroLink는 MCP 도구의 직접적인 패스스루(passthrough)를 지원할 예정입니다.
{: .prompt-info }
5단계 -- 속도 제한(Rate Limiting) 및 서킷 브레이킹(Circuit Breaking) 추가
프로덕션 환경의 MCP 서버는 남용(abuse)과 연쇄 장애(cascading failures)로부터 보호되어야 합니다. NeuroLink는 MCP 도구 실행을 위해 특별히 설계된 내장 속도 제한(rate limiting) 및 서킷 브레이킹(circuit breaking) 기능을 제공합니다.
import {
HTTPRateLimiter,
MCPCircuitBreaker,
...
속도 제한기(rate limiter)는 단일 클라이언트가 도구를 과도하게 사용하는 것을 방지합니다. 분당 100개의 요청 제한이 설정되어 있다면, 제어되지 않는 에이전트 루프(runaway agent loop)가 상당한 비용을 발생시키거나 데이터베이스를 압도하기 전에 차단(throttled)될 것입니다.
서킷 브레이커(circuit breaker)는 도구 실행(tool execution)의 실패율을 모니터링합니다. 5회 연속 실패(데이터베이스 연결 시간 초과, API 중단 등)가 발생하면, 서킷이 열리며(opens) 실행을 시도하지 않고 즉시 에러를 반환합니다. 30초가 지나면 서킷은 반개방(half-open) 상태로 진입하여 단일 테스트 요청을 허용합니다. 만약 요청이 성공하면 서킷은 닫히고(closes) 정상 운영이 재개됩니다. 실패할 경우, 서킷은 다시 30초 동안 열린 상태를 유지합니다.
속도 제한(rate limiting)과 서킷 브레이킹(circuit breaking)을 결합하면, 복잡한 커스텀 구현 없이도 MCP 서버에 프로덕션급(production-grade) 탄력성을 부여할 수 있습니다.
도구 검증(Tool validation) 심층 분석
validateTool() 함수는 개별 도구에 대한 세밀한 검증을 제공하며, 개발 및 테스트 단계에서 유용합니다:
import { validateTool } from "@juspay/neurolink";
const isValid = validateTool({
...
검증 체크 항목은 다음과 같은 여러 카테고리를 포함합니다:
- 이름 검증(Name validation): 이름은 비어 있지 않은 문자열이어야 하며, 가급적 camelCase 형식을 권장합니다.
- 설명 검증(Description validation): 설명은 반드시 존재해야 하며, LLM(대규모 언어 모델)에게 의미 있는 맥락을 제공해야 합니다.
- 실행 검증(Execute validation):
execute필드는 호출 가능한 비동기 함수(async function)여야 합니다. - 파라미터 검증(Parameter validation): 파라미터가 제공되는 경우, 올바른 타입과 설명을 포함한 유효한 Zod 스키마(Zod schemas)여야 합니다.
CI/CD 파이프라인에서 검증을 실행하면 잘못된 형식의 도구 정의가 프로덕션 환경으로 배포되는 것을 방지할 수 있습니다.
아키텍처 개요
다음은 NeuroLink와 통합된 MCP 서버의 전체 아키텍처입니다:
flowchart TD
A[createMCPServer] --> B[MCP Server Instance]
B --> C[registerTool x3]
...
흐름은 간단합니다: 서버를 생성하고, 도구를 등록하고, 이를 검증한 다음 NeuroLink에 연결합니다. 생성 과정 중에 LLM은 필요에 따라 도구를 호출하며, 이때 속도 제한과 서킷 브레이킹이 모든 실행을 보호합니다. 결과는 LLM으로 다시 전달되어 최종 응답으로 합성됩니다.
MCP 도구 테스트하기
테스트 가능성 (Testability)은 MCP 패턴의 가장 강력한 장점 중 하나입니다. 도구 (tools)가 정의된 입력과 출력을 가지기 때문에, AI의 개입 없이도 유닛 테스트 (unit test)를 수행할 수 있습니다.
// 유닛 테스트 (Unit test)
const result = await server.tools["queryDatabase"].execute({
query: "SELECT COUNT(*) FROM orders WHERE date > '2025-01-01'",
...
통합 테스트 (integration testing)를 위해서는, 도구들과 함께 neurolink.generate()를 통해 전체 흐름을 실행하고, 모델이 각 도구를 호출해야 할 시점을 올바르게 식별하는지, 그리고 결과를 어떻게 해석하는지 확인하십시오. 통합 테스트를 빠르고 결정론적 (deterministic)으로 유지하기 위해 외부 의존성 (database, Slack, email)을 모킹 (Mock) 하십시오.
엣지 케이스 (edge cases)를 철저히 테스트하십시오. 데이터베이스가 0개의 행을 반환하면 어떻게 될까요? Slack API가 다운되면 어떻게 될까요? 파일이 존재하지 않으면 어떻게 될까요? 각 도구는 처리되지 않은 예외 (unhandled exceptions)를 던지는 대신, LLM이 우아하게 해석할 수 있는 구조화된 에러 응답을 반환해야 합니다.
// 엣지 케이스 테스트: 잘못된 SQL
const invalidResult = await server.tools["queryDatabase"].execute({
query: "DROP TABLE orders",
...
Tip: 예외를 던지기보다 항상 도구에서 구조화된 에러 객체를 반환하십시오. LLM은
{ success: false, error: "..." }응답을 해석하여 접근 방식을 조정할 수 있지만, 처리되지 않은 예외는 도구 호출 체인을 완전히 종료시켜 버립니다.
{: .prompt-tip }
실제 MCP 서버 패턴
기본적인 내용을 넘어, 실제 운영 중인 MCP 배포 환경에서 볼 수 있는 패턴들은 다음과 같습니다.
**복합 도구 (Composite tools)**는 여러 작업을 단일 도구 호출로 래핑 (wrap) 합니다. LLM이 "queryDatabase"를 호출한 다음 별도로 "sendNotification"을 호출하는 대신, "generateAndSendReport" 도구가 내부적으로 전체 워크플로우를 처리합니다. 이는 도구 호출 횟수를 줄이고 LLM이 중간 단계에서 실수를 할 가능성을 낮춥니다.
**매개변수화된 권한 (Parameterized permissions)**는 호출 컨텍스트 (context)에 따라 도구 접근을 제한합니다. 도구는 민감한 작업을 실행하기 전에 사용자의 역할을 확인할 수 있으며, 호출자에게 필요한 접근 수준이 없는 경우 권한 에러를 반환할 수 있습니다.
// MCP 도구 실행을 위한 인증 미들웨어 (Authentication middleware)
function withAuth(tool: MCPTool, requiredRole: string): MCPTool {
return {
...
**캐싱 레이어 (Caching layers)**는 재사용을 위해 빈번하게 발생하는 도구 결과값을 저장합니다. 만약 1분 동안 10명의 사용자가 "이번 달 주문량이 얼마인가요?"라고 질문한다면, 데이터베이스 도구는 데이터베이스를 10번 조회하는 대신 캐싱된 (cached) 결과를 제공할 수 있습니다.
**감사 로그 (Audit logging)**는 모든 도구 호출을 매개변수(parameters), 호출자, 타임스탬프(timestamp) 및 결과와 함께 기록합니다. 이는 AI가 무엇을 왜 수행했는지 증명해야 하는 규제 산업 분야에서 필수적입니다.
구축한 내용
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
