@reclaimprotocol/agent 0.1.0 → 0.1.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 (204) hide show
  1. package/README.md +1 -1
  2. package/lib/cdp/capture-engine.d.ts +66 -0
  3. package/lib/cdp/capture-engine.js +153 -0
  4. package/lib/cdp/capture.d.ts +9 -20
  5. package/lib/cdp/capture.js +14 -138
  6. package/lib/cdp/index.d.ts +3 -0
  7. package/lib/cdp/index.js +6 -0
  8. package/lib/config/store.d.ts +7 -6
  9. package/lib/config/store.js +2 -2
  10. package/lib/index.d.ts +2 -2
  11. package/lib/index.js +1 -1
  12. package/lib/mcp/api/operations.gen.d.ts +101 -45
  13. package/lib/mcp/api/operations.gen.js +996 -126
  14. package/lib/mcp/api/to-zod.d.ts +24 -0
  15. package/lib/mcp/api/to-zod.js +94 -0
  16. package/lib/mcp/api/tools.js +1 -1
  17. package/lib/mcp/credentials/authenticate.d.ts +3 -0
  18. package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
  19. package/lib/mcp/credentials/build.d.ts +5 -0
  20. package/lib/mcp/credentials/build.js +18 -0
  21. package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
  22. package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
  23. package/lib/mcp/credentials/import.d.ts +2 -0
  24. package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
  25. package/lib/mcp/credentials/issue.d.ts +2 -0
  26. package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
  27. package/lib/mcp/credentials/keypair.d.ts +17 -0
  28. package/lib/mcp/credentials/keypair.js +48 -0
  29. package/lib/mcp/credentials/link.d.ts +3 -0
  30. package/lib/mcp/credentials/link.js +107 -0
  31. package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
  32. package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
  33. package/lib/mcp/credentials/owner-key.d.ts +23 -0
  34. package/lib/mcp/credentials/owner-key.js +58 -0
  35. package/lib/mcp/credentials/paths.d.ts +12 -0
  36. package/lib/mcp/credentials/paths.js +35 -0
  37. package/lib/mcp/credentials/poll.d.ts +27 -0
  38. package/lib/mcp/credentials/poll.js +61 -0
  39. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
  40. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
  41. package/lib/mcp/credentials/session-cache.d.ts +14 -0
  42. package/lib/mcp/credentials/session-cache.js +42 -0
  43. package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
  44. package/lib/mcp/credentials/status.js +76 -0
  45. package/lib/mcp/instructions.d.ts +1 -0
  46. package/lib/mcp/instructions.js +11 -0
  47. package/lib/mcp/register.d.ts +38 -0
  48. package/lib/mcp/register.js +80 -0
  49. package/lib/mcp/server.d.ts +2 -2
  50. package/lib/mcp/server.js +3 -3
  51. package/lib/mcp/start.js +10 -4
  52. package/lib/mcp/tools/agent/build.js +2 -15
  53. package/lib/mcp/tools/agent/index.d.ts +2 -0
  54. package/lib/mcp/tools/agent/index.js +3 -0
  55. package/lib/mcp/tools/agent/proof.js +1 -6
  56. package/lib/mcp/tools/agent/propose.js +13 -2
  57. package/lib/mcp/tools/agent/publish.js +10 -2
  58. package/lib/mcp/tools/agent/replay.js +0 -5
  59. package/lib/mcp/tools/analyze.d.ts +3 -0
  60. package/lib/mcp/tools/analyze.js +26 -0
  61. package/lib/mcp/tools/attach.d.ts +11 -0
  62. package/lib/mcp/tools/attach.js +72 -0
  63. package/lib/mcp/tools/authenticate/authenticate.js +107 -0
  64. package/lib/mcp/tools/authenticate/build.d.ts +15 -0
  65. package/lib/mcp/tools/authenticate/build.js +28 -0
  66. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +32 -0
  67. package/lib/mcp/tools/authenticate/decrypt-key.js +49 -0
  68. package/lib/mcp/tools/authenticate/eth-key.d.ts +38 -0
  69. package/lib/mcp/tools/authenticate/eth-key.js +130 -0
  70. package/lib/mcp/tools/authenticate/import.js +57 -0
  71. package/lib/mcp/tools/authenticate/index.d.ts +2 -0
  72. package/lib/mcp/tools/authenticate/index.js +4 -0
  73. package/lib/mcp/tools/authenticate/issue.js +42 -0
  74. package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
  75. package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
  76. package/lib/mcp/tools/capture.d.ts +10 -0
  77. package/lib/mcp/tools/capture.js +142 -0
  78. package/lib/mcp/tools/eval.d.ts +5 -0
  79. package/lib/mcp/tools/eval.js +127 -0
  80. package/lib/mcp/tools/find.d.ts +3 -0
  81. package/lib/mcp/tools/find.js +60 -0
  82. package/lib/mcp/tools/index.d.ts +3 -0
  83. package/lib/mcp/tools/index.js +6 -0
  84. package/lib/mcp/tools/owner-key.d.ts +2 -0
  85. package/lib/mcp/tools/owner-key.js +63 -0
  86. package/lib/mcp/tools/proof.d.ts +3 -0
  87. package/lib/mcp/tools/proof.js +214 -0
  88. package/lib/mcp/tools/propose.d.ts +9 -0
  89. package/lib/mcp/tools/propose.js +75 -0
  90. package/lib/mcp/tools/publish.d.ts +3 -0
  91. package/lib/mcp/tools/publish.js +59 -0
  92. package/lib/mcp/tools/replay.d.ts +3 -0
  93. package/lib/mcp/tools/replay.js +46 -0
  94. package/lib/mcp/tools/session.d.ts +10 -0
  95. package/lib/mcp/tools/session.js +29 -0
  96. package/lib/mcp/tools/verification/build.d.ts +12 -0
  97. package/lib/mcp/tools/verification/build.js +14 -0
  98. package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
  99. package/lib/mcp/tools/verification/diagnose.js +132 -0
  100. package/lib/mcp/tools/verification/index.d.ts +1 -0
  101. package/lib/mcp/tools/verification/index.js +2 -0
  102. package/lib/mcp/tools/verification/result.d.ts +12 -0
  103. package/lib/mcp/tools/verification/result.js +126 -0
  104. package/lib/mcp/verifications/build.d.ts +5 -0
  105. package/lib/mcp/verifications/build.js +14 -0
  106. package/lib/mcp/verifications/create.d.ts +3 -0
  107. package/lib/mcp/verifications/create.js +57 -0
  108. package/lib/mcp/verifications/find.d.ts +3 -0
  109. package/lib/mcp/verifications/find.js +37 -0
  110. package/lib/mcp/verifications/get.d.ts +3 -0
  111. package/lib/mcp/verifications/get.js +26 -0
  112. package/lib/mcp/verifications/update.d.ts +3 -0
  113. package/lib/mcp/verifications/update.js +78 -0
  114. package/lib/old/client.d.ts +3 -0
  115. package/lib/old/instructions.d.ts +0 -6
  116. package/lib/old/instructions.js +6 -24
  117. package/lib/old/to-register.d.ts +3 -0
  118. package/lib/old/to-register.js +35 -7
  119. package/lib/old/tools/publish.js +13 -1
  120. package/lib/proof/attestor.d.ts +33 -1
  121. package/lib/proof/attestor.js +67 -61
  122. package/lib/proof/index.d.ts +5 -0
  123. package/lib/proof/index.js +6 -0
  124. package/lib/proof/proof-core.d.ts +35 -0
  125. package/lib/proof/proof-core.js +73 -0
  126. package/lib/proof/zk-files.d.ts +14 -0
  127. package/lib/proof/zk-files.js +73 -0
  128. package/lib/provider/index.d.ts +5 -0
  129. package/lib/provider/index.js +9 -0
  130. package/lib/provider/to-version.d.ts +9 -12
  131. package/lib/provider/to-version.js +38 -4
  132. package/lib/provider/verification-checks.d.ts +12 -0
  133. package/lib/provider/verification-checks.js +30 -0
  134. package/lib/routes.gen.d.ts +271 -0
  135. package/lib/routes.gen.js +73 -0
  136. package/lib/types/openapi.gen.d.ts +3967 -0
  137. package/lib/types/openapi.gen.js +5 -0
  138. package/package.json +18 -10
  139. package/skill/SKILL.md +35 -14
  140. package/skill/builder-mode.md +9 -0
  141. package/skill/old-mode.md +13 -0
  142. package/src/cdp/capture-engine.ts +256 -0
  143. package/src/cdp/capture.ts +25 -185
  144. package/src/cdp/index.ts +6 -0
  145. package/src/config/store.ts +7 -6
  146. package/src/index.ts +2 -4
  147. package/src/mcp/api/operations.gen.ts +1023 -121
  148. package/src/mcp/api/to-zod.ts +115 -0
  149. package/src/mcp/api/tools.ts +9 -3
  150. package/src/mcp/instructions.ts +15 -0
  151. package/src/mcp/server.ts +5 -4
  152. package/src/mcp/start.ts +14 -4
  153. package/src/mcp/tools/agent/build.ts +2 -18
  154. package/src/mcp/tools/agent/index.ts +4 -0
  155. package/src/mcp/tools/agent/proof.ts +1 -7
  156. package/src/mcp/tools/agent/propose.ts +13 -2
  157. package/src/mcp/tools/agent/publish.ts +10 -2
  158. package/src/mcp/tools/agent/replay.ts +0 -5
  159. package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
  160. package/src/mcp/tools/authenticate/build.ts +32 -0
  161. package/src/mcp/tools/authenticate/decrypt-key.ts +64 -0
  162. package/src/mcp/tools/authenticate/eth-key.ts +182 -0
  163. package/src/mcp/tools/authenticate/import.ts +73 -0
  164. package/src/mcp/tools/authenticate/index.ts +4 -0
  165. package/src/mcp/tools/authenticate/issue.ts +50 -0
  166. package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
  167. package/src/mcp/tools/index.ts +6 -0
  168. package/src/mcp/tools/verification/build.ts +16 -0
  169. package/src/mcp/tools/verification/index.ts +2 -0
  170. package/src/mcp/tools/verification/result.ts +155 -0
  171. package/src/old/client.ts +3 -0
  172. package/src/old/instructions.ts +10 -25
  173. package/src/old/to-register.ts +62 -27
  174. package/src/old/tools/publish.ts +17 -1
  175. package/src/proof/attestor.ts +115 -69
  176. package/src/proof/index.ts +21 -0
  177. package/src/proof/proof-core.ts +109 -0
  178. package/src/proof/zk-files.ts +83 -0
  179. package/src/provider/index.ts +20 -0
  180. package/src/provider/to-version.ts +52 -11
  181. package/src/provider/verification-checks.ts +36 -0
  182. package/lib/mcp/tools/credentials/build.d.ts +0 -16
  183. package/lib/mcp/tools/credentials/build.js +0 -33
  184. package/lib/mcp/tools/credentials/status.js +0 -62
  185. package/src/mcp/tools/credentials/build.ts +0 -37
  186. package/src/mcp/tools/credentials/cache.ts +0 -161
  187. package/src/mcp/tools/credentials/env-file.ts +0 -101
  188. package/src/mcp/tools/credentials/import.ts +0 -72
  189. package/src/mcp/tools/credentials/issue.ts +0 -107
  190. package/src/mcp/tools/credentials/list-cached.ts +0 -66
  191. package/src/mcp/tools/credentials/register.ts +0 -49
  192. package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
  193. package/src/mcp/tools/credentials/status.ts +0 -82
  194. package/src/types/openapi.ts +0 -7
  195. /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
  196. /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
  197. /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
  198. /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
  199. /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
  200. /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
  201. /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
  202. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
  203. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
  204. /package/src/mcp/tools/{credentials → authenticate}/session-cache.ts +0 -0
