@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
@@ -5,8 +5,11 @@
5
5
  // responseMatches are the regex/contains checks the attestor runs on the
6
6
  // revealed slice; jsonPath/xPath live on responseRedactions (what the prover
7
7
  // reveals), never on responseMatches.
8
+ import { bytesToHex } from '@noble/hashes/utils.js';
8
9
  import { createClaimOnAttestor, } from '@reclaimprotocol/attestor-core';
9
- import { resolveOwnerPrivateKey } from "../mcp/tools/credentials/cache.js";
10
+ import { resolveEthKey } from "../mcp/tools/authenticate/eth-key.js";
11
+ import { buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl, } from "./proof-core.js";
12
+ import { ensureZkFiles } from "./zk-files.js";
10
13
  /** Trim a captured log stream to the last N entries — for shrinking the
11
14
  * LLM-visible response while the full stream stays in the on-disk dump. */
12
15
  export function tailLogs(entries, n = LOG_TAIL_LINES) {
@@ -79,47 +82,55 @@ function makeCaptureLogger() {
79
82
  };
80
83
  return { logger: make({}), entries };
81
84
  }
82
- /** Default public attestor websocket URL. Resolved per-call so an env-var
83
- * change or per-request override takes effect without restarting the
84
- * MCP server. */
85
- const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws';
86
- function resolveAttestorUrl(override) {
87
- return (override ?? process.env['RECLAIM_ATTESTOR_URL'] ?? FALLBACK_ATTESTOR_URL);
88
- }
89
85
  // ---------------------------------------------------------------------------
90
86
  // Main entry point
91
87
  // ---------------------------------------------------------------------------
92
- export async function runAttestorProof(provider, secrets, attestorUrl, ownerAddress) {
93
- // The owner is chosen up front (resolve_owner_key) and passed as
94
- // `ownerAddress`; here we just fetch its private key and sign.
95
- const ownerPrivateKey = ownerAddress
96
- ? resolveOwnerPrivateKey(ownerAddress)
97
- : undefined;
98
- if (!ownerPrivateKey) {
99
- return {
100
- proof: undefined,
101
- extractedValue: '',
102
- extractedParameters: {},
103
- verified: false,
104
- error: `No private key available for ownerAddress "${ownerAddress ?? ''}". ` +
105
- 'Call resolve_owner_key to pick a credential (or ' +
106
- 'issue_credentials / import_credentials to set one up), then ' +
107
- 'retry run_proof with its address as ownerAddress.',
108
- errorKind: 'missing-owner-key',
109
- };
88
+ export async function runAttestorProof(provider, secrets, opts = {}) {
89
+ // Sign with the caller-supplied key (the app/VC's ephemeral per-session
90
+ // key) when given; otherwise resolve env/.env/file. `ownerAddress` is a
91
+ // sanity check on the resolved key (ignored when a key is supplied).
92
+ let ownerPrivateKey;
93
+ if (opts.ownerPrivateKey) {
94
+ ownerPrivateKey = opts.ownerPrivateKey;
95
+ }
96
+ else {
97
+ const resolved = resolveEthKey();
98
+ if (!resolved) {
99
+ return {
100
+ proof: undefined,
101
+ extractedValue: '',
102
+ extractedParameters: {},
103
+ verified: false,
104
+ error: 'No eth proof-owner key available (no RECLAIM_PRIVATE_KEY in env, ' +
105
+ '.env, or RECLAIM_PRIVATE_KEY_FILE). Call resolve_owner_key, then ' +
106
+ 'issue_credentials (generate) or import_credentials (provide one), ' +
107
+ 'and retry run_proof.',
108
+ errorKind: 'missing-owner-key',
109
+ };
110
+ }
111
+ if (opts.ownerAddress
112
+ && opts.ownerAddress.toLowerCase() !== resolved.address) {
113
+ return {
114
+ proof: undefined,
115
+ extractedValue: '',
116
+ extractedParameters: {},
117
+ verified: false,
118
+ error: `Requested ownerAddress "${opts.ownerAddress}" does not match ` +
119
+ `the resolved eth key (${resolved.address}, source: ` +
120
+ `${resolved.source}). ` +
121
+ 'Call resolve_owner_key to get the current address, or update the ' +
122
+ 'configured key.',
123
+ errorKind: 'missing-owner-key',
124
+ };
125
+ }
126
+ ownerPrivateKey = resolved.privateKey;
110
127
  }
111
128
  try {
112
- const params = {
113
- url: provider.url,
114
- method: provider.method,
115
- ...(provider.headers !== undefined && { headers: provider.headers }),
116
- ...(provider.body !== undefined && { body: provider.body }),
117
- responseMatches: provider.responseMatches,
118
- responseRedactions: provider.responseRedactions ?? [],
119
- ...(provider.paramValues !== undefined && {
120
- paramValues: provider.paramValues,
121
- }),
122
- };
129
+ // Init the TLS crypto backend (idempotent), then fetch ZK circuits
130
+ // lazily on the first proof (not at install time).
131
+ ensureAttestorCrypto();
132
+ await ensureZkFiles();
133
+ const params = buildHttpParams(provider);
123
134
  // Diagnostic logger capture is gated behind RECLAIM_AGENT_TRACE so the
124
135
  // happy path doesn't pay the cost (the attestor SDK emits a *lot* of
125
136
  // trace/debug entries during ZK setup). Flip RECLAIM_AGENT_TRACE=1 to
@@ -137,8 +148,8 @@ export async function runAttestorProof(provider, secrets, attestorUrl, ownerAddr
137
148
  headers: secrets,
138
149
  },
139
150
  ownerPrivateKey,
140
- client: { url: resolveAttestorUrl(attestorUrl) },
141
- zkEngine: 'snarkjs',
151
+ client: { url: resolveAttestorUrl(opts.attestorUrl) },
152
+ zkEngine: opts.zkEngine ?? 'gnark',
142
153
  ...(capture !== undefined && { logger: capture.logger }),
143
154
  });
144
155
  }
@@ -156,38 +167,33 @@ export async function runAttestorProof(provider, secrets, attestorUrl, ownerAddr
156
167
  }
157
168
  return errorResult;
158
169
  }
