Metadata-Version: 2.4
Name: vivly
Version: 0.5.0b4
Summary: Python client + CLI for the Vivly social-data API (X / Reddit) with smart prompt-based routing.
Project-URL: Homepage, https://reddit-dev.vivly.in
Author-email: Vivly <vg@vivly.in>
License: MIT
License-File: LICENSE
Keywords: reddit,scraping,twitter,vivly,x
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Requires-Dist: python-dotenv>=1.0
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp[cli]>=1.0; extra == 'mcp'
Description-Content-Type: text/markdown

# vivly

Async Python client for the Vivly social-data API. One bearer key, two live
sources: **X (Twitter)** and **Reddit**, plus a smart **router** that picks
sources from a plain-language prompt. Comes with a CLI and an optional MCP
server so AI agents (Claude Desktop, Claude Code, Cursor, …) can call it
directly.

> **0.5.0b3 — breaking change.** The Python API is now async (`async with` +
> `await`). The CLI is unchanged. See [Usage](#usage).

## Install

```bash
pip install --pre vivly==0.5.0b3                # core SDK + CLI
pip install --pre "vivly[mcp]==0.5.0b3"         # + MCP server (vivly-mcp)
```

The `--pre` flag is required while we're in beta.

## Authenticate

The client needs a personal access token (looks like `vk_live_...`). Two ways:

```bash
# Option A: log in interactively (opens a browser, stores the token at
# ~/.config/vivly/config.json with 0600 perms)
vivly login

# Option B: set it yourself if you were handed a token
export VIVLY_API_KEY="vk_live_..."
```

A `.env` file in the working directory is auto-loaded. Resolution order:
explicit `api_key=` argument, then `VIVLY_API_KEY` (or legacy `VIVLY_SKILL_KEY`),
then `.env`, then `~/.config/vivly/config.json`.

## Usage

### CLI

```bash
vivly route "what reddit thinks of claude code"          # planner picks sources
vivly route "claude code launch reactions" --x           # pin to X
vivly route "san antonio spurs" --x --reddit             # pin both
vivly route "..." --limit 50 --pretty                    # 50 items, pretty JSON
vivly route "..." --out results.json                     # to file
vivly route "..." --no-interactive                       # take planner defaults
```

While a query runs you'll see a per-source progress block:

```
  ▰▰▰▱▱▱▱▱ reddit  fetching
  ▰▰▰▱▱▱▱▱ x       fetching
```

…finalising to per-source counts when the response lands.

### Python (async)

```python
import asyncio
from vivly import VivlyClient

async def main():
    async with VivlyClient() as v:
        # Reddit: one-shot retrieval. Auto-discovers subreddits, searches them,
        # and LLM-filters the results for relevance.
        r = await v.reddit.search("claude code agents", max_posts=10)
        for post in r["posts"]:
            print(post["score"], post["title"], post["url"])

        # X / Twitter: async helper that starts a scrape, polls, returns items.
        # (Runs a paid third-party actor server-side; expect a few seconds to
        # minutes — pass on_progress to surface per-poll status.)
        tweets = await v.x.scrape(
            '"san antonio" arena Spurs lang:en',
            max_items=50,
            on_progress=lambda ev: print(ev["phase"], ev["status"], f"{ev['elapsed']:.1f}s"),
        )
        for t in tweets:
            print(t["metrics"]["likes"], t["text"][:80])

        # Smart router: describe what you want; it plans + runs the right sources.
        result = await v.route(
            "what are people saying about claude code on reddit",
            force_sources=["reddit"],
            max_items=20,
            ask="auto",                          # never block on MCQs in scripts
        )

asyncio.run(main())
```

If you'd rather drive the X scrape yourself instead of the blocking helper:

```python
async with VivlyClient() as v:
    started = await v.x.scrape_start("query lang:en", max_items=50)
    status  = await v.x.scrape_status(started["run_id"])
    items   = await v.x.scrape_items(started["dataset_id"])
```

### MCP — expose Vivly to AI agents

Once `pip install vivly[mcp]` is in place, the `vivly-mcp` command runs a
[Model Context Protocol](https://modelcontextprotocol.io) server over stdio
exposing three tools: `reddit_search`, `x_scrape`, and `route`. Auth is read
from `VIVLY_API_KEY` exactly like the CLI — the model never sees the key.

Smoke-test in the inspector:

```bash
npx @modelcontextprotocol/inspector vivly-mcp
```

Register with Claude Code:

```bash
claude mcp add vivly vivly-mcp -e VIVLY_API_KEY=vk_live_...
```

Register with Claude Desktop — `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "vivly": {
      "command": "vivly-mcp",
      "env": { "VIVLY_API_KEY": "vk_live_..." }
    }
  }
}
```

Cursor uses the same shape at `~/.cursor/mcp.json`. After restart, your agent
sees `reddit_search` / `x_scrape` / `route` in its tool list and calls them
on its own.

## Errors

```python
from vivly import VivlyError, AuthError, RateLimitError, NotFoundError
```

`AuthError` (401/403) usually means a missing, revoked, or mistyped token.

## License

MIT
