@reclaimprotocol/agent 0.1.5 → 0.1.6
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/backend.d.ts +0 -24
- package/lib/authoring/find.d.ts +3 -0
- package/lib/authoring/find.js +54 -0
- package/lib/authoring/index.d.ts +3 -3
- package/lib/authoring/index.js +4 -3
- package/lib/authoring/propose.d.ts +3 -0
- package/lib/authoring/propose.js +109 -0
- package/lib/authoring/replay.d.ts +10 -0
- package/lib/authoring/replay.js +34 -0
- package/lib/authoring/tools.d.ts +13 -6
- package/lib/authoring/tools.js +108 -293
- package/lib/authoring/util.d.ts +27 -0
- package/lib/authoring/util.js +21 -0
- package/lib/consts.d.ts +6 -1
- package/lib/consts.js +9 -2
- package/lib/mcp/server.d.ts +2 -41
- package/lib/mcp/server.js +6 -34
- package/lib/mcp/start.js +4 -2
- package/lib/mcp/tools/agent/analyze.js +3 -3
- package/lib/mcp/tools/agent/build.d.ts +4 -4
- package/lib/mcp/tools/agent/build.js +13 -15
- package/lib/mcp/tools/agent/capture.d.ts +13 -1
- package/lib/mcp/tools/agent/capture.js +18 -85
- package/lib/mcp/tools/agent/eval.d.ts +5 -2
- package/lib/mcp/tools/agent/eval.js +11 -121
- package/lib/mcp/tools/agent/find.d.ts +10 -3
- package/lib/mcp/tools/agent/find.js +25 -31
- package/lib/mcp/tools/agent/proof.d.ts +13 -3
- package/lib/mcp/tools/agent/proof.js +81 -132
- package/lib/mcp/tools/agent/propose.d.ts +10 -9
- package/lib/mcp/tools/agent/propose.js +87 -64
- package/lib/mcp/tools/agent/replay.d.ts +10 -3
- package/lib/mcp/tools/agent/replay.js +29 -36
- package/lib/mcp/tools/agent/session.d.ts +1 -3
- package/lib/mcp/tools/agent/session.js +5 -5
- package/lib/mcp/tools/agent/shared.d.ts +5 -3
- package/lib/mcp/tools/agent/shared.js +29 -19
- package/lib/old/client.d.ts +35 -0
- package/lib/old/client.js +18 -0
- package/lib/old/login-cdp.d.ts +24 -5
- package/lib/old/login-cdp.js +29 -15
- package/lib/old/sdk-client.d.ts +39 -0
- package/lib/old/sdk-client.js +85 -0
- package/lib/old/to-register.d.ts +69 -0
- package/lib/old/to-register.js +52 -2
- package/lib/old/tools/authenticate.js +3 -7
- package/lib/old/tools/build.d.ts +6 -1
- package/lib/old/tools/build.js +11 -1
- package/lib/old/tools/get-provider-info.d.ts +16 -0
- package/lib/old/tools/get-provider-info.js +73 -0
- package/lib/old/tools/get-version-config.d.ts +17 -0
- package/lib/old/tools/get-version-config.js +47 -0
- package/lib/old/tools/publish.js +189 -2
- package/lib/old/tools/update-provider-metadata.d.ts +16 -0
- package/lib/old/tools/update-provider-metadata.js +61 -0
- package/lib/proof/attestor.d.ts +6 -3
- package/lib/proof/attestor.js +10 -1
- package/lib/proof/debug-response-dump.d.ts +4 -0
- package/lib/proof/debug-response-dump.js +55 -0
- package/lib/proof/patch-domhandler.d.ts +1 -0
- package/lib/proof/patch-domhandler.js +59 -0
- package/lib/proof/zk-files.js +7 -6
- package/lib/provider/dom-xpath.d.ts +17 -0
- package/lib/provider/dom-xpath.js +187 -0
- package/lib/provider/draft.d.ts +30 -2
- package/lib/provider/draft.js +117 -28
- package/lib/provider/index.d.ts +4 -0
- package/lib/provider/index.js +7 -0
- package/lib/provider/injection-doc.js +6 -0
- package/lib/provider/matcher.d.ts +17 -20
- package/lib/provider/matcher.js +394 -280
- package/lib/provider/schema.d.ts +7 -5
- package/lib/tools/registry.d.ts +14 -0
- package/lib/tools/registry.js +32 -0
- package/package.json +9 -1
- package/skill/SKILL.builder.md +42 -23
- package/skill/SKILL.old.md +6 -2
- package/skill/builder-mode.md +1 -1
- package/skill/injection-api.md +117 -0
- package/src/authoring/backend.ts +98 -0
- package/src/authoring/index.ts +20 -0
- package/src/authoring/tools.ts +273 -0
- package/src/authoring/util.ts +42 -0
- package/src/consts.ts +15 -2
- package/src/mcp/server.ts +11 -78
- package/src/mcp/start.ts +4 -1
- package/src/mcp/tools/agent/analyze.ts +3 -3
- package/src/mcp/tools/agent/build.ts +13 -21
- package/src/mcp/tools/agent/capture.ts +31 -115
- package/src/mcp/tools/agent/eval.ts +19 -155
- package/src/mcp/tools/agent/find.ts +33 -36
- package/src/mcp/tools/agent/proof.ts +87 -160
- package/src/mcp/tools/agent/propose.ts +93 -85
- package/src/mcp/tools/agent/replay.ts +32 -42
- package/src/mcp/tools/agent/session.ts +4 -5
- package/src/mcp/tools/agent/shared.ts +152 -0
- package/src/old/client.ts +56 -0
- package/src/old/login-cdp.ts +29 -23
- package/src/old/sdk-client.ts +105 -0
- package/src/old/to-register.ts +98 -6
- package/src/old/tools/authenticate.ts +3 -11
- package/src/old/tools/build.ts +12 -0
- package/src/old/tools/get-provider-info.ts +91 -0
- package/src/old/tools/get-version-config.ts +56 -0
- package/src/old/tools/publish.ts +244 -1
- package/src/old/tools/update-provider-metadata.ts +72 -0
- package/src/proof/attestor.ts +16 -4
- package/src/proof/debug-response-dump.ts +70 -0
- package/src/proof/patch-domhandler.ts +68 -0
- package/src/proof/zk-files.ts +7 -6
- package/src/provider/dom-xpath.ts +225 -0
- package/src/provider/draft.ts +168 -35
- package/src/provider/index.ts +8 -0
- package/src/provider/injection-doc.ts +6 -0
- package/src/provider/matcher.ts +477 -357
- package/src/provider/schema.ts +7 -5
- package/src/tools/registry.ts +92 -0
package/lib/authoring/tools.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The authoring tools, authored ONCE — each directly with the shared
|
|
3
|
+
* `defineTool` primitive (no parallel tool spec). `authoringTools(resolve)`
|
|
4
|
+
* returns them as plain {@link RegisteredTool}s; the MCP (local Chrome +
|
|
5
|
+
* attestor) and the cloud loop (Popcorn + TEE) each pass their own backend
|
|
6
|
+
* resolver, so navigate/inspect/propose/replay/prove logic lives in one place.
|
|
7
|
+
*
|
|
8
|
+
* The resolver hands back the backend per call — the MCP keys it by `captureId`
|
|
9
|
+
* (multi-session), the cloud returns its one run backend. A host that needs
|
|
10
|
+
* extra input schema (the MCP's `captureId` + rich `run_proof` contract) MAPS
|
|
11
|
+
* these tools through `extendTool` at registration; the tools stay plain.
|
|
12
|
+
*
|
|
13
|
+
* Secret guarantee: captured cookie/auth values never enter a tool result. The
|
|
14
|
+
* draft (recipe + secrets) is held server-side; the model only sees an opaque
|
|
15
|
+
* `draftId` and the secret-free provider.
|
|
16
|
+
*/
|
|
17
|
+
import { findTool } from "../mcp/tools/agent/find.js";
|
|
18
|
+
import { proposeTool } from "../mcp/tools/agent/propose.js";
|
|
19
|
+
import { replayTool } from "../mcp/tools/agent/replay.js";
|
|
5
20
|
import { defineTool } from "../tools/registry.js";
|
|
21
|
+
import { objectSchema, publicRequest } from "./util.js";
|
|
6
22
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
7
|
-
/** Secret-free projection of a captured request for tool output. */
|
|
8
|
-
function publicRequest(r) {
|
|
9
|
-
return {
|
|
10
|
-
requestId: r.requestId,
|
|
11
|
-
method: r.method,
|
|
12
|
-
url: r.url,
|
|
13
|
-
status: r.status,
|
|
14
|
-
contentType: r.contentType,
|
|
15
|
-
size: r.size,
|
|
16
|
-
...(r.graphqlOp ? { graphqlOp: r.graphqlOp } : {}),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
23
|
function matchesFilter(r, f) {
|
|
20
24
|
if (f.method && r.method !== f.method.toUpperCase()) {
|
|
21
25
|
return false;
|
|
@@ -57,111 +61,86 @@ async function evalInPage(backend, expression) {
|
|
|
57
61
|
return { value: res.result?.value };
|
|
58
62
|
}
|
|
59
63
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
+
* The authoring tools — navigate/inspect authored inline here; find/propose/
|
|
65
|
+
* replay live in their own files ({@link findTool}/{@link proposeTool}/
|
|
66
|
+
* {@link replayTool}) and are composed in below. Each is a plain `defineTool`
|
|
67
|
+
* primitive, so there is one canonical `RegisteredTool` per tool and no
|
|
68
|
+
* parallel tool shape. Every handler resolves its session backend via
|
|
69
|
+
* `resolveBackend(args)`: the cloud passes `() => backend` (one backend per
|
|
70
|
+
* run); the MCP passes an `args.captureId`-keyed resolver. Hosts that need
|
|
71
|
+
* extra input schema (the MCP's `captureId` session handle + the
|
|
72
|
+
* credential-aware `run_proof` contract) MAP these tools through `extendTool`
|
|
73
|
+
* (see mcp/tools/agent/shared.ts) — the tools themselves stay plain, and args
|
|
74
|
+
* parse loosely so the extra fields still reach the resolver/handler.
|
|
64
75
|
*/
|
|
65
|
-
export function
|
|
66
|
-
const mk = (spec) => {
|
|
67
|
-
const extra = host.toolExtras?.[spec.name];
|
|
68
|
-
const required = [
|
|
69
|
-
...(host.sessionSchema?.required ?? []),
|
|
70
|
-
...(spec.required ?? []),
|
|
71
|
-
...(extra?.required ?? []),
|
|
72
|
-
];
|
|
73
|
-
const inputSchema = {
|
|
74
|
-
type: 'object',
|
|
75
|
-
properties: {
|
|
76
|
-
...(host.sessionSchema?.properties ?? {}),
|
|
77
|
-
...spec.properties,
|
|
78
|
-
...(extra?.properties ?? {}),
|
|
79
|
-
},
|
|
80
|
-
...(required.length ? { required } : {}),
|
|
81
|
-
};
|
|
82
|
-
return defineTool({
|
|
83
|
-
name: spec.name,
|
|
84
|
-
description: extra?.description ?? spec.description,
|
|
85
|
-
inputSchema,
|
|
86
|
-
}, (args) => spec.run(host.resolveBackend(args), args));
|
|
87
|
-
};
|
|
76
|
+
export function authoringTools(resolveBackend) {
|
|
88
77
|
return [
|
|
89
|
-
|
|
78
|
+
defineTool({
|
|
90
79
|
name: 'navigate',
|
|
91
80
|
description: 'Navigate the browser tab to a URL, waiting for load.',
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const loadStatus = await load;
|
|
107
|
-
const final = await evalInPage(backend, 'location.href');
|
|
108
|
-
return { ok: true, loadStatus, finalUrl: final.value };
|
|
109
|
-
},
|
|
81
|
+
inputSchema: objectSchema({ url: { type: 'string' }, timeoutMs: { type: 'number' } }, ['url']),
|
|
82
|
+
}, async (args) => {
|
|
83
|
+
const backend = resolveBackend(args);
|
|
84
|
+
const timeoutMs = Number(args.timeoutMs) || 30_000;
|
|
85
|
+
await backend.sendCdp('Page.enable');
|
|
86
|
+
// Start listening BEFORE navigating so the load event isn't missed.
|
|
87
|
+
const load = backend.waitForEvent('Page.loadEventFired', timeoutMs);
|
|
88
|
+
const nav = (await backend.sendCdp('Page.navigate', { url: String(args.url) }));
|
|
89
|
+
if (nav.errorText) {
|
|
90
|
+
return { ok: false, error: nav.errorText };
|
|
91
|
+
}
|
|
92
|
+
const loadStatus = await load;
|
|
93
|
+
const final = await evalInPage(backend, 'location.href');
|
|
94
|
+
return { ok: true, loadStatus, finalUrl: final.value };
|
|
110
95
|
}),
|
|
111
|
-
|
|
96
|
+
defineTool({
|
|
112
97
|
name: 'eval_in_page',
|
|
113
98
|
description: 'Evaluate a JavaScript expression in the page and return its value '
|
|
114
99
|
+ '(use to read the displayed value, check login state, etc.).',
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}),
|
|
119
|
-
mk({
|
|
100
|
+
inputSchema: objectSchema({ expression: { type: 'string' } }, ['expression']),
|
|
101
|
+
}, (args) => evalInPage(resolveBackend(args), String(args.expression))),
|
|
102
|
+
defineTool({
|
|
120
103
|
name: 'wait_for_page',
|
|
121
104
|
description: 'Poll a JavaScript expression until it is truthy or the timeout '
|
|
122
105
|
+ 'elapses (e.g. wait for the user to finish logging in).',
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const { value } = await evalInPage(backend, expr);
|
|
133
|
-
if (value) {
|
|
134
|
-
return { ready: true };
|
|
135
|
-
}
|
|
136
|
-
await sleep(1000);
|
|
106
|
+
inputSchema: objectSchema({ expression: { type: 'string' }, timeoutMs: { type: 'number' } }, ['expression']),
|
|
107
|
+
}, async (args) => {
|
|
108
|
+
const backend = resolveBackend(args);
|
|
109
|
+
const expr = String(args.expression);
|
|
110
|
+
const deadline = Date.now() + (Number(args.timeoutMs) || 180_000);
|
|
111
|
+
while (Date.now() < deadline) {
|
|
112
|
+
const { value } = await evalInPage(backend, expr);
|
|
113
|
+
if (value) {
|
|
114
|
+
return { ready: true };
|
|
137
115
|
}
|
|
138
|
-
|
|
139
|
-
}
|
|
116
|
+
await sleep(1000);
|
|
117
|
+
}
|
|
118
|
+
return { ready: false };
|
|
140
119
|
}),
|
|
141
|
-
|
|
120
|
+
defineTool({
|
|
142
121
|
name: 'get_cookies',
|
|
143
122
|
description: 'List the NAMES of cookies currently set for the page (values are '
|
|
144
123
|
+ 'NEVER returned). The most reliable, language- and DOM-independent '
|
|
145
124
|
+ 'login signal: after the user logs in, an auth/session cookie '
|
|
146
125
|
+ 'appears (e.g. session/sid/token/auth/jwt/_user/logged_in). '
|
|
147
126
|
+ 'Includes httpOnly cookies the page JS cannot see.',
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
})
|
|
157
|
-
}
|
|
127
|
+
inputSchema: objectSchema({}),
|
|
128
|
+
}, async (args) => {
|
|
129
|
+
const backend = resolveBackend(args);
|
|
130
|
+
const res = (await backend.sendCdp('Network.getCookies'));
|
|
131
|
+
return (res.cookies ?? []).map((c) => ({
|
|
132
|
+
name: c.name,
|
|
133
|
+
...(c.domain ? { domain: c.domain } : {}),
|
|
134
|
+
...(c.session ? { session: true } : {}),
|
|
135
|
+
...(c.httpOnly ? { httpOnly: true } : {}),
|
|
136
|
+
}));
|
|
158
137
|
}),
|
|
159
|
-
|
|
138
|
+
defineTool({
|
|
160
139
|
name: 'list_requests',
|
|
161
140
|
description: 'List captured network requests (method/url/status/size), newest '
|
|
162
141
|
+ 'last. Optionally filter by method / urlGlob / status range / '
|
|
163
142
|
+ 'contentType / graphqlOp. Metadata only — secrets never included.',
|
|
164
|
-
|
|
143
|
+
inputSchema: objectSchema({
|
|
165
144
|
filter: {
|
|
166
145
|
type: 'object',
|
|
167
146
|
properties: {
|
|
@@ -173,206 +152,42 @@ export function buildAuthoringTools(host) {
|
|
|
173
152
|
graphqlOp: { type: 'string' },
|
|
174
153
|
},
|
|
175
154
|
},
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
155
|
+
}),
|
|
156
|
+
}, async (args) => {
|
|
157
|
+
const backend = resolveBackend(args);
|
|
158
|
+
const filter = (args.filter ?? {});
|
|
159
|
+
return [...backend.capture.requests.values()]
|
|
160
|
+
.filter((r) => matchesFilter(r, filter))
|
|
161
|
+
.map(publicRequest);
|
|
183
162
|
}),
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
+ 'the matched variant, score, and a short snippet — used to locate '
|
|
189
|
-
+ 'the request that returns a target. Server-side; bodies stay here.',
|
|
190
|
-
properties: {
|
|
191
|
-
value: { type: 'string' },
|
|
192
|
-
limit: { type: 'number' },
|
|
193
|
-
},
|
|
194
|
-
required: ['value'],
|
|
195
|
-
run: async (backend, args) => {
|
|
196
|
-
const variants = normalizeCandidates(String(args.value));
|
|
197
|
-
const ranked = [];
|
|
198
|
-
for (const r of backend.capture.requests.values()) {
|
|
199
|
-
if (!r.responseBody) {
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
let hit;
|
|
203
|
-
for (const v of variants) {
|
|
204
|
-
const idx = r.responseBody.indexOf(v);
|
|
205
|
-
if (idx >= 0) {
|
|
206
|
-
const start = Math.max(0, idx - 40);
|
|
207
|
-
const end = Math.min(r.responseBody.length, idx + v.length + 40);
|
|
208
|
-
hit = {
|
|
209
|
-
snippet: r.responseBody.slice(start, end), variant: v,
|
|
210
|
-
};
|
|
211
|
-
break;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (!hit) {
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
// Prefer JSON responses + smaller bodies (likely API endpoints).
|
|
218
|
-
const score = (r.contentType.includes('json') ? 10 : 0)
|
|
219
|
-
+ Math.max(0, 5 - Math.log10(Math.max(r.size, 1)));
|
|
220
|
-
ranked.push({
|
|
221
|
-
...publicRequest(r),
|
|
222
|
-
matchedVariant: hit.variant,
|
|
223
|
-
snippet: hit.snippet,
|
|
224
|
-
score,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
ranked.sort((a, b) => b.score - a.score);
|
|
228
|
-
const limit = Number(args.limit) || 10;
|
|
229
|
-
return { candidates: ranked.slice(0, limit) };
|
|
230
|
-
},
|
|
231
|
-
}),
|
|
232
|
-
mk({
|
|
233
|
-
name: 'propose_provider',
|
|
234
|
-
description: 'Draft a provider from ONE captured request. Pass `targets` (an '
|
|
235
|
-
+ 'array): one entry per value to extract, each with its `value` '
|
|
236
|
-
+ '(verbatim) and a distinct `name` (its param). The provider gets '
|
|
237
|
-
+ 'one match + redaction per target. Returns a draftId + recipe '
|
|
238
|
-
+ '(never secrets). Prefer a SINGLE request holding ALL values. '
|
|
239
|
-
+ 'Single-value shorthand: `target` + `name`. Per-target `hash` '
|
|
240
|
-
+ 'OPRF-hashes that value.',
|
|
241
|
-
properties: {
|
|
242
|
-
requestId: { type: 'string' },
|
|
243
|
-
name: { type: 'string' },
|
|
244
|
-
targets: {
|
|
245
|
-
type: 'array',
|
|
246
|
-
items: {
|
|
247
|
-
type: 'object',
|
|
248
|
-
properties: {
|
|
249
|
-
value: { type: 'string' },
|
|
250
|
-
name: { type: 'string' },
|
|
251
|
-
hash: {
|
|
252
|
-
type: 'string',
|
|
253
|
-
enum: ['oprf-raw', 'oprf', 'oprf-mpc'],
|
|
254
|
-
},
|
|
255
|
-
},
|
|
256
|
-
required: ['value', 'name'],
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
target: { type: 'string' },
|
|
260
|
-
hash: { type: 'string', enum: ['oprf-raw', 'oprf', 'oprf-mpc'] },
|
|
261
|
-
},
|
|
262
|
-
required: ['requestId', 'name'],
|
|
263
|
-
run: async (backend, args) => {
|
|
264
|
-
const requestId = String(args.requestId);
|
|
265
|
-
const providerName = String(args.name);
|
|
266
|
-
const defaultHash = args.hash;
|
|
267
|
-
// Explicit multi-value `targets`, else the single `target` shorthand.
|
|
268
|
-
const raw = Array.isArray(args.targets) ? args.targets : [];
|
|
269
|
-
const targets = raw.length
|
|
270
|
-
? raw.map((t) => {
|
|
271
|
-
const o = t;
|
|
272
|
-
return {
|
|
273
|
-
value: String(o.value),
|
|
274
|
-
name: String(o.name),
|
|
275
|
-
...(typeof o.hash === 'string'
|
|
276
|
-
? { hash: o.hash }
|
|
277
|
-
: {}),
|
|
278
|
-
};
|
|
279
|
-
})
|
|
280
|
-
: typeof args.target === 'string'
|
|
281
|
-
? [{
|
|
282
|
-
value: args.target,
|
|
283
|
-
name: providerName,
|
|
284
|
-
...(defaultHash ? { hash: defaultHash } : {}),
|
|
285
|
-
}]
|
|
286
|
-
: [];
|
|
287
|
-
if (targets.length === 0) {
|
|
288
|
-
throw new Error('propose_provider needs a `target` value or a non-empty '
|
|
289
|
-
+ '`targets` array');
|
|
290
|
-
}
|
|
291
|
-
for (const t of targets) {
|
|
292
|
-
if ((t.hash ?? defaultHash) !== undefined
|
|
293
|
-
&& t.value.length > OPRF_MAX_BYTES) {
|
|
294
|
-
throw new Error(`OPRF caps hashed values at ${OPRF_MAX_BYTES} bytes; got `
|
|
295
|
-
+ `${t.value.length}-byte "${t.value}". Use a tighter `
|
|
296
|
-
+ 'target or drop `hash`.');
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
const captured = backend.capture.requests.get(requestId);
|
|
300
|
-
if (!captured) {
|
|
301
|
-
throw new Error(`no captured request ${requestId}`);
|
|
302
|
-
}
|
|
303
|
-
if (!captured.responseBody) {
|
|
304
|
-
throw new Error(`request body unavailable for ${requestId}`);
|
|
305
|
-
}
|
|
306
|
-
const { provider, secretRefs, extractions } = draftProvider(captured, targets, providerName, defaultHash);
|
|
307
|
-
// Materialize the real secret header values for later replay/prove;
|
|
308
|
-
// they stay in the server-side draft, never in this result.
|
|
309
|
-
const secrets = {};
|
|
310
|
-
for (const ref of secretRefs) {
|
|
311
|
-
const real = captured.requestHeaders[ref.name];
|
|
312
|
-
if (real !== undefined) {
|
|
313
|
-
secrets[ref.name] = real;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
const draftId = backend.drafts.put({
|
|
317
|
-
provider, secretRefs, secrets, requestId, extractions,
|
|
318
|
-
});
|
|
319
|
-
const warnings = verificationWarnings(provider);
|
|
320
|
-
return {
|
|
321
|
-
draftId,
|
|
322
|
-
provider,
|
|
323
|
-
secretRefs,
|
|
324
|
-
...(warnings.length ? { verificationWarnings: warnings } : {}),
|
|
325
|
-
};
|
|
326
|
-
},
|
|
327
|
-
}),
|
|
328
|
-
mk({
|
|
329
|
-
name: 'replay_request',
|
|
330
|
-
description: 'Validate the drafted recipe before proving — confirm its selectors '
|
|
331
|
-
+ 'extract the target value. Returns diagnostics; run before '
|
|
332
|
-
+ 'proving. Set `withoutSecrets: true` for the AUTH-BOUND check: it '
|
|
333
|
-
+ 're-issues the request with NO secrets/cookies (an anonymous '
|
|
334
|
-
+ 'visitor). If the values still extract (valid:true), the endpoint '
|
|
335
|
-
+ 'is PUBLIC and the proof would not bind to the user — reject it and '
|
|
336
|
-
+ 'find an auth-bound request instead.',
|
|
337
|
-
properties: {
|
|
338
|
-
draftId: { type: 'string' },
|
|
339
|
-
withoutSecrets: { type: 'boolean' },
|
|
340
|
-
},
|
|
341
|
-
required: ['draftId'],
|
|
342
|
-
run: async (backend, args) => {
|
|
343
|
-
const draft = backend.drafts.get(String(args.draftId));
|
|
344
|
-
if (!draft) {
|
|
345
|
-
throw new Error(`no draft ${String(args.draftId)}`);
|
|
346
|
-
}
|
|
347
|
-
return backend.replay(draft, {
|
|
348
|
-
withoutSecrets: Boolean(args.withoutSecrets),
|
|
349
|
-
});
|
|
350
|
-
},
|
|
351
|
-
}),
|
|
352
|
-
mk({
|
|
163
|
+
findTool(resolveBackend),
|
|
164
|
+
proposeTool(resolveBackend),
|
|
165
|
+
replayTool(resolveBackend),
|
|
166
|
+
defineTool({
|
|
353
167
|
name: 'run_proof',
|
|
354
168
|
description: 'Prove the drafted recipe (zkTLS). Only call after replay_request '
|
|
355
169
|
+ 'looks right. Returns the proof result.',
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
170
|
+
inputSchema: objectSchema({ draftId: { type: 'string' } }, ['draftId']),
|
|
171
|
+
},
|
|
172
|
+
// The backend shapes its own result: the cloud returns a lean,
|
|
173
|
+
// secret-free summary (and records the proven recipe for publish); the
|
|
174
|
+
// MCP persists the claim + returns the rich developer view. The MCP adds
|
|
175
|
+
// `ownerAddress`/`attestorUrl` to the schema via `extendTool`; the
|
|
176
|
+
// handler reads them from args (loose parse keeps them).
|
|
177
|
+
async (args) => {
|
|
178
|
+
const backend = resolveBackend(args);
|
|
179
|
+
const draft = backend.drafts.get(String(args.draftId));
|
|
180
|
+
if (!draft) {
|
|
181
|
+
throw new Error(`no draft ${String(args.draftId)}`);
|
|
182
|
+
}
|
|
183
|
+
const opts = {};
|
|
184
|
+
if (typeof args.ownerAddress === 'string') {
|
|
185
|
+
opts.ownerAddress = args.ownerAddress;
|
|
186
|
+
}
|
|
187
|
+
if (typeof args.attestorUrl === 'string') {
|
|
188
|
+
opts.attestorUrl = args.attestorUrl;
|
|
189
|
+
}
|
|
190
|
+
return backend.prove(draft, opts);
|
|
376
191
|
}),
|
|
377
192
|
];
|
|
378
193
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small shared helpers for the authoring tools (see tools.ts). Kept separate so
|
|
3
|
+
* the per-tool files (find.ts, propose.ts, replay.ts) and the inline tools in
|
|
4
|
+
* tools.ts share one copy without a circular import.
|
|
5
|
+
*/
|
|
6
|
+
import type { JSONSchema } from 'zod/v4/core';
|
|
7
|
+
import type { CapturedRequest } from '../provider/schema.ts';
|
|
8
|
+
import type { AuthoringBackend } from './backend.ts';
|
|
9
|
+
/**
|
|
10
|
+
* Resolves the session backend for a tool call. The MCP keys it by
|
|
11
|
+
* `args.captureId` (multi-session); the cloud loop returns its one run backend.
|
|
12
|
+
* Each authoring tool closes over this so the same tool works for both hosts.
|
|
13
|
+
*/
|
|
14
|
+
export type ResolveBackend = (args: Record<string, unknown>) => AuthoringBackend;
|
|
15
|
+
/** Build an `object` JSON Schema for a tool input — a small literal shortener,
|
|
16
|
+
* not a tool abstraction. */
|
|
17
|
+
export declare function objectSchema(properties: Record<string, unknown>, required?: string[]): JSONSchema.ObjectSchema;
|
|
18
|
+
/** Secret-free projection of a captured request for tool output. */
|
|
19
|
+
export declare function publicRequest(r: CapturedRequest): {
|
|
20
|
+
graphqlOp?: string | undefined;
|
|
21
|
+
requestId: string;
|
|
22
|
+
method: string;
|
|
23
|
+
url: string;
|
|
24
|
+
status: number;
|
|
25
|
+
contentType: string;
|
|
26
|
+
size: number;
|
|
27
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Build an `object` JSON Schema for a tool input — a small literal shortener,
|
|
2
|
+
* not a tool abstraction. */
|
|
3
|
+
export function objectSchema(properties, required) {
|
|
4
|
+
return {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties,
|
|
7
|
+
...(required?.length ? { required } : {}),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** Secret-free projection of a captured request for tool output. */
|
|
11
|
+
export function publicRequest(r) {
|
|
12
|
+
return {
|
|
13
|
+
requestId: r.requestId,
|
|
14
|
+
method: r.method,
|
|
15
|
+
url: r.url,
|
|
16
|
+
status: r.status,
|
|
17
|
+
contentType: r.contentType,
|
|
18
|
+
size: r.size,
|
|
19
|
+
...(r.graphqlOp ? { graphqlOp: r.graphqlOp } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
package/lib/consts.d.ts
CHANGED
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { DEFAULT_BASE_URL } from '@reclaimprotocol/client';
|
|
19
19
|
import { DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL } from './old/client.ts';
|
|
20
|
+
import { DEFAULT_SDK_API_URL } from './old/sdk-client.ts';
|
|
20
21
|
/** Public attestor websocket URL — fallback for `RECLAIM_ATTESTOR_URL`. */
|
|
21
22
|
export declare const FALLBACK_ATTESTOR_URL = "wss://attestor.reclaimprotocol.org:444/ws";
|
|
22
23
|
/** The devtools dashboard login page — fallback for `RECLAIM_OLD_LOGIN_URL`. */
|
|
23
24
|
export declare const DEFAULT_LOGIN_URL = "https://dev.reclaimprotocol.org";
|
|
24
|
-
export { DEFAULT_BASE_URL, DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL };
|
|
25
|
+
export { DEFAULT_BASE_URL, DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL, DEFAULT_SDK_API_URL, };
|
|
25
26
|
/** Pino log level. Read once at logger construction, so an eager const is
|
|
26
27
|
* safe (no test mutates LOG_LEVEL at runtime). */
|
|
27
28
|
export declare const LOG_LEVEL: string;
|
|
@@ -47,6 +48,10 @@ export declare function agentDisabled(): boolean;
|
|
|
47
48
|
export declare function oldApiUrl(): string;
|
|
48
49
|
/** Old-devtools analytics-logs service base URL (separate host). */
|
|
49
50
|
export declare function oldLogsUrl(): string;
|
|
51
|
+
/** Public production SDK-backend base URL — the service the verification
|
|
52
|
+
* SDK/attestor itself reads a provider's recipe from at runtime. A
|
|
53
|
+
* DIFFERENT host from the old-devtools dashboard backend (`oldApiUrl`). */
|
|
54
|
+
export declare function sdkApiUrl(): string;
|
|
50
55
|
/** Builder backend base URL. Shared by mcp/start and the verification tool. */
|
|
51
56
|
export declare function apiUrl(): string;
|
|
52
57
|
/** Explicit builder API token (overrides the cached session token). */
|
package/lib/consts.js
CHANGED
|
@@ -19,6 +19,7 @@ import { DEFAULT_BASE_URL } from '@reclaimprotocol/client';
|
|
|
19
19
|
import { homedir } from 'node:os';
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import { DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL } from "./old/client.js";
|
|
22
|
+
import { DEFAULT_SDK_API_URL } from "./old/sdk-client.js";
|
|
22
23
|
// ---------------------------------------------------------------------------
|
|
23
24
|
// Default / fallback literals (used only as env fallbacks).
|
|
24
25
|
// ---------------------------------------------------------------------------
|
|
@@ -26,9 +27,9 @@ import { DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL } from "./old/client.js";
|
|
|
26
27
|
export const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws';
|
|
27
28
|
/** The devtools dashboard login page — fallback for `RECLAIM_OLD_LOGIN_URL`. */
|
|
28
29
|
export const DEFAULT_LOGIN_URL = 'https://dev.reclaimprotocol.org';
|
|
29
|
-
// Re-export the
|
|
30
|
+
// Re-export the non-env-fallback defaults so callers can keep importing
|
|
30
31
|
// their URL defaults from a single place if they wish.
|
|
31
|
-
export { DEFAULT_BASE_URL, DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL };
|
|
32
|
+
export { DEFAULT_BASE_URL, DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL, DEFAULT_SDK_API_URL, };
|
|
32
33
|
// ---------------------------------------------------------------------------
|
|
33
34
|
// Logging.
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
@@ -83,6 +84,12 @@ export function oldApiUrl() {
|
|
|
83
84
|
export function oldLogsUrl() {
|
|
84
85
|
return process.env.RECLAIM_OLD_LOGS_URL || DEFAULT_OLD_LOGS_URL;
|
|
85
86
|
}
|
|
87
|
+
/** Public production SDK-backend base URL — the service the verification
|
|
88
|
+
* SDK/attestor itself reads a provider's recipe from at runtime. A
|
|
89
|
+
* DIFFERENT host from the old-devtools dashboard backend (`oldApiUrl`). */
|
|
90
|
+
export function sdkApiUrl() {
|
|
91
|
+
return process.env.RECLAIM_SDK_API_URL || DEFAULT_SDK_API_URL;
|
|
92
|
+
}
|
|
86
93
|
/** Builder backend base URL. Shared by mcp/start and the verification tool. */
|
|
87
94
|
export function apiUrl() {
|
|
88
95
|
return process.env.RECLAIM_API_URL || DEFAULT_BASE_URL;
|
package/lib/mcp/server.d.ts
CHANGED
|
@@ -1,45 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type
|
|
3
|
-
export
|
|
4
|
-
name: string;
|
|
5
|
-
description: string;
|
|
6
|
-
inputSchema: JSONSchema.ObjectSchema;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Passed to every tool handler alongside its parsed args. Long-running,
|
|
10
|
-
* human-in-the-loop tools (e.g. a browser login flow that waits for someone
|
|
11
|
-
* to sign in) should call `reportProgress` every few seconds while waiting.
|
|
12
|
-
* MCP clients that attach a progress token to their request (most desktop
|
|
13
|
-
* clients do) treat an incoming `notifications/progress` as proof the call
|
|
14
|
-
* is still alive and reset their own request timeout instead of giving up —
|
|
15
|
-
* the SDK's client-side default is 60s, far shorter than a human can be
|
|
16
|
-
* expected to take to log in. A no-op when the client didn't request
|
|
17
|
-
* progress tracking for this call.
|
|
18
|
-
*/
|
|
19
|
-
export interface ToolContext {
|
|
20
|
-
reportProgress: (message: string) => Promise<void>;
|
|
21
|
-
}
|
|
22
|
-
export interface RegisteredTool {
|
|
23
|
-
definition: ToolDefinition;
|
|
24
|
-
/**
|
|
25
|
-
* @param args Tool arguments
|
|
26
|
-
* @param ctx Progress-reporting context (see {@link ToolContext}).
|
|
27
|
-
* Optional so direct/test callers can omit it.
|
|
28
|
-
* @returns A JSON stringifiable content to be returned to the llm
|
|
29
|
-
* @throws Any error encountered during tool execution will be caught and
|
|
30
|
-
* returned as a response using
|
|
31
|
-
* [remapErrorAsResponse](./src/mcp/remap_errors.ts).
|
|
32
|
-
*/
|
|
33
|
-
handler: (args: unknown, ctx?: ToolContext) => Promise<unknown>;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Define a tool with a single inputSchema (JSON Schema) used as the
|
|
37
|
-
* source of truth. The handler receives args already parsed and
|
|
38
|
-
* type-narrowed via a Zod schema derived from that same JSON Schema —
|
|
39
|
-
* so the description sent to the MCP client and the validation applied
|
|
40
|
-
* to inbound args cannot drift.
|
|
41
|
-
*/
|
|
42
|
-
export declare function defineTool<TArgs>(definition: ToolDefinition, handler: (args: TArgs, ctx: ToolContext) => Promise<unknown>): RegisteredTool;
|
|
2
|
+
import { type RegisteredTool } from '../tools/registry.ts';
|
|
3
|
+
export { defineTool, type RegisteredTool, type ToolDefinition, } from '../tools/registry.ts';
|
|
43
4
|
/**
|
|
44
5
|
* Registers a list of RegisteredTool entries on a high-level McpServer.
|
|
45
6
|
* Converts JSON Schema definitions to Zod and provides consistent
|