@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.
Files changed (92) hide show
  1. package/README.md +5 -5
  2. package/lib/authoring/tools.js +2 -1
  3. package/lib/consts.d.ts +1 -1
  4. package/lib/consts.js +1 -1
  5. package/lib/live/tunnel.d.ts +4 -0
  6. package/lib/live/tunnel.js +21 -12
  7. package/lib/mcp/api/operations.gen.d.ts +8 -8
  8. package/lib/mcp/api/operations.gen.js +339 -581
  9. package/lib/mcp/api/tools.d.ts +1 -1
  10. package/lib/mcp/instructions.js +7 -3
  11. package/lib/mcp/remap_errors.js +9 -5
  12. package/lib/mcp/start.js +1 -1
  13. package/lib/mcp/tools/agent/attach.d.ts +2 -2
  14. package/lib/mcp/tools/agent/attach.js +4 -0
  15. package/lib/mcp/tools/agent/build.d.ts +1 -1
  16. package/lib/mcp/tools/agent/live-view-url.d.ts +2 -0
  17. package/lib/mcp/tools/agent/live-view-url.js +11 -0
  18. package/lib/mcp/tools/agent/live-view.d.ts +4 -2
  19. package/lib/mcp/tools/agent/live-view.js +42 -39
  20. package/lib/mcp/tools/agent/propose.js +10 -4
  21. package/lib/mcp/tools/agent/publish.d.ts +1 -1
  22. package/lib/mcp/tools/agent/publish.js +49 -26
  23. package/lib/mcp/tools/agent/shared.js +0 -9
  24. package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
  25. package/lib/mcp/tools/authenticate/build.d.ts +1 -1
  26. package/lib/mcp/tools/authenticate/builder-access.d.ts +1 -1
  27. package/lib/mcp/tools/verification/result.js +12 -13
  28. package/lib/old/login-cdp.d.ts +17 -6
  29. package/lib/old/login-cdp.js +28 -11
  30. package/lib/old/to-register.d.ts +6 -0
  31. package/lib/old/to-register.js +35 -5
  32. package/lib/old/tools/authenticate.d.ts +5 -4
  33. package/lib/old/tools/authenticate.js +9 -5
  34. package/lib/old/tools/publish.d.ts +2 -2
  35. package/lib/old/tools/publish.js +47 -21
  36. package/lib/proof/attestor-auth.d.ts +13 -0
  37. package/lib/proof/attestor-auth.js +16 -0
  38. package/lib/proof/attestor.d.ts +7 -4
  39. package/lib/proof/attestor.js +7 -4
  40. package/lib/proof/index.d.ts +1 -0
  41. package/lib/proof/index.js +1 -0
  42. package/lib/proof/pod-prover.d.ts +2 -0
  43. package/lib/proof/pod-prover.js +5 -5
  44. package/lib/provider/injection-doc.d.ts +2 -0
  45. package/lib/provider/injection-doc.js +7 -1
  46. package/lib/provider/post-publish-browser.d.ts +12 -0
  47. package/lib/provider/post-publish-browser.js +40 -0
  48. package/lib/provider/to-version.d.ts +19 -7
  49. package/lib/provider/to-version.js +67 -25
  50. package/lib/tools/registry.d.ts +1 -1
  51. package/lib/tools/registry.js +7 -4
  52. package/package.json +2 -2
  53. package/skill/SKILL.builder.md +118 -13
  54. package/skill/SKILL.common.md +30 -19
  55. package/skill/SKILL.old.md +22 -12
  56. package/skill/builder-mode.md +19 -9
  57. package/skill/community-contributions.md +108 -0
  58. package/skill/hash-validation.md +8 -5
  59. package/skill/interception.md +35 -22
  60. package/skill/old-mode.md +11 -5
  61. package/skill/user-script.md +41 -10
  62. package/src/authoring/tools.ts +2 -1
  63. package/src/consts.ts +1 -2
  64. package/src/live/tunnel.ts +28 -14
  65. package/src/mcp/api/operations.gen.ts +341 -585
  66. package/src/mcp/api/tools.ts +1 -1
  67. package/src/mcp/instructions.ts +7 -3
  68. package/src/mcp/remap_errors.ts +9 -7
  69. package/src/mcp/start.ts +1 -1
  70. package/src/mcp/tools/agent/attach.ts +6 -2
  71. package/src/mcp/tools/agent/build.ts +1 -1
  72. package/src/mcp/tools/agent/live-view-url.ts +13 -0
  73. package/src/mcp/tools/agent/live-view.ts +44 -39
  74. package/src/mcp/tools/agent/propose.ts +10 -4
  75. package/src/mcp/tools/agent/publish.ts +63 -28
  76. package/src/mcp/tools/agent/shared.ts +5 -0
  77. package/src/mcp/tools/authenticate/authenticate.ts +1 -1
  78. package/src/mcp/tools/authenticate/build.ts +1 -1
  79. package/src/mcp/tools/authenticate/builder-access.ts +1 -1
  80. package/src/mcp/tools/verification/result.ts +11 -13
  81. package/src/old/login-cdp.ts +38 -11
  82. package/src/old/to-register.ts +56 -7
  83. package/src/old/tools/authenticate.ts +9 -5
  84. package/src/old/tools/publish.ts +64 -22
  85. package/src/proof/attestor-auth.ts +33 -0
  86. package/src/proof/attestor.ts +14 -8
  87. package/src/proof/index.ts +4 -0
  88. package/src/proof/pod-prover.ts +7 -5
  89. package/src/provider/injection-doc.ts +9 -1
  90. package/src/provider/post-publish-browser.ts +56 -0
  91. package/src/provider/to-version.ts +104 -27
  92. package/src/tools/registry.ts +12 -9
