@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.
Files changed (64) hide show
  1. package/lib/authoring/index.d.ts +1 -1
  2. package/lib/authoring/index.js +1 -1
  3. package/lib/authoring/util.js +3 -0
  4. package/lib/config/store.d.ts +7 -0
  5. package/lib/consts.d.ts +15 -0
  6. package/lib/consts.js +35 -0
  7. package/lib/index.js +1 -1
  8. package/lib/mcp/api/operations.gen.d.ts +8 -16
  9. package/lib/mcp/api/operations.gen.js +428 -479
  10. package/lib/mcp/remap_errors.js +7 -14
  11. package/lib/mcp/start.js +10 -3
  12. package/lib/mcp/tools/agent/proof.js +3 -3
  13. package/lib/mcp/tools/agent/publish.js +33 -0
  14. package/lib/mcp/tools/authenticate/authenticate.js +178 -93
  15. package/lib/mcp/tools/authenticate/eth-key.js +2 -3
  16. package/lib/mcp/tools/index.d.ts +1 -0
  17. package/lib/mcp/tools/index.js +1 -0
  18. package/lib/mcp/tools/mode/index.d.ts +1 -0
  19. package/lib/mcp/tools/mode/index.js +3 -0
  20. package/lib/mcp/tools/mode/mode.d.ts +17 -0
  21. package/lib/mcp/tools/mode/mode.js +69 -0
  22. package/lib/old/to-register.d.ts +16 -8
  23. package/lib/old/to-register.js +29 -21
  24. package/lib/old/tools/publish.js +86 -8
  25. package/lib/provider/injection-doc.js +4 -3
  26. package/lib/provider/schema.d.ts +26 -1
  27. package/lib/provider/to-version.d.ts +19 -2
  28. package/lib/provider/to-version.js +22 -4
  29. package/lib/tools/registry.d.ts +6 -13
  30. package/lib/tools/registry.js +29 -0
  31. package/package.json +5 -8
  32. package/scripts/prepend-shebang.js +1 -1
  33. package/skill/builder-mode.md +2 -0
  34. package/skill/old-mode.md +3 -1
  35. package/src/authoring/index.ts +1 -0
  36. package/src/authoring/util.ts +3 -0
  37. package/src/cdp/launcher.ts +1 -1
  38. package/src/config/store.ts +8 -0
  39. package/src/consts.ts +44 -0
  40. package/src/index.ts +1 -1
  41. package/src/mcp/api/operations.gen.ts +430 -487
  42. package/src/mcp/remap_errors.ts +8 -15
  43. package/src/mcp/start.ts +11 -2
  44. package/src/mcp/tools/agent/proof.ts +3 -3
  45. package/src/mcp/tools/agent/publish.ts +42 -0
  46. package/src/mcp/tools/authenticate/authenticate.ts +238 -108
  47. package/src/mcp/tools/authenticate/eth-key.ts +7 -5
  48. package/src/mcp/tools/index.ts +1 -0
  49. package/src/mcp/tools/mode/index.ts +3 -0
  50. package/src/mcp/tools/mode/mode.ts +82 -0
  51. package/src/old/to-register.ts +50 -27
  52. package/src/old/tools/publish.ts +108 -8
  53. package/src/proof/debug-response-dump.ts +1 -1
  54. package/src/proof/proof-core.ts +1 -1
  55. package/src/proof/zk-files.ts +2 -2
  56. package/src/provider/dom-xpath.ts +1 -1
  57. package/src/provider/draft.ts +3 -3
  58. package/src/provider/injection-doc.ts +4 -3
  59. package/src/provider/schema.ts +27 -1
  60. package/src/provider/to-version.ts +45 -6
  61. package/src/tools/registry.ts +19 -0
  62. package/src/cli/args.ts +0 -20
  63. package/src/cli/commands.ts +0 -27
  64. package/src/cli.ts +0 -7
@@ -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 old
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
- if (method !== 'GET' && method !== 'POST') {
14
- throw new Error('Old devtools supports only GET/POST requests; got ' +
15
- `"${provider.method}". Re-capture against a GET/POST endpoint.`);
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
- if (dotted) {
24
- throw new Error(`Old devtools does not support ${dotted[0]} — rename ` +
25
- 'consumer-supplied params to the {{context_<name>}} form ' +
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: false,
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
- // Old devtools wants `responseMatches` and `responseRedactions` as PARALLEL
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 so the backend templates them;
62
- // otherwise a fixed CONSTANT url. (REGEX is never drafted.)
63
- urlType: provider.url.includes('{{') ? 'TEMPLATE' : 'CONSTANT',
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 old backend
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 old backend runs together at verification
81
- * (the same way the builder version carries multiple `requests`). */
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 old backend's shape. A
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
  /**
@@ -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 OLD devtools backend. ' +
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 (old-devtools\' name for the builder\'s ' +
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. \n\n' +
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 old backend only ' +
386
- 'reads them when adding a version. Publish once to create ' +
387
- 'the provider, then call this again WITH the returned ' +
388
- 'providerId to set them.');
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
- return withNotes(await client.registerProvider(body));
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 — neither publish tool exposes
18
- * these as inputs (both only build the standard replay flow), so this is
19
- * read-the-field-and-recognize-it documentation, not authoring guidance.
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';
@@ -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: ResponseMatch[];
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, ReclaimProvider, ResponseMatch, ResponseRedaction } from './schema.ts';
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?: ResponseMatch[];
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: { portal: { useProxy: true } },
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
  },
@@ -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
@@ -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.15",
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": ">=20"
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/cli.js lib/mcp-bin.js",
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.15",
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 = ['cli.js', 'mcp-bin.js']
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)
@@ -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 LEGACY Reclaim devtools backend (temporary, while the new builder backend is in development), so its capabilities are intentionally limited.
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.
@@ -14,6 +14,7 @@ export { authoringTools } from './tools.ts'
14
14
  // subsystem.
15
15
  export {
16
16
  defineTool,
17
+ errorAsToolText,
17
18
  extendTool,
18
19
  type RegisteredTool,
19
20
  type ToolDefinition,
@@ -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,
@@ -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
- ): Promise<void> {
116
+ ) {
117
117
  const deadline = Date.now() + timeoutMs
118
118
  while(Date.now() < deadline) {
119
119
  try {