Labsco
Touchpoint-Labs logo

Touchpoint

β˜… 38

from Touchpoint-Labs

Give your AI agent eyes and hands on any desktop β€” cross-platform accessibility API with MCP server

πŸ”₯πŸ”₯πŸ”₯πŸ”₯βœ“ VerifiedFreeQuick setup
<p align="center"> <h1 align="center">Touchpoint</h1> <p align="center"> <strong>Give your AI agent eyes and hands on any desktop.</strong> </p> <p align="center"> <a href="https://pypi.org/project/touchpoint-py/"><img src="https://img.shields.io/pypi/v/touchpoint-py?color=blue" alt="PyPI"></a> <a href="https://pypi.org/project/touchpoint-py/"><img src="https://img.shields.io/pypi/pyversions/touchpoint-py" alt="Python"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a> <a href="#status"><img src="https://img.shields.io/badge/status-alpha-orange" alt="Alpha"></a> <br> <img src="https://img.shields.io/badge/Linux-FCC624?logo=linux&logoColor=black" alt="Linux"> <img src="https://img.shields.io/badge/macOS-000000?logo=apple&logoColor=white" alt="macOS"> <img src="https://img.shields.io/badge/Windows-0078D6?logo=windows&logoColor=white" alt="Windows"> </p> <p align="center"> <code>pip install touchpoint-py</code> </p> </p> <p align="center"><img src="https://raw.githubusercontent.com/Touchpoint-Labs/touchpoint/main/docs/demo.gif" width="720" alt="Touchpoint demo β€” AI agent creates a formatted Excel table using Touchpoint"></p> <p align="center"><em>AI agent researches data in Chrome, then creates a formatted Excel table β€” full task completed in ~12 minutes</em></p>

Touchpoint is a cross-platform Python library for reading and interacting with desktop UI through native accessibility APIs. One import, one API β€” works on Linux, macOS, and Windows, with built-in support for Chromium and Electron apps via CDP (Chrome DevTools Protocol).

Instead of scraping pixels or running vision models, Touchpoint reads the real accessibility tree β€” structured names, roles, states, and positions for every element on screen. Fast and reliable, with no vision model required. Ships with an MCP server so LLM agents like Claude, Cursor, or any local model can control any desktop app out of the box.

Copy & paste β€” that's it
import touchpoint as tp

elements = tp.find("Send", role=tp.Role.BUTTON, app="Slack")
tp.click(elements[0])

Why Touchpoint?

Screenshot / visionBrowser automationTouchpoint
Native desktop apps⚠️ inaccurate or slowβŒβœ…
Browsers⚠️ inaccurate or slowβœ…βœ… via CDP
Electron apps (Slack, VS Code, ...)⚠️ inaccurate or slow⚠️ web content onlyβœ… native + web
Structured element data❌ needs OCR/vision modelβœ… web onlyβœ… names, roles, states, positions
Works with local / non-vision modelsβŒβœ… web onlyβœ… all apps
Works across Linux, macOS, Windowsβœ…βœ…βœ…

Table of Contents


MCP Server

Touchpoint ships an MCP (Model Context Protocol) server ready for any MCP-compatible client. Use it to let LLM agents like Claude, Cursor, local models, or any tool that supports MCP control your desktop.

Two modes β€” vision and no-vision

Set TOUCHPOINT_MODE=no-vision (default: vision) to switch modes:

  • Vision mode β€” agents use screenshot() to see the screen and interact by element ID or coordinates. Best for frontier models with strong vision capabilities.
  • No-vision mode β€” agents use snapshot() to get a compact structured text tree of the active window, then act on element IDs directly. Works with any model including local ones that have no vision capability. Most action tools append auto-verify flags ((new window: ...), (focus moved), (no change detected)) so the agent can detect state changes without taking a screenshot.

Tools

