refactor(mcp_client): migrate web_search, fetch_url, get_ui_performance to Result[T] (Phase 7 sites 6,7,8)
Added web_search_result, fetch_url_result, get_ui_performance_result inside Result Variants region. The 3 legacy functions now delegate to their _result variants. Audit: mcp_client BC 8 -> 3 (sites 6,7,8 migrated). Remaining 3 sites are nested functions (1 in py_find_usages_result._search_file + 2 in derive_code_path_result.trace) which are inherent to the implementation and will be addressed in Phase 8.
This commit is contained in:
+79
-53
@@ -1034,6 +1034,58 @@ def get_tree_result(path: str, max_depth: int = 2) -> Result[str]:
|
||||
return Result(data="\n".join(tree_lines))
|
||||
except Exception as e:
|
||||
return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="mcp.get_tree_result", original=e)])
|
||||
|
||||
def web_search_result(query: str) -> Result[str]:
|
||||
url = "https://html.duckduckgo.com/html/?q=" + urllib.parse.quote(query)
|
||||
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
html = resp.read().decode('utf-8', errors='ignore')
|
||||
parser = _DDGParser()
|
||||
parser.feed(html)
|
||||
if not parser.results:
|
||||
return Result(data=f"No results found for '{query}'")
|
||||
lines = [f"Search Results for '{query}':"]
|
||||
for i, r in enumerate(parser.results[:5], 1):
|
||||
lines.append(f"{i}. {r['title']}\nURL: {r['link']}\nSnippet: {r['snippet']}\n")
|
||||
return Result(data="\n".join(lines))
|
||||
except Exception as e:
|
||||
return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="mcp.web_search_result", original=e)])
|
||||
|
||||
def fetch_url_result(url: str) -> Result[str]:
|
||||
if url.startswith("//duckduckgo.com/l/?uddg="):
|
||||
split_uddg = url.split("uddg=")
|
||||
if len(split_uddg) > 1:
|
||||
url = urllib.parse.unquote(split_uddg[1].split("&")[0])
|
||||
if not url.startswith("http"):
|
||||
url = "https://" + url
|
||||
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
html = resp.read().decode('utf-8', errors='ignore')
|
||||
parser = _TextExtractor()
|
||||
parser.feed(html)
|
||||
full_text = " ".join(parser.text)
|
||||
full_text = _re.sub(r'\s+', ' ', full_text)
|
||||
if not full_text.strip():
|
||||
return Result(data=f"FETCH OK: No readable text extracted from {url}. The page might be empty, JavaScript-heavy, or blocked.")
|
||||
if len(full_text) > 40000:
|
||||
return Result(data=full_text[:40000] + "\n... (content truncated)")
|
||||
return Result(data=full_text)
|
||||
except Exception as e:
|
||||
return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=str(e), source="mcp.fetch_url_result", original=e)])
|
||||
|
||||
def get_ui_performance_result() -> Result[str]:
|
||||
if perf_monitor_callback is None:
|
||||
return Result(data="INFO: UI Performance monitor is not available (headless/CLI mode). This tool is only functional when the Manual Slop GUI is running.")
|
||||
try:
|
||||
metrics = perf_monitor_callback()
|
||||
metric_str = str(metrics)
|
||||
for char in "{}'":
|
||||
metric_str = metric_str.replace(char, "")
|
||||
return Result(data=f"UI Performance Snapshot:\n{metric_str}")
|
||||
except Exception as e:
|
||||
return Result(data="", errors=[ErrorInfo(kind=ErrorKind.INTERNAL, message=f"Failed to retrieve UI performance: {str(e)}", source="mcp.get_ui_performance_result", original=e)])
|
||||
#endregion: Result Variants
|
||||
|
||||
def edit_file(path: str, old_string: str, new_string: str, replace_all: bool = False) -> str:
|
||||
@@ -1536,67 +1588,41 @@ class _TextExtractor(HTMLParser):
|
||||
self.text.append(cleaned)
|
||||
|
||||
def web_search(query: str) -> str:
|
||||
"""Search the web using DuckDuckGo HTML and return top results."""
|
||||
url = "https://html.duckduckgo.com/html/?q=" + urllib.parse.quote(query)
|
||||
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
html = resp.read().decode('utf-8', errors='ignore')
|
||||
parser = _DDGParser()
|
||||
parser.feed(html)
|
||||
if not parser.results:
|
||||
return f"No results found for '{query}'"
|
||||
lines = [f"Search Results for '{query}':"]
|
||||
for i, r in enumerate(parser.results[:5], 1):
|
||||
lines.append(f"{i}. {r['title']}\nURL: {r['link']}\nSnippet: {r['snippet']}\n")
|
||||
return "\n".join(lines)
|
||||
except Exception as e:
|
||||
return f"ERROR searching web for '{query}': {e}"
|
||||
"""Search the web using DuckDuckGo HTML and return top results.
|
||||
|
||||
Thin wrapper over web_search_result; the legacy str shape is
|
||||
preserved for backward compatibility, but the try/except Exception
|
||||
lives in the Result variant.
|
||||
"""
|
||||
resolved = web_search_result(query)
|
||||
if resolved.ok:
|
||||
return resolved.data
|
||||
return "; ".join(e.ui_message() for e in resolved.errors)
|
||||
|
||||
def fetch_url(url: str) -> str:
|
||||
"""Fetch a URL and return its text content (stripped of HTML tags)."""
|
||||
# Correct duckduckgo redirect links if passed
|
||||
if url.startswith("//duckduckgo.com/l/?uddg="):
|
||||
split_uddg = url.split("uddg=")
|
||||
if len(split_uddg) > 1:
|
||||
url = urllib.parse.unquote(split_uddg[1].split("&")[0])
|
||||
if not url.startswith("http"):
|
||||
url = "https://" + url
|
||||
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
html = resp.read().decode('utf-8', errors='ignore')
|
||||
parser = _TextExtractor()
|
||||
parser.feed(html)
|
||||
full_text = " ".join(parser.text)
|
||||
full_text = _re.sub(r'\s+', ' ', full_text)
|
||||
if not full_text.strip():
|
||||
return f"FETCH OK: No readable text extracted from {url}. The page might be empty, JavaScript-heavy, or blocked."
|
||||
# Limit to 40k chars to prevent context blowup
|
||||
if len(full_text) > 40000:
|
||||
return full_text[:40000] + "\n... (content truncated)"
|
||||
return full_text
|
||||
except Exception as e:
|
||||
return f"ERROR fetching URL '{url}': {e}"
|
||||
|
||||
#endregion: Web
|
||||
"""Fetch a URL and return its text content (stripped of HTML tags).
|
||||
|
||||
Thin wrapper over fetch_url_result; the legacy str shape is
|
||||
preserved for backward compatibility, but the try/except Exception
|
||||
lives in the Result variant.
|
||||
"""
|
||||
resolved = fetch_url_result(url)
|
||||
if resolved.ok:
|
||||
return resolved.data
|
||||
return "; ".join(e.ui_message() for e in resolved.errors)
|
||||
def get_ui_performance() -> str:
|
||||
"""
|
||||
Returns current UI performance metrics (FPS, Frame Time, CPU, Input Lag).
|
||||
[C: tests/test_mcp_perf_tool.py:test_mcp_perf_tool_retrieval]
|
||||
|
||||
Thin wrapper over get_ui_performance_result; the legacy str shape is
|
||||
preserved for backward compatibility, but the try/except Exception
|
||||
lives in the Result variant.
|
||||
"""
|
||||
if perf_monitor_callback is None:
|
||||
return "INFO: UI Performance monitor is not available (headless/CLI mode). This tool is only functional when the Manual Slop GUI is running."
|
||||
try:
|
||||
metrics = perf_monitor_callback()
|
||||
# Clean up the dict string for the AI
|
||||
metric_str = str(metrics)
|
||||
for char in "{}'":
|
||||
metric_str = metric_str.replace(char, "")
|
||||
return f"UI Performance Snapshot:\n{metric_str}"
|
||||
except Exception as e:
|
||||
return f"ERROR: Failed to retrieve UI performance: {str(e)}"
|
||||
resolved = get_ui_performance_result()
|
||||
if resolved.ok:
|
||||
return resolved.data
|
||||
return "; ".join(e.ui_message() for e in resolved.errors)
|
||||
# ------------------------------------------------------------------ tool dispatch
|
||||
|
||||
class StdioMCPServer:
|
||||
|
||||
Reference in New Issue
Block a user