@reclaimprotocol/agent 0.1.0-dev.1

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 (191) hide show
  1. package/README.md +287 -0
  2. package/lib/cdp/capture.d.ts +22 -0
  3. package/lib/cdp/capture.js +159 -0
  4. package/lib/cdp/client.d.ts +9 -0
  5. package/lib/cdp/client.js +11 -0
  6. package/lib/cdp/eviction.d.ts +16 -0
  7. package/lib/cdp/eviction.js +38 -0
  8. package/lib/cdp/launcher.d.ts +14 -0
  9. package/lib/cdp/launcher.js +102 -0
  10. package/lib/cdp/tabs.d.ts +6 -0
  11. package/lib/cdp/tabs.js +10 -0
  12. package/lib/cli/args.d.ts +14 -0
  13. package/lib/cli/args.js +19 -0
  14. package/lib/cli/commands.d.ts +1 -0
  15. package/lib/cli/commands.js +19 -0
  16. package/lib/cli.d.ts +1 -0
  17. package/lib/cli.js +7 -0
  18. package/lib/config/store.d.ts +45 -0
  19. package/lib/config/store.js +46 -0
  20. package/lib/index.d.ts +3 -0
  21. package/lib/index.js +7 -0
  22. package/lib/logger.d.ts +2 -0
  23. package/lib/logger.js +7 -0
  24. package/lib/mcp/api/operations.gen.d.ts +152 -0
  25. package/lib/mcp/api/operations.gen.js +2300 -0
  26. package/lib/mcp/api/tools.d.ts +3 -0
  27. package/lib/mcp/api/tools.js +36 -0
  28. package/lib/mcp/remap_errors.d.ts +2 -0
  29. package/lib/mcp/remap_errors.js +27 -0
  30. package/lib/mcp/server.d.ts +38 -0
  31. package/lib/mcp/server.js +76 -0
  32. package/lib/mcp/start.d.ts +1 -0
  33. package/lib/mcp/start.js +63 -0
  34. package/lib/mcp/tools/agent/analyze.d.ts +3 -0
  35. package/lib/mcp/tools/agent/analyze.js +26 -0
  36. package/lib/mcp/tools/agent/attach.d.ts +11 -0
  37. package/lib/mcp/tools/agent/attach.js +72 -0
  38. package/lib/mcp/tools/agent/build.d.ts +33 -0
  39. package/lib/mcp/tools/agent/build.js +74 -0
  40. package/lib/mcp/tools/agent/capture.d.ts +10 -0
  41. package/lib/mcp/tools/agent/capture.js +142 -0
  42. package/lib/mcp/tools/agent/eval.d.ts +5 -0
  43. package/lib/mcp/tools/agent/eval.js +127 -0
  44. package/lib/mcp/tools/agent/find.d.ts +3 -0
  45. package/lib/mcp/tools/agent/find.js +60 -0
  46. package/lib/mcp/tools/agent/proof.d.ts +3 -0
  47. package/lib/mcp/tools/agent/proof.js +214 -0
  48. package/lib/mcp/tools/agent/propose.d.ts +9 -0
  49. package/lib/mcp/tools/agent/propose.js +75 -0
  50. package/lib/mcp/tools/agent/publish.d.ts +3 -0
  51. package/lib/mcp/tools/agent/publish.js +122 -0
  52. package/lib/mcp/tools/agent/replay.d.ts +3 -0
  53. package/lib/mcp/tools/agent/replay.js +46 -0
  54. package/lib/mcp/tools/agent/session.d.ts +10 -0
  55. package/lib/mcp/tools/agent/session.js +29 -0
  56. package/lib/mcp/tools/credentials/authenticate.d.ts +3 -0
  57. package/lib/mcp/tools/credentials/authenticate.js +108 -0
  58. package/lib/mcp/tools/credentials/build.d.ts +16 -0
  59. package/lib/mcp/tools/credentials/build.js +33 -0
  60. package/lib/mcp/tools/credentials/cache.d.ts +70 -0
  61. package/lib/mcp/tools/credentials/cache.js +114 -0
  62. package/lib/mcp/tools/credentials/env-file.d.ts +25 -0
  63. package/lib/mcp/tools/credentials/env-file.js +71 -0
  64. package/lib/mcp/tools/credentials/import.d.ts +2 -0
  65. package/lib/mcp/tools/credentials/import.js +52 -0
  66. package/lib/mcp/tools/credentials/issue.d.ts +2 -0
  67. package/lib/mcp/tools/credentials/issue.js +84 -0
  68. package/lib/mcp/tools/credentials/list-cached.d.ts +2 -0
  69. package/lib/mcp/tools/credentials/list-cached.js +55 -0
  70. package/lib/mcp/tools/credentials/register.d.ts +18 -0
  71. package/lib/mcp/tools/credentials/register.js +30 -0
  72. package/lib/mcp/tools/credentials/resolve-owner-key.d.ts +12 -0
  73. package/lib/mcp/tools/credentials/resolve-owner-key.js +68 -0
  74. package/lib/mcp/tools/credentials/session-cache.d.ts +14 -0
  75. package/lib/mcp/tools/credentials/session-cache.js +19 -0
  76. package/lib/mcp/tools/credentials/status.d.ts +3 -0
  77. package/lib/mcp/tools/credentials/status.js +62 -0
  78. package/lib/mcp-bin.d.ts +1 -0
  79. package/lib/mcp-bin.js +7 -0
  80. package/lib/old/client.d.ts +67 -0
  81. package/lib/old/client.js +146 -0
  82. package/lib/old/flag.d.ts +11 -0
  83. package/lib/old/flag.js +13 -0
  84. package/lib/old/identity.d.ts +22 -0
  85. package/lib/old/identity.js +47 -0
  86. package/lib/old/instructions.d.ts +7 -0
  87. package/lib/old/instructions.js +29 -0
  88. package/lib/old/login-cdp.d.ts +23 -0
  89. package/lib/old/login-cdp.js +125 -0
  90. package/lib/old/to-register.d.ts +47 -0
  91. package/lib/old/to-register.js +133 -0
  92. package/lib/old/tools/authenticate.d.ts +18 -0
  93. package/lib/old/tools/authenticate.js +127 -0
  94. package/lib/old/tools/build.d.ts +9 -0
  95. package/lib/old/tools/build.js +16 -0
  96. package/lib/old/tools/me-providers.d.ts +10 -0
  97. package/lib/old/tools/me-providers.js +32 -0
  98. package/lib/old/tools/publish.d.ts +16 -0
  99. package/lib/old/tools/publish.js +97 -0
  100. package/lib/paths.d.ts +11 -0
  101. package/lib/paths.js +32 -0
  102. package/lib/pkg.d.ts +3 -0
  103. package/lib/pkg.js +13 -0
  104. package/lib/proof/attestor.d.ts +39 -0
  105. package/lib/proof/attestor.js +207 -0
  106. package/lib/proof/replay.d.ts +8 -0
  107. package/lib/proof/replay.js +73 -0
  108. package/lib/provider/constraints.d.ts +2 -0
  109. package/lib/provider/constraints.js +122 -0
  110. package/lib/provider/draft.d.ts +6 -0
  111. package/lib/provider/draft.js +68 -0
  112. package/lib/provider/graphql.d.ts +2 -0
  113. package/lib/provider/graphql.js +35 -0
  114. package/lib/provider/matcher.d.ts +36 -0
  115. package/lib/provider/matcher.js +436 -0
  116. package/lib/provider/normalize.d.ts +4 -0
  117. package/lib/provider/normalize.js +51 -0
  118. package/lib/provider/schema.d.ts +76 -0
  119. package/lib/provider/schema.js +1 -0
  120. package/lib/provider/secrets.d.ts +5 -0
  121. package/lib/provider/secrets.js +22 -0
  122. package/lib/provider/to-version.d.ts +22 -0
  123. package/lib/provider/to-version.js +68 -0
  124. package/lib/types/openapi.d.ts +4 -0
  125. package/lib/types/openapi.js +1 -0
  126. package/package.json +88 -0
  127. package/scripts/download-zk-files.ts +42 -0
  128. package/scripts/prepend-shebang.ts +17 -0
  129. package/skill/SKILL.md +248 -0
  130. package/src/cdp/capture.ts +215 -0
  131. package/src/cdp/client.ts +20 -0
  132. package/src/cdp/eviction.ts +52 -0
  133. package/src/cdp/launcher.ts +133 -0
  134. package/src/cdp/tabs.ts +22 -0
  135. package/src/cli/args.ts +20 -0
  136. package/src/cli/commands.ts +27 -0
  137. package/src/cli.ts +7 -0
  138. package/src/config/store.ts +86 -0
  139. package/src/index.ts +12 -0
  140. package/src/logger.ts +8 -0
  141. package/src/mcp/api/operations.gen.ts +2397 -0
  142. package/src/mcp/api/tools.ts +45 -0
  143. package/src/mcp/remap_errors.ts +35 -0
  144. package/src/mcp/server.ts +119 -0
  145. package/src/mcp/start.ts +78 -0
  146. package/src/mcp/tools/agent/analyze.ts +34 -0
  147. package/src/mcp/tools/agent/attach.ts +95 -0
  148. package/src/mcp/tools/agent/build.ts +99 -0
  149. package/src/mcp/tools/agent/capture.ts +202 -0
  150. package/src/mcp/tools/agent/eval.ts +161 -0
  151. package/src/mcp/tools/agent/find.ts +73 -0
  152. package/src/mcp/tools/agent/proof.ts +272 -0
  153. package/src/mcp/tools/agent/propose.ts +111 -0
  154. package/src/mcp/tools/agent/publish.ts +155 -0
  155. package/src/mcp/tools/agent/replay.ts +58 -0
  156. package/src/mcp/tools/agent/session.ts +46 -0
  157. package/src/mcp/tools/credentials/authenticate.ts +135 -0
  158. package/src/mcp/tools/credentials/build.ts +37 -0
  159. package/src/mcp/tools/credentials/cache.ts +161 -0
  160. package/src/mcp/tools/credentials/env-file.ts +101 -0
  161. package/src/mcp/tools/credentials/import.ts +72 -0
  162. package/src/mcp/tools/credentials/issue.ts +107 -0
  163. package/src/mcp/tools/credentials/list-cached.ts +66 -0
  164. package/src/mcp/tools/credentials/register.ts +49 -0
  165. package/src/mcp/tools/credentials/resolve-owner-key.ts +78 -0
  166. package/src/mcp/tools/credentials/session-cache.ts +31 -0
  167. package/src/mcp/tools/credentials/status.ts +82 -0
  168. package/src/mcp-bin.ts +7 -0
  169. package/src/old/client.ts +236 -0
  170. package/src/old/flag.ts +13 -0
  171. package/src/old/identity.ts +56 -0
  172. package/src/old/instructions.ts +30 -0
  173. package/src/old/login-cdp.ts +153 -0
  174. package/src/old/to-register.ts +234 -0
  175. package/src/old/tools/authenticate.ts +152 -0
  176. package/src/old/tools/build.ts +21 -0
  177. package/src/old/tools/me-providers.ts +42 -0
  178. package/src/old/tools/publish.ts +135 -0
  179. package/src/paths.ts +37 -0
  180. package/src/pkg.ts +24 -0
  181. package/src/proof/attestor.ts +286 -0
  182. package/src/proof/replay.ts +102 -0
  183. package/src/provider/constraints.ts +150 -0
  184. package/src/provider/draft.ts +105 -0
  185. package/src/provider/graphql.ts +42 -0
  186. package/src/provider/matcher.ts +616 -0
  187. package/src/provider/normalize.ts +65 -0
  188. package/src/provider/schema.ts +92 -0
  189. package/src/provider/secrets.ts +29 -0
  190. package/src/provider/to-version.ts +90 -0
  191. package/src/types/openapi.ts +7 -0
