@reclaimprotocol/agent 0.1.33 → 0.1.35

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.
Files changed (117) hide show
  1. package/README.md +3 -3
  2. package/lib/authoring/backend.d.ts +13 -0
  3. package/lib/authoring/index.d.ts +1 -1
  4. package/lib/authoring/tools.js +78 -42
  5. package/lib/config/store.d.ts +10 -0
  6. package/lib/mcp/api/operations.gen.d.ts +238 -4
  7. package/lib/mcp/api/operations.gen.js +6967 -672
  8. package/lib/mcp/api/tools.js +21 -8
  9. package/lib/mcp/instructions.js +12 -12
  10. package/lib/mcp/server.d.ts +1 -1
  11. package/lib/mcp/server.js +3 -0
  12. package/lib/mcp/start.js +8 -7
  13. package/lib/mcp/success-response.js +5 -1
  14. package/lib/mcp/tools/agent/analyze.js +5 -4
  15. package/lib/mcp/tools/agent/attach.js +11 -7
  16. package/lib/mcp/tools/agent/build.js +5 -1
  17. package/lib/mcp/tools/agent/capture.d.ts +2 -2
  18. package/lib/mcp/tools/agent/capture.js +15 -11
  19. package/lib/mcp/tools/agent/check-attestor.d.ts +5 -0
  20. package/lib/mcp/tools/agent/check-attestor.js +37 -0
  21. package/lib/mcp/tools/agent/eval.js +4 -3
  22. package/lib/mcp/tools/agent/find.js +9 -10
  23. package/lib/mcp/tools/agent/how-it-works.js +1 -0
  24. package/lib/mcp/tools/agent/live-view.js +49 -22
  25. package/lib/mcp/tools/agent/pod-proof.js +7 -11
  26. package/lib/mcp/tools/agent/proof.d.ts +12 -0
  27. package/lib/mcp/tools/agent/proof.js +32 -0
  28. package/lib/mcp/tools/agent/propose.js +4 -3
  29. package/lib/mcp/tools/agent/publish.d.ts +2 -1
  30. package/lib/mcp/tools/agent/publish.js +12 -6
  31. package/lib/mcp/tools/agent/replay.js +10 -7
  32. package/lib/mcp/tools/agent/session.js +16 -8
  33. package/lib/mcp/tools/agent/shared.d.ts +2 -1
  34. package/lib/mcp/tools/agent/shared.js +29 -24
  35. package/lib/mcp/tools/agent/start-integration.d.ts +15 -0
  36. package/lib/mcp/tools/agent/start-integration.js +62 -0
  37. package/lib/mcp/tools/authenticate/authenticate.js +9 -11
  38. package/lib/mcp/tools/authenticate/build.js +2 -0
  39. package/lib/mcp/tools/authenticate/builder-access.js +1 -0
  40. package/lib/mcp/tools/authenticate/eth-key.d.ts +8 -0
  41. package/lib/mcp/tools/authenticate/eth-key.js +27 -15
  42. package/lib/mcp/tools/authenticate/import.js +18 -31
  43. package/lib/mcp/tools/authenticate/issue-org-token.d.ts +8 -0
  44. package/lib/mcp/tools/authenticate/issue-org-token.js +60 -0
  45. package/lib/mcp/tools/authenticate/issue.js +9 -9
  46. package/lib/mcp/tools/authenticate/org-key-store.d.ts +10 -0
  47. package/lib/mcp/tools/authenticate/org-key-store.js +23 -0
  48. package/lib/mcp/tools/authenticate/resolve-owner-key.js +10 -12
  49. package/lib/mcp/tools/mode/mode.js +2 -0
  50. package/lib/mcp/tools/verification/build.d.ts +3 -1
  51. package/lib/mcp/tools/verification/build.js +10 -2
  52. package/lib/mcp/tools/verification/callback-listener.d.ts +51 -0
  53. package/lib/mcp/tools/verification/callback-listener.js +163 -0
  54. package/lib/mcp/tools/verification/index.d.ts +2 -0
  55. package/lib/mcp/tools/verification/index.js +2 -0
  56. package/lib/mcp/tools/verification/proxy-callback-events.d.ts +59 -0
  57. package/lib/mcp/tools/verification/proxy-callback-events.js +300 -0
  58. package/lib/mcp/tools/verification/read-callback-events.d.ts +3 -0
  59. package/lib/mcp/tools/verification/read-callback-events.js +135 -0
  60. package/lib/mcp/tools/verification/result.d.ts +6 -0
  61. package/lib/mcp/tools/verification/result.js +11 -8
  62. package/lib/proof/check-attestor.d.ts +67 -0
  63. package/lib/proof/check-attestor.js +156 -0
  64. package/lib/tools/registry.d.ts +12 -0
  65. package/lib/tools/registry.js +1 -0
  66. package/package.json +2 -2
  67. package/skill/SKILL.builder.md +84 -75
  68. package/skill/SKILL.common.md +23 -10
  69. package/skill/builder-mode.md +140 -138
  70. package/skill/start-integration.md +74 -0
  71. package/skill/user-script.md +64 -0
  72. package/skill/verification-client-integration.md +40 -28
  73. package/src/authoring/backend.ts +15 -0
  74. package/src/authoring/index.ts +2 -0
  75. package/src/authoring/tools.ts +86 -42
  76. package/src/config/store.ts +11 -0
  77. package/src/mcp/api/operations.gen.ts +7011 -704
  78. package/src/mcp/api/tools.ts +21 -8
  79. package/src/mcp/instructions.ts +12 -12
  80. package/src/mcp/server.ts +4 -0
  81. package/src/mcp/start.ts +9 -7
  82. package/src/mcp/success-response.ts +5 -1
  83. package/src/mcp/tools/agent/analyze.ts +5 -4
  84. package/src/mcp/tools/agent/attach.ts +11 -7
  85. package/src/mcp/tools/agent/build.ts +7 -1
  86. package/src/mcp/tools/agent/capture.ts +20 -12
  87. package/src/mcp/tools/agent/check-attestor.ts +49 -0
  88. package/src/mcp/tools/agent/eval.ts +4 -3
  89. package/src/mcp/tools/agent/find.ts +9 -10
  90. package/src/mcp/tools/agent/how-it-works.ts +1 -0
  91. package/src/mcp/tools/agent/live-view.ts +49 -22
  92. package/src/mcp/tools/agent/pod-proof.ts +7 -11
  93. package/src/mcp/tools/agent/proof.ts +55 -0
  94. package/src/mcp/tools/agent/propose.ts +4 -3
  95. package/src/mcp/tools/agent/publish.ts +12 -4
  96. package/src/mcp/tools/agent/replay.ts +10 -7
  97. package/src/mcp/tools/agent/session.ts +16 -8
  98. package/src/mcp/tools/agent/shared.ts +39 -24
  99. package/src/mcp/tools/agent/start-integration.ts +84 -0
  100. package/src/mcp/tools/authenticate/authenticate.ts +9 -11
  101. package/src/mcp/tools/authenticate/build.ts +2 -0
  102. package/src/mcp/tools/authenticate/builder-access.ts +1 -0
  103. package/src/mcp/tools/authenticate/eth-key.ts +48 -18
  104. package/src/mcp/tools/authenticate/import.ts +21 -36
  105. package/src/mcp/tools/authenticate/issue-org-token.ts +84 -0
  106. package/src/mcp/tools/authenticate/issue.ts +9 -9
  107. package/src/mcp/tools/authenticate/org-key-store.ts +25 -0
  108. package/src/mcp/tools/authenticate/resolve-owner-key.ts +10 -12
  109. package/src/mcp/tools/mode/mode.ts +2 -0
  110. package/src/mcp/tools/verification/build.ts +10 -2
  111. package/src/mcp/tools/verification/callback-listener.ts +203 -0
  112. package/src/mcp/tools/verification/index.ts +2 -0
  113. package/src/mcp/tools/verification/proxy-callback-events.ts +374 -0
  114. package/src/mcp/tools/verification/read-callback-events.ts +160 -0
  115. package/src/mcp/tools/verification/result.ts +11 -8
  116. package/src/proof/check-attestor.ts +209 -0
  117. package/src/tools/registry.ts +14 -0
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Attestor reachability check. Proves a public request with a throwaway owner
3
+ * key to show whether the attestor works from this machine, independent of any
4
+ * developer provider or site.
5
+ */
6
+ import { createClaimOnAttestor } from '@reclaimprotocol/attestor-core';
7
+ import { generateKeypair } from '@reclaimprotocol/client';
8
+ import "./patch-domhandler.js";
9
+ import { claimRejection, ensureAttestorCrypto, parseClaim, resolveAttestorUrl, } from "./proof-core.js";
10
+ import { ensureZkFiles } from "./zk-files.js";
11
+ /**
12
+ * Default time budget for one check. A live run takes about 8 s; a longer
13
+ * budget risks the MCP client's own tool-call timeout.
14
+ */
15
+ export const CHECK_ATTESTOR_TIMEOUT_MS = 45000;
16
+ /* eslint-disable @stylistic/max-len -- the source URL cannot wrap. */
17
+ /**
18
+ * Request params for the check: the public CoinGecko price request from the
19
+ * attestor-core examples. Source:
20
+ * https://raw.githubusercontent.com/reclaimprotocol/attestor-core/refs/heads/main/example/coin-gecko.json
21
+ */
22
+ /* eslint-enable @stylistic/max-len */
23
+ export const COIN_GECKO_PARAMS = {
24
+ url: 'https://api.coingecko.com/api/v3/simple/price'
25
+ + '?ids=cardano,solana&vs_currencies=usd',
26
+ method: 'GET',
27
+ body: '',
28
+ headers: {
29
+ 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) '
30
+ + 'AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1',
31
+ 'Sec-Fetch-Mode': 'same-origin',
32
+ 'Sec-Fetch-Site': 'same-origin',
33
+ },
34
+ responseMatches: [
35
+ {
36
+ invert: false,
37
+ type: 'regex',
38
+ value: 'cardano":{"usd":(?<cardanoPrice>\\d*\\.*\\d*)}',
39
+ },
40
+ {
41
+ invert: false,
42
+ type: 'regex',
43
+ value: 'solana":{"usd":(?<solanaPrice>\\d*\\.*\\d*)}',
44
+ },
45
+ ],
46
+ responseRedactions: [
47
+ { jsonPath: '$.cardano', regex: 'cardano":{"usd":(.*?)}', xPath: '' },
48
+ { jsonPath: '$.solana', regex: 'solana":{"usd":(.*?)}', xPath: '' },
49
+ ],
50
+ additionalClientOptions: {},
51
+ };
52
+ /** The only secret in the example request. */
53
+ const COIN_GECKO_SECRET_HEADERS = {
54
+ Referer: 'https://www.coingecko.com/en/nft',
55
+ };
56
+ /** Prefix of the error raised when the check exceeds its time budget. */
57
+ const CHECK_TIMEOUT_MESSAGE = 'attestor check timed out';
58
+ /**
59
+ * Signals that the attestor connection failed. Bare "timeout", "timed out",
60
+ * and "websocket" are left out: a target site's errors contain them too.
61
+ */
62
+ const NETWORK_ERROR_PATTERN = new RegExp([
63
+ 'connection closed',
64
+ 'ERROR_NETWORK_ERROR',
65
+ 'ECONNRESET',
66
+ 'ECONNREFUSED',
67
+ 'ENOTFOUND',
68
+ 'EAI_AGAIN',
69
+ 'ETIMEDOUT',
70
+ 'EHOSTUNREACH',
71
+ 'ENETUNREACH',
72
+ 'socket hang up',
73
+ 'websocket (connection|error|closed|failed)',
74
+ '(failed|unable|could not) to connect',
75
+ CHECK_TIMEOUT_MESSAGE,
76
+ ].join('|'), 'i');
77
+ /** True when `message` looks like a network failure rather than a rejection. */
78
+ export function isNetworkError(message) {
79
+ return message !== undefined && NETWORK_ERROR_PATTERN.test(message);
80
+ }
81
+ /** Prove the public CoinGecko request once and classify the outcome. */
82
+ export async function checkAttestor(opts = {}) {
83
+ const attestorUrl = resolveAttestorUrl(opts.attestorUrl);
84
+ const timeoutMs = opts.timeoutMs ?? CHECK_ATTESTOR_TIMEOUT_MS;
85
+ const createClaim = opts.createClaim ?? createClaimOnAttestor;
86
+ const prepare = opts.prepare ?? (async () => {
87
+ ensureAttestorCrypto();
88
+ await ensureZkFiles();
89
+ });
90
+ const startedAt = Date.now();
91
+ const finish = (outcome) => {
92
+ return { ...outcome, attestorUrl, durationMs: Date.now() - startedAt };
93
+ };
94
+ let timer;
95
+ const timeout = new Promise((_, reject) => {
96
+ timer = setTimeout(() => {
97
+ reject(new Error(`${CHECK_TIMEOUT_MESSAGE} after ${timeoutMs} ms`));
98
+ }, timeoutMs);
99
+ });
100
+ try {
101
+ try {
102
+ await Promise.race([prepare(), timeout]);
103
+ }
104
+ catch (err) {
105
+ const error = messageOf(err);
106
+ return finish({
107
+ ok: false,
108
+ error,
109
+ errorKind: error.startsWith(CHECK_TIMEOUT_MESSAGE)
110
+ ? 'attestor-unreachable'
111
+ : 'setup-failed',
112
+ });
113
+ }
114
+ const claim = await Promise.race([
115
+ createClaim({
116
+ name: 'http',
117
+ params: COIN_GECKO_PARAMS,
118
+ secretParams: { headers: COIN_GECKO_SECRET_HEADERS },
119
+ ownerPrivateKey: generateKeypair().privateKey,
120
+ client: { url: attestorUrl },
121
+ zkEngine: 'stwo',
122
+ }),
123
+ timeout,
124
+ ]);
125
+ const rejection = claimRejection(claim);
126
+ if (rejection !== undefined) {
127
+ return finish({
128
+ ok: false,
129
+ error: rejection,
130
+ errorKind: isNetworkError(rejection)
131
+ ? 'attestor-unreachable'
132
+ : 'attestor-error',
133
+ });
134
+ }
135
+ return finish({
136
+ ok: true,
137
+ extractedParameters: parseClaim(claim).extractedParameters,
138
+ });
139
+ }
140
+ catch (err) {
141
+ const error = messageOf(err);
142
+ return finish({
143
+ ok: false,
144
+ error,
145
+ errorKind: isNetworkError(error)
146
+ ? 'attestor-unreachable'
147
+ : 'attestor-error',
148
+ });
149
+ }
150
+ finally {
151
+ clearTimeout(timer);
152
+ }
153
+ }
154
+ function messageOf(err) {
155
+ return err instanceof Error ? err.message : String(err);
156
+ }
@@ -1,8 +1,20 @@
1
1
  import type { JSONSchema } from 'zod/v4/core';
