@@ -63,7 +63,7 @@ def get_robots_txt_url(url: str) -> str:
63
63
return robots_url
64
64
65
65
66
- async def check_may_autonomously_fetch_url (url : str , user_agent : str ) -> None :
66
+ async def check_may_autonomously_fetch_url (url : str , user_agent : str , proxy_url : str | None = None ) -> None :
67
67
"""
68
68
Check if the URL can be fetched by the user agent according to the robots.txt file.
69
69
Raises a McpError if not.
@@ -72,7 +72,7 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str) -> None:
72
72
73
73
robot_txt_url = get_robots_txt_url (url )
74
74
75
- async with AsyncClient () as client :
75
+ async with AsyncClient (proxies = proxy_url ) as client :
76
76
try :
77
77
response = await client .get (
78
78
robot_txt_url ,
@@ -109,14 +109,14 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str) -> None:
109
109
110
110
111
111
async def fetch_url (
112
- url : str , user_agent : str , force_raw : bool = False
112
+ url : str , user_agent : str , force_raw : bool = False , proxy_url : str | None = None
113
113
) -> Tuple [str , str ]:
114
114
"""
115
115
Fetch the URL and return the content in a form ready for the LLM, as well as a prefix string with status information.
116
116
"""
117
117
from httpx import AsyncClient , HTTPError
118
118
119
- async with AsyncClient () as client :
119
+ async with AsyncClient (proxies = proxy_url ) as client :
120
120
try :
121
121
response = await client .get (
122
122
url ,
@@ -179,13 +179,16 @@ class Fetch(BaseModel):
179
179
180
180
181
181
async def serve (
182
- custom_user_agent : str | None = None , ignore_robots_txt : bool = False
182
+ custom_user_agent : str | None = None ,
183
+ ignore_robots_txt : bool = False ,
184
+ proxy_url : str | None = None ,
183
185
) -> None :
184
186
"""Run the fetch MCP server.
185
187
186
188
Args:
187
189
custom_user_agent: Optional custom User-Agent string to use for requests
188
190
ignore_robots_txt: Whether to ignore robots.txt restrictions
191
+ proxy_url: Optional proxy URL to use for requests
189
192
"""
190
193
server = Server ("mcp-fetch" )
191
194
user_agent_autonomous = custom_user_agent or DEFAULT_USER_AGENT_AUTONOMOUS
@@ -229,10 +232,10 @@ async def call_tool(name, arguments: dict) -> list[TextContent]:
229
232
raise McpError (ErrorData (code = INVALID_PARAMS , message = "URL is required" ))
230
233
231
234
if not ignore_robots_txt :
232
- await check_may_autonomously_fetch_url (url , user_agent_autonomous )
235
+ await check_may_autonomously_fetch_url (url , user_agent_autonomous , proxy_url )
233
236
234
237
content , prefix = await fetch_url (
235
- url , user_agent_autonomous , force_raw = args .raw
238
+ url , user_agent_autonomous , force_raw = args .raw , proxy_url = proxy_url
236
239
)
237
240
original_length = len (content )
238
241
if args .start_index >= original_length :
@@ -259,7 +262,7 @@ async def get_prompt(name: str, arguments: dict | None) -> GetPromptResult:
259
262
url = arguments ["url" ]
260
263
261
264
try :
262
- content , prefix = await fetch_url (url , user_agent_manual )
265
+ content , prefix = await fetch_url (url , user_agent_manual , proxy_url = proxy_url )
263
266
# TODO: after SDK bug is addressed, don't catch the exception
264
267
except McpError as e :
265
268
return GetPromptResult (
0 commit comments