Model context protocol (MCP)
Model context protocol (MCP)은 애플리케이션이 언어 모델에 도구와 컨텍스트를 노출하는 방식을 표준화합니다. 공식 문서에 따르면 다음과 같습니다:
MCP는 애플리케이션이 LLM에 컨텍스트를 제공하는 방식을 표준화하는 오픈 프로토콜입니다. MCP를 AI 애플리케이션을 위한 USB-C 포트처럼 생각해 보세요. USB-C가 다양한 주변기기와 액세서리에 디바이스를 연결하는 표준화된 방법을 제공하듯, MCP는 AI 모델을 서로 다른 데이터 소스와 도구에 연결하는 표준화된 방법을 제공합니다.
Agents Python SDK는 여러 MCP 전송(transport)을 이해합니다. 이를 통해 기존 MCP 서버를 재사용하거나, 파일 시스템/HTTP/커넥터 기반 도구를 에이전트에 노출하도록 직접 MCP 서버를 구축할 수 있습니다.
MCP 통합 선택
에이전트에 MCP 서버를 연결하기 전에, 도구 호출이 어디에서 실행되어야 하는지와 어떤 전송 방식에 접근 가능한지를 결정하세요. 아래 매트릭스는 Python SDK가 지원하는 옵션을 요약합니다.
| 필요한 것 | 권장 옵션 |
|---|---|
| 모델을 대신해 OpenAI의 Responses API가 공개적으로 접근 가능한 MCP 서버를 호출하게 하기 | HostedMCPTool을 통한 Hosted MCP server tools |
| 로컬 또는 원격에서 직접 운영하는 Streamable HTTP 서버에 연결하기 | MCPServerStreamableHttp을 통한 Streamable HTTP MCP servers |
| Server-Sent Events를 사용하는 HTTP를 구현한 서버와 통신하기 | MCPServerSse를 통한 HTTP with SSE MCP servers |
| 로컬 프로세스를 실행하고 stdin/stdout으로 통신하기 | MCPServerStdio를 통한 stdio MCP servers |
아래 섹션에서는 각 옵션, 구성 방법, 그리고 어떤 전송을 언제 우선해야 하는지를 설명합니다.
에이전트 수준 MCP 구성
전송 방식 선택 외에도, Agent.mcp_config를 설정하여 MCP 도구가 준비되는 방식을 조정할 수 있습니다.
from agents import Agent
agent = Agent(
name="Assistant",
mcp_servers=[server],
mcp_config={
# Try to convert MCP tool schemas to strict JSON schema.
"convert_schemas_to_strict": True,
# If None, MCP tool failures are raised as exceptions instead of
# returning model-visible error text.
"failure_error_function": None,
},
)
참고:
convert_schemas_to_strict는 최선의 노력(best-effort) 방식입니다. 스키마를 변환할 수 없으면 원래 스키마가 사용됩니다.failure_error_function은 MCP 도구 호출 실패가 모델에 어떻게 노출되는지 제어합니다.failure_error_function이 설정되지 않으면 SDK는 기본 도구 오류 포매터를 사용합니다.- 서버 수준
failure_error_function은 해당 서버에 대해Agent.mcp_config["failure_error_function"]보다 우선합니다.
1. Hosted MCP server tools
호스티드 툴은 도구 호출의 전체 왕복(round-trip)을 OpenAI 인프라로 푸시합니다. 코드에서 도구를 나열하고 호출하는 대신, HostedMCPTool이 서버 라벨(및 선택적 커넥터 메타데이터)을 Responses API로 전달합니다. 모델은 원격 서버의 도구를 나열하고, Python 프로세스에 추가 콜백 없이 이를 호출합니다. 호스티드 툴은 현재 Responses API의 호스티드 MCP 통합을 지원하는 OpenAI 모델에서 동작합니다.
기본 호스티드 MCP 도구
에이전트의 tools 목록에 HostedMCPTool을 추가하여 호스티드 툴을 만드세요. tool_config 딕셔너리는 REST API로 보낼 JSON을 그대로 반영합니다:
import asyncio
from agents import Agent, HostedMCPTool, Runner
async def main() -> None:
agent = Agent(
name="Assistant",
tools=[
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "gitmcp",
"server_url": "https://gitmcp.io/openai/codex",
"require_approval": "never",
}
)
],
)
result = await Runner.run(agent, "Which language is this repository written in?")
print(result.final_output)
asyncio.run(main())
호스티드 서버는 도구를 자동으로 노출하며, mcp_servers에 추가하지 않습니다.
호스티드 MCP 결과 스트리밍
호스티드 툴은 함수 도구와 정확히 같은 방식으로 결과 스트리밍을 지원합니다. 모델이 아직 작업 중일 때 증가분 MCP 출력을 소비하려면 Runner.run_streamed를 사용하세요:
result = Runner.run_streamed(agent, "Summarise this repository's top languages")
async for event in result.stream_events():
if event.type == "run_item_stream_event":
print(f"Received: {event.item}")
print(result.final_output)
선택적 승인 플로우
서버가 민감한 작업을 수행할 수 있다면, 각 도구 실행 전에 사람 또는 프로그램 기반 승인을 요구할 수 있습니다. tool_config에서 require_approval을 단일 정책("always", "never") 또는 도구 이름을 정책에 매핑하는 딕셔너리로 설정하세요. Python에서 결정을 내리려면 on_approval_request 콜백을 제공하세요.
from agents import MCPToolApprovalFunctionResult, MCPToolApprovalRequest
SAFE_TOOLS = {"read_project_metadata"}
def approve_tool(request: MCPToolApprovalRequest) -> MCPToolApprovalFunctionResult:
if request.data.name in SAFE_TOOLS:
return {"approve": True}
return {"approve": False, "reason": "Escalate to a human reviewer"}
agent = Agent(
name="Assistant",
tools=[
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "gitmcp",
"server_url": "https://gitmcp.io/openai/codex",
"require_approval": "always",
},
on_approval_request=approve_tool,
)
],
)
이 콜백은 동기 또는 비동기일 수 있으며, 모델이 계속 실행하기 위해 승인 데이터가 필요할 때마다 호출됩니다.
커넥터 기반 호스티드 서버
호스티드 MCP는 OpenAI 커넥터도 지원합니다. server_url을 지정하는 대신 connector_id와 액세스 토큰을 제공하세요. Responses API가 인증을 처리하고 호스티드 서버가 커넥터의 도구를 노출합니다.
import os
HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "google_calendar",
"connector_id": "connector_googlecalendar",
"authorization": os.environ["GOOGLE_CALENDAR_AUTHORIZATION"],
"require_approval": "never",
}
)
스트리밍, 승인, 커넥터를 포함한 완전 동작 호스티드 툴 샘플은
examples/hosted_mcp에 있습니다.
2. Streamable HTTP MCP servers
네트워크 연결을 직접 관리하려면 MCPServerStreamableHttp를 사용하세요. Streamable HTTP 서버는 전송을 제어하거나, 지연 시간을 낮게 유지하면서 자체 인프라에서 서버를 실행하고자 할 때 이상적입니다.
import asyncio
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
from agents.model_settings import ModelSettings
async def main() -> None:
token = os.environ["MCP_SERVER_TOKEN"]
async with MCPServerStreamableHttp(
name="Streamable HTTP Python Server",
params={
"url": "http://localhost:8000/mcp",
"headers": {"Authorization": f"Bearer {token}"},
"timeout": 10,
},
cache_tools_list=True,
max_retry_attempts=3,
) as server:
agent = Agent(
name="Assistant",
instructions="Use the MCP tools to answer the questions.",
mcp_servers=[server],
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(agent, "Add 7 and 22.")
print(result.final_output)
asyncio.run(main())
생성자는 추가 옵션을 받습니다:
client_session_timeout_seconds는 HTTP 읽기 타임아웃을 제어합니다.use_structured_content는 텍스트 출력보다tool_result.structured_content를 우선할지 여부를 토글합니다.max_retry_attempts및retry_backoff_seconds_base는list_tools()와call_tool()에 대한 자동 재시도를 추가합니다.tool_filter는 일부 도구만 노출할 수 있게 합니다(도구 필터링 참고).require_approval은 로컬 MCP 도구에 대한 휴먼인더루프 승인 정책을 활성화합니다.failure_error_function은 모델에 표시되는 MCP 도구 실패 메시지를 커스터마이즈합니다. 대신 오류를 발생시키려면None으로 설정하세요.tool_meta_resolver는call_tool()전에 호출별 MCP_meta페이로드를 주입합니다.
로컬 MCP 서버의 승인 정책
MCPServerStdio, MCPServerSse, MCPServerStreamableHttp는 모두 require_approval을 받습니다.
지원 형태:
- 모든 도구에 대해
"always"또는"never" True/False(항상/절대 안 함과 동일)- 도구별 맵, 예:
{"delete_file": "always", "read_file": "never"} - 그룹 객체:
{"always": {"tool_names": [...]}, "never": {"tool_names": [...]}}
async with MCPServerStreamableHttp(
name="Filesystem MCP",
params={"url": "http://localhost:8000/mcp"},
require_approval={"always": {"tool_names": ["delete_file"]}},
) as server:
...
전체 일시정지/재개 플로우는 Human-in-the-loop 및 examples/mcp/get_all_mcp_tools_example/main.py를 참고하세요.
tool_meta_resolver로 호출별 메타데이터 전달
MCP 서버가 _meta에 요청 메타데이터(예: 테넌트 ID 또는 트레이스 컨텍스트)를 기대하는 경우 tool_meta_resolver를 사용하세요. 아래 예시는 Runner.run(...)에 dict를 context로 전달한다고 가정합니다.
from agents.mcp import MCPServerStreamableHttp, MCPToolMetaContext
def resolve_meta(context: MCPToolMetaContext) -> dict[str, str] | None:
run_context_data = context.run_context.context or {}
tenant_id = run_context_data.get("tenant_id")
if tenant_id is None:
return None
return {"tenant_id": str(tenant_id), "source": "agents-sdk"}
server = MCPServerStreamableHttp(
name="Metadata-aware MCP",
params={"url": "http://localhost:8000/mcp"},
tool_meta_resolver=resolve_meta,
)
실행 컨텍스트가 Pydantic 모델, dataclass, 또는 사용자 정의 클래스라면, 대신 속성 접근으로 테넌트 ID를 읽으세요.
MCP 도구 출력: 텍스트와 이미지
MCP 도구가 이미지 콘텐츠를 반환하면 SDK는 이를 자동으로 이미지 도구 출력 엔트리로 매핑합니다. 텍스트/이미지가 섞인 응답은 출력 항목 리스트로 전달되므로, 에이전트는 일반 함수 도구의 이미지 출력과 동일한 방식으로 MCP 이미지 결과를 소비할 수 있습니다.
3. HTTP with SSE MCP servers
Warning
MCP 프로젝트는 Server-Sent Events 전송을 더 이상 권장하지 않습니다. 새로운 통합에는 Streamable HTTP 또는 stdio를 선호하고, SSE는 레거시 서버에만 사용하세요.
MCP 서버가 HTTP with SSE 전송을 구현한다면 MCPServerSse를 인스턴스화하세요. 전송 방식 외에는 API가 Streamable HTTP 서버와 동일합니다.
from agents import Agent, Runner
from agents.model_settings import ModelSettings
from agents.mcp import MCPServerSse
workspace_id = "demo-workspace"
async with MCPServerSse(
name="SSE Python Server",
params={
"url": "http://localhost:8000/sse",
"headers": {"X-Workspace": workspace_id},
},
cache_tools_list=True,
) as server:
agent = Agent(
name="Assistant",
mcp_servers=[server],
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(agent, "What's the weather in Tokyo?")
print(result.final_output)
4. stdio MCP servers
로컬 서브프로세스로 실행되는 MCP 서버에는 MCPServerStdio를 사용하세요. SDK는 프로세스를 생성하고, 파이프를 열린 상태로 유지하며, 컨텍스트 매니저가 종료되면 자동으로 닫습니다. 이 옵션은 빠른 개념 검증(POC)이나 서버가 커맨드라인 엔트리 포인트만 노출하는 경우에 유용합니다.
from pathlib import Path
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
current_dir = Path(__file__).parent
samples_dir = current_dir / "sample_files"
async with MCPServerStdio(
name="Filesystem Server via npx",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)],
},
) as server:
agent = Agent(
name="Assistant",
instructions="Use the files in the sample directory to answer questions.",
mcp_servers=[server],
)
result = await Runner.run(agent, "List the files available to you.")
print(result.final_output)
5. MCP server manager
여러 MCP 서버가 있는 경우 MCPServerManager를 사용해 미리 연결하고, 연결된 하위 집합을 에이전트에 노출하세요.
from agents import Agent, Runner
from agents.mcp import MCPServerManager, MCPServerStreamableHttp
servers = [
MCPServerStreamableHttp(name="calendar", params={"url": "http://localhost:8000/mcp"}),
MCPServerStreamableHttp(name="docs", params={"url": "http://localhost:8001/mcp"}),
]
async with MCPServerManager(servers) as manager:
agent = Agent(
name="Assistant",
instructions="Use MCP tools when they help.",
mcp_servers=manager.active_servers,
)
result = await Runner.run(agent, "Which MCP tools are available?")
print(result.final_output)
주요 동작:
active_servers에는drop_failed_servers=True(기본값)일 때 연결에 성공한 서버만 포함됩니다.- 실패는
failed_servers및errors에서 추적됩니다. strict=True로 설정하면 첫 연결 실패에서 예외를 발생시킵니다.reconnect(failed_only=True)를 호출해 실패한 서버만 재시도하거나,reconnect(failed_only=False)로 모든 서버를 재시작하세요.connect_timeout_seconds,cleanup_timeout_seconds,connect_in_parallel로 라이프사이클 동작을 조정하세요.
도구 필터링
각 MCP 서버는 도구 필터를 지원하므로 에이전트에 필요한 함수만 노출할 수 있습니다. 필터링은 생성 시점 또는 실행(run)별 동적 방식으로 수행할 수 있습니다.
정적 도구 필터링
create_static_tool_filter를 사용해 간단한 허용/차단 목록을 구성하세요:
from pathlib import Path
from agents.mcp import MCPServerStdio, create_static_tool_filter
samples_dir = Path("/path/to/files")
filesystem_server = MCPServerStdio(
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)],
},
tool_filter=create_static_tool_filter(allowed_tool_names=["read_file", "write_file"]),
)
allowed_tool_names와 blocked_tool_names가 모두 제공되면, SDK는 먼저 허용 목록을 적용한 뒤 남은 집합에서 차단 도구를 제거합니다.
동적 도구 필터링
더 정교한 로직이 필요하다면 ToolFilterContext를 받는 callable을 전달하세요. callable은 동기 또는 비동기일 수 있으며, 도구를 노출해야 할 때 True를 반환합니다.
from pathlib import Path
from agents.mcp import MCPServerStdio, ToolFilterContext
samples_dir = Path("/path/to/files")
async def context_aware_filter(context: ToolFilterContext, tool) -> bool:
if context.agent.name == "Code Reviewer" and tool.name.startswith("danger_"):
return False
return True
async with MCPServerStdio(
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)],
},
tool_filter=context_aware_filter,
) as server:
...
필터 컨텍스트는 활성 run_context, 도구를 요청하는 agent, 그리고 server_name을 노출합니다.
프롬프트
MCP 서버는 에이전트 지시사항을 동적으로 생성하는 프롬프트도 제공할 수 있습니다. 프롬프트를 지원하는 서버는 두 가지 메서드를 노출합니다:
list_prompts()는 사용 가능한 프롬프트 템플릿을 열거합니다.get_prompt(name, arguments)는 (선택적으로 매개변수와 함께) 구체적인 프롬프트를 가져옵니다.
from agents import Agent
prompt_result = await server.get_prompt(
"generate_code_review_instructions",
{"focus": "security vulnerabilities", "language": "python"},
)
instructions = prompt_result.messages[0].content.text
agent = Agent(
name="Code Reviewer",
instructions=instructions,
mcp_servers=[server],
)
캐싱
모든 에이전트 실행은 각 MCP 서버에서 list_tools()를 호출합니다. 원격 서버는 눈에 띄는 지연 시간을 유발할 수 있으므로, 모든 MCP 서버 클래스는 cache_tools_list 옵션을 제공합니다. 도구 정의가 자주 바뀌지 않는다고 확신하는 경우에만 True로 설정하세요. 나중에 최신 목록을 강제로 가져오려면 서버 인스턴스에서 invalidate_tools_cache()를 호출하세요.
트레이싱
트레이싱은 다음을 포함해 MCP 활동을 자동으로 캡처합니다:
- 도구 목록을 나열하기 위한 MCP 서버 호출
- 도구 호출에 대한 MCP 관련 정보

추가 읽을거리
- Model Context Protocol – 사양 및 설계 가이드
- examples/mcp – 실행 가능한 stdio, SSE, Streamable HTTP 샘플
- examples/hosted_mcp – 승인 및 커넥터를 포함한 완전한 호스티드 MCP 데모