> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mobilerun.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Complete MobileAgent configuration guide - all parameters, minimal examples

## Quick Start

```python theme={null}
from mobilerun import MobileAgent, MobileConfig

# Minimal (uses defaults)
agent = MobileAgent(goal="Open settings")
result = await agent.run()

# Load from config.yaml
config = MobileConfig.from_yaml("config.yaml")
agent = MobileAgent(goal="Open settings", config=config)
result = await agent.run()
```

***

## MobileAgent Parameters

### Required

```python theme={null}
MobileAgent(
    goal="Your task",  # REQUIRED: Task description
)
```

### Optional Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `config` | `MobileConfig \| None` | `None` | Full config object (loads LLMs from profiles if `llms` not provided) |
| `llms` | `dict[str, LLM] \| LLM \| None` | `None` | LLM(s) - dict for per-agent, single LLM for all, or None to load from config |
| `custom_tools` | `dict` | `None` | Custom tool definitions |
| `credentials` | `Union[dict, CredentialManager, None]` | `None` | Dict of credential secrets or CredentialManager instance |
| `variables` | `dict \| None` | `None` | Custom variables accessible during execution |
| `output_model` | `Type[BaseModel] \| None` | `None` | Pydantic model for structured output extraction |
| `prompts` | `dict[str, str] \| None` | `None` | Custom Jinja2 prompt templates (NOT file paths) |
| `driver` | `DeviceDriver \| None` | `None` | Pre-configured device driver instance (AndroidDriver or IOSPortalHttpDriver) |
| `state_provider` | `StateProvider \| None` | `None` | Pre-configured state provider instance |
| `timeout` | `int` | `1000` | Workflow timeout in seconds |

***

## Configuration Classes

### AgentConfig

```python theme={null}
from mobilerun import AgentConfig, FastAgentConfig, ManagerConfig, ExecutorConfig, AppCardConfig

AgentConfig(
    # Core settings
    name="mobilerun",                 # Agent name (use "mobilerun" for default agent)
    max_steps=15,                    # Max execution steps
    reasoning=False,                 # Enable Manager/Executor workflow
    streaming=True,                  # Stream LLM responses
    vision_only=False,               # Use screenshots without an accessibility tree
    after_sleep_action=1.0,          # Wait after actions (seconds)
    wait_for_stable_ui=0.3,          # Wait for UI to stabilize (seconds)
    use_normalized_coordinates=False, # Use normalized coordinates instead of absolute pixels
    model_screenshot_max_side=None,  # Optional cap (px, long edge) on the model-facing screenshot

    # Sub-configs
    fast_agent=FastAgentConfig(...),
    manager=ManagerConfig(...),
    executor=ExecutorConfig(...),
    app_cards=AppCardConfig(...),
)
```

**FastAgentConfig**

```python theme={null}
FastAgentConfig(
    vision=False,                                              # Enable screenshots
    parallel_tools=True,                                       # Encourage multiple tool calls in a single response
    system_prompt="config/prompts/fast_agent/system.jinja2",   # Path to system prompt template
    user_prompt="config/prompts/fast_agent/user.jinja2",       # Path to user prompt template
)
```

**ManagerConfig**

```python theme={null}
ManagerConfig(
    vision=False,       # Enable screenshots
    system_prompt=None, # Use the built-in prompt for the selected mode
    stateless=False,    # Keep Manager conversation history
)
```

Leave `system_prompt` unset to use the built-in prompt for the selected Manager mode. To use a custom prompt, provide a file path or pass `manager_system` to `MobileAgent`. See [Prompt Templates](/framework/concepts/prompts).

**ExecutorConfig**

```python theme={null}
ExecutorConfig(
    vision=False,                                            # Enable screenshots
    system_prompt="config/prompts/executor/system.jinja2",   # Path to system prompt template
)
```

**AppCardConfig**

```python theme={null}
AppCardConfig(
    enabled=True,                    # Enable app-specific instructions
    mode="local",                    # "local" | "server" | "composite"
    app_cards_dir="config/app_cards",  # Directory for app card files
    server_url=None,                 # Server URL (for server/composite modes)
    server_timeout=2.0,              # Server request timeout (seconds)
    server_max_retries=2,            # Server retry attempts
)
```

