MCP server
Tools and resources that `optics mcp` gives your AI agent.
optics mcp runs a Model Context Protocol (MCP) server, a standard way for AI agents such as Claude, Cursor or VS Code to call outside tools. Your agent can start a session on a device or browser, look at the screen, and run Optics keywords one at a time.
For a guided walkthrough, see Build tests with an AI agent.
Install#
The server needs the mcp extra. optics setup can't install it, and the all bundle doesn't include it.
$ pip install "optics-framework[mcp]"You also need the extras for your driver, for example appium or playwright. See Engines and extras.
Start the server#
optics mcp [--transport stdio|http] [--host HOST] [--port PORT]| Flag | Default | What it does |
|---|---|---|
--transport | stdio | stdio: your agent starts the server and talks to it directly. http: the server listens at http://<host>:<port>/mcp. |
--host | 127.0.0.1 | The address to listen on. http only. |
--port | 8090 | The port to listen on. http only. |
We recommend stdio for an agent on your own machine. Use http when the server runs in a container or on another machine.
Connect your agent#
Most agents read a JSON file that lists MCP servers. For Claude Desktop it's claude_desktop_config.json; for Claude Code it's .mcp.json in your project. Other agents use the same shape.
{
"mcpServers": {
"optics": {
"command": "optics",
"args": ["mcp"]
}
}
}If your agent can't find optics, use the full path to it, for example /Users/ana/.optics/bin/optics.
For the http transport, point the agent at the URL instead:
{
"mcpServers": {
"optics": {
"url": "http://127.0.0.1:8090/mcp"
}
}
}When the server runs in Docker and Appium runs on your machine, pass url: "http://host.docker.internal:4723" to start_session.
Session tools#
start_session#
Starts a session and launches the app. Returns {"session_id", "driver_id"}. Keep the session_id: every other tool needs it.
| Parameter | Default | What it does |
|---|---|---|
driver | appium | appium, selenium, playwright or ble. |
url | Appium: http://127.0.0.1:4723 | The driver's server URL. |
capabilities | None | The driver's capabilities, as in config.yaml. |
elements_sources | The driver's three sources | For example ["appium_find_element", "appium_page_source", "appium_screenshot"]. Passing [] makes the session fail to start. |
text_detection | None | OCR engines, for example ["easyocr"]. |
image_detection | None | Image engines, for example ["templatematch"]. |
project_path | None | A project folder. Optics loads its image templates. It doesn't load elements or test cases. |
ai_self_heal | OPTICS_AI_SELF_HEAL, else off | Turns on AI self-heal. |
llm_provider | OPTICS_LLM_PROVIDER, else gemini | The LLM provider for self-heal. |
llm_model | OPTICS_LLM_MODEL, else gemini-2.5-flash | The model for self-heal. |
strict_element_match | Off | Accepts only exact text matches. |
The first session against a remote device farm can take 30 to 60 seconds to start.
terminate_session#
Closes the app and ends the session. Takes session_id. Returns {"session_id", "status": "terminated"}.
screenshot#
Returns the current screen as a PNG image. Takes session_id.
Keyword tools#
Every app, action and verification keyword is a tool named after the keyword in snake_case. Each takes session_id plus the keyword's parameters. All parameter values are strings. Leave out a parameter to use its default.
aoi_* stands for aoi_x, aoi_y, aoi_width and aoi_height, an area of the screen in percent (0–100). See Area of interest.
| Tool | Parameters (defaults) |
|---|---|
launch_app | app_identifier, app_activity, event_name |
launch_other_app | app_name, event_name |
close_and_terminate_app | none |
force_terminate_app | app_name, event_name |
get_app_version | app_package |
get_driver_session_id | none |
start_appium_session | event_name |
initialise_setup | none |
| Tool | Parameters (defaults) |
|---|---|
press_element | element, repeat (1), offset_x (0), offset_y (0), index (0), aoi_*, event_name |
detect_and_press | element, timeout (30), event_name |
select_dropdown_option | element, option, timeout (30), event_name |
enter_text | element, text, aoi_*, event_name, index (0) |
enter_text_direct | text, event_name |
enter_text_using_keyboard | text_input, event_name |
enter_number | element, number, aoi_*, event_name, index (0) |
clear_element_text | element, aoi_*, event_name, index (0) |
get_text | element, index (0), aoi_* |
press_keycode | keycode, event_name |
swipe | coor_x, coor_y, direction (right), swipe_length (50), event_name |
swipe_by_percentage | percent_x, percent_y, direction (right), swipe_length (50), event_name |
swipe_from_element | element, direction, swipe_length, aoi_*, event_name, index (0) |
swipe_until_element_appears | element, direction, timeout, event_name |
scroll | direction, event_name |
scroll_from_element | element, direction, scroll_length, aoi_*, event_name, index (0) |
scroll_until_element_appears | element, direction, timeout, event_name |
sleep | duration |
execute_script | script_or_json, event_name |
press_checkbox, press_radio_button, swipe_seekbar_to_right_android | Deprecated. Use press_element and swipe_from_element. |
| Tool | Parameters (defaults) |
|---|---|
validate_element | element, timeout (10), rule (all), event_name |
is_element | element, element_state, timeout, event_name |
assert_presence | elements, timeout_str (30), rule (any), event_name, fail (True) |
assert_visibility | elements, timeout_str (30), rule (any), event_name, fail (True) |
assert_equality | output, expression, event_name |
validate_screen | elements, timeout (30), rule (any), event_name |
get_interactive_elements | filter_config, compact (False) |
Separate several elements with |, for example elements: "text=Trail Runner 2|text=Pace Knit". Each tool's description in your agent comes from the keyword's own documentation. See Keywords for what each parameter does.
A tool returns the keyword's result. When self-heal recovers a step, it returns {"result", "healed": true, "heal_summary", "suggested_steps"} instead, so your agent can write the extra steps into the test.
Hidden coordinate taps#
press_by_coordinates and press_by_percentage are hidden by default, because agents that guess coordinates write fragile tests. To expose them, set OPTICS_MCP_ALLOW_COORDINATE_TAPS=1 before you start the server.
| Tool | Parameters (defaults) |
|---|---|
press_by_coordinates | coor_x, coor_y, repeat (1), event_name |
press_by_percentage | percent_x, percent_y, repeat (1), event_name |
Resources#
Resources are read-only data your agent can fetch by URI.
| URI | Type | What it returns |
|---|---|---|
optics://keywords | JSON | Every keyword, including flow control, with its description and parameters. |
optics://project-format | Markdown | How to write a CSV project: the files, columns, ${name} variables and flow-control keywords. |
optics://session/{session_id}/screenshot | PNG | The current screen. |
optics://session/{session_id}/source | JSON | {"page_source", "timestamp"}: the UI tree or HTML. |
optics://session/{session_id}/elements | JSON | A compact list of interactive elements on screen. Appium and Playwright only. |
optics://session/{session_id}/screen_elements | JSON | {"screenshot", "elements"}: a base64 PNG and the elements in the same frame. |
Capture Screenshot, Capture Pagesource and Get Screen Elements aren't tools; use these resources instead. There are no MCP prompts.
Environment variables#
Set these in the environment that starts optics mcp. In a JSON client config, add an env object next to command.
| Variable | What it does |
|---|---|
OPTICS_AI_SELF_HEAL | 1, true, yes or on turns self-heal on for every session unless start_session says otherwise. |
OPTICS_LLM_PROVIDER | The default LLM provider. Only gemini is supported. |
OPTICS_LLM_MODEL | The default model. |
OPTICS_MCP_ALLOW_COORDINATE_TAPS | 1, true, yes or on exposes the coordinate tap tools. |
GEMINI_API_KEY, GOOGLE_API_KEY and the Vertex AI variables | Gemini credentials. They never travel in a tool call. |
Typical agent loop#
Start a session#
Call
start_sessionwith the driver, URL and capabilities, for exampledriver: "appium",capabilities: {"platformName": "Android", "appPackage": "com.birch.shop", ...}. Keep thesession_id.Look at the screen#
Call
screenshot, then readoptics://session/{session_id}/elements(or callget_interactive_elements) for labels and positions. Read/sourcewhen you need XPaths.Act#
Call keyword tools with a locator:
press_elementwithelement: "text=Sign in",enter_textwith an XPath, ordetect_and_presswith a visible label. Look again after each action.Save the run as a test#
Read
optics://project-format, then write the steps you ran into CSV files with a config.yaml. Run them withoptics execute <folder>.End the session#
Call
terminate_session.
Limits#
- No flow control.
Run Loop,Condition,Evaluate,Read Data,Invoke APIand the other flow-control keywords aren't tools. Your agent can still write them into CSV modules foroptics execute. - No recording or saving. There are no tools to record, save, replay, find devices or run doctor. The agent writes project files itself.
- Coordinate taps are hidden unless you set
OPTICS_MCP_ALLOW_COORDINATE_TAPS. Self-heal can still tap by percentage when it recovers a step. - Sessions aren't shared. Sessions live in the server's memory.
optics serveandoptics livecan't see them, and restarting the server ends them. - Images come from a project. There's no upload tool. Pass
project_pathto use template images. - Selenium has no interactive element list. The elements resource and
get_interactive_elementsfail on Selenium; use/sourceinstead.
Errors#
A failing tool returns an error message. When Optics raises a coded error, the message starts with the code in brackets, for example [Code.E0106] Driver session is no longer active .... The code can carry a Code. prefix; it's the same code. A step that fails after trying every element value arrives as Execution failed: All fallback attempts failed for keyword ..., with the code inside the text. See Error codes.