@reclaimprotocol/agent 0.1.34 → 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.
- package/README.md +3 -3
- package/lib/mcp/api/operations.gen.js +340 -150
- package/lib/mcp/instructions.js +3 -3
- package/lib/mcp/start.js +8 -7
- package/lib/mcp/tools/agent/build.js +2 -0
- package/lib/mcp/tools/agent/check-attestor.d.ts +5 -0
- package/lib/mcp/tools/agent/check-attestor.js +37 -0
- package/lib/mcp/tools/agent/live-view.js +42 -18
- package/lib/mcp/tools/agent/proof.d.ts +12 -0
- package/lib/mcp/tools/agent/proof.js +32 -0
- package/lib/mcp/tools/agent/shared.d.ts +2 -1
- package/lib/mcp/tools/agent/shared.js +28 -24
- package/lib/mcp/tools/agent/start-integration.js +5 -4
- package/lib/mcp/tools/verification/build.d.ts +3 -1
- package/lib/mcp/tools/verification/build.js +10 -2
- package/lib/mcp/tools/verification/callback-listener.d.ts +51 -0
- package/lib/mcp/tools/verification/callback-listener.js +163 -0
- package/lib/mcp/tools/verification/index.d.ts +2 -0
- package/lib/mcp/tools/verification/index.js +2 -0
- package/lib/mcp/tools/verification/proxy-callback-events.d.ts +59 -0
- package/lib/mcp/tools/verification/proxy-callback-events.js +300 -0
- package/lib/mcp/tools/verification/read-callback-events.d.ts +3 -0
- package/lib/mcp/tools/verification/read-callback-events.js +135 -0
- package/lib/mcp/tools/verification/result.d.ts +6 -0
- package/lib/mcp/tools/verification/result.js +1 -1
- package/lib/proof/check-attestor.d.ts +67 -0
- package/lib/proof/check-attestor.js +156 -0
- package/package.json +2 -2
- package/skill/SKILL.builder.md +7 -1
- package/skill/SKILL.common.md +21 -9
- package/skill/builder-mode.md +17 -12
- package/skill/start-integration.md +36 -11
- package/skill/verification-client-integration.md +15 -9
- package/src/mcp/api/operations.gen.ts +344 -150
- package/src/mcp/instructions.ts +3 -3
- package/src/mcp/start.ts +9 -7
- package/src/mcp/tools/agent/build.ts +2 -0
- package/src/mcp/tools/agent/check-attestor.ts +49 -0
- package/src/mcp/tools/agent/live-view.ts +42 -18
- package/src/mcp/tools/agent/proof.ts +55 -0
- package/src/mcp/tools/agent/shared.ts +33 -23
- package/src/mcp/tools/agent/start-integration.ts +5 -4
- package/src/mcp/tools/verification/build.ts +10 -2
- package/src/mcp/tools/verification/callback-listener.ts +203 -0
- package/src/mcp/tools/verification/index.ts +2 -0
- package/src/mcp/tools/verification/proxy-callback-events.ts +374 -0
- package/src/mcp/tools/verification/read-callback-events.ts +160 -0
- package/src/mcp/tools/verification/result.ts +1 -1
- package/src/proof/check-attestor.ts +209 -0
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type RegisteredTool } from '../../server.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the org secret used to authenticate verification reads. Auth is a
|
|
4
|
+
* single per-org bearer secret (`rorg_…`), not an eth signature: the call's
|
|
5
|
+
* `orgToken`, else `RECLAIM_ORG_SECRET`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveOrgSecret(orgToken?: string): string;
|
|
2
8
|
/**
|
|
3
9
|
* `get_verification_result` — check a session's status and, once a result has
|
|
4
10
|
* been submitted, pull the stored payload(s) and verify locally. Auth is the
|
|
@@ -25,7 +25,7 @@ const RESULT_EVENTS = new Set([
|
|
|
25
25
|
* single per-org bearer secret (`rorg_…`), not an eth signature: the call's
|
|
26
26
|
* `orgToken`, else `RECLAIM_ORG_SECRET`.
|
|
27
27
|
*/
|
|
28
|
-
function resolveOrgSecret(orgToken) {
|
|
28
|
+
export function resolveOrgSecret(orgToken) {
|
|
29
29
|
const secret = orgToken?.trim() || orgSecret()?.trim() || savedOrgKey();
|
|
30
30
|
assert(secret, new Error('No organization API key. Verification reads authenticate with the org '
|
|
31
31
|
+ 'secret (rorg_…): run issue_org_token to save a key, pass orgToken, '
|
|
@@ -0,0 +1,67 @@
|
|
|
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 './patch-domhandler.ts';
|
|
8
|
+
/**
|
|
9
|
+
* Default time budget for one check. A live run takes about 8 s; a longer
|
|
10
|
+
* budget risks the MCP client's own tool-call timeout.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CHECK_ATTESTOR_TIMEOUT_MS = 45000;
|
|
13
|
+
/**
|
|
14
|
+
* Request params for the check: the public CoinGecko price request from the
|
|
15
|
+
* attestor-core examples. Source:
|
|
16
|
+
* https://raw.githubusercontent.com/reclaimprotocol/attestor-core/refs/heads/main/example/coin-gecko.json
|
|
17
|
+
*/
|
|
18
|
+
export declare const COIN_GECKO_PARAMS: {
|
|
19
|
+
url: string;
|
|
20
|
+
method: "GET";
|
|
21
|
+
body: string;
|
|
22
|
+
headers: {
|
|
23
|
+
'User-Agent': string;
|
|
24
|
+
'Sec-Fetch-Mode': string;
|
|
25
|
+
'Sec-Fetch-Site': string;
|
|
26
|
+
};
|
|
27
|
+
responseMatches: {
|
|
28
|
+
invert: boolean;
|
|
29
|
+
type: "regex";
|
|
30
|
+
value: string;
|
|
31
|
+
}[];
|
|
32
|
+
responseRedactions: {
|
|
33
|
+
jsonPath: string;
|
|
34
|
+
regex: string;
|
|
35
|
+
xPath: string;
|
|
36
|
+
}[];
|
|
37
|
+
additionalClientOptions: {};
|
|
38
|
+
};
|
|
39
|
+
export type AttestorCheckErrorKind =
|
|
40
|
+
/** Socket, connection, DNS, TLS, or timeout failure. */
|
|
41
|
+
'attestor-unreachable'
|
|
42
|
+
/** The attestor answered and rejected or failed the claim. */
|
|
43
|
+
| 'attestor-error'
|
|
44
|
+
/** Local setup (ZK files) failed before the attestor was contacted. */
|
|
45
|
+
| 'setup-failed';
|
|
46
|
+
export interface AttestorCheckResult {
|
|
47
|
+
ok: boolean;
|
|
48
|
+
attestorUrl: string;
|
|
49
|
+
durationMs: number;
|
|
50
|
+
extractedParameters?: Record<string, string>;
|
|
51
|
+
error?: string;
|
|
52
|
+
errorKind?: AttestorCheckErrorKind;
|
|
53
|
+
}
|
|
54
|
+
/** Creates the claim on the attestor (`createClaimOnAttestor` by default). */
|
|
55
|
+
export type CreateClaim = (args: Parameters<typeof createClaimOnAttestor>[0]) => Promise<unknown>;
|
|
56
|
+
export interface CheckAttestorOptions {
|
|
57
|
+
attestorUrl?: string;
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
/** Replaces the network call in tests. */
|
|
60
|
+
createClaim?: CreateClaim;
|
|
61
|
+
/** Replaces the ZK file download in tests. */
|
|
62
|
+
prepare?: () => Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/** True when `message` looks like a network failure rather than a rejection. */
|
|
65
|
+
export declare function isNetworkError(message: string | undefined): boolean;
|
|
66
|
+
/** Prove the public CoinGecko request once and classify the outcome. */
|
|
67
|
+
export declare function checkAttestor(opts?: CheckAttestorOptions): Promise<AttestorCheckResult>;
|
|
@@ -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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reclaimprotocol/agent",
|
|
3
3
|
"title": "Reclaim Agent",
|
|
4
|
-
"version": "0.1.
|
|
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.
|
|
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",
|
package/skill/SKILL.builder.md
CHANGED
|
@@ -315,6 +315,11 @@ provider JSON. That's the final result.
|
|
|
315
315
|
developer what's happening and drive the bootstrap yourself — see topic
|
|
316
316
|
`credentials`. No restart needed, and the existing `draftId` stays valid.
|
|
317
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
|
+
|
|
318
323
|
<!-- common: prove-tail -->
|
|
319
324
|
|
|
320
325
|
<!-- topic: publish -->
|
|
@@ -456,6 +461,7 @@ or omit `interceptorOptions`). If that fixes the provider, keep interception
|
|
|
456
461
|
off and replace intercepted claims with `window.Reclaim.requestClaim`. Full
|
|
457
462
|
checklist and remedies: topic `interception`.
|
|
458
463
|
|
|
459
|
-
**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`.
|
|
460
466
|
**Browser won't launch or attach** → topic `browser`.
|
|
461
467
|
**Missing signing key** → topic `credentials`.
|
package/skill/SKILL.common.md
CHANGED
|
@@ -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`
|
|
116
|
-
the `command` (`cloudflared tunnel --url http://localhost:<port>`). The
|
|
117
|
-
server does not run cloudflared.
|
|
118
|
-
2.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
|
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.
|
package/skill/builder-mode.md
CHANGED
|
@@ -42,19 +42,17 @@ or `run_proof` against it unless the developer asks.
|
|
|
42
42
|
|
|
43
43
|
### Try a provider without an app
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
to
|
|
45
|
+
Session creation returns 409 while the org has no callback subscription.
|
|
46
|
+
`read_callback_events` creates the org's pull listener, which satisfies that
|
|
47
|
+
check and holds results for you to read. No tunnel or public URL is needed.
|
|
48
48
|
|
|
49
|
-
1.
|
|
50
|
-
events with `create_org_callback_subscription` (for example, a tunnel to a
|
|
51
|
-
local listener that saves the body it receives).
|
|
49
|
+
1. Call `read_callback_events`.
|
|
52
50
|
2. `create_verification_session`, then give the developer `verificationUrl`.
|
|
53
|
-
3. Poll `get_verification_session` until it is terminal.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
3. Poll `get_verification_session` until it is terminal.
|
|
52
|
+
4. Call `read_callback_events({ sessionId })` for the verified result.
|
|
53
|
+
|
|
54
|
+
When you set up a new provider build, call `check_attestor` once. If it fails,
|
|
55
|
+
tell the developer before they spend time on the build.
|
|
58
56
|
|
|
59
57
|
## Secrets
|
|
60
58
|
|
|
@@ -77,11 +75,18 @@ TEE binding. Edit the app with your file tools. Use the HTTP API
|
|
|
77
75
|
`@reclaimprotocol/client`. The only exception is `results.receive` on the
|
|
78
76
|
callback, or when the developer asks for the client package.
|
|
79
77
|
|
|
78
|
+
For local development, call `proxy_callback_events({ port, path })` to forward
|
|
79
|
+
events to the app's callback route on localhost. The local listener works only
|
|
80
|
+
while this MCP server runs. Production needs a webhook:
|
|
81
|
+
`create_org_callback_subscription` with the deployed app's public URL. Without
|
|
82
|
+
one, session creation returns 409.
|
|
83
|
+
|
|
80
84
|
Management tools (`list_orgs`, `create_org_callback_subscription`,
|
|
81
85
|
`issue_org_token`) use the developer's sign-in. Session tools
|
|
82
86
|
(`create_verification_session`, `get_verification_session`,
|
|
83
87
|
`list_verification_events`, `list_verification_deliveries`,
|
|
84
|
-
`retry_verification_callback`
|
|
88
|
+
`retry_verification_callback`, `read_callback_events`,
|
|
89
|
+
`proxy_callback_events`) are the production API and use the org key.
|
|
85
90
|
|
|
86
91
|
**Existing `@reclaimprotocol/js-sdk` projects.** If `package.json` depends on
|
|
87
92
|
`@reclaimprotocol/js-sdk`, ask first:
|
|
@@ -12,11 +12,38 @@ Follow this plan end to end. Mark each step done as you go. Do not ask the devel
|
|
|
12
12
|
- [ ] Call `list_orgs`. Use the only organization, or ask the developer which one when there are several.
|
|
13
13
|
<!-- endif -->
|
|
14
14
|
|
|
15
|
-
## 2.
|
|
15
|
+
## 2. Set up the app
|
|
16
|
+
|
|
17
|
+
Setup does not need the provider, so it runs in parallel with step 3 or before it.
|
|
18
|
+
|
|
19
|
+
**Delegation.** If you can run subagents, hand this step to one subagent and start step 3 now: step 3 needs the developer at the browser, step 2 does not. Give the subagent a self-contained brief:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Goal: let this app's users prove "{{goal}}" with Reclaim.
|
|
23
|
+
Repository: <absolute path>. Organization: <org ID>.
|
|
24
|
+
Task: do step 2 of the plan below and report the file paths you changed, the
|
|
25
|
+
callback route URL, and the env variable that holds the provider ID.
|
|
26
|
+
Do not build the provider. Never print or commit the org key.
|
|
27
|
+
<paste the step 2 checklist>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Without subagents, do step 2 first.
|
|
31
|
+
|
|
32
|
+
- [ ] Detect the app's framework and package manager from the repository, and use them. Read `how_it_works({ topic: "integration" })`.
|
|
33
|
+
- [ ] Call `issue_org_token` with the organization, this project's absolute path, and the server env file. It writes the key to that file and never returns it. Never print or commit the key. Confirm the env file is in `.gitignore`.
|
|
34
|
+
- [ ] Add a backend route that creates a verification session (`POST /verifications/sessions`) and returns `verificationUrl`. Read the provider ID from server config (an env variable such as `RECLAIM_PROVIDER_ID`), so the route works before the provider exists.
|
|
35
|
+
- [ ] Add a callback route that verifies each result with `results.receive` from `@reclaimprotocol/client`, then stores only the requested data points.
|
|
36
|
+
- [ ] Add a frontend Verify button that opens `verificationUrl`, and shows the stored result when it arrives.
|
|
37
|
+
- [ ] Start the app. Call `proxy_callback_events({ port, path })` with the callback route's port and path. It forwards the org's verification events to that route on localhost, with no public URL.
|
|
38
|
+
<!-- ifnot:providerIds -->
|
|
39
|
+
- [ ] Call `check_attestor` once. If it fails, tell the developer before they spend time on the provider build: this machine cannot reach the attestor.
|
|
40
|
+
<!-- endif -->
|
|
41
|
+
|
|
42
|
+
## 3. Get the provider
|
|
16
43
|
|
|
17
44
|
<!-- if:providerIds -->
|
|
18
45
|
- [ ] Use these existing providers: {{providerIds}}. Read each with `get_provider` and keep only the data points "{{goal}}" needs. Show the developer and use only those; ignore the rest.
|
|
19
|
-
- [ ] If a provider needs any change (a missing data point, inputs the developer didn't ask for, a different value), make your own copy as the server instructions describe ("Choose the provider"). Otherwise skip to step
|
|
46
|
+
- [ ] If a provider needs any change (a missing data point, inputs the developer didn't ask for, a different value), make your own copy as the server instructions describe ("Choose the provider"). Otherwise skip to step 4.
|
|
20
47
|
<!-- endif -->
|
|
21
48
|
<!-- ifnot:providerIds -->
|
|
22
49
|
Build a new organization-private provider for exactly "{{goal}}". Do not search published providers first.
|
|
@@ -37,13 +64,11 @@ Build a new organization-private provider for exactly "{{goal}}". Do not search
|
|
|
37
64
|
- [ ] Call `dispose_browser` after asking the developer.
|
|
38
65
|
<!-- endif -->
|
|
39
66
|
|
|
40
|
-
##
|
|
67
|
+
## 4. Wire the provider and test end to end
|
|
41
68
|
|
|
42
|
-
- [ ]
|
|
43
|
-
- [ ]
|
|
44
|
-
- [ ]
|
|
45
|
-
- [ ]
|
|
46
|
-
- [ ]
|
|
47
|
-
- [ ]
|
|
48
|
-
- [ ] Add a frontend Verify button that opens `verificationUrl`, and shows the stored result when it arrives.
|
|
49
|
-
- [ ] Run the app, then tell the developer exactly how to test end to end: open the app, click Verify, sign in, and see the stored result.
|
|
69
|
+
- [ ] Set the provider ID in the app's server config.
|
|
70
|
+
- [ ] Confirm the callback route is forwarding: `proxy_callback_events` with no `stop` returns its status.
|
|
71
|
+
- [ ] Tell the developer exactly how to test: open the app, click Verify, sign in, and see the stored result.
|
|
72
|
+
- [ ] After they click Verify, read the delivered events from `proxy_callback_events` status (`recent`) or `read_callback_events`. If the result did not arrive, call `list_verification_deliveries` and fix the cause. Repeat until the app shows the stored result.
|
|
73
|
+
- [ ] Ask the developer to set up the production callback. The local listener works only while this coding agent's MCP server runs; once it stops, the deployed app receives no results. Call `create_org_callback_subscription` with the deployed app's public callback route URL for all five session-ending events: `verification_success`, `verification_rejected`, `verification_error`, `verification_cancelled`, and `session_expired`.
|
|
74
|
+
- [ ] If they have no deployed URL yet, tell them: "When you deploy, ask me to run `create_org_callback_subscription` with your public callback URL, or results will not reach the app."
|
|
@@ -21,12 +21,16 @@ server), or migrate to the session flow below. When migrating, remove
|
|
|
21
21
|
1. `get_devtools_mode` confirms Builder mode. `get_me` or `authenticate` signs
|
|
22
22
|
in the developer. Find the organization with `list_orgs`; call `create_org`
|
|
23
23
|
only when they want a new one.
|
|
24
|
-
2.
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
register
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
2. Receive callbacks. Local development: call
|
|
25
|
+
`proxy_callback_events({ port, path })` to forward the org's events to the
|
|
26
|
+
app's callback route on localhost; no public URL. Deployed app (required for
|
|
27
|
+
production): register a
|
|
28
|
+
public callback URL with `create_org_callback_subscription`, including
|
|
29
|
+
all five session-ending events. If the
|
|
30
|
+
app needs encrypted results, register its public key with `set_org_keypair`;
|
|
31
|
+
keep the private key only in the backend. `set_org_keypair` refuses to
|
|
32
|
+
replace an existing key unless you pass `replace: true`; pass it only when
|
|
33
|
+
the developer asks.
|
|
30
34
|
3. Org key: reuse the `RECLAIM_ORG_SECRET` the app already has. Otherwise an
|
|
31
35
|
organization owner calls `issue_org_token`. It writes the key to the
|
|
32
36
|
project's env file (default `.env`, variable `RECLAIM_ORG_SECRET`) and
|
|
@@ -75,10 +79,12 @@ Use the API base URL the developer's Builder runs at (production:
|
|
|
75
79
|
`https://build.reclaimprotocol.org`). Every call authenticates with
|
|
76
80
|
`authorization: Bearer $RECLAIM_ORG_SECRET`.
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
Deployed apps subscribe a callback once (or use
|
|
83
|
+
`create_org_callback_subscription`); local development uses
|
|
84
|
+
`proxy_callback_events` instead. Session creation returns 409 while the org has
|
|
85
|
+
no subscription, and a result is only delivered to
|
|
80
86
|
subscriptions that include the event the session ends with, so subscribe to all
|
|
81
|
-
five:
|
|
87
|
+
five session-ending events:
|
|
82
88
|
|
|
83
89
|
```bash
|
|
84
90
|
curl -X POST https://build.reclaimprotocol.org/verifications/callback-subscriptions \
|