***

### DeviceConfig

```python theme={null}
from mobilerun import DeviceConfig

DeviceConfig(
    serial=None,            # Android ADB serial/IP or iOS/Visual Remote server URL
    control_backend=None,   # Use the platform default or "visual-remote"
    device_id="auto",       # Device to control when a server offers more than one
    use_tcp=False,          # Prefer Android Portal HTTP over ContentProvider
    platform="android",     # "android" or "ios"
    portal_mode="auto",     # "auto" | "required" | "disabled"
    auto_setup=True,        # Auto-install/fix Android Portal before each run
    auth_token=None,        # Bearer token for a mobilerun-ios --local server
                            # started with --local-token. Loopback servers need
                            # no token. MOBILERUN_DEVICE_TOKEN env overrides this.
)
```

`portal_mode` controls Android Portal use:

* `auto` (default) uses Portal when available and uses ADB/UIAutomator if Portal cannot perform an action.
* `required` requires Portal and its keyboard. If Portal text input, app listing, screenshots, or UI reading fails, the operation fails instead of switching to ADB.
* `disabled` uses ADB/UIAutomator only.

`auto_setup=True` installs or repairs Portal before an Android run. It is skipped when `portal_mode="disabled"`.

***

### LoggingConfig

```python theme={null}
from mobilerun import LoggingConfig

LoggingConfig(
    debug=False,                   # Enable debug logs
    save_trajectory="none",        # "none" | "step" | "action"
    trajectory_path="trajectories", # Directory for trajectory files
    rich_text=False,               # Rich text formatting in logs
    trajectory_gifs=True,          # Save trajectory as animated GIFs
)
```

***

### TracingConfig

```python theme={null}
from mobilerun import TracingConfig

TracingConfig(
    enabled=False,                      # Enable tracing
    provider="phoenix",                 # "phoenix" or "langfuse"
    langfuse_screenshots=False,         # Upload screenshots to Langfuse (if enabled)

    # Langfuse settings (only used if provider="langfuse")
    langfuse_secret_key="",             # LANGFUSE_SECRET_KEY env var
    langfuse_public_key="",             # LANGFUSE_PUBLIC_KEY env var
    langfuse_host="",                   # Endpoint override; otherwise LANGFUSE_BASE_URL
    langfuse_user_id="anonymous",       # User ID for Langfuse tracing
    langfuse_session_id="",             # Empty = auto-generate UUID; custom value to persist across runs
)
```

***

### TelemetryConfig

```python theme={null}
from mobilerun import TelemetryConfig

TelemetryConfig(
    enabled=True,  # Enable anonymous telemetry
)
```

***

### ToolsConfig

```python theme={null}
from mobilerun import ToolsConfig

ToolsConfig(
    disabled_tools=None,  # Use Mobilerun defaults; pass a list to choose disabled tools
    stealth=False,        # Enable stealth mode (human-like timing + randomized coordinates)
)
```

**Example - Disable specific tools:**

```yaml theme={null}
tools:
  disabled_tools:
    - long_press
    - wait
  stealth: false
```

By default, Mobilerun disables `click_at`, `click_area`, and `long_press_at`. With vision enabled, `click_at` becomes available unless normalized coordinates are used. Pass a list to choose which tools to disable, or `[]` to disable none.

Screenshot-only modes require all three coordinate tools and reject configurations that disable them. See [Vision Mode](/framework/features/vision) for details.

***

### CredentialsConfig

```python theme={null}
from mobilerun import CredentialsConfig

CredentialsConfig(
    enabled=True,                        # Enable credential manager
    file_path="config/credentials.yaml",  # Path to credentials file
)
```

***

## LLM Configuration

### Single LLM (All Agents)

```python theme={null}
from mobilerun import MobileAgent, load_llm

llm = load_llm("GoogleGenAI", model="gemini-3.8-flash", temperature=0.2)
agent = MobileAgent(goal="...", llms=llm)
```

Create Google models with `mobilerun.load_llm()`, as shown above.

For xAI Grok, set `XAI_API_KEY` and use:

```python theme={null}
from mobilerun import load_llm

llm = load_llm("XAI", model="grok-4.6")
```

### Per-Agent LLMs

