@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/src/old/tools/publish.ts
CHANGED
|
@@ -240,15 +240,89 @@ export function createVersionFromCaptureTool(
|
|
|
240
240
|
'Extra requests the customInjection script is allowed to ' +
|
|
241
241
|
'fire (e.g. pagination) — add-version ONLY, the register ' +
|
|
242
242
|
'endpoint ignores this. Each item has the same shape as ' +
|
|
243
|
-
'`provider`, plus `multiple`/`required` (default true).'
|
|
243
|
+
'`provider`, plus `multiple`/`required` (default true). ' +
|
|
244
|
+
'IMPORTANT whenever a claim\'s request HAS a body: set `body` ' +
|
|
245
|
+
'here (see its own description below) — if only a small part ' +
|
|
246
|
+
'of the real body varies per user/run (e.g. one id field, not ' +
|
|
247
|
+
'the whole payload), template just that part with ' +
|
|
248
|
+
'`{{paramName}}` and keep the rest as fixed literal text; see ' +
|
|
249
|
+
'the "bodySniff" section of the customInjection field\'s guide ' +
|
|
250
|
+
'for the exact mechanics and a worked example.',
|
|
244
251
|
items: {
|
|
245
252
|
type: 'object',
|
|
246
253
|
properties: {
|
|
247
254
|
url: { type: 'string' },
|
|
248
255
|
method: { type: 'string' },
|
|
249
|
-
body: {
|
|
250
|
-
|
|
251
|
-
|
|
256
|
+
body: {
|
|
257
|
+
type: 'string',
|
|
258
|
+
description:
|
|
259
|
+
'Becomes `bodySniff: { enabled: true, template: <this ' +
|
|
260
|
+
'string> }` — set this whenever the request has a ' +
|
|
261
|
+
'body at all (skip only for a genuinely bodyless ' +
|
|
262
|
+
'request, typically GET). If the real body is FIXED ' +
|
|
263
|
+
'(identical for every user/run), put that literal body ' +
|
|
264
|
+
'text here verbatim. If only PART of it varies (e.g. ' +
|
|
265
|
+
'`{"request":1,"type":"user_info","id":"12345"}` where ' +
|
|
266
|
+
'only `id` differs per user), template ONLY that part: ' +
|
|
267
|
+
'`{"request":1,"type":"user_info",' +
|
|
268
|
+
'"id":"{{REQ_BODY_USER_ID}}"}` — keep everything else ' +
|
|
269
|
+
'as fixed literal text rather than templating the whole ' +
|
|
270
|
+
'body. Confirmed directly against attestor-core\'s ' +
|
|
271
|
+
'`hashProviderParams`: the hash embeds this string ' +
|
|
272
|
+
'LITERALLY, unresolved braces and all, never the real ' +
|
|
273
|
+
'substituted body, so a claim built with the SAME ' +
|
|
274
|
+
'literal template (plus a witness/extracted param per ' +
|
|
275
|
+
'placeholder, supplying the real value for ' +
|
|
276
|
+
'verification) hashes identically regardless of what ' +
|
|
277
|
+
'those real values are. This is the OPPOSITE of ' +
|
|
278
|
+
'`templateParams`\' `${var}` syntax below, which ' +
|
|
279
|
+
'genuinely IS substituted before hashing — do not ' +
|
|
280
|
+
'confuse `{{param}}` (body, never resolved) with ' +
|
|
281
|
+
'`${var}` (responseMatches/responseRedactions, resolved ' +
|
|
282
|
+
'at verify time). Before publishing, hand-verify that ' +
|
|
283
|
+
'substituting real sample values into this template ' +
|
|
284
|
+
'reproduces the real captured request body ' +
|
|
285
|
+
'byte-for-byte. Fallback if per-field templating keeps ' +
|
|
286
|
+
'mismatching (more fields vary than expected, unstable ' +
|
|
287
|
+
'key order, etc.) and you can\'t pin down every ' +
|
|
288
|
+
'variable field: template the WHOLE body as one ' +
|
|
289
|
+
'placeholder instead, `{{REQ_BODY_GRD}}` — the `GRD` ' +
|
|
290
|
+
'suffix makes it a greedy match (captures everything) ' +
|
|
291
|
+
'rather than the default non-greedy match, which is ' +
|
|
292
|
+
'needed when nothing fixed follows the placeholder. ' +
|
|
293
|
+
'This pins down nothing about body structure, so prefer ' +
|
|
294
|
+
'minimal per-field placeholders whenever you can ' +
|
|
295
|
+
'actually identify which fields vary.',
|
|
296
|
+
},
|
|
297
|
+
responseMatches: {
|
|
298
|
+
type: 'array',
|
|
299
|
+
description:
|
|
300
|
+
'Each item: `{ value: string, type?: "contains" | ' +
|
|
301
|
+
'"regex", invert?: boolean, isOptional?: boolean }`. ' +
|
|
302
|
+
'`value` must literally appear (or match, if `regex`) ' +
|
|
303
|
+
'somewhere in the response body for the claim to be ' +
|
|
304
|
+
'accepted; it commonly embeds one or more ' +
|
|
305
|
+
'`{{paramName}}` extraction placeholders (e.g. ' +
|
|
306
|
+
'`"name":"{{name_0}}"`) whose real value is redacted ' +
|
|
307
|
+
'out and supplied separately as a witness/extracted ' +
|
|
308
|
+
'param. Use `${var}` (not `{{var}}`) inside this string ' +
|
|
309
|
+
'only if you also declare that name in this entry\'s ' +
|
|
310
|
+
'own `templateParams` below — that\'s a DIFFERENT, ' +
|
|
311
|
+
'verify-time-substituted placeholder, resolved from the ' +
|
|
312
|
+
'submitted proof before hashing (unlike a body ' +
|
|
313
|
+
'`{{param}}`, which never resolves — see `body` above).',
|
|
314
|
+
},
|
|
315
|
+
responseRedactions: {
|
|
316
|
+
type: 'array',
|
|
317
|
+
description:
|
|
318
|
+
'Each item: `{ jsonPath?: string, xPath?: string, ' +
|
|
319
|
+
'regex?: string, hash?: string }`. Identifies WHICH ' +
|
|
320
|
+
'part of the response the matching `{{paramName}}` in ' +
|
|
321
|
+
'`responseMatches` actually redacts/extracts — e.g. ' +
|
|
322
|
+
'`jsonPath: "$.data.items[0].name"` for a JSON response. ' +
|
|
323
|
+
'Order/count should line up with `responseMatches`\' ' +
|
|
324
|
+
'placeholders one-to-one for a typical claim.',
|
|
325
|
+
},
|
|
252
326
|
multiple: {
|
|
253
327
|
type: 'boolean',
|
|
254
328
|
description: 'Allow matching many requests. Default true.',
|
|
@@ -26,7 +26,7 @@ let installed = false
|
|
|
26
26
|
/** Wrap attestor-core's http `getResponseRedactions` to dump the response it
|
|
27
27
|
* runs against. Idempotent; no-op unless RECLAIM_AGENT_DUMP_RESPONSE is
|
|
28
28
|
* set. */
|
|
29
|
-
export function installResponseDump()
|
|
29
|
+
export function installResponseDump() {
|
|
30
30
|
if(installed || !process.env['RECLAIM_AGENT_DUMP_RESPONSE']) {
|
|
31
31
|
return
|
|
32
32
|
}
|
package/src/proof/proof-core.ts
CHANGED
|
@@ -20,7 +20,7 @@ let cryptoInitialized = false
|
|
|
20
20
|
* caller; without this the first proof dies inside the TLS client with
|
|
21
21
|
* `crypto.randomBytes is not a function`. Shared by every attestor caller —
|
|
22
22
|
* the agent MCP `run_proof` tool and the app's verification runner. */
|
|
23
|
-
export function ensureAttestorCrypto()
|
|
23
|
+
export function ensureAttestorCrypto() {
|
|
24
24
|
if(cryptoInitialized) {
|
|
25
25
|
return
|
|
26
26
|
}
|
package/src/proof/zk-files.ts
CHANGED
|
@@ -27,7 +27,7 @@ let inFlight: Promise<void> | undefined
|
|
|
27
27
|
* All output goes to stderr (fd 2): the MCP server speaks JSON-RPC over stdout,
|
|
28
28
|
* so a stray write to fd 1 mid-session would corrupt the protocol.
|
|
29
29
|
*/
|
|
30
|
-
export function ensureZkFiles()
|
|
30
|
+
export function ensureZkFiles() {
|
|
31
31
|
inFlight ??= download().catch((err) => {
|
|
32
32
|
inFlight = undefined
|
|
33
33
|
throw err
|
|
@@ -35,7 +35,7 @@ export function ensureZkFiles(): Promise<void> {
|
|
|
35
35
|
return inFlight
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
async function download()
|
|
38
|
+
async function download() {
|
|
39
39
|
let mainPath: string
|
|
40
40
|
try {
|
|
41
41
|
mainPath = require.resolve('@reclaimprotocol/zk-symmetric-crypto')
|
|
@@ -190,7 +190,7 @@ function findElement(root: Node, targets: string[]): Element | undefined {
|
|
|
190
190
|
const complete: Element[] = []
|
|
191
191
|
const embedded: Element[] = []
|
|
192
192
|
|
|
193
|
-
const walk = (node: Node)
|
|
193
|
+
const walk = (node: Node) => {
|
|
194
194
|
if(isTag(node)) {
|
|
195
195
|
const el = node
|
|
196
196
|
const texts = el.children.filter(isText).map((c) => c.data)
|
package/src/provider/draft.ts
CHANGED
|
@@ -160,7 +160,7 @@ export function assertPortableMatches(
|
|
|
160
160
|
matches: ResponseMatch[],
|
|
161
161
|
redactions: ResponseRedaction[],
|
|
162
162
|
targets: DraftTarget[],
|
|
163
|
-
)
|
|
163
|
+
) {
|
|
164
164
|
// A baked email LOCAL-PART ("jane.doe@…") pins the provider to one user
|
|
165
165
|
// even when only the domain is captured as a param — the classic result of
|
|
166
166
|
// proving an email's domain substring. Reject it regardless of the declared
|
|
@@ -172,7 +172,7 @@ export function assertPortableMatches(
|
|
|
172
172
|
.replace(/\{\{[^}]*\}\}/g, '')
|
|
173
173
|
.replace(/\(\?<[^>]*>[^)]*\)/g, '')
|
|
174
174
|
.replace(/\\(.)/g, '$1')
|
|
175
|
-
const scanEmail = (text: string, where: string)
|
|
175
|
+
const scanEmail = (text: string, where: string) => {
|
|
176
176
|
if(EMAIL_LOCAL_BAKED.test(deregex(text))) {
|
|
177
177
|
throw new Error(
|
|
178
178
|
`drafted ${where} bakes a per-user email local-part as a literal — `
|
|
@@ -203,7 +203,7 @@ export function assertPortableMatches(
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
const stripParams = (s: string): string => s.replace(/\{\{[^}]*\}\}/g, '')
|
|
206
|
-
const scan = (text: string, where: string, own: string | null)
|
|
206
|
+
const scan = (text: string, where: string, own: string | null) => {
|
|
207
207
|
const bare = stripParams(text)
|
|
208
208
|
for(const v of guarded) {
|
|
209
209
|
if(bare.includes(v)) {
|
|
@@ -189,6 +189,10 @@ export function providerToCreateVersionRequest(
|
|
|
189
189
|
...providerToRequestSelection(req),
|
|
190
190
|
multiple: req.multiple ?? true,
|
|
191
191
|
required: req.required ?? true,
|
|
192
|
+
templateParamsMode: req.templateParamsMode ?? 'separate',
|
|
193
|
+
...(req.templateParams?.length
|
|
194
|
+
? { templateParams: req.templateParams }
|
|
195
|
+
: {}),
|
|
192
196
|
}))
|
|
193
197
|
const script = jsUserScripts?.trim()
|
|
194
198
|
|
package/src/tools/registry.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* glue (`registerToolList` onto an `McpServer`) stays in `mcp/server.ts`, which
|
|
12
12
|
* re-exports this primitive for the MCP-side tool files.
|
|
13
13
|
*/
|
|
14
|
+
import { type Boom, isBoom } from '@hapi/boom'
|
|
15
|
+
import type { ProblemDetails } from '@reclaimprotocol/client'
|
|
14
16
|
import type { JSONSchema } from 'zod/v4/core'
|
|
15
17
|
import { toZod } from '../mcp/api/to-zod.ts'
|
|
16
18
|
|
|
@@ -51,6 +53,20 @@ export function defineTool<TArgs>(
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Render a tool-handler error as the text every host shows the LLM.
|
|
58
|
+
* Boom errors carrying an RFC 9457 problem include its `detail`; anything
|
|
59
|
+
* else falls back to the plain message.
|
|
60
|
+
*/
|
|
61
|
+
export function errorAsToolText(err: unknown): string {
|
|
62
|
+
if(isBoom(err) && err.output?.statusCode) {
|
|
63
|
+
const detail = (err as Boom<ProblemDetails>).data?.detail
|
|
64
|
+
return detail ? `${err.message}: ${detail}` : err.message
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return err instanceof Error ? err.message : String(err)
|
|
68
|
+
}
|
|
69
|
+
|
|
54
70
|
/**
|
|
55
71
|
* Adapt a tool to a host by WIDENING its input schema — add properties, mark
|
|
56
72
|
* some required, optionally override the description — without touching the
|
|
@@ -73,6 +89,9 @@ export function extendTool(
|
|
|
73
89
|
...(base.inputSchema.required ?? []),
|
|
74
90
|
...(extra.required ?? []),
|
|
75
91
|
]
|
|
92
|
+
// The merged `properties` spread carries the base's untyped property values,
|
|
93
|
+
// which don't structurally match ObjectSchema's strict per-property schema
|
|
94
|
+
// type — assert the merged shape.
|
|
76
95
|
const inputSchema = {
|
|
77
96
|
...base.inputSchema,
|
|
78
97
|
properties: {
|
package/src/cli/args.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { parseArgs as nodeParseArgs } from 'node:util'
|
|
2
|
-
|
|
3
|
-
// Placeholder. Final shape decided once the backend openapi stabilises and
|
|
4
|
-
// the CLI surface is fleshed out (see AGENTS.md → "CLI").
|
|
5
|
-
export function parseCliArgs(argv: string[]) {
|
|
6
|
-
return nodeParseArgs({
|
|
7
|
-
args: argv,
|
|
8
|
-
options: {
|
|
9
|
-
'base-url': { type: 'string' },
|
|
10
|
-
token: { type: 'string' },
|
|
11
|
-
param: { type: 'string', multiple: true },
|
|
12
|
-
query: { type: 'string', multiple: true },
|
|
13
|
-
body: { type: 'string' },
|
|
14
|
-
help: { type: 'boolean', short: 'h' },
|
|
15
|
-
version: { type: 'boolean', short: 'v' },
|
|
16
|
-
},
|
|
17
|
-
allowPositionals: true,
|
|
18
|
-
strict: false,
|
|
19
|
-
})
|
|
20
|
-
}
|
package/src/cli/commands.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { notImplemented } from '@hapi/boom'
|
|
2
|
-
import { DESCRIPTION, NAME, VERSION } from '../pkg.ts'
|
|
3
|
-
import { parseCliArgs } from './args.ts'
|
|
4
|
-
|
|
5
|
-
// Entry point for the `reclaim` bin. Placeholder until backend openapi
|
|
6
|
-
// finalises — `--version` and `--help` work; everything else throws.
|
|
7
|
-
export async function run(argv: string[]) {
|
|
8
|
-
const parsed = parseCliArgs(argv)
|
|
9
|
-
|
|
10
|
-
if(parsed.values['version']) {
|
|
11
|
-
process.stdout.write(`${NAME} ${VERSION}\n`)
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if(parsed.values['help']) {
|
|
16
|
-
process.stdout.write(
|
|
17
|
-
`${NAME} ${VERSION}\n${DESCRIPTION}\n\n` +
|
|
18
|
-
'Usage: reclaim call <OperationId> ' +
|
|
19
|
-
'[--param k=v] [--query k=v] [--body <json>]\n',
|
|
20
|
-
)
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
throw notImplemented(
|
|
25
|
-
'reclaim CLI: pending @reclaimprotocol/app spec finalisation',
|
|
26
|
-
)
|
|
27
|
-
}
|