SPA-aware browsing library for AI agents
Project description
spagents
Give your AI agents eyes for the modern web.
Installation · Quick Start · MCP Server · Python SDK · How It Works
AI agents are blind to Single Page Applications. Tools like fetch and requests return empty HTML shells — no articles, no content, no interactive elements. SPAs render everything via JavaScript after the page loads.
spagents fixes this. It launches a real browser, intelligently waits for SPAs to finish rendering, and returns structured, agent-friendly data — articles, actions, inputs, navigation — ready for your agent to use.
How Claude handles dynamic content before spagents
How Claude handles dynamic content after spagents
Key features
- Smart content detection — Knows when a SPA is done rendering (not just
sleep(5)) - Structured extraction — Returns articles, links, metadata as typed Pydantic models
- Full interaction — Click, type, scroll, press keys, navigate — like a real user
- Action discovery — Finds every interactive element: buttons, inputs, ARIA roles, custom components
- Session persistence — Cookies, localStorage, and auth state preserved across navigations
- Three interfaces — Python SDK, CLI, and MCP server for Claude Desktop / AI agents
Installation
pip install spagents
playwright install chromium
From source
git clone https://github.com/your-username/spagents.git
cd spagents
uv sync
uv run playwright install chromium
Quick start
CLI
# Structured JSON output (default)
spagents browse "https://news.kagi.com"
# Human-readable text
spagents browse "https://news.kagi.com" --format text
# Interactive REPL session
spagents interactive "https://news.kagi.com"
Python SDK
import asyncio
from spagents import BrowserManager
async def main():
async with BrowserManager() as browser:
session = await browser.new_session()
# Browse a SPA — content is fully rendered
state = await session.navigate("https://news.kagi.com")
for article in state.content.articles:
print(f"{article.category}: {article.headline}")
# Interact with the page
for action in state.actions:
if "Technology" in action.description:
state = await session.click(action.selector)
break
# Type into inputs, press keys
state = await session.type_text("#search", "climate change")
state = await session.press_key("Enter")
await session.close()
asyncio.run(main())
MCP Server
Connect spagents to Claude Desktop or any MCP-compatible AI agent:
spagents mcp
Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"spagents": {
"command": "spagents",
"args": ["mcp"]
}
}
}
Claude Code
Add the MCP server to your project or user settings:
claude mcp add spagents -- spagents mcp
Or add it directly to your .claude/settings.json:
{
"mcpServers": {
"spagents": {
"command": "spagents",
"args": ["mcp"]
}
}
}
Now Claude can browse any SPA:
You: "What's the top story on Kagi News today?"
Claude: uses
browsetool → reads structured articles → gives you the answer
MCP tools
| Tool | Description |
|---|---|
browse |
Navigate to a URL, return rendered content + interactive actions |
click |
Click an element by CSS selector |
type_text |
Type into an input field |
press_key |
Press a keyboard key (Enter, Tab, Escape, etc.) |
list_actions |
Discover all interactive elements on the page |
navigate |
Go to a new URL within an existing session |
scroll |
Scroll up or down, trigger infinite scroll |
extract_content |
Re-extract content from the current page |
close_session |
Close a browser session and free resources |
Interactive REPL commands
click <n> Click action by number
click <selector> Click by CSS selector
type <n> <text> Type text into an input by action number
type "<selector>" <text> Type text into an input by CSS selector
press <key> Press a key (Enter, Escape, Tab, ArrowDown, etc.)
select <n> <value> Select dropdown option by action number
scroll [down|up] Scroll the page
actions List all interactive elements
extract Re-extract page content
navigate <url> Navigate to a new URL
json Dump current state as JSON
quit Exit the session
How it works
spagents wraps Playwright and adds three intelligent layers:
1. Content ready detection
A multi-signal detector that knows when a SPA has actually finished rendering:
| Signal | What it checks |
|---|---|
| Network quiescence | No pending XHR/fetch requests for 500ms (ignoring analytics noise) |
| DOM stabilization | MutationObserver sees no changes for 300ms after initial render |
| Content heuristic | Meaningful text exists, no loading spinners, real links present |
This replaces naive approaches like sleep(5) or Playwright's networkidle (which breaks on long-polling and WebSocket connections).
2. Content extraction
Extracts structured data from the rendered DOM:
- Articles with headlines, summaries, sources, highlights, quotes, and sections
- Links with surrounding context (which heading or section they're under)
- Metadata from OG tags, meta descriptions, and page title
3. Action discovery
Finds every interactive element on the page through four phases:
- Semantic HTML —
<a>,<button>,<input>,<select> - ARIA roles —
role="button",role="tab",role="listitem", etc. - Custom components —
tabindex,onclick,cursor: pointer - Disambiguation — Duplicate labels get context from parent containers
CLI reference
spagents browse <url> [OPTIONS]
--format, -f Output format: json (default) or text
--timeout, -t Content detection timeout in ms (default: 15000)
--no-headless Run browser with visible window
spagents interactive <url> [OPTIONS]
--timeout, -t Content detection timeout in ms (default: 15000)
--no-headless Run browser with visible window
spagents mcp [OPTIONS]
--transport Transport: stdio (default) or sse
--port, -p Port for SSE transport (default: 8000)
License
MIT with an amended community contribution requirement for organizations with more than 100 employees. See LICENSE.md for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spagents-0.1.4.tar.gz.
File metadata
- Download URL: spagents-0.1.4.tar.gz
- Upload date:
- Size: 664.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e56d4cf6f2b171d993402ef3d884598931d6200914164676dca9e6faffd3126d
|
|
| MD5 |
cf52e841b89cd7259a44d75356682cf2
|
|
| BLAKE2b-256 |
ef4870b143226810ba74c22053c4c3e518a4e9008e7e093860d722f92f2819b0
|
Provenance
The following attestation bundles were made for spagents-0.1.4.tar.gz:
Publisher:
publish.yml on moseswynn/spagents
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spagents-0.1.4.tar.gz -
Subject digest:
e56d4cf6f2b171d993402ef3d884598931d6200914164676dca9e6faffd3126d - Sigstore transparency entry: 1186506558
- Sigstore integration time:
-
Permalink:
moseswynn/spagents@3d90ae708617629283de8fcf4097ee405530f029 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/moseswynn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3d90ae708617629283de8fcf4097ee405530f029 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file spagents-0.1.4-py3-none-any.whl.
File metadata
- Download URL: spagents-0.1.4-py3-none-any.whl
- Upload date:
- Size: 31.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9790141808be32e7cd9b0fa1c3405ac8f97d0826456896cac77718d50f516bf5
|
|
| MD5 |
2167dd129da13241f0d9b134182815d5
|
|
| BLAKE2b-256 |
c15f04f74ef22c39a4421156c2759c99a87d13961bffc128b844d6f3bf7f5dfc
|
Provenance
The following attestation bundles were made for spagents-0.1.4-py3-none-any.whl:
Publisher:
publish.yml on moseswynn/spagents
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spagents-0.1.4-py3-none-any.whl -
Subject digest:
9790141808be32e7cd9b0fa1c3405ac8f97d0826456896cac77718d50f516bf5 - Sigstore transparency entry: 1186506561
- Sigstore integration time:
-
Permalink:
moseswynn/spagents@3d90ae708617629283de8fcf4097ee405530f029 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/moseswynn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3d90ae708617629283de8fcf4097ee405530f029 -
Trigger Event:
workflow_dispatch
-
Statement type: