@reclaimprotocol/agent 0.1.32 → 0.1.34

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 (91) hide show
  1. package/lib/authoring/backend.d.ts +13 -0
  2. package/lib/authoring/index.d.ts +1 -1
  3. package/lib/authoring/tools.js +78 -42
  4. package/lib/config/store.d.ts +10 -0
  5. package/lib/mcp/api/operations.gen.d.ts +247 -4
  6. package/lib/mcp/api/operations.gen.js +6519 -337
  7. package/lib/mcp/api/tools.d.ts +10 -2
  8. package/lib/mcp/api/tools.js +55 -4
  9. package/lib/mcp/instructions.js +11 -11
  10. package/lib/mcp/server.d.ts +1 -1
  11. package/lib/mcp/server.js +3 -0
  12. package/lib/mcp/start.js +6 -1
  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 +3 -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/eval.js +4 -3
  20. package/lib/mcp/tools/agent/find.js +9 -10
  21. package/lib/mcp/tools/agent/how-it-works.js +1 -0
  22. package/lib/mcp/tools/agent/live-view.js +10 -7
  23. package/lib/mcp/tools/agent/pod-proof.js +7 -11
  24. package/lib/mcp/tools/agent/propose.js +4 -3
  25. package/lib/mcp/tools/agent/publish.d.ts +2 -1
  26. package/lib/mcp/tools/agent/publish.js +12 -6
  27. package/lib/mcp/tools/agent/replay.js +10 -7
  28. package/lib/mcp/tools/agent/session.js +16 -8
  29. package/lib/mcp/tools/agent/shared.js +3 -2
  30. package/lib/mcp/tools/agent/start-integration.d.ts +15 -0
  31. package/lib/mcp/tools/agent/start-integration.js +61 -0
  32. package/lib/mcp/tools/authenticate/authenticate.js +9 -11
  33. package/lib/mcp/tools/authenticate/build.js +2 -0
  34. package/lib/mcp/tools/authenticate/builder-access.js +1 -0
  35. package/lib/mcp/tools/authenticate/eth-key.d.ts +8 -0
  36. package/lib/mcp/tools/authenticate/eth-key.js +27 -15
  37. package/lib/mcp/tools/authenticate/import.js +18 -31
  38. package/lib/mcp/tools/authenticate/issue-org-token.d.ts +8 -0
  39. package/lib/mcp/tools/authenticate/issue-org-token.js +60 -0
  40. package/lib/mcp/tools/authenticate/issue.js +9 -9
  41. package/lib/mcp/tools/authenticate/org-key-store.d.ts +10 -0
  42. package/lib/mcp/tools/authenticate/org-key-store.js +23 -0
  43. package/lib/mcp/tools/authenticate/resolve-owner-key.js +10 -12
  44. package/lib/mcp/tools/mode/mode.js +2 -0
  45. package/lib/mcp/tools/verification/result.js +52 -12
  46. package/lib/tools/registry.d.ts +12 -0
  47. package/lib/tools/registry.js +1 -0
  48. package/package.json +2 -2
  49. package/skill/SKILL.builder.md +77 -73
  50. package/skill/SKILL.common.md +2 -1
  51. package/skill/builder-mode.md +135 -120
  52. package/skill/start-integration.md +49 -0
  53. package/skill/user-script.md +64 -0
  54. package/skill/verification-client-integration.md +131 -28
  55. package/src/authoring/backend.ts +15 -0
  56. package/src/authoring/index.ts +2 -0
  57. package/src/authoring/tools.ts +86 -42
  58. package/src/config/store.ts +11 -0
  59. package/src/mcp/api/operations.gen.ts +6563 -373
  60. package/src/mcp/api/tools.ts +70 -5
  61. package/src/mcp/instructions.ts +11 -11
  62. package/src/mcp/server.ts +4 -0
  63. package/src/mcp/start.ts +6 -1
  64. package/src/mcp/success-response.ts +5 -1
  65. package/src/mcp/tools/agent/analyze.ts +5 -4
  66. package/src/mcp/tools/agent/attach.ts +11 -7
  67. package/src/mcp/tools/agent/build.ts +5 -1
  68. package/src/mcp/tools/agent/capture.ts +20 -12
  69. package/src/mcp/tools/agent/eval.ts +4 -3
  70. package/src/mcp/tools/agent/find.ts +9 -10
  71. package/src/mcp/tools/agent/how-it-works.ts +1 -0
  72. package/src/mcp/tools/agent/live-view.ts +10 -7
  73. package/src/mcp/tools/agent/pod-proof.ts +7 -11
  74. package/src/mcp/tools/agent/propose.ts +4 -3
  75. package/src/mcp/tools/agent/publish.ts +12 -4
  76. package/src/mcp/tools/agent/replay.ts +10 -7
  77. package/src/mcp/tools/agent/session.ts +16 -8
  78. package/src/mcp/tools/agent/shared.ts +7 -2
  79. package/src/mcp/tools/agent/start-integration.ts +83 -0
  80. package/src/mcp/tools/authenticate/authenticate.ts +9 -11
  81. package/src/mcp/tools/authenticate/build.ts +2 -0
  82. package/src/mcp/tools/authenticate/builder-access.ts +1 -0
  83. package/src/mcp/tools/authenticate/eth-key.ts +48 -18
  84. package/src/mcp/tools/authenticate/import.ts +21 -36
  85. package/src/mcp/tools/authenticate/issue-org-token.ts +84 -0
  86. package/src/mcp/tools/authenticate/issue.ts +9 -9
  87. package/src/mcp/tools/authenticate/org-key-store.ts +25 -0
  88. package/src/mcp/tools/authenticate/resolve-owner-key.ts +10 -12
  89. package/src/mcp/tools/mode/mode.ts +2 -0
  90. package/src/mcp/tools/verification/result.ts +58 -13
  91. package/src/tools/registry.ts +14 -0
