개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
AI

사내 REST API를 LLM이 직접 호출하게 만드는 법: TypeScript MCP 서버 구현과 Gateway 패턴

팀에서 AI 에이전트를 도입하려다 "그래서 우리 내부 API는 어떻게 연결해?"라는 질문에 막혀본 적 있으신가요? 저도 처음엔 LLM에게 사내 API 문서를 통째로 붙여넣거나, 직접 fetch 코드를 프롬프트에 넣어주는 방식을 시도했습니다. 잘 됩니다—딱 한 번만요. 그 다음 요청부터는 LLM이 엔드포인트 URL을 창작하거나, 인증 헤더를 잊어버리거나, 존재하지 않는 파라미터를 보내기 시작합니다.

MCP(Model Context Protocol) 서버 래핑은 기존 API 로직을 건드리지 않고 LLM이 구조화된 방식으로 사내 시스템을 호출할 수 있게 해주는 아키텍처 패턴입니다. 프롬프트에 API 명세를 욱여넣는 방식 대신, LLM이 표준화된 Tool 인터페이스를 통해 직접 함수를 호출하는 구조입니다. 이 글을 끝까지 읽으면 기존 REST API를 MCP Tool로 노출하는 TypeScript 코드를 작성할 수 있게 됩니다. Node.js + TypeScript 기본 환경이 설치되어 있다는 가정 하에 설명합니다.

Claude Code, Cursor, GitHub Copilot이 모두 MCP 클라이언트로 동작하는 지금, 사내 시스템을 MCP로 노출하는 것은 팀 전체의 AI 생산성 인프라를 구축하는 일이기도 합니다.


핵심 개념

MCP 래핑이란 무엇인가

아이디어 자체는 단순합니다. 기존 REST API 앞에 JSON-RPC 기반의 얇은 인터페이스 레이어를 추가하는 겁니다. LLM은 이 레이어를 통해 "어떤 함수를 호출할 수 있는지", "각 파라미터가 무엇인지"를 명확하게 파악하고, 타입 안전한 방식으로 호출합니다.

용어 정리: JSON-RPC는 JSON을 이용한 원격 프로시저 호출 프로토콜입니다. MCP는 이를 기반으로 LLM ↔ 외부 도구 간 표준 통신 방식을 정의합니다.

MCP는 세 가지 기본 추상화를 제공합니다:

추상화 역할 사내 API 매핑 예시
Tool LLM이 부수효과를 일으키거나 계산을 요청하는 함수 createTicket, triggerDeploy
Resource 읽기 전용 데이터 노출 사내 위키, API 문서, 코드베이스
Prompt 재사용 가능한 프롬프트 템플릿 특정 도메인 전용 지시문

사내 API 래핑에서 실질적으로 핵심이 되는 건 Tool입니다. 내부 API 엔드포인트를 Tool로 일대일 매핑하거나, 여러 엔드포인트를 의미 있는 단위로 묶어 하나의 Tool로 노출하는 작업이 대부분입니다.

Transport: stdio vs Streamable HTTP

Transport 선택은 서버의 배포 방식을 결정합니다. 2025년 3월 기준으로 기존 HTTP+SSE 방식은 공식 deprecated됐고, 두 가지 선택지가 남아있습니다:

Transport 사용 시나리오 특징
stdio 로컬 개발, 단일 사용자 CLI 도구 프로세스 간 표준 입출력, 설정 간단
Streamable HTTP 팀·조직 규모의 원격 서버 HTTP 기반, OAuth 2.1 필수, 스케일 아웃 가능

사내 API를 팀 전체에 노출하려면 Streamable HTTP + OAuth 2.1 조합이 권장 방식입니다. 로컬에서 빠르게 테스트할 때는 stdio로 시작하고, 팀 공용 서버로 올릴 시점에 Streamable HTTP로 전환하는 순서가 현실적입니다.

스키마 설계의 핵심

저는 처음에 내부 API의 중첩 객체 구조를 그대로 Tool 파라미터로 노출했다가, LLM이 metadata.labels[0].value를 labelsValue라는 존재하지 않는 필드로 보내는 상황을 겪었습니다. 그 뒤로 파라미터 평탄화를 원칙으로 삼게 됐습니다. 스키마 모호성 = LLM의 Tool 오호출이라는 등식이 실무에서 꽤 자주 성립합니다.

