Gradio 앱을 MCP(Model Context Protocol) 서버로 실행하기
LLM이 실제 작업을 수행하려면 외부 도구가 필요합니다. 모델은 텍스트를 생성할 수 있지만, 이미지를 만들거나, 파일을 처리하거나, 특정 계산을 정확히 수행하거나, 사내 API를 호출하려면 별도의 실행 환경이 있어야 합니다.
MCP(Model Context Protocol)는 이런 도구와 LLM 클라이언트를 연결하기 위한 표준 프로토콜입니다. Claude Desktop, Cursor, Cline 같은 MCP 클라이언트는 MCP 서버에 등록된 도구를 호출할 수 있고, MCP 서버는 실제 함수를 실행한 뒤 결과를 다시 클라이언트에 전달합니다.
Gradio는 이 과정을 매우 단순하게 만들어 줍니다. 이미 Gradio 앱으로 만들어둔 Python 함수가 있다면, .launch()에 mcp_server=True만 추가해 MCP 서버로 노출할 수 있습니다.
핵심은 아래 한 줄입니다.
demo.launch(mcp_server=True)이 글에서는 Gradio 앱을 MCP 서버로 실행하는 기본 방법부터, Hugging Face Spaces에 배포해 원격 MCP 서버로 사용하는 방법, 그리고 연결이 잘 되지 않을 때 확인해야 할 문제 해결 포인트까지 정리합니다.
참고 문서: https://www.gradio.app/guides/building-mcp-server-with-gradio
시작하기 전에 필요한 것
먼저 MCP 기능을 포함해 Gradio를 설치합니다.
pip install "gradio[mcp]"이 명령은 Gradio와 함께 MCP 관련 의존성을 설치합니다.
MCP 서버만으로는 충분하지 않습니다. 서버를 호출할 MCP 클라이언트도 필요합니다. 예를 들어 다음과 같은 앱이 MCP 클라이언트 역할을 할 수 있습니다.
- Claude Desktop
- Cursor
- Cline
- Tiny Agents
- MCP를 지원하는 기타 LLM 클라이언트
Claude Desktop을 사용하는 경우 설정 파일은 보통 아래 위치에 있습니다.
macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
Windows:
%APPDATA%\Claude\claude_desktop_config.jsonMCP 서버는 무엇인가요?
MCP 서버는 LLM이 외부 도구를 표준 방식으로 사용할 수 있게 해주는 서버입니다.