```python theme={null}
from mobilerun import MobileAgent, load_llm
from llama_index.llms.openai import OpenAI

agent = MobileAgent(
    goal="...",
    llms={
        "manager": OpenAI(model="gpt-4o"),                    # Planning
        "executor": load_llm("GoogleGenAI", model="gemini-3.8-flash"),  # Action selection
        "fast_agent": load_llm("GoogleGenAI", model="gemini-3.8-flash"),  # Direct execution
        "app_opener": OpenAI(model="gpt-4o-mini"),            # App launching
        "structured_output": load_llm("GoogleGenAI", model="gemini-3.8-flash"),  # Output extraction
    }
)
```

**LLM Keys:**

* `manager` - Planning (reasoning mode only)
* `executor` - Action selection (reasoning mode only)
* `fast_agent` - Direct execution
* `app_opener` - App launching helper
* `structured_output` - Final output extraction

***

## Custom Tools

```python theme={null}
def my_tool(param: str, **kwargs) -> str:
    """Tool description."""
    return f"Result: {param}"

agent = MobileAgent(
    goal="...",
    custom_tools={
        "my_tool": {
            "parameters": {
                "param": {"type": "string", "required": True},
            },
            "description": "Tool description with usage example",
            "function": my_tool
        }
    }
)
```

***

## Credentials

### Dict Format (Recommended)

```python theme={null}
agent = MobileAgent(
    goal="...",
    credentials={
        "USERNAME": "alice@example.com",
        "PASSWORD": "secret123"
    }
)
# Agent can call type_secret("USERNAME", index) and type_secret("PASSWORD", index)
```

### Config Format

```python theme={null}
from mobilerun import MobileConfig, CredentialsConfig

config = MobileConfig(
    credentials=CredentialsConfig(
        enabled=True,
        file_path="config/credentials.yaml"
    )
)

agent = MobileAgent(
    goal="...",
    config=config
)
```

***

## Custom Variables

```python theme={null}
agent = MobileAgent(
    goal="...",
    variables={
        "api_url": "https://api.example.com",
        "user_id": "12345",
        "custom_data": {"key": "value"}
    }
)
# Access in shared_state.custom_variables
```

***

## Structured Output

```python theme={null}
from pydantic import BaseModel

class FlightInfo(BaseModel):
    airline: str
    flight_number: str
    confirmation_code: str

agent = MobileAgent(
    goal="Book a flight and extract details",
    output_model=FlightInfo
)
result = await agent.run()
print(result.structured_output.airline)  # Typed output
```

***

## Custom Prompts

```python theme={null}
from mobilerun import AgentConfig, MobileAgent, MobileConfig

custom_manager_prompt = """
You are an expert mobile agent.
Goal: {{ instruction }}
Be precise and efficient.

Return one result:
- <plan>...</plan> while work remains.
- <request_accomplished success="true">...</request_accomplished> when complete.
- <request_accomplished success="false">...</request_accomplished> when blocked.
"""

agent = MobileAgent(
    goal="...",
    config=MobileConfig(agent=AgentConfig(reasoning=True)),
    prompts={
        "manager_system": custom_manager_prompt,
    }
)
```

