scripts
List all loaded scripts, retrieve full source of a specific script, or save script source to a local file. Used for inspecting scripts during JavaScript reverse engineering.
Instructions
Script inspection (v0.9.0 unified).
Replaces list_scripts / get_script_source / save_script.
Args: action: "list" — list all loaded scripts (src, type, inline preview) "get" — get full source of one script (requires url; use "inline:" for inline scripts) "save" — save script source to local file (requires url + save_path) url: Script URL or "inline:" (required for "get" and "save"). save_path: Local file path (required for "save").
Returns: For "list": list of script info dicts. For "get": dict with source string. For "save": dict with status, path, size.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | ||
| url | No | ||
| save_path | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- The `scripts` MCP tool handler - dispatches list/get/save actions for script inspection. Registered via @mcp.tool() decorator.
@mcp.tool() async def scripts( action: str, url: str | None = None, save_path: str | None = None, ) -> dict | list | str: """Script inspection (v0.9.0 unified). Replaces list_scripts / get_script_source / save_script. Args: action: "list" — list all loaded scripts (src, type, inline preview) "get" — get full source of one script (requires url; use "inline:<index>" for inline scripts) "save" — save script source to local file (requires url + save_path) url: Script URL or "inline:<index>" (required for "get" and "save"). save_path: Local file path (required for "save"). Returns: For "list": list of script info dicts. For "get": dict with source string. For "save": dict with status, path, size. """ if action == "list": return await _list_scripts() elif action == "get": if not url: return {"error": "url is required for action='get'"} src = await _get_script_source(url) return {"source": src, "url": url, "length": len(src) if isinstance(src, str) else 0} elif action == "save": if not url: return {"error": "url is required for action='save'"} if not save_path: return {"error": "save_path is required for action='save'"} return await _save_script(url, save_path) else: return {"error": f"unknown action: {action}. Use list/get/save"} - The `search_code` MCP tool handler - searches keywords in loaded scripts across all or a single script.
@mcp.tool() async def search_code( keyword: str, script_url: str | None = None, context_chars: int = 200, context_lines: int = 3, max_results: int = 200, ) -> dict: """Search keyword in loaded scripts (v0.9.0 unified). Replaces search_code (all scripts) + search_code_in_script (single script). Args: keyword: The keyword to search for (case-sensitive substring match). script_url: If None, search across ALL loaded scripts. If given, search within that one script only (supports "inline:<index>" for inline scripts). Single-script mode auto-detects minified files and uses character-based context. context_chars: Context window in char mode (default 200 = +/-200 chars). Used when searching single minified scripts. context_lines: Context window in line mode (default 3). max_results: Maximum matches to return (default 200). Returns: dict with matches, total_matches, mode ("line" | "char"), etc. """ if script_url is None: return await _search_code_all(keyword, max_results) else: return await _search_code_in_script( script_url, keyword, context_lines, context_chars, max_results ) - Internal helper `_list_scripts` - enumerates all <script> elements on the page via DOM query.
async def _list_scripts() -> list[dict]: try: page = await browser_manager.get_active_page() scripts_list = await page.evaluate("""() => { const scripts = document.querySelectorAll('script'); return Array.from(scripts).map((s, i) => ({ index: i, src: s.src || null, type: s.type || 'text/javascript', is_module: s.type === 'module', inline_length: s.src ? 0 : (s.textContent || '').length, preview: s.src ? null : (s.textContent || '').substring(0, 200) })); }""") # v1.0.1: add hint for large scripts for s in scripts_list: size = s.get("inline_length", 0) src = s.get("src") if src and size == 0: # For external scripts, we don't know size yet from DOM alone # but we can hint based on common patterns pass elif size > 100_000: s["hint"] = ( f"Large script ({size} bytes). Consider saving for " f"offline analysis: scripts(action='save', " f"url='inline:{s['index']}', save_path='./script_{s['index']}.js')" ) return scripts_list except Exception as e: return [{"error": str(e)}] - Internal helper `_get_script_source` - retrieves inline or external script source via page.evaluate.
async def _get_script_source(url: str) -> str: try: page = await browser_manager.get_active_page() if url.startswith("inline:"): idx = int(url.split(":")[1]) source = await page.evaluate(f"""() => {{ const scripts = document.querySelectorAll('script'); return scripts[{idx}] ? scripts[{idx}].textContent : null; }}""") return source or f"Inline script at index {idx} not found" else: source = await page.evaluate(f"""async () => {{ try {{ const resp = await fetch("{url}"); return await resp.text(); }} catch(e) {{ return "Fetch error: " + e.message; }} }}""") return source except Exception as e: return f"Error: {e}" - src/camoufox_reverse_mcp/server.py:16-16 (registration)Registration import that loads the script_analysis module (and thus the @mcp.tool() decorated functions) into the server.
from .tools import script_analysis # noqa: E402, F401 — scripts() + search_code()