Just type an address after itda and your AI can use the site.
English | 한국어 | 日本語 | 简体中文 | 繁體中文 | Español | Français | Deutsch | Português (Brasil) | Tiếng Việt | Bahasa Indonesia | Русский
Give itda one address. It learns what the site can do and tries it. It gives your AI agent a short list of actions in plain words. Reads just work. Nothing changes on the site until you say yes. It runs on your own computer, and it works on the old intranet behind a login too.
It is for the sites your company runs and the services you have an account on, not the web at large.
The same run as text
$ itda news.ycombinator.com
1/5 Looking at news.ycombinator.com...
The page opens without signing in.
Found: a list of 30 records, a form, a feed.
2/5 What agents will be able to do:
read home List what Hacker News shows (inputs: p)
read feed Read the RSS feed
3/5 Trying every read once...
✓ home 30 records
✓ feed ok
4/5 Saved as 'news-ycombinator'.
Connect Claude Desktop? [Y/n]
5/5 Adding itda to Claude Desktop...
Added itda to Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json).
Open Claude Desktop and itda will be there.
Done. Ask your agent for something on 'news-ycombinator', for example "use news-ycombinator to list what Hacker News shows".Ask your agent: "use news-ycombinator to list what Hacker News shows". A site behind a sign-in form asks for your user name and password instead. itda fills in the site's own form for you and keeps the session on your computer. If that is not enough (single sign-on, a one-time code), your own browser opens. You sign in and use the site as usual while itda watches.
Status: early, and used against real sites. The runtime, CLI, MCP server, approvals (in a terminal or inside the agent app), audit log, drafting from OpenAPI documents, browser recordings or fetched pages, API keys, browser sign-in and CSRF preflight steps work today. Signed site packs are next. See docs/ARCHITECTURE.md.
Agents that drive a browser re-read every page and spend thousands of tokens on each screenshot or accessibility snapshot. They click whatever the page tells them to and break silently when a layout changes. itda moves that work to an install step:
| Browser-driving agent | itda | |
|---|---|---|
| What the agent sees | Whole pages, every step | One action's typed result, capped at 24 KB |
| A changed or signed-out page | Read as data | Detected from the declared response (response_mismatch, auth_required) |
| Changes on the site | Whatever the model decides | Single-use approval for exact inputs, granted by a person in a terminal or the agent app |
| Text on the page saying "ignore previous instructions" | In the prompt | Marked untrusted data, never a tool |
| Credentials | Typed into the page by the model | In the OS keyring; the agent sees account ids only |
| Being blocked | Retry, rotate, imitate a browser | Try the read once more in your own installed browser, remember what worked, pause nothing |
| What happened | Chat history | Hash-chained audit log you can verify |
Every line here is true of the code today. itda setup says which case it met;
itda draft page or itda record say what they left out.
Works today
- Pages drawn on the server: tables, lists and repeated cards become records, and a page that is one thing becomes a read of its fields. Search forms become reads, post forms become changes, and feeds come along.
- Pages that embed their data for their scripts (
__NEXT_DATA__,ld+json, an Apollo or preloaded state): one read per list in it. - Pages drawn by scripts that call an API answering JSON, a React or Vue portal
say:
itda setupopens your browser on the page and watches what it loads by itself. It drafts from those calls. It does not click or type. - Forms that want a CSRF token: a preflight step reads a fresh one from the form page right before each approved post.
- Pages that are a shell around frames: the content sits in an
<iframe>on the same site, often behind its own sign-in. itda reads each frame as a page of the site and signs in first when the frame asks for it. - Cookie sessions: a sign-in through the site's own form with your user name
and password, or your own browser for single sign-on and one-time codes. A
session carried in the address (
?sid=...), and a token a single-page app keeps in the browser and sends asAuthorization: Bearer, work the same way. - An OpenAPI or Swagger description the site publishes, and a GraphQL endpoint that answers introspection: one read per operation or query field.
- Sites that run GitLab, Jira, Confluence, Redmine, Discourse, MediaWiki, WordPress, Gitea or Forgejo: their own API, from a ready-made template.
- Files in and files out: a link the page labels as an attachment becomes a read that saves the file on your computer and returns its path, size and hash, never the bytes. A form with a file control becomes a change that sends a file you name; you approve it by name, size and hash.
- Anything you recorded yourself using, in your browser or as a HAR file.
- Pages behind a challenge page, when the browser on your computer gets past it. itda tries the read once more inside that browser, hidden, kept for that one site and account, and remembers what worked, so the next call starts there. The browser stays warm between CLI calls and closes after five idle minutes; changing a search term does not restart it. Everything the page loads passes the same address and read checks as a direct request. See the transport policy.
Works after you record it
- A page that shows a plain fetch nothing and loads nothing by itself. It
fetches only after you click, search or sign in.
itda recordwatches you use it and drafts from the calls it saw.
Does not work
- WebSocket. itda speaks plain HTTP, and a recording keeps no socket traffic.
- APIs that sign every request (a signature or nonce computed per call). itda keeps a fixed token or API key in the keyring. It does not reproduce a value that changes with every call, so the site refuses the request.
- Pages assembled from HTML fragments (htmx and the like). Not by design: itda may still read a fragment containing a whole table, but not what the script does with it.
- Content drawn on a canvas or into an image. itda reads text in HTML, JSON and XML; it does not look at pixels.
- A challenge your browser does not get past either, or a captcha. itda does not solve captchas. The read fails and says so; nothing is paused. See docs/HONEST_CLIENT.md.
- Anything your account cannot see. itda has no account of its own; it uses yours.
itda check <site> runs every read needing no input and never sends a change.
It ends with one verdict: ok, signed_out, site_changed, blocked or
unreachable. The exit code follows it (11 for site_changed), so a scheduled
job can warn you before an agent runs into it. Over MCP, itda_check returns
the same. itda fsck <site> reads the local record itda keeps of what it tried
(shapes and verdicts, never values) and lists what was never tried, what needs
an input and what fails now and then, each with the command to run.
When the verdict is site_changed, itda refresh <site> drafts the site again
from what it was set up from: its pages, the API description, the GraphQL
endpoint, or the kept shape of the recording, every value blanked. It shows what
would change per action and writes nothing until you say yes. itda keeps actions
you edited by hand and updates actions the new draft changed. It drops an action
the sources no longer draft only if its read also failed, and tries each new
action once, adding it if it answers. itda withdraws approvals waiting for the
site and checks the site again right after. A definition installed with
itda add has no sources and cannot be refreshed; edit it by hand.
That is the trade-off. A browser agent adapts on every run, and is slow and
nondeterministic for it. itda is one request and the same answer every time;
after a challenge it is one more try in your browser, and the same answer still.
It breaks loudly when the site moves. docs/TRANSPORT_BENCHMARKS.md has the
numbers, including the one itda does not yet meet.
Playwright MCP, browser-use and computer use drive a browser on every task. itda does not replace them: a page you will use once, or a site from the list above that itda does not cover, is theirs. itda is for the site an agent will call again and again. Record it once in a browser, review the actions, then run them over HTTP with a typed answer, a single-use approval for every change and a log. Use both: a browser agent to explore, itda for the calls that run every day.
# macOS and Linux
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/brnyxx/itda-cli/releases/latest/download/itda-cli-installer.sh | sh# Windows
powershell -ExecutionPolicy Bypass -c "irm https://github.com/brnyxx/itda-cli/releases/latest/download/itda-cli-installer.ps1 | iex"Download an archive from
Releases, or build from source
with cargo install --git https://github.com/brnyxx/itda-cli itda-cli.
Claude Desktop, with no terminal: download the bundle for
macOS (Apple silicon),
macOS (Intel) or
Windows and open the file. Claude
Desktop installs itda with its tools, and "use the-site-you-use.example" sets
a site up from inside the conversation. Approving a change still happens in a
terminal with itda approve, so for changes install the command too.
Type itda and the address of a site you use. It finds agent apps on your
computer and connects them after one Enter. It speaks twelve languages: LANG
picks one and ITDA_LANG overrides it. A bare itda asks for the address;
itda setup <address> takes flags for scripts.
itda recognises sites that run known software. When the page says it is a
GitLab, Jira, Confluence, Redmine, Discourse, MediaWiki, WordPress or Gitea
(also Forgejo), and one request to that software's own API confirms it, the
agent gets that software's real API from a ready-made template
(sites/software/), signed in the way that software does it.
What the page itself shows joins it. itda setup --as jira forces a template;
--no-template reads the page like any other.
A page drawn by scripts shows itda only a title, so itda opens your browser on it for a few seconds. It watches what the page loads by itself and drafts from those calls. Nothing is clicked or typed, and the window closes on its own. Only a page that still shows nothing, or needs a sign-in, asks you to use the site in your browser while itda watches.
Once an agent is connected, you can also just ask it: "set up
news.ycombinator.com for me". It calls itda_setup. itda looks at the site,
drafts and tries it the same way. Its actions appear as tools in the same
conversation. A scripted page opens briefly in a browser window on your screen.
When a read says to sign in, itda_login opens your own browser; the agent only
sees the account id. When setup keeps one read or none, the agent can draft the
site itself from its docs with itda_prepare: it sees shapes and evidence, never
what the site holds, and itda keeps only what the evidence names and what answers.
Skip the terminal and hand this to Claude Code, Cursor or Codex, with the address of a site you use:
Install itda and set up the-site-you-use.example for me.
1. Install it: curl -LsSf https://github.com/brnyxx/itda-cli/releases/latest/download/itda-cli-installer.sh | sh
(on Windows: irm https://github.com/brnyxx/itda-cli/releases/latest/download/itda-cli-installer.ps1 | iex)
2. Run: itda setup https://the-site-you-use.example --client claude-code -y
(use cursor, codex or claude-desktop if that is what you are)
3. Read https://github.com/brnyxx/itda-cli/blob/main/AGENTS.md, then use the site through itda.
Reads just work. Anything that changes the site waits for my approval.
If a read says to sign in, tell me to run: itda login <site> --browser
The pieces are commands of their own. For pages you can open without signing in, give itda their addresses. Tables, lists and repeated cards become records. A page that is one thing (a package, an article, a product) becomes a read of its headline, body and labelled values. A list whose links share one shape gets a read of one record by its id. Search forms become reads; post forms become changes. Feeds come along. A GraphQL endpoint the page names is asked once what it offers, one read per query field. When the site publishes an API description, its reads join the draft:
itda draft page https://example.com/board --install # shows what it found on the page
itda call example.get-board --page 2 --json
itda check example # does the page still match?For a site behind a sign-in form, give itda setup the page and your user name.
It sends the site's own form once, with the password you type. It
keeps the session in the system keyring and drafts from the page as you see it
signed in. The definition remembers the form, so itda login board signs in
again when the session expires:
itda setup https://intranet.example/board --name board --user me # asks for the password
itda call board.list-notices --json # reads run right away
itda call board.create-notice --title "Server maintenance" # changes wait for your approval
itda login board --user me # when the session expiresFor single sign-on or a one-time code, record yourself using the site instead. itda opens your own browser in a fresh window. It watches what the pages ask the server for while you sign in, search and post a form; it never clicks or types:
itda record https://intranet.example/board --name board --install # press Enter when done
# then say yes to keep the session or token it used
itda mcp install claude-desktop # or let your agent call themexamples/lab runs Gitea, Redmine, WordPress, MediaWiki, a small legacy
intranet and three uglier cases behind their sign-ins: a board inside an iframe,
a table a script draws from an XHR, and a session carried in the URL. A test
suite walks each from setup to an approved change.
The recording stays in memory and only the shape of each call reaches the
draft: no header, cookie or value you typed. A HAR file saved from the
browser's developer tools works the same way with itda draft har.
Public APIs need no recording. These ready-made sites from sites/ are built in and need no account:
itda add github # or npm, crates-io, pypi
itda call github.repo --owner rust-lang --repo rustTry the bundled sandbox, a fake intranet that only listens on 127.0.0.1: follow
examples/sandbox. A site definition is one YAML
file. itda add spec.yaml installs it after showing where it sends requests and
which actions change the site. itda doctor prints the data directory's
location. docs/SPEC.md explains how to write a definition.
itda setup <url> # page or recording -> named actions -> tried -> agent, with questions
itda draft page <url>... [--install] # draft a site from the pages you use
itda record <url> [--install] # or from using it in your browser, behind a sign-in
itda draft har <file> / openapi <file> # or from a saved recording or an API description
itda add spec.yaml / remove <site> # install or remove a site (also a URL or github)
itda ls # installed sites and their actions
itda describe <site> # their inputs and a command to copy each
itda call <site>.<action> --input value # run one; --json for agents
itda login <site> --user me # sign in through the site's own form (password from stdin or a prompt)
echo "$KEY" | itda login <site> --secret api-key # or store an API key in the OS keyring
itda login <site> --browser # or sign in in your own browser, once (sealed under the data directory)
itda accounts / logout <site> # see or remove saved sign-ins
itda approvals / approve <id> / reject <id>
itda audit [--verify] # what agents did
itda check [<site>|--all] # does every read still match the site?
itda refresh <site> # a site changed: draft it again, keep what you decided
itda resume <site> # after a pause in direct-HTTP mode, or an expired key
itda feedback # report a problem: opens a new issue, filled initda mcp install claude-desktop # or cursor, vscode, codex
itda mcp install claude-code # prints the `claude mcp add` command
# add --approve-in-client to approve changes in the client's own form
# itda mcp --site <name> serves one site only, for a project's own configurationThe installer merges into the client's config and keeps your other servers. It
backs up the old file as .bak. --dry-run shows the change first. itda mcp
serves every installed site:
- Up to 30 actions appear as one tool each (
sandbox.list-notices), with input schemas and read-only, destructive and idempotent hints. - Above that, three stable tools (
itda_search,itda_describe,itda_call) keep the tool list short and prompt caches valid.--modeoverrides this. - Results are the same JSON envelope as
itda call --json, marked untrusted. - A change returns
approval_requiredwith an id. No tool approves; the agent has to ask you.
Agents that only have a shell can call itda call … --json and branch on the
exit code:
| Exit | Meaning |
|---|---|
| 0 | ok |
| 2 | bad input or unknown action |
| 10 | sign in again (itda login) |
| 11 | the page no longer matches its definition |
| 12 | the site refused this read, in the browser too; paused: false means nothing to resume |
| 13 | rate limited |
| 14 | the site refused this one request as not allowed; signing in again will not help |
| 20 | needs approval, or the user has not answered yet (or changes are off) |
| 21 | approval used, rejected, expired or withdrawn because the inputs changed |
| 22 | itda sent a change but its outcome is unknown; with outcome_check, the read the change names says whether it landed |
| 30 | network error |
- Local first. Definitions run on your device. Credentials never leave it.
- Read-only by default. An action is a read only when its method, name, path, inputs and GraphQL document all agree. Everything else is a change.
- People approve changes. Each approval covers one request with exact inputs, account, definition and credential. It works once and expires.
- A challenge gets one more try, in your browser. A read the site answers
with a challenge page runs once more inside the browser installed on your
computer; itda remembers what worked. A change is sent once and never through
the browser.
ITDA_TRANSPORT=httpkeeps direct HTTP only. See docs/HONEST_CLIENT.md. - Verified actions. A definition declares what a correct response looks like, so "signed out" and "the site changed" are errors, not data.
Crates depend only on crates below them; cargo xtask check-layers enforces it.
| Layer | Crate | Purpose |
|---|---|---|
| 0 | itda-types |
Error contract, URL checks (Public Suffix List), redaction |
| 1 | itda-html, itda-store |
Script-free HTML extraction; the local SQLite store |
| 2 | itda-spec |
Definition format, request building, response verification |
| 3 | itda-policy |
Read/write classification, definition safety checks, approvals |
| 4 | itda-browser |
Sign-in, recording, isolated browser read transport |
| 5 | itda-net |
Adaptive transport, strategy cache, pacing, private-address guard |
| 6 | itda-vault |
Accounts and secrets in the OS keyring or environment |
| 7 | itda-runtime |
Runs actions: credentials, approvals, shaping, audit log |
| 9 | itda-setup |
The setup, sign-in and check steps, for the CLI and the MCP server alike |
| 10 | itda-mcp |
MCP server over stdio |
| 11 | apps/cli |
The itda command |
cargo test --workspace --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo xtask check-layersrust-toolchain.toml pins Rust 1.98 (edition 2024). Tests only talk to local
servers they start themselves. Commit and PR rules are in
CONTRIBUTING.md.
docs/THREAT_MODEL.md lists what itda defends against and what it does not. Report vulnerabilities as described in SECURITY.md.
Licensed under Apache-2.0. Parts of this project are derived from Hycli (Apache-2.0); see NOTICE. Each derived file says so in its header and describes what changed. This project is not affiliated with Hycli and does not use its name or marks.