@@ -3,6 +3,7 @@ import assert from 'node:assert';
3
3
  import { apiUrl, orgSecret } from "../../../consts.js";
4
4
  import { defineTool } from "../../server.js";
5
5
  import { resolveDecryptionKey } from "../authenticate/decrypt-key.js";
6
+ import { savedOrgKey } from "../authenticate/org-key-store.js";
6
7
  /** Statuses that can carry a Builder-managed terminal result. */
7
8
  const TERMINAL = new Set([
8
9
  'success',
@@ -11,16 +12,24 @@ const TERMINAL = new Set([
11
12
  'cancelled',
12
13
  'expired',
13
14
  ]);
15
+ /** Events whose delivery is a signed result; the rest are plaintext event
16
+ * notifications. Mirrors `verification_result_events()`. */
17
+ const RESULT_EVENTS = new Set([
18
+ 'verification_success',
19
+ 'verification_rejected',
20
+ 'verification_error',
21
+ 'verification_cancelled',
22
+ ]);
14
23
  /**
15
24
  * Resolve the org secret used to authenticate verification reads. Auth is a
16
- * single per-org bearer secret (`rorg_…`), not an eth signature — read it from
17
- * `RECLAIM_ORG_SECRET`.
25
+ * single per-org bearer secret (`rorg_…`), not an eth signature: the call's
26
+ * `orgToken`, else `RECLAIM_ORG_SECRET`.
18
27
  */
19
- function resolveOrgSecret() {
20
- const secret = orgSecret()?.trim();
21
- assert(secret, new Error('RECLAIM_ORG_SECRET is not set. Verification reads authenticate with ' +
22
- 'the org secret (rorg_…) — issue one from the dashboard ' +
23
- '(POST /orgs/{orgId}/token) and export it as RECLAIM_ORG_SECRET.'));
28
+ function resolveOrgSecret(orgToken) {
29
+ const secret = orgToken?.trim() || orgSecret()?.trim() || savedOrgKey();
30
+ assert(secret, new Error('No organization API key. Verification reads authenticate with the org '
31
+ + 'secret (rorg_…): run issue_org_token to save a key, pass orgToken, '
32
+ + 'or export RECLAIM_ORG_SECRET for the MCP server.'));
24
33
  return secret;
25
34
  }
26
35
  /**
@@ -36,8 +45,12 @@ function resolveOrgSecret() {
36
45
  export function verificationResultTool() {
37
46
  return defineTool({
38
47
  name: 'get_verification_result',
39
- description: 'Fetches and verifies a terminal Builder result for one session. '
40
- + 'Requires `RECLAIM_ORG_SECRET`; encrypted deliveries also require '
48
+ description: 'Fetches and verifies a terminal Builder result for one session, '
49
+ + 'while Builder still holds it: until its callback is delivered, or '
50
+ + '7 days undelivered. The callback is the normal way to get results. '
51
+ + 'Authenticates with the org API key: `orgToken`, else '
52
+ + '`RECLAIM_ORG_SECRET`, else the key saved by `issue_org_token`. '
53
+ + 'Encrypted deliveries also require '
41
54
  + 'the matching ETH decryption key. Returns `ready: false` until a '
42
55
  + 'payload is available. Invalid results fail verification and are not '
43
56
  + 'returned. Use `list_verification_events` to diagnose '
@@ -50,10 +63,21 @@ export function verificationResultTool() {
50
63
  type: 'string',
51
64
  description: 'The verification session id from create_verification_session.',
52
65
  },
66
+ orgToken: {
67
+ type: 'string',
68
+ pattern: '^rorg_',
69
+ maxLength: 200,
70
+ description: 'The organization API key (`rorg_…`): from '
71
+ + '`issue_org_token`, or the app\'s RECLAIM_ORG_SECRET. '
72
+ + 'Defaults to the MCP server\'s RECLAIM_ORG_SECRET, then the '
73
+ + 'key saved by `issue_org_token`. Never echo it back to the '
74
+ + 'user.',
75
+ },
53
76
  },
54
77
  },
78
+ annotations: { readOnlyHint: true, openWorldHint: true },
55
79
  }, async (args) => {
56
- const secret = resolveOrgSecret();
80
+ const secret = resolveOrgSecret(args.orgToken);
57
81
  // Optional eth decryption key — absent ⇒ plaintext-only.
58
82
  const decryptionKey = await resolveDecryptionKey();
59
83
  const reclaim = ReclaimVerification.create({
@@ -70,13 +94,29 @@ export function verificationResultTool() {
70
94
  const resultRes = await reclaim.api('GetVerificationResult', {
71
95
  params: { sessionId: args.reclaimSessionId },
72
96
  });
73
- const { items } = resultRes.data;
97
+ const signed = resultRes.data.items
98
+ .filter((item) => RESULT_EVENTS.has(item.event));
99
+ if (!signed.length) {
100
+ return {
101
+ reclaimSessionId: args.reclaimSessionId,
102
+ status,
103
+ ready: false,
104
+ note: 'Session is terminal but no result is stored: none submitted '
105
+ + 'yet, or no callback subscription covers its ending event.',
106
+ };
107
+ }
108
+ // Builder keeps a result (which can carry proofs) only until its
109
+ // callback is delivered, or 7 days undelivered.
110
+ const items = signed.flatMap((item) => (item.payload === null ? [] : [{ ...item, payload: item.payload }]));
74
111
  if (!items.length) {
75
112
  return {
76
113
  reclaimSessionId: args.reclaimSessionId,
77
114
  status,
78
115
  ready: false,
79
- note: 'Session is terminal but no result has been submitted yet.',
116
+ delivered: signed.some((item) => (item.deliveryStatus === 'delivered')),
117
+ note: 'Builder no longer holds this result: it was delivered to '
118
+ + 'the callback (read it there), or went undelivered for 7 days. '
119
+ + 'See list_verification_deliveries.',
80
120
  };
81
121
  }
82
122
  // Each item's `payload` is an ECIES ciphertext encrypted to the org's
@@ -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.32",
4
+ "version": "0.1.34",
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.32",
86
+ "@reclaimprotocol/client": "0.1.34",
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
 
@@ -313,16 +325,13 @@ parameterization applied. `initialUrl` is the page where the user logs in / the
313
325
  value is displayed (for example, `https://github.com/Syed`) — **not** the API
314
326
  endpoint URL inside the provider.
315
327
 
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".
328
+ **1. Resolve the `providerId`.** For a new claim, call
329
+ `create_provider(orgId, domain, slug, title, description)` (`orgId` from
330
+ `list_orgs`; `domain` such as `github.com`, `slug` such as `github-username`,
331
+ `title` such as "GitHub Username"). It starts PRIVATE with an empty seed
332
+ version. Use an existing `providerId` only when your org owns the provider and
333
+ you are revising it: the capture lands as a NEW VERSION on it. For another
334
+ org's provider that almost fits, create your own provider and version.
326
335
 
327
336
  **2. Persist the version.**
328
337
  `create_provider_version_from_capture(providerId, provider, initialUrl, notes)`
@@ -339,12 +348,13 @@ or archive an existing version to apply provider changes.
339
348
  required at verification time. This proves that every request matched; it
340
349
  does not automatically bind a value extracted from one response to another
341
350
  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.
351
+ - **Navigation script:** the script kept with `test_user_script` (`keep: true`)
352
+ is published as `webSettings.jsUserScripts`. Pass `jsUserScripts` only to
353
+ override it. → topic `user-script`. For extra requests the script may fire
354
+ (for example, pagination), pass `allowedJsRequests`. Before populating it for
355
+ a `verifyRequestProof({ providerId })` use case, read topic
356
+ `hash-validation`: a session-unique URL there makes `verifyRequestProof`
357
+ reject every real proof.
348
358
 
349
359
  **3. `requiredContext` is derived for you.** The publish tool scans the recipe's
350
360
  url/body for `{{context.<name>}}` placeholders and emits a JSON Schema requiring
@@ -367,10 +377,8 @@ developer it's awaiting admin review.
367
377
  context params — for example, `context: { "username": "…" }` in
368
378
  `create_verification_session`. Quote the `requiredContext` from the created
369
379
  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).
380
+ matches the supplied values exactly. Then continue as the server
381
+ instructions say under "What the developer wants".
374
382
 
375
383
  **6. If the response includes `_notes` for an attached browser, follow them for
376
384
  an optional authenticated-state retest.** Only when its profile is still signed
@@ -383,34 +391,30 @@ the session for further edits; call `dispose_browser` only after they confirm.
383
391
  A `builder` session is quota-accounted, so don't leave it idle.
384
392
 
385
393
  <!-- topic: discover -->
386
- ## Discover and resolve providers for the developer's app
394
+ ## Discover and resolve existing providers
387
395
 
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:
396
+ Use this flow only when the developer asks to reuse existing providers or
397
+ doesn't know what a site supports. Otherwise build a new provider (server
398
+ instructions, "Choose the provider").
390
399
 
391
400
  1. Ask which website their users prove data from.
392
401
  2. `discover_data_points({ domain })` → what's verifiable there. Confirm the
393
402
  data points with the developer.
394
403
  3. `discover_resolve({ domain, dataPoints })` → the smallest provider set
395
404
  (`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
- Integrate or new app → topic `integration`.
408
-
409
- `merge.mergeable` only signals the set *could* collapse into one provider — you
410
- still pass them all; the Builder runs them in one session.
411
-
412
- For client integration, result verification, or TEE binding, read topic
413
- `integration`.
405
+ any `uncovered` points (author those with the checklist in topic
406
+ `overview`, then re-resolve).
407
+ 4. Use only the data points the developer confirmed: show and read only those
408
+ and ignore the rest. A provider that needs any change gets your own copy
409
+ (server instructions, "Choose the provider"). Otherwise it's usable as-is;
410
+ don't capture, replay, or prove it unless the developer asks.
411
+ 5. To try them: `create_verification_session({ providers: [{ providerId }, …],
412
+ context, verificationClientUrl })` with EVERY resolved provider id and the
413
+ collected context. The server instructions ("Try a provider without an
414
+ app") cover the callback and polling. To integrate: topic `integration`.
415
+
416
+ `merge.mergeable` only signals the set *could* collapse into one provider. Pass
417
+ them all; the Builder runs them in one session.
414
418
 
415
419
  <!-- topic: integration -->
416
420
 
@@ -424,7 +424,8 @@ conversation.
424
424
  1. **`resolve_owner_key`** returns only the derived address.
425
425
  - `{ ready: true, address }` → call `run_proof` with it. No prompts.
426
426
  - `{ ready: false }` → ask the developer once: **their own key** →
427
- `import_credentials({ privateKey: "0x…" })` (a `keyFile` path also works),
427
+ `import_credentials({ keyFile })` (a path to a file holding the key; never
428
+ ask them to paste it),
428
429
  or **a fresh one** → `issue_credentials({})` (a fine default for test
429
430
  proofs — owner identity doesn't matter). Either writes to the project
430
431
  `.env` (mode 0600, added to `.gitignore`), never to `~/.reclaim`. Then