Skip to content

Commit a7e1beb

Browse files
committed
fix: address 22 user-facing frictions across CLI, REST, MCP, and browser tools
Node version enforcement (bin/schrute.cjs), test DB isolation via SCHRUTE_DATA_DIR, serve auth token hint, skills delete --yes, empty body parser, CLI rate-limit auto-retry, MCP HTTP admin opt-in, import preview with transactional writes + policy persistence, OpenAPI probe paths, progress indicators, doctor audit chain downgraded to warning, browser_fill_form schema, v0 deprecation header, --url/--token on subcommands, search ranking lexical filter, config get --reveal, skills list --status, batch execute rate-limit retry, README MCP note.
1 parent 57832c6 commit a7e1beb

27 files changed

Lines changed: 1241 additions & 267 deletions

README.md

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
Teach your AI a website once. After that, it replays the same backend requests directly — no browser needed.
99
</p>
1010

11-
Schrute watches real browser traffic, turns repeatable actions into MCP tools, and reuses browser auth when needed. No hand-written API integration, and often no API keys, because Schrute learns from the requests your browser already knows how to make.
11+
Schrute watches real browser traffic, learns the underlying API patterns, and turns them into replayable skills you can call via **MCP**, **REST API**, or **CLI**. No hand-written API integration, and often no API keys, because Schrute learns from the requests your browser already knows how to make.
1212

13-
- Faster repeated tasks
13+
- Faster repeated tasks — browser once, direct HTTP after
1414
- Less brittle than selector-only browser automation
15-
- No hand-written API integration for every site
15+
- Works through Cloudflare-protected sites via real-Chrome fallback
16+
- **MCP** · **REST API** · **CLI** — use from AI agents, any language, or your terminal
1617

1718
Measured on repeated runs of tested workflows:
1819

