# Build your own agent loop

Three small, complete phone agents on your own model, from the Examples folder, and the helper that gives them Silan's tools.

The Silan repository's `Examples/agent-loops` folder holds three complete phone agents: a model looks at the device through Silan's tools and acts until the task is done. All three start `silan mcp`, the same MCP server the one-click apps start, hand the model Silan's tools exactly as MCP lists them, and use Silan's public skills as the system prompt. Status: **supported**, proven on a Mac with no phone attached. The phone run is still owed.

<figure class="shot"><video src="/media/agent-loop-check-phone.mp4" poster="/media/agent-loop-check-phone-poster.jpg" width="414" height="896" controls muted playsinline preload="none" aria-label="What an agent loop did on an iPhone 11 through the MCP tools: open the app, tap plus and minus on the water row, read the count back (recorded 44 s)."></video><figcaption>What an agent loop did on an iPhone 11 through the MCP tools: open the app, tap plus and minus on the water row, read the count back (recorded 44 s).</figcaption></figure>

## The pieces

| File | Model | Needs |
|---|---|---|
| `claude_loop.py` | Claude, through the Messages API | `pip install anthropic`, `ANTHROPIC_API_KEY` |
| `openai_loop.py` | Any OpenAI Responses API model with function calling and image input | `pip install openai`, `OPENAI_API_KEY`, `--model` or `OPENAI_MODEL` |
| `open_model_loop.py` | Any OpenAI-compatible chat server on your Mac or network, such as Ollama, LM Studio, llama.cpp or vLLM | Nothing extra. `--model`, and `--base-url` or `OPENAI_BASE_URL` |
| `silan_mcp.py` | The helper the three loops share | Standard library, Python 3.9 or later |

### What silan_mcp.py does

- `find_silan()` looks for the command at `SILAN_CLI`, on your PATH, at `~/.local/bin/silan`, then inside `/Applications/Silan.app` and `~/Applications/Silan.app`. With none it says how to install Silan.
- `SilanTools(profile="core")` starts `silan mcp --profile core` (or `testing`, or `full`) and speaks MCP over stdio. `tools()` returns every tool with its name, description and JSON schema, paging through the list. `call(name, arguments)` runs a tool. A tool that fails, say a missing element, comes back with `isError` true rather than an exception. A call may run up to 900 seconds, enough for a long `wait_for`.
- `instructions()` builds the system prompt from the `silan` and `silan-safety` skills in `Skills/`, with their front matter stripped, so the loop follows the same rules as a connected AI app: look before you act, prefer refs to pixels, use `wait_for` instead of sleeping, never type a password, stop before anything you cannot undo.

### How each loop differs

- **Claude.** Adaptive thinking is on, and the skills and tool list are cached across turns. After the newest three screenshots, older ones are replaced by a short note to save tokens, while the element list in each observation stays. `--no-fallbacks` turns off the server-side refusal fallbacks.
- **OpenAI.** The conversation lives on the API side through `previous_response_id`. Each turn sends only the new tool results and the newest screenshot as a user image.
- **Open models.** Standard library only. The default address is Ollama's, `http://127.0.0.1:11434/v1`. Without `--vision` the model works from the element lists alone, which is often enough. Only the `silan` skill is the system prompt, and the loop stops after 40 turns.

Every loop takes `--device` (from `silan devices`), `--profile core|testing|full` and `--max-turns`. Without `--device` the model calls `list_devices` and picks one.

## Setup

1. Install Silan and set up a device, or let Silan make a simulator ([Install and quickstart](/quickstart/)).
2. Get a copy of the Silan repository and open `Examples/agent-loops`. Keep the `Skills` folder where it is, two levels up, so the loops find the skills.
3. Pick a loop and install what it needs, from the table above. Set its key in the environment.
4. Find your device id: `/Applications/Silan.app/Contents/Helpers/silan devices`.

The first tool call brings Silan's Allow sheet, because Python has no Developer ID signature: it names the Python file, says Silan cannot check who made it, and offers **Allow this time** only. Press it. The grant ends when the loop exits.

## Try it

```sh
cd Examples/agent-loops
python3 claude_loop.py "Open Settings and turn on Dark Mode" --device ios:00008120-001A2B3C4D5E6F70
python3 openai_loop.py "Open the Clock app and start the stopwatch" --model <model> --device ios:00008120-001A2B3C4D5E6F70
python3 open_model_loop.py "Open the Clock app" --model <local model> --device android:emulator-5554
```

Each loop prints one line per step: `-> tap {...}` for a call, `<- ...` for the start of the answer, and the final answer at the end. A step that needs your yes answers `APPROVAL_REQUIRED` and the model is told so.

The offline tests check how the loops find `silan` and read the skills, with no key and no network:

```sh
python3 -B tests/test_loops.py
```

With `SILAN_CLI`, `SILAN_URL`, `SILAN_API_KEY` and `SILAN_TEST_DEVICE` set, the last test also drives the real `silan mcp` against your engine and a device.

## Limits

- Proven with no phone. A run of these loops on a real device is not yet proved.
- The loops are examples, not a library. The Python library proper is [coming soon](/integrations/python/).
- Every call goes through Silan's safety rules. The agent asks before it pays, sends, posts or deletes, and passwords never reach the model: the loops use `fill_credential` and `request_person_login` like any other tool.
- Python runs with **Allow this time** only, so you answer the sheet once per run. To skip the question, give `SilanTools` a key: run with `SILAN_API_KEY` set and `silan mcp` uses the HTTP listener.

:::note[Proof]
Proved on this Mac with no phone, by a Silan worker: the three loops run a phone agent through `silan mcp`. Source: the use-case facts, "Bring your own model or agent loop", and `Examples/README.md`.
:::