CategoryVision modeNo-vision mode
Orientscreenshot, snapshot, apps, windowssnapshot, diff_snapshot, apps, windows
Findfind, get_elementfind
Readread_textread_text
Actionsclick (element or coordinates), set_value, set_numeric_value, select_text, focus, actionclick (element only), set_value, set_numeric_value, select_text, focus, action
Keyboardtype_text, press_keytype_text, press_key
Mousemouse_move, scrollscroll
Windowactivate_window, minimize_window, fullscreen_window, close_window, move_window, resize_windowactivate_window, minimize_window, fullscreen_window, close_window
Waitingwait_for, wait_for_app, wait_for_windowwait_for, wait_for_app, wait_for_window
Healthdiagnosticsdiagnostics

The MCP server includes built-in instructions that teach agents the correct workflow for each mode β€” including the orient β†’ act β†’ verify loop, when to use read_text vs find, and how to recover from errors.

Copy & paste β€” that's it
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”Œβ”€β”€β”€β–Άβ”‚  ORIENT  β”‚  screenshot Β· apps Β· windows
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
    β”‚         β–Ό
    β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚    β”‚  LOCATE  β”‚  find Β· snapshot Β· get_element
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
    β”‚         β–Ό
    β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚    β”‚   ACT    β”‚  click Β· set_value Β· type_text Β· press_key
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
    β”‚         β–Ό
    β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚    β”‚  VERIFY  │───▢ Done βœ…
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
    β”‚         β”‚ not yet
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Client setup

<details> <summary><strong>Claude Desktop</strong></summary>

Config file location:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
Copy & paste β€” that's it
{
  "mcpServers": {
    "touchpoint": {
      "command": "touchpoint-mcp"
    }
  }
}

If using a virtualenv, use the full path: "/path/to/venv/bin/touchpoint-mcp"

</details> <details> <summary><strong>VS Code / GitHub Copilot</strong></summary>

Add to .vscode/mcp.json in your workspace:

Copy & paste β€” that's it
{
  "servers": {
    "touchpoint": {
      "command": "touchpoint-mcp"
    }
  }
}
</details> <details> <summary><strong>Cursor</strong></summary>

Create or edit ~/.cursor/mcp.json:

Copy & paste β€” that's it
{
  "mcpServers": {
    "touchpoint": {
      "command": "touchpoint-mcp"
    }
  }
}
</details> <details> <summary><strong>Windsurf</strong></summary>

Edit ~/.codeium/windsurf/mcp_config.json:

Copy & paste β€” that's it
{
  "mcpServers": {
    "touchpoint": {
      "command": "touchpoint-mcp"
    }
  }
}
</details> <details> <summary><strong>Claude Code (CLI)</strong></summary>
Copy & paste β€” that's it
claude mcp add touchpoint -- touchpoint-mcp
</details> <details> <summary><strong>OpenClaw</strong></summary>

Add to mcpServers in ~/.openclaw/openclaw.json:

Copy & paste β€” that's it
{
  "mcpServers": {
    "touchpoint": {
      "command": "touchpoint-mcp"
    }
  }
}
</details>

Environment variables

<details> <summary>All optional β€” click to see available settings</summary> <br>
VariableExampleDescription
TOUCHPOINT_CDP_DISCOVERtrueAuto-discover CDP ports from running processes
TOUCHPOINT_CDP_PORTS{"Chrome": 9222}Explicit app-to-port mapping (JSON)
TOUCHPOINT_CDP_APPGoogle ChromeSingle app name (pair with _PORT)
TOUCHPOINT_CDP_PORT9222Single port (pair with _APP)
TOUCHPOINT_CDP_REFRESH_INTERVAL5.0Seconds between CDP port scans
TOUCHPOINT_SCALE_FACTOR1.25Display scale override
TOUCHPOINT_FUZZY_THRESHOLD0.6Minimum match score for find() (0.0–1.0)
TOUCHPOINT_FALLBACK_INPUTtrueUse coordinate fallback when native actions fail
TOUCHPOINT_MAX_ELEMENTS5000Maximum elements per query
TOUCHPOINT_MAX_DEPTH20Default tree depth limit
TOUCHPOINT_AX_MESSAGING_TIMEOUT1.0Max seconds to wait for a macOS AX app reply
</details>