Each prompt key receives a different context. See [Prompt Templates](/framework/concepts/prompts#context-variables) for the exact variables available to Manager, Executor, and FastAgent templates.

***

## Complete Example

```python theme={null}
from mobilerun import (
    MobileAgent, MobileConfig, load_llm,
    AgentConfig, FastAgentConfig, DeviceConfig, LoggingConfig, TracingConfig
)
from llama_index.llms.openai import OpenAI
from pydantic import BaseModel

# Structured output
class Output(BaseModel):
    name: str
    value: int

# Custom tool
def send_email(to: str, subject: str, **kwargs) -> str:
    """Send email."""
    return f"Sent to {to}"

# Build configuration
config = MobileConfig(
    agent=AgentConfig(
        max_steps=30,
        reasoning=True,
        after_sleep_action=1.5,
        fast_agent=FastAgentConfig(vision=True)
    ),
    device=DeviceConfig(
        serial="emulator-5554",
        platform="android",
        use_tcp=False
    ),
    logging=LoggingConfig(
        debug=True,
        save_trajectory="step",
        trajectory_gifs=True
    ),
    tracing=TracingConfig(enabled=True),
)

agent = MobileAgent(
    goal="Complex task",
    config=config,

    # LLMs
    llms={
        "manager": OpenAI(model="gpt-4o"),                    # Planning
        "executor": load_llm("GoogleGenAI", model="gemini-3.8-flash"),  # Action selection
        "fast_agent": load_llm("GoogleGenAI", model="gemini-3.8-flash"),  # Direct execution
        "app_opener": OpenAI(model="gpt-4o-mini"),            # App launching
        "structured_output": load_llm("GoogleGenAI", model="gemini-3.8-flash"),  # Output extraction
    },

    # Custom tools
    custom_tools={
        "send_email": {
            "parameters": {
                "to": {"type": "string", "required": True},
                "subject": {"type": "string", "required": True},
            },
            "description": "Send email to recipient with subject",
            "function": send_email
        }
    },

    # Credentials
    credentials={"USERNAME": "alice", "PASSWORD": "secret"},

    # Variables
    variables={"api_url": "https://api.example.com"},

    # Structured output
    output_model=Output,

    # Timeout
    timeout=600
)

result = await agent.run()
```

***

## YAML Config (CLI)

For CLI usage, create `config.yaml`:

```yaml theme={null}
agent:
  name: mobilerun
  max_steps: 15
  reasoning: false
  streaming: true
  vision_only: false
  after_sleep_action: 1.0
  wait_for_stable_ui: 0.3
  use_normalized_coordinates: false
  # model_screenshot_max_side: 1280  # Optional cap for local vision models

  fast_agent:
    vision: false
    parallel_tools: true
    system_prompt: config/prompts/fast_agent/system.jinja2
    user_prompt: config/prompts/fast_agent/user.jinja2

  manager:
    vision: false
    system_prompt: null  # Use the built-in prompt for the selected mode
    stateless: false

  executor:
    vision: false
    system_prompt: config/prompts/executor/system.jinja2

  app_cards:
    enabled: true
    mode: local
    app_cards_dir: config/app_cards
    server_url: null
    server_timeout: 2.0
    server_max_retries: 2

llm_profiles:
  manager:
    provider: GoogleGenAI
    model: gemini-3.8-flash
    temperature: 0.2
    kwargs:
      max_tokens: 8192

  executor:
    provider: GoogleGenAI
    model: gemini-3.8-flash
    temperature: 0.1
    kwargs:
      max_tokens: 4096

  fast_agent:  # Direct execution
    provider: GoogleGenAI
    model: gemini-3.8-flash
    temperature: 0.2
    kwargs:
      max_tokens: 8192

  app_opener:
    provider: OpenAI
    model: gpt-4o-mini
    temperature: 0.0

  structured_output:
    provider: GoogleGenAI
    model: gemini-3.8-flash
    temperature: 0.0

device:
  serial: null
  control_backend: null
  device_id: auto
  use_tcp: false
  platform: android
  portal_mode: auto  # "auto" | "required" | "disabled"
  auto_setup: true  # Auto-install/fix Portal APK before each run
  auth_token: null  # Token for mobilerun-ios --local (iOS only).
                    # MOBILERUN_DEVICE_TOKEN env var overrides this.

tools:
  disabled_tools: null  # Use Mobilerun defaults
  stealth: false

telemetry:
  enabled: true

tracing:
  enabled: false
  provider: phoenix  # "phoenix" or "langfuse"
  langfuse_screenshots: false  # Upload screenshots to Langfuse
  langfuse_secret_key: ""
  langfuse_public_key: ""
  langfuse_host: ""
  langfuse_user_id: anonymous
  langfuse_session_id: ""

logging:
  debug: false
  save_trajectory: none
  trajectory_path: trajectories
  rich_text: false
  trajectory_gifs: true

credentials:
  enabled: false
  file_path: config/credentials.yaml
```

### Ollama profiles

Ollama profiles accept the same portable `kwargs` as other providers — `max_tokens` and `context_window` work consistently across providers, and Mobilerun translates them to Ollama's native parameters at runtime.

```yaml theme={null}
llm_profiles:
  manager:
    provider: Ollama
    model: qwen3:8b
    base_url: http://localhost:11434
    kwargs:
      max_tokens: 2048       # Caps output length (mapped to Ollama's num_predict)
      context_window: 32768  # Controls KV cache size (mapped to num_ctx)
```

| Key | Default for Ollama | Notes |
| - | - | - |
| `max_tokens` | unset (no cap) | Caps generated tokens. If you set `additional_kwargs.num_predict`, that explicit value wins. |
| `context_window` | `32768` | KV cache size. Set `-1` to use the model's maximum (preallocates the full KV cache and can spill to CPU on large-context models). An explicit `additional_kwargs.num_ctx` is mirrored into `context_window` automatically. |

<Tip>
  The 32K default keeps Ollama on the GPU for most models. If you need the model's full context, set `context_window: -1` explicitly.
</Tip>

***

## CLI Overrides

```bash theme={null}
# Override agent settings
mobilerun run "Task" --steps 30 --reasoning --vision

# Override device
mobilerun run "Task" --device emulator-5554 --tcp

# Override LLM (applies to ALL agents)
mobilerun run "Task" --provider GoogleGenAI --model gemini-3.8-flash

# Override logging
mobilerun run "Task" --debug --save-trajectory action --tracing

# Custom config file
mobilerun run "Task" --config /path/to/config.yaml
```

**Run flags:**

* `--config PATH` - Custom config file
* `--device SERIAL` - Device serial/IP
* `--agent NAME` - Run an installed external Android agent
* `--provider PROVIDER` - LLM provider override
* `--model MODEL` - LLM model override; pass it together with `--provider`
* `--temperature FLOAT` - LLM temperature
* `--steps INT` - Max steps
* `--base_url URL` - API base URL (for Ollama/OpenRouter/MiniMax)
* `--api_base URL` - API base URL (for OpenAI-like)
* `--vision/--no-vision` - Enable/disable vision for all agents
* `--vision-only/--no-vision-only` - Use screenshots without an accessibility tree
* `--reasoning/--no-reasoning` - Enable/disable reasoning mode
* `--stream/--no-stream` - Enable/disable streamed console output
* `--tracing/--no-tracing` - Enable/disable tracing
* `--debug/--no-debug` - Enable/disable debug logs
* `--tcp/--no-tcp` - Enable/disable TCP communication
* `--control-backend visual-remote` - Connect to a Visual Remote server
* `--device-id ID` - Select one device when a server offers more than one
* `--save-trajectory none|step|action` - Trajectory saving level
* `--ios` - Run on iOS device

See the [CLI guide](/framework/guides/cli) for provider examples and option defaults.

***

## Environment Variables

Set API keys via environment variables:

```bash theme={null}
export GOOGLE_API_KEY=your-key
export OPENAI_API_KEY=your-key
export ANTHROPIC_API_KEY=your-key
export XAI_API_KEY=your-key
export DEEPSEEK_API_KEY=your-key
export MINIMAX_API_KEY=your-key
export ZAI_API_KEY=your-key
export MOBILERUN_DEVICE_TOKEN=your-token  # Optional authenticated iOS local server
export MOBILERUN_CONFIG=/path/to/config.yaml  # Custom config path
```

### MiniMax

MiniMax defaults to `MiniMax-M3`. Use `https://api.minimax.io/v1` for global accounts or `https://api.minimaxi.com/v1` for Mainland China accounts:

```bash theme={null}
mobilerun configure \
  --provider minimax \
  --model MiniMax-M3 \
  --base-url https://api.minimax.io/v1
```

If `MiniMax-M3` is unavailable in Mainland China, use `MiniMax-M2.7`. To use `MINIMAX_API_KEY` instead of a saved key, select the environment-key option in the wizard.

### Manual OAuth login

Set `MOBILERUN_OAUTH_MANUAL=true` to select manual authentication for Anthropic and Gemini, or device-code authentication for OpenAI, instead of a local browser callback server. Use it on SSH sessions or other machines where a local browser callback is not possible. The accepted enabled values are `1`, `true`, and `yes` (case-insensitive).

```bash theme={null}
export MOBILERUN_OAUTH_MANUAL=true
```

The legacy `DROIDRUN_OAUTH_MANUAL` variable still works as a fallback. When both are set, a nonempty `MOBILERUN_OAUTH_MANUAL` value takes precedence.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.