2
+ /** MCP tool hints that let a client decide when to ask for approval. */
3
+ export interface ToolAnnotations {
4
+ /** The tool never changes state. */
5
+ readOnlyHint?: boolean;
6
+ /** The tool can delete or overwrite state. */
7
+ destructiveHint?: boolean;
8
+ /** Repeating a call with the same input has no further effect. */
9
+ idempotentHint?: boolean;
10
+ /** The tool reaches systems outside this machine. */
11
+ openWorldHint?: boolean;
12
+ }
2
13
  export interface ToolDefinition {
3
14
  name: string;
4
15
  description: string;
5
16
  inputSchema: JSONSchema.ObjectSchema;
17
+ annotations?: ToolAnnotations;
6
18
  }
7
19
  export interface RegisteredTool {
8
20
  definition: ToolDefinition;
@@ -75,6 +75,7 @@ export function extendTool(tool, extra) {
75
75
  name: base.name,
76
76
  description: extra.description ?? base.description,
77
77
  inputSchema,
78
+ ...(base.annotations ? { annotations: base.annotations } : {}),
78
79
  },
79
80
  handler: tool.handler,
80
81
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reclaimprotocol/agent",
3
3
  "title": "Reclaim Agent",
4
- "version": "0.1.33",
4
+ "version": "0.1.35",
5
5
  "description": "Reclaim Protocol tools for AI coding agents. Reclaim lets your app verify data points about its users, such as a follower count, an account balance, or an order history. This package is the Reclaim MCP server: it gives your agent the tools to find, build, test, and fix data verification providers by capturing and replaying browser requests, drafting provider recipes, running zkTLS proofs, and publishing provider versions.",
6
6
  "type": "module",
7
7
  "main": "./lib/index.js",
@@ -83,7 +83,7 @@
83
83
  "@modelcontextprotocol/sdk": "^1.29.0",
84
84
  "@noble/hashes": "^2.2.0",
85
85
  "@reclaimprotocol/attestor-core": "^5.1.5",
86
- "@reclaimprotocol/client": "0.1.33",
86
+ "@reclaimprotocol/client": "0.1.35",
87
87
  "@reclaimprotocol/tls": "^0.1.3",
88
88
  "chrome-remote-interface": "^0.33.0",
89
89
  "domhandler": "^6.0.0",
@@ -11,32 +11,38 @@ Builder.
11
11
 
12
12
  ## The loop, at a glance
13
13
 
14
- Copy this checklist and check off each step as you finish it. Read the linked
15
- topic before a step you aren't already fluent in.
16
-
17
- - [ ] 1. `attach_browser` — pick a browser source with the developer.
18
- → topic `browser`
19
- - [ ] 2. `list_tabs` → `start_capture(tabId)` ⇒ **`captureId`**, your handle for
20
- everything below. → topic `capture`
21
- - [ ] 3. Drive the page: `navigate`, `wait_for_page`, `eval_in_page`,
22
- `get_cookies`. Detect login, land on the page showing the value, trigger
23
- the request. → topic `capture`
24
- - [ ] 4. `find_requests_containing` → `analyze_request_constraints` → pick an
25
- **authenticated** request. → topic `capture`
26
- - [ ] 5. `propose_provider(captureId, requestId, …)` ⇒ **`draftId`** (recipe +
27
- captured secrets, held server-side — secrets never enter the transcript).
28
- → topic `draft`
29
- - [ ] 6. Parameterize user-specific URL segments; mark secrets. → topic `params`
30
- - [ ] 7. `replay_request(captureId, draftId)`, then again with
14
+ Build a new org-private provider for the claim the developer described. Ask
15
+ one question if the site, account, or value is ambiguous. Copy this checklist
16
+ and check off each step. Read the linked topic before a step you don't know.
17
+
18
+ - [ ] 1. Name the data points: only the values the claim must prove, usually
19
+ one or two. No identity or profile fields.
20
+ - [ ] 2. `attach_browser`, then `list_tabs` → `start_capture(tabId)` ⇒
21
+ **`captureId`**. → topics `browser`, `capture`
22
+ - [ ] 3. Open the sign-in page and wait for the developer to sign in
23
+ (`wait_for_page`). → topic `capture`
24
+ - [ ] 4. Write the navigation script: sign-in redirect, then each data
25
+ point's page in order. → topic `user-script`
26
+ - [ ] 5. `test_user_script({ script, keep: true })`. Fix and retest until it
27
+ reaches every page, also after a reload.
28
+ - [ ] 6. Capture by letting the script navigate: `find_requests_containing` →
29
+ `analyze_request_constraints` → pick an **authenticated** request per
30
+ data point. → topic `capture`
31
+ - [ ] 7. `propose_provider(captureId, requestId, …)` ⇒ **`draftId`**
32
+ (`targets` = the data points). → topic `draft`
33
+ - [ ] 8. Parameterize user-specific URL segments; mark secrets.
34
+ → topic `params`
35
+ - [ ] 9. `replay_request(captureId, draftId)`, then again with
31
36
  `withoutSecrets: true`. → topic `prove`
32
- - [ ] 8. `run_proof(captureId, draftId, ownerAddress)`.
37
+ - [ ] 10. `run_proof(captureId, draftId, ownerAddress)`.
33
38
  → topics `prove`, `credentials`
34
- - [ ] 9. Resolve a `providerId` (**reuse before create**) →
35
- `create_provider_version_from_capture` → `publish_provider_version` or
39
+ - [ ] 11. `create_provider` (or a new version of a provider your org owns) →
40
+ `create_provider_version_from_capture` (publishes the kept script as
41
+ `webSettings.jsUserScripts`) → `publish_provider_version` or
36
42
  `submit_provider_version_for_review`. → topic `publish`
37
43
 
38
- Multi-request provider? Run steps 5–8 once per request, then publish every
39
- proven draft in one call. See topic `publish`.
44
+ Data points on different requests? Run steps 7-10 once per request, then
45
+ publish every proven draft in one call. See topic `publish`.
40
46
 
41
47
  <!-- topic: contributions -->
42
48
 
@@ -84,7 +90,7 @@ integration. Prefer Builder for new integrations.
84
90
  2. **Pick a tab and start the capture.** `list_tabs`, then
85
91
  `start_capture(tabId)` ⇒ a **`captureId`**: the session handle every browser
86
92
  and inspect tool takes as its first argument (`navigate`, `eval_in_page`,
87
- `wait_for_page`, `get_cookies`, `list_requests`, `find_requests_containing`,
93
+ `wait_for_page`, `list_requests`, `find_requests_containing`,
88
94
  `propose_provider`). If a tab is already on the right origin use it,
89
95
  otherwise the first is usually fine. Capturing from the start is harmless —
90
96
  it also records the login navigation. Then `navigate(captureId, url)` to
@@ -93,13 +99,18 @@ integration. Prefer Builder for new integrations.
93
99
 
94
100
  3. <!-- common: login-detect -->
95
101
 
96
- 4. **Navigate to the page that displays the value.** Construct the URL from what
97
- you know about the site; eval for anything you need (for example, GitHub's
98
- `meta[name="user-login"]`'s `content`), then `navigate(captureId, url)`.
102
+ 4. **Write and keep the navigation script** (topic `user-script`) as soon as
103
+ the developer is signed in, or the sign-in URL is known. Test it with
104
+ `test_user_script` and `keep: true`. The script visits each data point's
105
+ page; build its URLs from what you know about the site and read page state
106
+ with `eval_in_page` when needed (for example, GitHub's
107
+ `meta[name="user-login"]`'s `content`).
99
108
 
100
- 5. **Trigger the request.** Capture has been live since step 2, so make the
101
- call fire: `eval_in_page(captureId, "location.reload()")`, or click an
102
- element / hit a SPA route programmatically. Don't ask the developer to click.
109
+ 5. **Trigger the requests by letting the script navigate.** Capture has been
110
+ live since step 2. Reload the page (`eval_in_page(captureId,
111
+ "location.reload()")`) so the kept script runs from the start. Don't click
112
+ by hand or ask the developer to; if a request needs an interaction, add it
113
+ to the script.
103
114
 
104
115
  6. <!-- common: identify-value -->
105
116
 
@@ -126,8 +137,9 @@ integration. Prefer Builder for new integrations.
126
137
  under that `draftId`; the secrets never enter the transcript, so you carry the
127
138
  `draftId`, not the provider JSON, into replay/prove.
128
139
 
129
- **Extracting more than one value?** Prefer a **single request whose response
130
- contains all of them** and draft them together — one `responseMatches` +
140
+ **Extracting more than one value?** Add a value only when the claim needs it.
141
+ Prefer a **single request whose response contains all of them** and draft them
142
+ together — one `responseMatches` +
131
143
  `responseRedactions` pair per value. Find that request by running
132
144
  `find_requests_containing` for each value and picking a `requestId` common to
133
145
  all. Then pass a `targets` array, each entry with its `value` (exactly as it
@@ -136,7 +148,7 @@ appears) and a distinct `name`:
136
148
  ```
137
149
  propose_provider(captureId, requestId, name, targets: [
138
150
  { value: "Scratch-net", name: "username" },
139
- { value: "Alex", name: "name" },
151
+ { value: "2019", name: "member_since_year" },
140
152
  ])
141
153
  ```
142
154
 
@@ -303,6 +315,11 @@ provider JSON. That's the final result.
303
315
  developer what's happening and drive the bootstrap yourself — see topic
304
316
  `credentials`. No restart needed, and the existing `draftId` stays valid.
305
317
 
318
+ **Network error from `run_proof`.** Read its `diagnosis`. Attestor reachable
319
+ → the target site likely blocks the attestor's network; call
320
+ `run_proof_via_pod`. Attestor unreachable → `check_attestor` confirms it; the
321
+ fix is on the developer's network. Don't loop retries.
322
+
306
323
  <!-- common: prove-tail -->
307
324
 
308
325
  <!-- topic: publish -->
@@ -313,16 +330,13 @@ parameterization applied. `initialUrl` is the page where the user logs in / the
313
330
  value is displayed (for example, `https://github.com/Syed`) — **not** the API
314
331
  endpoint URL inside the provider.
315
332
 
316
- **1. Reuse before create — this is how duplicates happen.**
317
- `create_provider_version_from_capture` requires a `providerId`. Before minting a
318
- new one, search your existing providers with `providers` (by domain / data
319
- point) — a site you've already built is there. Match → pass its
320
- `providerId` and the capture lands as a NEW VERSION on it. Only call
321
- `create_provider(orgId, domain, slug, title, description)` when none exists
322
- (`orgId` from `list_orgs`; `domain` such as `github.com`, `slug` such as
323
- `github-username`, `title` such as "GitHub Username"). It starts PRIVATE with an
324
- empty seed version. Routing every run through `create_provider` is what stacks
325
- duplicates in "My providers".
333
+ **1. Resolve the `providerId`.** For a new claim, call
334
+ `create_provider(orgId, domain, slug, title, description)` (`orgId` from
335
+ `list_orgs`; `domain` such as `github.com`, `slug` such as `github-username`,
336
+ `title` such as "GitHub Username"). It starts PRIVATE with an empty seed
337
+ version. Use an existing `providerId` only when your org owns the provider and
338
+ you are revising it: the capture lands as a NEW VERSION on it. For another
339
+ org's provider that almost fits, create your own provider and version.
326
340
 
327
341
  **2. Persist the version.**
328
342
  `create_provider_version_from_capture(providerId, provider, initialUrl, notes)`
@@ -339,12 +353,13 @@ or archive an existing version to apply provider changes.
339
353
  required at verification time. This proves that every request matched; it
340
354
  does not automatically bind a value extracted from one response to another
341
355
  request.
342
- - **Login/navigation-gated data:** supply `jsUserScripts` (plain page JS that
343
- drives login/waits/navigation so the target request fires), or
344
- `allowedJsRequests` (extra requests the script may fire, for example,
345
- pagination). → topic `user-script`. Before populating `allowedJsRequests` for
346
- a `verifyRequestProof({ providerId })` use case, read topic `hash-validation` — a
347
- session-unique URL there makes verifyRequestProof reject every real proof.
356
+ - **Navigation script:** the script kept with `test_user_script` (`keep: true`)
357
+ is published as `webSettings.jsUserScripts`. Pass `jsUserScripts` only to
358
+ override it. → topic `user-script`. For extra requests the script may fire
359
+ (for example, pagination), pass `allowedJsRequests`. Before populating it for
360
+ a `verifyRequestProof({ providerId })` use case, read topic
361
+ `hash-validation`: a session-unique URL there makes `verifyRequestProof`
362
+ reject every real proof.
348
363
 
349
364
  **3. `requiredContext` is derived for you.** The publish tool scans the recipe's
350
365
  url/body for `{{context.<name>}}` placeholders and emits a JSON Schema requiring
@@ -367,10 +382,8 @@ developer it's awaiting admin review.
367
382
  context params — for example, `context: { "username": "…" }` in
368
383
  `create_verification_session`. Quote the `requiredContext` from the created
369
384
  version verbatim — a session missing those fields gets a 400, and verification
370
- matches the supplied values exactly. Then ask once how they want to use it —
371
- try it now here, integrate it into an existing backend or frontend, or create a
372
- new app — unless they already said (see "Using a provider" in the server
373
- instructions).
385
+ matches the supplied values exactly. Then continue as the server
386
+ instructions say under "What the developer wants".
374
387
 
375
388
  **6. If the response includes `_notes` for an attached browser, follow them for
376
389
  an optional authenticated-state retest.** Only when its profile is still signed
@@ -383,35 +396,30 @@ the session for further edits; call `dispose_browser` only after they confirm.
383
396
  A `builder` session is quota-accounted, so don't leave it idle.
384
397
 
385
398
  <!-- topic: discover -->
386
- ## Discover and resolve providers for the developer's app
399
+ ## Discover and resolve existing providers
387
400
 
388
- When a developer wants to add verification but doesn't know which providers to
389
- use, run the same flow the Discover UI does:
401
+ Use this flow only when the developer asks to reuse existing providers or
402
+ doesn't know what a site supports. Otherwise build a new provider (server
403
+ instructions, "Choose the provider").
390
404
 
391
405
  1. Ask which website their users prove data from.
392
406
  2. `discover_data_points({ domain })` → what's verifiable there. Confirm the
393
407
  data points with the developer.
394
408
  3. `discover_resolve({ domain, dataPoints })` → the smallest provider set
395
409
  (`providers`, greedy combine), the `requiredContext` inputs to collect, and
396
- any `uncovered` points (author those with the flow above, then re-resolve).
397
- 4. Resolved providers are published and usable as-is: don't capture, replay,
398
- or prove them unless the developer asks you to check or test one. Ask once
399
- whether they just want to try them or want them in an app — just try it
400
- now, integrate into an existing backend or frontend (this directory or
401
- another path), or create a new app — unless they already said.
402
- 5. Just try it (no app, callback, or client package) →
403
- `create_verification_session({ providers: [{ providerId }, …], context,
404
- verificationClientUrl })` with EVERY resolved provider id and the collected
405
- context, give them `verificationUrl`, poll `get_verification_session` until
406
- it is terminal, and report the outcome with `get_verification_result`
407
- (builder-mode instructions cover the org key and temporary callback).
408
- Integrate or new app → topic `integration`.
409
-
410
- `merge.mergeable` only signals the set *could* collapse into one provider — you
411
- still pass them all; the Builder runs them in one session.
412
-
413
- For client integration, result verification, or TEE binding, read topic
414
- `integration`.
410
+ any `uncovered` points (author those with the checklist in topic
411
+ `overview`, then re-resolve).
412
+ 4. Use only the data points the developer confirmed: show and read only those
413
+ and ignore the rest. A provider that needs any change gets your own copy
414
+ (server instructions, "Choose the provider"). Otherwise it's usable as-is;
415
+ don't capture, replay, or prove it unless the developer asks.
416
+ 5. To try them: `create_verification_session({ providers: [{ providerId }, …],
417
+ context, verificationClientUrl })` with EVERY resolved provider id and the
418
+ collected context. The server instructions ("Try a provider without an
419
+ app") cover the callback and polling. To integrate: topic `integration`.
420
+
421
+ `merge.mergeable` only signals the set *could* collapse into one provider. Pass
422
+ them all; the Builder runs them in one session.
415
423
 
416
424
  <!-- topic: integration -->
417
425
 
@@ -453,6 +461,7 @@ or omit `interceptorOptions`). If that fixes the provider, keep interception
453
461
  off and replace intercepted claims with `window.Reclaim.requestClaim`. Full
454
462
  checklist and remedies: topic `interception`.
455
463
 
456
- **Proof failures / attestor errors** → topic `prove`.
464
+ **Proof failures / attestor errors** → topic `prove`. For a network error,
465
+ read `run_proof`'s `diagnosis`, then `check_attestor` or `run_proof_via_pod`.
457
466
  **Browser won't launch or attach** → topic `browser`.
458
467
  **Missing signing key** → topic `credentials`.
@@ -112,13 +112,18 @@ Ask plainly:
112
112
 
113
113
  If they say yes:
114
114
 
115
- 1. Call `share_browser_view` with no arguments. It returns the viewer `port` and
116
- the `command` (`cloudflared tunnel --url http://localhost:<port>`). The MCP
117
- server does not run cloudflared.
118
- 2. Run `command` yourself in the background. If `cloudflared` is not installed,
119
- ask the developer to install it; do not install it without their approval.
120
- 3. Read its output for the `https://<words>.trycloudflare.com` URL. Never guess
121
- or reconstruct the hostname.
115
+ 1. Call `share_browser_view` with no arguments. It returns the viewer `port`
116
+ and the `command` (`cloudflared tunnel --url http://localhost:<port>`). The
117
+ MCP server does not run cloudflared.
118
+ 2. Ask the developer to choose, and start no tunnel until they answer:
119
+ - **They run it (recommended).** They run `! <command>` in this session and
120
+ paste the `https://<words>.trycloudflare.com` URL. The tunnel then lives
121
+ in their shell, and they decide when it closes.
122
+ - **You run it.** They approve you running `command` in the background. If
123
+ `cloudflared` is not installed, ask them to install it; do not install it
124
+ without their approval.
125
+ 3. When you run it, read its output for the URL. Never guess or reconstruct the
126
+ hostname.
122
127
  4. Call `share_browser_view({ publicUrl })` with that URL. **Relay the returned
123
128
  `url` verbatim, on its own line.** It includes `liveview.html?magnify=1`.
124
129
 
@@ -138,8 +143,8 @@ If they say yes:
138
143
  - **Opening it on a phone lays the page out for that phone**, with touch,
139
144
  pinch-zoom and the soft keyboard. That is the reason to reach for `container`
140
145
  when a site only works on mobile.
141
- - **Stop the cloudflared process you started as soon as they're done.** The
142
- browser keeps running. `dispose_browser` and `reset_session` stop the
146
+ - **Stop a cloudflared process you started as soon as they're done.** If they
147
+ started it, tell them to stop it. The browser keeps running. `dispose_browser` and `reset_session` stop the
143
148
  container itself, but not your cloudflared process.
144
149
  - **Expose only the viewer port** that `share_browser_view` returns, never the
145
150
  browser's CDP port.
@@ -381,6 +386,13 @@ supplies are context params.
381
386
  `result.attestorLogs` and dumps `~/.reclaim/proof-runs/<timestamp>.json`
382
387
  (`runLogPath`). Off by default — suggest it for a recurring failure.
383
388
 
389
+ **Network errors** ("connection closed", `ERROR_NETWORK_ERROR`, timeouts):
390
+ read the result's `diagnosis`. It says whether the attestor is reachable
391
+ from this machine. If it is, the target site likely blocks the attestor's
392
+ network; in builder mode, run `run_proof_via_pod`. If it is not, call
393
+ `check_attestor` after the developer fixes their network. Do not loop
394
+ retries; one `run_proof` per change.
395
+
384
396
  **Do not auto-retry with a different `attestorUrl`.** There is one live
385
397
  public attestor; backup hostnames (`attestor-2.*`, and so on) **do not
386
398
  exist**. `attestorUrl` is only for a developer-supplied private/staging URL.
@@ -424,7 +436,8 @@ conversation.
424
436
  1. **`resolve_owner_key`** returns only the derived address.
425
437
  - `{ ready: true, address }` → call `run_proof` with it. No prompts.
426
438
  - `{ ready: false }` → ask the developer once: **their own key** →
427
- `import_credentials({ privateKey: "0x…" })` (a `keyFile` path also works),
439
+ `import_credentials({ keyFile })` (a path to a file holding the key; never
440
+ ask them to paste it),
428
441
  or **a fresh one** → `issue_credentials({})` (a fine default for test
429
442
  proofs — owner identity doesn't matter). Either writes to the project
430
443
  `.env` (mode 0600, added to `.gitignore`), never to `~/.reclaim`. Then