From 98d59b65171f0307ceeb1bbe4df3b60c3180f4fb Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Mon, 17 Aug 2026 15:10:14 +0200 Subject: [PATCH 1/8] docs(ruby-gem): align MCP and capture guides with AutoFallback Document YAML capture, apply's zero-item ship gate, inspect recon, and mcp.json Botasaurus env so agents stop teaching Faraday-only auto. --- .../ruby-gem/guides/ai-agent-workflows.mdx | 33 ++++++---- .../guides/capturing-feed-configs.mdx | 41 +++++------- .../docs/ruby-gem/reference/mcp-server.mdx | 63 ++++++++++--------- 3 files changed, 72 insertions(+), 65 deletions(-) diff --git a/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx b/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx index df090b5b..a13e92af 100644 --- a/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx +++ b/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx @@ -12,8 +12,9 @@ import { Code } from "@astrojs/starlight/components"; Without MCP, agents must invoke shell commands, manage output buffers, and parse unstructured text. With MCP: - The agent automatically discovers available tools, arguments, and return types. -- Responses are structured (JSON Feed objects, configuration schemas, and RSS XML). +- Responses are structured (JSON Feed items, YAML configs, RSS XML). - Agents can inspect, capture, validate, and verify feeds iteratively in a closed feedback loop. +- `html2rss://runtime` reports whether Botasaurus is configured in this MCP process (boolean only). ## Client Setup @@ -41,7 +42,10 @@ Add `html2rss` to your Cursor MCP settings (`~/.cursor/mcp.json` or `.cursor/mcp "mcpServers": { "html2rss": { "command": "mise", - "args": ["exec", "--", "html2rss", "mcp"] + "args": ["exec", "--", "html2rss", "mcp"], + "env": { + "BOTASAURUS_SCRAPER_URL": "http://127.0.0.1:4010" + } } } } @@ -61,7 +65,10 @@ Add `html2rss` to your Claude Desktop configuration (`~/Library/Application Supp "mcpServers": { "html2rss": { "command": "mise", - "args": ["exec", "--", "html2rss", "mcp"] + "args": ["exec", "--", "html2rss", "mcp"], + "env": { + "BOTASAURUS_SCRAPER_URL": "http://127.0.0.1:4010" + } } } } @@ -71,23 +78,25 @@ Add `html2rss` to your Claude Desktop configuration (`~/Library/Application Supp ## Autonomous Workflow Patterns +**Budget:** `scrape_url` is one call. Durable config is `capture_config` → `validate_config` → `apply_config`. Call `inspect_url` only when the result is weak or you need recon (final URL, status, https→http, native RSS/Atom). + ### Pattern A: One-Shot Content Scraping When an agent needs articles immediately without saving a feed configuration: -1. The agent calls `scrape_url` with the target URL. -2. `html2rss` runs auto-source extraction (Schema.org, JSON state, semantic HTML) and returns a JSON Feed items array. -3. If the page is protected or rendered with JavaScript, the agent calls `inspect_url` to diagnose the structure, then retries `scrape_url` with `strategy: "botasaurus"`. +1. Call `scrape_url` with `strategy: "auto"`. Auto already runs Faraday then Botasaurus. +2. An empty item list is still success for articles-now. Read `html2rss://runtime` if `botasaurus_configured` is false. +3. Call `inspect_url` only if the result is weak or you need recon. Do not retry `scrape_url` with explicit `faraday` after auto. ### Pattern B: Iterative Feed Config Authoring When an agent is tasked with creating a durable YAML feed configuration: -1. **Inspect:** The agent calls `inspect_url` to check content type, SST node counts, and eligible scrapers. -2. **Capture:** The agent runs `capture_config` to derive CSS selectors for items, title, link, and description. -3. **Refine:** The agent reviews the derived selectors or asks the human user for domain-specific adjustments. -4. **Validate:** The agent passes the configuration to `validate_config` to verify schema conformance. -5. **Apply:** The agent tests the final configuration with `apply_config` to produce and inspect live RSS XML. +1. **Capture:** Call `capture_config`. It returns YAML (`items` + `enhance: true`). Check `_meta.articles_count` and `has_selectors`. +2. **Recon (optional):** Call `inspect_url` for `final_url`, `status`, `scheme_downgrade`, and `alternate_feeds` if the draft is weak. +3. **Rewrite:** If the destination is html2rss-configs, add `directory.topics` and explicit channel `title`/`url`. Strive to keep `enhance: true` (set `false` only when chrome leaks into items). +4. **Validate:** Pass `yaml` (or `config`) to `validate_config`. Exactly one of those arguments. +5. **Apply:** Call `apply_config`. `isError` plus `_meta.item_count: 0` means the config is not shippable — channel title in the XML is not success. ## JavaScript-Rendered Sites (Botasaurus) @@ -100,4 +109,4 @@ For dynamic JavaScript single-page applications or sites protected by anti-bot m lang="bash" /> -Ensure `BOTASAURUS_SCRAPER_URL` is accessible (typically `http://127.0.0.1:4010`) in the environment where the MCP server runs. Agents can then pass `strategy: "botasaurus"` to `scrape_url`, `inspect_url`, and `capture_config`. +Put `BOTASAURUS_SCRAPER_URL` (typically `http://127.0.0.1:4010`) in the MCP server `env` block above — a shell export does not reach the Cursor/Claude MCP process. Confirm with `html2rss://runtime` (`botasaurus_configured: true`). AutoFallback then hops without a second tool call. diff --git a/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx b/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx index 9b8bb7ce..aa153525 100644 --- a/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx +++ b/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx @@ -5,11 +5,11 @@ description: Derive a reusable YAML feed config from a URL with html2rss capture import { Aside, Code } from "@astrojs/starlight/components"; -`html2rss capture` analyzes a page through the auto-source pipeline and prints a reusable feed config with derived CSS selectors. Use it when you want a first draft faster than hand-writing selectors from scratch. +`html2rss capture` analyzes a page through the auto-source pipeline and prints a reusable YAML draft with an **items selector plus `enhance: true`** — not title/url/description selector soup. `enhance: true` fills missing article fields at feed-build time. ## When to Capture @@ -57,8 +57,7 @@ See the [CLI reference](/ruby-gem/reference/cli-reference/#capture) for the full Date: Mon, 17 Aug 2026 15:34:57 +0200 Subject: [PATCH 2/8] docs(ruby-gem): document MCP envelope results Point MCP guides at ok/next_step/guidance/payload so agents stop reading _meta or a raw items array. --- .../ruby-gem/guides/ai-agent-workflows.mdx | 8 ++-- .../guides/capturing-feed-configs.mdx | 2 +- .../docs/ruby-gem/reference/mcp-server.mdx | 39 ++++++++++++------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx b/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx index a13e92af..91c9cd0c 100644 --- a/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx +++ b/src/content/docs/ruby-gem/guides/ai-agent-workflows.mdx @@ -12,7 +12,7 @@ import { Code } from "@astrojs/starlight/components"; Without MCP, agents must invoke shell commands, manage output buffers, and parse unstructured text. With MCP: - The agent automatically discovers available tools, arguments, and return types. -- Responses are structured (JSON Feed items, YAML configs, RSS XML). +- Responses are structured envelopes (`ok`, `next_step`, `guidance`, `payload`) — articles in `payload.items`, YAML in `payload.yaml`, RSS in `payload.rss`. - Agents can inspect, capture, validate, and verify feeds iteratively in a closed feedback loop. - `html2rss://runtime` reports whether Botasaurus is configured in this MCP process (boolean only). @@ -85,18 +85,18 @@ Add `html2rss` to your Claude Desktop configuration (`~/Library/Application Supp When an agent needs articles immediately without saving a feed configuration: 1. Call `scrape_url` with `strategy: "auto"`. Auto already runs Faraday then Botasaurus. -2. An empty item list is still success for articles-now. Read `html2rss://runtime` if `botasaurus_configured` is false. +2. An empty item list is still success for articles-now. Read `payload.items`. Follow `next_step` (including `read_runtime` / `html2rss://runtime` if Botasaurus is unset). 3. Call `inspect_url` only if the result is weak or you need recon. Do not retry `scrape_url` with explicit `faraday` after auto. ### Pattern B: Iterative Feed Config Authoring When an agent is tasked with creating a durable YAML feed configuration: -1. **Capture:** Call `capture_config`. It returns YAML (`items` + `enhance: true`). Check `_meta.articles_count` and `has_selectors`. +1. **Capture:** Call `capture_config`. YAML is `payload.yaml` (`items` + `enhance: true`). Check `payload.articles_count` and `has_selectors`. 2. **Recon (optional):** Call `inspect_url` for `final_url`, `status`, `scheme_downgrade`, and `alternate_feeds` if the draft is weak. 3. **Rewrite:** If the destination is html2rss-configs, add `directory.topics` and explicit channel `title`/`url`. Strive to keep `enhance: true` (set `false` only when chrome leaks into items). 4. **Validate:** Pass `yaml` (or `config`) to `validate_config`. Exactly one of those arguments. -5. **Apply:** Call `apply_config`. `isError` plus `_meta.item_count: 0` means the config is not shippable — channel title in the XML is not success. +5. **Apply:** Call `apply_config`. `isError` plus `payload.item_count: 0` means the config is not shippable — channel title in the XML is not success. ## JavaScript-Rendered Sites (Botasaurus) diff --git a/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx b/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx index aa153525..180f46d6 100644 --- a/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx +++ b/src/content/docs/ruby-gem/guides/capturing-feed-configs.mdx @@ -90,7 +90,7 @@ Capture focuses on: It does not invent per-field title/url/description selectors, author, `published_at`, categories, or enclosure selectors. `enhance: true` fills missing article fields via the HTML article extractor at feed-build time. -MCP `capture_config` returns the same YAML. `validate_config` / `apply_config` accept that YAML string (XOR a config hash). `apply_config` is `isError` when the feed has zero items. +MCP `capture_config` returns that YAML in `payload.yaml`. `validate_config` / `apply_config` accept the YAML string (XOR a config hash). `apply_config` is `isError` when the feed has zero items (`payload.item_count`). ## Recommended Follow-Up diff --git a/src/content/docs/ruby-gem/reference/mcp-server.mdx b/src/content/docs/ruby-gem/reference/mcp-server.mdx index 624aa6bf..5c7b2ed5 100644 --- a/src/content/docs/ruby-gem/reference/mcp-server.mdx +++ b/src/content/docs/ruby-gem/reference/mcp-server.mdx @@ -43,7 +43,17 @@ The MCP server supports two transport mechanisms: Read `html2rss://runtime` for `botasaurus_configured` (boolean only). Set `BOTASAURUS_SCRAPER_URL` on the **MCP process** env (see [AI agent workflows](/ruby-gem/guides/ai-agent-workflows/)). -**Tool-call budget:** `scrape_url` = 1 call. Durable config = `capture_config` → `validate_config` → `apply_config`. Call `inspect_url` only when scrape/capture is weak or you need recon. +**Tool-call budget:** `scrape_url` = 1 call. Durable config = `capture_config` → `validate_config` → `apply_config`. Call `inspect_url` only when scrape/capture is weak or you need recon. Follow envelope `next_step` / `guidance` rather than guessing the next tool. + +## Result envelope + +Every tool returns the same JSON object in both the text content and `structuredContent` (there is no `_meta` quality channel): + +```json +{ "ok": true, "next_step": "validate_config", "guidance": "...", "payload": {} } +``` + +`isError` matches `ok: false`. Hosts that parsed scrape text as a raw JSON array must read `payload.items` instead. ## Tools Reference @@ -58,9 +68,8 @@ One-shot article extraction as JSON Feed items. Use when you need articles immed - `strategy` _(string, optional)_: Request strategy (`auto`, `faraday`, `botasaurus`). Default: `auto`. - `limit` _(integer, optional)_: Maximum articles to keep. Default: `25`. - `items_selector` _(string, optional)_: CSS selector hint for items. -- **Return value:** JSON string containing an array of article objects (`title`, `url`, `description`, `published_at`, `author`, `image`, `categories`). -- **Metadata (`_meta`):** Includes `total`, `requested_strategy`, `channel_title`, and Status tallies. -- **Error handling:** Unhandled exceptions return `isError: true`. An empty item list is still success (articles-now is not a ship gate). +- **Return value:** Envelope with `payload.items` (JSON Feed article objects: `title`, `url`, `description`, `published_at`, `author`, `image`, `categories`). Also `payload.total`, `requested_strategy`, `channel_title`, and `admission_drops` when present. +- **Error handling:** Unhandled exceptions return `isError: true` with `payload.class` / `payload.message`. An empty item list is still success (`ok: true`; follow `next_step`, which may be `inspect_url` or `read_runtime`). ### `inspect_url` @@ -69,12 +78,13 @@ Diagnostic page analysis plus recon from the same fetch. `auto` uses Faraday; pi - **Parameters:** - `url` _(string, required)_: Source page URL. - `strategy` _(string, optional)_: Request strategy (`auto`, `faraday`, `botasaurus`). Default: `auto`. -- **Return value:** Pretty-printed JSON object with diagnostic details: +- **Return value:** Envelope whose `payload` is the diagnostic object: - `requested_url` / `final_url`: requested URL vs `Response#url` after redirects. - `status`: HTTP status when the strategy reports one. - `scheme_downgrade`: `true` when the fetch went https → http. - `alternate_feeds`: `rel=alternate` RSS/Atom links from the document head (no `/feed` path guessing). - `content_type` / `html_response` / `scraper_eligibility` / `sst_stats` / `sst.segment_stats`. +- **next_step:** `done` when native alternate feeds exist, `capture_config` when articles were found, otherwise `scrape_url`. ### `capture_config` @@ -84,8 +94,8 @@ Analyzes a target URL and derives a reusable YAML draft (`items` selector + `enh - `url` _(string, required)_: Source page URL. - `strategy` _(string, optional)_: Request strategy (`auto`, `faraday`, `botasaurus`). Default: `auto`. - `items_selector` _(string, optional)_: Optional CSS selector hint for items. -- **Return value:** YAML document (string keys). Treat as a draft: catalog feeds still need `directory.topics` and explicit channel `title`/`url`. Strive to keep `enhance: true`. -- **Metadata (`_meta`):** Includes `articles_count`, `channel_title`, `has_selectors`, and `requested_strategy`. +- **Return value:** Envelope with `payload.yaml` (string keys; same serializer as CLI `html2rss capture`). Treat as a draft: catalog feeds still need `directory.topics` and explicit channel `title`/`url`. Strive to keep `enhance: true`. +- **payload:** Also `articles_count`, `channel_title`, `has_selectors`, and `requested_strategy`. Follow `next_step` (`validate_config` or `inspect_url`). ### `validate_config` @@ -94,8 +104,8 @@ Validates a feed configuration against the official `html2rss` JSON schema (`htm - **Parameters** (exactly one of `config` or `yaml`): - `config` _(object, optional)_: Feed configuration object. - `yaml` _(string, optional)_: Feed configuration YAML (same form as CLI capture / catalog files). -- **Return value:** Text response `Config is valid.` on success. -- **Error handling:** Returns `isError: true` with a serialized JSON error dictionary if schema validation fails, or if both/neither argument is provided. +- **Return value:** Envelope with empty `payload` on success (`next_step`: `apply_config`). +- **Error handling:** Returns `isError: true` with `payload.errors` if schema validation fails, or `next_step: validate_config` if both/neither argument is provided. ### `apply_config` @@ -105,9 +115,8 @@ Executes a validated feed configuration against a page and returns RSS 2.0 XML. - `url` _(string, required)_: Source page URL (populates `channel.url` if omitted from config). - `config` _(object, optional)_: Feed configuration object (XOR `yaml`). - `yaml` _(string, optional)_: Feed configuration YAML (XOR `config`). -- **Return value:** RSS 2.0 XML string. -- **Metadata (`_meta`):** `item_count` from the rendered RSS `items` (not a regex over the XML). -- **Error handling:** `isError: true` when the scrape produced no items, or on unhandled exceptions. +- **Return value:** Envelope with `payload.rss` (RSS 2.0 XML) and `payload.item_count` from the rendered RSS `items` (not a regex over the XML). +- **Error handling:** `isError: true` when the scrape produced no items (`payload.item_count: 0`), or on unhandled exceptions. ## Resources Reference @@ -117,7 +126,7 @@ The MCP server exposes reference data under the `html2rss://` URI scheme: | :-------------------------- | :----------------- | :----------------------------------------------------------------------------------------------------------------------- | | **`html2rss://schema`** | `application/json` | The complete JSON Schema for `html2rss` feed configurations, including selector rules, extractors, and request controls. | | **`html2rss://extractors`** | `application/json` | Alphabetical list of all registered extractor names (`attribute`, `html`, `href`, `text`, `static`, etc.). | -| **`html2rss://strategies`** | `application/json` | List of all registered request strategies (`auto`, `faraday`, `botasaurus`, `local_file`). | +| **`html2rss://strategies`** | `application/json` | Published MCP request strategies (`auto`, `faraday`, `botasaurus`). Does not list `local_file`. | | **`html2rss://runtime`** | `application/json` | Boolean `botasaurus_configured` from process env. Never includes the scraper URL. | ## Prompts Reference @@ -135,10 +144,10 @@ One `scrape_url` call with `strategy: auto` (AutoFallback already hops). Call `i Four-step workflow for a durable feed config: -1. Call `capture_config` — YAML draft. Check `_meta.articles_count` and `has_selectors`. Strive `enhance: true`. +1. Call `capture_config` — YAML is `payload.yaml`. Check `payload.articles_count` and `has_selectors`. Strive `enhance: true`. 2. If weak or you need recon, call `inspect_url`. Auto already hops to Botasaurus. 3. Call `validate_config` with `yaml` (or `config`) — must not be `isError`. -4. Call `apply_config` — `isError` if zero items. Confirm `_meta.item_count` before shipping. +4. Call `apply_config` — `isError` if zero items. Confirm `payload.item_count` before shipping. If the destination is html2rss-configs, rewrite the draft for `directory.topics` and explicit channel `title`/`url`. From 7fc7d0f9bb1fa388f45a978d14b85ca2dabe4ee6 Mon Sep 17 00:00:00 2001 From: Gil Desmarais Date: Wed, 19 Aug 2026 01:49:39 +0200 Subject: [PATCH 3/8] docs(domain): document admission, enhance leftover, and Status drops Cleanup owns feed-item admission; enhance fills missing card fields and drops listing chrome. Status.admission_drops and auto_feed_result/--explain expose the same telemetry. --- .../ruby-gem/guides/managing-feed-configs.mdx | 4 +- .../docs/ruby-gem/reference/auto-source.mdx | 46 +++++++++++-------- .../docs/ruby-gem/reference/selectors.mdx | 6 ++- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/content/docs/ruby-gem/guides/managing-feed-configs.mdx b/src/content/docs/ruby-gem/guides/managing-feed-configs.mdx index d346afa0..e1a635f2 100644 --- a/src/content/docs/ruby-gem/guides/managing-feed-configs.mdx +++ b/src/content/docs/ruby-gem/guides/managing-feed-configs.mdx @@ -76,7 +76,9 @@ Prefer `Html2rss.feed_result` when one scrape must render as both RSS and JSON F - `to_rss` / `to_json_feed(feed_url:)` — render formats - `status` — scrape telemetry (`Html2rss::Status`) -`status.to_h` is the stable observability payload. Always includes `version` and `dedup_dropped`. When present, it may also include `scraper_tallies`, `selected_strategy`, `attempt_count`, and `strategy_attempts` (auto-fallback attempts; empty outside `strategy: auto`). +`status.to_h` is the stable observability payload. Always includes `version` and `dedup_dropped`. When present, it may also include `scraper_tallies`, `selected_strategy`, `attempt_count`, `strategy_attempts` (auto-fallback attempts; empty outside `strategy: auto`), and `admission_drops` (Cleanup reason → count). + +For URL-only auto discovery, `Html2rss.auto_feed_result(url)` returns the same `FeedResult` (CLI `html2rss auto --explain` prints `status.to_h` on stderr). ` tag used by WordPress and pulls posts from the REST API without parsing article HTML. See [WordPress API](/ruby-gem/reference/wordpress-api/). -2. **`sitemap`:** Automatically locates XML sitemap documents (``, `/sitemap.xml`, or `/robots.txt`), filtering entries by priority and recency, with support for Google News tags (``). -3. **`meta_oembed`:** Extracts single-item articles, video pages, and media updates from OpenGraph/Twitter meta tags and resolves JSON oEmbed endpoints (``). -4. **`schema`:** Parses `