Skip to main content

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

Claude sends error message that dynamic content can't be loaded.

How Claude handles dynamic content after spagents

Claude successfully retrieves the dynamic content

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 browse tool → 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:

  1. Semantic HTML — <a>, <button>, <input>, <select>
  2. ARIA roles — role="button", role="tab", role="listitem", etc.
  3. Custom components — tabindex, onclick, cursor: pointer
  4. 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.

Metadata

Release files for spagents 0.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spagents 0.1.4
File Size Uploaded Provenance Actions
spagents-0.1.4.tar.gz 664.2 kB With provenance Details for spagents-0.1.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for spagents 0.1.4
File Interpreter ABI Platform Actions
spagents-0.1.4-py3-none-any.whl Python 3 none any Details for spagents-0.1.4-py3-none-any.whl

Total release size: 695.4 kB

Release files / spagents-0.1.4.tar.gz

Download URL spagents-0.1.4.tar.gz
Size 664.2 kB
Tags Source
SHA-256 checksum
How to use checksums
e56d4cf6f2b171d993402ef3d884598931d6200914164676dca9e6faffd3126d
BLAKE2b-256 checksum
How to use checksums
ef4870b143226810ba74c22053c4c3e518a4e9008e7e093860d722f92f2819b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 26, 2026.

Transparency log

Release files / spagents-0.1.4-py3-none-any.whl

Download URL spagents-0.1.4-py3-none-any.whl
Size 31.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9790141808be32e7cd9b0fa1c3405ac8f97d0826456896cac77718d50f516bf5
BLAKE2b-256 checksum
How to use checksums
c15f04f74ef22c39a4421156c2759c99a87d13961bffc128b844d6f3bf7f5dfc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 26, 2026.

Transparency log

Security for spagents 0.1.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for spagents-0.1.4.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page