@trusty-squire/mcp 1.1.17-rc.1 → 1.1.17
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.
- package/README.md +10 -422
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,78 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
<h1 align="center">Trusty Squire</h1>
|
|
8
8
|
|
|
9
|
-
<p align="center">
|
|
10
|
-
<a href="https://www.npmjs.com/package/@trusty-squire/mcp"><img src="https://img.shields.io/npm/v/@trusty-squire/mcp?logo=npm&color=cb3837" alt="npm version" /></a>
|
|
11
|
-
<a href="https://www.npmjs.com/package/@trusty-squire/mcp"><img src="https://img.shields.io/npm/dm/@trusty-squire/mcp?color=cb3837" alt="npm downloads" /></a>
|
|
12
|
-
<a href="https://github.com/Trusty-Squire/trusty-squire/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/Trusty-Squire/trusty-squire/ci.yml?branch=main&label=CI&logo=github" alt="CI status" /></a>
|
|
13
|
-
<a href="https://github.com/Trusty-Squire/trusty-squire/stargazers"><img src="https://img.shields.io/github/stars/Trusty-Squire/trusty-squire?logo=github&color=eac54f" alt="GitHub stars" /></a>
|
|
14
|
-
<a href="https://github.com/Trusty-Squire/trusty-squire/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="license" /></a>
|
|
15
|
-
</p>
|
|
16
|
-
|
|
17
9
|
<p align="center"><strong>Empower agents with auth and payments.</strong></p>
|
|
18
|
-
<p align="center">MCP tools to automate auth and pay — your keys and card stay out of agent context.</p>
|
|
19
|
-
|
|
20
|
-
Trusty Squire is an **MCP server that lets Claude Code, Codex, Cursor, OpenCode, Goose, and other coding agents sign up, provision, and purchase on your behalf**. It opens a real browser, works through signup, sign-in, setup, and checkout flows one step at a time, clears the bot-detection and email-verification steps that make operator tools stall, and hands the job back to a person only when one is actually required. That covers wiring up OAuth and API keys for the app you're building as much as it covers paying a checkout, sending a gift, or booking something — the same operator primitives drive all of it.
|
|
21
|
-
|
|
22
|
-
Provider secrets and payment cards are write-only: the agent's credential tools return references and authenticated results, never stored plaintext. The raw secret never needs to enter the agent's context, so it can't be pasted into a commit, leaked in a log, or read back out over chat. When a task genuinely needs the plaintext somewhere the agent controls, `fetch_credential` releases it — for one credential, once, and only after you sign that exact request with your passkey. Backend access is a host-scoped, rate-limited, independently revocable grant, so a leaked token is killed without rotating the provider key — and you connect Google or GitHub yourself in a real browser, so the agent never types your password. Full [threat model below](#security-and-threat-model).
|
|
23
|
-
|
|
24
|
-
## Direct payment observation
|
|
25
|
-
|
|
26
|
-
The agent drives the live checkout from ordinary browser evidence. It reads the
|
|
27
|
-
amount, currency, DCC choice, card controls, validation errors, requests, and
|
|
28
|
-
rendered state with `operate_observe`, `operate_network`, and
|
|
29
|
-
`operate_screenshot`, then uses the ordinary click, type, select, press, scroll,
|
|
30
|
-
and wait loop to advance the purchase. A spinner is evidence; the operator does
|
|
31
|
-
not translate it into a payment stage.
|
|
32
|
-
|
|
33
|
-
When card fields are ready, call `list_payment_cards` and then
|
|
34
|
-
`inject_card` with the addressed session, the purchase terms, the selected
|
|
35
|
-
`card_ref`, and an observation ref for each field to fill. `inject_card` uses the
|
|
36
|
-
existing single human approval for that purchase, verifies the signed release,
|
|
37
|
-
and opens the card only inside the operator. A pending approval is resumed with
|
|
38
|
-
the returned `approval_id`; retries may supply changed field refs under that same
|
|
39
|
-
still-valid approval. The primitive fills only the named fields and returns a
|
|
40
|
-
per-field `filled`, `not_found`, `detached`, or `native_error` result. Expiry,
|
|
41
|
-
cardholder name, and billing are not inject targets and are not secret: the
|
|
42
|
-
result carries `exp_month`, `exp_year`, `name`, and any stored billing alongside
|
|
43
|
-
`last4`, and you type those with `operate_type`/`operate_select`. It never
|
|
44
|
-
searches for a provider, chooses a saved-card UI, rereads the total, submits,
|
|
45
|
-
clears fields, or diagnoses the checkout.
|
|
46
10
|
|
|
47
|
-
|
|
48
|
-
the actual field (notably Braintree and Stripe). Choose the ref for the visible
|
|
49
|
-
card control, not a helper input. Before placing the order, re-observe and
|
|
50
|
-
confirm that no competing merchant-saved-card radio or option remains selected.
|
|
51
|
-
If a 3-D Secure challenge appears, the operator detects it on the next
|
|
52
|
-
observation or action result, notifies the cardholder once through the purchase
|
|
53
|
-
notification path, and reports `three_ds` with state `challenge_detected`; keep
|
|
54
|
-
observing the live checkout while the cardholder completes it. `three_ds` can
|
|
55
|
-
also carry state `sdk_error_retryable`: no challenge rendered and nothing
|
|
56
|
-
notified, because the processor's own SDK failed to launch its challenge UI
|
|
57
|
-
(e.g. `THREEDS_CARDINAL_SDK_ERROR` in the page's error telemetry). That failure
|
|
58
|
-
is transient — the checkout re-arms, and resubmitting the payment is expected
|
|
59
|
-
to launch the challenge. It is advisory only: nothing is gated, and a detected
|
|
60
|
-
challenge always takes precedence. Once a challenge has rendered in the
|
|
61
|
-
session, the advisory is never reported again, so a resubmit prompt can never
|
|
62
|
-
ride a checkout that already completed one.
|
|
11
|
+
## What you can do
|
|
63
12
|
|
|
64
|
-
|
|
65
|
-
mask for that released PAN and security code. Normal DOM/AX observations, raw
|
|
66
|
-
attribute/subtree reads, network headers and bodies, errors, console evidence,
|
|
67
|
-
and screenshots replace or cover complete values and ordinary PAN prefixes of
|
|
68
|
-
at least eight digits. Merchant last4, brand, name, expiry, billing address,
|
|
69
|
-
amounts, currency/DCC, HTTP errors, API keys, and 3-D Secure controls remain
|
|
70
|
-
visible. The agent re-observes partial fills, retries targets if needed, selects
|
|
71
|
-
currency, clicks place order, and follows 3-D Secure from the same generic
|
|
72
|
-
evidence stream. There is no `operate_payment_status` or operator-owned
|
|
73
|
-
submit/outcome state machine.
|
|
13
|
+
Ask your coding agent to:
|
|
74
14
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
operator does not add a broad secret scanner or claim to defeat that page.
|
|
15
|
+
- **Get an API key.** "Sign me up for Pinecone and get an API key." It signs up, verifies the email, creates the key, and stores it in your vault instead of the chat.
|
|
16
|
+
- **Sign in anywhere.** It uses your Google or GitHub account on any site that offers it.
|
|
17
|
+
- **Buy things.** "Order one Glow Serum from whitejade.xyz." You approve the purchase once, and your card number never reaches the agent.
|
|
18
|
+
- **Call APIs with stored keys.** The agent uses a saved key without ever seeing it.
|
|
19
|
+
- **Finish setup.** OAuth apps, dashboards, and verification steps, end to end.
|
|
81
20
|
|
|
82
21
|
## Install
|
|
83
22
|
|
|
@@ -85,363 +24,12 @@ operator does not add a broad secret scanner or claim to defeat that page.
|
|
|
85
24
|
npx @trusty-squire/mcp connect
|
|
86
25
|
```
|
|
87
26
|
|
|
88
|
-
|
|
27
|
+
Sign in with Google or GitHub, and Trusty Squire adds itself to your coding agent. Restart the agent and ask for what you need.
|
|
89
28
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
`connect` is also the only way to sign in again: `--force-relogin` switches the bound account and `--force-relogin=google` or `--force-relogin=github` refreshes one provider session. It re-checks provider sessions before reporting success; when the shared browser keeps the profile busy, that check uses committed cookies, which can outlive a server-side session. Use `--force-relogin` to refresh a stale session. The detailed [provider-probe and ceremony contract](docs/browser-broker.md) describes completion and failure handling.
|
|
93
|
-
|
|
94
|
-
Local state is kept separately for each connected account, and `connect` pins
|
|
95
|
-
that account in the agent's MCP configuration; connecting another account does
|
|
96
|
-
not replace the first account's state. To remove just one account, run
|
|
97
|
-
`npx @trusty-squire/mcp logout --account=<id>`; without `--account`, `logout`
|
|
98
|
-
removes the most recently connected account. `--account` requires a nonempty
|
|
99
|
-
account ID.
|
|
100
|
-
|
|
101
|
-
To choose a target explicitly:
|
|
29
|
+
To pick the agent yourself:
|
|
102
30
|
|
|
103
31
|
```bash
|
|
104
32
|
npx @trusty-squire/mcp connect --target=codex
|
|
105
33
|
```
|
|
106
34
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
npx @trusty-squire/mcp connect --json --target=codex
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Every exit path answers, including a run that fails before it resolves a target, so the machine channel is never empty and always ends. Each line is a connect report (it carries `state`) unless connect refused the arguments, in which case the one line is `{"error":"usage","message":"…"}` and the exit code is 64 — a rejected flag is not a connection state.
|
|
114
|
-
|
|
115
|
-
Each report carries seven fields:
|
|
116
|
-
|
|
117
|
-
- `terminal` — `false` while the run is still going, `true` on the line that ends it. Exactly one line per run is terminal, and it is the last one.
|
|
118
|
-
|
|
119
|
-
- `state` — one of four:
|
|
120
|
-
- `connected` — the bot's Chrome profile carries a Google session bound to your account. `account` is set. `reason: null` means a post-ceremony provider probe said so; `reason: "cached_cookie_evidence"` means the no-ceremony fast path read the profile's cookie store and opened nothing, so the session is present but not proven live.
|
|
121
|
-
- `needs-sign-in` — a sign-in is outstanding **and Squire holds its live URL**. `sign_in_url` is always a string here; this is the only state that promises one. Non-terminal lines in this state are how a caller gets that URL, and the browser's placement, while both still reach something.
|
|
122
|
-
- `busy` — Squire could not read or use the bot profile to answer, including a ceremony refused because another session holds the browser (the profile gate's refusal, or a resident broker declining to yield). `holder` says who has it. A broker that died mid-ceremony is not this — that is the run failing, and it reports `no-browser` with `reason: "run_failed"`.
|
|
123
|
-
- `no-browser` — no browser here is signed in the way this install needs. `reason` names the cause: the sign-in happened outside Squire's browser or the ceremony ended without the session the operator needs (`provider_session_missing`), the scoped provider refresh you asked for did not land (`requested_provider_missing`), the sign-in returned a different account (`account_mismatch`), the link lapsed (`install_expired`), or the run failed before it could settle (`run_failed`). The one cause with `reason: null` — "nothing here could be shown the page" — is named by `browser_location: {kind:"unreachable"}` instead.
|
|
124
|
-
- `sign_in_url` — the one sign-in URL, or `null`. Always a string when `state` is `needs-sign-in`; a `no-browser` run may also carry one when the install is still open but nothing here could show it.
|
|
125
|
-
- `account` — `{ id, providers }` whenever the run proved which account this machine is bound to, which is not only when it ends `connected`: a `no-browser` run whose provider session or scoped refresh missed, and a `busy` run whose stored session names an account, both carry it. `null` when the run never proved a binding. `account.providers` lists only the provider sessions actually observed: `[]` means the profile was read and held none, and `null` means the probe could not read it at all — so treat `null` as "unknown", never as "none".
|
|
126
|
-
- `holder` — who holds the bot profile, as read: `{kind:"none"}`, `{kind:"other",code:"singleton_lock"|"operation_lease",pid}`, or `{kind:"unknown",reason:"cross_host"}`. `singleton_lock` is a live Chrome on the profile that this run did not launch — its own ceremony window is never reported as a holder; `operation_lease` is another Squire run that claimed it for work (a `--force-relogin` wipe holds the lease with no browser running). A lock left behind by a dead process is `none`, not a holder, and `busy` with `holder:{kind:"none"}` means nothing holds the profile — Squire just could not verify it (see `reason`).
|
|
127
|
-
- `browser_location` — where the ceremony browser actually opened, reported by the code that placed it: `{kind:"host_screen",display?}` (the screen you are at), `{kind:"virtual",url}` (a display nobody is sitting at — `url` is the noVNC address that reaches it, reported on a non-terminal line the moment the browser lands there, while the tunnel is up), `{kind:"unreachable",reason}` (nothing here could be shown the page — also the answer when a ceremony ran but no path ever reported a placement, with `reason` naming the failure), `{kind:"none"}` (no browser was opened), or `{kind:"unknown",reason}` (a browser opened that Squire did not place — `--skip-browser` hands the link to the machine's default browser). Squire decides this; callers do not detect screens.
|
|
128
|
-
- `reason` — only what the five fields above cannot say: `provider_session_missing`, `requested_provider_missing`, `account_mismatch`, `profile_unverifiable`, `install_expired`, `cached_cookie_evidence`, `run_failed`. `null` otherwise.
|
|
129
|
-
|
|
130
|
-
Connect never waits longer than the sign-in link lives — the wait is the link's lifetime counted from the moment connect received it, so a skewed machine clock cannot shorten or overrun it. A `sign_in_url` it hands back is still open; a wait that reached that deadline, or a link the server reported lapsed, is `no-browser` with `reason: "install_expired"` and no URL at all. A ceremony display that outlives its own bound answers the same way, on a terminal line written before connect exits — so a run that wedged before it ever reached that wait still ends the machine channel.
|
|
131
|
-
|
|
132
|
-
Supported targets: `claude-code`, `cursor`, `codex`, `opencode`, `goose`, `cline`, `continue`, and `hermes`.
|
|
133
|
-
|
|
134
|
-
The isolated `operate_*` browser runtime currently requires Linux and a local Chrome connection;
|
|
135
|
-
remote CDP, macOS, and Windows operator sessions are not supported in this migration stage.
|
|
136
|
-
|
|
137
|
-
## What happens
|
|
138
|
-
|
|
139
|
-
1. Your coding agent names the website and the outcome it needs: an account,
|
|
140
|
-
authenticated setup, app publishing, a purchase, a gift, or a booking.
|
|
141
|
-
2. Trusty Squire works through the service flow one step at a time. Every task
|
|
142
|
-
opens its own fresh browser profile and restores the snapshot's non-Google
|
|
143
|
-
signed-in state, so independent sessions can run concurrently without opening
|
|
144
|
-
the canonical login profile. Google state is restored inside the serialized
|
|
145
|
-
`operate_login` boundary; sanctioned Gmail verification
|
|
146
|
-
uses a separate temporary identity browser.
|
|
147
|
-
3. If the flow produces an API key or client secret, Trusty Squire captures it
|
|
148
|
-
into the vault without returning the raw value through its credential tools.
|
|
149
|
-
4. The agent can make an authenticated request, create a host-scoped app grant,
|
|
150
|
-
or use a saved card for a supported checkout after you approve the purchase.
|
|
151
|
-
5. Eligible successful flows can become signed registry skills, so later runs can replay verified steps instead of rediscovering every click.
|
|
152
|
-
|
|
153
|
-
If a site requires phone verification, a hard CAPTCHA, an unresolved 3-D Secure
|
|
154
|
-
challenge, an unsupported payment method, or another human decision, the run
|
|
155
|
-
hands control back and tells you. It does not guess or pretend the task completed.
|
|
156
|
-
|
|
157
|
-
## Supported services
|
|
158
|
-
|
|
159
|
-
Discovery pages are generated only for services with an active skill in the Trusty Squire registry. The first five detailed pages cover Braintrust, Cerebras, Clerk, DeepInfra, and Zilliz Cloud. Each sample has explicit signup evidence in its active registry record and a provider request checked against official API documentation. The service hub also lists every active registry entry; the remaining detail pages stay unpublished until their workflow and unique content pass review.
|
|
160
|
-
|
|
161
|
-
Browse the [active service catalog](https://trustysquire.ai/services). Maintainers can detect registry drift before merging with:
|
|
162
|
-
|
|
163
|
-
```bash
|
|
164
|
-
pnpm seo:verify-services
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
The registry controls which service pages exist. An external list is never used to claim support.
|
|
168
|
-
|
|
169
|
-
## Keep provider keys out of agent context
|
|
170
|
-
|
|
171
|
-
Ask the agent to create a scoped backend grant:
|
|
172
|
-
|
|
173
|
-
```text
|
|
174
|
-
Grant this backend access to Clerk through Trusty Squire with a limit of 100 requests per hour.
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Before minting the grant, make `api.clerk.com` the credential's primary allowed host in the Vault. The egress proxy refuses every other upstream host. This explicit policy step is required when the signup host and provider API host differ.
|
|
178
|
-
|
|
179
|
-
The agent calls the real MCP tool with the service and requested limit:
|
|
180
|
-
|
|
181
|
-
```text
|
|
182
|
-
grant_app_access({
|
|
183
|
-
service: "clerk",
|
|
184
|
-
rate_limit_per_hour: 100
|
|
185
|
-
})
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Rate limits are opt-in. Omitting `rate_limit_per_hour` creates an unlimited
|
|
189
|
-
grant; host scoping, auditing, and revocation still apply. When a grant reaches
|
|
190
|
-
an explicit hourly limit, the proxy returns `429 rate_limited` with
|
|
191
|
-
`scope: "grant"`, `Retry-After`, and window/reset metadata.
|
|
192
|
-
|
|
193
|
-
The proxy passes the provider's response through as it arrives and forwards the
|
|
194
|
-
upstream response headers (cookies, hop-by-hop headers, and the upstream
|
|
195
|
-
content-length removed), so a streaming completion — `stream: true`,
|
|
196
|
-
`text/event-stream` — reaches the app event by event rather than in one block
|
|
197
|
-
once generation has finished. The body is not buffered and carries no size cap.
|
|
198
|
-
|
|
199
|
-
The result contains a host-scoped egress `base_url` and a `token`, not the Clerk secret key. The token is returned once through the MCP result and remains valid until revoked. That means the scoped grant token can enter agent context; it is not the provider key. Move it directly into backend-only deployment secret storage, never browser code, logs, or source control. If you need zero grant-token exposure to the model, use `use_credential` for agent-initiated requests instead. Trusty Squire removes the grant authorization at the boundary and injects the vaulted provider credential into the upstream request.
|
|
200
|
-
|
|
201
|
-
## Security and threat model
|
|
202
|
-
|
|
203
|
-
- Provider credentials are encrypted in the vault and are write-only to agent credential tools. Those tools return references or authenticated results, not stored plaintext. `fetch_credential` is the single exception and it is not the agent's to take: it returns the raw value only after you approve that specific fetch with your passkey, once per approval, with the reveal recorded in the audit ledger.
|
|
204
|
-
- The raw provider key is injected only into the outbound provider request. It does not need to land in chat, generated code, the consuming app, or the project's `.env` file.
|
|
205
|
-
- App grants are host-scoped, auditable, rate-limitable, and independently revocable. A leaked grant can be revoked without rotating the provider key.
|
|
206
|
-
- You connect Google or GitHub in a real browser. Trusty Squire does not ask the coding agent to type those passwords.
|
|
207
|
-
- Saved cards are encrypted in your browser with a passkey-derived key. For a
|
|
208
|
-
purchase, your phone releases the card only after approving the exact purchase
|
|
209
|
-
details shown on the approval page. The caller then observes the live checkout,
|
|
210
|
-
places the order, and verifies the result through the generic operator tools. The API
|
|
211
|
-
temporarily relays only operator-sealed card ciphertext and its signed mandate.
|
|
212
|
-
Trusty Squire's API never receives plaintext PAN or CVV. The normal operator
|
|
213
|
-
read path masks the released PAN (including ordinary formatted spellings and
|
|
214
|
-
prefixes of at least eight digits) and security code before model-facing
|
|
215
|
-
output; hostile transformed page output is outside that narrow boundary. See the
|
|
216
|
-
[security model](https://github.com/trusty-squire/trusty-squire/blob/main/SECURITY.md#client-encrypted-card-data)
|
|
217
|
-
for the signed mandate's binding contract.
|
|
218
|
-
- Browser screenshots and diagnostics remain verbatim except for the released
|
|
219
|
-
card's narrow PAN/security-code mask. Treat all other rendered values as
|
|
220
|
-
potentially sensitive.
|
|
221
|
-
- Trusty Squire does not bypass phone verification, hard CAPTCHAs, 3-D Secure,
|
|
222
|
-
payment authorization, or decisions that belong to a person. It stops for
|
|
223
|
-
human input.
|
|
224
|
-
|
|
225
|
-
See the [security model](https://github.com/trusty-squire/trusty-squire/blob/main/SECURITY.md)
|
|
226
|
-
for the card and credential trust boundaries, and
|
|
227
|
-
[architecture](https://github.com/trusty-squire/trusty-squire/blob/main/docs/ARCHITECTURE.md)
|
|
228
|
-
for the system and data flows.
|
|
229
|
-
|
|
230
|
-
## MCP tools
|
|
231
|
-
|
|
232
|
-
Discover the installed tool inventory and schemas with `tools/list`. For the
|
|
233
|
-
operator surface and input contracts, see
|
|
234
|
-
[operator-tool-surface.md](docs/operator-tool-surface.md).
|
|
235
|
-
The evidence required to qualify an operator build is in
|
|
236
|
-
[operator-acceptance-runbook.md](docs/operator-acceptance-runbook.md).
|
|
237
|
-
Continue a pending card release by re-calling `inject_card` with its returned
|
|
238
|
-
`approval_id`. `operate_screenshot(session_id,
|
|
239
|
-
frame_index?, frame_url_contains?, full_page?)` is a read-only debugging capture
|
|
240
|
-
(page or one isolated frame, e.g. a cross-origin 3-D Secure/captcha challenge)
|
|
241
|
-
returned as an actual MCP image. It returns real pixels, with only injected
|
|
242
|
-
PAN/security-code controls and identified ordinary copies covered after card release.
|
|
243
|
-
For controls visible only in the image, see
|
|
244
|
-
[screenshot-coordinate clicks](docs/operator-tool-surface.md#clicking-a-screenshot-visible-control).
|
|
245
|
-
The maintainer-only `list_extract_failures` → `get_extract_failure`
|
|
246
|
-
DOM-diagnostics pair is excluded from that surface; set
|
|
247
|
-
`TRUSTY_SQUIRE_DIAGNOSTICS=1` in the MCP server environment to opt into the
|
|
248
|
-
diagnostics profile.
|
|
249
|
-
|
|
250
|
-
Operate sessions default to `format:"compact"` observations: a bounded,
|
|
251
|
-
paged `browser-use-control-query` control map containing every actionable
|
|
252
|
-
button, link, textbox, select, checkbox, radio, tab, menuitem, and file
|
|
253
|
-
control, including those outside the viewport. Non-control markup and page text
|
|
254
|
-
are absent from that shape by construction, never redacted. Use `query`,
|
|
255
|
-
`role`, or `cursor` to filter or page the same map, then scroll or act on a
|
|
256
|
-
returned ref. Use `format:"full"` only when the DOM tree is needed for page
|
|
257
|
-
text, attributes, or layout context. Both formats are otherwise verbatim, with
|
|
258
|
-
the same narrow released-card value mask. The authoritative observation
|
|
259
|
-
contract is [observation-model.md](docs/observation-model.md), and the detailed
|
|
260
|
-
full-DOM structure is in
|
|
261
|
-
[browser-use-serializer-port.md](docs/browser-use-serializer-port.md). A browser action can
|
|
262
|
-
require re-observation before a ref is used again. Click, type, select, press,
|
|
263
|
-
and scroll also default to compact observations; pass `format:"full"` on that
|
|
264
|
-
action only when its verbatim DOM is needed. See the observation contract above
|
|
265
|
-
for delta handling and response envelopes. The detailed DOM-tree contract lives
|
|
266
|
-
in [browser-use-serializer-port.md](docs/browser-use-serializer-port.md).
|
|
267
|
-
|
|
268
|
-
- Rejected tool calls return a JSON `error` envelope with a stable `code` and
|
|
269
|
-
message. Malformed and unknown calls fail only that request; they do not stop
|
|
270
|
-
the stdio process or discard the broker-owned operator session.
|
|
271
|
-
`server_unavailable` includes `retry.max_attempts: 1`: retry once, and never
|
|
272
|
-
kill or restart the shared operator process.
|
|
273
|
-
- `operate_start` opens a scoped website session and `operate_observe` reads its
|
|
274
|
-
current state. For a signup, checkout, or other goal-shaped task, call
|
|
275
|
-
`operate_drive` with the goal and facts (or pass `url` to open and drive in
|
|
276
|
-
one call) instead of planning each click and type yourself; resume that
|
|
277
|
-
session with `answer` and/or added facts if it hands back. Drive ordinary
|
|
278
|
-
controls with `operate_click`, `operate_type`,
|
|
279
|
-
`operate_select`, `operate_press`, and `operate_scroll`; use
|
|
280
|
-
`operate_navigate` for scoped navigation. Acting tools target a current `ref`.
|
|
281
|
-
`operate_type` accepts either literal `text` or a protected session `slot`,
|
|
282
|
-
never both. `operate_click` alone may use its guarded internal DOM-dispatch
|
|
283
|
-
fallback after a proven non-dispatch; it is not a public alternative action.
|
|
284
|
-
Frame scope and stale-ref handling remain fail-closed. For click-triggered
|
|
285
|
-
pickers, follow the [picker and popup guidance](docs/operator-tool-surface.md#pickers-and-popup-return).
|
|
286
|
-
Use `operate_login` for atomic OAuth and the username/password lifecycle,
|
|
287
|
-
`operate_extract` to [capture credentials](docs/operator-tool-surface.md#credential-capture-and-retrieval),
|
|
288
|
-
and `operate_fill_credential` to load protected slots. The only mailbox
|
|
289
|
-
access is `operate_read_inbox`, the consent-gated Gmail verification read;
|
|
290
|
-
CAPTCHA solving, general inbox polling, local upload, and specialized cart
|
|
291
|
-
mutation are not operator verbs; inspect and drive the page's ordinary UI or
|
|
292
|
-
hand the task back to the user.
|
|
293
|
-
Browser requests need no host declarations. See the
|
|
294
|
-
[egress contract](docs/operator-tool-surface.md#browser-egress-is-unrestricted)
|
|
295
|
-
for legacy parameter compatibility and the unchanged payment/vault boundaries.
|
|
296
|
-
Operator servers automatically share the [browser broker](docs/browser-broker.md),
|
|
297
|
-
which owns the user's Chrome profile. Before a session starts,
|
|
298
|
-
the operator checks the live Google My Account identity; if the profile is
|
|
299
|
-
signed out, it returns a clear login handoff before navigating to the service.
|
|
300
|
-
If a restart or reconnect leaves the browser profile busy, follow the
|
|
301
|
-
[reconnect recovery guide](docs/DESIGN-warm-browser-reuse.md#recovering-after-reconnect).
|
|
302
|
-
For proxy configuration and shared-browser compatibility, see the
|
|
303
|
-
[broker configuration guide](docs/browser-broker.md#configuration-and-operation).
|
|
304
|
-
Under the browser-use DOM format, an expired, forged, wrong-generation, cross-page, or drifted
|
|
305
|
-
`@e:` handle fails opaquely with `reobserve_required`; re-observe and choose a
|
|
306
|
-
current handle. Under V1, DOM churn returns `target_stale` with the last
|
|
307
|
-
observation generation, `reobserve_required: true`, best-effort label-keyed
|
|
308
|
-
`replacement_candidates`, and `retry_policy: "do_not_retry_old_ref"`.
|
|
309
|
-
Malformed flat-verb calls return `error.code: "invalid_arguments"` without
|
|
310
|
-
ending the shared server process or discarding the active session. For a
|
|
311
|
-
provider login, pass the observed provider-button ref to `operate_login`.
|
|
312
|
-
It retains the product tab across provider-owned popup redirects and closes.
|
|
313
|
-
Completion requires attempt-local navigation evidence from the selected
|
|
314
|
-
provider to the exact declared return path and fixed query. A declared chain
|
|
315
|
-
may contain one return destination or one callback followed by one dashboard;
|
|
316
|
-
in the two-destination case, only the terminal dashboard completes login. An
|
|
317
|
-
unrelated same-origin page, a longer chain, or a chain that returns to the
|
|
318
|
-
provider remains pending. When the authorized completion destination is still
|
|
319
|
-
open, that destination becomes the session's operation page: its post-login
|
|
320
|
-
observation and later page-bound operations (actions, reads, screenshots,
|
|
321
|
-
navigation, verification, and checkout) stay bound to it, leaving the retained
|
|
322
|
-
product tab untouched. An owned tab opened by a later action becomes the
|
|
323
|
-
operation page for following calls. It returns the normal post-login
|
|
324
|
-
observation even if `detail` is `none`; if that observed destination closes
|
|
325
|
-
before handoff, it returns a terminal `oauth_completed` snapshot with refs
|
|
326
|
-
unavailable and directs the host to `operate_observe`, which resumes on the
|
|
327
|
-
retained viable product page. Every OAuth login
|
|
328
|
-
is serialized from action start through completion and a short release cooldown; other
|
|
329
|
-
session work remains parallel. OAuth setup and post-completion DOM settlement
|
|
330
|
-
use a short deadline capped at 30 seconds. Once an authorized OAuth control
|
|
331
|
-
opens an owned provider popup or navigates the product tab, the chooser,
|
|
332
|
-
consent, and 2FA phase receives a fresh human deadline: five minutes by
|
|
333
|
-
default, or the full positive value of
|
|
334
|
-
`TRUSTY_SQUIRE_OAUTH_ACTION_TIMEOUT_MS` when configured. At an active-phase
|
|
335
|
-
deadline, it rechecks captured, attempt-local return evidence:
|
|
336
|
-
an observed return completes the action rather than being reported as pending.
|
|
337
|
-
Only when no such completion evidence exists does the call return a normal
|
|
338
|
-
observation with `oauth.state: "awaiting_human"`, a `reason` naming only what
|
|
339
|
-
was observed, and `next_action: "operate_observe"`; it does not error or close
|
|
340
|
-
the session. A consent screen or a 2FA/verification challenge is usually still
|
|
341
|
-
showing, so re-observe and drive it; the session stays open and usable. A
|
|
342
|
-
denial the provider actually reported (an OAuth `error=` code on the return
|
|
343
|
-
URL) fails the action, with that code in the message. For interrupted clicks,
|
|
344
|
-
retained popups, and pre-dispatch failures, follow the
|
|
345
|
-
[OAuth error and recovery contract](docs/operator-tool-surface.md#using-the-rest-of-the-surface).
|
|
346
|
-
If an observation races the transition, it reports `oauth.state: "in_progress"`
|
|
347
|
-
and directs the host to observe again.
|
|
348
|
-
- Call `inject_card` with the exact observed refs for the card-entry fields.
|
|
349
|
-
It fills only those refs under the existing purchase approval; the agent
|
|
350
|
-
observes partial results and drives every later checkout action itself. Pick
|
|
351
|
-
the real visible field rather than a hosted-provider autofill/focus helper;
|
|
352
|
-
before placing the order, re-observe for a competing selected saved card. A
|
|
353
|
-
rendered 3-D Secure challenge is detected by the operator, which notifies the
|
|
354
|
-
cardholder once and reports `three_ds` with state `challenge_detected`; keep
|
|
355
|
-
observing while the cardholder completes it. The other state,
|
|
356
|
-
`sdk_error_retryable`, reports that the processor's SDK failed to launch the
|
|
357
|
-
challenge UI at all — nothing is notified and nothing is gated; the checkout
|
|
358
|
-
re-arms and a resubmitted payment is expected to launch the challenge.
|
|
359
|
-
- `operate_finish` closes the session with a flat `outcome` enum — never a
|
|
360
|
-
nested union. `none` only closes; `credentials` requires `store` and preserves
|
|
361
|
-
credential extraction and vault storage; `result` requires `summary` or
|
|
362
|
-
`data`. Agent-provided result data is reported information, not proof that a
|
|
363
|
-
login, provisioning operation, or mutation completed. Finish first fences new
|
|
364
|
-
calls and drives the owned terminal transition; callers must not infer closure
|
|
365
|
-
merely from a delivery timeout. Callers should finish promptly instead of
|
|
366
|
-
treating an open browser as durable background state. The current server advertises an additive receipt through `tools/list` with `session_id`,
|
|
367
|
-
`operation_id`, `execution`, `mutation`, `cleanup`, and `closed:boolean`.
|
|
368
|
-
Closure is established only when `closed` is true. Older servers may omit
|
|
369
|
-
these fields; a missing receipt never proves cleanup.
|
|
370
|
-
- `operate_recipe_save` saves a postcondition-verified local recipe under a
|
|
371
|
-
closed task verb plus the service's registrable domain. It records stable target
|
|
372
|
-
attributes and exact provenance for Squire-supplied values, not observed refs
|
|
373
|
-
or plaintext secrets. Recipes that pass a share-eligibility check (no
|
|
374
|
-
personal or secret-shaped literals) and a registrable-domain lock are also
|
|
375
|
-
written live to the shared registry, making them immediately reusable by
|
|
376
|
-
other installs without a promotion step. `operate_recipe_run` binds the
|
|
377
|
-
replaying user's own values and replays those steps, preferring the local
|
|
378
|
-
recipe and falling back to the shared one. A recipe cannot navigate outside
|
|
379
|
-
the site it was recorded for; normal keyed replay refuses a violation before
|
|
380
|
-
navigation and continues with cold driving. On one ordinary missed step,
|
|
381
|
-
replay returns a local repair point and can continue in the same session.
|
|
382
|
-
Older name-only recipes remain planning hints.
|
|
383
|
-
- `list_payment_cards` returns saved-card labels and opaque references.
|
|
384
|
-
`inject_card` takes one explicit `card_ref`, purchase terms, and per-field
|
|
385
|
-
refs plus the addressed `session_id`. It creates or resumes the single approval
|
|
386
|
-
and returns per-field browser outcomes plus approval metadata, last4, expiry,
|
|
387
|
-
cardholder name, and stored billing; it never returns PAN/CVV or submits.
|
|
388
|
-
- `list_credentials` and `use_credential` find saved credentials and make authenticated API calls without returning raw values.
|
|
389
|
-
Before provisioning, call `list_credentials` with
|
|
390
|
-
`{"service":["exa","groq","cartesia"],"fields":"summary"}` to check for
|
|
391
|
-
existing keys without returning the whole inventory. `service` accepts one
|
|
392
|
-
string or a nonempty array and matches exact service names after trimming
|
|
393
|
-
whitespace and ignoring case. `fields: "summary"` selects compact metadata;
|
|
394
|
-
calling with `{}` preserves the full metadata inventory. Discover the exact
|
|
395
|
-
projection and inputs through the installed server's `tools/list` description
|
|
396
|
-
and schema.
|
|
397
|
-
- `fetch_credential` returns a credential's raw value to the agent — the one path that does. It first returns an approval link and no value; you open it and sign with your passkey; the agent resumes with the returned `approval_id` and receives the value once. The agent may pass a short `reason`; the page asks the reveal as a question naming the credential, shows which agent is asking, quotes that stated reason, and says when the approval expires. Denial or expiry releases nothing, and a mutation or payment approval cannot be used here. Reach for it only when the key must land somewhere the agent controls (a GitHub Actions secret, a `.env`) with no server-side injection path — `use_credential` is the right tool for calling an API.
|
|
398
|
-
- `edit_credential` changes only an existing credential's non-secret name,
|
|
399
|
-
`allowed_hosts`, or `login_hosts`; `delete_credential` soft-deletes one. Each
|
|
400
|
-
first returns a Telegram/passkey approval link bound to the operation, exact
|
|
401
|
-
credential reference, and edit before→after. Resume with only the returned
|
|
402
|
-
`approval_id`. Neither tool can read or alter the secret value; use
|
|
403
|
-
`store_credential` to rotate a secret.
|
|
404
|
-
- `grant_app_access` and `revoke_app_access` create and remove scoped backend access.
|
|
405
|
-
- `audit_log` reports credential activity without exposing credential values. It
|
|
406
|
-
defaults to a shaped security ledger: lifecycle events and anomalies (non-2xx,
|
|
407
|
-
429, rejected calls) as rows, routine proxied egress collapsed into per
|
|
408
|
-
credential/host/burst rollups with per-grant running totals. Pass a rollup's
|
|
409
|
-
`id` as `expand` for its individual calls, or `view: "raw"` for the flat
|
|
410
|
-
per-request stream.
|
|
411
|
-
|
|
412
|
-
## One README for GitHub and npm
|
|
413
|
-
|
|
414
|
-
This root file is the canonical README. The npm pack lifecycle copies it into `@trusty-squire/mcp` byte-for-byte, then removes the generated package-local copy after packing. GitHub and npm therefore publish the same product explanation.
|
|
415
|
-
|
|
416
|
-
## Development
|
|
417
|
-
|
|
418
|
-
```bash
|
|
419
|
-
git clone https://github.com/Trusty-Squire/trusty-squire.git
|
|
420
|
-
cd trusty-squire
|
|
421
|
-
./scripts/bootstrap.sh
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
After bootstrap, `pnpm typecheck` and `pnpm test` should pass. Stop local services with `docker compose -f docker-compose.dev.yml down`; add `-v` to reset their data.
|
|
425
|
-
|
|
426
|
-
Requirements: Node 20.11.0 (`.nvmrc`), pnpm 8.15+, Docker, and Docker Compose.
|
|
427
|
-
|
|
428
|
-
Repository map:
|
|
429
|
-
|
|
430
|
-
```text
|
|
431
|
-
trusty-squire/
|
|
432
|
-
├── apps/
|
|
433
|
-
│ ├── api/ Accounts, OAuth, machine tokens, proxy, inbox, vault, and billing
|
|
434
|
-
│ ├── mcp/ MCP server, browser operation tools, and credential tools
|
|
435
|
-
│ ├── registry/ Signed website skills and verification service
|
|
436
|
-
│ └── web/ Marketing site and vault UI
|
|
437
|
-
└── packages/
|
|
438
|
-
├── vault/ Encrypted credential storage and audit log
|
|
439
|
-
├── skill-schema/ Shared schema for replayable website skills
|
|
440
|
-
└── recipe-schema/ Shared wire schema for operator replay recipes
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
Product and public-web changes should follow [PRODUCT.md](https://github.com/trusty-squire/trusty-squire/blob/main/docs/PRODUCT.md) and [DESIGN.md](https://github.com/trusty-squire/trusty-squire/blob/main/docs/DESIGN.md).
|
|
444
|
-
|
|
445
|
-
## License
|
|
446
|
-
|
|
447
|
-
[MIT](https://github.com/trusty-squire/trusty-squire/blob/main/LICENSE) © Trusty Squire
|
|
35
|
+
Works with Claude Code, Codex, Cursor, OpenCode, Goose, Cline, Continue, and Hermes.
|