Repository navigation
feat: opt-in agent extras (state, appendix, highlights), MCP server, YouTube transcripts, office docs - #6
Merged
Merged
Conversation
- state / appendix / query highlights are opt-in per call (library, CLI flags, HTTP body); a call without them takes the pre-existing code path, so results and JSON shapes are unchanged for current users - highlights rank page passages with local BM25 by default, or TypeSafe's Jev model when SCRAPER_HIGHLIGHTS_GRADER=jev + TYPESAFE_API_KEY; any Jev failure falls back to BM25 and never fails the scrape - each highlight carries a citation link: a text fragment into the page, or the timestamp for a YouTube transcript - page HTML for state/appendix is captured inside render(), so no tier changes what it returns; tier_3/tier_7 re-apply the capture on their worker threads - switchback-mcp exposes scrape + search as MCP tools (scrapes run serialised on a worker thread so the sync browser tiers work) - tier_1 returns YouTube transcripts, retrying through SCRAPER_EGRESS_PROXY when YouTube blocks a cloud IP; tier_2 converts docx/pptx/xlsx/xls/epub via markitdown instead of returning the raw zip bytes - new deps live in [mcp], [youtube], [documents] and are kept out of [all], so upgrading an existing install pulls nothing new; without them those URLs behave exactly as before - README roadmap lists what is deferred: layout-aware markdown, OCR, audio, hosted service
…classes Default results had gained state/appendix/highlights fields, which changed asdict()/astuple() output for existing callers. The extras now live on ScrapeResultWithExtras / ScrapeOutcomeWithExtras, returned only when an extra is requested. Tests pin the original field lists.
CONTRIBUTING gains the compatibility rules learned from the extras PR (never change public result fields; opt-in subclasses, keyword-only params, JSON keys only on request). CI now runs the offline tests, so the tests that pin result fields and CLI/HTTP JSON keys guard every PR.
Installing a package is not opt-in: users may already have markitdown or youtube-transcript-api for something else. SCRAPER_YOUTUBE=1 and SCRAPER_DOCUMENTS=1 now turn these on; without them those URLs return exactly what they did before, installed or not.
With SCRAPER_YOUTUBE=1, a YouTube URL now tries tier_1 even when youtube.com was previously won by a later tier; without a transcript it resumes at the learned tier. Other URLs, and YouTube with the switch off, route as before.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Everything is opt-in. A call that doesn't ask for the new extras takes the pre-existing code path, so existing users see no change.
scrape(url, state=True)·--state·"state": true__NEXT_DATA__, Nuxt,window.__X__globals, JSON-LDappendix=True·--appendixquery="…"·--querySCRAPER_HIGHLIGHTS_GRADER=jev+TYPESAFE_API_KEYlink: a text-fragment URL into the page, or the video timestamp for YouTubepip install "switchback[mcp]"→switchback-mcpscrape+searchtools for Claude Code / Cursor / any MCP clientpip install "switchback[youtube]"SCRAPER_EGRESS_PROXY.pip install "switchback[documents]"Deferred to the README Roadmap: layout-aware markdown, OCR for scanned pages and images, audio transcription, hosted service.
Why existing users are safe
scrape(url)with no extras never creates a capture box, still reads the content cache, and returns the same fields. The new dataclass fields default toNoneand come after the existing ones, so positional construction still works.url, source_method, markdown|htmlunless an extra is requested. Tests assert the key lists.[mcp],[youtube]and[documents], and are not added to[all]. Without them, YouTube and office URLs behave exactly as before. A test forces the "markitdown missing" path.render(). tier_3 and tier_7 re-apply the capture on their worker threads. Only tier_1 (YouTube branch) and tier_2 (docs branch) gained new code paths, and both fall through to the old behavior.Testing
python -m build,twine check: all pass. The wheel includes the new modules and theswitchback-mcpentry point.PK\x03\x04…) as markdown. With[documents]installed they now return real markdown; without it they behave as before.<title>, alt text, hidden elements); those links just open the page at the top.{"ok": false, …}.Rollback
git revert <merge-commit>. Nothing persistent changes: no state-file format changes and no migrations.