poltertab 1.3.0 → 1.5.0-beta.1
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.
- package/README.md +77 -23
- package/mcp-server/bridge.js +593 -0
- package/mcp-server/config.js +85 -0
- package/mcp-server/extract-all.js +159 -0
- package/mcp-server/index.js +68 -931
- package/mcp-server/memory.js +81 -0
- package/mcp-server/output.js +103 -0
- package/mcp-server/tools.js +439 -0
- package/package.json +1 -2
- package/skills/browser-navigation-strategy/SKILL.md +162 -34
package/README.md
CHANGED
|
@@ -82,14 +82,13 @@ The wizard targets Claude Code. Any other MCP client works too — register
|
|
|
82
82
|
Running from a clone instead of npm? Point the client at the file directly:
|
|
83
83
|
`{"command": "node", "args": ["/absolute/path/to/poltertab/mcp-server/index.js"]}`
|
|
84
84
|
|
|
85
|
-
> **Legacy:** This extension began as a ZeroClaw plugin and still speaks ZeroClaw's built-in REST bridge (`backend = "bridge"` in `~/.zeroclaw/config.toml`). That path is optional and not required for the MCP setup above.
|
|
86
|
-
|
|
87
85
|
## Supported MCP Tools
|
|
88
86
|
|
|
89
|
-
The server exposes
|
|
87
|
+
The server exposes 23 tools to the LLM (all prefixed with `browser_`). Every
|
|
90
88
|
page-facing tool also takes optional `tabId` and `session` to target a specific
|
|
91
|
-
tab — omit both and PolterTab uses the last tab it navigated
|
|
92
|
-
|
|
89
|
+
tab — omit both and PolterTab uses the last tab it navigated. The first
|
|
90
|
+
`browser_navigate` of a session opens its own tab rather than commandeering the
|
|
91
|
+
one you are looking at.
|
|
93
92
|
|
|
94
93
|
### Page actions
|
|
95
94
|
|
|
@@ -104,15 +103,77 @@ your active tab.
|
|
|
104
103
|
|
|
105
104
|
### Reading the page
|
|
106
105
|
|
|
106
|
+
Every tool here takes `output_file`, which writes the payload to
|
|
107
|
+
`~/.poltertab/downloads/` and returns only a summary — row count, field names,
|
|
108
|
+
fill rates and two sample records. Use it for anything bigger than a glance; the
|
|
109
|
+
raw payload should never be the default path into a context window.
|
|
110
|
+
|
|
107
111
|
| Action | Parameters | Description |
|
|
108
112
|
| -------- | ----------- | ------------- |
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
113
|
+
| `browser_extract` | `record`, `fields` (required), `anchor`, `max_text`, `probe`, `output_file` | Repeating records with fields grouped per record — see below |
|
|
114
|
+
| `browser_extract_all` | `url_template`, `record`, `fields` (required), `key`, `limit`, `offset`, `start_page`, `max_pages`, `fill_tolerance`, `output_file` | Paginate and extract in one call, no model round-trip per page |
|
|
115
|
+
| `browser_scrape` | `selector`, `attribute`, `multiple`, `fields`, `max_text`, `output_file` | Scrape given elements, or the whole page. `attribute` accepts `text` as well as any attribute name; `href`/`src` come back absolute. `fields` picks which parts of a full-page scrape to return — `["meta","jsonld"]` gets og: tags and schema.org blobs without 50KB of body text |
|
|
116
|
+
| `browser_snapshot` | `interactive_only`, `max_nodes`, `max_depth`, `output_file` | Ref-tagged tree of visible and interactive nodes, capped at 400 |
|
|
117
|
+
| `browser_get_text` | `selector` (required), `max_text`, `output_file` | Text content of one element. Flags it in the result when text was cut |
|
|
112
118
|
| `browser_get_title` | — | Page title and URL |
|
|
113
119
|
| `browser_get_url` | — | Page URL and title |
|
|
114
120
|
| `browser_screenshot` | — | Visible tab as base64 PNG (focuses the tab) |
|
|
115
121
|
|
|
122
|
+
### Extracting records
|
|
123
|
+
|
|
124
|
+
`browser_scrape` returns a flat list, which loses track of which value belongs
|
|
125
|
+
to which record. That is fine for one element and dangerous for ninety: the
|
|
126
|
+
moment a field is optional, a flat scrape of names against phone numbers shifts
|
|
127
|
+
every later phone up a row and mis-assigns the rest of the page.
|
|
128
|
+
|
|
129
|
+
`browser_extract` takes a record container and resolves each field *inside* it:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"record": ".agent-card",
|
|
134
|
+
"fields": {
|
|
135
|
+
"name": { "sel": "a.agent-card-name", "get": "text" },
|
|
136
|
+
"url": { "sel": "a.agent-card-name", "get": "href" },
|
|
137
|
+
"phone": { "sel": "a[href^='tel:']", "get": "href", "strip": "tel:" },
|
|
138
|
+
"socials": { "sel": "a.social-button", "get": "href", "many": true }
|
|
139
|
+
},
|
|
140
|
+
"anchor": "url",
|
|
141
|
+
"output_file": "agents.csv"
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- **A missing field yields `null`.** It never shifts.
|
|
146
|
+
- `sel` is relative to the record root; omit it (or use `"."`) for the root itself.
|
|
147
|
+
- `get` is `text` (default), `href`, `src`, or any attribute name.
|
|
148
|
+
- `anchor` names the field a real record always has. Records missing it — the
|
|
149
|
+
placeholder cards that pad the last page of a listing — are dropped and
|
|
150
|
+
counted in `dropped` rather than emitted as rows of nulls.
|
|
151
|
+
|
|
152
|
+
The result reports what happened, so an empty column cannot pass for an answer:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
{ "count": 12, "dropped": 0,
|
|
156
|
+
"fill_rates": { "name": 12, "phone": 10, "socials": 0 },
|
|
157
|
+
"warnings": ["socials: 0/12 within record scope, but 40 matches page-wide
|
|
158
|
+
for a.social-button — record boundary likely too narrow"] }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
That warning is the loosening probe. Any field that comes back entirely empty is
|
|
162
|
+
re-checked page-wide, because the container that *looks* like the record often
|
|
163
|
+
excludes a sibling holding some of the fields — and "0 socials" reads as "these
|
|
164
|
+
people have no social accounts" rather than "your selector scope is wrong".
|
|
165
|
+
|
|
166
|
+
**`browser_extract_all`** takes the same spec plus a `url_template` containing
|
|
167
|
+
`{page}`, and runs the loop itself. It dedups on `key` and halts on the first of:
|
|
168
|
+
`limit_reached`, `empty_page`, `duplicate_page` (a page whose records repeat an
|
|
169
|
+
earlier one — what a site does when it ignores an unrecognised page-size
|
|
170
|
+
param), `fill_rate_deviation` (a page whose fill rates collapse against the
|
|
171
|
+
first page's baseline, which is how a spec learned on page 1 quietly degrades on
|
|
172
|
+
page 10), or `max_pages`. It always reports which condition fired and returns
|
|
173
|
+
everything collected up to that point, so a halt never costs you the run.
|
|
174
|
+
|
|
175
|
+
`.csv` and `.jsonl` output files are written in those formats.
|
|
176
|
+
|
|
116
177
|
### Network capture
|
|
117
178
|
|
|
118
179
|
For sites whose DOM is obfuscated or virtualized, read the JSON driving the UI
|
|
@@ -148,20 +209,14 @@ next use.
|
|
|
148
209
|
|
|
149
210
|
Selectors are resolved in order:
|
|
150
211
|
|
|
151
|
-
1. **
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
`zc-browser.sh` lets you send commands directly to the legacy ZeroClaw REST bridge for testing (only relevant if you use the optional ZeroClaw bridge path above):
|
|
212
|
+
1. **Snapshot ref** — `@e12`, as returned by `browser_snapshot`. Resolves to the
|
|
213
|
+
exact node the snapshot described, and beats a generated class chain.
|
|
214
|
+
2. **CSS selector** — `#id`, `.class`, `div > span`
|
|
215
|
+
3. **XPath** — `//div[@class="foo"]`
|
|
216
|
+
4. **Text match** — exact text content of an element
|
|
158
217
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
./zc-browser.sh scrape selector="h1"
|
|
162
|
-
./zc-browser.sh get_title
|
|
163
|
-
./zc-browser.sh health
|
|
164
|
-
```
|
|
218
|
+
A miss on all four is retried with shadow-root piercing, so a selector that only
|
|
219
|
+
exists inside a web component still resolves.
|
|
165
220
|
|
|
166
221
|
## Troubleshooting & Options
|
|
167
222
|
|
|
@@ -225,7 +280,6 @@ poltertab/
|
|
|
225
280
|
│ ├── popup.html/js # Connected-agents UI
|
|
226
281
|
│ ├── options.html/js # Port configuration UI
|
|
227
282
|
│ └── icons/
|
|
228
|
-
├── zc-browser.sh # CLI wrapper (legacy)
|
|
229
283
|
└── README.md
|
|
230
284
|
```
|
|
231
285
|
|
|
@@ -271,7 +325,7 @@ Nothing the server accumulates is stored inside the package:
|
|
|
271
325
|
```
|
|
272
326
|
~/.poltertab/
|
|
273
327
|
├── navigation_memory/ # Per-domain obstacles and fixes (browser_save_site_memory)
|
|
274
|
-
└── downloads/ # output_file payloads from
|
|
328
|
+
└── downloads/ # output_file payloads from any read tool
|
|
275
329
|
```
|
|
276
330
|
|
|
277
331
|
This matters for a global npm install. The package sits in `node_modules/`, so
|