[Gradio] MCP 도구 호출 레이턴시 개선 — HTTP 루프백 제거
PR 링크: gradio-app/gradio#12961 상태: Merged | 변경: +69 / -16
들어가며
Gradio의 MCP(Model Context Protocol) 서버에서 도구(tool) 호출 시, 기존에는 모든 요청이 gradio_client를 통해 HTTP 루프백(loopback)으로 처리되었다. 즉 같은 프로세스 내에서 HTTP 요청을 자기 자신에게 보내고, SSE로 결과를 수신하는 구조였다. queue=False로 설정된 간단한 이벤트에도 이 경로를 탔기 때문에 thread dispatch, TCP round-trip, SSE 파싱 등 불필요한 오버헤드가 발생했다.
핵심 코드 분석
call_tool 메서드의 분기 처리
기존에는 queued/non-queued 구분 없이 모든 호출이 client.submit()을 거쳤다. 변경 후에는 block_fn.queue 여부에 따라 fast path와 slow path를 명확히 분리한다.
Before:
async def call_tool(self, name, arguments):
progress_token = None
if self.mcp_server.request_context.meta is not None:
progress_token = self.mcp_server.request_context.meta.progressToken
client = await run_sync(self._get_or_create_client)
endpoint_name, processed_args, request_headers, block_fn = (
self._prepare_tool_call_args(name, arguments)
)
processed_args = self.insert_empty_state(block_fn.inputs, processed_args)
job = client.submit(
*processed_args, api_name=endpoint_name, headers=request_headers
)
if progress_token is None or not block_fn.queue:
output_data = await self._execute_tool_without_progress(job)
else:
output_data = await self._execute_tool_with_progress(job, progress_token)
After:
async def call_tool(self, name, arguments):
endpoint_name, processed_args, request_headers, block_fn = (
self._prepare_tool_call_args(name, arguments)
)
processed_args = self.insert_empty_state(block_fn.inputs, processed_args)
if not block_fn.queue:
# Fast path: call blocks.process_api() directly
session_state = SessionState(self.blocks)
raw_output = await self.blocks.process_api(
block_fn=block_fn,
inputs=processed_args,
state=session_state,
request=self.mcp_server.request_context.request,
)
output_data = raw_output["data"]
else:
# Queued path: use HTTP loopback for streaming/progress
progress_token = None
if self.mcp_server.request_context.meta is not None:
progress_token = self.mcp_server.request_context.meta.progressToken
client = await run_sync(self._get_or_create_client)
job = client.submit(
*processed_args, api_name=endpoint_name, headers=request_headers,
)
if progress_token is None:
output_data = await self._execute_tool_without_progress(job)
else:
output_data = await self._execute_tool_with_progress(job, progress_token)
핵심 변경: block_fn.queue가 False이면 client.submit() 대신 self.blocks.process_api()를 직접 호출한다. HTTP 클라이언트 생성, TCP 연결, SSE 파싱이 전부 생략된다.
SessionState 직접 생성
from gradio.state_holder import SessionState
session_state = SessionState(self.blocks)
raw_output = await self.blocks.process_api(
block_fn=block_fn,
inputs=processed_args,
state=session_state,
request=self.mcp_server.request_context.request,
)
HTTP 경로에서는 세션 관리가 미들웨어에서 자동 처리되지만, direct call에서는 SessionState를 수동으로 생성해야 한다.
왜 이게 좋은가
- 레이턴시 감소: thread dispatch + TCP round-trip + SSE 오버헤드가 제거되어 MCP 도구 호출 응답 시간이 최대 10배 개선된다.
- Queued 이벤트에 영향 없음:
queue=True인 이벤트는 기존 HTTP 루프백 경로를 그대로 사용하여 progress notification, streaming 등의 기능이 보존된다. - 가이드 문서 업데이트:
queue=False설정의 성능 이점을 공식 문서에 명시하여 사용자가 선택할 수 있게 했다.
정리
MCP 통합에서 non-queued 이벤트의 불필요한 HTTP 루프백을 제거한 깔끔한 최적화다. 변경 코드량이 적고(+69/-16), queued/non-queued를 명확히 분리하여 기존 기능에 영향을 주지 않는다. MCP 도구 호출이 빈번한 환경에서 체감 성능 차이가 클 것으로 예상된다.
참고 자료
- gradio-app/gradio#12961 — 원본 PR
- Gradio MCP Guide — MCP 서버 구축 가이드
- Model Context Protocol — MCP 공식 문서
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
PR Analysis 의 다른글
- 이전글 [triton] PyTorch 없이 Triton CUDA 백엔드 독립 사용 지원
- 현재글 : [Gradio] MCP 도구 호출 레이턴시 개선 — HTTP 루프백 제거
- 다음글 [triton] Multi-CTA 튜토리얼 추가: CGA 기반 협력 연산
댓글