Before — 중첩 객체 그대로 노출 (LLM이 자주 오호출):

typescript
{
  metadata: z.object({
    labels: z.array(z.object({
      key: z.string(),
      value: z.string()
    }))
  })
}

After — 평탄화 (LLM이 정확하게 채움):

typescript
{
  label_key: z.string().describe("레이블 키 (예: env, team)"),
  label_value: z.string().describe("레이블 값 (예: production, backend)")
}

좋은 Tool 스키마의 원칙을 요약하면:

  • 파라미터는 가능한 한 평탄화(flatten) — 중첩 객체보다 단순 필드
  • describe()로 각 파라미터의 의미와 허용 값을 명시
  • enum 타입을 적극 활용해 LLM의 선택지를 제한

장단점 분석

MCP 래핑 도입을 결정하기 전에 트레이드오프를 먼저 살펴보는 게 좋습니다.

장점

항목 내용
빠른 통합 기존 API 로직 재작성 없이 MCP 인터페이스 레이어만 추가
멀티 클라이언트 지원 Claude, GPT, Copilot, Cursor 등 MCP를 지원하는 모든 클라이언트에서 동일하게 호출 가능
OpenAPI 자동화 스펙이 있으면 수작업 Tool 작성 최소화
표준 보안 레이어 OAuth 2.1 기반 인증 체계를 프로토콜 레벨에서 강제
에이전트 생산성 자연어 지시로 복잡한 사내 작업 자동화 가능

단점 및 주의사항

항목 내용 대응 방안
프롬프트 인젝션 Tool 설명이나 반환값에 악의적 지시가 포함되면 LLM이 의도치 않은 동작 수행 가능 반환 데이터를 신뢰하지 않는 원칙으로 설계, 출력 sanitization
컨텍스트 오염 Tool 수가 많으면 LLM 컨텍스트 윈도우가 Tool 설명으로 가득 차 성능 저하 도메인별 서버 분리, 동적 Tool 로딩 고려
스키마 복잡성 내부 API의 복잡한 객체를 단순화하는 재설계 필요 평탄화된 파라미터 구조, enum 적극 활용
감사 로그 부재 AI 에이전트의 API 호출 기록이 없으면 컴플라이언스 감사 불가 MCP 서버 또는 게이트웨이 레벨에서 모든 호출 로깅
권한 과잉 한 MCP 서버가 필요 이상으로 넓은 API 접근권을 가지는 경우 Tool별 최소 권한 원칙 적용

용어 정리: Tool Poisoning Attack은 MCP Tool의 description 또는 반환값에 악의적인 LLM 지시를 심어 에이전트가 의도치 않은 동작을 수행하도록 유도하는 공격 방식입니다. 외부에서 유입된 데이터(사용자 입력, 외부 API 응답)가 Tool 반환값에 포함될 수 있기 때문에 내부 시스템에서도 무시할 수 없는 위협입니다.

실무에서 가장 흔한 실수

  1. Tool 설명(description)을 대충 작성하는 것 — "티켓을 만든다" 같은 한 줄짜리 설명은 LLM이 언제, 어떻게 이 Tool을 써야 할지 파악하기 어렵게 만듭니다. 파라미터 각각에 구체적인 예시와 허용 값을 describe()로 명시하는 것이 Tool 호출 정확도에 직결됩니다.

  2. 인증 토큰을 Tool 파라미터로 받는 것 — LLM이 토큰을 직접 다루게 되면 로그에 토큰이 노출되거나 프롬프트 인젝션을 통해 탈취될 위험이 있습니다. 인증 정보는 서버 환경변수에서 주입하고 Tool 인터페이스에는 절대 노출하지 않는 것이 권장됩니다.

  3. stdio 서버를 팀 전체에 그대로 배포하는 것 — 로컬 개발용 stdio 서버를 팀 공용으로 그대로 쓰면 인증, 레이트 리밋, 감사 로그가 모두 없는 상태로 사내 API가 노출됩니다. 팀 규모가 되면 Streamable HTTP + OAuth 2.1로 전환하는 것이 권장됩니다.


실전 적용

TypeScript SDK 직접 구현: 사내 이슈 트래커를 Tool로 래핑하기

Jira, Linear, 혹은 자체 티켓 시스템의 REST API를 MCP Tool로 래핑하면, 에디터 AI에게 "이 버그 P1 티켓으로 만들어줘"라고 말하는 것만으로 실제 이슈가 생성됩니다.