@@ -1,5 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
- import type { ReclaimClient } from '@reclaimprotocol/client'
2
+ import type { ReclaimClient } from '@reclaimprotocol/client/api'
3
3
  import type {
4
4
  IOperationArgs,
5
5
  IOperationId
@@ -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 Popcorn browser, or a custom CDP endpoint), '
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 the right providers for a dev\'s app and creating a '
90
- + 'verification session.',
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 '
@@ -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 type { ProblemDetails } from '../index.ts'
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
- // The client throws a Boom-style ProblemError, so the status lives on
15
- // `output.statusCode` / `problem.status`, not `status`.
16
- if(!isBoom(err) || !err.output?.statusCode) {
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
- ...(data ? { structuredContent: data } : {}),
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 Popcorn pod's own TEE api base (builder mode only) — lets
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 LOCAL attached tab
3
- * behind a public URL so someone who is not at this machine can watch it, and
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 the CDP-attached modes (`dedicated`, `attach`, `custom`) on
7
- * purpose. A `builder`-mode browser is a Popcorn pod that already ships its
8
- * own hosted live view on `attach_browser`'s `liveViewUrl`; tunnelling a
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
- 'Put the attached browser\'s live view behind a PUBLIC HTTPS URL (a '
60
- + 'cloudflared quick tunnel) so someone elsewhere can watch it and '
61
- + 'act in it. Use it to hand a login or a 2FA step to the person '
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
- + 'ONLY for `container` mode, whose browser runs its own viewer. '
66
- + 'A `builder` browser already has a hosted live view: use the '
67
- + '`liveViewUrl` attach_browser returned. A local browser '
68
- + '(`dedicated`, `attach`, `custom`) cannot be shared at all — no '
69
- + 'tunnel can fix that, because there is no viewer behind it. Tell '
70
- + 'them so, and offer to re-attach: `container` is free and needs '
71
- + 'Docker, `builder` needs no install but is chargeable. '
72
- + 'THE URL IS THE ONLY CREDENTIAL: the random hostname is the whole '
73
- + 'secret, with no token or login behind it, so anyone who learns '
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
- + (attached.liveViewUrl
86
+ + (hostedViewUrl
92
87
  ? 'That browser already has a hosted live view — use the '
93
- + `\`liveViewUrl\` from attach_browser: ${attached.liveViewUrl}`
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 startTunnel(viewerPort, {
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 = `${tunnel.url}/liveview.html?magnify=1`
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
- 'Close the public URL opened by share_browser_view: shuts the '
156
- + 'tunnel down, so the URL stops resolving for whoever still has '
157
- + 'it. Call it as soon as the person you shared with is finished. '
158
- + 'Nothing else changes — the browser stays attached, its own live '
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
- 'The param key, or the source of one — see '
60
- + '`shortenNames`. Must stay distinct from other targets\' '
61
- + 'names after whichever treatment applies.',
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 type { ReclaimClient } from '@reclaimprotocol/client'
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). Omit `version` to ' +
163
- 'auto-bump past the highest existing one. The version lands on the ' +
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
- 'Semver to publish as. Omit to auto-pick the next ' +
206
- 'version above the highest existing one.',
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: 'number', minimum: 0 },
209
- minor: { type: 'number', minimum: 0 },
210
- patch: { type: 'number', minimum: 0 },
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 not exposed ' +
222
- 'there yet, so prefer plain browser JS.',
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
- // Auto-pick the next version if not explicit
284
- const ver = args.version ?? (await nextVersion(client, args.providerId))
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
- if(attached) {
319
- notes.push(
320
- `A "${attached.mode}" browser session is still attached` +
321
- (attached.browserSessionId
322
- ? ` (browserSessionId: ${attached.browserSessionId})`
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 dev know how/when the script runs (and the window.Reclaim gap).
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 not yet exposed ' +
336
- 'there, so prefer plain browser JS for navigation/interaction.',
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 { spawn } from 'node:child_process'
3
3
  import { createServer } from 'node:http'
4
4
  import * as oauth from 'openid-client'
@@ -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'
@@ -1,4 +1,4 @@
1
- import type { ReclaimClient } from '@reclaimprotocol/client'
1
+ import type { ReclaimClient } from '@reclaimprotocol/client/api'
2
2
  import { defineTool, type RegisteredTool } from '../../server.ts'
3
3
  import { authenticateTool } from './authenticate.ts'
4
4
 
@@ -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
- 'Check a verification session and return its result when ready. ' +
49
- 'Authenticates with the org secret (RECLAIM_ORG_SECRET). Fetches the ' +
50
- 'session status; if it is terminal and a result has been submitted, ' +
51
- 'pulls the stored result(s) and verifies them locally. Encrypted ' +
52
- 'deliveries (ECIES over secp256k1) are decrypted with your optional ' +
53
- 'eth key (RECLAIM_DECRYPT_KEY or reclaim-decrypt.key); plaintext ' +
54
- 'deliveries are read directly. Returns ' +
55
- '`ready: false` while the session is still pending or no result has ' +
56
- 'been delivered yet. Use list_verification_events for the full ' +
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 a compact JWE (encrypted to the org's
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 verifyResultFull(jws, {
118
+ const outcome = await reclaim.results.verify(jws, {
121
119
  expectedReclaimSessionId: args.sessionId,
122
120
  })
123
121
  results.push({