@@ -92,6 +92,13 @@ def list_files() -> str:
9292 return "\n " .join (lines ) if lines else "(empty)"
9393
9494
95+ def web_fetch (url : str ) -> str :
96+ """Fetch a URL and return the response body (first 4KB)."""
97+ from urllib .request import urlopen
98+ resp = urlopen (url , timeout = 10 )
99+ return resp .read (4096 ).decode ("utf-8" , errors = "replace" )
100+
101+
95102# ---------------------------------------------------------------------------
96103# Agent loop
97104# ---------------------------------------------------------------------------
@@ -147,6 +154,19 @@ async def run_agent(user_prompt: str, workspace: str):
147154 # No capabilities needed — default read-only is sufficient
148155 input_schema = {"type" : "object" , "properties" : {}},
149156 )
157+ mcp .add_tool (
158+ "web_fetch" , web_fetch ,
159+ description = "Fetch a URL and return the response body. Only httpbin.org is allowed." ,
160+ capabilities = {
161+ "net_connect" : [443 ],
162+ "net_allow_hosts" : ["httpbin.org" ], # DNS restricted to this host
163+ },
164+ input_schema = {
165+ "type" : "object" ,
166+ "properties" : {"url" : {"type" : "string" , "description" : "URL to fetch" }},
167+ "required" : ["url" ],
168+ },
169+ )
150170
151171 # -- Show per-tool policies --
152172 print (f"Workspace: { workspace } " )
@@ -155,7 +175,8 @@ async def run_agent(user_prompt: str, workspace: str):
155175 p = mcp .get_policy (name )
156176 rw = "read-only" if not p .fs_writable else "read-write"
157177 net = f"ports { list (p .net_connect )} " if p .net_connect else "none"
158- print (f" { name :15s} fs={ rw :10s} net={ net } " )
178+ hosts = f" hosts={ list (p .net_allow_hosts )} " if p .net_allow_hosts else ""
179+ print (f" { name :15s} fs={ rw :10s} net={ net } { hosts } " )
159180 print ()
160181
161182 # -- OpenAI agent loop --
0 commit comments