@reclaimprotocol/agent 0.1.14 → 0.1.17
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 +8 -0
- package/lib/consts.js +20 -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/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 +16 -0
- package/lib/mcp/tools/mode/mode.js +49 -0
- package/lib/old/tools/publish.js +75 -4
- package/lib/provider/to-version.js +4 -0
- 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/injection-api.md +98 -7
- 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 +24 -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/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 +58 -0
- package/src/old/tools/publish.ts +78 -4
- 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/to-version.ts +4 -0
- 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/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.17",
|
|
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.17",
|
|
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/injection-api.md
CHANGED
|
@@ -133,13 +133,104 @@ field isn't touched by `create_provider_version_from_capture` at all).
|
|
|
133
133
|
## `bodySniff` — unrelated to the above
|
|
134
134
|
|
|
135
135
|
A per-request field (sits next to `url`/`method` on each `requestData` entry,
|
|
136
|
-
not a version-level setting): `{ enabled: boolean, template: string }`.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
not a version-level setting): `{ enabled: boolean, template: string }`.
|
|
137
|
+
|
|
138
|
+
**`bodySniff.enabled` should be `true` whenever the request HAS a body at
|
|
139
|
+
all** — this isn't a special case reserved for dynamic bodies. Only skip it
|
|
140
|
+
(leave `enabled: false` / template empty) when the request genuinely sends
|
|
141
|
+
no body (typically a GET). Both publish tools in this package already set
|
|
142
|
+
`enabled`/`template` for you from `provider.body` for a normal
|
|
143
|
+
capture-and-replay provider — you don't need to touch it directly there.
|
|
144
|
+
Read the rest of this section if you're authoring a claim via live
|
|
145
|
+
interception (`window.Reclaim.requestClaim`) whose body isn't simply the
|
|
146
|
+
one literal string you captured once — most commonly because some SMALL
|
|
147
|
+
part of it is dynamic per user/run (e.g. a user id, not necessarily the
|
|
148
|
+
whole payload).
|
|
149
|
+
|
|
150
|
+
**What actually gets hashed.** Confirmed directly from attestor-core
|
|
151
|
+
(`hashProviderParams` in `@reclaimprotocol/attestor-core`): the claim/spec
|
|
152
|
+
hash is `keccak256(canonicalStringify({ url, method, body, responseMatches,
|
|
153
|
+
responseRedactions }))`, where `body` is whatever plain string sits in
|
|
154
|
+
`params.body` — attestor-core does **no** template resolution, substitution,
|
|
155
|
+
or `{{}}`-stripping on it; it hashes exactly the string it's given, verbatim.
|
|
156
|
+
`bodySniff` as a concept (the `{enabled, template}` wrapper) doesn't exist
|
|
157
|
+
inside attestor-core at all — it's translated to a plain `body: string`
|
|
158
|
+
one layer up (old-devtools' `providerUtils.ts`: `body: bodySniff.enabled ?
|
|
159
|
+
bodySniff.template : ''`), and THAT literal string — including any
|
|
160
|
+
unresolved `{{paramName}}` text still in it — is what reaches
|
|
161
|
+
`hashProviderParams`.
|
|
162
|
+
|
|
163
|
+
**The consequence — and why this differs from `templateParams`.** A live
|
|
164
|
+
claim's own `body` field (submitted via `requestClaim`) can ALSO be set to
|
|
165
|
+
a literal template string containing `{{paramName}}`, rather than the real
|
|
166
|
+
substituted content, with the real value supplied separately as a witness
|
|
167
|
+
param. Since attestor-core hashes `body` as an opaque string with no
|
|
168
|
+
resolution, a claim whose `body` is the literal template `T` and a spec
|
|
169
|
+
whose `body`/`bodySniff.template` is that SAME literal template `T` hash
|
|
170
|
+
identically — regardless of what real value the `{{paramName}}` hole stood
|
|
171
|
+
for. This is the OPPOSITE of `templateParams`/`${var}` (used in
|
|
172
|
+
`responseMatches`/`responseRedactions`), which — per the JS SDK's
|
|
173
|
+
`generateSpecsFromRequestSpecTemplate` — genuinely gets substituted with a
|
|
174
|
+
real value at verify time before hashing. Don't conflate the two: `${var}`
|
|
175
|
+
in a match/redaction template IS resolved before hashing; `{{var}}` in a
|
|
176
|
+
body template is NEVER resolved before hashing, on either side.
|
|
177
|
+
|
|
178
|
+
**Practical use**: check exactly which part of the body actually changes
|
|
179
|
+
across users/runs — usually it's one small field (an id, a token, a short
|
|
180
|
+
list), not the whole payload. Template ONLY that minimal changing part;
|
|
181
|
+
leave everything else as fixed literal text. For a body like
|
|
182
|
+
`{"request":1,"type":"user_info","id":"12345"}` where only `id` varies, the
|
|
183
|
+
template is `{"request":1,"type":"user_info","id":"{{REQ_BODY_USER_ID}}"}`
|
|
184
|
+
— NOT e.g. `{{whole_body}}` wrapping the entire object, which would throw
|
|
185
|
+
away the ability to structurally pin down everything else about the
|
|
186
|
+
request. Then:
|
|
187
|
+
1. In the claim you build for `requestClaim`, set `bodySniff: { enabled:
|
|
188
|
+
true, template: '{"request":1,"type":"user_info","id":"{{REQ_BODY_USER_ID}}"}' }`
|
|
189
|
+
and add a matching witness/extracted param
|
|
190
|
+
`REQ_BODY_USER_ID: theRealUserId` so the real value is still
|
|
191
|
+
independently verifiable, just not part of the hash.
|
|
192
|
+
2. In the corresponding `allowedInjectedRequestData` entry, set `body` to
|
|
193
|
+
that EXACT SAME literal template string (byte-for-byte, same
|
|
194
|
+
`{{REQ_BODY_USER_ID}}` placeholder, same everything else) — NOT a
|
|
195
|
+
`${var}` substitution, since `body` doesn't go through that substitution
|
|
196
|
+
step.
|
|
197
|
+
3. Before publishing, sanity-check by hand that
|
|
198
|
+
`template.replace('{{REQ_BODY_USER_ID}}', 'a real sample id')`
|
|
199
|
+
reproduces the real captured/expected request body byte-for-byte — if it
|
|
200
|
+
doesn't (extra whitespace, different key order, a field that turned out
|
|
201
|
+
to ALSO vary but wasn't templated, etc.), the hashes won't match either,
|
|
202
|
+
since the SPEC side uses the literal template as-is and the real claim's
|
|
203
|
+
body must independently be built to match that same shape when the
|
|
204
|
+
placeholder(s) are filled in. If MULTIPLE fields vary independently,
|
|
205
|
+
template each one with its own `{{placeholder}}` and its own witness
|
|
206
|
+
param — you're not limited to one hole per template.
|
|
207
|
+
|
|
208
|
+
**Fallback — whole-body placeholder.** If precise per-field templating
|
|
209
|
+
keeps causing hash mismatches (e.g. more fields turn out to vary than
|
|
210
|
+
expected, key order isn't stable, whitespace differs run-to-run) and you
|
|
211
|
+
can't pin down every variable field individually, fall back to templating
|
|
212
|
+
the ENTIRE body as one placeholder instead: `{{REQ_BODY_GRD}}`. The `GRD`
|
|
213
|
+
suffix matters — per the response-matching template engine
|
|
214
|
+
(`convertTemplateToRegex`), a placeholder name ending in `GRD` is treated as
|
|
215
|
+
a GREEDY match (`(.*)`, matching everything including embedded delimiters)
|
|
216
|
+
rather than the default non-greedy `(.*?)` (which stops at the first
|
|
217
|
+
occurrence of whatever comes after it in the template) — for a placeholder
|
|
218
|
+
covering the entire remaining body with nothing fixed after it, greedy is
|
|
219
|
+
what you want. This is strictly a fallback: it means NOTHING about the
|
|
220
|
+
body's structure is pinned down by the hash anymore (any body at all would
|
|
221
|
+
hash the same), so prefer the minimal-placeholder approach above whenever
|
|
222
|
+
you can actually identify which fields vary.
|
|
223
|
+
|
|
224
|
+
Caveat: I confirmed `hashProviderParams` treats `body` as an opaque string
|
|
225
|
+
with no `{{}}` resolution directly from the attestor-core bundle shipped in
|
|
226
|
+
this repo's `node_modules`. I did NOT find, in this same bundle, the code
|
|
227
|
+
path that actually invokes `hashProviderParams` against a live submitted
|
|
228
|
+
claim to gate proof acceptance — that caller lives elsewhere (the mobile/
|
|
229
|
+
InApp SDK client and/or a server-side verifier not checked into this repo).
|
|
230
|
+
So this is strong but not 100% end-to-end-traced confirmation; treat it as
|
|
231
|
+
"very likely correct, empirically consistent with real proof/verifyProof
|
|
232
|
+
behavior observed in practice" rather than "certain from first-hand
|
|
233
|
+
tracing of the whole call chain."
|
|
143
234
|
|
|
144
235
|
## Recognizing this on a provider you didn't author
|
|
145
236
|
|
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 {
|
package/src/config/store.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface ReclaimAgentConfig {
|
|
|
18
18
|
/**
|
|
19
19
|
* The single on-disk document at `~/.reclaim/config.json`. Each top-level
|
|
20
20
|
* key is an independent section owned by a different subsystem:
|
|
21
|
+
* - `mode` — persisted backend preference (old-devtools vs builder),
|
|
22
|
+
* set via the `set_devtools_mode` tool. Applied at server
|
|
23
|
+
* start; an explicit `USE_OLD_DEVTOOLS` env var overrides
|
|
24
|
+
* it (see `resolveOldMode` in consts.ts).
|
|
21
25
|
* - `browser_agent` — Chrome attach mode/port (the agent tools)
|
|
22
26
|
* - `session` — the auth bearer token (authenticate)
|
|
23
27
|
* - `old_devtools` — externally-supplied identity for OLD-devtools mode
|
|
@@ -32,11 +36,15 @@ export interface ReclaimAgentConfig {
|
|
|
32
36
|
*/
|
|
33
37
|
export interface ReclaimConfig {
|
|
34
38
|
version: 1
|
|
39
|
+
mode?: DevtoolsMode
|
|
35
40
|
browser_agent?: ReclaimAgentConfig
|
|
36
41
|
session?: CachedSession
|
|
37
42
|
old_devtools?: OldIdentity
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/** Which backend the MCP server targets. See `resolveOldMode` in consts.ts. */
|
|
46
|
+
export type DevtoolsMode = 'old' | 'builder'
|
|
47
|
+
|
|
40
48
|
/**
|
|
41
49
|
* Read/write the unified Reclaim config document. The file holds the
|
|
42
50
|
* (short-lived) session bearer token, so it is always locked to `0600` and
|
package/src/consts.ts
CHANGED
|
@@ -72,6 +72,30 @@ export function useOldDevtools(): boolean {
|
|
|
72
72
|
return (process.env.USE_OLD_DEVTOOLS ?? '').trim().toLowerCase() !== 'false'
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/** True when `USE_OLD_DEVTOOLS` is set to a non-empty value (explicit). */
|
|
76
|
+
function oldDevtoolsEnvSet(): boolean {
|
|
77
|
+
return (process.env.USE_OLD_DEVTOOLS ?? '').trim() !== ''
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Effective mode, layering the persisted preference under the env var:
|
|
82
|
+
* explicit `USE_OLD_DEVTOOLS` wins → persisted config `mode` → default (old).
|
|
83
|
+
* The persisted value comes from `~/.reclaim/config.json` (set via the
|
|
84
|
+
* `set_devtools_mode` tool); the caller reads it so this module needn't import
|
|
85
|
+
* `ConfigStore` (which would create an import cycle).
|
|
86
|
+
*/
|
|
87
|
+
export function resolveOldMode(persisted?: 'old' | 'builder'): boolean {
|
|
88
|
+
if(oldDevtoolsEnvSet()) {
|
|
89
|
+
return useOldDevtools()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if(persisted) {
|
|
93
|
+
return persisted === 'old'
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
// ---------------------------------------------------------------------------
|
|
76
100
|
// Chrome / CDP launcher.
|
|
77
101
|
// ---------------------------------------------------------------------------
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Package entry — SDK re-exports + agent public API.
|
|
2
|
-
// The
|
|
2
|
+
// The MCP server bin lives at ./mcp-bin.ts.
|
|
3
3
|
|
|
4
4
|
// SDK — re-export everything from @reclaimprotocol/client so consumers
|
|
5
5
|
// only need one package for both the typed HTTP client and the agent tools.
|