@@ -1,9 +1,16 @@
1
- import type { ReclaimProvider } from '../provider/schema.ts'
1
+ import type { ReclaimProvider, ResponseRedaction } from '../provider/schema.ts'
2
2
  import type {
3
3
  RegisterProviderBody,
4
4
  UpdateProviderConfigBody,
5
5
  } from './client.ts'
6
6
 
7
+ /** Default web settings, mirroring the builder openapi defaults
8
+ * (`ProviderWebSettings.geoLocation`, `PortalClientOptions.useProxy`). The old
9
+ * backend accepts both: `geoLocation` lives on the providerConfig, `useProxy`
10
+ * is a Provider-level flag (register only). */
11
+ const DEFAULT_GEO_LOCATION = '{{DYNAMIC_GEO}}'
12
+ const DEFAULT_USE_PROXY = true
13
+
7
14
  /**
8
15
  * Translate a drafted `ReclaimProvider` (from `propose_provider`) into the
9
16
  * OLD devtools backend shapes. Deliberately a SEPARATE module from
@@ -22,7 +29,10 @@ import type {
22
29
  export interface OldProviderOpts {
23
30
  initialUrl: string
24
31
  description?: string
32
+ /** Defaults to `{{DYNAMIC_GEO}}` when omitted. */
25
33
  geoLocation?: string
