MCPHook¶
Use MCPHook to connect a Dag
task to an MCP (Model Context Protocol)
server. The hook manages the server’s connection configuration – transport
type, URL or subprocess command, and credentials – and builds the matching
client transport, so a Dag never hard-codes a server URL or an auth token.
Three transport types are supported: HTTP (Streamable HTTP), SSE, and stdio.
See also
MCP Server Connection for connection configuration and JSON examples.
Role in a Dag: use MCPToolset, not the hook directly¶
Most Dags should not instantiate MCPHook directly. The recommended entry
point is MCPToolset (see
Toolsets — Airflow Hooks as AI Agent Tools), which resolves the hook lazily from an Airflow connection
and manages the underlying session lifecycle for you:
@dag(tags=["example"])
def example_mcp_toolset():
"""Use an MCP server configured via an Airflow connection."""
AgentOperator(
task_id="mcp_agent",
prompt="What tools are available? Run the hello tool.",
llm_conn_id="pydanticai_default",
system_prompt="You are a helpful assistant with access to MCP tools.",
toolsets=[
MCPToolset(mcp_conn_id="my_mcp_server"),
],
)
Pass one or more MCPToolset instances to
AgentOperator’s
toolsets parameter and it drives MCPHook internally on every call.
Under the hood, MCPToolset builds an MCPHook from mcp_conn_id,
tool_prefix, token_provider, and env_provider, then calls
hook.get_conn() the first time it needs the server.
What MCPHook itself does¶
MCPHook.get_conn() reads the connection’s transport type from
Extra.transport and returns a configured pydantic-ai
MCPToolset – an upstream
pydantic-ai class, distinct from Airflow’s own
MCPToolset described above
– built over the matching FastMCP transport:
http(default):fastmcp.client.transports.StreamableHttpTransportsse:fastmcp.client.transports.SSETransportstdio:fastmcp.client.transports.StdioTransport
When tool_prefix is set, the returned toolset is wrapped so every tool
name gets that prefix (e.g. "weather" yields weather_get_forecast).
The result is cached for the lifetime of the hook instance.
test_connection() validates that the connection has the fields required
for its configured transport, but does not connect to the server – doing so
requires the async context manager that MCPToolset drives.
Short-lived credentials¶
For HTTP/SSE servers that need a freshly minted bearer token (Snowflake
managed MCP servers, OAuth/refresh tokens, Workload Identity Federation,
GitHub App installation tokens), pass token_provider instead of storing a
static token in the connection password. For stdio servers whose
subprocess needs a credential that lives in a different connection or is
minted fresh per call, pass env_provider instead of a static
Extra.env value. Both are zero-argument callables invoked once, the
first time the hook establishes a connection, and their return values are
registered with secret masking. See MCP Server Connection
for the full explanation and an env_provider example.
Connection fields¶
MCPHook uses the mcp connection type. Its custom fields
(transport, command, args) come from the connection’s
extra JSON:
host – Server URL. Required for the
httpandssetransports.password – Optional auth token, labeled “Auth Token” in the connection form. Sent as a static
Authorization: Bearer <token>header.Extra.transport –
http(default),sse, orstdio.Extra.command – Command to run for the
stdiotransport (e.g.uvx,python).Extra.args – JSON array of arguments for the
stdiocommand (e.g.["mcp-run-python"]).Extra.env – JSON object of environment variables for the
stdiosubprocess. Ignored forhttp/sse.Extra.timeout – Connection init timeout in seconds for
stdio. Default10.
See MCP Server Connection for the field-by-field walkthrough and transport-specific JSON examples (HTTP, SSE, stdio, stdio with a custom timeout, stdio with subprocess environment variables).
Dependencies¶
Install the mcp extra:
pip install "apache-airflow-providers-common-ai[mcp]"