Browser & Electron Apps (CDP)

Native accessibility APIs return limited data for Electron and Chromium apps (Slack, Discord, VS Code, etc.). Touchpoint's CDP backend connects via Chrome DevTools Protocol to get the full web content.

Auto-discovery is enabled by default β€” Touchpoint automatically finds running browsers and Electron apps that were launched with a debug port. No manual configuration needed beyond launching the app with the flag.

Setup

  1. Launch the app with a debug port:
Copy & paste β€” that's it
# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/tp-chrome

# macOS
open -na "Google Chrome" --args --remote-debugging-port=9222 --user-data-dir=/tmp/tp-chrome

# Windows
start chrome --remote-debugging-port=9222 --user-data-dir=%TEMP%\tp-chrome
  1. Configure Touchpoint:
Copy & paste β€” that's it
import touchpoint as tp

tp.configure(cdp_discover=True)             # auto-discover from running processes
# or
tp.configure(cdp_ports={"Google Chrome": 9222})  # explicit mapping
  1. Control what you get with the source parameter:
Copy & paste β€” that's it
tp.elements(app="Google Chrome", source="full")     # native chrome + web content (default)
tp.elements(app="Google Chrome", source="cdp_ax")   # web content only (CDP accessibility tree)
tp.elements(app="Google Chrome", source="native")   # native UI only (toolbar, tabs, menus)
tp.elements(app="Google Chrome", source="dom")      # DOM walker (catches what AX misses)

CDP results are merged with native backend results β€” you get the toolbar and window controls from AT-SPI2/UIA/AX, combined with the full web page content from CDP, in a single elements() call.

source="ax" remains accepted as a compatibility alias for source="cdp_ax". Prefer cdp_ax in new code so it is not confused with the native macOS AX backend.


API Reference

Discovery

FunctionDescription
tp.apps()List application names in the accessibility tree
tp.windows()All windows with id, title, app, position, size, active state
tp.elements(app, role, states, ...)UI elements, with filtering, tree mode, and formatting
tp.element_at(x, y)Deepest element at screen coordinates
tp.get_element(id)Fresh snapshot of a single element by ID

Search & Wait

FunctionDescription
tp.find(query, app, role, ...)Search by name β€” 4-stage matching: exact β†’ contains β†’ word β†’ fuzzy
tp.wait_for(query, ...)Poll until elements appear (or disappear with gone=True)
tp.wait_for_app(app, ...)Poll until an app appears or disappears
tp.wait_for_window(title, ...)Poll until a window appears or disappears

Actions

FunctionDescription
tp.click(element)Click via accessibility action, with coordinate fallback
tp.double_click(element)Double-click
tp.right_click(element)Right-click / context menu
tp.set_value(element, text)Set text content (replace=True to clear first)
tp.set_numeric_value(element, n)Set slider or spinbox value
tp.select_text(element, text)Select a substring within text content across Linux, Windows, macOS, and web/CDP
tp.select_text_range(element, start, end)Select a character range when you already know the offsets
tp.focus(element)Move keyboard focus
tp.action(element, name)Execute a raw accessibility action by name
tp.activate_window(window)Bring a window to the foreground (restores from minimized)
tp.minimize_window(window)Minimize a window. Use activate_window to restore.
tp.fullscreen_window(window, fullscreen=True)Enter or exit fullscreen for a window
tp.close_window(window)Politely close a window
tp.move_window(window, x, y)Move a window to a new screen position
tp.resize_window(window, width, height)Resize a window to width Γ— height pixels

Input

FunctionDescription
tp.type_text(text)Type into the currently focused element
tp.press_key(key)Press and release a key ("enter", "tab", "escape")
tp.hotkey(*keys)Key combination (tp.hotkey("ctrl", "s"))
tp.click_at(x, y)Click at screen coordinates
tp.double_click_at(x, y)Double-click at coordinates
tp.right_click_at(x, y)Right-click at coordinates
tp.mouse_move(x, y)Move the cursor
tp.scroll(direction, amount)Scroll at current cursor position

