지난 장까지 우리는 꽤 괜찮은 MCP 서버를 만들 수 있게 되었다. 도구도 만들고, 리소스도 내놓고, 프롬프트도 다뤘다. 여기쯤 오면 자연스럽게 이런 생각이 든다.
"좋아. 서버는 만들었다. 그런데 이걸 내 앱에서 어떻게 쓰지?"
바로 그 질문이 이번 장의 핵심이다.
지금까지는 Claude Desktop, VS Code, Cursor 같은 이미 준비된 호스트에 MCP 서버를 붙이는 입장이었다. 그런데 이제는 반대편으로 가 볼 차례다. 당신의 웹 앱, 데스크톱 앱, 사내 업무 도구, 자동화 시스템이 직접 MCP 클라이언트를 품을 수 있다. 다시 말해, 남의 무대에 손님으로 들어가는 단계를 지나, 이제 당신의 앱이 MCP 세계의 중심이 되는 것이다.
이번 장에서는 자체 애플리케이션에 MCP 클라이언트를 붙이는 구조, 여러 서버를 동시에 다루는 오케스트레이션 방식, 그리고 사용자에게 자연스럽게 보이도록 UI에 녹여내는 방법까지 차근차근 살펴보자.
왜 내 앱이 직접 MCP를 품어야 할까
이미 잘 만든 호스트 앱들이 있는데 굳이 직접 만들 필요가 있나 싶을 수 있다. 하지만 제품을 만들기 시작하면 이유가 아주 선명해진다.
첫째, 사용자 경험을 직접 설계할 수 있다. 사용자는 단순한 채팅창만 원하지 않을 때가 많다. 실행 이력, 승인 버튼, 결과 미리보기, 대시보드, 폼 입력, 상태 표시 같은 요소가 함께 있어야 일이 편해진다.
둘째, 여러 서버를 하나의 흐름으로 묶을 수 있다. 검색 서버로 자료를 찾고, 파일 서버에서 문서를 읽고, Git 서버에서 코드를 확인하고, 데이터베이스 서버에서 수치를 가져온 뒤, 마지막에 보고서 서버로 정리하는 식의 흐름을 하나의 앱 안에서 조율할 수 있다.
셋째, 권한과 정책을 당신이 쥘 수 있다. 읽기 도구는 자동 실행해도 괜찮지만, 삭제나 전송처럼 민감한 행동은 반드시 사용자 승인을 받도록 만들 수 있다. 이런 세밀한 설계는 자체 앱에서 훨씬 잘 된다.
쉽게 말하면 이렇다. MCP 서버는 능력자이고, 앱은 감독이다. 능력자만 많이 모아 둔다고 공연이 완성되지는 않는다. 누가 언제 등장하고, 무엇을 먼저 하고, 어디까지 허용할지를 정하는 사람이 필요하다.
이번 장의 주인공: Host로서의 내 앱
MCP 구조를 다시 떠올려 보자. 호스트는 사용자를 만나는 애플리케이션이고, 클라이언트는 그 안에서 서버와 통신하는 모듈이며, 서버는 기능을 제공하는 쪽이다.
이번 장에서 중요한 것은 호스트 하나가 여러 클라이언트를 가질 수 있다는 점이다. 그리고 각 클라이언트는 보통 하나의 서버와 세션을 유지한다. 즉, 앱 하나가 여러 MCP 서버를 동시에 관리하는 그림이 자연스럽다.
머릿속에 이렇게 그리면 편하다.
사용자 → 내 앱(Host) → 여러 MCP 클라이언트 → 여러 MCP 서버
이 구조가 중요한 이유는 분명하다. 서버끼리는 서로를 직접 알 필요가 없고, 사용자는 복잡한 연결을 알 필요도 없다. 모든 조율은 호스트인 당신의 앱이 맡는다. MCP의 멋짐은 바로 이 분업에서 나온다.
가장 기본적인 통합 방식: 앱 백엔드에 MCP 클라이언트 넣기
실전에서는 브라우저나 프런트엔드가 직접 MCP 서버를 제어하기보다, 앱의 백엔드가 MCP 클라이언트를 품는 방식이 가장 다루기 쉽다.
이렇게 하면 서버 실행 명령, 환경 변수, 인증 정보, 권한 정책을 백엔드에서 안전하게 관리할 수 있다. 사용자는 화면에서 질문만 던지고, 실제 MCP 통신은 백엔드가 책임진다.
예를 들어 사내 업무용 웹 앱이 있다고 해 보자. 사용자가 "이번 주 에러 로그를 요약해 줘"라고 입력하면 앱은 대략 다음 순서로 움직인다.
어떤 MCP 서버들이 연결되어 있는지 확인한다.
필요한 도구 목록을 조회하거나 캐시에서 읽는다.
모델이 적절한 도구를 고르도록 돕는다.
MCP 클라이언트로 해당 도구를 호출한다.
결과를 자연어와 UI로 가공해 사용자에게 보여준다.
여기서 중요한 것은 앱이 단순 전달자가 아니라는 점이다. 앱은 결과를 예쁘게 보여주고, 위험한 작업은 막고, 반복 호출은 줄이고, 오류가 나면 다시 시도하게 만든다. 즉, MCP를 제품으로 바꾸는 마지막 손질을 담당한다.
단일 서버부터 붙여 보자
처음부터 멀티 서버 오케스트라를 꿈꾸면 머리가 바빠진다. 첫걸음은 늘 서버 하나면 충분하다. 예를 들어 파일 시스템 MCP 서버 하나에만 연결하는 앱을 만들어 보자.
이 앱이 해야 할 일은 생각보다 단순하다. 서버 실행 정보를 준비하고, 세션을 열고, 초기화한 뒤, 도구 목록을 가져오고, 필요할 때 도구를 호출하면 된다.
이 흐름은 새 동료와 일하는 과정과 비슷하다. 먼저 연락처를 저장하고, 인사를 나누고, 어떤 일을 할 수 있는지 듣고, 실제 부탁을 한다.
단일 서버 통합의 기본 단계
서버 실행 정보 준비
클라이언트 생성
세션 초기화
도구 목록 조회
필요한 도구 호출
결과 가공 후 사용자에게 표시
여기서 하나 고민할 지점이 있다. 요청이 들어올 때마다 연결을 새로 열 것인가, 아니면 연결을 유지할 것인가 하는 문제다.
개발 초반에는 요청마다 새 연결을 여는 방식이 이해하기 쉽다. 하지만 실제 서비스에서는 세션 재사용이 더 효율적일 때가 많다. 너무 자주 연결을 만들고 끊으면, 일은 조금 하고 인사만 백 번 하는 꼴이 된다.
파이썬 앱에서 MCP 클라이언트를 품는 감각
파이썬에서는 stdio 기반 서버를 띄우고 세션을 붙이는 방식이 비교적 직관적이다. 아래 예시는 개념을 보여주기 위한 아주 작은 형태다.
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def connect_filesystem():
server_params = StdioServerParameters(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "."]
)
stack = AsyncExitStack()
stdio, write = await stack.enter_async_context(stdio_client(server_params))
session = await stack.enter_async_context(ClientSession(stdio, write))
await session.initialize()
tools = await session.list_tools()
return stack, session, tools이 코드에서 중요한 것은 문법보다 책임의 위치다. 도구를 찾고 실행하는 주도권이 이제 외부 호스트가 아니라 당신의 앱 안으로 들어왔다는 점이 핵심이다.
이제 당신은 단순히 "MCP 서버를 사용할 수 있다"가 아니라 "내 앱이 이 서버를 운영 방식에 맞게 다룬다"는 단계로 올라선 것이다.
타입스크립트와 웹 앱에서는 어떻게 생각하면 좋을까
타입스크립트 환경에서는 보통 백엔드 API와 MCP 클라이언트를 묶어 다루는 형태가 자연스럽다. Express, Fastify, NestJS, Next.js 서버 액션 어느 쪽이든 구조는 비슷하다.
프런트엔드는 /ask 같은 엔드포인트로 질문을 보내고, 백엔드는 내부적으로 MCP 도구를 호출해 결과를 반환한다. 또 /tools 같은 엔드포인트를 만들어 현재 연결된 도구 목록을 UI에 보여줄 수도 있다.
이 방식의 장점은 명확하다. 브라우저는 단순하고 안전해지고, 백엔드는 MCP 연결과 권한 정책을 통제할 수 있다. 사용자는 "채팅창 뒤에 무엇이 붙어 있는지"를 몰라도 되지만, 필요할 때는 도구 목록과 실행 이력을 투명하게 확인할 수 있다.
내 앱의 첫 번째 UX 과제: 도구를 어떻게 보여줄까
MCP를 제품에 통합하면 의외로 제일 먼저 부딪히는 문제가 기술이 아니라 설명이다. 사용자는 "이 앱이 지금 무엇을 할 수 있는지"를 알아야 안심한다.
도구를 그냥 이름만 나열하면 부족하다. read_file과 delete_file은 둘 다 파일 서버 도구지만, 사용자 입장에서 느끼는 무게가 전혀 다르다. 읽기는 비교적 가볍고, 삭제는 심장이 한 박자 빨라지는 행동이다.
좋은 앱은 도구를 함수 이름이 아니라 능력 카드처럼 보여준다. 예를 들어 "파일 읽기", "Git 변경 내역 조회", "이슈 생성", "메시지 전송"처럼 사용자가 이해하는 말로 보여주는 편이 좋다.
도구 화면에는 보통 다음 정보가 있으면 편하다.
이 도구가 무엇을 하는지
어떤 입력이 필요한지
읽기 전용인지, 쓰기 행동인지
자동 실행 가능한지, 승인 필요한지
최근에 언제 실행되었는지
이렇게 보여 주면 사용자는 갑자기 마법 지팡이가 튀어나온 느낌을 받지 않는다. "아, 이 앱은 이런 능력을 가지고 있구나" 하고 납득하게 된다.
이제 진짜 핵심: 여러 서버를 동시에 다루기
실전 앱은 보통 서버 하나로 끝나지 않는다. 파일, Git, 검색, 데이터베이스, 사내 API, 알림, 결제, 티켓 시스템이 함께 등장한다.
이쯤 되면 앱은 지휘자가 된다. 각 서버는 자기 역할만 알면 되고, 전체 흐름은 호스트가 조율한다.
예를 들어 이런 요청을 생각해 보자.
"이번 주 장애 관련 커밋과 로그를 확인해서 요약 보고서를 만들고, 운영 채널에 초안까지 올려 줘."
이 한 문장 안에 사실 여러 서버가 숨어 있다.
Git 서버가 장애 관련 커밋을 찾는다.
로그 서버가 해당 기간 에러를 조회한다.
파일 또는 문서 서버가 운영 매뉴얼을 읽는다.
보고서 생성 서버가 초안을 만든다.
메시징 서버가 채널에 전송한다.
사용자는 하나의 요청만 했지만, 앱은 내부에서 여러 서버를 순서대로 혹은 병렬로 움직이게 만든다. 이게 바로 오케스트레이션이다.
멀티 서버 오케스트레이션에서 가장 먼저 생기는 문제
여러 서버를 묶으면 신나는 만큼 골칫거리도 늘어난다. 대표적인 문제는 세 가지다.
어떤 서버를 언제 호출할지 결정해야 한다.
이름이 비슷한 도구가 겹칠 수 있다.
한 서버의 결과를 다른 서버의 입력으로 이어 붙여야 한다.
예를 들어 search라는 이름의 도구가 검색 서버에도 있고 문서 서버에도 있을 수 있다. 이럴 때 앱이 멍하니 있으면 모델도 헷갈리고 사용자도 헷갈린다. 그래서 멀티 서버 환경에서는 도구 이름 공간, 설명, 라우팅 규칙이 매우 중요하다.
쉽게 말해, 축구팀에 김민수 선수가 셋 있으면 그냥 "민수야!" 하고 불러선 안 된다. 수비 민수, 공격 민수, 골키퍼 민수처럼 구분이 필요하다.
여러 서버를 다루는 세 가지 대표 패턴
앱에서 멀티 서버를 통합하는 방식은 크게 세 가지 감각으로 이해할 수 있다.
직접 연결 패턴
가장 단순한 방식이다. 앱이 각 MCP 서버에 직접 클라이언트를 붙인다. 파일 서버용 클라이언트, Git 서버용 클라이언트, 검색 서버용 클라이언트를 각각 따로 가진다.
이 방식은 이해하기 쉽고 시작하기 좋다. 서버 수가 적고 역할이 분명할 때 특히 잘 맞는다. 다만 서버가 많아질수록 관리 포인트도 늘어난다.
게이트웨이 패턴
앱과 여러 서버 사이에 중간 조율 계층을 둔다. 앱은 이 게이트웨이만 상대하고, 게이트웨이가 실제 MCP 서버들과의 연결을 책임진다.
이 방식은 권한 정책, 로깅, 캐싱, 공통 에러 처리, 도구 이름 정리를 한곳에서 다루기 좋다. 대신 구조가 한 단계 복잡해진다.
감독자 패턴
앱 안에 상위 오케스트레이터를 두고, 이 상위 계층이 어떤 작업을 어느 서버에 보낼지 결정한다. 일종의 팀장 역할이다.
예를 들어 "검색 먼저, 그다음 Git 확인, 마지막으로 보고서 생성" 같은 흐름을 상위 오케스트레이터가 관리한다. 복잡한 워크플로에 특히 잘 맞는다.
처음에는 직접 연결로 시작하고, 규모가 커지면 게이트웨이나 감독자 패턴으로 옮겨 가는 경우가 많다. 작은 카페를 열 땐 사장 혼자 주문도 받고 커피도 내리지만, 손님이 많아지면 바리스타와 매니저가 나뉘는 것과 비슷하다.
멀티 서버 구성을 코드로 생각해 보기
개념적으로는 이런 식으로 서버 설정을 한곳에 모아 둘 수 있다.
{
"servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/app/data"]
},
"git": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git", "/repo"]
},
"internalDocs": {
"command": "python",
"args": ["internal_docs_server.py"]
}
}
}이제 앱은 이 설정을 읽고 서버별 클라이언트를 만들 수 있다. 중요한 것은 설정 파일이 단순한 실행 목록이 아니라는 점이다. 여기에 권한 범위, 환경 변수, 타임아웃, 재시도 정책, 위험도 분류까지 얹으면 앱은 점점 진짜 제품다워진다.
도구 선택은 모델에게 맡길까, 규칙으로 고정할까
자체 앱을 만들면 한 번쯤 고민하게 된다. 어떤 도구를 쓸지 모델이 스스로 고르게 둘 것인가, 아니면 앱이 규칙으로 미리 정할 것인가.
정답은 상황에 따라 다르다.
자유도가 높은 탐색형 작업은 모델에게 어느 정도 선택권을 줘도 좋다. 예를 들어 "이 문제를 조사해 줘" 같은 요청은 검색, 문서 조회, Git 확인 중 무엇이 먼저 필요할지 유연하게 판단할 수 있다.
반대로 정형화된 업무는 규칙 기반이 더 안전하다. 예를 들어 "운영 채널에 메시지 보내기" 같은 행동은 반드시 초안 생성 후 승인 버튼을 눌렀을 때만 실행되게 만드는 편이 낫다.
좋은 앱은 둘을 적절히 섞는다. 조사와 요약은 유연하게, 삭제와 전송은 엄격하게. 자유로운 창작과 엄격한 실행을 한 냄비에 다 넣고 끓이면 맛이 이상해진다.
클라이언트 통합에서 세션 관리는 생각보다 중요하다
MCP는 상태가 있는 세션 기반 통신이다. 그래서 앱을 만들 때 세션 수명 주기를 무시하면 나중에 꼭 고생한다.
질문 하나 들어올 때마다 세션을 매번 새로 만들면 단순하긴 하지만, 속도와 비용이 아쉬울 수 있다. 반대로 세션을 너무 오래 붙잡고 있으면 자원 누수나 stale 상태 같은 문제가 생길 수 있다.
실전에서는 보통 이런 균형을 잡는다.
자주 쓰는 서버는 연결 풀이나 장기 세션으로 유지한다.
무거운 서버나 드문 서버는 필요할 때만 연결한다.
세션이 깨졌을 때 자동 재연결 로직을 둔다.
초기화 결과나 도구 목록은 적절히 캐시한다.
이 부분은 마치 식당 예약 시스템과 비슷하다. 손님이 올 때마다 테이블을 새로 만들 수는 없고, 그렇다고 평생 비워 둘 수도 없다. 적당히 돌려 써야 한다.
앱에서 권한과 승인 흐름을 어떻게 설계할까
MCP를 제품에 넣는 순간, 기술보다 더 중요한 것이 하나 생긴다. 바로 "무엇을 자동으로 해도 되는가"다.
읽기 도구는 대부분 부담이 적다. 파일 읽기, 로그 조회, 문서 검색 정도는 자동 실행해도 괜찮은 경우가 많다. 하지만 쓰기 행동은 다르다. 파일 수정, 이슈 생성, 메시지 전송, 배포 실행, 결제 요청 같은 도구는 위험도가 훨씬 높다.
그래서 앱은 도구를 적어도 세 층으로 구분해 두는 편이 좋다.
자동 허용 가능한 읽기 도구
실행 전 확인이 필요한 변경 도구
특별 권한이 필요한 민감 도구
UI도 여기에 맞춰 달라져야 한다. 읽기 도구는 자연스럽게 실행하고, 변경 도구는 확인 대화상자를 띄우고, 민감 도구는 추가 인증이나 관리자 승인까지 붙일 수 있다.
이 구조가 중요한 이유는 단순하다. MCP가 잘못해서 무서운 게 아니라, 잘 되는데 너무 잘 돼서 무섭기 때문이다.
앱 프런트엔드에 어떤 화면이 있으면 좋을까
자체 MCP 앱은 단순 채팅 화면보다 조금 더 풍부한 구성이 잘 어울린다. 반드시 화려할 필요는 없지만, 몇 가지 핵심 화면은 큰 도움이 된다.
대화 또는 요청 입력 영역
사용 가능한 도구 목록 패널
최근 실행 기록 패널
승인 대기 작업 패널
결과 미리보기 영역
예를 들어 사용자가 "리포지터리에서 TODO를 찾아 요약해 줘"라고 하면, 화면 한쪽에는 호출된 도구가 보이고, 다른 쪽에는 읽은 파일 수와 요약 결과가 보일 수 있다. 만약 메시지 전송까지 이어진다면 전송 전 미리보기와 승인 버튼을 띄울 수도 있다.
이렇게 하면 사용자는 AI가 검은 상자처럼 움직인다고 느끼지 않는다. "지금 검색했고, 지금 파일을 읽었고, 이제 초안을 만들었구나"를 이해하게 된다. 신뢰는 설명에서 자란다.
MCP Apps를 호스트 입장에서 이해하기
직전 장에서 상호작용 UI를 제공하는 MCP Apps 이야기를 살짝 봤다. 이번에는 그걸 클라이언트와 앱 통합 관점에서 보자.
텍스트 응답만으로 부족한 작업이 있다. 대시보드, 지도, 차트, 설정 폼, PDF 뷰어, 미디어 미리보기 같은 것들이다. 이때 호스트 앱이 MCP Apps를 지원하면, 서버가 제공한 인터랙티브 UI를 대화 안이나 앱 화면 안에 렌더링할 수 있다.
사용자 입장에서 이것은 꽤 큰 변화다. 별도 웹사이트로 튕겨 나가지 않고도 같은 맥락 안에서 조작을 이어 갈 수 있기 때문이다. 예를 들어 "지역별 매출 보여 줘"라는 요청 뒤에 텍스트 숫자만 잔뜩 나오는 대신, 앱 안에 바로 클릭 가능한 차트가 뜰 수 있다.
호스트를 만드는 입장에서는 고민이 하나 늘어난다. 단순 텍스트 렌더링이 아니라 샌드박스, 메시지 전달, 보안 정책, 도구 호출 프록시 같은 책임이 따라온다. 하지만 잘 붙이면 효과는 확실하다. 채팅이 곧 작업 화면이 된다.
자체 앱에서 MCP Apps를 붙일 때의 감각
핵심은 "대화와 UI를 분리하지 말자"다. 사용자가 차트를 보다가 다시 질문하고, 그 질문 결과가 또 UI로 이어져야 흐름이 매끄럽다.
예를 들어 예산 시뮬레이터 앱을 만든다고 해 보자. 사용자가 "마케팅 예산을 지역별로 나눠 보여 줘"라고 말하면 앱은 MCP 도구를 호출하고, 결과를 인터랙티브 예산 조정 UI로 띄운다. 사용자는 슬라이더를 조절하고, 앱은 다시 MCP 도구를 호출해 새로운 결과를 계산한다.
이때 호스트 앱은 단순 채팅앱이 아니라 대화형 워크벤치가 된다. 말로 시작해서 손으로 조작하고, 다시 말로 정리하는 흐름이 이어진다.
오류 처리: 앱은 친절한 통역사여야 한다
MCP 서버와 직접 통신하다 보면 오류는 반드시 만난다. 서버 실행 실패, 경로 오류, 권한 부족, 타임아웃, 잘못된 인자, 세션 종료, 외부 API 장애 같은 일은 생각보다 자주 생긴다.
이때 좋지 않은 앱은 내부 에러를 그대로 쏟아낸다. 사용자에게 Tool call failed: ENOENT 같은 메시지를 그대로 보여 주면 분위기가 갑자기 콘솔창이 된다.
좋은 앱은 오류를 두 층으로 다룬다.
개발자에게는 자세한 로그를 남긴다. 어떤 서버에서, 어떤 도구를, 어떤 인자로 호출했는지, 실패 시점은 언제였는지 기록한다.
사용자에게는 이해 가능한 말로 바꿔 보여 준다. 예를 들어 "파일 서버에 연결하지 못했습니다. 경로 설정을 확인해 주세요."처럼 문제의 위치와 다음 행동을 알려 주는 편이 낫다.
통역사가 화를 내면 회의가 망한다. 앱은 언제나 친절한 통역사여야 한다.
흔한 실수 1: 모든 서버를 한꺼번에 붙이려는 욕심
처음 통합할 때 자주 생기는 실수는 "기왕 하는 김에 다 붙이자"다. 파일, Git, DB, 메일, 슬랙, 검색, 캘린더를 한 번에 엮기 시작하면 금세 미로가 된다.
처음에는 한 서버, 한 시나리오가 좋다. 예를 들어 "파일 읽기와 요약"만 완성해도 충분하다. 그다음 "Git 변경 확인"을 붙이고, 그다음 "보고서 전송"을 더하는 식으로 확장하는 편이 훨씬 안전하다.
MCP는 확장성이 장점이지만, 확장성이 있다는 말이 처음부터 다 확장하라는 뜻은 아니다.
흔한 실수 2: 도구 이름 충돌을 방치하는 것
멀티 서버 환경에서 search, list, read 같은 이름은 정말 자주 겹친다. 이때 그냥 이름만 믿고 두면 모델도 헷갈리고 사람도 헷갈린다.
좋은 방법은 서버 이름이나 도메인을 앞에 붙여 구분하는 것이다. 예를 들어 내부 표현이나 UI에서 git.search_commits, docs.search_pages, filesystem.read_file처럼 다루면 훨씬 명확하다.
실행용 내부 이름과 사용자 노출용 표시 이름을 분리하는 것도 좋다. 사용자는 친절한 설명을 보고, 시스템은 충돌 없는 식별자를 쓴다.
흔한 실수 3: 앱이 아무 판단도 하지 않는 것
"어차피 모델이 다 알아서 하겠지"라고 생각하고 앱이 너무 손을 놓아버리면 문제가 생긴다. 모델은 똑똑하지만 제품 책임자는 아니다. 시간 제한, 승인 정책, 민감 도구 차단, 재시도 횟수, 최대 호출 수 같은 규칙은 앱이 책임져야 한다.
좋은 앱은 모델을 믿되, 제품 운영은 규칙으로 잡는다. 운전은 자율주행이 도와줘도 브레이크는 차에 있어야 한다.
실전 예시: 사내 리포트 도우미 앱 만들기
이제 하나의 예시로 감을 잡아 보자. 당신이 만드는 앱은 "사내 리포트 도우미"다. 사용자는 자연어로 질문하고, 앱은 여러 MCP 서버를 엮어 보고서를 만든다.
이 앱에는 세 개의 서버가 붙어 있다고 해 보자.
데이터베이스 서버: 수치 조회
문서 서버: 정책과 용어집 조회
메시징 서버: 보고서 전송
사용자가 "지난달 환불률과 주요 원인을 요약해서 팀 채널에 초안만 올려 줘"라고 말한다.
앱은 먼저 DB 서버에서 환불률을 조회한다. 그다음 문서 서버에서 환불 관련 정책과 분류 기준을 읽는다. 그런 뒤 모델이 초안을 작성하게 한다. 마지막으로 메시징 서버 호출 직전에는 전송 승인 UI를 띄운다.
여기서 중요한 점은 서버가 똑똑해서가 아니라 앱이 질서를 만들었다는 것이다. 조회는 자동, 전송은 승인 후. 이 한 줄 정책이 사용자 신뢰를 만든다.
구현 순서 추천: 이렇게 만들면 덜 헤맨다
자체 앱에 MCP를 붙일 때는 다음 순서로 가면 덜 헷갈린다.
먼저 단일 서버 연결을 완성한다.
도구 목록을 UI에 보여준다.
도구 하나를 수동 호출해 본다.
질문에서 도구 선택까지 자동화한다.
실행 기록과 오류 메시지를 화면에 붙인다.
승인 흐름을 추가한다.
두 번째 서버를 붙여 멀티 서버 조합을 만든다.
마지막으로 캐싱, 재시도, 세션 재사용을 다듬는다.
이 순서는 화려해 보이지 않지만 강하다. 작은 성공을 쌓으면서 앱을 키울 수 있기 때문이다.
어떤 앱이 되면 성공한 걸까
좋은 MCP 통합 앱은 도구를 많이 붙인 앱이 아니다. 사용자가 "이 앱이 내 일을 잘 이해하고 안전하게 도와준다"고 느끼는 앱이다.
즉, 성공의 기준은 서버 개수가 아니다. 다음 질문에 답할 수 있으면 꽤 잘 만들고 있는 것이다.
사용자가 앱의 능력을 이해할 수 있는가
위험한 행동은 적절히 통제되는가
여러 서버가 하나의 흐름으로 자연스럽게 이어지는가
오류가 나도 사용자가 길을 잃지 않는가
결과가 채팅에만 머물지 않고 실제 작업으로 이어지는가
이 다섯 가지가 맞아떨어지면, 당신의 앱은 단순한 MCP 실험장이 아니라 실제로 일하는 제품이 된다.
마무리
이번 장에서는 MCP 서버를 사용하는 입장에서 한 단계 더 나아가, 이제는 내 애플리케이션이 직접 MCP 클라이언트를 품고 세계를 조율하는 방법을 살펴봤다. 단일 서버 연결부터 시작해, 여러 서버를 오케스트레이션하고, 권한과 승인 흐름을 설계하고, UI와 실행 기록, 상호작용 화면까지 앱에 녹이는 감각을 익혔다.
여기서 중요한 변화는 시선의 전환이다. 더 이상 당신은 "어떤 앱에 MCP를 붙일까"를 고민하는 사람이 아니다. 이제는 "내 앱이 어떤 MCP 경험을 만들까"를 설계하는 사람이다.
서버를 만드는 힘이 공구를 만드는 능력이라면, 클라이언트와 앱에 통합하는 힘은 그 공구들로 작업장을 설계하는 능력이다. 그리고 실전에서는 바로 그 능력이 사용자 경험을 결정한다.
다음 장에서는 이 멋진 작업장을 안전하게 지키는 법을 다룬다. MCP 세계에서 보안은 장식이 아니라 바닥 공사다. 바닥이 튼튼해야, 그 위에 무엇을 올려도 안심할 수 있다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.