예를 들어 LLM에게 “이 이미지를 편집해줘”, “이 숫자의 소인수분해를 계산해줘”, “이 텍스트를 음성으로 바꿔줘”라고 요청한다고 해보겠습니다. 모델이 모든 작업을 직접 수행하는 것은 아닙니다. 대신 MCP 서버에 등록된 도구를 호출하고, 서버는 실제 Python 함수나 API를 실행한 뒤 결과를 반환합니다.
Gradio를 사용하면 이 도구 서버를 별도의 백엔드로 직접 구현하지 않아도 됩니다. Gradio 앱의 함수, 타입 힌트, 독스트링을 바탕으로 MCP 도구 스키마를 자동으로 만들어 줍니다.
정리하면 다음과 같습니다.
- Gradio 함수는 MCP 도구가 됩니다.
- 함수 이름은 도구 이름으로 사용됩니다.
- 독스트링은 도구 설명으로 사용됩니다.
- 타입 힌트는 입력 스키마를 만드는 데 사용됩니다.
- Gradio 앱은 웹 UI와 MCP 서버를 함께 실행할 수 있습니다.
예시로 살펴보기 - 글자 세기 도구
LLM은 간단한 계산도 가끔 틀립니다. 예를 들어 "strawberry"에 "r"이 몇 개 들어 있는지 묻는 문제는 자주 언급되는 예시입니다. 이런 작업을 LLM이 직접 추론하게 하지 말고, 글자 수를 세는 작은 도구를 제공하면 더 안정적으로 처리할 수 있습니다.
먼저 단어 또는 문장에서 특정 글자가 몇 번 등장하는지 세는 Gradio 앱을 만들어 보겠습니다.
import gradio as gr
def letter_counter(word: str, letter: str) -> int:
"""단어 또는 문장에서 특정 글자가 몇 번 등장하는지 계산합니다.
Args:
word: 검색할 단어 또는 문장.
letter: 개수를 셀 글자.
Returns:
특정 글자가 등장한 횟수.
"""
return word.lower().count(letter.lower())
demo = gr.Interface(
fn=letter_counter,
inputs=[
gr.Textbox(value="strawberry", label="Text"),
gr.Textbox(value="r", label="Letter"),
],
outputs=gr.Number(label="Count"),
title="Letter Counter",
description="텍스트와 글자를 입력하면 해당 글자가 몇 번 등장하는지 계산합니다.",
api_name="predict",
)
if __name__ == "__main__":
demo.launch(mcp_server=True)여기서 중요한 부분은 두 가지입니다.
첫째, 함수에 타입 힌트를 명시했습니다.
def letter_counter(word: str, letter: str) -> int:둘째, 함수에 독스트링을 작성했습니다.
"""단어 또는 문장에서 특정 글자가 몇 번 등장하는지 계산합니다.
Args:
word: 검색할 단어 또는 문장.
letter: 개수를 셀 글자.
Returns:
특정 글자가 등장한 횟수.
"""Gradio는 이 정보를 바탕으로 MCP 도구의 이름, 설명, 입력 스키마를 구성합니다. LLM 입장에서는 단순히 “함수를 호출한다”가 아니라, 어떤 도구가 있고 어떤 입력을 넣어야 하는지 이해할 수 있어야 합니다. 그래서 타입 힌트와 독스트링은 단순한 코드 주석이 아니라 MCP 도구 품질을 좌우하는 중요한 정보입니다.
마지막으로 .launch()에 mcp_server=True를 추가합니다.
demo.launch(mcp_server=True)이렇게 실행하면 Gradio는 일반 웹 UI와 함께 MCP 서버도 시작합니다. 콘솔에는 MCP 클라이언트에서 사용할 수 있는 서버 URL이 출력됩니다.

MCP 클라이언트에 연결하기
Gradio 앱을 실행하면 MCP 서버 URL을 확인할 수 있습니다. 일반적으로는 Gradio 앱 하단의 View API를 클릭한 뒤, MCP 탭에서 클라이언트 설정에 복사할 수 있는 값을 확인하는 것이 가장 안전합니다.

Gradio 버전과 클라이언트에 따라 URL 형식은 조금 다르게 보일 수 있습니다. 예시는 다음과 같습니다.

http://your-server:port/gradio_api/mcp/sse또는 환경에 따라 아래와 같은 형식으로 안내될 수 있습니다.
http://your-server:port/gradio_api/mcp/MCP 클라이언트가 원격 SSE 기반 MCP 서버를 직접 지원한다면, 다음과 같은 형식으로 설정할 수 있습니다.
{
"mcpServers": {
"letter-counter": {
"url": "http://your-server:port/gradio_api/mcp/sse"
}
}
}

일부 클라이언트가 원격 SSE 서버를 직접 지원하지 않는 경우에는 mcp-remote 같은 브리지 도구를 사용할 수 있습니다. 이 경우 Node.js가 필요합니다.
{
"mcpServers": {
"letter-counter": {
"command": "npx",
"args": [
"mcp-remote",
"http://your-server:port/gradio_api/mcp/sse"
]
}
}
}설정을 변경한 뒤에는 MCP 클라이언트를 다시 시작합니다. 많은 클라이언트는 설정 파일 변경을 즉시 반영하지 않기 때문에, 재시작하지 않으면 서버가 보이지 않을 수 있습니다.


