assertify-mcp 0.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assertify_mcp-0.6.3/.gitignore +13 -0
- assertify_mcp-0.6.3/CHANGELOG.md +83 -0
- assertify_mcp-0.6.3/LICENSE +21 -0
- assertify_mcp-0.6.3/PKG-INFO +414 -0
- assertify_mcp-0.6.3/README.md +389 -0
- assertify_mcp-0.6.3/SECURITY.md +24 -0
- assertify_mcp-0.6.3/pyproject.toml +58 -0
- assertify_mcp-0.6.3/src/assertify/__init__.py +1 -0
- assertify_mcp-0.6.3/src/assertify/__main__.py +3 -0
- assertify_mcp-0.6.3/src/assertify/browser.py +1791 -0
- assertify_mcp-0.6.3/src/assertify/cli.py +120 -0
- assertify_mcp-0.6.3/src/assertify/files.py +177 -0
- assertify_mcp-0.6.3/src/assertify/human.py +95 -0
- assertify_mcp-0.6.3/src/assertify/icon.png +0 -0
- assertify_mcp-0.6.3/src/assertify/install.py +107 -0
- assertify_mcp-0.6.3/src/assertify/native.py +259 -0
- assertify_mcp-0.6.3/src/assertify/prompt.py +75 -0
- assertify_mcp-0.6.3/src/assertify/relay.py +629 -0
- assertify_mcp-0.6.3/src/assertify/safety.py +252 -0
- assertify_mcp-0.6.3/src/assertify/server.py +495 -0
- assertify_mcp-0.6.3/src/assertify/storage.py +287 -0
- assertify_mcp-0.6.3/src/assertify/vault.py +157 -0
- assertify_mcp-0.6.3/tests/conftest.py +10 -0
- assertify_mcp-0.6.3/tests/test_accounts.py +97 -0
- assertify_mcp-0.6.3/tests/test_domains.py +35 -0
- assertify_mcp-0.6.3/tests/test_downloads.py +368 -0
- assertify_mcp-0.6.3/tests/test_e2e.py +220 -0
- assertify_mcp-0.6.3/tests/test_relay.py +154 -0
- assertify_mcp-0.6.3/tests/test_safety.py +51 -0
- assertify_mcp-0.6.3/tests/test_upload.py +114 -0
- assertify_mcp-0.6.3/tests/test_vault.py +59 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.6.3 (2026-11-07)
|
|
4
|
+
|
|
5
|
+
- Claude plugin: Assertify can be installed with `/plugin install assertify --marketplace romaklym/assertify` and is ready to submit to the Claude plugin directory. It bundles the MCP server and a `use-websites` skill.
|
|
6
|
+
- Published on PyPI as `assertify-mcp`, so `uvx assertify-mcp` runs the server. New `assertify-mcp` command, the same as `assertify`.
|
|
7
|
+
|
|
8
|
+
## 0.6.2 (2026-11-06)
|
|
9
|
+
|
|
10
|
+
- Fixed downloading Discord images and other attachments that open in a viewer: `download` with the link's ref now fetches the file the link points to instead of clicking it and waiting.
|
|
11
|
+
- Long links in snapshots end with `…` when shortened, and `download(url=...)` accepts that shortened link and uses the page's full signed link.
|
|
12
|
+
|
|
13
|
+
## 0.6.1 (2026-11-06)
|
|
14
|
+
|
|
15
|
+
- Downloads from files on another host now work (Discord attachments on `cdn.discordapp.com`, files on S3 or Google's content servers). Clicking such a link fetches the file directly instead of timing out, and `download(url=...)` accepts a file outside the login's domains when the page links to it, using the page's own signed link. Other off-site URLs are still refused, and off-site web pages aren't saved.
|
|
16
|
+
- Fixed: a link that opens a new tab to a blocked domain no longer hangs the tab.
|
|
17
|
+
|
|
18
|
+
## 0.6.0 (2026-11-06)
|
|
19
|
+
|
|
20
|
+
- Sign in with Google (and other providers that block automated browsers): `start_login` now opens your installed Chrome as a normal window with a throwaway profile, instead of an automated one. Assertify only reads its cookies while you sign in, reads the pages once when you're done, then closes it and deletes the profile. Set `ASSERTIFY_LOGIN_WINDOW=playwright` for the old window; `ASSERTIFY_CHROME_PATH` picks the browser.
|
|
21
|
+
- Frames and iframes: snapshots, clicks, typing and `read_text` now reach inside them (old framed dashboards, embedded editors like TinyMCE, report viewers). Ad and CAPTCHA frames are left out.
|
|
22
|
+
- Shadow DOM: buttons and fields inside web components now show up and can be clicked.
|
|
23
|
+
- Repeated buttons in tables ("edit", "delete", "View") show which row they belong to, e.g. `row="Smith"`.
|
|
24
|
+
- Icon-only buttons and links get a name from their icon's title or label, or else a readable element id.
|
|
25
|
+
- Dropdowns no longer show their internal value as the label.
|
|
26
|
+
- New `dialog_text` on `click`: answers a browser prompt box.
|
|
27
|
+
- `complete_login` warns when the login window is still on a sign-in page (the login probably hadn't finished).
|
|
28
|
+
- New `download` tool: saves exports, reports and invoices from a site and reads them back, CSV/TSV and Excel as tables, PDFs and text files as text. Click a download button, fetch a link with the login, or save the file the page just started.
|
|
29
|
+
- New `browser_settings` tool for intranets, routers and old admin pages: you type the password for the browser's own sign-in box (HTTP Basic/NTLM) in a small window and can trust a self-signed certificate. The agent never sees the password; it's stored encrypted and sent only to that site. Agents get a hint to use it when a site asks for one or its certificate isn't trusted.
|
|
30
|
+
- Logins that live outside cookies now stick: the login window saves sessionStorage and IndexedDB as well as localStorage (WhatsApp Web, Teams and similar web apps), and agent sessions save and restore sessionStorage.
|
|
31
|
+
- The native Chrome login window now also saves a site's storage when you just close the window, reading it only from tabs that aren't sign-in pages.
|
|
32
|
+
- IndexedDB values that aren't plain JSON now carry over: dates, binary data, files and Blobs, Maps and Sets, big integers and exportable encryption keys. Assertify writes the databases back itself before the agent's first visit, and saving an agent session no longer turns files and keys into empty objects (Playwright's own save did). A database it can't copy (an encryption key the browser locks, or over 25 MB) is kept from the earlier save if there is one, and named in the save message and in `list_profiles` (`storage_not_copied`).
|
|
33
|
+
- When the user logs in again during a session, open tabs now also get the new sessionStorage and IndexedDB, not only cookies and localStorage.
|
|
34
|
+
- Sites that hide their storage from page scripts (Discord deletes `window.localStorage`) now save their login: storage is read in an isolated script world the page can't touch, every ~5 seconds while the window is open. A read that fails no longer wipes what was already captured, logins kept only in storage (no cookies) are saved, and `list_profiles` and save messages show storage counts next to cookies.
|
|
35
|
+
- Clearer empty snapshots: says when a page is still loading or failed to load its scripts, or is made only of frames. Page text no longer carries runs of blank lines.
|
|
36
|
+
|
|
37
|
+
## 0.5.0 (2026-11-05)
|
|
38
|
+
|
|
39
|
+
- Phones: a Flutter app for iPhone and Android (`mobile/`). Log in to any site in the app's browser; agents then work in a hidden browser on the phone with your login.
|
|
40
|
+
- New `assertify relay`: an MCP server for Claude mobile, ChatGPT/Codex and other remote agents that forwards to the paired phone over a WebSocket. Token-protected, one phone at a time.
|
|
41
|
+
- Block, unblock, report and mute buttons now need `confirmed=true` too.
|
|
42
|
+
|
|
43
|
+
## 0.4.0 (2026-11-02)
|
|
44
|
+
|
|
45
|
+
- New `upload_files` tool: attach files or whole folders (photos for a post, a PDF for an email). Uses the site's file dialog like a person, else its upload field, else drag-and-drop. Respects single-file fields and file-type limits.
|
|
46
|
+
- Uploads never include secrets: hidden folders, keys, certificates, `.env`, cookie stores and the vault are refused. Optional `ASSERTIFY_UPLOAD_DIRS` allowlist and `ASSERTIFY_MAX_UPLOAD_MB` cap.
|
|
47
|
+
- Snapshots mention upload fields on the page, and show visible ones as `file-upload`.
|
|
48
|
+
|
|
49
|
+
## 0.3.1 (2026-11-02)
|
|
50
|
+
|
|
51
|
+
- Windows: pressing Enter now finishes `assertify login` (it used to wait for the window to close), and the CLI no longer crashes on consoles that aren't UTF-8.
|
|
52
|
+
- macOS/Windows: renaming a profile when only the letter case changes (`Work` → `work`) no longer deletes it.
|
|
53
|
+
- Linux: a clear message when there's no screen for the login window.
|
|
54
|
+
- CI now tests Windows too. README diagram shows multiple accounts.
|
|
55
|
+
|
|
56
|
+
## 0.3.0 (2026-11-02)
|
|
57
|
+
|
|
58
|
+
- Several accounts per site: log in without a name and the login is named after the account's email; `list_profiles(site)` / `assertify list <site>`; new `open_sessions` opens every account of a site (or a list) at once; `open_session` also accepts the account email; new `rename_profile` / `assertify rename`.
|
|
59
|
+
- Profile names may contain `@` and `+`, so an email works as a name.
|
|
60
|
+
|
|
61
|
+
## 0.2.0 (2026-11-02)
|
|
62
|
+
|
|
63
|
+
Hardening: harder to detect, safer with your accounts, better with pop-ups and expired logins.
|
|
64
|
+
|
|
65
|
+
- Human-like input: curved mouse moves to random points, per-key typing, uneven scrolling, random pauses (`ASSERTIFY_HUMANIZE=0` to turn off).
|
|
66
|
+
- Stealth: real Chrome first, no automation flag or `navigator.webdriver`, the login window's user agent, language and time zone reused by the agent.
|
|
67
|
+
- Pop-ups: listed first in snapshots with a warning; new `dismiss_popup`; clicks behind a pop-up are refused with a hint; cookie banners declined automatically; `alert`/`confirm` boxes reported and cancelled unless `accept_dialog=true`.
|
|
68
|
+
- Safety: send/buy/delete/connect-style buttons and Enter in message boxes need `confirmed=true`; read-only logins (`--read-only`); page text labelled as untrusted; only http(s) URLs; downloads disabled.
|
|
69
|
+
- Terms of service: LinkedIn, Facebook, Instagram, Threads, X and TikTok get slower pacing and a lower page-load cap; hourly cap for all sites (`ASSERTIFY_MAX_PAGES_PER_HOUR`).
|
|
70
|
+
- Expired sessions: snapshots flag CAPTCHAs and sign-in pages; new `handoff` opens the page for you and refreshes the agent's session afterwards.
|
|
71
|
+
- Redesign-proofing: `click`, `type_text` and `select_option` accept visible text instead of a ref; new `find` searches the whole page.
|
|
72
|
+
- Fix: a blocked navigation no longer leaves the session on Chrome's error page.
|
|
73
|
+
|
|
74
|
+
## 0.1.0 (2026-11-02)
|
|
75
|
+
|
|
76
|
+
First public release.
|
|
77
|
+
|
|
78
|
+
- Log in once in a visible browser; the session is saved encrypted (AES-256-GCM, OS keychain key).
|
|
79
|
+
- Headless, logged-in browser sessions for agents: snapshot, click, type, select, scroll, navigate, read, tabs.
|
|
80
|
+
- Screenshots shown to the agent and saved locally; optional screenshot on every step.
|
|
81
|
+
- Per-login domain guard.
|
|
82
|
+
- `assertify setup` installs the browser; automatic download or Chrome/Edge fallback on first use.
|
|
83
|
+
- Claude Desktop extension (`.mcpb`) and MCP Registry listing.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Roman Klym
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: assertify-mcp
|
|
3
|
+
Version: 0.6.3
|
|
4
|
+
Summary: Let AI agents use any website you're logged in to. Log in once; the session stays encrypted on your machine.
|
|
5
|
+
Project-URL: Homepage, https://github.com/romaklym/assertify
|
|
6
|
+
Project-URL: Issues, https://github.com/romaklym/assertify/issues
|
|
7
|
+
Author: Roman Klym
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: ai-agents,browser-automation,claude,codex,mcp,model-context-protocol,playwright
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: cryptography>=42
|
|
19
|
+
Requires-Dist: keyring>=25
|
|
20
|
+
Requires-Dist: mcp>=2.0
|
|
21
|
+
Requires-Dist: playwright>=1.51
|
|
22
|
+
Requires-Dist: pypdf>=5
|
|
23
|
+
Requires-Dist: websockets>=13
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
<!-- mcp-name: io.github.romaklym/assertify -->
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
<img src="https://raw.githubusercontent.com/romaklym/assertify/main/assets/icon.png" width="112" alt="Assertify logo">
|
|
30
|
+
|
|
31
|
+
# Assertify
|
|
32
|
+
|
|
33
|
+
**Let your AI agent use any website you're logged in to.**<br>
|
|
34
|
+
Log in once. Your session stays encrypted on your machine. Claude, Codex, Cursor and any MCP client can then browse, click, type and read for you.
|
|
35
|
+
|
|
36
|
+
[](https://github.com/romaklym/assertify/actions/workflows/ci.yml)
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://www.python.org)
|
|
39
|
+
[](https://modelcontextprotocol.io)
|
|
40
|
+
[](https://playwright.dev)
|
|
41
|
+
|
|
42
|
+
[Quick start](#-quick-start) · [Connect your client](#-connect-your-ai-client) · [Tools](#-tools) · [Security](#-security) · [FAQ](#-faq)
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
You: Check my LinkedIn messages and summarize anything from recruiters.
|
|
50
|
+
Agent: open_session("linkedin") → click "Messaging" → read_text …
|
|
51
|
+
You have 3 recruiter messages. The most recent is from Dana at Acme, about a Senior QA role …
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## ✨ Why Assertify
|
|
55
|
+
|
|
56
|
+
Most sites have no API, or one that can't do what you need. Assertify gives your agent a real browser that is **already logged in as you**, without ever handing it your password.
|
|
57
|
+
|
|
58
|
+
- 🔐 **Log in once, by hand.** A normal browser window opens; you sign in (2FA, passkeys, SSO all work). The agent never sees your password.
|
|
59
|
+
- 🧳 **Encrypted, local, yours.** Sessions are AES-256-GCM encrypted with a key in your OS keychain. Assertify itself sends nothing anywhere. There is no server and no account.
|
|
60
|
+
- 🤖 **Works with every MCP client.** Claude Code, Claude Desktop, Codex, Cursor, VS Code, Gemini CLI, Grok, Windsurf…
|
|
61
|
+
- 👀 **See what the agent sees.** Every step can come with a screenshot, shown in the chat and saved to disk.
|
|
62
|
+
- 🛡️ **Fenced in.** Each login is locked to its own domains, so a prompt injection on a page can't walk your logged-in browser somewhere else.
|
|
63
|
+
- 👥 **Many accounts per site.** Five Gmail accounts? Log in to each; they're named after their emails. Use one, or open them all at once.
|
|
64
|
+
- ✋ **Asks before it acts.** Buttons that send, post, buy, delete or connect are paused until you say yes. Logins can be made read-only.
|
|
65
|
+
- 🧍 **Behaves like a person.** Real Chrome, your own browser fingerprint, curved mouse moves, uneven typing and natural pauses. No `navigator.webdriver` flag.
|
|
66
|
+
- 📎 **Uploads files and folders.** Photos for a Facebook post, a PDF for an email: the agent clicks the upload button and picks your files, just like you would. Key and password files are always refused.
|
|
67
|
+
- 🪟 **Handles pop-ups.** Cookie banners are declined for you; "share your contact info" style pop-ups are flagged so the agent decides, or closes them.
|
|
68
|
+
- 🔁 **Hands back to you when needed.** CAPTCHAs, 2FA prompts and expired logins open in a window for you to finish; the agent then carries on.
|
|
69
|
+
- 🪶 **Fast and token-cheap.** The agent reads a compact outline (`[e12] button "Send"`), not raw HTML.
|
|
70
|
+
|
|
71
|
+
## ⚙️ How it works
|
|
72
|
+
|
|
73
|
+
```mermaid
|
|
74
|
+
flowchart LR
|
|
75
|
+
U(["👤 You"]) -- "log in once<br/>per account" --> L["Login window<br/><small>real browser, 2FA works</small>"]
|
|
76
|
+
L -- "named by<br/>email" --> V
|
|
77
|
+
K[["🔑 OS keychain<br/><small>macOS · Windows · Linux</small>"]] -. key .-> V
|
|
78
|
+
|
|
79
|
+
subgraph V["🔒 Encrypted vault"]
|
|
80
|
+
direction LR
|
|
81
|
+
P1["personal@gmail.com"]
|
|
82
|
+
P2["work@gmail.com"]
|
|
83
|
+
P3["shop@gmail.com"]
|
|
84
|
+
P4["you@company.com<br/><small>LinkedIn</small>"]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
V -- decrypt --> S["⚙️ Assertify server<br/><small>asks before send · buy · delete</small>"]
|
|
88
|
+
A(["🤖 AI agent"]) <-- "pick one account<br/>or all at once" --> S
|
|
89
|
+
S -- "side by side" --> B
|
|
90
|
+
|
|
91
|
+
subgraph B["Headless browsers, logged in"]
|
|
92
|
+
direction LR
|
|
93
|
+
H1["Gmail · personal@"]
|
|
94
|
+
H2["Gmail · work@"]
|
|
95
|
+
H3["Gmail · shop@"]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
classDef person fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
|
|
99
|
+
classDef safe fill:#ecfdf5,stroke:#10b981,color:#064e3b
|
|
100
|
+
classDef live fill:#fff7ed,stroke:#f97316,color:#7c2d12
|
|
101
|
+
class U,A,L,S person
|
|
102
|
+
class P1,P2,P3,P4,K safe
|
|
103
|
+
class H1,H2,H3 live
|
|
104
|
+
style V fill:#f0fdf4,stroke:#10b981
|
|
105
|
+
style B fill:#fffbeb,stroke:#f59e0b
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Log in once per account; each one is saved under its email. Your agent then opens one account by name, or every account for a site at once, each in its own logged-in browser. Everything above runs on your computer, on macOS, Windows or Linux.
|
|
109
|
+
|
|
110
|
+
## 🚀 Quick start
|
|
111
|
+
|
|
112
|
+
You need [uv](https://docs.astral.sh/uv/getting-started/installation/):
|
|
113
|
+
|
|
114
|
+
- **macOS / Linux:** `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
|
115
|
+
- **Windows (PowerShell):** `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
|
116
|
+
|
|
117
|
+
Then add Assertify to your AI client with one command (no download or clone needed):
|
|
118
|
+
|
|
119
|
+
| Client | Command |
|
|
120
|
+
|---|---|
|
|
121
|
+
| Claude Code | `claude mcp add -s user assertify -- uvx --from git+https://github.com/romaklym/assertify assertify serve` |
|
|
122
|
+
| Codex | `codex mcp add assertify -- uvx --from git+https://github.com/romaklym/assertify assertify serve` |
|
|
123
|
+
| Claude Desktop (macOS) | `curl -fLo /tmp/assertify.mcpb https://github.com/romaklym/assertify/releases/latest/download/assertify.mcpb && open /tmp/assertify.mcpb` |
|
|
124
|
+
| VS Code (Copilot) | `code --add-mcp '{"name":"assertify","command":"uvx","args":["--from","git+https://github.com/romaklym/assertify","assertify","serve"]}'` |
|
|
125
|
+
|
|
126
|
+
Other clients are under [Connect your AI client](#-connect-your-ai-client). Restart the client afterwards. The first start takes a minute while it installs, and Assertify downloads the browser it needs on first use (or uses your installed Chrome or Edge).
|
|
127
|
+
|
|
128
|
+
To pin a version, add a tag: `git+https://github.com/romaklym/assertify@v0.6.3`.
|
|
129
|
+
|
|
130
|
+
<details>
|
|
131
|
+
<summary><b>Install from source instead</b></summary>
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git clone https://github.com/romaklym/assertify.git
|
|
135
|
+
cd assertify
|
|
136
|
+
uv sync
|
|
137
|
+
uv run assertify setup
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`setup` downloads the browser (Chromium, about 300 MB, once), checks your keychain, and prints the command to connect your AI client. Use `uv --directory /path/to/assertify run assertify serve` as the server command.
|
|
141
|
+
</details>
|
|
142
|
+
|
|
143
|
+
<details>
|
|
144
|
+
<summary><b>Linux notes</b></summary>
|
|
145
|
+
|
|
146
|
+
- If the browser won't start, install its system libraries: `sudo uv run playwright install-deps chromium`.
|
|
147
|
+
- The key goes in Secret Service (GNOME Keyring or KWallet). On a server with no keychain, set `ASSERTIFY_PASSPHRASE` instead.
|
|
148
|
+
- Logging in needs a screen for the browser window. On a server, log in on your desktop and copy `~/.assertify-mcp` over (with the same passphrase), or use `ssh -X`.
|
|
149
|
+
|
|
150
|
+
</details>
|
|
151
|
+
|
|
152
|
+
Then save your first login, by asking your agent (*"log me in to GitHub"*) or from the terminal:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
uvx --from git+https://github.com/romaklym/assertify assertify login github https://github.com/login
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
A browser window opens. Sign in, then close the window (or press Enter in the terminal). Done. Your agent can now use GitHub as you.
|
|
159
|
+
|
|
160
|
+
> [!TIP]
|
|
161
|
+
> When you ask your agent, it calls `start_login`, the window opens, and the session saves when you close it.
|
|
162
|
+
|
|
163
|
+
## 🔌 Connect your AI client
|
|
164
|
+
|
|
165
|
+
Every client runs the same command: `uvx --from git+https://github.com/romaklym/assertify assertify serve`. If you cloned the repo, use `uv --directory /path/to/assertify run assertify serve` instead.
|
|
166
|
+
|
|
167
|
+
<details open>
|
|
168
|
+
<summary><b>Claude Code</b></summary>
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
claude mcp add -s user assertify -- uvx --from git+https://github.com/romaklym/assertify assertify serve
|
|
172
|
+
```
|
|
173
|
+
</details>
|
|
174
|
+
|
|
175
|
+
<details>
|
|
176
|
+
<summary><b>Claude Desktop</b></summary>
|
|
177
|
+
|
|
178
|
+
**Easiest:** download [`assertify.mcpb`](https://github.com/romaklym/assertify/releases/latest/download/assertify.mcpb) and double-click it. Claude Desktop installs it with its icon and handles Python for you. From the terminal on macOS:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
curl -fLo /tmp/assertify.mcpb https://github.com/romaklym/assertify/releases/latest/download/assertify.mcpb && open /tmp/assertify.mcpb
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Or add it to `claude_desktop_config.json` by hand:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"mcpServers": {
|
|
189
|
+
"assertify": {
|
|
190
|
+
"command": "uvx",
|
|
191
|
+
"args": ["--from", "git+https://github.com/romaklym/assertify", "assertify", "serve"]
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
</details>
|
|
197
|
+
|
|
198
|
+
<details>
|
|
199
|
+
<summary><b>Codex</b></summary>
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
codex mcp add assertify -- uvx --from git+https://github.com/romaklym/assertify assertify serve
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If the Codex app can't find `uvx`, use its full path (`which uvx`, e.g. `/opt/homebrew/bin/uvx`).
|
|
206
|
+
</details>
|
|
207
|
+
|
|
208
|
+
<details>
|
|
209
|
+
<summary><b>Cursor</b></summary>
|
|
210
|
+
|
|
211
|
+
`~/.cursor/mcp.json`:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"mcpServers": {
|
|
216
|
+
"assertify": {
|
|
217
|
+
"command": "uvx",
|
|
218
|
+
"args": ["--from", "git+https://github.com/romaklym/assertify", "assertify", "serve"]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
</details>
|
|
224
|
+
|
|
225
|
+
<details>
|
|
226
|
+
<summary><b>VS Code (Copilot)</b></summary>
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
code --add-mcp '{"name":"assertify","command":"uvx","args":["--from","git+https://github.com/romaklym/assertify","assertify","serve"]}'
|
|
230
|
+
```
|
|
231
|
+
</details>
|
|
232
|
+
|
|
233
|
+
<details>
|
|
234
|
+
<summary><b>Gemini CLI, Grok, Windsurf, Zed and others</b></summary>
|
|
235
|
+
|
|
236
|
+
Any client that runs local (stdio) MCP servers works. Use this command:
|
|
237
|
+
|
|
238
|
+
```text
|
|
239
|
+
uvx --from git+https://github.com/romaklym/assertify assertify serve
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
For JSON configs, use the `mcpServers` block shown under Cursor.
|
|
243
|
+
</details>
|
|
244
|
+
|
|
245
|
+
## 🧰 Tools
|
|
246
|
+
|
|
247
|
+
| Tool | What it does |
|
|
248
|
+
|---|---|
|
|
249
|
+
| `list_profiles(site?)` | Your saved logins (name, account email, site), optionally only one site's |
|
|
250
|
+
| `start_login(url, profile?)` / `complete_login` | Open a window for **you** to log in, then encrypt and save the session. No name: it's named after the account's email |
|
|
251
|
+
| `rename_profile` · `delete_profile` | Rename or remove a saved login |
|
|
252
|
+
| `open_session(profile, url?, screenshots?)` | Start a logged-in headless browser; returns a `session_id` and a page outline |
|
|
253
|
+
| `open_sessions(site? \| profiles?)` | Open several logins at once (e.g. every Gmail account), one `session_id` each |
|
|
254
|
+
| `snapshot` | URL, title and interactive elements as `[e12] button "Save"`; pop-ups first, warnings for CAPTCHAs and sign-in pages |
|
|
255
|
+
| `find(query)` | Search the whole page for elements by label |
|
|
256
|
+
| `click` · `type_text` · `select_option` · `press_key` · `scroll` · `back` · `navigate` · `wait_for` | Act by ref **or by visible text** (`click(text="Messages")`), so small redesigns don't break a flow; each returns a fresh outline |
|
|
257
|
+
| `upload_files(paths, ref? \| text?)` | Attach files or whole folders: through the site's file dialog (`text="Photo/video"`), its upload field, or drag-and-drop. Only attaches; posting is a separate, confirmed click |
|
|
258
|
+
| `download(ref? \| text? \| url?)` | Save an export, report or invoice and read it: CSV/TSV and Excel come back as tables, PDFs and text files as text. Saved to a private folder |
|
|
259
|
+
| `browser_settings(profile?, url?)` | For sites with the browser's own sign-in box (HTTP Basic/NTLM) or a self-signed certificate: opens a small window where **you** type that password and can trust the certificate. The agent never sees it |
|
|
260
|
+
| `dismiss_popup` | Close the pop-up on top with its close / "Not now" button |
|
|
261
|
+
| `handoff(reason?)` | Open the current page in a window for **you** (CAPTCHA, 2FA, expired login); the session picks up your fresh login |
|
|
262
|
+
| `read_text` | The visible text of the page, marked as untrusted website content |
|
|
263
|
+
| `screenshot(ref?, full_page?)` | Screenshot of the page or one element, shown to the agent and saved to a file |
|
|
264
|
+
| `set_screenshots(enabled)` | Attach a screenshot to every step (great for watching a run) |
|
|
265
|
+
| `switch_tab` | Popups become the active tab automatically; this switches back |
|
|
266
|
+
| `save_session` / `close_session` | Write refreshed cookies back to the vault (closing saves by default) |
|
|
267
|
+
| `evaluate` | Run JavaScript in the page. **Off** unless `ASSERTIFY_ALLOW_EVAL=1` |
|
|
268
|
+
|
|
269
|
+
### 👀 Seeing what the agent sees
|
|
270
|
+
|
|
271
|
+
Ask *"show me a screenshot"* or *"open my GitHub session with screenshots on so I can follow along"*. Images appear in the chat in clients that show them, and every one is saved to `~/.assertify-mcp/screenshots/<session>/NNN-<step>.jpg`. The folder is private to you and cleared after 7 days.
|
|
272
|
+
|
|
273
|
+
## 👥 Several accounts on one site
|
|
274
|
+
|
|
275
|
+
Log in once per account. Leave the name out and Assertify names each login after the email address it finds on the page (falls back to `google.com-1`, `google.com-2`… when a site doesn't show one):
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
assertify login https://mail.google.com # sign in as you@gmail.com -> saved as "you@gmail.com"
|
|
279
|
+
assertify login https://mail.google.com # sign in as work@gmail.com -> saved as "work@gmail.com"
|
|
280
|
+
assertify login work https://mail.google.com # or pick the name yourself
|
|
281
|
+
assertify list google.com # just the Google logins
|
|
282
|
+
assertify rename google.com-1 side-project
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Then just ask:
|
|
286
|
+
|
|
287
|
+
- *"Check my work@gmail.com inbox"*: the agent opens that one login.
|
|
288
|
+
- *"Any unread mail in any of my Gmail accounts?"*: the agent calls `open_sessions(site="google.com")` and gets one session per account, which it can work through side by side.
|
|
289
|
+
|
|
290
|
+
Each account lives in its own encrypted file and its own isolated browser, so cookies never mix. On sites that restrict automation, avoid running the same action on many accounts at once.
|
|
291
|
+
|
|
292
|
+
## 📎 Uploading files
|
|
293
|
+
|
|
294
|
+
Tell your agent what to upload and where:
|
|
295
|
+
|
|
296
|
+
> *"Post the photos in ~/Pictures/trip to my Facebook with the caption 'Back home'"*
|
|
297
|
+
|
|
298
|
+
The agent opens the composer, calls `upload_files(paths=["~/Pictures/trip"], text="Photo/video")`, writes the caption, then asks you before pressing **Post**. Under the hood:
|
|
299
|
+
|
|
300
|
+
1. **File dialog first.** It clicks the upload button with a natural mouse move and picks the files in the browser's file dialog, the same events a site sees when you do it.
|
|
301
|
+
2. **Hidden upload field next.** Most sites hide the real `<input type="file">` behind a button; Assertify finds the one near what the agent pointed at, or the one that accepts these file types.
|
|
302
|
+
3. **Drag and drop last.** For drop-only areas it drops the files like a drag from your desktop (up to 50 MB).
|
|
303
|
+
|
|
304
|
+
A folder means every file in it, subfolders included, skipping hidden files like `.DS_Store`. Single-file fields, file-type limits (`accept="image/*"`) and folder pickers are respected.
|
|
305
|
+
|
|
306
|
+
**What can never be uploaded:** anything in hidden folders (`~/.ssh`, `~/.aws`, …), key, certificate, password-manager and `.env` files, browser cookie stores, and Assertify's own vault. Paths must be full paths the user gave; agents are told never to upload a file because a page or email asked. Limits: 100 files and 2 GB per call. To allow only certain folders, set `ASSERTIFY_UPLOAD_DIRS`.
|
|
307
|
+
|
|
308
|
+
## 📥 Downloads, intranets and app logins
|
|
309
|
+
|
|
310
|
+
- **Downloads:** ask for "last month's CSV export" and the agent calls `download(text="Export CSV")`, or `download(url=...)` for a direct file link, or `download()` for a file the page just started. Files go to `~/.assertify-mcp/downloads/<session>/` (mode `0600`, deleted after 7 days; `ASSERTIFY_DOWNLOAD_DIR` picks another folder). Only links on the login's allowed domains can be fetched, 100 MB at most, and files are only read, never opened or run.
|
|
311
|
+
- **The browser's own sign-in box and self-signed certificates** (intranets, routers, NAS boxes, old admin pages): the agent calls `browser_settings`, a small window opens, and you type the username and password and can tick "trust this site's certificate". They're saved encrypted with the login, sent only to that site, and never shown to the agent or in `list_profiles`.
|
|
312
|
+
- **Apps that keep their login outside cookies** (WhatsApp Web, Teams and other web apps that use IndexedDB or sessionStorage): the login window saves those too, including files, binary data and encryption keys in IndexedDB, so the agent's session stays signed in. Sites that hide their storage from page scripts (Discord) work too. The one thing that can't be copied is an encryption key the browser marks as locked; the login then says which site data was left behind.
|
|
313
|
+
|
|
314
|
+
## 🧍 Staying unnoticed
|
|
315
|
+
|
|
316
|
+
Sites flag browsers that act like robots. Assertify tries hard not to:
|
|
317
|
+
|
|
318
|
+
- **Real Chrome** when it's installed (falls back to Chromium), without the "controlled by automated software" flag or `navigator.webdriver`.
|
|
319
|
+
- **Your fingerprint.** The user agent, language and time zone from your login window are reused by the agent's browser, so the site keeps seeing the same "device".
|
|
320
|
+
- **Human input.** The mouse travels along a curve to a random spot inside each button, keys are typed one by one at uneven speed, scrolling comes in flicks, and there are short random pauses between actions and between page loads.
|
|
321
|
+
- **A speed limit.** Page loads per login per hour are capped (400 by default, 60 on sites that ban automation).
|
|
322
|
+
|
|
323
|
+
Set `ASSERTIFY_HUMANIZE=0` to turn the human input off (faster; fine for your own tools and test sites).
|
|
324
|
+
|
|
325
|
+
## 📱 On your phone
|
|
326
|
+
|
|
327
|
+
Assertify also runs on iPhone and Android, for agents on your phone such as Claude mobile or ChatGPT/Codex.
|
|
328
|
+
|
|
329
|
+
1. Run the relay on a computer or server: `assertify relay --public-url https://relay.example.com` (or no flag on your home Wi-Fi). It prints an MCP URL and a pairing link.
|
|
330
|
+
2. Install the app from [`mobile/`](mobile/), tap **Pair** and paste the link.
|
|
331
|
+
3. Log in to sites in the app's browser by hand.
|
|
332
|
+
4. Add the MCP URL to your agent as a custom connector and ask away: "check my LinkedIn messages".
|
|
333
|
+
|
|
334
|
+
The agent works in a hidden browser on the phone, with the same tools, pop-up handling and confirmations as on the desktop. Your logins never leave the phone; the relay only passes commands and page snapshots. Anyone with the relay token can use your logged-in sites, so keep it secret and use HTTPS when the relay is on the internet. See [mobile/README.md](mobile/README.md).
|
|
335
|
+
|
|
336
|
+
## 🪟 Pop-ups, dialogs and expired logins
|
|
337
|
+
|
|
338
|
+
- **Cookie banners** are answered with "Reject all" / "Only necessary" automatically (`ASSERTIFY_AUTO_CONSENT=0` to turn off).
|
|
339
|
+
- **Other pop-ups** (newsletter, "share your contact info", app promos) are listed first in the snapshot under `## Pop-up` with a warning. The agent decides based on your request, or calls `dismiss_popup`. A click on something hidden behind a pop-up is refused with a hint instead of silently hitting the overlay.
|
|
340
|
+
- **Browser dialogs** (`alert`, `confirm`, "Are you sure?") are cancelled and reported; the agent repeats the click with `accept_dialog=true` when it means it.
|
|
341
|
+
- **CAPTCHAs, 2FA and expired sessions** show a warning in the snapshot. The agent never tries to solve them: it calls `handoff`, a window opens on your screen at the same page, you finish, close it, and the agent continues with the new cookies.
|
|
342
|
+
|
|
343
|
+
## ⚖️ Use it responsibly
|
|
344
|
+
|
|
345
|
+
Many sites' terms forbid automated access. LinkedIn, Facebook, Instagram, Threads, X and TikTok say so explicitly and do restrict accounts for it. Assertify recognises these sites, runs slower on them, caps page loads lower and tells the agent to keep tasks small and personal. Light personal use (checking your own messages, updating your own profile) is usually fine; scraping, bulk messaging or mass connecting is how accounts get restricted. You are responsible for following the terms of the sites you use.
|
|
346
|
+
|
|
347
|
+
## 🔒 Security
|
|
348
|
+
|
|
349
|
+
- **Storage:** `~/.assertify-mcp/profiles/<name>.enc`, AES-256-GCM, file mode `0600`, folder `0700`. The profile name is bound into the ciphertext, so files can't be swapped or renamed.
|
|
350
|
+
- **Key:** a random 256-bit key in your OS keychain (macOS Keychain, Windows Credential Locker, Linux Secret Service). On machines without one, set `ASSERTIFY_PASSPHRASE` to derive the key with scrypt instead.
|
|
351
|
+
- **Domain guard:** navigation outside a login's `allowed_domains` is blocked. By default that's the login URL's domain and its subdomains; add more with `--domain` (e.g. for SSO).
|
|
352
|
+
- **No passwords in context:** password fields show as `value=<hidden>` in snapshots.
|
|
353
|
+
- **No telemetry, no server, no account.** Nothing leaves your machine except your own browser traffic to the sites you use.
|
|
354
|
+
- **Ask before acting:** buttons labelled send, post, buy, pay, delete, connect, follow, apply and similar, and pressing Enter in a message box, are paused until the agent calls again with `confirmed=true`, which it is told to do only after you approve.
|
|
355
|
+
- **Read-only logins:** `assertify login <name> <url> --read-only` (or `read_only=true` in `start_login`) lets agents browse and read but never type, submit, send or buy.
|
|
356
|
+
- **Browser sign-in passwords** (`browser_settings`): typed by you in a separate window, stored in the encrypted vault next to the cookies, sent only to the site they're for, and never returned to the agent. Trusting a certificate is your choice per login, in that same window.
|
|
357
|
+
- **Uploads:** only full paths, never secrets (hidden folders, keys, `.env`, cookie stores, the vault), symlinks resolved first, optional folder allowlist (`ASSERTIFY_UPLOAD_DIRS`). Read-only logins can't upload.
|
|
358
|
+
- **Untrusted pages:** page text is returned wrapped and labelled as website content, and agents are told never to follow instructions found in pages, emails or messages. Only `http(s)` pages can be opened. Files are saved only through `download`, and their contents are labelled as website data too.
|
|
359
|
+
|
|
360
|
+
> [!WARNING]
|
|
361
|
+
> A saved session is as powerful as being logged in. Anyone who can run code as your user while your keychain is unlocked can use it, the same as your browser's own cookies. Only connect agents you trust.
|
|
362
|
+
|
|
363
|
+
Found a vulnerability? See [SECURITY.md](SECURITY.md).
|
|
364
|
+
|
|
365
|
+
## 🎛️ Configuration
|
|
366
|
+
|
|
367
|
+
| Variable | Effect |
|
|
368
|
+
|---|---|
|
|
369
|
+
| `ASSERTIFY_HOME` | Vault location (default `~/.assertify-mcp`) |
|
|
370
|
+
| `ASSERTIFY_PASSPHRASE` | Use a passphrase instead of the OS keychain |
|
|
371
|
+
| `ASSERTIFY_HEADED=1` | Show the agent's browser window (debugging, or sites that block headless) |
|
|
372
|
+
| `ASSERTIFY_SCREENSHOTS=1` | Screenshot every step by default |
|
|
373
|
+
| `ASSERTIFY_NO_AUTO_INSTALL=1` | Never download Chromium automatically |
|
|
374
|
+
| `ASSERTIFY_ALLOW_EVAL=1` | Enable the `evaluate` tool |
|
|
375
|
+
| `ASSERTIFY_ALLOW_ANY_DOMAIN=1` | Turn off the domain guard |
|
|
376
|
+
| `ASSERTIFY_HUMANIZE=0` | Turn off human-like mouse, typing and pauses |
|
|
377
|
+
| `ASSERTIFY_BROWSER` | `chrome`, `chromium` or `msedge` (default: Chrome if installed, else Chromium) |
|
|
378
|
+
| `ASSERTIFY_MAX_PAGES_PER_HOUR` | Page-load cap per login (default 400; 60 on strict sites) |
|
|
379
|
+
| `ASSERTIFY_AUTO_CONSENT=0` | Don't auto-decline cookie banners |
|
|
380
|
+
| `ASSERTIFY_UPLOAD_DIRS` | Only allow uploads from these folders (separated by `:` on macOS/Linux, `;` on Windows) |
|
|
381
|
+
| `ASSERTIFY_MAX_UPLOAD_MB` | Upload size cap per call (default 2048) |
|
|
382
|
+
| `ASSERTIFY_DOWNLOAD_DIR` | Where `download` saves files (default `~/.assertify-mcp/downloads`, cleaned after 7 days) |
|
|
383
|
+
|
|
384
|
+
CLI: `assertify setup | login [name] <url> [--domain d] [--read-only] | list [site] | rename <name> <new> | delete <name> | serve`.
|
|
385
|
+
|
|
386
|
+
## ❓ FAQ
|
|
387
|
+
|
|
388
|
+
**Does it work with 2FA, passkeys and "Sign in with Google"?**
|
|
389
|
+
Yes. You log in yourself in a real browser window, so anything you can do there works. Add the SSO domain with `--domain` if the site bounces through one.
|
|
390
|
+
|
|
391
|
+
**Why did a site log me out?**
|
|
392
|
+
Sessions expire on the site's own schedule, and some sites ask for a CAPTCHA or 2FA again now and then. When that happens the agent calls `handoff` and a window opens for you to sign in again; nothing else to do. Assertify also saves refreshed cookies when a session closes, which keeps rolling sessions alive. If a site keeps rejecting the headless browser, set `ASSERTIFY_HEADED=1`.
|
|
393
|
+
|
|
394
|
+
**Will a site redesign break my agent?**
|
|
395
|
+
Less than you'd think. The agent reads the page fresh every step and can click by visible text (`click(text="Messages")`) instead of fixed selectors, so it adapts like a person would. A full redesign can still confuse it; it will tell you what it sees.
|
|
396
|
+
|
|
397
|
+
**Can I use it from my phone or a server?**
|
|
398
|
+
Not yet. Assertify is local-first by design: your sessions never leave your computer.
|
|
399
|
+
|
|
400
|
+
**Known limits**
|
|
401
|
+
A few apps keep their sign-in in encryption keys the browser won't let anyone copy; those logins can't move to the agent's browser (the save message says so). No anti-detection is perfect: a site determined to spot automation may still do so. Snapshots don't look inside iframes or closed shadow roots yet.
|
|
402
|
+
|
|
403
|
+
## 🤝 Contributing
|
|
404
|
+
|
|
405
|
+
Issues and PRs are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). The short version:
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
uv sync && uv run assertify setup
|
|
409
|
+
uv run pytest && uv run ruff check .
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## 📄 License
|
|
413
|
+
|
|
414
|
+
[MIT](LICENSE) © Roman Klym
|