@@ -0,0 +1,111 @@
1
+ import { randomUUID } from 'node:crypto'
2
+ import { draftProvider } from '../../../provider/draft.ts'
3
+ import { OPRF_MAX_BYTES } from '../../../provider/matcher.ts'
4
+ import type { OprfMode } from '../../../provider/schema.ts'
5
+ import { defineTool, type RegisteredTool } from '../../server.ts'
6
+ import type { CaptureStore } from './capture.ts'
7
+
8
+ export interface SecretStore {
9
+ put: (values: Record<string, string>) => string
10
+ resolve: (ref: string) => Record<string, string> | undefined
11
+ clear: () => void
12
+ }
13
+
14
+ export function makeSecretStore(): SecretStore {
15
+ const map = new Map<string, Record<string, string>>()
16
+ return {
17
+ put: (values) => {
18
+ const id = `sref_${randomUUID()}`
19
+ map.set(id, values)
20
+ return id
21
+ },
22
+ resolve: (ref) => map.get(ref),
23
+ clear: () => map.clear(),
24
+ }
25
+ }
26
+
27
+ export function proposeTool(
28
+ captures: CaptureStore,
29
+ secrets: SecretStore,
30
+ ): RegisteredTool {
31
+ return defineTool<{
32
+ captureId: string
33
+ requestId: string
34
+ target: string
35
+ name: string
36
+ hash?: OprfMode
37
+ }>(
38
+ {
39
+ name: 'propose_provider',
40
+ description:
41
+ 'Draft a Reclaim provider JSON from a captured request + ' +
42
+ 'a target value. ' +
43
+ 'Secret header values are stored server-side and replaced with an ' +
44
+ 'opaque secretParamsRef ' +
45
+ 'in the response — raw secret values never appear in tool output. ' +
46
+ "`hash` is opt-in: when set, the captured value is OPRF'd so " +
47
+ 'the attestor never sees ' +
48
+ 'cleartext. Skill should ask the dev first. `oprf-raw` ' +
49
+ 'is the only variant that runs ' +
50
+ 'without external deps; `oprf` needs gnark (Linux/macOS), ' +
51
+ '`oprf-mpc` needs a TEE attestor.',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {
55
+ captureId: { type: 'string' },
56
+ requestId: { type: 'string' },
57
+ target: { type: 'string' },
58
+ name: { type: 'string' },
59
+ hash: { type: 'string', enum: ['oprf-raw', 'oprf', 'oprf-mpc'] },
60
+ },
61
+ required: ['captureId', 'requestId', 'target', 'name'],
62
+ },
63
+ },
64
+ async({ captureId, requestId, target, name, hash }) => {
65
+ if(hash !== undefined && target.length > OPRF_MAX_BYTES) {
66
+ throw new Error(
67
+ `OPRF caps hashed values at ${OPRF_MAX_BYTES} bytes; ` +
68
+ `got ${target.length}-byte target ` +
69
+ `"${target}". Try a tighter target ` +
70
+ '(e.g. just the relevant substring) or call ' +
71
+ 'propose_provider without `hash`.',
72
+ )
73
+ }
74
+
75
+ const session = captures.sessions.get(captureId)
76
+ if(!session) {
77
+ throw new Error(`no capture ${captureId}`)
78
+ }
79
+
80
+ const r = session.requests.get(requestId)
81
+ if(!r) {
82
+ throw new Error(`no request ${requestId}`)
83
+ }
84
+
85
+ const body = session.bodies.get(requestId)
86
+ if(!body.available) {
87
+ throw new Error(`request body unavailable: ${body.reason}`)
88
+ }
89
+
90
+ const reqWithBody = { ...r, responseBody: body.body }
91
+ const { provider, secretRefs } = draftProvider(
92
+ reqWithBody,
93
+ target,
94
+ name,
95
+ hash,
96
+ )
97
+
98
+ // Stash real secret values for later replay/proof.
99
+ const realSecrets: Record<string, string> = {}
100
+ for(const ref of secretRefs) {
101
+ const real = r.requestHeaders[ref.name]
102
+ if(real !== undefined) {
103
+ realSecrets[ref.name] = real
104
+ }
105
+ }
106
+
107
+ const secretParamsRef = secrets.put(realSecrets)
108
+ return { provider, secretRefs, secretParamsRef }
109
+ },
110
+ )
111
+ }
@@ -0,0 +1,155 @@
1
+ import type { ReclaimClient } from '@reclaimprotocol/client'
2
+ import type { ReclaimProvider } from '../../../provider/schema.ts'
3
+ import {
4
+ nextVersion,
5
+ providerToCreateVersionRequest,
6
+ } from '../../../provider/to-version.ts'
7
+ import { defineTool, type RegisteredTool } from '../../server.ts'
8
+
9
+ export function createVersionFromCaptureTool(
10
+ client: ReclaimClient,
11
+ ): RegisteredTool {
12
+ return defineTool<{
13
+ providerId: string
14
+ provider: ReclaimProvider
15
+ initialUrl: string
16
+ notes?: string
17
+ version?: { major: number, minor: number, patch: number }
18
+ }>(
19
+ {
20
+ name: 'create_provider_version_from_capture',
21
+ description:
22
+ 'Persist a drafted ReclaimProvider as a verification version. ' +
23
+ 'Call this after run_proof succeeds, passing the `provider` object ' +
24
+ 'from propose_provider VERBATIM. This tool builds the full version ' +
25
+ 'body (the requests[] array, webSettings, etc.) for you — do NOT ' +
26
+ 'hand-construct that shape or call create_provider_version directly. ' +
27
+ 'Omit `version` to have the tool auto-select the next available ' +
28
+ 'version (it lists existing versions and bumps past the highest). ' +
29
+ 'For PRIVATE verifications the version goes live immediately; ' +
30
+ 'for PUBLIC ones call submit_provider_version_for_review afterwards.',
31
+ inputSchema: {
32
+ type: 'object',
33
+ properties: {
34
+ providerId: {
35
+ type: 'string',
36
+ description: 'UUID of the verification to publish the version on.',
37
+ },
38
+ provider: {
39
+ type: 'object',
40
+ description:
41
+ 'The `provider` object from propose_provider, passed ' +
42
+ 'through verbatim — the tool builds the version body ' +
43
+ '(the requests[] array etc.) for you.',
44
+ properties: {
45
+ name: { type: 'string', description: 'Verification name.' },
46
+ url: {
47
+ type: 'string',
48
+ description:
49
+ 'Exact API URL the attestor replays, with {{param}} ' +
50
+ 'placeholders for any templated values.',
51
+ },
52
+ method: {
53
+ type: 'string',
54
+ enum: ['GET', 'POST', 'PUT', 'PATCH'],
55
+ },
56
+ headers: {
57
+ type: 'object',
58
+ description:
59
+ 'Request headers from the capture. Secret headers are ' +
60
+ 'stripped before persistence; the client re-supplies them ' +
61
+ 'at runtime.',
62
+ },
63
+ body: {
64
+ type: 'string',
65
+ description: 'Request body template, if any.',
66
+ },
67
+ responseMatches: {
68
+ type: 'array',
69
+ description:
70
+ 'What the attestor checks on the revealed response slice.',
71
+ items: {
72
+ type: 'object',
73
+ properties: {
74
+ type: { type: 'string', enum: ['regex', 'contains'] },
75
+ value: { type: 'string' },
76
+ },
77
+ required: ['type', 'value'],
78
+ },
79
+ },
80
+ responseRedactions: {
81
+ type: 'array',
82
+ description:
83
+ 'Which portions of the response the prover reveals ' +
84
+ '(xPath / jsonPath / regex; hash to OPRF the value). ' +
85
+ 'Paired with responseMatches by index — the tool folds ' +
86
+ 'each one into the matching entry\'s `extract` in the ' +
87
+ 'persisted version, so keep them aligned 1-to-1.',
88
+ items: {
89
+ type: 'object',
90
+ properties: {
91
+ xPath: { type: 'string' },
92
+ jsonPath: { type: 'string' },
93
+ regex: { type: 'string' },
94
+ hash: {
95
+ type: 'string',
96
+ enum: ['oprf', 'oprf-mpc', 'oprf-raw'],
97
+ },
98
+ },
99
+ },
100
+ },
101
+ paramValues: {
102
+ type: 'object',
103
+ description:
104
+ 'Real values for the {{param}} placeholders, ' +
105
+ 'e.g. { "username": "Syed" }.',
106
+ },
107
+ },
108
+ required: ['name', 'url', 'method', 'responseMatches'],
109
+ },
110
+ initialUrl: {
111
+ type: 'string',
112
+ description:
113
+ 'The website URL the user landed on to trigger the request ' +
114
+ '(e.g. https://github.com/Syed). The URL the verification ' +
115
+ 'flow opens for end-users — NOT the API URL inside the provider.',
116
+ },
117
+ notes: {
118
+ type: 'string',
119
+ description:
120
+ 'Short change note (e.g. "Initial draft from browser capture").',
121
+ },
122
+ version: {
123
+ type: 'object',
124
+ description:
125
+ 'Semver to publish as. Omit to auto-pick the next ' +
126
+ 'version above the highest existing one.',
127
+ properties: {
128
+ major: { type: 'number', minimum: 0 },
129
+ minor: { type: 'number', minimum: 0 },
130
+ patch: { type: 'number', minimum: 0 },
131
+ },
132
+ required: ['major', 'minor', 'patch'],
133
+ },
134
+ },
135
+ required: ['providerId', 'provider', 'initialUrl'],
136
+ },
137
+ },
138
+ async(args) => {
139
+ // Auto-pick the next version if not explicit
140
+ const ver = args.version ?? (await nextVersion(client, args.providerId))
141
+
142
+ const body = providerToCreateVersionRequest(
143
+ args.provider,
144
+ args.initialUrl,
145
+ args.notes,
146
+ ver,
147
+ )
148
+ const result = (await client.call('CreateProviderVersion', {
149
+ params: { providerId: args.providerId },
150
+ body,
151
+ }))
152
+ return result.data
153
+ },
154
+ )
155
+ }
@@ -0,0 +1,58 @@
1
+ import { replayProvider } from '../../../proof/replay.ts'
2
+ import type { ReclaimProvider } from '../../../provider/schema.ts'
3
+ import { defineTool, type RegisteredTool } from '../../server.ts'
4
+ import type { SecretStore } from './propose.ts'
5
+
6
+ export function replayTool(secrets: SecretStore): RegisteredTool {
7
+ return defineTool<{ provider: unknown, secretParamsRef?: string }>(
8
+ {
9
+ name: 'replay_request',
10
+ description:
11
+ 'Replay a drafted provider against the live endpoint ' +
12
+ 'using Node fetch (no attestor). ' +
13
+ 'Pre-flight gate before run_proof — ' +
14
+ 'diagnoses CSRF / token-rotation / matcher-too-tight ' +
15
+ 'issues with richer hints than the attestor would produce.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ // `type: 'object'` is required so MCP clients (the LLM)
20
+ // serialize the value as an object, not a JSON-stringified
21
+ // string. `properties: {}` is required so zod-from-json-schema
22
+ // compiles to a passthrough instead of strict-strip — leaving
23
+ // it off makes the server receive `{}`.
24
+ provider: { type: 'object', properties: {} },
25
+ secretParamsRef: { type: 'string' },
26
+ },
27
+ required: ['provider'],
28
+ },
29
+ },
30
+ async({ provider, secretParamsRef }) => {
31
+ let resolved: Record<string, string> = {}
32
+ if(secretParamsRef !== undefined) {
33
+ const r = secrets.resolve(secretParamsRef)
34
+ if(!r) {
35
+ throw new Error(
36
+ `unknown secretParamsRef "${secretParamsRef}" — ` +
37
+ 'the MCP server has restarted or reset_session was called. ' +
38
+ 'Re-run propose_provider to mint a fresh ref.',
39
+ )
40
+ }
41
+
42
+ resolved = r
43
+ }
44
+
45
+ const result = await replayProvider(provider as ReclaimProvider, resolved)
46
+ const out: Record<string, unknown> = {
47
+ status: result.status,
48
+ matchesOriginal: result.matchesOriginal,
49
+ bodySnippet: result.body.slice(0, 256),
50
+ }
51
+ if(result.hints !== undefined) {
52
+ out.hints = result.hints
53
+ }
54
+
55
+ return out
56
+ },
57
+ )
58
+ }
@@ -0,0 +1,46 @@
1
+ import type { ConfigStore } from '../../../config/store.ts'
2
+ import { defineTool, type RegisteredTool } from '../../server.ts'
3
+ import type { AttachState } from './attach.ts'
4
+ import type { CaptureStore } from './capture.ts'
5
+
6
+ export function sessionTools(
7
+ attachRef: { current?: AttachState, config: ConfigStore },
8
+ captures: CaptureStore,
9
+ secrets: { clear: () => void },
10
+ ): RegisteredTool[] {
11
+ return [
12
+ defineTool<Record<string, never>>(
13
+ {
14
+ name: 'get_session',
15
+ description:
16
+ 'Return current attach state, active captures, and persisted config.',
17
+ inputSchema: { type: 'object', properties: {} },
18
+ },
19
+ async() => ({
20
+ config: attachRef.config.read()?.browser_agent,
21
+ attached: Boolean(attachRef.current),
22
+ port: attachRef.current?.port,
23
+ captures: [...captures.sessions.keys()],
24
+ activeTab: captures.activeTab,
25
+ }),
26
+ ),
27
+ defineTool<Record<string, never>>(
28
+ {
29
+ name: 'reset_session',
30
+ description:
31
+ 'Stop all captures, drop in-memory secrets, and clear capture state.',
32
+ inputSchema: { type: 'object', properties: {} },
33
+ },
34
+ async() => {
35
+ for(const s of captures.sessions.values()) {
36
+ await s.stop().catch(() => {})
37
+ }
38
+
39
+ captures.sessions.clear()
40
+ delete captures.activeTab
41
+ secrets.clear()
42
+ return { reset: true }
43
+ },
44
+ ),
45
+ ]
46
+ }
@@ -0,0 +1,135 @@
1
+ import type { ReclaimClient } from '@reclaimprotocol/client'
2
+ import type {
3
+ CredentialAttachSession as Session
4
+ } from '../../../types/openapi.ts'
5
+ import { defineTool, type RegisteredTool } from '../../server.ts'
6
+ import { writeSession } from './session-cache.ts'
7
+
8
+ export function authenticateTool(client: ReclaimClient): RegisteredTool {
9
+ return defineTool<{
10
+ code?: string
11
+ }>(
12
+ {
13
+ name: 'reclaim_authenticate',
14
+ description:
15
+ 'Authorize the CLI / MCP to make authenticated API calls by having ' +
16
+ 'the user sign in on the dashboard. Two steps:\n\n' +
17
+ 'Step 1 — call with no arguments. Returns a `code` and `attachUrl`. ' +
18
+ 'Tell the user to open the URL, sign in, and authorize.\n\n' +
19
+ 'Step 2 — once the user says they have authorized, call again ' +
20
+ 'with the same `code` from step 1 to check if confirmed. ' +
21
+ 'Returns `LINKED` with a 2-day bearer token when confirmed, ' +
22
+ '`PENDING` if not yet, or `EXPIRED` if too long has passed.\n\n' +
23
+ 'The token is cached in ~/.reclaim/config.json automatically.\n\n' +
24
+ 'To then link a credential to an org (needed only to use it ' +
25
+ 'for verification in production), use the token with the ' +
26
+ 'generated tools: `list_orgs` → `link_credential` with the ' +
27
+ "credential's `address` and the target `orgId`.",
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ code: {
32
+ type: 'string',
33
+ pattern: '^[A-HJ-NP-Z2-9]{5}$',
34
+ description:
35
+ '5-character code from a previous step-1 call. ' +
36
+ 'When provided, checks whether the user has ' +
37
+ 'confirmed that session instead of opening a new one.',
38
+ },
39
+ },
40
+ },
41
+ },
42
+ async(args) => {
43
+ // Step 2: check an existing session
44
+ if(args.code) {
45
+ return checkSession(client, args.code)
46
+ }
47
+
48
+ // Step 1: open a new auth-only session
49
+ const sessionResult = (await client.call('CreateAttachSession'))
50
+ const session = sessionResult.data
51
+
52
+ return {
53
+ status: 'PENDING',
54
+ code: session.code,
55
+ attachUrl: session.attachUrl,
56
+ expiresAt: session.expiresAt,
57
+ instructions:
58
+ `Open ${session.attachUrl} in your browser, sign in, and click ` +
59
+ '"Authorize this device". ' +
60
+ 'Once done, call authenticate again with code: ' +
61
+ `"${session.code}" to confirm.`,
62
+ }
63
+ },
64
+ )
65
+ }
66
+
67
+ async function checkSession(
68
+ client: ReclaimClient,
69
+ code: string,
70
+ ): Promise<unknown> {
71
+ let session: Session
72
+ try {
73
+ const sessionResult = (await client.call('GetAttachSession', {
74
+ params: { code }
75
+ }))
76
+ session = sessionResult.data
77
+ } catch(err) {
78
+ const status = (err as { status?: number }).status
79
+ if(status === 404) {
80
+ return {
81
+ status: 'NOT_FOUND',
82
+ hint:
83
+ 'Session not found — it may have expired (15-min window). ' +
84
+ 'Call authenticate without a code to start a new session.',
85
+ }
86
+ }
87
+
88
+ throw new Error(`Failed to check session: ${(err as Error).message}`)
89
+ }
90
+
91
+ if(session.status === 'LINKED') {
92
+ const token = session.token?.trim()
93
+ if(token) {
94
+ writeSession({
95
+ token,
96
+ expiresAt: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
97
+ })
98
+ client.setToken(token)
99
+ return {
100
+ status: 'LINKED',
101
+ token,
102
+ note: 'Token cached in ~/.reclaim/config.json and active now.',
103
+ }
104
+ }
105
+
106
+ return {
107
+ status: 'LINKED_NO_TOKEN',
108
+ hint:
109
+ 'Session confirmed but no token issued. ' +
110
+ 'Restart the backend (npm run dev) and try again.',
111
+ }
112
+ }
113
+
114
+ if(session.status === 'EXPIRED') {
115
+ return {
116
+ status: 'EXPIRED',
117
+ hint:
118
+ 'Session expired (15-min window passed). ' +
119
+ 'Call authenticate without a code to start a new session.',
120
+ }
121
+ }
122
+
123
+ // Still PENDING
124
+ return {
125
+ status: 'PENDING',
126
+ code: session.code,
127
+ attachUrl: session.attachUrl,
128
+ hint:
129
+ 'Session is still waiting. ' +
130
+ `Make sure you opened ${session.attachUrl}, ` +
131
+ 'signed in, and clicked "Authorize this device". ' +
132
+ `Call authenticate again with code: "${session.code}"` +
133
+ ' once you have done so.',
134
+ }
135
+ }
@@ -0,0 +1,37 @@
1
+ import type { ReclaimClient } from '@reclaimprotocol/client'
2
+ import type { RegisteredTool } from '../../server.ts'
3
+ import { authenticateTool } from './authenticate.ts'
4
+ import { importTool } from './import.ts'
5
+ import { issueTool } from './issue.ts'
6
+ import { listCachedTool } from './list-cached.ts'
7
+ import { resolveOwnerKeyTool } from './resolve-owner-key.ts'
8
+ import { statusTool } from './status.ts'
9
+
10
+ /**
11
+ * Local-only credential tools — no backend client needed. These manage app
12
+ * key-pairs entirely on disk / in the env (mint, import, list, resolve which
13
+ * key to sign proofs with), so they are useful in BOTH builder and
14
+ * OLD-devtools mode.
15
+ */
16
+ export function buildLocalCredentialTools(): RegisteredTool[] {
17
+ return [
18
+ listCachedTool(),
19
+ resolveOwnerKeyTool(),
20
+ issueTool(),
21
+ importTool(),
22
+ ]
23
+ }
24
+
25
+ /**
26
+ * Full builder-mode credential set: the local tools plus the two that talk to
27
+ * the builder backend (`reclaim_authenticate` device-code flow,
28
+ * `reclaim_status`). Old mode does NOT use these — it has its own
29
+ * authenticate and no credential-status analog.
30
+ */
31
+ export function buildCredentialTools(client: ReclaimClient): RegisteredTool[] {
32
+ return [
33
+ authenticateTool(client),
34
+ statusTool(client),
35
+ ...buildLocalCredentialTools(),
36
+ ]
37
+ }