Skip to main content

Introduction

The mitmproxy addon API allows you to extend mitmproxy’s functionality by creating custom addons. Addons can intercept and modify traffic, implement custom logic, and integrate with external systems.

Creating an Addon

An addon is a Python class or module with methods that correspond to event hooks. When mitmproxy processes traffic, it triggers these hooks, allowing your addon to respond to different events.

Basic Structure

class MyAddon:
    def __init__(self):
        self.num = 0

    def request(self, flow):
        """Called when a request is received."""
        self.num += 1
        print(f"Request #{self.num}: {flow.request.url}")

    def response(self, flow):
        """Called when a response is received."""
        flow.response.headers["X-Custom-Header"] = "mitmproxy"

# Register the addon
addons = [MyAddon()]

The Addon Lifecycle

Addons go through several lifecycle stages:
1

Load

The load(loader) hook is called when the addon is first loaded. Use this to register options and commands.
def load(self, loader):
    loader.add_option(
        name="myoption",
        typespec=bool,
        default=False,
        help="Enable custom behavior"
    )
2

Configure

The configure(updated) hook is called when configuration changes. The updated parameter contains the names of changed options.
def configure(self, updated):
    if "myoption" in updated:
        print(f"myoption changed to: {ctx.options.myoption}")
3

Running

The running() hook is called when the proxy is fully started and ready to handle traffic.
def running(self):
    print("Proxy is ready!")
4

Done

The done() hook is called when the addon is being shut down.
def done(self):
    print("Addon shutting down")

Key Concepts

Flows

A flow represents a single transaction between a client and server. Different protocols have different flow types:
  • HTTPFlow: HTTP/HTTPS traffic
  • TCPFlow: Raw TCP connections
  • UDPFlow: UDP datagrams
  • DNSFlow: DNS queries and responses

The Context Object

The global context object mitmproxy.ctx provides access to the master and various utilities:
import mitmproxy.ctx as ctx

def request(self, flow):
    # Access options
    if ctx.options.myoption:
        flow.request.headers["X-Option"] = "enabled"
    
    # Access the master
    ctx.master.commands.execute("view.flows.reload")

The Loader Object

The loader is passed to the load() hook and provides methods for registering options and commands:
loader.add_option
function
Register a new configuration option.Parameters:
  • name (str): Option name
  • typespec (type): Python type (bool, str, int, etc.)
  • default (Any): Default value
  • help (str): Help text
  • choices (Sequence[str], optional): Valid choices for the option
loader.add_command
function
Register a new command programmatically.Parameters:
  • path (str): Command path (e.g., “myaddon.mycommand”)
  • func (Callable): Function to execute

The AddonManager

The AddonManager class (defined in mitmproxy.addonmanager) manages all loaded addons and handles event dispatching.

Key Methods

register(addon)
addon
Register a new addon and call its load event. Also registers any sub-addons.
add(*addons)
None
Add addons to the end of the chain and run their load event.
remove(addon)
None
Remove an addon and all its sub-addons.
get(name)
addon | None
Retrieve an addon by name. Names are lowercase class names by default.
trigger(event)
None
Synchronously trigger an event across all addons.
trigger_event(event)
awaitable
Asynchronously trigger an event across all addons. Supports both sync and async hooks.

Async Support

Addons support both synchronous and asynchronous hook functions:
import asyncio

class AsyncAddon:
    async def request(self, flow):
        """Async hooks are automatically awaited."""
        await asyncio.sleep(0.1)
        flow.request.headers["X-Async"] = "true"
    
    def response(self, flow):
        """Sync hooks work too."""
        flow.response.headers["X-Sync"] = "true"
Async handlers cannot be called from synchronous contexts. If you use trigger() instead of trigger_event(), async hooks will raise an error.

Error Handling

Errors in addon hooks are caught and logged automatically. Your addon won’t crash mitmproxy:
def request(self, flow):
    try:
        # Your code here
        risky_operation()
    except Exception as e:
        # Errors are logged but won't stop mitmproxy
        raise

Logging

Use Python’s standard logging module:
import logging

logger = logging.getLogger(__name__)

class MyAddon:
    def request(self, flow):
        logger.info(f"Processing: {flow.request.url}")
        logger.debug(f"Headers: {flow.request.headers}")
        logger.error("Something went wrong!")
The old ctx.log API is deprecated. Always use Python’s logging module.

Sub-Addons

Addons can contain other addons:
class SubAddon:
    def request(self, flow):
        print("Sub-addon request")

class ParentAddon:
    def __init__(self):
        self.addons = [SubAddon()]
    
    def request(self, flow):
        print("Parent addon request")

# Both hooks will be called
addons = [ParentAddon()]

Next Steps

Event Hooks

Learn about all available event hooks

Commands

Create custom commands for mitmproxy

Examples

See practical addon examples

Protocol Reference

Deep dive into flow types and protocol details

API Changes

Some events have been renamed or removed in recent versions:
  • clientconnectclient_connected (mitmproxy 7+)
  • clientdisconnectclient_disconnected (mitmproxy 7+)
  • serverconnectserver_connect and server_connected (mitmproxy 7+)
  • serverdisconnectserver_disconnected (mitmproxy 7+)
  • add_log → Use Python’s logging module (deprecated in mitmproxy 9+)
For more details, see the API Changelog.

Build docs developers (and LLMs) love