먼저 의존성을 설치합니다:

bash
pnpm add @modelcontextprotocol/sdk zod axios

그다음 서버 코드입니다. internalClient가 어디서 왔는지 처음 보면 헷갈릴 수 있는데, axios 인스턴스를 환경변수에서 초기화하는 방식입니다:

typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from "axios";
 
// 인증 토큰은 환경변수에서 주입 — Tool 파라미터로 받지 않습니다
const internalClient = axios.create({
  baseURL: process.env.INTERNAL_API_URL,
  headers: {
    Authorization: `Bearer ${process.env.API_TOKEN}`
  }
});
 
const server = new McpServer({ name: "internal-issue-tracker", version: "1.0.0" });
 
server.tool(
  "create_ticket",
  "사내 이슈 트래커에 새 티켓을 생성합니다",
  {
    title: z.string().describe("티켓 제목 (명확하고 간결하게)"),
    priority: z.enum(["P1", "P2", "P3"]).describe("우선순위: P1=긴급, P2=높음, P3=일반"),
    assignee: z.string().email().optional().describe("담당자 이메일 주소")
  },
  async ({ title, priority, assignee }) => {
    const res = await internalClient.post("/issues", { title, priority, assignee });
    return {
      content: [{ type: "text", text: `티켓 생성 완료: ${res.data.id} — ${res.data.url}` }]
    };
  }
);
 
// 서버 시작 — 로컬 개발 시 stdio transport 사용
const transport = new StdioServerTransport();
await server.connect(transport);
포인트 설명
z.enum(["P1", "P2", "P3"]) LLM이 임의 값을 넣지 못하도록 선택지를 고정
.describe() 각 파라미터의 의미를 LLM에게 직접 전달
axios.create(...) 인증 헤더를 클라이언트 레벨에서 처리 — Tool 인터페이스에는 노출되지 않음
server.connect(transport) 이 줄이 있어야 서버가 실제로 실행됩니다

OpenAPI 스펙 자동 변환: 수작업 없이 MCP 서버 만들기

사내 API에 OpenAPI(Swagger) 스펙이 이미 있다면 Tool을 수작업으로 작성하지 않아도 됩니다. TypeScript 환경이라면 CLI 도구를 활용할 수 있습니다:

bash
npx openapi-mcp-generator --input ./api-spec.yaml --output ./mcp-server

Python 생태계라면 FastMCP의 OpenAPI 통합이 가장 빠릅니다:

python
from fastmcp import FastMCP
from your_internal_api import app  # 기존 FastAPI 앱
 
# FastAPI 앱을 MCP 서버로 변환
mcp = FastMCP.from_fastapi(app=app)
 
if __name__ == "__main__":
    mcp.run()

한 가지 솔직하게 짚어둘 점이 있습니다. from your_internal_api import app처럼 한 줄로 쓰면 간단해 보이지만, 실제로는 해당 FastAPI 앱의 의존성 설치, 환경변수 설정, DB 연결 등이 모두 함께 따라옵니다. 기존 앱과 같은 Python 환경에서 실행할 수 있다면 가장 빠른 방법이고, 환경이 분리되어 있다면 OpenAPI 스펙 파일을 export해서 openapi-mcp-generator로 처리하는 편이 현실적입니다.

OpenAPI 스펙의 operationId, summary, description이 그대로 Tool 이름과 설명으로 매핑됩니다. 스펙 품질이 곧 MCP Tool 품질이 된다는 점에서, 이미 잘 관리된 OpenAPI 스펙을 보유한 팀에게 특히 효과적입니다.

팀 규모 확장: MCP Gateway 패턴

팀이 여럿이 되고 도메인마다 MCP 서버를 각자 운영하기 시작하면 인증과 감사 로그가 제각각이 되는 문제가 생깁니다. 솔직히 이 시점에서 "그냥 각 서버에 토큰 하드코딩하면 안 되나"라는 생각이 드는데—그 선택이 나중에 보안 감사 때 발목을 잡습니다.

