@reclaimprotocol/agent 0.1.15 → 0.1.18
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/lib/authoring/index.d.ts +1 -1
- package/lib/authoring/index.js +1 -1
- package/lib/authoring/util.js +3 -0
- package/lib/config/store.d.ts +7 -0
- package/lib/consts.d.ts +15 -0
- package/lib/consts.js +35 -0
- package/lib/index.js +1 -1
- package/lib/mcp/api/operations.gen.d.ts +8 -16
- package/lib/mcp/api/operations.gen.js +428 -479
- package/lib/mcp/remap_errors.js +7 -14
- package/lib/mcp/start.js +10 -3
- package/lib/mcp/tools/agent/proof.js +3 -3
- package/lib/mcp/tools/agent/publish.js +33 -0
- package/lib/mcp/tools/authenticate/authenticate.js +178 -93
- package/lib/mcp/tools/authenticate/eth-key.js +2 -3
- package/lib/mcp/tools/index.d.ts +1 -0
- package/lib/mcp/tools/index.js +1 -0
- package/lib/mcp/tools/mode/index.d.ts +1 -0
- package/lib/mcp/tools/mode/index.js +3 -0
- package/lib/mcp/tools/mode/mode.d.ts +17 -0
- package/lib/mcp/tools/mode/mode.js +69 -0
- package/lib/old/to-register.d.ts +16 -8
- package/lib/old/to-register.js +29 -21
- package/lib/old/tools/publish.js +86 -8
- package/lib/provider/injection-doc.js +4 -3
- package/lib/provider/schema.d.ts +26 -1
- package/lib/provider/to-version.d.ts +19 -2
- package/lib/provider/to-version.js +22 -4
- package/lib/tools/registry.d.ts +6 -13
- package/lib/tools/registry.js +29 -0
- package/package.json +5 -8
- package/scripts/prepend-shebang.js +1 -1
- package/skill/builder-mode.md +2 -0
- package/skill/old-mode.md +3 -1
- package/src/authoring/index.ts +1 -0
- package/src/authoring/util.ts +3 -0
- package/src/cdp/launcher.ts +1 -1
- package/src/config/store.ts +8 -0
- package/src/consts.ts +44 -0
- package/src/index.ts +1 -1
- package/src/mcp/api/operations.gen.ts +430 -487
- package/src/mcp/remap_errors.ts +8 -15
- package/src/mcp/start.ts +11 -2
- package/src/mcp/tools/agent/proof.ts +3 -3
- package/src/mcp/tools/agent/publish.ts +42 -0
- package/src/mcp/tools/authenticate/authenticate.ts +238 -108
- package/src/mcp/tools/authenticate/eth-key.ts +7 -5
- package/src/mcp/tools/index.ts +1 -0
- package/src/mcp/tools/mode/index.ts +3 -0
- package/src/mcp/tools/mode/mode.ts +82 -0
- package/src/old/to-register.ts +50 -27
- package/src/old/tools/publish.ts +108 -8
- package/src/proof/debug-response-dump.ts +1 -1
- package/src/proof/proof-core.ts +1 -1
- package/src/proof/zk-files.ts +2 -2
- package/src/provider/dom-xpath.ts +1 -1
- package/src/provider/draft.ts +3 -3
- package/src/provider/injection-doc.ts +4 -3
- package/src/provider/schema.ts +27 -1
- package/src/provider/to-version.ts +45 -6
- package/src/tools/registry.ts +19 -0
- package/src/cli/args.ts +0 -20
- package/src/cli/commands.ts +0 -27
- package/src/cli.ts +0 -7
package/lib/old/to-register.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
1
2
|
/** Default web settings, mirroring the builder openapi defaults
|
|
2
|
-
* (`ProviderWebSettings.geoLocation`, `PortalClientOptions.useProxy`). The
|
|
3
|
+
* (`ProviderWebSettings.geoLocation`, `PortalClientOptions.useProxy`). The
|
|
3
4
|
* backend accepts both: `geoLocation` lives on the providerConfig, `useProxy`
|
|
4
5
|
* is a Provider-level flag (register only). */
|
|
5
6
|
const DEFAULT_GEO_LOCATION = '{{DYNAMIC_GEO}}';
|
|
@@ -10,29 +11,29 @@ const DEFAULT_USE_PROXY = true;
|
|
|
10
11
|
* entries — a `ReclaimProvider` satisfies it structurally either way. */
|
|
11
12
|
function requestDataFromProvider(provider) {
|
|
12
13
|
const method = String(provider.method).toUpperCase();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
// The old backend has no dotted interpolation: consumer-supplied params
|
|
14
|
+
assert(method === 'GET' || method === 'POST', new Error('Devtools supports only GET/POST requests; got ' +
|
|
15
|
+
`"${provider.method}". Re-capture against a GET/POST endpoint.`));
|
|
16
|
+
// The devtools backend has no dotted interpolation: consumer-supplied params
|
|
18
17
|
// must use the `{{context_<name>}}` form here, not the builder's
|
|
19
18
|
// `{{context.<name>}}`. Catch the mix-up at publish with a clear error
|
|
20
19
|
// instead of a template that silently never resolves.
|
|
21
20
|
const dotted = `${provider.url}\n${provider.body ?? ''}`
|
|
22
21
|
.match(/\{\{context\.[A-Za-z_][A-Za-z0-9_]*\}\}/);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'for this backend.');
|
|
27
|
-
}
|
|
22
|
+
assert(!dotted, new Error(`Devtools does not support ${dotted?.[0]} — rename ` +
|
|
23
|
+
'consumer-supplied params to the {{context_<name>}} form ' +
|
|
24
|
+
'for this backend.'));
|
|
28
25
|
const responseMatches = (provider.responseMatches ?? [])
|
|
29
26
|
.map((m, i) => ({
|
|
30
27
|
value: m.value,
|
|
31
28
|
type: m.type ?? 'contains',
|
|
32
|
-
isOptional
|
|
29
|
+
// The backend honors isOptional on BOTH register and add-version
|
|
30
|
+
// (responseMatches pass through verbatim) — forward the author's
|
|
31
|
+
// flag, default false. (`invert` has no old-backend equivalent and
|
|
32
|
+
// stays dropped.)
|
|
33
|
+
isOptional: m.isOptional ?? false,
|
|
33
34
|
order: i,
|
|
34
35
|
}));
|
|
35
|
-
//
|
|
36
|
+
// Devtools wants `responseMatches` and `responseRedactions` as PARALLEL
|
|
36
37
|
// arrays — same shape the builder uses, so pass them through directly. We do
|
|
37
38
|
// NOT enforce any match/redaction count relationship here (and never pad —
|
|
38
39
|
// empty placeholder matches break proof generation): the author keeps full
|
|
@@ -58,33 +59,35 @@ function requestDataFromProvider(provider) {
|
|
|
58
59
|
});
|
|
59
60
|
const requestData = {
|
|
60
61
|
url: provider.url,
|
|
61
|
-
// `{{param}}` placeholders → TEMPLATE
|
|
62
|
-
//
|
|
63
|
-
urlType: provider.
|
|
62
|
+
// Explicit override wins; otherwise `{{param}}` placeholders → TEMPLATE
|
|
63
|
+
// so the backend templates them, else a fixed CONSTANT url.
|
|
64
|
+
urlType: provider.urlType
|
|
65
|
+
?? (provider.url.includes('{{') ? 'TEMPLATE' : 'CONSTANT'),
|
|
64
66
|
method: method,
|
|
65
67
|
responseMatches,
|
|
66
68
|
responseRedactions,
|
|
67
69
|
// The verification client re-supplies cookies/auth at runtime; secrets
|
|
68
70
|
// are never persisted (propose_provider already stripped them).
|
|
69
|
-
credentials: 'include',
|
|
71
|
+
credentials: provider.credentials ?? 'include',
|
|
70
72
|
};
|
|
71
73
|
if (provider.body !== undefined) {
|
|
72
74
|
requestData.bodySniff = { enabled: true, template: provider.body };
|
|
73
75
|
}
|
|
74
|
-
// NOTE: `responseVariables` is intentionally omitted — the
|
|
76
|
+
// NOTE: `responseVariables` is intentionally omitted — the devtools backend
|
|
75
77
|
// auto-extracts it from `{{var}}` placeholders in responseMatches. Sending
|
|
76
78
|
// it ourselves would risk drifting from the server's own extraction.
|
|
77
79
|
return requestData;
|
|
78
80
|
}
|
|
79
81
|
/** One `requestData` entry per drafted provider. A multi-request provider
|
|
80
|
-
* becomes parallel entries that the
|
|
81
|
-
* (the same way the builder version carries multiple
|
|
82
|
+
* becomes parallel entries that the devtools backend runs together at
|
|
83
|
+
* verification (the same way the builder version carries multiple
|
|
84
|
+
* `requests`). */
|
|
82
85
|
function requestDataFrom(providers) {
|
|
83
86
|
return providers.map(requestDataFromProvider);
|
|
84
87
|
}
|
|
85
88
|
/** Translate fresh `allowedInjectedRequestData` drafts (the author's input
|
|
86
89
|
* shape, `AllowedJsRequest[]` — same shape the builder translator uses for
|
|
87
|
-
* its own `allowedJsRequests`) into the
|
|
90
|
+
* its own `allowedJsRequests`) into the devtools backend's shape. A
|
|
88
91
|
* carried-forward value read back from an existing version is ALREADY in
|
|
89
92
|
* this shape and should be passed straight through instead — don't run it
|
|
90
93
|
* through this a second time. */
|
|
@@ -165,6 +168,11 @@ export function providerToConfigBody(providers, opts, version, versionInfo) {
|
|
|
165
168
|
if (opts.extensionConfig !== undefined) {
|
|
166
169
|
providerConfig.extensionConfig = opts.extensionConfig;
|
|
167
170
|
}
|
|
171
|
+
// The config (add-version) controller persists providerConfig verbatim,
|
|
172
|
+
// so this lands as-is; the register controller ignores it entirely.
|
|
173
|
+
if (opts.disableRequestReplay !== undefined) {
|
|
174
|
+
providerConfig.disableRequestReplay = opts.disableRequestReplay;
|
|
175
|
+
}
|
|
168
176
|
return { providerConfig, version, versionInfo };
|
|
169
177
|
}
|
|
170
178
|
/**
|
package/lib/old/tools/publish.js
CHANGED
|
@@ -4,6 +4,12 @@ import { INJECTION_GUIDE } from "../../provider/injection-doc.js";
|
|
|
4
4
|
import { countsFor, matchRedactionWarnings, } from "../../provider/match-redaction-warning.js";
|
|
5
5
|
import { bumpPatchString, extractVersions, injectedRequestDataFrom, providerToConfigBody, providerToRegisterBody, } from "../to-register.js";
|
|
6
6
|
const DEFAULT_VERSION_INFO = 'Updated from MCP browser capture';
|
|
7
|
+
/** Pull the new provider's id out of a register response, tolerant of the
|
|
8
|
+
* envelope (`{providerId}`, `{provider:{providerId}}`, `{id}`). */
|
|
9
|
+
function extractProviderId(resp) {
|
|
10
|
+
const obj = resp;
|
|
11
|
+
return obj?.providerId ?? obj?.provider?.providerId ?? obj?.id;
|
|
12
|
+
}
|
|
7
13
|
/**
|
|
8
14
|
* The old backend lets you add a version only to a provider YOU own. A
|
|
9
15
|
* `providerId` for a public provider (or anyone else's) comes back as
|
|
@@ -44,7 +50,7 @@ function rethrowOwnershipError(err, providerId) {
|
|
|
44
50
|
export function createVersionFromCaptureTool(client) {
|
|
45
51
|
return defineTool({
|
|
46
52
|
name: 'create_provider_version_from_capture',
|
|
47
|
-
description: 'Publish drafted ReclaimProvider(s) to the
|
|
53
|
+
description: 'Publish drafted ReclaimProvider(s) to the devtools backend. ' +
|
|
48
54
|
'Pass the `provider` object from propose_provider VERBATIM and the ' +
|
|
49
55
|
'`initialUrl` the user landed on (the login/landing page, NOT the ' +
|
|
50
56
|
'API url). \n\n' +
|
|
@@ -62,7 +68,7 @@ export function createVersionFromCaptureTool(client) {
|
|
|
62
68
|
"PUBLIC provider you don't own, omit `providerId` to register your " +
|
|
63
69
|
'own copy (you cannot edit someone else\'s provider here). \n\n' +
|
|
64
70
|
'For data gated behind login/navigation, pass `customInjection` — a ' +
|
|
65
|
-
'page-injection script (
|
|
71
|
+
'page-injection script (the devtools name for the builder\'s ' +
|
|
66
72
|
'jsUserScripts) that drives the page to where the request fires. \n\n' +
|
|
67
73
|
'BY DESIGN, this backend only ever CREATES A NEW VERSION (POST) — ' +
|
|
68
74
|
'there is no partial-patch/edit endpoint, and this tool does NOT ' +
|
|
@@ -79,7 +85,14 @@ export function createVersionFromCaptureTool(client) {
|
|
|
79
85
|
'values plus your change) back into this call. ' +
|
|
80
86
|
'`allowedInjectedRequestData`/`useIncognitoWebview`/' +
|
|
81
87
|
'`extensionConfig` only take effect on add-version — the register ' +
|
|
82
|
-
'endpoint (no `providerId`) ignores them.
|
|
88
|
+
'endpoint (no `providerId`) ignores them. `disableRequestReplay` ' +
|
|
89
|
+
'is also add-version-only, but when it is true the tool follows ' +
|
|
90
|
+
'a register up with an add-version call automatically so it takes ' +
|
|
91
|
+
'effect on new providers too. Per-request fields you may set on ' +
|
|
92
|
+
'`provider`/`providers`: `credentials` (omit/same-origin/include, ' +
|
|
93
|
+
'default include), `urlType` (REGEX/CONSTANT/TEMPLATE, default ' +
|
|
94
|
+
'derived from {{placeholders}}), and `isOptional` on each ' +
|
|
95
|
+
'responseMatch (default false). \n\n' +
|
|
83
96
|
'Captured requests must be GET/POST. OPRF redactions are supported.',
|
|
84
97
|
inputSchema: {
|
|
85
98
|
type: 'object',
|
|
@@ -94,6 +107,19 @@ export function createVersionFromCaptureTool(client) {
|
|
|
94
107
|
method: { type: 'string' },
|
|
95
108
|
headers: { type: 'object' },
|
|
96
109
|
body: { type: 'string' },
|
|
110
|
+
credentials: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
enum: ['omit', 'same-origin', 'include'],
|
|
113
|
+
description: 'How the verification client supplies cookies/auth ' +
|
|
114
|
+
'when replaying this request. Default `include`.',
|
|
115
|
+
},
|
|
116
|
+
urlType: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
enum: ['REGEX', 'CONSTANT', 'TEMPLATE'],
|
|
119
|
+
description: 'How the backend treats the URL. Default derived: ' +
|
|
120
|
+
'TEMPLATE when the URL has {{placeholders}}, else ' +
|
|
121
|
+
'CONSTANT.',
|
|
122
|
+
},
|
|
97
123
|
responseMatches: { type: 'array' },
|
|
98
124
|
responseRedactions: { type: 'array' },
|
|
99
125
|
paramValues: { type: 'object' },
|
|
@@ -122,6 +148,14 @@ export function createVersionFromCaptureTool(client) {
|
|
|
122
148
|
method: { type: 'string' },
|
|
123
149
|
headers: { type: 'object' },
|
|
124
150
|
body: { type: 'string' },
|
|
151
|
+
credentials: {
|
|
152
|
+
type: 'string',
|
|
153
|
+
enum: ['omit', 'same-origin', 'include'],
|
|
154
|
+
},
|
|
155
|
+
urlType: {
|
|
156
|
+
type: 'string',
|
|
157
|
+
enum: ['REGEX', 'CONSTANT', 'TEMPLATE'],
|
|
158
|
+
},
|
|
125
159
|
responseMatches: { type: 'array' },
|
|
126
160
|
responseRedactions: { type: 'array' },
|
|
127
161
|
paramValues: { type: 'object' },
|
|
@@ -202,6 +236,13 @@ export function createVersionFromCaptureTool(client) {
|
|
|
202
236
|
properties: {
|
|
203
237
|
url: { type: 'string' },
|
|
204
238
|
method: { type: 'string' },
|
|
239
|
+
urlType: {
|
|
240
|
+
type: 'string',
|
|
241
|
+
enum: ['REGEX', 'CONSTANT', 'TEMPLATE'],
|
|
242
|
+
description: 'How the backend treats this URL. Default derived ' +
|
|
243
|
+
'from {{placeholders}} (TEMPLATE) like a normal ' +
|
|
244
|
+
'request entry.',
|
|
245
|
+
},
|
|
205
246
|
body: {
|
|
206
247
|
type: 'string',
|
|
207
248
|
description: 'Becomes `bodySniff: { enabled: true, template: <this ' +
|
|
@@ -315,6 +356,14 @@ export function createVersionFromCaptureTool(client) {
|
|
|
315
356
|
description: 'Free-form JSON passed through to the client. Add-version ' +
|
|
316
357
|
'ONLY.',
|
|
317
358
|
},
|
|
359
|
+
disableRequestReplay: {
|
|
360
|
+
type: 'boolean',
|
|
361
|
+
description: 'Skip replaying the main document request (HAWKEYE ' +
|
|
362
|
+
'provider-level flag). Honored on add-version; for a ' +
|
|
363
|
+
'provider (no `providerId`) the register endpoint ignores ' +
|
|
364
|
+
'it, so when true the tool automatically follows the ' +
|
|
365
|
+
'register up with an add-version call that applies it.',
|
|
366
|
+
},
|
|
318
367
|
},
|
|
319
368
|
required: ['initialUrl'],
|
|
320
369
|
},
|
|
@@ -348,6 +397,7 @@ export function createVersionFromCaptureTool(client) {
|
|
|
348
397
|
stepsToFollow: args.stepsToFollow,
|
|
349
398
|
useIncognitoWebview: args.useIncognitoWebview,
|
|
350
399
|
extensionConfig: args.extensionConfig,
|
|
400
|
+
disableRequestReplay: args.disableRequestReplay,
|
|
351
401
|
allowedInjectedRequestData: args.allowedInjectedRequestData
|
|
352
402
|
? injectedRequestDataFrom(args.allowedInjectedRequestData)
|
|
353
403
|
: undefined
|
|
@@ -372,6 +422,11 @@ export function createVersionFromCaptureTool(client) {
|
|
|
372
422
|
'every page load (before navigating to initialUrl), matching ' +
|
|
373
423
|
'the builder\'s jsUserScripts.');
|
|
374
424
|
}
|
|
425
|
+
// disableRequestReplay is honored ONLY on add-version, so a
|
|
426
|
+
// register can't set it — when true we immediately follow the
|
|
427
|
+
// register with an add-version call (which also lands any other
|
|
428
|
+
// register-ignored fields).
|
|
429
|
+
const followUp = args.disableRequestReplay === true;
|
|
375
430
|
const REGISTER_IGNORED_KEYS = [
|
|
376
431
|
'allowedInjectedRequestData',
|
|
377
432
|
'useIncognitoWebview',
|
|
@@ -382,13 +437,36 @@ export function createVersionFromCaptureTool(client) {
|
|
|
382
437
|
if (ignoredOnRegister.length) {
|
|
383
438
|
notes.push(`${ignoredOnRegister.join(', ')} ` +
|
|
384
439
|
`${ignoredOnRegister.length > 1 ? 'are' : 'is'} ` +
|
|
385
|
-
'ignored on register (no providerId) — the
|
|
386
|
-
'reads them when adding a version.
|
|
387
|
-
|
|
388
|
-
|
|
440
|
+
'ignored on register (no providerId) — the devtools backend only ' +
|
|
441
|
+
'reads them when adding a version. ' +
|
|
442
|
+
(followUp
|
|
443
|
+
? 'They were applied by the follow-up version the tool ' +
|
|
444
|
+
'created to set disableRequestReplay.'
|
|
445
|
+
: 'Publish once to create the provider, then call this ' +
|
|
446
|
+
'again WITH the returned providerId to set them.'));
|
|
389
447
|
}
|
|
390
448
|
const body = providerToRegisterBody(providers, opts);
|
|
391
|
-
|
|
449
|
+
const registered = await client.registerProvider(body);
|
|
450
|
+
if (followUp) {
|
|
451
|
+
const newProviderId = extractProviderId(registered);
|
|
452
|
+
if (newProviderId) {
|
|
453
|
+
const versionsResp = await client.listVersions(newProviderId);
|
|
454
|
+
const followVersion = bumpPatchString(extractVersions(versionsResp));
|
|
455
|
+
const configBody = providerToConfigBody(providers, { ...opts, disableRequestReplay: true }, followVersion, DEFAULT_VERSION_INFO);
|
|
456
|
+
await client.updateProviderConfig(newProviderId, configBody);
|
|
457
|
+
notes.push('disableRequestReplay is ignored by the register endpoint — ' +
|
|
458
|
+
`applied it via a follow-up version (${followVersion}) on ` +
|
|
459
|
+
'the new provider, which also lands any register-ignored ' +
|
|
460
|
+
'fields you passed.');
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
notes.push('Could not apply disableRequestReplay: the register ' +
|
|
464
|
+
'response did not include the new providerId. Call this ' +
|
|
465
|
+
'tool again WITH `providerId` and `disableRequestReplay: ' +
|
|
466
|
+
'true` to set it.');
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return withNotes(registered);
|
|
392
470
|
}
|
|
393
471
|
// New version of an existing provider — pick the next patch above
|
|
394
472
|
// the highest existing version. A provider you don't own (e.g. a
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
* It also covers the HAWKEYE-style live-interception path (`injectionType` /
|
|
15
15
|
* `interceptorType`, `disableRequestReplay` / `isDocumentRequestReplayEnabled`)
|
|
16
16
|
* and `bodySniff`, reverse-engineered from the old-devtools Prisma schema,
|
|
17
|
-
* controller code, and dashboard field hints
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* controller code, and dashboard field hints. The replay flag IS settable via
|
|
18
|
+
* the publish tools (`disableRequestReplay` on old-devtools — add-version or
|
|
19
|
+
* the register follow-up; `interceptorOptions` on the builder), so this is
|
|
20
|
+
* recognition + authoring guidance for the remaining internals.
|
|
20
21
|
*/
|
|
21
22
|
import { readFileSync } from 'node:fs';
|
|
22
23
|
import { fileURLToPath } from 'node:url';
|
package/lib/provider/schema.d.ts
CHANGED
|
@@ -11,6 +11,24 @@ export type HttpMethod = HttpParams['method'];
|
|
|
11
11
|
* jsonPath/xPath belong on the redaction, not here.
|
|
12
12
|
*/
|
|
13
13
|
export type ResponseMatch = HttpParams['responseMatches'][number];
|
|
14
|
+
/**
|
|
15
|
+
* A response match as drafted for PUBLISH: the attestor's match plus
|
|
16
|
+
* `isOptional` — a backend-side matcher flag (both the old devtools backend
|
|
17
|
+
* and the builder store it; the attestor's own params have no such field, so
|
|
18
|
+
* `propose_provider` drafts never set it — only an explicit publish input
|
|
19
|
+
* does). Default false on both backends.
|
|
20
|
+
*/
|
|
21
|
+
export type DraftResponseMatch = ResponseMatch & {
|
|
22
|
+
isOptional?: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** How the verification client supplies cookies/auth when replaying a
|
|
25
|
+
* request. Both backends accept the same enum (old devtools on
|
|
26
|
+
* `requestData`, builder on `RequestSelection`); default `include`. */
|
|
27
|
+
export type WebCredentials = 'omit' | 'same-origin' | 'include';
|
|
28
|
+
/** Old-devtools `urlType` for a requestData entry. The builder backend has
|
|
29
|
+
* no such field (templating is implicit in the URL's `{{param}}`
|
|
30
|
+
* placeholders), so the builder translator ignores it. */
|
|
31
|
+
export type OldUrlType = 'REGEX' | 'CONSTANT' | 'TEMPLATE';
|
|
14
32
|
/**
|
|
15
33
|
* Selector telling the prover which portion of the response to reveal:
|
|
16
34
|
* `xPath`/`jsonPath` narrows the slice, `regex` (with named groups) matches
|
|
@@ -59,8 +77,15 @@ export interface ReclaimProvider {
|
|
|
59
77
|
method: HttpMethod;
|
|
60
78
|
headers?: Record<string, string>;
|
|
61
79
|
body?: string;
|
|
62
|
-
responseMatches:
|
|
80
|
+
responseMatches: DraftResponseMatch[];
|
|
63
81
|
responseRedactions?: ResponseRedaction[];
|
|
82
|
+
/** How the verification client supplies cookies/auth on replay. Both
|
|
83
|
+
* backends default to `include` when unset. */
|
|
84
|
+
credentials?: WebCredentials;
|
|
85
|
+
/** Old-devtools-only url type override (`REGEX`/`CONSTANT`/`TEMPLATE`).
|
|
86
|
+
* Auto-derived from `{{param}}` placeholders when unset; ignored by the
|
|
87
|
+
* builder backend. */
|
|
88
|
+
urlType?: OldUrlType;
|
|
64
89
|
/** Public param refs (templated into URL/body), populated by draft layer. */
|
|
65
90
|
paramValues?: Record<string, string>;
|
|
66
91
|
/** Redaction mode for the transcript (attestor default: 'key-update'). */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReclaimClient } from '@reclaimprotocol/client';
|
|
2
2
|
import type { CreateProviderVersionRequest, ProviderVersionRef } from '@reclaimprotocol/client/openapi';
|
|
3
|
-
import type { HttpMethod,
|
|
3
|
+
import type { DraftResponseMatch, HttpMethod, OldUrlType, ReclaimProvider, ResponseRedaction, WebCredentials } from './schema.ts';
|
|
4
4
|
/** One entry of the version's `requests[]` (a `RequestSelection`), derived
|
|
5
5
|
* from the openapi type so it can't drift from what the server accepts. */
|
|
6
6
|
type RequestSelection = CreateProviderVersionRequest['requests'][number];
|
|
@@ -13,8 +13,15 @@ export interface DraftRequest {
|
|
|
13
13
|
body?: string;
|
|
14
14
|
writeRedactionMode?: ReclaimProvider['writeRedactionMode'];
|
|
15
15
|
additionalClientOptions?: ReclaimProvider['additionalClientOptions'];
|
|
16
|
-
responseMatches?:
|
|
16
|
+
responseMatches?: DraftResponseMatch[];
|
|
17
17
|
responseRedactions?: ResponseRedaction[];
|
|
18
|
+
/** How the verification client supplies cookies/auth on replay. Default
|
|
19
|
+
* `include` (both backends). */
|
|
20
|
+
credentials?: WebCredentials;
|
|
21
|
+
/** Old-devtools `urlType` override. Ignored by THIS translator (the
|
|
22
|
+
* builder backend has no urlType) — read only by the old-devtools
|
|
23
|
+
* translator (`old/to-register.ts`). */
|
|
24
|
+
urlType?: OldUrlType;
|
|
18
25
|
}
|
|
19
26
|
/** A request the page's injection script is allowed to fire, plus the
|
|
20
27
|
* pagination/optionality flags of `RequestSelectionTemplate`. Reuses the
|
|
@@ -47,6 +54,16 @@ export interface CreateVersionOptions {
|
|
|
47
54
|
jsUserScripts?: string;
|
|
48
55
|
/** Requests the injection script may fire, as `RequestSelectionTemplate`s. */
|
|
49
56
|
allowedJsRequests?: AllowedJsRequest[];
|
|
57
|
+
/** In-app interception options, mapped verbatim to
|
|
58
|
+
* `webSettings.clientOptions.inapp.interceptorOptions`. `interceptorType`
|
|
59
|
+
* is required by the backend whenever the object is present.
|
|
60
|
+
* `isDocumentRequestReplayEnabled` is the INVERSE of old-devtools'
|
|
61
|
+
* `disableRequestReplay` (default false). */
|
|
62
|
+
interceptorOptions?: {
|
|
63
|
+
interceptorType: 'HAWKEYE' | 'MSWJS' | 'CDP';
|
|
64
|
+
isDocumentRequestReplayEnabled?: boolean;
|
|
65
|
+
interceptorSettings?: string;
|
|
66
|
+
};
|
|
50
67
|
}
|
|
51
68
|
/**
|
|
52
69
|
* Derive the version's `requiredContext` JSON Schema from the
|
|
@@ -61,13 +61,13 @@ export function providerToRequestSelection(provider) {
|
|
|
61
61
|
const responseMatches = (provider.responseMatches ?? []).map((match) => ({
|
|
62
62
|
type: match.type,
|
|
63
63
|
value: match.value,
|
|
64
|
-
isOptional: false,
|
|
64
|
+
isOptional: match.isOptional ?? false,
|
|
65
65
|
}));
|
|
66
66
|
const responseRedactions = provider.responseRedactions ?? [];
|
|
67
67
|
return {
|
|
68
68
|
url: provider.url,
|
|
69
69
|
method: provider.method,
|
|
70
|
-
credentials: 'include',
|
|
70
|
+
credentials: provider.credentials ?? 'include',
|
|
71
71
|
...(provider.body ? { requestBodyTemplate: provider.body } : {}),
|
|
72
72
|
...(provider.writeRedactionMode
|
|
73
73
|
? { writeRedactionMode: provider.writeRedactionMode }
|
|
@@ -91,7 +91,7 @@ function pickGeoLocation(providers) {
|
|
|
91
91
|
return DYNAMIC_GEO;
|
|
92
92
|
}
|
|
93
93
|
export function providerToCreateVersionRequest(providers, initialUrl, opts = {}) {
|
|
94
|
-
const { notes, version, jsUserScripts, allowedJsRequests } = opts;
|
|
94
|
+
const { notes, version, jsUserScripts, allowedJsRequests, interceptorOptions, } = opts;
|
|
95
95
|
const requiredContext = deriveRequiredContext(providers, {
|
|
96
96
|
allowedJsRequests,
|
|
97
97
|
jsUserScripts,
|
|
@@ -102,6 +102,10 @@ export function providerToCreateVersionRequest(providers, initialUrl, opts = {})
|
|
|
102
102
|
...providerToRequestSelection(req),
|
|
103
103
|
multiple: req.multiple ?? true,
|
|
104
104
|
required: req.required ?? true,
|
|
105
|
+
templateParamsMode: req.templateParamsMode ?? 'separate',
|
|
106
|
+
...(req.templateParams?.length
|
|
107
|
+
? { templateParams: req.templateParams }
|
|
108
|
+
: {}),
|
|
105
109
|
}));
|
|
106
110
|
const script = jsUserScripts?.trim();
|
|
107
111
|
return {
|
|
@@ -114,7 +118,21 @@ export function providerToCreateVersionRequest(providers, initialUrl, opts = {})
|
|
|
114
118
|
// apply schema defaults to request bodies.
|
|
115
119
|
webSettings: {
|
|
116
120
|
geoLocation: pickGeoLocation(providers),
|
|
117
|
-
clientOptions: {
|
|
121
|
+
clientOptions: {
|
|
122
|
+
portal: { useProxy: true },
|
|
123
|
+
...(interceptorOptions
|
|
124
|
+
? {
|
|
125
|
+
inapp: {
|
|
126
|
+
interceptorOptions: {
|
|
127
|
+
// openapi `default: false` — the generated type
|
|
128
|
+
// requires the key even when the author omits it.
|
|
129
|
+
isDocumentRequestReplayEnabled: false,
|
|
130
|
+
...interceptorOptions,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
: {}),
|
|
135
|
+
},
|
|
118
136
|
// Only set when non-empty; an empty string would store a no-op script.
|
|
119
137
|
...(script ? { jsUserScripts: script } : {}),
|
|
120
138
|
},
|
package/lib/tools/registry.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The ONE tool primitive for the agent. A tool is authored once with
|
|
3
|
-
* {@link defineTool} — `{ definition, handler }`, where the handler receives
|
|
4
|
-
* args already parsed + validated against the definition's JSON Schema (via
|
|
5
|
-
* zod), so the schema shown to the model and the validation applied to inbound
|
|
6
|
-
* args cannot drift.
|
|
7
|
-
*
|
|
8
|
-
* This lives outside `mcp/` on purpose: it depends only on zod (NOT the MCP
|
|
9
|
-
* SDK), so non-MCP consumers — the cloud authoring loop — can author and run
|
|
10
|
-
* these tools without pulling in `@modelcontextprotocol/sdk`. The MCP-specific
|
|
11
|
-
* glue (`registerToolList` onto an `McpServer`) stays in `mcp/server.ts`, which
|
|
12
|
-
* re-exports this primitive for the MCP-side tool files.
|
|
13
|
-
*/
|
|
14
1
|
import type { JSONSchema } from 'zod/v4/core';
|
|
15
2
|
export interface ToolDefinition {
|
|
16
3
|
name: string;
|
|
@@ -34,6 +21,12 @@ export interface RegisteredTool {
|
|
|
34
21
|
* and the validation applied to inbound args cannot drift.
|
|
35
22
|
*/
|
|
36
23
|
export declare function defineTool<TArgs>(definition: ToolDefinition, handler: (args: TArgs) => Promise<unknown>): RegisteredTool;
|
|
24
|
+
/**
|
|
25
|
+
* Render a tool-handler error as the text every host shows the LLM.
|
|
26
|
+
* Boom errors carrying an RFC 9457 problem include its `detail`; anything
|
|
27
|
+
* else falls back to the plain message.
|
|
28
|
+
*/
|
|
29
|
+
export declare function errorAsToolText(err: unknown): string;
|
|
37
30
|
/**
|
|
38
31
|
* Adapt a tool to a host by WIDENING its input schema — add properties, mark
|
|
39
32
|
* some required, optionally override the description — without touching the
|
package/lib/tools/registry.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE tool primitive for the agent. A tool is authored once with
|
|
3
|
+
* {@link defineTool} — `{ definition, handler }`, where the handler receives
|
|
4
|
+
* args already parsed + validated against the definition's JSON Schema (via
|
|
5
|
+
* zod), so the schema shown to the model and the validation applied to inbound
|
|
6
|
+
* args cannot drift.
|
|
7
|
+
*
|
|
8
|
+
* This lives outside `mcp/` on purpose: it depends only on zod (NOT the MCP
|
|
9
|
+
* SDK), so non-MCP consumers — the cloud authoring loop — can author and run
|
|
10
|
+
* these tools without pulling in `@modelcontextprotocol/sdk`. The MCP-specific
|
|
11
|
+
* glue (`registerToolList` onto an `McpServer`) stays in `mcp/server.ts`, which
|
|
12
|
+
* re-exports this primitive for the MCP-side tool files.
|
|
13
|
+
*/
|
|
14
|
+
import { isBoom } from '@hapi/boom';
|
|
1
15
|
import { toZod } from "../mcp/api/to-zod.js";
|
|
2
16
|
/**
|
|
3
17
|
* Define a tool with a single inputSchema (JSON Schema) as the source of truth.
|
|
@@ -15,6 +29,18 @@ export function defineTool(definition, handler) {
|
|
|
15
29
|
},
|
|
16
30
|
};
|
|
17
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Render a tool-handler error as the text every host shows the LLM.
|
|
34
|
+
* Boom errors carrying an RFC 9457 problem include its `detail`; anything
|
|
35
|
+
* else falls back to the plain message.
|
|
36
|
+
*/
|
|
37
|
+
export function errorAsToolText(err) {
|
|
38
|
+
if (isBoom(err) && err.output?.statusCode) {
|
|
39
|
+
const detail = err.data?.detail;
|
|
40
|
+
return detail ? `${err.message}: ${detail}` : err.message;
|
|
41
|
+
}
|
|
42
|
+
return err instanceof Error ? err.message : String(err);
|
|
43
|
+
}
|
|
18
44
|
/**
|
|
19
45
|
* Adapt a tool to a host by WIDENING its input schema — add properties, mark
|
|
20
46
|
* some required, optionally override the description — without touching the
|
|
@@ -30,6 +56,9 @@ export function extendTool(tool, extra) {
|
|
|
30
56
|
...(base.inputSchema.required ?? []),
|
|
31
57
|
...(extra.required ?? []),
|
|
32
58
|
];
|
|
59
|
+
// The merged `properties` spread carries the base's untyped property values,
|
|
60
|
+
// which don't structurally match ObjectSchema's strict per-property schema
|
|
61
|
+
// type — assert the merged shape.
|
|
33
62
|
const inputSchema = {
|
|
34
63
|
...base.inputSchema,
|
|
35
64
|
properties: {
|
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.18",
|
|
5
5
|
"description": "Reclaim Protocol's tools for agents — Reclaim Protocol lets your app verify **data points** about its users — a follower count, an account balance, an order history. This package gives your AI coding agent the tools to **find, build, test and fix the _data verification providers_** (the recipes that verify a data point) by inspecting and replaying captured requests, checking sessions, evaluate proofs, publishing updates, and much more.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./lib/index.js",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"bin": {
|
|
36
|
-
"reclaim": "./lib/cli.js",
|
|
37
36
|
"reclaim-mcp-server": "./lib/mcp-bin.js"
|
|
38
37
|
},
|
|
39
38
|
"files": [
|
|
@@ -45,7 +44,7 @@
|
|
|
45
44
|
"README.md"
|
|
46
45
|
],
|
|
47
46
|
"engines": {
|
|
48
|
-
"node": ">=
|
|
47
|
+
"node": ">=22.0.0"
|
|
49
48
|
},
|
|
50
49
|
"repository": {
|
|
51
50
|
"url": "https://github.com/reclaimprotocol/builder",
|
|
@@ -53,12 +52,11 @@
|
|
|
53
52
|
},
|
|
54
53
|
"websiteUrl": "https://docs.reclaimprotocol.org",
|
|
55
54
|
"scripts": {
|
|
56
|
-
"build": "tsc && node scripts/prepend-shebang.js && chmod 755 lib/
|
|
55
|
+
"build": "tsc && node scripts/prepend-shebang.js && chmod 755 lib/mcp-bin.js",
|
|
57
56
|
"prepublishOnly": "npm run build",
|
|
58
57
|
"dev": "tsc --watch",
|
|
59
|
-
"test": "node --env-file=.test.env --test tests/*.test.ts",
|
|
58
|
+
"test": "node --env-file=.test.env --test-timeout=120000 --test tests/*.test.ts",
|
|
60
59
|
"check": "tsc -p tsconfig.test.json",
|
|
61
|
-
"start:cli": "node lib/cli.js",
|
|
62
60
|
"start:mcp": "node lib/mcp-bin.js",
|
|
63
61
|
"inspect": "npx @modelcontextprotocol/inspector node lib/mcp-bin.js",
|
|
64
62
|
"download:zk-files": "node scripts/download-zk-files.js"
|
|
@@ -67,7 +65,6 @@
|
|
|
67
65
|
"mcp",
|
|
68
66
|
"model-context-protocol",
|
|
69
67
|
"sdk",
|
|
70
|
-
"cli",
|
|
71
68
|
"ai",
|
|
72
69
|
"agent",
|
|
73
70
|
"chrome-devtools-protocol",
|
|
@@ -86,7 +83,7 @@
|
|
|
86
83
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
87
84
|
"@noble/hashes": "^2.2.0",
|
|
88
85
|
"@reclaimprotocol/attestor-core": "^5.0.8",
|
|
89
|
-
"@reclaimprotocol/client": "0.1.
|
|
86
|
+
"@reclaimprotocol/client": "0.1.18",
|
|
90
87
|
"@reclaimprotocol/tls": "^0.1.3",
|
|
91
88
|
"chrome-remote-interface": "^0.33.0",
|
|
92
89
|
"domhandler": "^6.0.0",
|
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
4
4
|
|
|
5
5
|
const SHEBANG = '#!/usr/bin/env node\n'
|
|
6
6
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
-
const BIN_FILES = ['
|
|
7
|
+
const BIN_FILES = ['mcp-bin.js']
|
|
8
8
|
|
|
9
9
|
for(const file of BIN_FILES) {
|
|
10
10
|
const path = join(ROOT, 'lib', file)
|
package/skill/builder-mode.md
CHANGED
|
@@ -13,3 +13,5 @@ This server targets the Reclaim BUILDER backend. It exposes three tool layers: g
|
|
|
13
13
|
**Discover & resolve (find providers for the user's app):** when a dev wants to add verification but doesn't know which providers to use, run the same flow the Discover UI does. Ask which website their users prove data from, then `discover_data_points({ domain })` to list what's verifiable there. Confirm the data points with the dev, then `discover_resolve({ domain, dataPoints })` → the smallest provider set (`providers`, greedy combine), the `requiredContext` inputs to collect from the dev, and any `uncovered` points (author those with the provider-authoring flow above, then re-resolve). Create the session with `create_verification_session({ providers: [{ providerId }, …], context, verificationClientUrl })`, passing EVERY resolved provider id and the collected context. `merge.mergeable` only signals the set could collapse into one provider — you still pass them all; the Builder runs them in one session.
|
|
14
14
|
|
|
15
15
|
**Verification sessions:** consumers authenticate with a per-org token (`rorg_…`, issued in the dashboard via `POST /orgs/{orgId}/token`) and create sessions via `POST /verifications/sessions`. Results are ECIES-encrypted (secp256k1 + AES-256-GCM) to the org's eth public key when its `canEncryptResult` is enabled — set via `PUT /orgs/{orgId}/keypair` with an eth public key — and delivered to the org's callback subscriptions (plaintext otherwise). Use the org/session tools (`get_verification_session`, `list_verification_events`, `list_org_verification_sessions`) to troubleshoot.
|
|
16
|
+
|
|
17
|
+
**Backend mode:** this server can alternatively run against the devtools backend with a reduced tool set (no generated API tools, no orgs/review flows). `get_devtools_mode` reports the active mode and why it's in effect; `set_devtools_mode` persists a switch to `~/.reclaim/config.json` — it takes effect on the NEXT server start (restart/reconnect), and an explicit `USE_OLD_DEVTOOLS` env var overrides the persisted choice.
|
package/skill/old-mode.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
This server is running against the
|
|
1
|
+
This server is running against the devtools backend, so its capabilities are intentionally limited.
|
|
2
2
|
|
|
3
3
|
**Provider-authoring flow:**
|
|
4
4
|
|
|
@@ -15,3 +15,5 @@ Only GET/POST endpoints can be captured into providers here. There is no organiz
|
|
|
15
15
|
**Diagnosing a session:** `session_analytics_logs` fetches the analytics logs for a verification session by id (from the logs service) — use it to see what happened in a session (matches, claims, failures).
|
|
16
16
|
|
|
17
17
|
**Templating:** bare `{{name}}` placeholders are provider params (values extracted from the response). Values the CONSUMER must supply at verification time (e.g. a username in the URL path) use the `{{context_<name>}}` form — this backend does not support the dotted `{{context.<name>}}` interpolation the new builder uses.
|
|
18
|
+
|
|
19
|
+
**Backend mode:** this is the reduced devtools surface — the BUILDER backend (generated API tools, orgs, verification sessions, public review) is one switch away. `get_devtools_mode` reports the active mode and why it's in effect; `set_devtools_mode` persists a switch to `~/.reclaim/config.json` — it takes effect on the NEXT server start (restart/reconnect), and an explicit `USE_OLD_DEVTOOLS` env var overrides the persisted choice.
|
package/src/authoring/index.ts
CHANGED
package/src/authoring/util.ts
CHANGED
|
@@ -21,6 +21,9 @@ export function objectSchema(
|
|
|
21
21
|
properties: Record<string, unknown>,
|
|
22
22
|
required?: string[],
|
|
23
23
|
): JSONSchema.ObjectSchema {
|
|
24
|
+
// `properties` is an untyped Record here (this is a literal shortener, not a
|
|
25
|
+
// schema validator), so it doesn't structurally match ObjectSchema's strict
|
|
26
|
+
// per-property schema type — assert the shape.
|
|
24
27
|
return {
|
|
25
28
|
type: 'object',
|
|
26
29
|
properties,
|
package/src/cdp/launcher.ts
CHANGED
|
@@ -113,7 +113,7 @@ export function launchChrome(opts: LaunchOptions): ChildProcess {
|
|
|
113
113
|
export async function waitForCdpReady(
|
|
114
114
|
port: number,
|
|
115
115
|
timeoutMs = 10_000,
|
|
116
|
-
)
|
|
116
|
+
) {
|
|
117
117
|
const deadline = Date.now() + timeoutMs
|
|
118
118
|
while(Date.now() < deadline) {
|
|
119
119
|
try {
|