Gradio 앱이 MCP 도구로 바뀌는 방식
Gradio와 MCP를 함께 사용할 때 가장 편한 점은 도구 변환을 Gradio가 자동으로 처리한다는 점입니다.
Gradio 앱에 API 엔드포인트가 있으면, Gradio는 이를 MCP 도구로 노출합니다. 도구 이름, 설명, 입력 스키마는 함수 이름, 독스트링, 타입 힌트, 컴포넌트 설정을 바탕으로 생성됩니다.
도구와 스키마가 어떻게 생성됐는지 확인하려면 아래 경로를 열어볼 수 있습니다.
http://your-server:port/gradio_api/mcp/schema또는 Gradio 앱 하단의 View API → MCP에서 확인할 수 있습니다.
기본적으로 다음 정보가 중요합니다.
| 항목 | MCP 도구에서의 역할 |
|---|---|
| 함수 이름 | 도구 이름 |
| 독스트링 | 도구 설명 |
| 타입 힌트 | 입력·출력 스키마 |
| Gradio 입력 컴포넌트의 기본값 | LLM이 값을 지정하지 않았을 때의 기본값 |
api_name | API 엔드포인트 이름 |
예를 들어 letter_counter 함수는 LLM에게 “글자 수를 세는 도구”로 보입니다. LLM은 사용자의 요청을 보고 word와 letter에 적절한 값을 넣어 이 도구를 호출할 수 있습니다.
MCP 서버 실행 방법 두 가지
Gradio에서 MCP 서버를 활성화하는 방법은 크게 두 가지입니다.
첫 번째는 코드에서 직접 설정하는 방식입니다.
demo.launch(mcp_server=True)두 번째는 환경변수를 사용하는 방식입니다.
export GRADIO_MCP_SERVER=True일반적으로는 코드에 명시하는 편이 더 직관적입니다. 반면 배포 환경에서 MCP 활성화 여부를 분리해 관리하고 싶다면 환경변수를 사용하는 방식도 고려할 수 있습니다.
파일과 이미지 입력을 다룰 때의 주의점
텍스트 입력만 다룰 때는 비교적 단순합니다. 하지만 이미지나 파일을 입력으로 받는 Gradio 앱을 MCP 서버로 노출하면 클라이언트마다 처리 방식이 달라질 수 있습니다.
원격 MCP 서버를 사용할 때는 파일이나 이미지를 HTTP/HTTPS URL로 전달하는 방식이 가장 호환성 측면에서 안전합니다.
예를 들어 이미지 분석 도구를 만든다면, LLM이 로컬 파일 경로를 넘기는 방식보다 접근 가능한 이미지 URL을 넘기는 방식이 문제를 줄일 수 있습니다.
def image_orientation(image_url: str) -> str:
"""이미지 URL을 받아 이미지 방향을 판별합니다.
Args:
image_url: 분석할 이미지의 URL.
Returns:
이미지가 세로 방향인지 가로 방향인지 나타내는 문자열.
"""
...물론 실제 Gradio 앱에서는 gr.Image 같은 컴포넌트를 사용할 수 있습니다. 다만 MCP 클라이언트, 배포 방식, 파일 전달 방식이 얽히면 문제가 복잡해질 수 있습니다. 운영 환경에서는 파일 입력을 어떤 형식으로 받을지 먼저 정하는 것이 좋습니다.
Hugging Face Spaces에 배포해 원격 MCP 서버로 사용하기
Gradio 앱을 Hugging Face Spaces에 배포하면, 원격에서 접근할 수 있는 MCP 서버로 사용할 수 있습니다.
개발 흐름은 단순합니다.
- Gradio 앱을 만듭니다.
.launch(mcp_server=True)를 추가합니다.- Hugging Face Spaces에 배포합니다.
- MCP 클라이언트 설정에 Space의 MCP URL을 등록합니다.
예시는 다음과 같습니다.
{
"mcpServers": {
"gradio-space": {
"url": "https://your-space.hf.space/gradio_api/mcp/sse"
}
}
}비공개 Space를 MCP 서버로 사용하려면 인증 정보가 필요합니다. 이 경우 요청 헤더에 Hugging Face 토큰을 넣어야 합니다.
{
"mcpServers": {
"private-gradio-space": {
"url": "https://your-private-space.hf.space/gradio_api/mcp/sse",
"headers": {
"Authorization": "Bearer <YOUR-HUGGING-FACE-TOKEN>"
}
}
}
}토큰은 코드에 직접 하드코딩하지 않는 것이 좋습니다. 로컬 설정 파일, 환경변수, 시크릿 관리 도구를 사용해 관리하는 편이 안전합니다.
기존 Space를 MCP 서버로 전환하기
이미 만들어진 Hugging Face Space를 MCP 서버로 바꾸고 싶다면, 보통 세 가지를 확인하면 됩니다.
첫째, 해당 Space가 본인 소유가 아니라면 먼저 Duplicate합니다. 그래야 코드를 수정하고 설정을 바꿀 수 있습니다. 원본 Space가 GPU를 사용하고 있다면, 복제한 Space의 하드웨어도 동일하게 맞춰야 정상 동작할 가능성이 높습니다.
둘째, LLM이 호출할 함수에 타입 힌트와 독스트링을 추가합니다. MCP 도구는 사람이 직접 보는 UI가 아니라 LLM이 이해하고 호출하는 도구입니다. 따라서 함수 설명이 부정확하면 모델이 도구를 잘못 사용할 수 있습니다.
셋째, 앱 실행 코드에 mcp_server=True를 추가합니다.
demo.launch(mcp_server=True)이 세 단계만으로도 기존 Gradio Space를 MCP 서버로 전환할 수 있습니다.
도구 설명을 더 세밀하게 제어하기
기본적으로 Gradio는 함수 이름과 독스트링을 바탕으로 MCP 도구 설명을 만듭니다. 대부분의 간단한 도구에는 이 방식으로 충분합니다.
하지만 운영 환경에서는 LLM에게 노출되는 설명을 더 명확하게 다듬고 싶을 수 있습니다. 예를 들어 특정 함수는 UI에서는 필요하지만 LLM 도구로는 노출하고 싶지 않을 수 있고, 반대로 LLM에게는 더 자세한 사용 지침을 제공하고 싶을 수도 있습니다.
이럴 때는 api_description이나 show_api 같은 설정을 활용할 수 있습니다.
예를 들어 특정 API를 LLM에게 노출하지 않으려면 해당 엔드포인트의 API 노출 여부를 조정합니다.
import gradio as gr
def internal_helper(text: str) -> str:
"""내부 처리용 함수입니다."""
return text.strip()
demo = gr.Interface(
fn=internal_helper,
inputs=gr.Textbox(),
outputs=gr.Textbox(),
show_api=False,
)
demo.launch(mcp_server=True)MCP 서버에 노출되는 도구가 많아질수록 “무엇을 노출하지 않을지”가 중요해집니다. 모든 함수를 도구로 열어두면 LLM이 잘못된 함수를 호출하거나, 사용자에게 보여줄 필요가 없는 내부 기능이 노출될 수 있습니다.
여러 Gradio Space를 하나의 MCP 서버로 묶기
Gradio 앱 하나를 MCP 서버로 노출하는 방식이 가장 단순합니다. 하지만 여러 Hugging Face Space를 하나의 MCP 서버에서 필요할 때 호출하고 싶을 수도 있습니다.
이 경우에는 FastMCP와 gradio_client를 조합해 사용자 정의 MCP 서버를 만들 수 있습니다. 아래 예시는 여러 Space를 호출할 수 있는 MCP 서버의 기본 구조입니다.
from mcp.server.fastmcp import FastMCP
from gradio_client import Client
mcp = FastMCP("gradio-spaces")
clients = {}
def get_client(space_id: str) -> Client:
"""지정한 Space에 대한 Gradio Client를 가져오거나 새로 생성합니다."""
if space_id not in clients:
clients[space_id] = Client(space_id)
return clients[space_id]
@mcp.tool()
def call_space(prompt: str, space_id: str, api_name: str = "/predict") -> str:
"""Hugging Face Space에 배포된 Gradio 앱을 호출합니다.
Args:
prompt: Space에 전달할 입력 프롬프트.
space_id: 호출할 Hugging Face Space ID.
api_name: 호출할 Gradio API 엔드포인트 이름.
Returns:
Space 실행 결과.
"""
client = get_client(space_id)
result = client.predict(prompt, api_name=api_name)
return str(result)
if __name__ == "__main__":
mcp.run(transport="stdio")이 코드를 파일로 저장합니다.
gradio_mcp_server.py필요한 패키지를 설치합니다.
pip install mcp gradio-clientClaude Desktop 같은 MCP 클라이언트에서는 다음과 같이 로컬 MCP 서버를 등록할 수 있습니다.
{
"mcpServers": {
"gradio-spaces": {
"command": "python",
"args": [
"/absolute/path/to/gradio_mcp_server.py"
]
}
}
}이 방식은 다음과 같은 경우에 유용합니다.
- 여러 Space를 하나의 MCP 서버에서 관리하고 싶을 때
- Gradio 앱의 특정 엔드포인트만 도구로 노출하고 싶을 때
- 도구 이름, 설명, 입력 스키마를 더 세밀하게 관리하고 싶을 때
- 원격 Space 호출 전후에 인증, 로깅, 검증 로직을 추가하고 싶을 때
다만 이 방식은 단순한 mcp_server=True보다 직접 관리해야 할 부분이 많습니다. 빠르게 실험하려면 Gradio 기본 MCP 기능을 쓰고, 여러 도구를 묶거나 운영 제어가 필요해질 때 사용자 정의 MCP 서버를 고려하는 것이 좋습니다.
문제 해결 팁
MCP는 빠르게 발전하고 있는 생태계입니다. 클라이언트와 서버 구현이 계속 바뀌고 있기 때문에, 설정이 맞아 보여도 연결 문제가 발생할 수 있습니다. 문제가 생기면 아래 항목부터 확인하는 것이 좋습니다.
1. 타입 힌트와 독스트링을 확인합니다
Gradio는 함수의 타입 힌트와 독스트링을 읽어 MCP 도구 설명과 입력 스키마를 만듭니다. 이 정보가 부정확하면 LLM이 도구를 잘못 이해할 수 있습니다.
좋은 예시는 다음과 같습니다.
from PIL import Image
def image_orientation(image: Image.Image) -> str:
"""이미지가 세로 방향인지 가로 방향인지 판별합니다.
Args:
image: 확인할 이미지.
Returns:
이미지 방향. 세로 방향이면 "Portrait", 가로 방향이면 "Landscape".
"""
return "Portrait" if image.height > image.width else "Landscape"확인할 부분은 다음입니다.
- 함수 인자에 타입 힌트가 있는가
- 반환 타입이 명시되어 있는가
- 독스트링이 함수의 실제 동작을 정확히 설명하는가
Args:아래 인자 이름이 실제 함수 인자와 일치하는가- LLM이 읽어도 도구 사용법을 이해할 수 있는가
생성된 스키마는 아래 경로에서 확인할 수 있습니다.
http://your-server:port/gradio_api/mcp/schema2. 입력 타입이 복잡하면 우선 문자열로 받습니다
일부 MCP 클라이언트는 숫자, 파일, 복잡한 객체 타입을 기대한 방식대로 처리하지 못할 수 있습니다. 확실하지 않다면 입력을 우선 str로 받고, 함수 내부에서 필요한 타입으로 변환하는 방식이 더 안정적일 수 있습니다.
예를 들어 소인수분해 도구를 만든다면 다음처럼 작성할 수 있습니다.
def prime_factors(n: str) -> list[int]:
"""양의 정수를 소인수분해합니다.
Args:
n: 소인수분해할 정수. 1보다 커야 합니다.
Returns:
소인수 목록.
"""
n_int = int(n)
if n_int <= 1:
raise ValueError("입력은 1보다 큰 정수여야 합니다.")
factors = []
while n_int % 2 == 0:
factors.append(2)
n_int //= 2
divisor = 3
while divisor * divisor <= n_int:
while n_int % divisor == 0:
factors.append(divisor)
n_int //= divisor
divisor += 2
if n_int > 1:
factors.append(n_int)
return factors이 방식은 LLM 클라이언트가 입력값을 문자열로 넘겨도 함수 내부에서 명확하게 처리할 수 있다는 장점이 있습니다.
3. 클라이언트가 SSE를 지원하는지 확인합니다
Gradio MCP 서버가 원격 SSE 엔드포인트로 노출되는 경우, MCP 클라이언트가 해당 방식을 직접 지원해야 합니다. 클라이언트가 SSE 서버를 직접 붙이지 못한다면 mcp-remote 같은 브리지 도구를 사용할 수 있습니다.
{
"mcpServers": {
"gradio": {
"command": "npx",
"args": [
"mcp-remote",
"http://your-server:port/gradio_api/mcp/sse"
]
}
}
}이 설정을 사용하려면 Node.js가 설치되어 있어야 합니다.
4. 클라이언트와 서버를 모두 재시작합니다
MCP 설정을 바꿨는데도 도구가 보이지 않는다면, 먼저 클라이언트와 서버를 모두 재시작합니다.
확인 순서는 다음과 같습니다.
- Gradio 서버가 정상 실행 중인지 확인합니다.
- 콘솔에 MCP 서버 URL이 출력됐는지 확인합니다.
- MCP 클라이언트 설정 파일이 올바른 위치에 저장됐는지 확인합니다.
- JSON 문법 오류가 없는지 확인합니다.
- MCP 클라이언트를 재시작합니다.
- 그래도 안 되면 서버도 재시작합니다.
설정 파일의 쉼표 하나가 빠져도 MCP 서버가 로드되지 않을 수 있습니다. 연결 문제를 디버깅할 때는 JSON 문법부터 확인하는 것이 좋습니다.
5. 인증이 필요한 경우 헤더를 확인합니다
비공개 Space나 인증이 필요한 API를 MCP 도구로 노출하는 경우, 클라이언트 설정에 인증 헤더가 들어가야 합니다.
{
"mcpServers": {
"private-gradio-space": {
"url": "https://your-private-space.hf.space/gradio_api/mcp/sse",
"headers": {
"Authorization": "Bearer <YOUR-HUGGING-FACE-TOKEN>"
}
}
}
}토큰이 만료됐거나 권한이 부족하면 도구 호출이 실패할 수 있습니다. 운영 환경에서는 토큰 권한 범위를 최소화하고, 필요한 경우 주기적으로 교체하는 것이 좋습니다.
운영 관점에서 확인해야 할 것
mcp_server=True는 프로토타입을 매우 빠르게 만들 수 있게 해줍니다. 하지만 운영 환경에서는 단순히 연결되는지만 보면 부족합니다.
운영 전에 최소한 다음을 확인하는 것이 좋습니다.
| 확인 항목 | 이유 |
| 도구 설명 | LLM이 도구를 정확히 선택할 수 있어야 합니다. |
| 입력 스키마 | 잘못된 입력이 들어왔을 때 실패 방식이 명확해야 합니다. |
| 인증 | 비공개 데이터나 유료 API가 노출되지 않아야 합니다. |
| 파일 처리 | 로컬 파일, URL, 임시 파일 처리 방식이 클라이언트와 맞아야 합니다. |
| 지연 시간 | LLM 도구 호출은 사용자 대기 시간에 직접 영향을 줍니다. |
| 로깅 | 어떤 도구가 어떤 입력으로 호출됐는지 추적할 수 있어야 합니다. |
| 오류 메시지 | LLM이 실패 원인을 이해하고 재시도할 수 있어야 합니다. |
| 노출 범위 | UI용 함수와 LLM 도구용 함수를 구분해야 합니다. |
MCP 서버는 “LLM이 실행할 수 있는 백엔드”에 가깝습니다. 따라서 단순 데모와 운영 도구는 기준이 달라야 합니다.
마무리
Gradio 앱을 MCP 서버로 실행하는 방법은 매우 간단합니다.
demo.launch(mcp_server=True)이 한 줄만으로 기존 Gradio 앱을 LLM이 호출할 수 있는 도구 서버로 바꿀 수 있습니다. 이미 만든 이미지 생성기, 텍스트 처리기, 계산기, 데이터 변환기, 사내 유틸리티 함수도 MCP 도구로 노출할 수 있습니다.
Gradio MCP의 장점은 단순함입니다. 별도의 API 서버를 처음부터 만들지 않아도 되고, 함수의 타입 힌트와 독스트링만 잘 정리해두면 Gradio가 도구 스키마를 자동으로 만들어 줍니다. Hugging Face Spaces에 배포하면 원격 MCP 서버로도 사용할 수 있습니다.
다만 운영 환경에서는 한 가지를 더 봐야 합니다. MCP 서버는 LLM에게 실행 권한을 주는 인터페이스입니다. 어떤 함수를 노출할지, 어떤 입력을 받을지, 어떤 권한으로 실행할지, 실패했을 때 어떻게 복구할지를 함께 설계해야 합니다.
빠른 실험에는 mcp_server=True로 충분합니다. 하지만 실제 서비스나 사내 도구로 확장하려면 도구 설명, 인증, 파일 처리, 로깅, 오류 처리까지 함께 점검하는 것이 좋습니다.