@reclaimprotocol/agent 0.1.27 → 0.1.29
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/README.md +5 -5
- package/lib/authoring/tools.js +2 -1
- package/lib/consts.d.ts +1 -1
- package/lib/consts.js +1 -1
- package/lib/live/tunnel.d.ts +4 -0
- package/lib/live/tunnel.js +21 -12
- package/lib/mcp/api/operations.gen.d.ts +8 -8
- package/lib/mcp/api/operations.gen.js +339 -581
- package/lib/mcp/api/tools.d.ts +1 -1
- package/lib/mcp/instructions.js +7 -3
- package/lib/mcp/remap_errors.js +9 -5
- package/lib/mcp/start.js +1 -1
- package/lib/mcp/tools/agent/attach.d.ts +2 -2
- package/lib/mcp/tools/agent/attach.js +4 -0
- package/lib/mcp/tools/agent/build.d.ts +1 -1
- package/lib/mcp/tools/agent/live-view-url.d.ts +2 -0
- package/lib/mcp/tools/agent/live-view-url.js +11 -0
- package/lib/mcp/tools/agent/live-view.d.ts +4 -2
- package/lib/mcp/tools/agent/live-view.js +42 -39
- package/lib/mcp/tools/agent/propose.js +10 -4
- package/lib/mcp/tools/agent/publish.d.ts +1 -1
- package/lib/mcp/tools/agent/publish.js +49 -26
- package/lib/mcp/tools/agent/shared.js +0 -9
- package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
- package/lib/mcp/tools/authenticate/build.d.ts +1 -1
- package/lib/mcp/tools/authenticate/builder-access.d.ts +1 -1
- package/lib/mcp/tools/verification/result.js +12 -13
- package/lib/old/login-cdp.d.ts +17 -6
- package/lib/old/login-cdp.js +28 -11
- package/lib/old/to-register.d.ts +6 -0
- package/lib/old/to-register.js +35 -5
- package/lib/old/tools/authenticate.d.ts +5 -4
- package/lib/old/tools/authenticate.js +9 -5
- package/lib/old/tools/publish.d.ts +2 -2
- package/lib/old/tools/publish.js +47 -21
- package/lib/proof/attestor-auth.d.ts +13 -0
- package/lib/proof/attestor-auth.js +16 -0
- package/lib/proof/attestor.d.ts +7 -4
- package/lib/proof/attestor.js +7 -4
- package/lib/proof/index.d.ts +1 -0
- package/lib/proof/index.js +1 -0
- package/lib/proof/pod-prover.d.ts +2 -0
- package/lib/proof/pod-prover.js +5 -5
- package/lib/provider/injection-doc.d.ts +2 -0
- package/lib/provider/injection-doc.js +7 -1
- package/lib/provider/post-publish-browser.d.ts +12 -0
- package/lib/provider/post-publish-browser.js +40 -0
- package/lib/provider/to-version.d.ts +19 -7
- package/lib/provider/to-version.js +67 -25
- package/lib/tools/registry.d.ts +1 -1
- package/lib/tools/registry.js +7 -4
- package/package.json +2 -2
- package/skill/SKILL.builder.md +118 -13
- package/skill/SKILL.common.md +30 -19
- package/skill/SKILL.old.md +22 -12
- package/skill/builder-mode.md +19 -9
- package/skill/community-contributions.md +108 -0
- package/skill/hash-validation.md +8 -5
- package/skill/interception.md +35 -22
- package/skill/old-mode.md +11 -5
- package/skill/user-script.md +41 -10
- package/src/authoring/tools.ts +2 -1
- package/src/consts.ts +1 -2
- package/src/live/tunnel.ts +28 -14
- package/src/mcp/api/operations.gen.ts +341 -585
- package/src/mcp/api/tools.ts +1 -1
- package/src/mcp/instructions.ts +7 -3
- package/src/mcp/remap_errors.ts +9 -7
- package/src/mcp/start.ts +1 -1
- package/src/mcp/tools/agent/attach.ts +6 -2
- package/src/mcp/tools/agent/build.ts +1 -1
- package/src/mcp/tools/agent/live-view-url.ts +13 -0
- package/src/mcp/tools/agent/live-view.ts +44 -39
- package/src/mcp/tools/agent/propose.ts +10 -4
- package/src/mcp/tools/agent/publish.ts +63 -28
- package/src/mcp/tools/agent/shared.ts +5 -0
- package/src/mcp/tools/authenticate/authenticate.ts +1 -1
- package/src/mcp/tools/authenticate/build.ts +1 -1
- package/src/mcp/tools/authenticate/builder-access.ts +1 -1
- package/src/mcp/tools/verification/result.ts +11 -13
- package/src/old/login-cdp.ts +38 -11
- package/src/old/to-register.ts +56 -7
- package/src/old/tools/authenticate.ts +9 -5
- package/src/old/tools/publish.ts +64 -22
- package/src/proof/attestor-auth.ts +33 -0
- package/src/proof/attestor.ts +14 -8
- package/src/proof/index.ts +4 -0
- package/src/proof/pod-prover.ts +7 -5
- package/src/provider/injection-doc.ts +9 -1
- package/src/provider/post-publish-browser.ts +56 -0
- package/src/provider/to-version.ts +104 -27
- package/src/tools/registry.ts +12 -9
package/src/mcp/api/tools.ts
CHANGED
package/src/mcp/instructions.ts
CHANGED
|
@@ -70,11 +70,13 @@ const SHARED_TOPIC_FILES: Record<string, string> = {
|
|
|
70
70
|
* topics a mode actually has are listed back to it. */
|
|
71
71
|
const TOPIC_SUMMARIES: Record<string, string> = {
|
|
72
72
|
'overview': 'Terminology, your stance, and the whole loop at a glance.',
|
|
73
|
+
'contributions': 'Contributing a public provider as a volunteer: the '
|
|
74
|
+
+ 'candidate-and-review workflow and the extra rules it adds.',
|
|
73
75
|
'auth': 'Signing in to the backend, and which token authorizes what.',
|
|
74
76
|
'devtools-migration': 'Importing a public legacy provider with the same '
|
|
75
77
|
+ 'UUID.',
|
|
76
78
|
'browser': 'Choosing a browser source (local Chrome, the Reclaim runtime in '
|
|
77
|
-
+ 'Docker, a remote
|
|
79
|
+
+ 'Docker, a remote Builder browser, or a custom CDP endpoint), '
|
|
78
80
|
+ 'geolocation, live view, and launch troubleshooting.',
|
|
79
81
|
'capture': 'Tabs, login detection, driving the page, and picking an '
|
|
80
82
|
+ 'authenticated request to prove.',
|
|
@@ -86,8 +88,10 @@ const TOPIC_SUMMARIES: Record<string, string> = {
|
|
|
86
88
|
+ 'diagnosing failures.',
|
|
87
89
|
'publish': 'Creating/reusing a provider, versions, multi-request providers, '
|
|
88
90
|
+ 'and going live.',
|
|
89
|
-
'discover': 'Finding
|
|
90
|
-
+ '
|
|
91
|
+
'discover': 'Finding providers, resolving them, and creating a verification '
|
|
92
|
+
+ 'session.',
|
|
93
|
+
'integration': 'Builder Verification Client integration: the `api=2` bridge, '
|
|
94
|
+
+ 'result verification, and optional TEE binding.',
|
|
91
95
|
'user-script': 'Authoring jsUserScripts / customInjection to drive '
|
|
92
96
|
+ 'login-gated pages.',
|
|
93
97
|
'interception': 'injectionType / interceptorType, live interception, the '
|
package/src/mcp/remap_errors.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { Boom } from '@hapi/boom'
|
|
2
1
|
import { isBoom } from '@hapi/boom'
|
|
3
2
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
|
|
4
|
-
import
|
|
3
|
+
import { isProblemDetails, isProblemError } from '@reclaimprotocol/client/api'
|
|
5
4
|
import { errorAsToolText } from '../tools/registry.ts'
|
|
6
5
|
|
|
7
6
|
// One error shape for every MCP tool. The text comes from errorAsToolText
|
|
@@ -11,19 +10,22 @@ export const remapErrorAsResponse = (
|
|
|
11
10
|
): CallToolResult => {
|
|
12
11
|
const text = errorAsToolText(err)
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const data = isProblemError(err)
|
|
14
|
+
? err.data
|
|
15
|
+
: isBoom(err) && err.output?.statusCode
|
|
16
|
+
&& isProblemDetails(err.data)
|
|
17
|
+
? err.data
|
|
18
|
+
: undefined
|
|
19
|
+
if(!data) {
|
|
17
20
|
return {
|
|
18
21
|
isError: true as const,
|
|
19
22
|
content: [{ type: 'text' as const, text }],
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
const data = (err as Boom<ProblemDetails>).data
|
|
24
26
|
return {
|
|
25
27
|
isError: true as const,
|
|
26
|
-
|
|
28
|
+
structuredContent: data,
|
|
27
29
|
content: [{ type: 'text' as const, text }],
|
|
28
30
|
}
|
|
29
31
|
}
|
package/src/mcp/start.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
-
import { ReclaimClient } from '@reclaimprotocol/client'
|
|
3
|
+
import { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
4
4
|
import { ConfigStore } from '../config/store.ts'
|
|
5
5
|
import {
|
|
6
6
|
agentDisabled,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { badRequest } from '@hapi/boom'
|
|
2
|
-
import type { ReclaimClient } from '@reclaimprotocol/client'
|
|
2
|
+
import type { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
3
3
|
import assert from 'node:assert'
|
|
4
4
|
import {
|
|
5
5
|
type CdpHandles,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import type { ConfigStore } from '../../../config/store.ts'
|
|
21
21
|
import { defaultConfigPath } from '../../../config/store.ts'
|
|
22
22
|
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
23
|
+
import { ensureLiveViewUrl } from './live-view-url.ts'
|
|
23
24
|
|
|
24
25
|
export type BrowserMode =
|
|
25
26
|
| 'dedicated'
|
|
@@ -45,7 +46,7 @@ export interface AttachState {
|
|
|
45
46
|
liveViewUrl?: string
|
|
46
47
|
browserSessionId?: string
|
|
47
48
|
orgId?: string
|
|
48
|
-
/** The
|
|
49
|
+
/** The hosted Builder browser's TEE API base — lets
|
|
49
50
|
* `run_proof_via_pod` prove through the SAME enclave that holds the
|
|
50
51
|
* browser's TLS session (and its country-routed egress), instead of the
|
|
51
52
|
* local in-process attestor SDK. */
|
|
@@ -420,6 +421,9 @@ export function attachTool(
|
|
|
420
421
|
const next = await connect(
|
|
421
422
|
mode, { ...parsed, orgId }, persisted, builderClient, deps,
|
|
422
423
|
)
|
|
424
|
+
next.liveViewUrl = next.liveViewUrl
|
|
425
|
+
? ensureLiveViewUrl(next.liveViewUrl)
|
|
426
|
+
: undefined
|
|
423
427
|
try {
|
|
424
428
|
await beforeReplace?.()
|
|
425
429
|
await disposeAttached(state)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* session for the shared authoring tools.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type { ReclaimClient } from '@reclaimprotocol/client'
|
|
13
|
+
import type { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
14
14
|
import { ConfigStore } from '../../../config/store.ts'
|
|
15
15
|
import { ensureAttestorCrypto } from '../../../proof/proof-core.ts'
|
|
16
16
|
import { type RegisteredTool } from '../../server.ts'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const LIVE_VIEW_PATH = 'liveview.html'
|
|
2
|
+
|
|
3
|
+
/** Add the browser runtime's viewer route and magnification when absent. */
|
|
4
|
+
export function ensureLiveViewUrl(value: string): string {
|
|
5
|
+
const url = new URL(value)
|
|
6
|
+
const pathname = url.pathname.replace(/\/+$/, '')
|
|
7
|
+
if(!pathname.toLowerCase().endsWith(`/${LIVE_VIEW_PATH}`)) {
|
|
8
|
+
url.pathname = `${pathname}/${LIVE_VIEW_PATH}`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
url.searchParams.set('magnify', '1')
|
|
12
|
+
return url.toString()
|
|
13
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `share_browser_view` / `stop_browser_view` — put the
|
|
3
|
-
* behind a public URL so someone who is not at this
|
|
4
|
-
* optionally drive it.
|
|
2
|
+
* `share_browser_view` / `stop_browser_view` — put the attached container
|
|
3
|
+
* browser's built-in viewer behind a public URL so someone who is not at this
|
|
4
|
+
* machine can watch it and optionally drive it.
|
|
5
5
|
*
|
|
6
|
-
* Scoped to
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* second one would duplicate it and burn a share URL for nothing, so this
|
|
10
|
-
* refuses and points at the one that exists.
|
|
6
|
+
* Scoped to `container` mode on purpose. A `builder`-mode browser already has
|
|
7
|
+
* a hosted live view at `attach_browser`'s `liveViewUrl`; tunnelling a second
|
|
8
|
+
* one would duplicate it, so this refuses and points at the existing URL.
|
|
11
9
|
*
|
|
12
10
|
* The share is deliberately explicit and single-instance: one live view per
|
|
13
11
|
* attached browser, started only when asked for, and torn down by
|
|
@@ -19,6 +17,7 @@ import { startTunnel, type Tunnel } from '../../../live/tunnel.ts'
|
|
|
19
17
|
import { LOGGER } from '../../../logger.ts'
|
|
20
18
|
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
21
19
|
import { type AttachState, SHAREABLE_MODES } from './attach.ts'
|
|
20
|
+
import { ensureLiveViewUrl } from './live-view-url.ts'
|
|
22
21
|
|
|
23
22
|
export interface LiveShare {
|
|
24
23
|
tunnel: Tunnel
|
|
@@ -30,6 +29,8 @@ export interface LiveShareRef {
|
|
|
30
29
|
current?: LiveShare
|
|
31
30
|
}
|
|
32
31
|
|
|
32
|
+
type TunnelStarter = typeof startTunnel
|
|
33
|
+
|
|
33
34
|
/** Tear down whatever share is running. Safe when none is. Exported so the
|
|
34
35
|
* session tools can call it from `dispose_browser` / `reset_session`. */
|
|
35
36
|
export async function stopLiveShare(ref: LiveShareRef): Promise<boolean> {
|
|
@@ -50,47 +51,41 @@ export async function stopLiveShare(ref: LiveShareRef): Promise<boolean> {
|
|
|
50
51
|
export function liveViewTools(
|
|
51
52
|
attachRef: { current?: AttachState },
|
|
52
53
|
shareRef: LiveShareRef,
|
|
54
|
+
openTunnel: TunnelStarter = startTunnel,
|
|
53
55
|
): RegisteredTool[] {
|
|
54
56
|
return [
|
|
55
57
|
defineTool<Record<string, never>>(
|
|
56
58
|
{
|
|
57
59
|
name: 'share_browser_view',
|
|
58
60
|
description:
|
|
59
|
-
'
|
|
60
|
-
+ '
|
|
61
|
-
+ '
|
|
62
|
-
+ 'who holds the credentials, or to let a colleague watch a '
|
|
63
|
-
+ 'capture. Returns the URL — relay it to the developer VERBATIM, '
|
|
61
|
+
'Opens a public HTTPS tunnel to the attached `container` browser '
|
|
62
|
+
+ 'viewer. Use it when someone else must sign in or control it. '
|
|
63
|
+
+ 'Returns `url` with `liveview.html?magnify=1`; relay it verbatim '
|
|
64
64
|
+ 'on its own line. '
|
|
65
|
-
+ '
|
|
66
|
-
+ '
|
|
67
|
-
+ '`
|
|
68
|
-
+ '
|
|
69
|
-
+ '
|
|
70
|
-
+ '
|
|
71
|
-
+ '
|
|
72
|
-
+ '
|
|
73
|
-
+ '
|
|
74
|
-
+ 'it can see and drive this browser as the signed-in user. Share '
|
|
75
|
-
+ 'it only with the person who needs it, and call stop_browser_view '
|
|
76
|
-
+ 'the moment they are done. '
|
|
77
|
-
+ 'Needs cloudflared: an existing install on PATH is used as-is, '
|
|
78
|
-
+ 'otherwise it is downloaded once to ~/.reclaim/bin on first '
|
|
79
|
-
+ 'share (never at install time). Set RECLAIM_CLOUDFLARED_PATH to '
|
|
80
|
-
+ 'point at your own copy instead.',
|
|
65
|
+
+ 'Only `container` supports this. For `builder`, relay '
|
|
66
|
+
+ 'the `liveViewUrl` from attach_browser; local modes '
|
|
67
|
+
+ '(`dedicated`, `attach`, `custom`) '
|
|
68
|
+
+ 'cannot be shared. The URL is a credential: anyone with it can '
|
|
69
|
+
+ 'control the signed-in browser. Share it only with the intended '
|
|
70
|
+
+ 'person and call stop_browser_view when done. `propagating: true` '
|
|
71
|
+
+ 'means the tunnel remains live while the Cloudflare edge starts; '
|
|
72
|
+
+ 'reuse the URL and reload after a few seconds rather than creating '
|
|
73
|
+
+ 'another tunnel.',
|
|
81
74
|
inputSchema: { type: 'object', properties: {} },
|
|
82
75
|
},
|
|
83
76
|
async() => {
|
|
84
|
-
|
|
85
77
|
const attached = attachRef.current
|
|
86
78
|
assert(attached, badRequest(
|
|
87
79
|
'No browser attached. Call attach_browser first.',
|
|
88
80
|
))
|
|
81
|
+
const hostedViewUrl = attached.liveViewUrl
|
|
82
|
+
? ensureLiveViewUrl(attached.liveViewUrl)
|
|
83
|
+
: undefined
|
|
89
84
|
assert(SHAREABLE_MODES.has(attached.mode), badRequest(
|
|
90
85
|
`share_browser_view does not apply to \`${attached.mode}\` mode. `
|
|
91
|
-
+ (
|
|
86
|
+
+ (hostedViewUrl
|
|
92
87
|
? 'That browser already has a hosted live view — use the '
|
|
93
|
-
+ `\`liveViewUrl\` from attach_browser: ${
|
|
88
|
+
+ `\`liveViewUrl\` from attach_browser: ${hostedViewUrl}`
|
|
94
89
|
: 'That browser has no live view to put behind a URL. To let '
|
|
95
90
|
+ 'someone else in, re-attach with mode "container" (the '
|
|
96
91
|
+ 'Reclaim runtime in Docker — free, needs Docker) or '
|
|
@@ -113,12 +108,12 @@ export function liveViewTools(
|
|
|
113
108
|
+ '"container".',
|
|
114
109
|
))
|
|
115
110
|
|
|
116
|
-
const tunnel = await
|
|
111
|
+
const tunnel = await openTunnel(viewerPort, {
|
|
117
112
|
probePath: '/liveview.html?magnify=1',
|
|
118
113
|
})
|
|
119
114
|
// The path is the runtime's own viewer page; `magnify=1` is what makes
|
|
120
115
|
// it lay out for the device that opens it.
|
|
121
|
-
const url =
|
|
116
|
+
const url = ensureLiveViewUrl(tunnel.url)
|
|
122
117
|
const share: LiveShare = {
|
|
123
118
|
tunnel, url,
|
|
124
119
|
}
|
|
@@ -127,6 +122,12 @@ export function liveViewTools(
|
|
|
127
122
|
|
|
128
123
|
return {
|
|
129
124
|
url,
|
|
125
|
+
...(share.tunnel.ready
|
|
126
|
+
? {}
|
|
127
|
+
: {
|
|
128
|
+
propagating: true,
|
|
129
|
+
cloudflaredLog: share.tunnel.cloudflaredLog,
|
|
130
|
+
}),
|
|
130
131
|
...(share.tunnel.binary.source === 'downloaded'
|
|
131
132
|
? { cloudflaredInstalled: true }
|
|
132
133
|
: {}),
|
|
@@ -135,6 +136,11 @@ export function liveViewTools(
|
|
|
135
136
|
? ['cloudflared was not installed, so it was downloaded once '
|
|
136
137
|
+ 'to ~/.reclaim/bin. Mention that; later shares reuse it.']
|
|
137
138
|
: []),
|
|
139
|
+
...(share.tunnel.ready
|
|
140
|
+
? []
|
|
141
|
+
: ['The tunnel hostname exists but the public viewer is still '
|
|
142
|
+
+ 'propagating. Relay the URL now; if it errors, wait a few '
|
|
143
|
+
+ 'seconds and reload. Do not start a second tunnel.']),
|
|
138
144
|
'Relay `url` to the developer verbatim, on its own line.',
|
|
139
145
|
'The URL is a credential: it needs no login, and anyone with it '
|
|
140
146
|
+ 'can see this browser and act in it as the signed-in user.',
|
|
@@ -152,11 +158,10 @@ export function liveViewTools(
|
|
|
152
158
|
{
|
|
153
159
|
name: 'stop_browser_view',
|
|
154
160
|
description:
|
|
155
|
-
'
|
|
156
|
-
+ '
|
|
157
|
-
+ '
|
|
158
|
-
+ '
|
|
159
|
-
+ 'view keeps serving locally, and any capture keeps running.',
|
|
161
|
+
'Stops the public tunnel opened by share_browser_view; the URL stops '
|
|
162
|
+
+ 'resolving. Call it when the viewer is done. The browser stays '
|
|
163
|
+
+ 'attached, its local viewer keeps serving, and capture continues. '
|
|
164
|
+
+ 'Returns `stopped: false` when no public share is active.',
|
|
160
165
|
inputSchema: { type: 'object', properties: {} },
|
|
161
166
|
},
|
|
162
167
|
async() => ({ stopped: await stopLiveShare(shareRef) }),
|
|
@@ -34,7 +34,12 @@ export function proposeTool(resolveBackend: ResolveBackend): RegisteredTool {
|
|
|
34
34
|
+ 'identifier (letters/digits/underscore, not starting with a '
|
|
35
35
|
+ 'digit); we recommend lower_snake_case (for example, '
|
|
36
36
|
+ '"employer_name"), '
|
|
37
|
-
+ 'but you choose the exact name.'
|
|
37
|
+
+ 'but you choose the exact name. For a parameter used only to build a '
|
|
38
|
+
+ 'later claim request, preserve a `REQ_` prefix (body, header, or '
|
|
39
|
+
+ 'intermediate value) or `URL_` prefix (path or query value) by '
|
|
40
|
+
+ 'setting `shortenNames: false`. The prefix hides that internal '
|
|
41
|
+
+ 'parameter from the data-point UI; it remains available to named '
|
|
42
|
+
+ 'regexes and `{{param}}` substitutions.',
|
|
38
43
|
inputSchema: objectSchema(
|
|
39
44
|
{
|
|
40
45
|
requestId: { type: 'string' },
|
|
@@ -56,9 +61,10 @@ export function proposeTool(resolveBackend: ResolveBackend): RegisteredTool {
|
|
|
56
61
|
name: {
|
|
57
62
|
type: 'string',
|
|
58
63
|
description:
|
|
59
|
-
'
|
|
60
|
-
+ '
|
|
61
|
-
+ '
|
|
64
|
+
'Parameter key. See `shortenNames`. Keep it unique. '
|
|
65
|
+
+ 'For hidden internal values, use `REQ_` or `URL_`. Set '
|
|
66
|
+
+ '`shortenNames: false`. The prefix hides it from the '
|
|
67
|
+
+ 'data-point UI but preserves substitution.',
|
|
62
68
|
},
|
|
63
69
|
hash: {
|
|
64
70
|
type: 'string',
|
|
@@ -1,18 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { badRequest } from '@hapi/boom'
|
|
2
|
+
import type { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
3
|
+
import type {
|
|
4
|
+
CreateProviderVersionRequest
|
|
5
|
+
} from '@reclaimprotocol/client/openapi'
|
|
6
|
+
import assert from 'node:assert'
|
|
2
7
|
import type { JSONSchema } from 'zod/v4/core'
|
|
3
8
|
import {
|
|
4
9
|
ALLOWED_REQUESTS_HINT,
|
|
10
|
+
COOKIE_ATTACHMENT_HINT,
|
|
5
11
|
USER_SCRIPT_HINT,
|
|
6
12
|
} from '../../../provider/injection-doc.ts'
|
|
7
13
|
import {
|
|
8
14
|
countsFor,
|
|
9
15
|
matchRedactionWarnings,
|
|
10
16
|
} from '../../../provider/match-redaction-warning.ts'
|
|
17
|
+
import {
|
|
18
|
+
postPublishBrowserNotes
|
|
19
|
+
} from '../../../provider/post-publish-browser.ts'
|
|
11
20
|
import type { ReclaimProvider } from '../../../provider/schema.ts'
|
|
12
21
|
import type { AllowedJsRequest } from '../../../provider/to-version.ts'
|
|
13
22
|
import {
|
|
14
|
-
nextVersion,
|
|
15
23
|
providerToCreateVersionRequest,
|
|
24
|
+
resolveNewVersion,
|
|
25
|
+
type VersionBump,
|
|
16
26
|
} from '../../../provider/to-version.ts'
|
|
17
27
|
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
18
28
|
import type { AttachState } from './attach.ts'
|
|
@@ -136,6 +146,8 @@ interface PublishArgs {
|
|
|
136
146
|
initialUrl: string
|
|
137
147
|
notes?: string
|
|
138
148
|
version?: { major: number, minor: number, patch: number }
|
|
149
|
+
bump?: VersionBump
|
|
150
|
+
requiredContext?: CreateProviderVersionRequest['requiredContext']
|
|
139
151
|
jsUserScripts?: string
|
|
140
152
|
allowedJsRequests?: AllowedJsRequest[]
|
|
141
153
|
interceptorOptions?: {
|
|
@@ -159,8 +171,11 @@ export function createVersionFromCaptureTool(
|
|
|
159
171
|
'(requests[], webSettings, …) for you; do NOT hand-construct that ' +
|
|
160
172
|
'shape or call create_provider_version directly. Exactly one of ' +
|
|
161
173
|
'`provider` (one request) or `providers` (a multi-request provider — ' +
|
|
162
|
-
'every entry is required at verification time).
|
|
163
|
-
'
|
|
174
|
+
'every entry is required at verification time). This tool always ' +
|
|
175
|
+
'creates a new immutable version; never edit an existing version. ' +
|
|
176
|
+
'Omit `version` and `bump` for a patch bump, pass `bump` for a ' +
|
|
177
|
+
'major/minor/patch bump, or pass the exact higher `version` the user ' +
|
|
178
|
+
'directed. The version lands on the ' +
|
|
164
179
|
'draft branch: call `publish_provider_version` (PRIVATE) or ' +
|
|
165
180
|
'`submit_provider_version_for_review` (PUBLIC) to go live. ' +
|
|
166
181
|
'`requiredContext` is derived from any {{context.<name>}} ' +
|
|
@@ -202,15 +217,31 @@ export function createVersionFromCaptureTool(
|
|
|
202
217
|
version: {
|
|
203
218
|
type: 'object',
|
|
204
219
|
description:
|
|
205
|
-
'
|
|
206
|
-
'
|
|
220
|
+
'Exact new semver requested by the user. It must be higher ' +
|
|
221
|
+
'than the current highest version. Do not pass with `bump`.',
|
|
207
222
|
properties: {
|
|
208
|
-
major: { type: '
|
|
209
|
-
minor: { type: '
|
|
210
|
-
patch: { type: '
|
|
223
|
+
major: { type: 'integer', minimum: 0 },
|
|
224
|
+
minor: { type: 'integer', minimum: 0 },
|
|
225
|
+
patch: { type: 'integer', minimum: 0 },
|
|
211
226
|
},
|
|
212
227
|
required: ['major', 'minor', 'patch'],
|
|
213
228
|
},
|
|
229
|
+
bump: {
|
|
230
|
+
type: 'string',
|
|
231
|
+
enum: ['major', 'minor', 'patch'],
|
|
232
|
+
description:
|
|
233
|
+
'Semantic increment from the highest version. Defaults to ' +
|
|
234
|
+
'`patch`. Do not pass with an exact `version`.',
|
|
235
|
+
},
|
|
236
|
+
requiredContext: {
|
|
237
|
+
type: 'object',
|
|
238
|
+
description:
|
|
239
|
+
'Complete JSON Schema for consumer-supplied session context. ' +
|
|
240
|
+
'Omit to derive string properties from {{context.<name>}} ' +
|
|
241
|
+
'placeholders. Pass refinements while creating this new ' +
|
|
242
|
+
'version; never update the created version in place.',
|
|
243
|
+
additionalProperties: true,
|
|
244
|
+
},
|
|
214
245
|
jsUserScripts: {
|
|
215
246
|
type: 'string',
|
|
216
247
|
description:
|
|
@@ -218,8 +249,9 @@ export function createVersionFromCaptureTool(
|
|
|
218
249
|
' Stored as webSettings.jsUserScripts; the Builder ' +
|
|
219
250
|
'verification client runs it before every page load ' +
|
|
220
251
|
'(including before navigating to initialUrl), matching ' +
|
|
221
|
-
'old-devtools customInjection. window.Reclaim is
|
|
222
|
-
'
|
|
252
|
+
'old-devtools customInjection. window.Reclaim is available ' +
|
|
253
|
+
'for supported bridge actions; prefer plain browser JS for ' +
|
|
254
|
+
'navigation and interaction.',
|
|
223
255
|
},
|
|
224
256
|
allowedJsRequests: {
|
|
225
257
|
type: 'array',
|
|
@@ -238,7 +270,8 @@ export function createVersionFromCaptureTool(
|
|
|
238
270
|
'In-app interception settings, stored as ' +
|
|
239
271
|
'webSettings.clientOptions.inapp.interceptorOptions — only ' +
|
|
240
272
|
'needed for a genuine live-interception provider (a real ' +
|
|
241
|
-
'HAWKEYE/MSWJS script, not the standard capture→replay flow).'
|
|
273
|
+
'HAWKEYE/MSWJS script, not the standard capture→replay flow). ' +
|
|
274
|
+
COOKIE_ATTACHMENT_HINT,
|
|
242
275
|
properties: {
|
|
243
276
|
interceptorType: {
|
|
244
277
|
type: 'string',
|
|
@@ -280,12 +313,19 @@ export function createVersionFromCaptureTool(
|
|
|
280
313
|
|
|
281
314
|
const providers = hasMany ? args.providers! : [args.provider!]
|
|
282
315
|
|
|
283
|
-
|
|
284
|
-
|
|
316
|
+
assert(
|
|
317
|
+
!(args.version && args.bump),
|
|
318
|
+
badRequest('Pass either `version` or `bump`, not both.'),
|
|
319
|
+
)
|
|
320
|
+
const ver = await resolveNewVersion(client, args.providerId, {
|
|
321
|
+
version: args.version,
|
|
322
|
+
bump: args.bump,
|
|
323
|
+
})
|
|
285
324
|
|
|
286
325
|
const body = providerToCreateVersionRequest(providers, args.initialUrl, {
|
|
287
326
|
notes: args.notes,
|
|
288
327
|
version: ver,
|
|
328
|
+
requiredContext: args.requiredContext,
|
|
289
329
|
jsUserScripts: args.jsUserScripts,
|
|
290
330
|
allowedJsRequests: args.allowedJsRequests,
|
|
291
331
|
interceptorOptions: args.interceptorOptions,
|
|
@@ -315,25 +355,20 @@ export function createVersionFromCaptureTool(
|
|
|
315
355
|
// before closing it (a builder-mode session is quota-accounted and
|
|
316
356
|
// otherwise just sits until its TTL expires).
|
|
317
357
|
const attached = attachRef?.current
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
: '') +
|
|
324
|
-
'. Ask the developer whether they want to keep it open for ' +
|
|
325
|
-
'further edits — only call dispose_browser once they confirm ' +
|
|
326
|
-
'they\'re done with it.',
|
|
327
|
-
)
|
|
328
|
-
}
|
|
358
|
+
notes.push(...postPublishBrowserNotes(attached, {
|
|
359
|
+
initialUrl: args.initialUrl,
|
|
360
|
+
hasUserScript: !!args.jsUserScripts?.trim(),
|
|
361
|
+
hasInterceptor: !!args.interceptorOptions,
|
|
362
|
+
}))
|
|
329
363
|
|
|
330
|
-
// Let the
|
|
364
|
+
// Let the developer know how and when the script runs.
|
|
331
365
|
if(args.jsUserScripts?.trim()) {
|
|
332
366
|
notes.push(
|
|
333
367
|
'jsUserScripts will run in the Builder verification client before ' +
|
|
334
368
|
'every page load (before navigating to initialUrl), matching ' +
|
|
335
|
-
'old-devtool customInjection. window.Reclaim is
|
|
336
|
-
'
|
|
369
|
+
'old-devtool customInjection. window.Reclaim is available for ' +
|
|
370
|
+
'supported bridge actions; prefer plain browser JS for navigation ' +
|
|
371
|
+
'and interaction.',
|
|
337
372
|
)
|
|
338
373
|
}
|
|
339
374
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* `start_capture`, since one capture bundles the tab, its requests, and its
|
|
8
8
|
* drafts) and the rich, credential-aware `run_proof` schema.
|
|
9
9
|
*/
|
|
10
|
+
import type { JSONSchema } from 'zod/v4/core'
|
|
10
11
|
import {
|
|
11
12
|
type AuthoringBackend,
|
|
12
13
|
authoringTools,
|
|
@@ -151,6 +152,10 @@ const RUN_PROOF_EXTRAS = {
|
|
|
151
152
|
attestorUrl: { type: 'string' },
|
|
152
153
|
},
|
|
153
154
|
required: ['ownerAddress'],
|
|
155
|
+
} satisfies {
|
|
156
|
+
description: string
|
|
157
|
+
properties: JSONSchema.ObjectSchema['properties']
|
|
158
|
+
required: string[]
|
|
154
159
|
}
|
|
155
160
|
|
|
156
161
|
/** The shared authoring tools mapped to the MCP's shape: resolve each call's
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReclaimClient } from '@reclaimprotocol/client'
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client/api'
|
|
2
2
|
import type { RegisteredTool } from '../../server.ts'
|
|
3
3
|
import { authenticateTool } from './authenticate.ts'
|
|
4
4
|
import { importTool } from './import.ts'
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
createReclaim,
|
|
3
3
|
decryptCallback,
|
|
4
4
|
type DecryptionKey,
|
|
5
|
-
verifyResultFull,
|
|
6
5
|
} from '@reclaimprotocol/client'
|
|
7
6
|
import assert from 'node:assert'
|
|
8
7
|
import { apiUrl, orgSecret } from '../../../consts.ts'
|
|
@@ -45,16 +44,15 @@ export function verificationResultTool(): RegisteredTool {
|
|
|
45
44
|
{
|
|
46
45
|
name: 'get_verification_result',
|
|
47
46
|
description:
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'lifecycle log.',
|
|
47
|
+
'Reads a verification session and locally checks each submitted ' +
|
|
48
|
+
'result. Requires RECLAIM_ORG_SECRET. Encrypted ECIES deliveries ' +
|
|
49
|
+
'also require the matching optional eth key ' +
|
|
50
|
+
'(RECLAIM_DECRYPT_KEY or reclaim-decrypt.key); plaintext deliveries ' +
|
|
51
|
+
'are read directly. Returns `ready: false` until the session is ' +
|
|
52
|
+
'terminal and at least one payload is readable. When `ready: true`, ' +
|
|
53
|
+
'act only on result entries where `valid` is true; `ready` means a ' +
|
|
54
|
+
'payload was read, not that verification succeeded. Use ' +
|
|
55
|
+
'list_verification_events to diagnose an unfinished session.',
|
|
58
56
|
inputSchema: {
|
|
59
57
|
type: 'object',
|
|
60
58
|
required: ['sessionId'],
|
|
@@ -101,7 +99,7 @@ export function verificationResultTool(): RegisteredTool {
|
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
// Each item's `payload` is
|
|
102
|
+
// Each item's `payload` is an ECIES ciphertext (encrypted to the org's
|
|
105
103
|
// credential) or the plaintext signed JWS, as raw text. `decryptCallback`
|
|
106
104
|
// branches on the body shape; an encrypted delivery with no key throws
|
|
107
105
|
// a clear error, which we surface per-subscription rather than skip.
|
|
@@ -117,7 +115,7 @@ export function verificationResultTool(): RegisteredTool {
|
|
|
117
115
|
continue
|
|
118
116
|
}
|
|
119
117
|
|
|
120
|
-
const outcome = await
|
|
118
|
+
const outcome = await reclaim.results.verify(jws, {
|
|
121
119
|
expectedReclaimSessionId: args.sessionId,
|
|
122
120
|
})
|
|
123
121
|
results.push({
|