arthur-runtime 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arthur_runtime-0.1.0/.gitignore +8 -0
- arthur_runtime-0.1.0/CONTEXT.md +26 -0
- arthur_runtime-0.1.0/LICENSE +21 -0
- arthur_runtime-0.1.0/PKG-INFO +300 -0
- arthur_runtime-0.1.0/README.md +286 -0
- arthur_runtime-0.1.0/issues/01-minimal-cdp-websocket-client.md +15 -0
- arthur_runtime-0.1.0/issues/02-chromium-process-lifecycle.md +14 -0
- arthur_runtime-0.1.0/issues/03-semantic-snapshot-generator.md +14 -0
- arthur_runtime-0.1.0/issues/04-cdp-synthetic-input.md +15 -0
- arthur_runtime-0.1.0/issues/05-synchronous-browser-api.md +14 -0
- arthur_runtime-0.1.0/issues/06-persistent-repl-engine.md +15 -0
- arthur_runtime-0.1.0/issues/07-fastmcp-server-and-cli.md +13 -0
- arthur_runtime-0.1.0/issues/map.md +48 -0
- arthur_runtime-0.1.0/issues/spec-arthur-headless-runtime.md +124 -0
- arthur_runtime-0.1.0/pyproject.toml +39 -0
- arthur_runtime-0.1.0/src/arthur/__init__.py +25 -0
- arthur_runtime-0.1.0/src/arthur/browser.py +699 -0
- arthur_runtime-0.1.0/src/arthur/cdp.py +291 -0
- arthur_runtime-0.1.0/src/arthur/cli.py +122 -0
- arthur_runtime-0.1.0/src/arthur/dom.py +649 -0
- arthur_runtime-0.1.0/src/arthur/errors.py +157 -0
- arthur_runtime-0.1.0/src/arthur/input.py +201 -0
- arthur_runtime-0.1.0/src/arthur/launcher.py +321 -0
- arthur_runtime-0.1.0/src/arthur/repl.py +378 -0
- arthur_runtime-0.1.0/src/arthur/server.py +101 -0
- arthur_runtime-0.1.0/tests/test_browser.py +155 -0
- arthur_runtime-0.1.0/tests/test_cdp.py +218 -0
- arthur_runtime-0.1.0/tests/test_dom.py +259 -0
- arthur_runtime-0.1.0/tests/test_input.py +212 -0
- arthur_runtime-0.1.0/tests/test_launcher.py +51 -0
- arthur_runtime-0.1.0/tests/test_repl.py +88 -0
- arthur_runtime-0.1.0/tests/test_server.py +52 -0
- arthur_runtime-0.1.0/uv.lock +1584 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Arthur — Domain Vocabulary & Context
|
|
2
|
+
|
|
3
|
+
This glossary defines the shared domain vocabulary and architectural seams of the **Arthur** headless Chromium runtime.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Domain Concepts
|
|
8
|
+
|
|
9
|
+
### 1. Browser & Tabs
|
|
10
|
+
- **`Browser`**: The top-level synchronous runtime facade exposed to AI agents and the REPL. Manages browser process lifecycle and delegates actions to the active tab.
|
|
11
|
+
- **`Tab`**: A synchronous proxy to an attached page target (`sessionId`), exposing navigation, semantic snapshot inspection, element querying, and synthetic interactions.
|
|
12
|
+
- **`TabManager`**: The internal lifecycle tracker mapping CDP `targetId`s to sequential integer IDs, session attachments, URLs, and active tab pointers.
|
|
13
|
+
|
|
14
|
+
### 2. DOM Engine & Ref-IDs
|
|
15
|
+
- **`DOM Engine`**: The in-page JavaScript runtime (`window.__arthur_dom_op`) that parses the accessible DOM tree, evaluates WAI-ARIA roles/names, and maintains interactive element coordinates.
|
|
16
|
+
- **`Ref-ID`**: A deterministic integer reference (formatted canonically as `[#1]`, `[#2]`) assigned to actionable DOM nodes in a snapshot outline, enabling token-efficient agent targeting without XPath/CSS selectors.
|
|
17
|
+
- **`Target Reference`**: Polymorphic target specifier accepted by all interaction methods: numeric Ref-ID (`1`), string Ref-ID (`"[#1]"` or `"#1"`), or CSS selector (`"button.submit"`).
|
|
18
|
+
- **`Diagnostic Auto-Snapshot`**: An automatic lightweight semantic DOM outline captured and attached to exception payloads when a REPL command fails, allowing single-turn self-healing.
|
|
19
|
+
|
|
20
|
+
### 3. CDP Transport & Input
|
|
21
|
+
- **`CDPClient`**: Asynchronous, thread-safe Chrome DevTools Protocol WebSocket transport with numeric request/response multiplexing and session targeting (`flatten: true`).
|
|
22
|
+
- **`InteractionDriver`**: Synthetic input coordinator handling coordinate-accurate mouse clicks, text typing with Enter submissions, dropdown selections, hover, and scrolling over CDP.
|
|
23
|
+
|
|
24
|
+
### 4. REPL & Budgeting
|
|
25
|
+
- **`REPL Session`**: Persistent in-memory Python execution environment that retains variables, imports, and helper functions across agent turns via AST statement/expression compilation.
|
|
26
|
+
- **`Output Budget`**: Hard character/token limit and formatting pipeline with telemetry defanging (sanitizing image beacons and raw active tags).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shivansh Singh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: arthur-runtime
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight Headless Chromium Runtime for Gloria and AI Agents
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: mcp>=1.0.0
|
|
8
|
+
Requires-Dist: websockets>=12.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
<div align="center">
|
|
16
|
+
|
|
17
|
+
# Arthur
|
|
18
|
+
|
|
19
|
+
### Lightweight Headless Chromium Runtime & MCP Server for AI Agents
|
|
20
|
+
|
|
21
|
+
[](https://opensource.org/licenses/MIT)
|
|
22
|
+
[](https://www.python.org/downloads/)
|
|
23
|
+
[](https://modelcontextprotocol.io/)
|
|
24
|
+
[](https://github.com/astral-sh/uv)
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
Direct CDP WebSockets • Semantic Ref-ID Snapshots • Persistent Python REPL • FastMCP Server
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Overview
|
|
35
|
+
|
|
36
|
+
**Arthur** is a standalone, lightweight headless Chromium runtime engineered specifically for AI agents (such as Gloria, Claude, and autonomous coding assistants).
|
|
37
|
+
|
|
38
|
+
Arthur eliminates browser extensions, native messaging hosts, and heavy automation drivers by connecting directly to Chromium via **Chrome DevTools Protocol (CDP) WebSockets**. It manages an ephemeral sandboxed Chromium process, retains persistent Python REPL state across turns, and generates concise, token-efficient semantic DOM snapshots with assigned **Ref-IDs** (`[#1]`, `[#2]`).
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
Agent / MCP Client
|
|
42
|
+
│
|
|
43
|
+
▼ execute_python(code)
|
|
44
|
+
FastMCP Server (stdio / Streamable HTTP)
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
Python REPL Session (stateful memory & auto-snapshots)
|
|
48
|
+
│
|
|
49
|
+
▼
|
|
50
|
+
Arthur Browser API (synchronous facade)
|
|
51
|
+
│
|
|
52
|
+
▼ CDP WebSockets
|
|
53
|
+
Headless Chromium (--headless=new)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Quickstart
|
|
59
|
+
|
|
60
|
+
### 1. Instant Zero-Clone Execution via `uvx`
|
|
61
|
+
|
|
62
|
+
No repository cloning or manual environment management is required:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Run MCP Server (stdio transport)
|
|
66
|
+
uvx --from git+https://github.com/sh7vansh/arthur arthur mcp
|
|
67
|
+
|
|
68
|
+
# Run Interactive Terminal REPL
|
|
69
|
+
uvx --from git+https://github.com/sh7vansh/arthur arthur repl
|
|
70
|
+
|
|
71
|
+
# Run One-Shot Command
|
|
72
|
+
uvx --from git+https://github.com/sh7vansh/arthur arthur repl -c "browser.navigate('https://example.com'); print(browser.snapshot())"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### 2. Connect to MCP Clients (Claude Desktop, Cursor, etc.)
|
|
78
|
+
|
|
79
|
+
Add Arthur to your MCP settings configuration (e.g. `claude_desktop_config.json`):
|
|
80
|
+
|
|
81
|
+
#### Using `uvx` (Zero-Clone):
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"arthur": {
|
|
86
|
+
"command": "uvx",
|
|
87
|
+
"args": [
|
|
88
|
+
"--from",
|
|
89
|
+
"git+https://github.com/sh7vansh/arthur",
|
|
90
|
+
"arthur",
|
|
91
|
+
"mcp"
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Using Local Repository:
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"arthur": {
|
|
103
|
+
"command": "uv",
|
|
104
|
+
"args": [
|
|
105
|
+
"--directory",
|
|
106
|
+
"/path/to/Arthur",
|
|
107
|
+
"run",
|
|
108
|
+
"arthur",
|
|
109
|
+
"mcp"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Local Installation
|
|
119
|
+
|
|
120
|
+
### Prerequisites
|
|
121
|
+
- **Python 3.10+**
|
|
122
|
+
- **[uv](https://astral.sh/uv/)** package manager
|
|
123
|
+
- **Google Chrome** or **Chromium** (Arthur automatically discovers existing local installations)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Clone the repository
|
|
127
|
+
git clone https://github.com/sh7vansh/arthur.git
|
|
128
|
+
cd arthur
|
|
129
|
+
|
|
130
|
+
# Install dependencies in an isolated virtual environment
|
|
131
|
+
uv sync --all-extras
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Key Capabilities
|
|
137
|
+
|
|
138
|
+
### 1. In-Page Semantic DOM Engine & Ref-IDs
|
|
139
|
+
Arthur evaluates an in-page accessibility parser that extracts the visible DOM, calculates WAI-ARIA accessible roles/names, and generates a compact, token-efficient semantic tree with assigned numeric **Ref-IDs**:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
PAGE: "Example Domain" (https://example.com)
|
|
143
|
+
- heading[level=1] "Example Domain"
|
|
144
|
+
- paragraph: "This domain is for use in illustrative examples in documents."
|
|
145
|
+
- link [#1] "More information..." (href="https://www.iana.org/domains/example")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Agents target elements directly using Ref-IDs (`1`, `"[#1]"`), avoiding brittle CSS or XPath selectors.
|
|
149
|
+
|
|
150
|
+
### 2. Coordinate-Accurate Synthetic Input
|
|
151
|
+
Translates high-level agent actions into coordinate-precise CDP events:
|
|
152
|
+
- **`browser.click(target)`**: Resolves bounding center coordinates and dispatches `Input.dispatchMouseEvent`.
|
|
153
|
+
- **`browser.type(target, text, press_enter=True)`**: Focuses the element, clears existing input, inserts text, and simulates real key events.
|
|
154
|
+
- **`browser.select(target, value)`**: Handles `<select>` dropdown menus.
|
|
155
|
+
- **`browser.hover(target)`** & **`browser.scroll(x, y)`**: Simulates mouse hover and viewport/element scrolling.
|
|
156
|
+
|
|
157
|
+
### 3. Persistent Stateful Python REPL
|
|
158
|
+
The `execute_python` tool retains variables, functions, and imports across successive agent turns:
|
|
159
|
+
```python
|
|
160
|
+
# Turn 1: Define helper and fetch data
|
|
161
|
+
import json
|
|
162
|
+
browser.navigate("https://news.ycombinator.com")
|
|
163
|
+
titles = browser.eval_js("[...document.querySelectorAll('.titleline > a')].map(a => a.innerText)")
|
|
164
|
+
|
|
165
|
+
# Turn 2: State persists across tool calls
|
|
166
|
+
print(f"Captured {len(titles)} articles:")
|
|
167
|
+
print(titles[:3])
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 4. Single-Turn Self-Healing & Diagnostics
|
|
171
|
+
- **Diagnostic Auto-Snapshot**: When an unhandled exception occurs, Arthur automatically captures the latest DOM snapshot (`[diagnostic_auto_snapshot]`) and appends it to the error payload, allowing the agent to self-heal in a single turn without extra roundtrips.
|
|
172
|
+
- **Fuzzy Suggestions**: If an element reference becomes stale after a dynamic DOM mutation, Arthur provides fuzzy match suggestions from snapshot history.
|
|
173
|
+
|
|
174
|
+
### 5. Output Budgeting & Telemetry Defanging
|
|
175
|
+
All execution output passes through a strict budgeting pipeline:
|
|
176
|
+
- Prevents context window explosion by truncating output exceeding token/character limits.
|
|
177
|
+
- Automatically defangs tracking image beacons (`` -> `[IMAGE_BLOCKED]`) and unsafe active HTML tags.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Python API Reference
|
|
182
|
+
|
|
183
|
+
The synchronous `browser` instance is pre-injected into the REPL environment:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
# --- Navigation & Inspection ---
|
|
187
|
+
browser.navigate("https://example.com", timeout=30.0)
|
|
188
|
+
snapshot_text = browser.snapshot()
|
|
189
|
+
current_url = browser.url
|
|
190
|
+
page_title = browser.title
|
|
191
|
+
|
|
192
|
+
# --- Synthetic Interactions (Ref-ID, String Ref, or CSS Selector) ---
|
|
193
|
+
browser.click(1) # Click Ref-ID #1
|
|
194
|
+
browser.click("[#1]") # String Ref-ID format
|
|
195
|
+
browser.click("button.submit-btn") # CSS selector fallback
|
|
196
|
+
browser.type(2, "search query", press_enter=True)
|
|
197
|
+
browser.select(3, "Option Value")
|
|
198
|
+
browser.hover(1)
|
|
199
|
+
browser.scroll(x=0, y=500)
|
|
200
|
+
|
|
201
|
+
# --- Synchronization & Waiting ---
|
|
202
|
+
browser.wait_for(1, state="visible", timeout=10.0)
|
|
203
|
+
browser.wait_for_url(r"^https://example\.com/dashboard", timeout=15.0)
|
|
204
|
+
|
|
205
|
+
# --- Evaluation & Captures ---
|
|
206
|
+
result = browser.eval_js("window.innerWidth")
|
|
207
|
+
png_bytes = browser.screenshot()
|
|
208
|
+
text_content = browser.get_text(1)
|
|
209
|
+
attr_value = browser.get_attribute(1, "data-custom")
|
|
210
|
+
|
|
211
|
+
# --- Multi-Tab Management ---
|
|
212
|
+
new_tab = browser.new_tab("https://google.com")
|
|
213
|
+
tabs = browser.tabs # List of open Tab instances
|
|
214
|
+
active = browser.active_tab
|
|
215
|
+
tab_2 = browser.get_tab(2)
|
|
216
|
+
browser.close_tab(2)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Remote Deployment & Transports
|
|
222
|
+
|
|
223
|
+
Arthur supports multiple network transports for remote, cloud, and containerized deployments.
|
|
224
|
+
|
|
225
|
+
### 1. Stateless Streamable HTTP (Recommended for Remote / Cloud)
|
|
226
|
+
|
|
227
|
+
For remote servers, VMs, Docker containers, Cloudflare Tunnels, and reverse proxies, **Stateless Streamable HTTP is the most reliable transport**.
|
|
228
|
+
|
|
229
|
+
#### Why Stateless HTTP is Superior for Remote Setups:
|
|
230
|
+
- **Resilient to Network Drops**: Unlike stateful SSE connections that drop or report "Session Expired" when a network glitch occurs between agent turns, stateless HTTP treats each tool execution as an independent request.
|
|
231
|
+
- **Proxy & Tunnel Friendly**: Works cleanly behind Nginx, Cloudflare Tunnels, AWS ALBs, and Ngrok without hitting idle connection timeouts (e.g. 60s stream timeouts).
|
|
232
|
+
- **Preserved Backend State**: While the HTTP wire transport is stateless, Arthur's in-memory Python REPL session, Chromium browser instance, cookies, and open tabs remain fully persistent on the server.
|
|
233
|
+
|
|
234
|
+
#### Starting Stateless Streamable HTTP:
|
|
235
|
+
```bash
|
|
236
|
+
# Start server on remote host (listening on 0.0.0.0:8000)
|
|
237
|
+
uv run arthur mcp --transport streamable-http --stateless --host 0.0.0.0 --port 8000
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### Client Configuration:
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"mcpServers": {
|
|
244
|
+
"arthur": {
|
|
245
|
+
"url": "http://remote-server-ip:8000/mcp"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### 2. SSH Stdio Tunneling (Zero-Port Remote Access)
|
|
254
|
+
|
|
255
|
+
Run Arthur securely over SSH without opening public firewall ports:
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"mcpServers": {
|
|
260
|
+
"remote-arthur": {
|
|
261
|
+
"command": "ssh",
|
|
262
|
+
"args": [
|
|
263
|
+
"user@remote-host",
|
|
264
|
+
"uvx --from git+https://github.com/sh7vansh/arthur arthur mcp"
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### 3. Server-Sent Events (`/sse`)
|
|
274
|
+
|
|
275
|
+
For legacy MCP clients requiring standard SSE endpoints:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
uv run arthur mcp --transport sse --host 0.0.0.0 --port 8000
|
|
279
|
+
```
|
|
280
|
+
- **Endpoint**: `http://<host>:8000/sse`
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Testing & Development
|
|
285
|
+
|
|
286
|
+
Run the test suite to validate CDP WebSocket transport, headless Chromium lifecycle, in-page DOM operations, and persistent REPL execution:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Run pytest test suite
|
|
290
|
+
uv run pytest
|
|
291
|
+
|
|
292
|
+
# Run type checker
|
|
293
|
+
uv run mypy src
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
This project is licensed under the **MIT License** — see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Arthur
|
|
4
|
+
|
|
5
|
+
### Lightweight Headless Chromium Runtime & MCP Server for AI Agents
|
|
6
|
+
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.python.org/downloads/)
|
|
9
|
+
[](https://modelcontextprotocol.io/)
|
|
10
|
+
[](https://github.com/astral-sh/uv)
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
Direct CDP WebSockets • Semantic Ref-ID Snapshots • Persistent Python REPL • FastMCP Server
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Overview
|
|
21
|
+
|
|
22
|
+
**Arthur** is a standalone, lightweight headless Chromium runtime engineered specifically for AI agents (such as Gloria, Claude, and autonomous coding assistants).
|
|
23
|
+
|
|
24
|
+
Arthur eliminates browser extensions, native messaging hosts, and heavy automation drivers by connecting directly to Chromium via **Chrome DevTools Protocol (CDP) WebSockets**. It manages an ephemeral sandboxed Chromium process, retains persistent Python REPL state across turns, and generates concise, token-efficient semantic DOM snapshots with assigned **Ref-IDs** (`[#1]`, `[#2]`).
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
Agent / MCP Client
|
|
28
|
+
│
|
|
29
|
+
▼ execute_python(code)
|
|
30
|
+
FastMCP Server (stdio / Streamable HTTP)
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
Python REPL Session (stateful memory & auto-snapshots)
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
Arthur Browser API (synchronous facade)
|
|
37
|
+
│
|
|
38
|
+
▼ CDP WebSockets
|
|
39
|
+
Headless Chromium (--headless=new)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quickstart
|
|
45
|
+
|
|
46
|
+
### 1. Instant Zero-Clone Execution via `uvx`
|
|
47
|
+
|
|
48
|
+
No repository cloning or manual environment management is required:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Run MCP Server (stdio transport)
|
|
52
|
+
uvx --from git+https://github.com/sh7vansh/arthur arthur mcp
|
|
53
|
+
|
|
54
|
+
# Run Interactive Terminal REPL
|
|
55
|
+
uvx --from git+https://github.com/sh7vansh/arthur arthur repl
|
|
56
|
+
|
|
57
|
+
# Run One-Shot Command
|
|
58
|
+
uvx --from git+https://github.com/sh7vansh/arthur arthur repl -c "browser.navigate('https://example.com'); print(browser.snapshot())"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### 2. Connect to MCP Clients (Claude Desktop, Cursor, etc.)
|
|
64
|
+
|
|
65
|
+
Add Arthur to your MCP settings configuration (e.g. `claude_desktop_config.json`):
|
|
66
|
+
|
|
67
|
+
#### Using `uvx` (Zero-Clone):
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"arthur": {
|
|
72
|
+
"command": "uvx",
|
|
73
|
+
"args": [
|
|
74
|
+
"--from",
|
|
75
|
+
"git+https://github.com/sh7vansh/arthur",
|
|
76
|
+
"arthur",
|
|
77
|
+
"mcp"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Using Local Repository:
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"mcpServers": {
|
|
88
|
+
"arthur": {
|
|
89
|
+
"command": "uv",
|
|
90
|
+
"args": [
|
|
91
|
+
"--directory",
|
|
92
|
+
"/path/to/Arthur",
|
|
93
|
+
"run",
|
|
94
|
+
"arthur",
|
|
95
|
+
"mcp"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Local Installation
|
|
105
|
+
|
|
106
|
+
### Prerequisites
|
|
107
|
+
- **Python 3.10+**
|
|
108
|
+
- **[uv](https://astral.sh/uv/)** package manager
|
|
109
|
+
- **Google Chrome** or **Chromium** (Arthur automatically discovers existing local installations)
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Clone the repository
|
|
113
|
+
git clone https://github.com/sh7vansh/arthur.git
|
|
114
|
+
cd arthur
|
|
115
|
+
|
|
116
|
+
# Install dependencies in an isolated virtual environment
|
|
117
|
+
uv sync --all-extras
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Key Capabilities
|
|
123
|
+
|
|
124
|
+
### 1. In-Page Semantic DOM Engine & Ref-IDs
|
|
125
|
+
Arthur evaluates an in-page accessibility parser that extracts the visible DOM, calculates WAI-ARIA accessible roles/names, and generates a compact, token-efficient semantic tree with assigned numeric **Ref-IDs**:
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
PAGE: "Example Domain" (https://example.com)
|
|
129
|
+
- heading[level=1] "Example Domain"
|
|
130
|
+
- paragraph: "This domain is for use in illustrative examples in documents."
|
|
131
|
+
- link [#1] "More information..." (href="https://www.iana.org/domains/example")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Agents target elements directly using Ref-IDs (`1`, `"[#1]"`), avoiding brittle CSS or XPath selectors.
|
|
135
|
+
|
|
136
|
+
### 2. Coordinate-Accurate Synthetic Input
|
|
137
|
+
Translates high-level agent actions into coordinate-precise CDP events:
|
|
138
|
+
- **`browser.click(target)`**: Resolves bounding center coordinates and dispatches `Input.dispatchMouseEvent`.
|
|
139
|
+
- **`browser.type(target, text, press_enter=True)`**: Focuses the element, clears existing input, inserts text, and simulates real key events.
|
|
140
|
+
- **`browser.select(target, value)`**: Handles `<select>` dropdown menus.
|
|
141
|
+
- **`browser.hover(target)`** & **`browser.scroll(x, y)`**: Simulates mouse hover and viewport/element scrolling.
|
|
142
|
+
|
|
143
|
+
### 3. Persistent Stateful Python REPL
|
|
144
|
+
The `execute_python` tool retains variables, functions, and imports across successive agent turns:
|
|
145
|
+
```python
|
|
146
|
+
# Turn 1: Define helper and fetch data
|
|
147
|
+
import json
|
|
148
|
+
browser.navigate("https://news.ycombinator.com")
|
|
149
|
+
titles = browser.eval_js("[...document.querySelectorAll('.titleline > a')].map(a => a.innerText)")
|
|
150
|
+
|
|
151
|
+
# Turn 2: State persists across tool calls
|
|
152
|
+
print(f"Captured {len(titles)} articles:")
|
|
153
|
+
print(titles[:3])
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 4. Single-Turn Self-Healing & Diagnostics
|
|
157
|
+
- **Diagnostic Auto-Snapshot**: When an unhandled exception occurs, Arthur automatically captures the latest DOM snapshot (`[diagnostic_auto_snapshot]`) and appends it to the error payload, allowing the agent to self-heal in a single turn without extra roundtrips.
|
|
158
|
+
- **Fuzzy Suggestions**: If an element reference becomes stale after a dynamic DOM mutation, Arthur provides fuzzy match suggestions from snapshot history.
|
|
159
|
+
|
|
160
|
+
### 5. Output Budgeting & Telemetry Defanging
|
|
161
|
+
All execution output passes through a strict budgeting pipeline:
|
|
162
|
+
- Prevents context window explosion by truncating output exceeding token/character limits.
|
|
163
|
+
- Automatically defangs tracking image beacons (`` -> `[IMAGE_BLOCKED]`) and unsafe active HTML tags.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Python API Reference
|
|
168
|
+
|
|
169
|
+
The synchronous `browser` instance is pre-injected into the REPL environment:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
# --- Navigation & Inspection ---
|
|
173
|
+
browser.navigate("https://example.com", timeout=30.0)
|
|
174
|
+
snapshot_text = browser.snapshot()
|
|
175
|
+
current_url = browser.url
|
|
176
|
+
page_title = browser.title
|
|
177
|
+
|
|
178
|
+
# --- Synthetic Interactions (Ref-ID, String Ref, or CSS Selector) ---
|
|
179
|
+
browser.click(1) # Click Ref-ID #1
|
|
180
|
+
browser.click("[#1]") # String Ref-ID format
|
|
181
|
+
browser.click("button.submit-btn") # CSS selector fallback
|
|
182
|
+
browser.type(2, "search query", press_enter=True)
|
|
183
|
+
browser.select(3, "Option Value")
|
|
184
|
+
browser.hover(1)
|
|
185
|
+
browser.scroll(x=0, y=500)
|
|
186
|
+
|
|
187
|
+
# --- Synchronization & Waiting ---
|
|
188
|
+
browser.wait_for(1, state="visible", timeout=10.0)
|
|
189
|
+
browser.wait_for_url(r"^https://example\.com/dashboard", timeout=15.0)
|
|
190
|
+
|
|
191
|
+
# --- Evaluation & Captures ---
|
|
192
|
+
result = browser.eval_js("window.innerWidth")
|
|
193
|
+
png_bytes = browser.screenshot()
|
|
194
|
+
text_content = browser.get_text(1)
|
|
195
|
+
attr_value = browser.get_attribute(1, "data-custom")
|
|
196
|
+
|
|
197
|
+
# --- Multi-Tab Management ---
|
|
198
|
+
new_tab = browser.new_tab("https://google.com")
|
|
199
|
+
tabs = browser.tabs # List of open Tab instances
|
|
200
|
+
active = browser.active_tab
|
|
201
|
+
tab_2 = browser.get_tab(2)
|
|
202
|
+
browser.close_tab(2)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Remote Deployment & Transports
|
|
208
|
+
|
|
209
|
+
Arthur supports multiple network transports for remote, cloud, and containerized deployments.
|
|
210
|
+
|
|
211
|
+
### 1. Stateless Streamable HTTP (Recommended for Remote / Cloud)
|
|
212
|
+
|
|
213
|
+
For remote servers, VMs, Docker containers, Cloudflare Tunnels, and reverse proxies, **Stateless Streamable HTTP is the most reliable transport**.
|
|
214
|
+
|
|
215
|
+
#### Why Stateless HTTP is Superior for Remote Setups:
|
|
216
|
+
- **Resilient to Network Drops**: Unlike stateful SSE connections that drop or report "Session Expired" when a network glitch occurs between agent turns, stateless HTTP treats each tool execution as an independent request.
|
|
217
|
+
- **Proxy & Tunnel Friendly**: Works cleanly behind Nginx, Cloudflare Tunnels, AWS ALBs, and Ngrok without hitting idle connection timeouts (e.g. 60s stream timeouts).
|
|
218
|
+
- **Preserved Backend State**: While the HTTP wire transport is stateless, Arthur's in-memory Python REPL session, Chromium browser instance, cookies, and open tabs remain fully persistent on the server.
|
|
219
|
+
|
|
220
|
+
#### Starting Stateless Streamable HTTP:
|
|
221
|
+
```bash
|
|
222
|
+
# Start server on remote host (listening on 0.0.0.0:8000)
|
|
223
|
+
uv run arthur mcp --transport streamable-http --stateless --host 0.0.0.0 --port 8000
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Client Configuration:
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"mcpServers": {
|
|
230
|
+
"arthur": {
|
|
231
|
+
"url": "http://remote-server-ip:8000/mcp"
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### 2. SSH Stdio Tunneling (Zero-Port Remote Access)
|
|
240
|
+
|
|
241
|
+
Run Arthur securely over SSH without opening public firewall ports:
|
|
242
|
+
|
|
243
|
+
```json
|
|
244
|
+
{
|
|
245
|
+
"mcpServers": {
|
|
246
|
+
"remote-arthur": {
|
|
247
|
+
"command": "ssh",
|
|
248
|
+
"args": [
|
|
249
|
+
"user@remote-host",
|
|
250
|
+
"uvx --from git+https://github.com/sh7vansh/arthur arthur mcp"
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### 3. Server-Sent Events (`/sse`)
|
|
260
|
+
|
|
261
|
+
For legacy MCP clients requiring standard SSE endpoints:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
uv run arthur mcp --transport sse --host 0.0.0.0 --port 8000
|
|
265
|
+
```
|
|
266
|
+
- **Endpoint**: `http://<host>:8000/sse`
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Testing & Development
|
|
271
|
+
|
|
272
|
+
Run the test suite to validate CDP WebSocket transport, headless Chromium lifecycle, in-page DOM operations, and persistent REPL execution:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
# Run pytest test suite
|
|
276
|
+
uv run pytest
|
|
277
|
+
|
|
278
|
+
# Run type checker
|
|
279
|
+
uv run mypy src
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## License
|
|
285
|
+
|
|
286
|
+
This project is licensed under the **MIT License** — see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# 01 — Minimal CDP WebSocket Client & Session Dispatcher
|
|
2
|
+
|
|
3
|
+
**What to build:** An asynchronous, thread-safe Chrome DevTools Protocol (CDP) WebSocket client that connects to Chromium's remote debugging endpoint, correlates auto-incrementing numeric command IDs with awaiting futures, multiplexes target sessions (`sessionId`) across a single connection, and dispatches asynchronous CDP event streams to registered listeners.
|
|
4
|
+
|
|
5
|
+
**Blocked by:** None — can start immediately.
|
|
6
|
+
|
|
7
|
+
**Status:** closed
|
|
8
|
+
|
|
9
|
+
- [x] Connects to Chromium's DevTools WebSocket endpoint via lightweight async transport.
|
|
10
|
+
- [x] Provides generic `call(method, params, session_id)` interface resolving responses and propagating errors.
|
|
11
|
+
- [x] Correlates request IDs with awaiting futures for concurrent command execution.
|
|
12
|
+
- [x] Supports target session multiplexing via `Target.attachToTarget` (with `flatten: true`).
|
|
13
|
+
- [x] Dispatches incoming CDP domain events to registered event callbacks.
|
|
14
|
+
- [x] Gracefully detects connection drops and raises structured `BrowserUnavailableError`.
|
|
15
|
+
- [x] Unit & mock-server test suite passing.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 02 — Headless Chromium Process Lifecycle & Ephemeral Sandbox
|
|
2
|
+
|
|
3
|
+
**What to build:** Cross-platform Chromium binary discovery, ephemeral `--user-data-dir` sandboxing, launching modern `--headless=new` with ephemeral port allocation, DevToolsActivePort/WebSocket discovery, and guaranteed zero-leak process termination on exit or POSIX signals.
|
|
4
|
+
|
|
5
|
+
**Blocked by:** None — can start immediately.
|
|
6
|
+
|
|
7
|
+
**Status:** closed
|
|
8
|
+
|
|
9
|
+
- [x] Discovers local Chromium binary across `CHROMIUM_PATH`, system paths (`chromium`, `google-chrome`, etc.), and sandbox fallbacks.
|
|
10
|
+
- [x] Creates isolated temporary user data directory per session.
|
|
11
|
+
- [x] Launches Chromium subprocess with `--headless=new`, `--remote-debugging-port=0`, `--no-first-run`, and minimal memory flags.
|
|
12
|
+
- [x] Reads assigned DevTools port / WebSocket URL reliably from `DevToolsActivePort`.
|
|
13
|
+
- [x] Registers `atexit` and signal handlers (`SIGINT`, `SIGTERM`) to kill subprocess tree and purge temporary user directory.
|
|
14
|
+
- [x] Unit & lifecycle integration tests passing.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 03 — In-Page Semantic Snapshot Generator & Ref-ID Registry
|
|
2
|
+
|
|
3
|
+
**What to build:** In-page DOM engine evaluation via CDP `Runtime.evaluate` that generates compact, token-efficient semantic text snapshots with interactive roles, accessible names, Ref-IDs (`[#1]`, `[#2]`), element bounding coordinates, and historical state for stale reference detection.
|
|
4
|
+
|
|
5
|
+
**Blocked by:** 01 (Minimal CDP WebSocket Client), 02 (Headless Chromium Process Lifecycle).
|
|
6
|
+
|
|
7
|
+
**Status:** closed
|
|
8
|
+
|
|
9
|
+
- [x] Injects and evaluates DOM engine in target page context via CDP `Runtime.evaluate`.
|
|
10
|
+
- [x] Resolves accessible roles (`button`, `link`, `textbox`, `combobox`, `checkbox`, `heading`, etc.) and accessible names.
|
|
11
|
+
- [x] Assigns sequential Ref-IDs (`[#1]`, `[#2]`) and stores element references & bounding rect coordinates in `window.__AG_REGISTRY__`.
|
|
12
|
+
- [x] Returns compact semantic text snapshot representation.
|
|
13
|
+
- [x] Detects stale Ref-IDs upon query and returns structured `ElementNotFoundError` with fuzzy match suggestions.
|
|
14
|
+
- [x] Integration tests passing against local HTML test fixtures.
|