MCP Gateway는 이 문제를 해결하는 패턴입니다. Gateway는 MCP 프로토콜 레벨의 역방향 프록시로 동작합니다. AI 에이전트가 단일 엔드포인트(Gateway)에 연결하면, Gateway는 OAuth 2.1 인증을 처리한 뒤 내부적으로 각 도메인 MCP 서버로 Tool 호출을 라우팅합니다. 도메인 서버들은 인터넷에 노출되지 않고 Gateway 뒤에만 있으면 됩니다.

[Claude / AI Agent]
        |
        ↓ (단일 연결점)
[MCP Gateway]
  - OAuth 2.1 인증 처리
  - 레이트 리밋
  - 감사 로그 (모든 Tool 호출 기록)
  - 도메인별 서버로 라우팅
        |
   _____|_____
  |     |     |
  ↓     ↓     ↓
[HR   [CI/CD [Analytics
 API]  API]   API]
 MCP   MCP    MCP
 Srv   Srv    Srv
(내부망에만 존재)

실사용 사례로 검증된 도구들:

도구 특징
Kong AI MCP Proxy 기존 HTTP API를 MCP로 브릿징, 레이트 리밋·인증 통합
Azure API Management + Entra ID Microsoft 스택 환경에서 MCP + AD 페더레이션
mcp-gateway-registry Keycloak/Entra 연동 오픈소스 게이트웨이 레지스트리

MCP SDK 월간 다운로드가 18개월 만에 약 970배 성장한 배경에는 이런 Gateway 패턴의 엔터프라이즈 확산이 있습니다. 2026년 기준으로 이 패턴은 팀 규모 이상의 사내 AI 인프라에서 사실상 표준이 되고 있습니다.


마치며

사내 API를 MCP로 래핑하면 기존 코드는 그대로 두면서 팀 전체가 AI 에이전트를 통해 사내 시스템을 자연어로 다룰 수 있는 인프라가 만들어집니다.

지금 바로 시작해볼 수 있는 3단계입니다. 상황에 따라 선택하시면 됩니다:

  1. 사내 API에 OpenAPI 스펙이 있다면 npx openapi-mcp-generator --input ./your-api-spec.yaml --output ./mcp-server로 골격 코드를 먼저 생성해볼 수 있습니다. 이 경우 2단계(수작업 Tool 작성)는 건너뛰어도 됩니다.

  2. 스펙이 없다면 pnpm add @modelcontextprotocol/sdk zod axios를 설치하고, 팀이 매일 쓰는 API 하나(티켓 생성, 배포 상태 조회 등)를 위 TypeScript 예시를 참고해 Tool 하나로 먼저 변환해보시면 됩니다. Tool 하나만으로도 에이전트가 자연어 지시를 실제 호출로 바꾸는 경험을 바로 할 수 있습니다.

  3. Claude Code나 Cursor의 MCP 설정에 로컬 서버를 연결해 직접 사용해보시면 됩니다. 에이전트가 Tool을 호출할 때 어떤 파라미터를 채우는지 보면, 스키마에서 무엇을 개선해야 할지 바로 드러납니다. 이 단계를 거치고 나면 describe()와 enum이 왜 중요한지 실감하게 됩니다.


참고 자료

시작점으로 추천:

  • Wrapping an Existing API with MCP: How to Expose Your Current APIs to LLMs | Gun.io
  • How to build MCP servers with TypeScript SDK | DEV Community
  • OpenAPI 🤝 FastMCP | FastMCP 공식 문서

심화 학습:

  • Should you wrap MCP around your existing API? | Scalekit
  • MCP Best Practices: Architecture & Implementation Guide | modelcontextprotocol.info
  • From OpenAPI Spec to MCP Server: A Practical Guide | Xata
  • API MCP Server Architecture Guide | Stainless
  • What Is an MCP Gateway and Why Your Enterprise Needs One in 2026 | Composio
  • Advanced authentication and authorization for MCP Gateway | Red Hat Developer
  • Understanding Authorization in MCP | MCP 공식 문서
  • MCP Server Security Best Practices: 2026 Engineering Guide | Digital Applied
  • Model Context Protocol has prompt injection security problems | Simon Willison
  • From REST to MCP: An Empirical Study of API Wrapping | arXiv
#MCP#TypeScript#REST-API#LLM#Gateway패턴#OpenAPI#OAuth2#Zod#AI에이전트#JSON-RPC
공유하기

목차