34
+ /** Defaults to `true` when omitted. Register only (Provider-level flag). */
35
+ useProxy?: boolean
26
36
  providerType?: 'PRIVATE' | 'PUBLIC'
27
37
  }
28
38
 
@@ -63,6 +73,20 @@ function requestDataFromProvider(provider: ReclaimProvider): OldRequestData {
63
73
  )
64
74
  }
65
75
 
76
+ // The old backend has no dotted interpolation: consumer-supplied params
77
+ // must use the `{{context_<name>}}` form here, not the builder's
78
+ // `{{context.<name>}}`. Catch the mix-up at publish with a clear error
79
+ // instead of a template that silently never resolves.
80
+ const dotted = `${provider.url}\n${provider.body ?? ''}`
81
+ .match(/\{\{context\.[A-Za-z_][A-Za-z0-9_]*\}\}/)
82
+ if(dotted) {
83
+ throw new Error(
84
+ `Old devtools does not support ${dotted[0]} — rename ` +
85
+ 'consumer-supplied params to the {{context_<name>}} form ' +
86
+ 'for this backend.',
87
+ )
88
+ }
89
+
66
90
  const responseMatches: OldResponseMatch[] = provider.responseMatches.map(
67
91
  (m, i) => ({
68
92
  value: m.value,
@@ -72,29 +96,40 @@ function requestDataFromProvider(provider: ReclaimProvider): OldRequestData {
72
96
  }),
73
97
  )
74
98
 
75
- const responseRedactions: OldResponseRedaction[] = (
76
- provider.responseRedactions ?? []
77
- ).map((r, i) => {
78
- const out: OldResponseRedaction = { order: i }
79
- if(r.xPath !== undefined) {
80
- out.xPath = r.xPath
81
- }
99
+ // Old devtools wants `responseMatches` and `responseRedactions` as PARALLEL
100
+ // arrays. A drafted provider may instead carry each redaction nested in its
101
+ // match's `extract` (the builder shape). Prefer the explicit parallel array;
102
+ // fall back to the nested `extract`s so redactions are never dropped.
103
+ const nestedRedactions = provider.responseMatches
104
+ .map((m) => (m as { extract?: ResponseRedaction }).extract)
105
+ .filter((e): e is ResponseRedaction => Boolean(e))
106
+ const sourceRedactions = provider.responseRedactions?.length
107
+ ? provider.responseRedactions
108
+ : nestedRedactions
82
109
 
83
- if(r.jsonPath !== undefined) {
84
- out.jsonPath = r.jsonPath
85
- }
110
+ const responseRedactions: OldResponseRedaction[] = sourceRedactions.map(
111
+ (r, i) => {
112
+ const out: OldResponseRedaction = { order: i }
113
+ if(r.xPath !== undefined) {
114
+ out.xPath = r.xPath
115
+ }
86
116
 
87
- if(r.regex !== undefined) {
88
- out.regex = r.regex
89
- }
117
+ if(r.jsonPath !== undefined) {
118
+ out.jsonPath = r.jsonPath
119
+ }
90
120
 
91
- // OPRF redactions are supported — pass the mode through unchanged.
92
- if(r.hash !== undefined) {
93
- out.hash = r.hash
94
- }
121
+ if(r.regex !== undefined) {
122
+ out.regex = r.regex
123
+ }
95
124
 
96
- return out
97
- })
125
+ // OPRF redactions are supported — pass the mode through unchanged.
126
+ if(r.hash !== undefined) {
127
+ out.hash = r.hash
128
+ }
129
+
130
+ return out
131
+ },
132
+ )
98
133
 
99
134
  const requestData: OldRequestData = {
100
135
  url: provider.url,
@@ -127,16 +162,15 @@ export function providerToRegisterBody(
127
162
  name: provider.name,
128
163
  loginUrl: opts.initialUrl,
129
164
  providerType: opts.providerType ?? 'PRIVATE',
165
+ // Web-settings defaults mirror the builder openapi defaults.
166
+ geoLocation: opts.geoLocation ?? DEFAULT_GEO_LOCATION,
167
+ useProxy: opts.useProxy ?? DEFAULT_USE_PROXY,
130
168
  requestData: [requestDataFromProvider(provider)],
131
169
  }
132
170
  if(opts.description !== undefined) {
133
171
  body.description = opts.description
134
172
  }
135
173
 
136
- if(opts.geoLocation !== undefined) {
137
- body.geoLocation = opts.geoLocation
138
- }
139
-
140
174
  return body
141
175
  }
142
176
 
@@ -149,13 +183,14 @@ export function providerToConfigBody(
149
183
  version: string,
150
184
  versionInfo: string,
151
185
  ): UpdateProviderConfigBody {
186
+ // `useProxy` is a Provider-level flag on the old backend, not part of a
187
+ // version's providerConfig — so the config (new-version) path only carries
188
+ // `geoLocation`. It defaults the same way as register.
152
189
  const providerConfig: Record<string, unknown> = {
153
190
  loginUrl: opts.initialUrl,
191
+ geoLocation: opts.geoLocation ?? DEFAULT_GEO_LOCATION,
154
192
  requestData: [requestDataFromProvider(provider)],
155
193
  }
156
- if(opts.geoLocation !== undefined) {
157
- providerConfig.geoLocation = opts.geoLocation
158
- }
159
194
 
160
195
  return { providerConfig, version, versionInfo }
161
196
  }
@@ -33,6 +33,7 @@ export function createVersionFromCaptureTool(
33
33
  providerId?: string
34
34
  description?: string
35
35
  geoLocation?: string
36
+ useProxy?: boolean
36
37
  providerType?: 'PRIVATE' | 'PUBLIC'
37
38
  notes?: string
38
39
  }>(
@@ -83,7 +84,18 @@ export function createVersionFromCaptureTool(
83
84
  type: 'string',
84
85
  description: 'Provider description (new-provider register only).',
85
86
  },
86
- geoLocation: { type: 'string' },
87
+ geoLocation: {
88
+ type: 'string',
89
+ description:
90
+ 'Geo the verification appears to originate from. Defaults to ' +
91
+ "'{{DYNAMIC_GEO}}' (the user's geo) when omitted.",
92
+ },
93
+ useProxy: {
94
+ type: 'boolean',
95
+ description:
96
+ 'Route portal/attestation traffic through a proxy. Defaults to ' +
97
+ 'true when omitted. Applied on register only.',
98
+ },
87
99
  providerType: { type: 'string', enum: ['PRIVATE', 'PUBLIC'] },
88
100
  notes: {
89
101
  type: 'string',
@@ -105,6 +117,10 @@ export function createVersionFromCaptureTool(
105
117
  opts.geoLocation = args.geoLocation
106
118
  }
107
119
 
120
+ if(args.useProxy !== undefined) {
121
+ opts.useProxy = args.useProxy
122
+ }
123
+
108
124
  if(args.providerType !== undefined) {
109
125
  opts.providerType = args.providerType
110
126
  }
@@ -6,11 +6,16 @@
6
6
  // revealed slice; jsonPath/xPath live on responseRedactions (what the prover
7
7
  // reveals), never on responseMatches.
8
8
 
9
+ import { bytesToHex } from '@noble/hashes/utils.js'
9
10
  import {
10
- createClaimOnAttestor, type Logger, type ProviderParams,
11
+ createClaimOnAttestor, type Logger,
11
12
  } from '@reclaimprotocol/attestor-core'
12
- import { resolveOwnerPrivateKey } from '../mcp/tools/credentials/cache.ts'
13
+ import { resolveEthKey } from '../mcp/tools/authenticate/eth-key.ts'
13
14
  import type { ReclaimProvider } from '../provider/schema.ts'
15
+ import {
16
+ buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl,
17
+ } from './proof-core.ts'
18
+ import { ensureZkFiles } from './zk-files.ts'
14
19
 
15
20
  export interface AttestorLogEntry {
16
21
  level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'
@@ -18,6 +23,37 @@ export interface AttestorLogEntry {
18
23
  ctx?: Record<string, unknown>
19
24
  }
20
25
 
26
+ /** The attestor's signed claim data + signature, for building the nested proof
27
+ * JWS a Consumer verifies against the attestor's key. Populated when
28
+ * `includeAttestorClaim` is set (the app's verification runner needs it; the
29
+ * MCP `run_proof` tool ignores it). */
30
+ export interface AttestorClaim {
31
+ /** ProviderClaimData: provider, parameters, context (JSON string), owner,
32
+ * timestampS, epoch, identifier. */
33
+ claim: Record<string, unknown>
34
+ /** Hex (0x) attestor ETH address that signed the claim (the nested `kid`). */
35
+ attestorAddress: string
36
+ /** Hex (0x) attestor claim signature (EIP-191 over the claim sign-data). */
37
+ claimSignature: string
38
+ }
39
+
40
+ /** Options for {@link runAttestorProof}. */
41
+ export interface RunAttestorProofOptions {
42
+ attestorUrl?: string
43
+ /** When resolving the key from env/.env/file, the address the caller expects
44
+ * to sign with (sanity check). Ignored when `ownerPrivateKey` is given. */
45
+ ownerAddress?: string
46
+ /** Sign with this key directly (e.g. the app/VC's ephemeral per-session key)
47
+ * instead of resolving from env/.env/file. */
48
+ ownerPrivateKey?: string
49
+ /** ZK engine for `createClaimOnAttestor`. Defaults to `'gnark'`; the app's
50
+ * server-side verification runner uses `'snarkjs'`. */
51
+ zkEngine?: 'gnark' | 'snarkjs'
52
+ /** Extract the structured {@link AttestorClaim} (claim + attestor signature)
53
+ * onto the result for nested-proof construction. */
54
+ includeAttestorClaim?: boolean
55
+ }
56
+
21
57
  export interface ProofResult {
22
58
  proof: unknown
23
59
  extractedValue: string
@@ -26,6 +62,9 @@ export interface ProofResult {
26
62
  owner?: string
27
63
  verified: boolean
28
64
  error?: string
65
+ /** Structured claim + signature for nested-proof verification. Set only when
66
+ * `includeAttestorClaim` was requested and the claim carries signatures. */
67
+ attestorClaim?: AttestorClaim
29
68
  /** Set to `'missing-owner-key'` when no credential was available to
30
69
  * sign with — distinguishes this recoverable case from other proof
31
70
  * errors so the skill can point the dev at `issue_credentials` /
@@ -138,16 +177,6 @@ function makeCaptureLogger(): {
138
177
  return { logger: make({}), entries }
139
178
  }
140
179
 
141
- /** Default public attestor websocket URL. Resolved per-call so an env-var
142
- * change or per-request override takes effect without restarting the
143
- * MCP server. */
144
- const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws'
145
- function resolveAttestorUrl(override?: string): string {
146
- return (
147
- override ?? process.env['RECLAIM_ATTESTOR_URL'] ?? FALLBACK_ATTESTOR_URL
148
- )
149
- }
150
-
151
180
  // ---------------------------------------------------------------------------
152
181
  // Main entry point
153
182
  // ---------------------------------------------------------------------------
@@ -155,41 +184,60 @@ function resolveAttestorUrl(override?: string): string {
155
184
  export async function runAttestorProof(
156
185
  provider: ReclaimProvider,
157
186
  secrets: Record<string, string>,
158
- attestorUrl?: string,
159
- ownerAddress?: string,
187
+ opts: RunAttestorProofOptions = {},
160
188
  ): Promise<ProofResult> {
161
- // The owner is chosen up front (resolve_owner_key) and passed as
162
- // `ownerAddress`; here we just fetch its private key and sign.
163
- const ownerPrivateKey = ownerAddress
164
- ? resolveOwnerPrivateKey(ownerAddress)
165
- : undefined
166
- if(!ownerPrivateKey) {
167
- return {
168
- proof: undefined,
169
- extractedValue: '',
170
- extractedParameters: {},
171
- verified: false,
172
- error:
173
- `No private key available for ownerAddress "${ownerAddress ?? ''}". ` +
174
- 'Call resolve_owner_key to pick a credential (or ' +
175
- 'issue_credentials / import_credentials to set one up), then ' +
176
- 'retry run_proof with its address as ownerAddress.',
177
- errorKind: 'missing-owner-key',
189
+ // Sign with the caller-supplied key (the app/VC's ephemeral per-session
190
+ // key) when given; otherwise resolve env/.env/file. `ownerAddress` is a
191
+ // sanity check on the resolved key (ignored when a key is supplied).
192
+ let ownerPrivateKey: string
193
+ if(opts.ownerPrivateKey) {
194
+ ownerPrivateKey = opts.ownerPrivateKey
195
+ } else {
196
+ const resolved = resolveEthKey()
197
+ if(!resolved) {
198
+ return {
199
+ proof: undefined,
200
+ extractedValue: '',
201
+ extractedParameters: {},
202
+ verified: false,
203
+ error:
204
+ 'No eth proof-owner key available (no RECLAIM_PRIVATE_KEY in env, ' +
205
+ '.env, or RECLAIM_PRIVATE_KEY_FILE). Call resolve_owner_key, then ' +
206
+ 'issue_credentials (generate) or import_credentials (provide one), ' +
207
+ 'and retry run_proof.',
208
+ errorKind: 'missing-owner-key',
209
+ }
178
210
  }
211
+
212
+ if(
213
+ opts.ownerAddress
214
+ && opts.ownerAddress.toLowerCase() !== resolved.address
215
+ ) {
216
+ return {
217
+ proof: undefined,
218
+ extractedValue: '',
219
+ extractedParameters: {},
220
+ verified: false,
221
+ error:
222
+ `Requested ownerAddress "${opts.ownerAddress}" does not match ` +
223
+ `the resolved eth key (${resolved.address}, source: ` +
224
+ `${resolved.source}). ` +
225
+ 'Call resolve_owner_key to get the current address, or update the ' +
226
+ 'configured key.',
227
+ errorKind: 'missing-owner-key',
228
+ }
229
+ }
230
+
231
+ ownerPrivateKey = resolved.privateKey
179
232
  }
180
233
 
181
234
  try {
182
- const params: ProviderParams<'http'> = {
183
- url: provider.url,
184
- method: provider.method,
185
- ...(provider.headers !== undefined && { headers: provider.headers }),
186
- ...(provider.body !== undefined && { body: provider.body }),
187
- responseMatches: provider.responseMatches,
188
- responseRedactions: provider.responseRedactions ?? [],
189
- ...(provider.paramValues !== undefined && {
190
- paramValues: provider.paramValues,
191
- }),
192
- }
235
+ // Init the TLS crypto backend (idempotent), then fetch ZK circuits
236
+ // lazily on the first proof (not at install time).
237
+ ensureAttestorCrypto()
238
+ await ensureZkFiles()
239
+
240
+ const params = buildHttpParams(provider)
193
241
 
194
242
  // Diagnostic logger capture is gated behind RECLAIM_AGENT_TRACE so the
195
243
  // happy path doesn't pay the cost (the attestor SDK emits a *lot* of
@@ -209,8 +257,8 @@ export async function runAttestorProof(
209
257
  headers: secrets,
210
258
  },
211
259
  ownerPrivateKey,
212
- client: { url: resolveAttestorUrl(attestorUrl) },
213
- zkEngine: 'snarkjs',
260
+ client: { url: resolveAttestorUrl(opts.attestorUrl) },
261
+ zkEngine: opts.zkEngine ?? 'gnark',
214
262
  ...(capture !== undefined && { logger: capture.logger as Logger }),
215
263
  })
216
264
  } catch(err) {
@@ -229,27 +277,8 @@ export async function runAttestorProof(
229
277
  return errorResult
230
278
  }
231
279
 
232
- // ClaimTunnelResponse carries the claim under `.claim`.
233
- const claimRecord = claim as Record<string, unknown>
234
- const claimData =
235
- claimRecord['claim'] as Record<string, unknown> | undefined
236
-
237
- // claimData.context is a JSON-encoded string carrying
238
- // { extractedParameters: {...}, providerHash, ... }.
239
- let context: Record<string, unknown> = {}
240
- const rawContext = claimData?.['context']
241
- if(typeof rawContext === 'string') {
242
- try {
243
- context = JSON.parse(rawContext) as Record<string, unknown>
244
- } catch{
245
- // Leave context empty if it isn't valid JSON.
246
- }
247
- }
248
-
249
- const extractedParameters =
250
- (context['extractedParameters'] as Record<string, string> | undefined) ??
251
- {}
252
- const extractedValue = Object.values(extractedParameters)[0] ?? ''
280
+ const { extractedValue, extractedParameters, identifier, owner, claimData }
281
+ = parseClaim(claim)
253
282
 
254
283
  const result: ProofResult = {
255
284
  proof: claim,
@@ -257,16 +286,33 @@ export async function runAttestorProof(
257
286
  extractedParameters,
258
287
  verified: true,
259
288
  }
260
- const identifier = claimData?.['identifier']
261
- if(typeof identifier === 'string') {
289
+ if(identifier !== undefined) {
262
290
  result.identifier = identifier
263
291
  }
264
292
 
265
- const owner = claimData?.['owner']
266
- if(typeof owner === 'string') {
293
+ if(owner !== undefined) {
267
294
  result.owner = owner
268
295
  }
269
296
 
297
+ // Structured claim + signature so the result JWS can carry a nested,
298
+ // attestor-signed proof the Consumer verifies independently.
299
+ if(opts.includeAttestorClaim) {
300
+ const sigs = (claim as {
301
+ signatures?: { attestorAddress?: unknown, claimSignature?: unknown }
302
+ }).signatures
303
+ if(
304
+ claimData
305
+ && typeof sigs?.attestorAddress === 'string'
306
+ && sigs.claimSignature instanceof Uint8Array
307
+ ) {
308
+ result.attestorClaim = {
309
+ claim: claimData,
310
+ attestorAddress: sigs.attestorAddress,
311
+ claimSignature: '0x' + bytesToHex(sigs.claimSignature),
312
+ }
313
+ }
314
+ }
315
+
270
316
  // Only set when RECLAIM_AGENT_TRACE=1. Full stream — the MCP layer
271
317
  // trims for the LLM-visible response; the on-disk dump gets it all.
272
318
  if(attestorLogs !== undefined && attestorLogs.length > 0) {
@@ -0,0 +1,21 @@
1
+ // Proof primitives shared with the app's verification runner: the attestor
2
+ // param/claim plumbing, the ZK-file bootstrap, and the agent's own
3
+ // `runAttestorProof`. Exposed as `@reclaimprotocol/agent/proof`.
4
+ export type {
5
+ AttestorClaim,
6
+ AttestorLogEntry,
7
+ ProofResult,
8
+ RunAttestorProofOptions,
9
+ } from './attestor.ts'
10
+ export { runAttestorProof, tailLogs, warnsAndErrors } from './attestor.ts'
11
+ export {
12
+ buildHttpParams,
13
+ ensureAttestorCrypto,
14
+ type ParsedClaim,
15
+ parseClaim,
16
+ resolveAttestorUrl,
17
+ } from './proof-core.ts'
18
+ export { ensureZkFiles } from './zk-files.ts'
19
+ // Re-exported attestor-core claim primitive so the agent is the single
20
+ // integration point with @reclaimprotocol/attestor-core across the builder.
21
+ export { createClaimOnAttestor } from '@reclaimprotocol/attestor-core'
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Shared attestor-claim plumbing used by both proof callers: the agent's MCP
3
+ * `run_proof` (which resolves the owner key from the credential cache and
4
+ * captures trace logs) and the app's verification runner (which signs with an
5
+ * ephemeral per-session key and builds a nested-proof claim). Only those
6
+ * concerns differ — building the HTTP params, resolving the attestor URL, and
7
+ * parsing the claim response are identical, so they live here.
8
+ */
9
+ import type { ProviderParams } from '@reclaimprotocol/attestor-core'
10
+ import { setCryptoImplementation } from '@reclaimprotocol/tls'
11
+ import { webcryptoCrypto } from '@reclaimprotocol/tls/webcrypto'
12
+ import type { ReclaimProvider } from '../provider/schema.ts'
13
+
14
+ const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws'
15
+
16
+ let cryptoInitialized = false
17
+
18
+ /** One-time crypto backend init for `@reclaimprotocol/tls` (idempotent). The
19
+ * package ships with an empty `crypto = {}` and defers the backend to the
20
+ * caller; without this the first proof dies inside the TLS client with
21
+ * `crypto.randomBytes is not a function`. Shared by every attestor caller —
22
+ * the agent MCP `run_proof` tool and the app's verification runner. */
23
+ export function ensureAttestorCrypto(): void {
24
+ if(cryptoInitialized) {
25
+ return
26
+ }
27
+
28
+ setCryptoImplementation(webcryptoCrypto)
29
+ cryptoInitialized = true
30
+ }
31
+
32
+ /** Resolve the attestor websocket URL: explicit override → env → public
33
+ * default. Resolved per-call so an env change or per-request override takes
34
+ * effect without restarting. */
35
+ export function resolveAttestorUrl(override?: string): string {
36
+ return (
37
+ override ?? process.env['RECLAIM_ATTESTOR_URL'] ?? FALLBACK_ATTESTOR_URL
38
+ )
39
+ }
40
+
41
+ /** Map a verification's provider recipe straight to the attestor's HTTP
42
+ * params — never synthesized from captured traffic. */
43
+ export function buildHttpParams(
44
+ provider: ReclaimProvider,
45
+ ): ProviderParams<'http'> {
46
+ return {
47
+ url: provider.url,
48
+ method: provider.method,
49
+ ...(provider.headers !== undefined && { headers: provider.headers }),
50
+ ...(provider.body !== undefined && { body: provider.body }),
51
+ responseMatches: provider.responseMatches,
52
+ responseRedactions: provider.responseRedactions ?? [],
53
+ ...(provider.paramValues !== undefined && {
54
+ paramValues: provider.paramValues,
55
+ }),
56
+ }
57
+ }
58
+
59
+ export interface ParsedClaim {
60
+ /** ProviderClaimData: provider, parameters, context, owner, identifier, … */
61
+ claimData?: Record<string, unknown>
62
+ extractedParameters: Record<string, string>
63
+ extractedValue: string
64
+ identifier?: string
65
+ owner?: string
66
+ }
67
+
68
+ /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
69
+ * owner) out of a `createClaimOnAttestor` result. The full result still
70
+ * carries signatures the caller may need for nested-proof building. */
71
+ export function parseClaim(claim: unknown): ParsedClaim {
72
+ const claimRecord = claim as Record<string, unknown>
73
+ const claimData = claimRecord['claim'] as Record<string, unknown> | undefined
74
+
75
+ // claimData.context is a JSON-encoded string carrying
76
+ // { extractedParameters: {...}, providerHash, ... }.
77
+ let context: Record<string, unknown> = {}
78
+ const rawContext = claimData?.['context']
79
+ if(typeof rawContext === 'string') {
80
+ try {
81
+ context = JSON.parse(rawContext) as Record<string, unknown>
82
+ } catch{
83
+ // Leave context empty if it isn't valid JSON.
84
+ }
85
+ }
86
+
87
+ const extractedParameters =
88
+ (context['extractedParameters'] as Record<string, string> | undefined) ?? {}
89
+
90
+ const parsed: ParsedClaim = {
91
+ extractedParameters,
92
+ extractedValue: Object.values(extractedParameters)[0] ?? '',
93
+ }
94
+ if(claimData) {
95
+ parsed.claimData = claimData
96
+ }
97
+
98
+ const identifier = claimData?.['identifier']
99
+ if(typeof identifier === 'string') {
100
+ parsed.identifier = identifier
101
+ }
102
+
103
+ const owner = claimData?.['owner']
104
+ if(typeof owner === 'string') {
105
+ parsed.owner = owner
106
+ }
107
+
108
+ return parsed
109
+ }
@@ -0,0 +1,83 @@
1
+ // Lazy download of the @reclaimprotocol/zk-symmetric-crypto circuit resources
2
+ // that the snarkjs proof engine needs. These are ~280MB and are NOT fetched in
3
+ // a `postinstall` (that blocked the MCP server's first launch past the connect
4
+ // timeout — see the install notes). Instead we fetch them on the first proof,
5
+ // once, so the server starts immediately and only `run_proof` pays the cost.
6
+
7
+ import { spawn } from 'node:child_process'
8
+ import { existsSync } from 'node:fs'
9
+ import { createRequire } from 'node:module'
10
+ import { dirname, join } from 'node:path'
11
+
12
+ const require = createRequire(import.meta.url)
13
+
14
+ let inFlight: Promise<void> | undefined
15
+
16
+ /**
17
+ * Ensure the zk circuit resources are on disk before a proof runs. Idempotent
18
+ * and concurrency-safe: the download runs at most once per process, and a
19
+ * failed attempt clears the latch so a later proof can retry.
20
+ *
21
+ * Resolves silently when the resources are already present (the warm npx cache,
22
+ * or after a manual `npm run download:zk-files`) and when
23
+ * `@reclaimprotocol/zk-symmetric-crypto` isn't installed at all (SDK-only
24
+ * consumers have nothing to prove).
25
+ *
26
+ * All output goes to stderr (fd 2): the MCP server speaks JSON-RPC over stdout,
27
+ * so a stray write to fd 1 mid-session would corrupt the protocol.
28
+ */
29
+ export function ensureZkFiles(): Promise<void> {
30
+ inFlight ??= download().catch((err) => {
31
+ inFlight = undefined
32
+ throw err
33
+ })
34
+ return inFlight
35
+ }
36
+
37
+ async function download(): Promise<void> {
38
+ let mainPath: string
39
+ try {
40
+ mainPath = require.resolve('@reclaimprotocol/zk-symmetric-crypto')
41
+ } catch{
42
+ return
43
+ }
44
+
45
+ // mainPath is <pkg>/lib/index.js → resources live at <pkg>/resources, the
46
+ // downloader at <pkg>/lib/scripts/download-files.js. We only use the snarkjs
47
+ // engine (createClaimOnAttestor zkEngine: 'snarkjs'), so its presence is the
48
+ // signal that the circuits are ready.
49
+ const libDir = dirname(mainPath)
50
+ const pkgDir = dirname(libDir)
51
+ if(existsSync(join(pkgDir, 'resources', 'snarkjs'))) {
52
+ return
53
+ }
54
+
55
+ const downloadScript = join(libDir, 'scripts', 'download-files.js')
56
+ if(!existsSync(downloadScript)) {
57
+ throw new Error(
58
+ 'zk circuit downloader not found in '
59
+ + '@reclaimprotocol/zk-symmetric-crypto; '
60
+ + 'run `npm run download:zk-files` and retry.',
61
+ )
62
+ }
63
+
64
+ process.stderr.write(
65
+ '[reclaim-agent] downloading zkTLS proof circuits '
66
+ + '(~280MB, one-time)…\n',
67
+ )
68
+ // stdio fd 1→2: send the downloader's stdout to our stderr so its progress
69
+ // never lands on the MCP server's JSON-RPC stdout.
70
+ await new Promise<void>((resolve, reject) => {
71
+ const child = spawn(process.execPath, [downloadScript], {
72
+ stdio: ['ignore', 2, 2],
73
+ })
74
+ child.on('error', reject)
75
+ child.on('exit', (code) => {
76
+ if(code === 0) {
77
+ resolve()
78
+ } else {
79
+ reject(new Error(`zk download exited ${code}`))
80
+ }
81
+ })
82
+ })
83
+ }
@@ -0,0 +1,20 @@
1
+ // Provider primitives shared with the app's capture pipeline:
2
+ // the attestor-derived recipe types, secret classification, and the GraphQL
3
+ // op parser. Exposed as `@reclaimprotocol/agent/provider`.
4
+ export { extractGraphqlOp, looksLikeGraphqlEndpoint } from './graphql.ts'
5
+ export type {
6
+ CapturedRequest,
7
+ HttpMethod,
8
+ OprfMode,
9
+ ReclaimProvider,
10
+ RequestConcern,
11
+ ResponseMatch,
12
+ ResponseRedaction,
13
+ SecretParamRef,
14
+ } from './schema.ts'
15
+ export { classifyHeader, splitSecrets } from './secrets.ts'
16
+ // Re-exported attestor-core provider primitives (the registry, the param hash,
17
+ // and the param type) so the builder imports them via the agent rather than
18
+ // depending on @reclaimprotocol/attestor-core directly.
19
+ export { hashProviderParams, providers } from '@reclaimprotocol/attestor-core'
20
+ export type { ProviderParams } from '@reclaimprotocol/attestor-core'