Skip to main content
Glama

execute_in_session

Execute JavaScript code within an active Frida session to dynamically instrument and analyze running processes for reverse engineering and debugging purposes.

Instructions

Execute JavaScript code within an existing interactive Frida session.

This tool allows for dynamic scripting against a process previously attached to
via `create_interactive_session`.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
session_idYesThe unique identifier of the active Frida session. This ID is obtained when the session is first created.
javascript_codeYesA string containing the JavaScript code to be executed in the target process's context. The script can use Frida's JavaScript API (e.g., Interceptor, Memory, Module, rpc).
keep_aliveNoA boolean flag indicating whether the script should remain loaded in the target process after its initial execution. If False (default), the script is unloaded after initial run. If True, it persists for hooks/RPC and messages are retrieved via get_session_messages. Note: With keep_alive=True, JavaScript code should manage log volume (limits, deduplication) to prevent too many messages.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core handler that executes the provided JavaScript code in the specified Frida session. Supports both one-off execution and persistent scripts with message queuing for async events like hooks.
    def execute_in_session(
        session_id: str = Field(
            description="The unique identifier of the active Frida session. This ID is obtained when the session is first created."
        ),
        javascript_code: str = Field(
            description="A string containing the JavaScript code to be executed in the target process's context. The script can use Frida's JavaScript API (e.g., Interceptor, Memory, Module, rpc)."
        ),
        keep_alive: bool = Field(
            default=False,
            description="A boolean flag indicating whether the script should remain loaded in the target process after its initial execution. If False (default), the script is unloaded after initial run. If True, it persists for hooks/RPC and messages are retrieved via get_session_messages. Note: With keep_alive=True, JavaScript code should manage log volume (limits, deduplication) to prevent too many messages.",
        ),
    ) -> Dict[str, Any]:
        """Execute JavaScript code within an existing interactive Frida session.
    
        This tool allows for dynamic scripting against a process previously attached to
        via `create_interactive_session`.
        """
        if session_id not in _scripts:
            raise ValueError(f"Session with ID {session_id} not found")
    
        session = _scripts[session_id]
        lock = _message_locks[session_id]
    
        try:
            # For interactive use, we need to handle console.log output
            # and properly format the result
    
            # Wrap the code to capture console.log output and return values
            # This basic wrapper sends back immediate script result/errors and console.log output
            # For keep_alive=True, subsequent messages from the script (e.g., from Interceptor)
            # will be handled by the persistent on_message handler.
            wrapped_code = f"""
            (function() {{
                function formatValue(value) {{
                    if (value === null) {{
                        return 'null';
                    }}
                    if (value === undefined) {{
                        return 'undefined';
                    }}
    
                    var valueType = typeof value;
                    if (valueType === 'string') {{
                        return value;
                    }}
                    if (valueType === 'number' || valueType === 'boolean' || valueType === 'bigint') {{
                        return String(value);
                    }}
                    if (valueType === 'symbol' || valueType === 'function') {{
                        try {{
                            return value.toString();
                        }} catch (e) {{
                            return '[unrepresentable value]';
                        }}
                    }}
                    if (valueType === 'object') {{
                        try {{
                            var cache = new Set();
                            var json = JSON.stringify(value, function(key, val) {{
                                if (typeof val === 'object' && val !== null) {{
                                    if (cache.has(val)) {{
                                        return '[Circular]';
                                    }}
                                    cache.add(val);
                                }}
                                return val;
                            }});
                            if (json !== undefined) {{
                                return json;
                            }}
                        }} catch (jsonErr) {{
                            // Fall through to more generic formatting below
                        }}
                        try {{
                            return Object.prototype.toString.call(value);
                        }} catch (descriptorErr) {{
                            // Fall through to final String conversion
                        }}
                    }}
    
                    try {{
                        return String(value);
                    }} catch (stringErr) {{
                        return '[unrepresentable value]';
                    }}
                }}
    
                var initialLogs = [];
                var originalLog = console.log;
    
                console.log = function() {{
                    var args = Array.prototype.slice.call(arguments);
                    var logMsg = args.map(formatValue).join(' ');
                    initialLogs.push(logMsg);
                    originalLog.apply(console, arguments); // Also keep original console behavior
                }};
    
                var scriptResult;
                var scriptError;
                try {{
                    scriptResult = eval({javascript_code!r});
                }} catch (e) {{
                    scriptError = {{ message: e.toString(), stack: e.stack }};
                }}
    
                console.log = originalLog; // Restore
    
                send({{ // This send is for the initial execution result
                    type: 'execution_receipt',
                    result: scriptError ? undefined : formatValue(scriptResult),
                    error: scriptError,
                    initial_logs: initialLogs
                }});
            }})();
            """
    
            script = session.create_script(wrapped_code)
    
            # This list captures messages from the initial execution of the script (the wrapper)
            initial_execution_results = []
    
            def on_initial_message(message, data):
                # This handler is for the initial execution wrapper's send()
                if (
                    message["type"] == "send"
                    and message["payload"]["type"] == "execution_receipt"
                ):
                    initial_execution_results.append(message["payload"])
                elif message["type"] == "error":  # Script compilation/syntax errors
                    initial_execution_results.append(
                        {"script_error": message["description"], "details": message}
                    )
    
            # This handler is for persistent messages if keep_alive is true
            def on_persistent_message(message, data):
                with lock:
                    _script_messages[session_id].append(
                        {
                            "type": message["type"],
                            "payload": message.get("payload"),
                            "data": data,
                        }
                    )
    
            if keep_alive:
                # For keep_alive, messages go to the global queue _script_messages
                # The script object itself will handle these.
                script.on("message", on_persistent_message)
                # Store the script object if we need to interact with it later (e.g., specific unload)
                # For now, it's attached to the session and will be cleaned up when session is detached or process ends.
                if (
                    session_id not in global_persistent_scripts
                ):  # Requires global_persistent_scripts dict
                    global_persistent_scripts[session_id] = []
                global_persistent_scripts[session_id].append(script)
    
            else:
                # For non-persistent scripts, use the local handler for immediate results
                script.on("message", on_initial_message)
    
            script.load()
    
            # For non-persistent scripts, give a short time for the initial_execution_results
            # For persistent scripts, this sleep is less critical as it's about setting up.
            if not keep_alive:
                time.sleep(0.2)  # Slightly increased for safety
    
            # Process initial results (for both modes, but primarily for non-keep_alive)
            final_result = {}
            if initial_execution_results:
                # Use the first message from the execution_receipt
                receipt = initial_execution_results[0]
                if "script_error" in receipt:
                    final_result = {
                        "status": "error",
                        "error": "Script execution error",
                        "details": receipt["script_error"],
                    }
                elif receipt.get("error"):
                    final_result = {
                        "status": "error",
                        "error": receipt["error"]["message"],
                        "stack": receipt["error"]["stack"],
                        "initial_logs": receipt.get("initial_logs", []),
                    }
                else:
                    final_result = {
                        "status": "success",
                        "result": receipt["result"],
                        "initial_logs": receipt.get("initial_logs", []),
                    }
            elif keep_alive:
                final_result = {
                    "status": "success",
                    "message": "Script loaded persistently. Use get_session_messages to retrieve asynchronous messages.",
                    "initial_logs": [],
                }
            else:  # No messages received, could be an issue or just a silent script
                final_result = {
                    "status": "nodata",  # Or "success" if empty result is fine
                    "message": "Script loaded but sent no initial messages.",
                    "initial_logs": [],
                }
    
            if not keep_alive:
                script.unload()
                final_result["script_unloaded"] = True
            else:
                final_result["script_unloaded"] = False
                final_result["info"] = (
                    "Script is persistent. Remember to manage its lifecycle if necessary."
                )
    
            return final_result
    
        except frida.InvalidOperationError as e:  # E.g. session detached
            return {
                "status": "error",
                "error": f"Frida operation error: {str(e)} (Session may be detached)",
            }
        except Exception as e:
            return {"status": "error", "error": str(e)}
  • Input schema defined using Pydantic Field objects, specifying session_id, javascript_code, and keep_alive parameters with descriptions and defaults.
        session_id: str = Field(
            description="The unique identifier of the active Frida session. This ID is obtained when the session is first created."
        ),
        javascript_code: str = Field(
            description="A string containing the JavaScript code to be executed in the target process's context. The script can use Frida's JavaScript API (e.g., Interceptor, Memory, Module, rpc)."
        ),
        keep_alive: bool = Field(
            default=False,
            description="A boolean flag indicating whether the script should remain loaded in the target process after its initial execution. If False (default), the script is unloaded after initial run. If True, it persists for hooks/RPC and messages are retrieved via get_session_messages. Note: With keep_alive=True, JavaScript code should manage log volume (limits, deduplication) to prevent too many messages.",
        ),
    ) -> Dict[str, Any]:
  • Registers the execute_in_session function as an MCP tool using the FastMCP @mcp.tool() decorator.
    @mcp.tool()
  • Global state variables used by execute_in_session to store sessions, messages, locks, and persistent scripts.
    # Global dictionary to store scripts and their messages
    # This allows us to retrieve messages from scripts after they've been created
    _scripts = {}
    _script_messages = {}
    _message_locks = {}
    global_persistent_scripts = {}  # Added for managing persistent scripts
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key behavioral traits: the tool executes code dynamically in a target process, uses Frida's JavaScript API, and includes details about the 'keep_alive' parameter's persistence behavior and message management. However, it lacks information on error handling, execution timeouts, or security implications, preventing a perfect score.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, with the first sentence stating the core purpose. The second sentence adds necessary context without redundancy. Every sentence earns its place by clarifying prerequisites and tool relationships, making it efficient and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (dynamic code execution in sessions), no annotations, and an output schema (which handles return values), the description is mostly complete. It covers purpose, prerequisites, and key behavioral aspects but could improve by addressing error scenarios or execution limits, leaving minor gaps for a mutation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, providing comprehensive parameter documentation. The description adds minimal value beyond the schema, only briefly mentioning the 'keep_alive' parameter's effect on script persistence. No additional syntax, format, or usage examples are provided, so it meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Execute JavaScript code') and resource ('within an existing interactive Frida session'), distinguishing it from sibling tools like 'create_interactive_session' (which creates sessions) and 'get_session_messages' (which retrieves messages). The verb+resource combination is precise and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool: 'within an existing interactive Frida session' and references the prerequisite tool 'create_interactive_session'. It also implies when not to use it (e.g., for session creation or message retrieval), providing clear context for selection among siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rmorgans/frida-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server