핵심 개념MCP 래핑이란 무엇인가Transport:스키마 설계의 핵심장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수실전 적용TypeScript SDK 직접 구현: 사내 이슈 트래커를 Tool로 래핑하기OpenAPI 스펙 자동 변환: 수작업 없이 MCP 서버 만들기팀 규모 확장: MCP Gateway 패턴마치며참고 자료

추천 포스트

Pydantic AI로 LLM 응답을 타입 안전하게 검증하기
AI

Pydantic AI로 LLM 응답을 타입 안전하게 검증하기

LLM을 프로덕션에 붙여본 분이라면 이런 상황을 한 번쯤 겪어봤을 겁니다. GPT한테 JSON으로 응답하라고 시스템 프롬프트까지 꼼꼼히 썼는데, 정작 런타임에 가 터지는 상황. 아니면 가 어떤 날은 고 어떤 날은 인 상황. LLM 출력을 딕셔너리로 받아 쓰는 코드는 언제나 이런 시한폭탄...

2026년 06월 07일읽는 데 22분
Long-Horizon 에이전트 비용 60~90% 줄이기: 캐싱·압축·라우팅 전략
AI

Long-Horizon 에이전트 비용 60~90% 줄이기: 캐싱·압축·라우팅 전략

AI 에이전트를 프로덕션에 올리고 나서 처음 청구서를 받았을 때의 충격을 아직도 기억한다. 단순 챗봇이라면 예측 가능했을 텐데, 에이전트는 달랐다. 버그 하나 고치는 데 컨텍스트가 수만 토큰씩 쌓이고, 실패하면 그 비용 그대로 날리고 처음부터 다시. 단일 실행이 예상의 열 배를 넘어가는...

2026년 06월 07일읽는 데 24분
AI가 짜고 AI가 검토한다: `/code-review ultra` 멀티에이전트 파이프라인 구축기
AI

AI가 짜고 AI가 검토한다: `/code-review ultra` 멀티에이전트 파이프라인 구축기

솔직히 말하면, 저도 처음 이 개념을 들었을 때 "그게 실제로 돌아가?" 싶었습니다. 에이전트가 혼자 코드를 쓰는 것도 신기한데, 그 코드를 또 다른 에이전트가 검토하고 피드백까지 남긴다니. 그런데 2026년 현재, 이건 이미 실무에서 쓰이는 이야기입니다. 에이전틱 코딩 루프(Ag...

2026년 06월 07일읽는 데 20분
에이전틱 AI 설계 7대 패턴
AI

에이전틱 AI 설계 7대 패턴

Use + ReAct | KB, 티켓 DB 등 외부 시스템 반복 조회 | | 응답 작성 | 응대 에이전트 | Reflection | 발송 전 어조·정확도 자체 검토 | | 에스컬레이션 | 전체 파이프라인 | Human-in-the-Loop | 자동 해결 불가 시 담당자 연결 | ...

2026년 06월 06일읽는 데 9분
오픈웨이트 vs 클로즈드 AI 2026: 벤치마크 격차가 좁혀진 지금, 선택의 기준이 바뀌었다
AI

오픈웨이트 vs 클로즈드 AI 2026: 벤치마크 격차가 좁혀진 지금, 선택의 기준이 바뀌었다

솔직히 말하면, 1년 전까지만 해도 저는 클로즈드 모델이 한동안은 압도적 우위를 유지할 거라 생각했습니다. GPT-4급 성능을 내려면 OpenAI API 키를 꽂는 게 당연한 수순이었고, 오픈웨이트 진영은 "쓸 만은 하지만 아직..." 수준에 머물렀으니까요. 그런데 그 확신이 흔들리기 ...

2026년 06월 06일읽는 데 23분
Qwen3-Coder 로컬 실행: RTX 3090 한 장으로 SWE-bench 70% AI 코딩 에이전트 세팅하기
AI

Qwen3-Coder 로컬 실행: RTX 3090 한 장으로 SWE-bench 70% AI 코딩 에이전트 세팅하기

클라우드 AI 비용 청구서가 두 달 연속 두 배씩 오르는 걸 보면서 진지하게 대안을 찾기 시작했습니다. 솔직히 "오픈소스가 얼마나 하겠어"라는 편견보다, 이전에 몇 가지 오픈소스 코딩 모델을 써봤다가 실망했던 경험이 더 컸습니다. 특히 레거시 코드베이스에서 파일 여러 개를 넘나드는 리팩...

2026년 06월 06일읽는 데 22분