159
- // ClaimTunnelResponse carries the claim under `.claim`.
160
- const claimRecord = claim;
161
- const claimData = claimRecord['claim'];
162
- // claimData.context is a JSON-encoded string carrying
163
- // { extractedParameters: {...}, providerHash, ... }.
164
- let context = {};
165
- const rawContext = claimData?.['context'];
166
- if (typeof rawContext === 'string') {
167
- try {
168
- context = JSON.parse(rawContext);
169
- }
170
- catch {
171
- // Leave context empty if it isn't valid JSON.
172
- }
173
- }
174
- const extractedParameters = context['extractedParameters'] ??
175
- {};
176
- const extractedValue = Object.values(extractedParameters)[0] ?? '';
170
+ const { extractedValue, extractedParameters, identifier, owner, claimData } = parseClaim(claim);
177
171
  const result = {
178
172
  proof: claim,
179
173
  extractedValue,
180
174
  extractedParameters,
181
175
  verified: true,
182
176
  };
183
- const identifier = claimData?.['identifier'];
184
- if (typeof identifier === 'string') {
177
+ if (identifier !== undefined) {
185
178
  result.identifier = identifier;
186
179
  }
187
- const owner = claimData?.['owner'];
188
- if (typeof owner === 'string') {
180
+ if (owner !== undefined) {
189
181
  result.owner = owner;
190
182
  }
183
+ // Structured claim + signature so the result JWS can carry a nested,
184
+ // attestor-signed proof the Consumer verifies independently.
185
+ if (opts.includeAttestorClaim) {
186
+ const sigs = claim.signatures;
187
+ if (claimData
188
+ && typeof sigs?.attestorAddress === 'string'
189
+ && sigs.claimSignature instanceof Uint8Array) {
190
+ result.attestorClaim = {
191
+ claim: claimData,
192
+ attestorAddress: sigs.attestorAddress,
193
+ claimSignature: '0x' + bytesToHex(sigs.claimSignature),
194
+ };
195
+ }
196
+ }
191
197
  // Only set when RECLAIM_AGENT_TRACE=1. Full stream — the MCP layer
192
198
  // trims for the LLM-visible response; the on-disk dump gets it all.
193
199
  if (attestorLogs !== undefined && attestorLogs.length > 0) {
@@ -0,0 +1,5 @@
1
+ export type { AttestorClaim, AttestorLogEntry, ProofResult, RunAttestorProofOptions, } from './attestor.ts';
2
+ export { runAttestorProof, tailLogs, warnsAndErrors } from './attestor.ts';
3
+ export { buildHttpParams, ensureAttestorCrypto, type ParsedClaim, parseClaim, resolveAttestorUrl, } from './proof-core.ts';
4
+ export { ensureZkFiles } from './zk-files.ts';
5
+ export { createClaimOnAttestor } from '@reclaimprotocol/attestor-core';
@@ -0,0 +1,6 @@
1
+ export { runAttestorProof, tailLogs, warnsAndErrors } from "./attestor.js";
2
+ export { buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl, } from "./proof-core.js";
3
+ export { ensureZkFiles } from "./zk-files.js";
4
+ // Re-exported attestor-core claim primitive so the agent is the single
5
+ // integration point with @reclaimprotocol/attestor-core across the builder.
6
+ export { createClaimOnAttestor } from '@reclaimprotocol/attestor-core';
@@ -0,0 +1,35 @@
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 type { ReclaimProvider } from '../provider/schema.ts';
11
+ /** One-time crypto backend init for `@reclaimprotocol/tls` (idempotent). The
12
+ * package ships with an empty `crypto = {}` and defers the backend to the
13
+ * caller; without this the first proof dies inside the TLS client with
14
+ * `crypto.randomBytes is not a function`. Shared by every attestor caller —
15
+ * the agent MCP `run_proof` tool and the app's verification runner. */
16
+ export declare function ensureAttestorCrypto(): void;
17
+ /** Resolve the attestor websocket URL: explicit override → env → public
18
+ * default. Resolved per-call so an env change or per-request override takes
19
+ * effect without restarting. */
20
+ export declare function resolveAttestorUrl(override?: string): string;
21
+ /** Map a verification's provider recipe straight to the attestor's HTTP
22
+ * params — never synthesized from captured traffic. */
23
+ export declare function buildHttpParams(provider: ReclaimProvider): ProviderParams<'http'>;
24
+ export interface ParsedClaim {
25
+ /** ProviderClaimData: provider, parameters, context, owner, identifier, … */
26
+ claimData?: Record<string, unknown>;
27
+ extractedParameters: Record<string, string>;
28
+ extractedValue: string;
29
+ identifier?: string;
30
+ owner?: string;
31
+ }
32
+ /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
33
+ * owner) out of a `createClaimOnAttestor` result. The full result still
34
+ * carries signatures the caller may need for nested-proof building. */
35
+ export declare function parseClaim(claim: unknown): ParsedClaim;
@@ -0,0 +1,73 @@
1
+ import { setCryptoImplementation } from '@reclaimprotocol/tls';
2
+ import { webcryptoCrypto } from '@reclaimprotocol/tls/webcrypto';
3
+ const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws';
4
+ let cryptoInitialized = false;
5
+ /** One-time crypto backend init for `@reclaimprotocol/tls` (idempotent). The
6
+ * package ships with an empty `crypto = {}` and defers the backend to the
7
+ * caller; without this the first proof dies inside the TLS client with
8
+ * `crypto.randomBytes is not a function`. Shared by every attestor caller —
9
+ * the agent MCP `run_proof` tool and the app's verification runner. */
10
+ export function ensureAttestorCrypto() {
11
+ if (cryptoInitialized) {
12
+ return;
13
+ }
14
+ setCryptoImplementation(webcryptoCrypto);
15
+ cryptoInitialized = true;
16
+ }
17
+ /** Resolve the attestor websocket URL: explicit override → env → public
18
+ * default. Resolved per-call so an env change or per-request override takes
19
+ * effect without restarting. */
20
+ export function resolveAttestorUrl(override) {
21
+ return (override ?? process.env['RECLAIM_ATTESTOR_URL'] ?? FALLBACK_ATTESTOR_URL);
22
+ }
23
+ /** Map a verification's provider recipe straight to the attestor's HTTP
24
+ * params — never synthesized from captured traffic. */
25
+ export function buildHttpParams(provider) {
26
+ return {
27
+ url: provider.url,
28
+ method: provider.method,
29
+ ...(provider.headers !== undefined && { headers: provider.headers }),
30
+ ...(provider.body !== undefined && { body: provider.body }),
31
+ responseMatches: provider.responseMatches,
32
+ responseRedactions: provider.responseRedactions ?? [],
33
+ ...(provider.paramValues !== undefined && {
34
+ paramValues: provider.paramValues,
35
+ }),
36
+ };
37
+ }
38
+ /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
39
+ * owner) out of a `createClaimOnAttestor` result. The full result still
40
+ * carries signatures the caller may need for nested-proof building. */
41
+ export function parseClaim(claim) {
42
+ const claimRecord = claim;
43
+ const claimData = claimRecord['claim'];
44
+ // claimData.context is a JSON-encoded string carrying
45
+ // { extractedParameters: {...}, providerHash, ... }.
46
+ let context = {};
47
+ const rawContext = claimData?.['context'];
48
+ if (typeof rawContext === 'string') {
49
+ try {
50
+ context = JSON.parse(rawContext);
51
+ }
52
+ catch {
53
+ // Leave context empty if it isn't valid JSON.
54
+ }
55
+ }
56
+ const extractedParameters = context['extractedParameters'] ?? {};
57
+ const parsed = {
58
+ extractedParameters,
59
+ extractedValue: Object.values(extractedParameters)[0] ?? '',
60
+ };
61
+ if (claimData) {
62
+ parsed.claimData = claimData;
63
+ }
64
+ const identifier = claimData?.['identifier'];
65
+ if (typeof identifier === 'string') {
66
+ parsed.identifier = identifier;
67
+ }
68
+ const owner = claimData?.['owner'];
69
+ if (typeof owner === 'string') {
70
+ parsed.owner = owner;
71
+ }
72
+ return parsed;
73
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Ensure the zk circuit resources are on disk before a proof runs. Idempotent
3
+ * and concurrency-safe: the download runs at most once per process, and a
4
+ * failed attempt clears the latch so a later proof can retry.
5
+ *
6
+ * Resolves silently when the resources are already present (the warm npx cache,
7
+ * or after a manual `npm run download:zk-files`) and when
8
+ * `@reclaimprotocol/zk-symmetric-crypto` isn't installed at all (SDK-only
9
+ * consumers have nothing to prove).
10
+ *
11
+ * All output goes to stderr (fd 2): the MCP server speaks JSON-RPC over stdout,
12
+ * so a stray write to fd 1 mid-session would corrupt the protocol.
13
+ */
14
+ export declare function ensureZkFiles(): Promise<void>;
@@ -0,0 +1,73 @@
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
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import { dirname, join } from 'node:path';
10
+ const require = createRequire(import.meta.url);
11
+ let inFlight;
12
+ /**
13
+ * Ensure the zk circuit resources are on disk before a proof runs. Idempotent
14
+ * and concurrency-safe: the download runs at most once per process, and a
15
+ * failed attempt clears the latch so a later proof can retry.
16
+ *
17
+ * Resolves silently when the resources are already present (the warm npx cache,
18
+ * or after a manual `npm run download:zk-files`) and when
19
+ * `@reclaimprotocol/zk-symmetric-crypto` isn't installed at all (SDK-only
20
+ * consumers have nothing to prove).
21
+ *
22
+ * All output goes to stderr (fd 2): the MCP server speaks JSON-RPC over stdout,
23
+ * so a stray write to fd 1 mid-session would corrupt the protocol.
24
+ */
25
+ export function ensureZkFiles() {
26
+ inFlight ??= download().catch((err) => {
27
+ inFlight = undefined;
28
+ throw err;
29
+ });
30
+ return inFlight;
31
+ }
32
+ async function download() {
33
+ let mainPath;
34
+ try {
35
+ mainPath = require.resolve('@reclaimprotocol/zk-symmetric-crypto');
36
+ }
37
+ catch {
38
+ return;
39
+ }
40
+ // mainPath is <pkg>/lib/index.js → resources live at <pkg>/resources, the
41
+ // downloader at <pkg>/lib/scripts/download-files.js. We only use the snarkjs
42
+ // engine (createClaimOnAttestor zkEngine: 'snarkjs'), so its presence is the
43
+ // signal that the circuits are ready.
44
+ const libDir = dirname(mainPath);
45
+ const pkgDir = dirname(libDir);
46
+ if (existsSync(join(pkgDir, 'resources', 'snarkjs'))) {
47
+ return;
48
+ }
49
+ const downloadScript = join(libDir, 'scripts', 'download-files.js');
50
+ if (!existsSync(downloadScript)) {
51
+ throw new Error('zk circuit downloader not found in '
52
+ + '@reclaimprotocol/zk-symmetric-crypto; '
53
+ + 'run `npm run download:zk-files` and retry.');
54
+ }
55
+ process.stderr.write('[reclaim-agent] downloading zkTLS proof circuits '
56
+ + '(~280MB, one-time)…\n');
57
+ // stdio fd 1→2: send the downloader's stdout to our stderr so its progress
58
+ // never lands on the MCP server's JSON-RPC stdout.
59
+ await new Promise((resolve, reject) => {
60
+ const child = spawn(process.execPath, [downloadScript], {
61
+ stdio: ['ignore', 2, 2],
62
+ });
63
+ child.on('error', reject);
64
+ child.on('exit', (code) => {
65
+ if (code === 0) {
66
+ resolve();
67
+ }
68
+ else {
69
+ reject(new Error(`zk download exited ${code}`));
70
+ }
71
+ });
72
+ });
73
+ }
@@ -0,0 +1,5 @@
1
+ export { extractGraphqlOp, looksLikeGraphqlEndpoint } from './graphql.ts';
2
+ export type { CapturedRequest, HttpMethod, OprfMode, ReclaimProvider, RequestConcern, ResponseMatch, ResponseRedaction, SecretParamRef, } from './schema.ts';
3
+ export { classifyHeader, splitSecrets } from './secrets.ts';
4
+ export { hashProviderParams, providers } from '@reclaimprotocol/attestor-core';
5
+ export type { ProviderParams } from '@reclaimprotocol/attestor-core';
@@ -0,0 +1,9 @@
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.js";
5
+ export { classifyHeader, splitSecrets } from "./secrets.js";
6
+ // Re-exported attestor-core provider primitives (the registry, the param hash,
7
+ // and the param type) so the builder imports them via the agent rather than
8
+ // depending on @reclaimprotocol/attestor-core directly.
9
+ export { hashProviderParams, providers } from '@reclaimprotocol/attestor-core';
@@ -1,22 +1,19 @@
1
1
  import type { ReclaimClient } from '@reclaimprotocol/client';
2
- import type { CreateProviderVersionRequest as CreateVersionBody, ProviderVersionRef as VersionRef } from '../types/openapi.ts';
2
+ import type { CreateProviderVersionRequest, ProviderVersionRef } from '@reclaimprotocol/client/openapi';
3
3
  import type { ReclaimProvider } from './schema.ts';
4
4
  /**
5
- * Translate a drafted ReclaimProvider into the body for
6
- * POST /providers/{providerId}/versions.
7
- *
8
- * `initialUrl` is the landing page the user navigated to during
9
- * capture — distinct from `provider.url` which is the specific
10
- * API URL the attestor replays.
11
- *
12
- * Secrets are NOT persisted — the verification client re-supplies
13
- * them at runtime via `credentials: 'include'`.
5
+ * Derive the version's `requiredContext` JSON Schema from the
6
+ * `{{context.<name>}}` placeholders in the provider's url/body — mechanical,
7
+ * so a published recipe can never reference a context value the Builder
8
+ * doesn't require. Returns undefined when the recipe has none (the field is
9
+ * optional). Authors can refine types/descriptions in the version editor.
14
10
  */
15
- export declare function providerToCreateVersionRequest(provider: ReclaimProvider, initialUrl: string, notes?: string, version?: VersionRef): CreateVersionBody;
11
+ export declare function deriveRequiredContext(provider: ReclaimProvider): CreateProviderVersionRequest['requiredContext'];
12
+ export declare function providerToCreateVersionRequest(provider: ReclaimProvider, initialUrl: string, notes?: string, version?: ProviderVersionRef): CreateProviderVersionRequest;
16
13
  /**
17
14
  * Fetch the current versions for a provider and return the next version above
18
15
  * the highest existing one (bumping the patch number), or 1.0.0 when the
19
16
  * provider has no versions yet. A failed lookup propagates — silently
20
17
  * resetting to 1.0.0 would re-publish a v3 provider as v1.0.0.
21
18
  */
22
- export declare function nextVersion(client: ReclaimClient, providerId: string): Promise<VersionRef>;
19
+ export declare function nextVersion(client: ReclaimClient, providerId: string): Promise<ProviderVersionRef>;
@@ -9,7 +9,35 @@
9
9
  * Secrets are NOT persisted — the verification client re-supplies
10
10
  * them at runtime via `credentials: 'include'`.
11
11
  */
12
+ /** `{{context.<name>}}` placeholders in a request URL/body: values the
13
+ * CONSUMER supplies at verification time via the session `context`. Bare
14
+ * `{{name}}` placeholders (extraction params) are not consumer-supplied
15
+ * and are ignored. */
16
+ const CONTEXT_PARAM_RE = /\{\{context\.([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
17
+ /**
18
+ * Derive the version's `requiredContext` JSON Schema from the
19
+ * `{{context.<name>}}` placeholders in the provider's url/body — mechanical,
20
+ * so a published recipe can never reference a context value the Builder
21
+ * doesn't require. Returns undefined when the recipe has none (the field is
22
+ * optional). Authors can refine types/descriptions in the version editor.
23
+ */
24
+ export function deriveRequiredContext(provider) {
25
+ const haystack = `${provider.url}\n${provider.body ?? ''}`;
26
+ const names = new Set();
27
+ for (const m of haystack.matchAll(CONTEXT_PARAM_RE)) {
28
+ names.add(m[1]);
29
+ }
30
+ if (!names.size) {
31
+ return undefined;
32
+ }
33
+ const properties = {};
34
+ for (const name of names) {
35
+ properties[name] = { type: 'string' };
36
+ }
37
+ return { type: 'object', required: [...names], properties };
38
+ }
12
39
  export function providerToCreateVersionRequest(provider, initialUrl, notes, version) {
40
+ const requiredContext = deriveRequiredContext(provider);
13
41
  // Inline responseRedactions into matches[*].extract (1-to-1 by index).
14
42
  const matches = (provider.responseMatches ?? []).map((match, i) => {
15
43
  const redaction = provider.responseRedactions?.[i];
@@ -33,10 +61,16 @@ export function providerToCreateVersionRequest(provider, initialUrl, notes, vers
33
61
  },
34
62
  ],
35
63
  allowedJsRequests: [],
36
- webSettings: { geoLocation: '{{DYNAMIC_GEO}}' },
37
- ...(provider.paramValues
38
- ? { requiredParameters: provider.paramValues }
39
- : {}),
64
+ // Mirror the builder openapi defaults explicitly — the server does not
65
+ // apply schema defaults to request bodies.
66
+ webSettings: {
67
+ geoLocation: '{{DYNAMIC_GEO}}',
68
+ clientOptions: { portal: { useProxy: true } },
69
+ },
70
+ // Derived from `{{context.<name>}}` placeholders only — a captured
71
+ // provider's `paramValues` are extracted response parameters, not
72
+ // consumer context, and never become requiredContext.
73
+ ...(requiredContext ? { requiredContext } : {}),
40
74
  ...(notes ? { notes } : {}),
41
75
  };
42
76
  }
@@ -0,0 +1,12 @@
1
+ import type { ReclaimProvider } from './schema.ts';
2
+ /**
3
+ * Advisory pre-publish lint for a drafted provider — never a gate. Surfaces
4
+ * only what the AUTHOR must decide; everything testable is tested by the
5
+ * verification client itself (CDP capture sees all traffic — navigations,
6
+ * XHRs, fetches — and `checkCandidate` in packages/app src/verify/run.ts
7
+ * selects the captured request that satisfies the recipe, skipping the
8
+ * rest). Interpolation and matching are plain text and work for any
9
+ * captured content; extract selectors apply client-side in spec order
10
+ * xPath → jsonPath → regex, each to the prior selection.
11
+ */
12
+ export declare function verificationWarnings(provider: ReclaimProvider): string[];
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Advisory pre-publish lint for a drafted provider — never a gate. Surfaces
3
+ * only what the AUTHOR must decide; everything testable is tested by the
4
+ * verification client itself (CDP capture sees all traffic — navigations,
5
+ * XHRs, fetches — and `checkCandidate` in packages/app src/verify/run.ts
6
+ * selects the captured request that satisfies the recipe, skipping the
7
+ * rest). Interpolation and matching are plain text and work for any
8
+ * captured content; extract selectors apply client-side in spec order
9
+ * xPath → jsonPath → regex, each to the prior selection.
10
+ */
11
+ export function verificationWarnings(provider) {
12
+ const out = [];
13
+ // Untemplated user-specific URL parts: any captured param value baked
14
+ // literally into the URL makes the provider user-specific. Only the dev
15
+ // can decide: consumer-supplied → {{context.<name>}} (declared in
16
+ // requiredContext; consumers then send the exact value and the VC matches
17
+ // only that value), bare {{<name>}} (resolved from the end-user's own
18
+ // traffic), or re-capture a non-user-specific endpoint.
19
+ for (const [name, value] of Object.entries(provider.paramValues ?? {})) {
20
+ if (value.length >= 3 && provider.url.includes(value)) {
21
+ out.push(`The URL contains the captured value "${value}" (param ` +
22
+ `"${name}") as a literal. Decide with the dev: if consumers ` +
23
+ 'supply it, replace with {{context.' + name + '}} (published ' +
24
+ 'requiredContext will require it; verification matches only ' +
25
+ 'the supplied value). Leaving it literal pins the provider ' +
26
+ 'to this user.');
27
+ }
28
+ }
29
+ return out;
30
+ }