@@ -44,7 +45,9 @@ npm install -g schrute
4445
schrute setup
4546
```
4647

47-
Add to your MCP client config (Claude Code, Cursor, Windsurf, Cline, or any MCP client):
48+
### MCP (AI agents)
49+
50+
Add to your MCP client config (Claude Code, Cursor, Windsurf, Cline):
4851

4952
```json
5053
{
@@ -57,7 +60,23 @@ Add to your MCP client config (Claude Code, Cursor, Windsurf, Cline, or any MCP
5760
}
5861
```
5962

60-
Your AI agent now has `schrute_explore`, `schrute_record`, and 40+ other tools.
63+
> **Note:** MCP HTTP requires `Accept: application/json, text/event-stream` header.
64+
65+
### CLI
66+
67+
```bash
68+
schrute explore https://example.com # Open browser, record traffic
69+
schrute record --name my-action # Mark an action
70+
schrute stop # Generate skills
71+
schrute execute my_skill.v1 # Replay the learned API call
72+
```
73+
74+
### REST API
75+
76+
```bash
77+
schrute serve --http --port 3000 # Start HTTP server
78+
curl http://127.0.0.1:3000/api/sites # Use from any language
79+
```
6180

6281
## See it work in 60 seconds
6382

@@ -121,7 +140,7 @@ Every example below was recorded on 2026-03-17, on macOS (Apple Silicon), over W
121140

122141
**Returned:** `{"origin": "49.43.xxx.x"}`
123142

124-
**What changed after learning:** Four browser navigations became four replayable MCP tools. Each call returns JSON directly — no page load, no DOM parsing, no selectors.
143+
**What changed after learning:** Four browser navigations became four replayable skills — callable via MCP tools, REST API, or CLI. Each call returns JSON directly — no page load, no DOM parsing, no selectors.
125144

126145
---
127146

@@ -150,7 +169,7 @@ Every example below was recorded on 2026-03-17, on macOS (Apple Silicon), over W
150169
- Time: **1,033ms**
151170
- Returned: Full Wikipedia search results (10 articles with titles, snippets, page IDs)
152171

153-
**What changed after learning:** A single MCP tool that takes a search query and returns structured Wikipedia results. The agent calls `schrute_execute({ skillId: "en_wikipedia_org.get_api_php.v1", params: { "query.srsearch": "quantum computing" } })` instead of navigating Wikipedia's UI.
172+
**What changed after learning:** A single skill that takes a search query and returns structured Wikipedia results. Call it via MCP (`schrute_execute`), REST API (`POST /api/sites/en.wikipedia.org/skills/get_api_php`), or CLI (`schrute execute en_wikipedia_org.get_api_php.v1 "query.srsearch=quantum computing"`) — instead of navigating Wikipedia's UI.
154173

155174
---
156175

@@ -190,25 +209,39 @@ Every example below was recorded on 2026-03-17, on macOS (Apple Silicon), over W
190209

191210
**Site:** www.coingecko.com (finance/crypto)
192211

193-
**Why this workflow matters:** Shows how Schrute handles sites behind Cloudflare. Direct HTTP fails — the skill stays at Tier 3 (browser-proxied) and uses the browser's Cloudflare clearance cookies.
212+
**Why this workflow matters:** CoinGecko is protected by Cloudflare Turnstile, which blocks headless browsers. When Schrute detects a Cloudflare challenge, it launches a real Chrome browser to attempt clearance, then retries the API call. If the challenge auto-clears (common for Turnstile), execution succeeds automatically. If manual intervention is needed, Schrute returns a 202 with a hint to solve the challenge in the opened browser.
194213

195214
**First run:**
196215
- Path: `schrute explore` → agent navigates CoinGecko, clicks on Bitcoin, views price charts → `schrute stop`
197-
- Pipeline result: **5 skills generated** from the captured API calls
216+
- Pipeline result: **5+ skills generated** from captured API traffic (exact count depends on page traffic)
198217

199218
**Learned skills:**
200219
- `www_coingecko_com.get_24_hours_json.v1``GET /price_charts/bitcoin/usd/24_hours.json`
201-
- `www_coingecko_com.get_max_longer_cache_json.v1``GET /price_charts/bitcoin/usd/max_longer_cache.json`
202-
- `www_coingecko_com.get_insight_annotations.v1``GET /price_charts/bitcoin/insight_annotations`
203-
- Plus 2 more (user info, OTP center)
220+
- `www_coingecko_com.get_coins.v1``GET /price_charts/.../coins`
221+
- `www_coingecko_com.get_csrf_meta_json.v1``GET /accounts/csrf_meta.json`
222+
- Plus user info, OTP center, assets endpoints
204223
- Auth used: Cloudflare cookies (browser session)
205224
- Safety class: read-only
206225

207-
**Direct HTTP attempt:** Failed after 9,129ms — Cloudflare returns a challenge page, not JSON.
226+
**Execution with live-Chrome recovery:**
208227

209-
**Why it still works:** At Tier 3, Schrute executes the `fetch()` inside the browser context, which already has Cloudflare clearance cookies. The request succeeds where direct HTTP cannot. This skill will not promote to Tier 1 because the endpoint requires Cloudflare cookies — Schrute detects this and keeps it at the browser-proxied tier.
228+
```
229+
schrute execute www_coingecko_com.get_24_hours_json.v1 --yes
230+
```
210231

211-
**What this shows:** Not every skill promotes to direct HTTP. Schrute adapts to the site's security model instead of breaking against it.
232+
| Step | What happens | Time |
233+
|------|-------------|------|
234+
| 1 | Browser-proxied fetch (Playwright) | Blocked by Cloudflare |
235+
| 2 | Full-browser fetch (Playwright) | Blocked by Cloudflare |
236+
| 3 | Schrute detects Cloudflare challenge page | ~0ms |
237+
| 4 | Launches real Chrome, navigates to CoinGecko | Challenge clears (in tested run) |
238+
| 5 | Retries API call through live Chrome session | **Success** |
239+
240+
- Total latency: **~7,400ms** (includes Chrome launch + challenge clearance)
241+
- Returned: 288 BTC/USD price data points (24h) with timestamps and volumes
242+
- Recovery status: `live_chrome_opened`
243+
244+
**What this shows:** Schrute handles Cloudflare-protected sites by escalating to real Chrome when headless Playwright is blocked. In the tested run, Turnstile auto-cleared and execution succeeded on the first call. Sites with interactive CAPTCHAs may require manual clearance in the opened browser window.
212245

213246
---
214247

@@ -235,17 +268,9 @@ Every example below was recorded on 2026-03-17, on macOS (Apple Silicon), over W
235268
| dog.ceo | `get_all` | 551ms ||| None | 3/6 |
236269
| dog.ceo | `get_random` | 558ms | 472ms || None | 3/6 |
237270
| en.wikipedia.org | `get_api_php` | 1,033ms ||| None | 0/4 |
238-
| www.coingecko.com | `get_24_hours_json` | 9,129ms (fail) ||| Cloudflare cookies ||
239-
240-
All runs at Tier 3 (browser-proxied). Skills promote to Tier 1 (direct HTTP, ~5-50ms) after 5+ consecutive successful validations. Cloudflare-protected skills remain at Tier 3.
271+
| www.coingecko.com | `get_24_hours_json` | ~7,400ms ||| CF cookies ||
241272

242-
**Methodology:**
243-
- Machine: MacBook (Apple Silicon)
244-
- Network: WiFi, India
245-
- Browser engine: Playwright Chromium
246-
- Cache state: warm (browser session open)
247-
- Timing: `latencyMs` field from Schrute execution result
248-
- Date tested: 2026-03-17
273+
All runs at Tier 3 (browser-proxied) except CoinGecko which uses live-Chrome recovery. Skills promote to Tier 1 (direct HTTP, ~5-50ms) after 5+ consecutive successful validations. Cloudflare-protected skills remain at Tier 3 with real-Chrome fallback.
249274

250275
## Where Schrute works best
251276

@@ -287,7 +312,7 @@ Before a learned skill executes, Schrute enforces:
287312

288313
Dangerous browser tools (`browser_evaluate`, `browser_run_code`) are blocked entirely.
289314

290-
For the full 9-gate security model, see [SECURITY.md](SECURITY.md).
315+
For the full security model, see [SECURITY.md](SECURITY.md).
291316

292317
## Auth, cookies, and storage
293318

bin/schrute.cjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env node
2+
'use strict';
3+
const major = parseInt(process.version.slice(1), 10);
4+
if (major < 22) {
5+
console.error(`Error: Node >= 22 required (found ${process.version}). Run: nvm use 22`);
6+
process.exit(1);
7+
}
8+
import('../dist/index.js');

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
}
1616
},
1717
"bin": {
18-
"schrute": "dist/index.js"
18+
"schrute": "bin/schrute.cjs"
1919
},
2020
"scripts": {
21-
"prebuild": "node scripts/sync-version.js",
21+
"prebuild": "node -e \"if(parseInt(process.version.slice(1))<22){console.error('Node>=22 required');process.exit(1)}\" && node scripts/sync-version.js",
2222
"build": "tsc -p tsconfig.json",
2323
"prepublishOnly": "node scripts/sync-version.js && npm run build",
2424
"build:native": "cd native && cargo build --release && cp target/release/libschrute_native.dylib index.node 2>/dev/null; cp target/release/schrute_native.dll index.node 2>/dev/null; cp target/release/libschrute_native.so index.node 2>/dev/null; echo 'Native build complete'",
@@ -27,10 +27,10 @@
2727
"test:watch": "vitest",
2828
"test:coverage": "vitest run --coverage",
2929
"lint": "tsc --noEmit",
30-
"start": "node dist/index.js",
31-
"serve": "node dist/index.js serve",
32-
"setup": "node dist/index.js setup",
33-
"doctor": "node dist/index.js doctor",
30+
"start": "node bin/schrute.cjs",
31+
"serve": "node bin/schrute.cjs serve",
32+
"setup": "node bin/schrute.cjs setup",
33+
"doctor": "node bin/schrute.cjs doctor",
3434
"rebuild:native": "bash scripts/rebuild-native.sh",
3535
"build:binary": "npm run build && pkg dist/index.js --config pkg.config.json",
3636
"build:binary:macos": "npm run build:binary -- --target node22-macos-arm64",
@@ -91,6 +91,7 @@
9191
"schrute"
9292
],
9393
"files": [
94+
"bin/",
9495
"dist/",
9596
".claude-plugin/",
9697
"commands/",

0 commit comments

Comments
 (0)