Screenshot & Config

FunctionDescription
tp.screenshot(app, element, ...)Full desktop or cropped to app/window/element/monitor
tp.monitor_count()Number of connected monitors
tp.configure(...)Set runtime options (see Configuration)
tp.diagnostics()Report backend, input, CDP, timeout, and dependency health

All action functions accept an Element object or a string ID. elements(), find(), and get_element() support format="flat", format="json", or format="tree" (elements only) to return pre-formatted strings instead of objects. Window management is implemented across Linux AT-SPI2, Windows UIA, and macOS AX backends.


Architecture

Copy & paste β€” that's it
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               import touchpoint as tp                 β”‚
β”‚  tp.find() Β· tp.click() Β· tp.screenshot() Β· ...       β”‚
β”‚                    (Public API)                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚     Backend (ABC)       β”‚    InputProvider (ABC)      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  AT-SPI2     (Linux)    β”‚  Xdotool       (X11)        β”‚
β”‚  UIA         (Windows)  β”‚  SendInput     (Win32)      β”‚
β”‚  AX          (macOS)    β”‚  CGEvent       (macOS)      β”‚
β”‚  CDP         (browsers) β”‚                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Utilities: formatter Β· matcher Β· screenshot Β· scale  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Two-layer design:

  • Backend reads the accessibility tree and runs structured actions (click, set_value, focus). Element-aware and reliable.
  • InputProvider simulates raw keyboard and mouse input. Coordinate-based and element-blind. Used as an automatic fallback when a native accessibility action isn't available.

CDP runs alongside the platform backend. Their results are merged: native window chrome (toolbar, tabs, menus) from AT-SPI2/UIA/AX, plus full web content from CDP, unified under one API.

For detailed internals, see ARCHITECTURE.md.


Development

Copy & paste β€” that's it
git clone https://github.com/Touchpoint-Labs/touchpoint.git
cd touchpoint
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

Status

Alpha β€” fully functional and tested on all three platforms. The API may change before 1.0 based on user feedback.

PlatformBackendInputCDPTests
Linux (X11)βœ… AT-SPI2βœ… xdotoolβœ…βœ…
Windowsβœ… UIAβœ… SendInputβœ…βœ…
macOSβœ… AXβœ… CGEventβœ…βœ…

Known limitations

  • Wayland input β€” The Linux InputProvider uses xdotool, which requires X11. On pure Wayland (no XWayland), keyboard/mouse simulation is unavailable. The accessibility tree and native actions still work.

  • Synchronous CDP β€” CDP calls block on WebSocket responses. JavaScript dialogs (alert, confirm, prompt) are auto-dismissed to prevent deadlocks. An async rewrite is planned.

  • No browser navigation API β€” Touchpoint doesn't have built-in URL navigation. Agents can navigate by interacting with UI elements directly: find the address bar, type a URL, press Enter.

  • CDP windows are page targets, not OS windows β€” but window management still works: tp.activate_window() brings the target forward via CDP, and minimize/fullscreen/close/move/resize on a surfaced cdp: window are routed to the underlying native OS window (resolved by owning PID) and handled by the platform backend. They raise ActionFailedError only if no native OS window for that target can be found (e.g. it has been closed).

  • Backend role/state parity is still uneven β€” macOS AX and Windows UIA both improved significantly in 0.3.0, but Windows still relies on more heuristics and has more unmapped long-tail roles than the other backends.


Roadmap

High Priority

  • Async CDP architecture β€” non-blocking WebSocket, proper dialog queuing, concurrent multi-tab queries

Medium Priority

  • Backend role/state parity β€” close remaining role mapping gaps, especially UIA long-tail roles on Windows
  • Wayland input backend β€” libei / xdg-desktop-portal RemoteDesktop when X11 isn't available

Lower Priority

  • Tooltip and notification visibility
  • Element caching

License

MIT