@reclaimprotocol/agent 0.1.4 → 0.1.5

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.
@@ -1,10 +1,14 @@
1
- # Authoring a Reclaim Provider / Verification
2
-
3
- > **Terminology note:** "provider" and "verification" refer to the same
4
- > thing. The API and tool parameters use **provider** / **providerId**
5
- > (the technical term). Conversations with developers prefer
6
- > **verification** (the user-facing term). Either is acceptable — never
7
- > correct the developer for using one or the other.
1
+ # Authoring a Reclaim Provider
2
+
3
+ > **Terminology note:** A **provider** is the recipe you author here — the
4
+ > instructions for extracting a data point from a website (the
5
+ > `responseMatches` / `responseRedactions` spec, identified by a
6
+ > `providerId`). Always call it a **provider**; never call a provider a
7
+ > "verification." A **verification** (or **verification session**) is the
8
+ > separate runtime act in which an end-user proves a data point *using* a
9
+ > provider — reserve that word for the session/result, never for the recipe.
10
+ > If a developer calls the recipe a "verification," gently use "provider"
11
+ > yourself rather than correcting them.
8
12
 
9
13
  You drive the browser through the capture → draft → replay → prove loop using the `reclaim-agent` MCP server. **Your default stance is driver, not orchestrator.** Only pause to ask the dev for things that genuinely require a human: typing credentials, completing 2FA, solving a captcha, confirming an ambiguous value. Navigation, reloads, DOM inspection, request capture — all yours.
10
14
 
@@ -175,7 +179,7 @@ replace with `{{context.<name>}}`. E.g. `https://github.com/Scratch-net/repos`
175
179
 
176
180
  13. **Publish the proved provider.** `provider` is the exact JSON from step 9 with the step-10 URL parameterisation applied, and `initialUrl` is the page where the user logs in / the value is displayed (e.g. `https://github.com/Syed`) — **not** the API endpoint URL inside the provider.
177
181
 
178
- 1. **Create the verification if it doesn't exist:** `create_verification(orgId, domain, slug, title, description)` — find `orgId` via `list_orgs`. `domain` e.g. `github.com`; `slug` e.g. `github-username`; `title` e.g. "GitHub Username". Starts PRIVATE with an empty seed version. Skip if the dev already has a `providerId`.
182
+ 1. **Create the provider if it doesn't exist:** `create_provider(orgId, domain, slug, title, description)` — find `orgId` via `list_orgs`. `domain` e.g. `github.com`; `slug` e.g. `github-username`; `title` e.g. "GitHub Username". Starts PRIVATE with an empty seed version. Skip if the dev already has a `providerId`.
179
183
  2. **Persist the version:** `create_provider_version_from_capture(providerId, provider, initialUrl, notes)` — `notes` e.g. `"Initial draft from browser capture"`. The version lands on the `draft` branch.
180
184
  3. **`requiredContext` is derived for you.** The publish tool scans the recipe's url/body for `{{context.<name>}}` placeholders (step 10) and emits a JSON Schema requiring each one — the Builder then validates every session-create's `context` against it (a clear 400 instead of a late attestor failure). Extra context fields are always allowed. The derived properties are plain strings; to refine types or add human-readable descriptions, call `update_provider_version` with a `requiredContext` override while the version is on `draft`, e.g.:
181
185
 
@@ -183,7 +187,7 @@ replace with `{{context.<name>}}`. E.g. `https://github.com/Scratch-net/repos`
183
187
  { "requiredContext": { "type": "object", "required": ["username"], "properties": { "username": { "type": "string", "description": "GitHub username to verify" } } } }
184
188
  ```
185
189
  4. **Go live:** PRIVATE → `publish_provider_version`; PUBLIC → `submit_provider_version_for_review(providerId, version)` and tell the dev it's awaiting admin review.
186
- 5. **Tell the dev how consumers call it:** sessions for this verification must include the context params, e.g. `context: { "username": "..." }` in `POST /verifications/sessions`. Quote the `requiredContext` returned on the created version — a session missing those fields gets a 400, and verification matches the supplied values exactly.
190
+ 5. **Tell the dev how consumers call it:** sessions for this provider must include the context params, e.g. `context: { "username": "..." }` in `POST /verifications/sessions`. Quote the `requiredContext` returned on the created version — a session missing those fields gets a 400, and verification matches the supplied values exactly.
187
191
 
188
192
  When done, tell the developer the version is live (PRIVATE) or submitted for review (PUBLIC), and that they can integrate using the `providerId`.
189
193
 
@@ -1,10 +1,14 @@
1
- # Authoring a Reclaim Provider / Verification
2
-
3
- > **Terminology note:** "provider" and "verification" refer to the same
4
- > thing. The API and tool parameters use **provider** / **providerId**
5
- > (the technical term). Conversations with developers prefer
6
- > **verification** (the user-facing term). Either is acceptable — never
7
- > correct the developer for using one or the other.
1
+ # Authoring a Reclaim Provider
2
+
3
+ > **Terminology note:** A **provider** is the recipe you author here — the
4
+ > instructions for extracting a data point from a website (the
5
+ > `responseMatches` / `responseRedactions` spec, identified by a
6
+ > `providerId`). Always call it a **provider**; never call a provider a
7
+ > "verification." A **verification** (or **verification session**) is the
8
+ > separate runtime act in which an end-user proves a data point *using* a
9
+ > provider — reserve that word for the session/result, never for the recipe.
10
+ > If a developer calls the recipe a "verification," gently use "provider"
11
+ > yourself rather than correcting them.
8
12
 
9
13
  You drive the browser through the capture → draft → replay → prove loop using the `reclaim-agent` MCP server. **Your default stance is driver, not orchestrator.** Only pause to ask the dev for things that genuinely require a human: typing credentials, completing 2FA, solving a captcha, confirming an ambiguous value. Navigation, reloads, DOM inspection, request capture — all yours.
10
14
 
@@ -164,9 +168,9 @@ Call `reclaim_authenticate({})` with no arguments. It opens the Reclaim dashboar
164
168
  13. **Publish the proved provider.** `provider` is the exact JSON from step 9 with the step-10 URL parameterisation applied, and `initialUrl` is the page where the user logs in / the value is displayed (e.g. `https://github.com/Syed`) — **not** the API endpoint URL inside the provider.
165
169
 
166
170
  Call `create_provider_version_from_capture`:
167
- - **New verification:** omit `providerId`. It registers a new provider (version 1.0.0). Pass `provider`, `initialUrl`, and optionally `description`, `geoLocation`, `providerType` (`PRIVATE` default | `PUBLIC`).
168
- - **New version of an existing verification:** pass its `providerId` — the patch is auto-bumped above the highest existing version. Pass `notes` (≥10 chars) as the change note.
169
- - `get_me_providers({ searchQuery? })` lists the verifications you own — use it to find a `providerId` or to confirm the result. It's the only provider-listing tool in this mode.
171
+ - **New provider:** omit `providerId`. It registers a new provider (version 1.0.0). Pass `provider`, `initialUrl`, and optionally `description`, `geoLocation`, `providerType` (`PRIVATE` default | `PUBLIC`).
172
+ - **New version of an existing provider:** pass its `providerId` — the patch is auto-bumped above the highest existing version. Pass `notes` (≥10 chars) as the change note.
173
+ - `get_me_providers({ searchQuery? })` lists the providers you own — use it to find a `providerId` or to confirm the result. It's the only provider-listing tool in this mode.
170
174
  - There's no separate "create-then-version" step and no org / public-review workflow here.
171
175
  - Consumer-supplied params (step 10) keep the `{{context_<name>}}` form in this mode; there is no `requiredContext` declaration on the legacy backend.
172
176
 
@@ -75,7 +75,7 @@ export function isStaticAsset(url: string): boolean {
75
75
  try {
76
76
  path = new URL(url).pathname
77
77
  } catch{
78
- path = url.split(/[?#]/)[0] ?? url
78
+ path = url.split(/[?#]/)[0] || url
79
79
  }
80
80
 
81
81
  // Extension of the last path segment only, so `/v1.2/data` isn't misread.
@@ -98,7 +98,7 @@ export const MCP_TOOLS = {
98
98
  }),
99
99
  },
100
100
  "AdminProviders": {
101
- description: "All verifications (admin-only)\n\nAdmin only. Lists all verifications across all organizations. Supports filtering by visibility, status, and free-text search on title/domain/slug.",
101
+ description: "All providers (admin-only)\n\nAdmin only. Lists all providers across all organizations. Supports filtering by visibility, status, and free-text search on title/domain/slug.",
102
102
  inputSchema: toZod({
103
103
  "type": "object",
104
104
  "properties": {
@@ -485,7 +485,7 @@ export const MCP_TOOLS = {
485
485
  }),
486
486
  },
487
487
  "CreateProvider": {
488
- description: "Create a verification\n\nCreate a new Reclaim verification. Use this only after `providers` returned nothing useful for the domain. The caller must be an active member of the target organization.\nCreates a new verification in the specified organization. `visibility` is coerced to `PRIVATE` at creation regardless of what's sent; the author may toggle to `PUBLIC` later via `PATCH /providers/{id}`. While `PRIVATE`, versions go to the go live freely; once `PUBLIC`, non-admin versions need admin review. See `Provider.visibility`.\nThe server also seeds an initial `0.1.0` version on the `draft` branch in the same transaction so the verification is immediately editable. Its `initialUrl` defaults to `https://<domain>`; all other version fields fall back to their schema-declared defaults (structured verification, empty requests, empty web settings). `Provider.latestVersion` stays `null` until the draft is promoted to the live channel.",
488
+ description: "Create a provider\n\nCreate a new Reclaim provider. Use this only after `providers` returned nothing useful for the domain. The caller must be an active member of the target organization.\nCreates a new provider in the specified organization. `visibility` is coerced to `PRIVATE` at creation regardless of what's sent; the author may toggle to `PUBLIC` later via `PATCH /providers/{id}`. While `PRIVATE`, versions go to the go live freely; once `PUBLIC`, non-admin versions need admin review. See `Provider.visibility`.\nThe server also seeds an initial `0.1.0` version on the `draft` branch in the same transaction so the provider is immediately editable. Its `initialUrl` defaults to `https://<domain>`; all other version fields fall back to their schema-declared defaults (a `structured` verificationType, empty requests, empty web settings). `Provider.latestVersion` stays `null` until the draft is promoted to the live channel.",
489
489
  inputSchema: toZod({
490
490
  "type": "object",
491
491
  "properties": {
@@ -618,7 +618,7 @@ export const MCP_TOOLS = {
618
618
  }),
619
619
  },
620
620
  "CreateProviderVersion": {
621
- description: "Create a new verification version\n\nCreates a new version of the verification. The starting branch\ndepends on the verification's `visibility` and the caller's role:\n\n- `PRIVATE` verification (any caller): the live channel (no review).\n\n- `PUBLIC` verification, admin caller (Reclaim staff): the live channel\n (no review).\n\n- `PUBLIC` verification, non-admin caller: `draft` (or `ai` if\n authored by the AI assistant). Must pass review (see\n `POST /providers/{id}/versions/{version}/review`) before\n reaching the live channel.\n",
621
+ description: "Create a new provider version\n\nCreates a new version of the provider. The starting branch\ndepends on the provider's `visibility` and the caller's role:\n\n- `PRIVATE` provider (any caller): the live channel (no review).\n\n- `PUBLIC` provider, admin caller (Reclaim staff): the live channel\n (no review).\n\n- `PUBLIC` provider, non-admin caller: `draft` (or `ai` if\n authored by the AI assistant). Must pass review (see\n `POST /providers/{id}/versions/{version}/review`) before\n reaching the live channel.\n",
622
622
  inputSchema: toZod({
623
623
  "type": "object",
624
624
  "properties": {
@@ -644,7 +644,7 @@ export const MCP_TOOLS = {
644
644
  "body": {
645
645
  "type": "object",
646
646
  "additionalProperties": false,
647
- "description": "Body for creating a new version of a provider. Every new version starts on the `draft` branch — see `CreateProviderVersion`. The request body never sets `branch` directly; going live is a separate, explicit step (publish for PRIVATE, submit-for-review → approve for PUBLIC). The version's `verificationType` is always `structured` and is not settable here; a `prompt` field is reserved for a future AI authoring flow and is also not accepted on this surface.",
647
+ "description": "Body for creating a new version of a provider. Every new version starts on the `draft` branch — see `CreateProviderVersion`. The request body never sets `branch` directly; going live is a separate, explicit step (publish for PRIVATE, submit-for-review → approve for PUBLIC). `verificationType` defaults to `structured`; pass `llm` with a `prompt` to author an AI-verification provider.",
648
648
  "required": [
649
649
  "initialUrl",
650
650
  "requests",
@@ -652,6 +652,20 @@ export const MCP_TOOLS = {
652
652
  "webSettings"
653
653
  ],
654
654
  "properties": {
655
+ "verificationType": {
656
+ "type": "string",
657
+ "description": "How a provider version verifies a claim. `structured` uses the explicit match rules on this version (the `matches` / `allowedJsMatches` and related settings). `llm` defers the verification decision to a language model and requires `prompt` to be set.",
658
+ "enum": [
659
+ "llm",
660
+ "structured"
661
+ ]
662
+ },
663
+ "prompt": {
664
+ "type": "string",
665
+ "description": "Extraction prompt, used (and required) when `verificationType` is `llm`.",
666
+ "minLength": 0,
667
+ "maxLength": 1000000
668
+ },
655
669
  "version": {
656
670
  "type": "object",
657
671
  "additionalProperties": false,
@@ -1211,7 +1225,7 @@ export const MCP_TOOLS = {
1211
1225
  }),
1212
1226
  },
1213
1227
  "DecideProviderVersionReview": {
1214
- description: "Record a decision on a verification version review\n\nAdmin only (Reclaim staff). Approves or rejects a verification version currently on the `in_review` branch. `APPROVED` clears the branch tag and promotes the version to the live channel; `REJECTED` moves it to the `rejected` branch where the author can address feedback and resubmit. A rejected version stays rejected — the author either fixes and resubmits, or abandons it.\nUntil RBAC is designed, \"admin\" is detected via the user's email domain (`@reclaimprotocol.org`). Non-admin callers receive a 403.",
1228
+ description: "Record a decision on a provider version review\n\nAdmin only (Reclaim staff). Approves or rejects a provider version currently on the `in_review` branch. `APPROVED` clears the branch tag and promotes the version to the live channel; `REJECTED` moves it to the `rejected` branch where the author can address feedback and resubmit. A rejected version stays rejected — the author either fixes and resubmits, or abandons it.\nUntil RBAC is designed, \"admin\" is detected via the user's email domain (`@reclaimprotocol.org`). Non-admin callers receive a 403.",
1215
1229
  inputSchema: toZod({
1216
1230
  "type": "object",
1217
1231
  "properties": {
@@ -1384,7 +1398,7 @@ export const MCP_TOOLS = {
1384
1398
  }),
1385
1399
  },
1386
1400
  "DeleteProvider": {
1387
- description: "Delete a verification\n\nOwner of the verification's organization only. Soft-archives the verification (sets `status` to `ARCHIVED`) so historical references remain resolvable. Supports optimistic concurrency via `If-Match`.",
1401
+ description: "Delete a provider\n\nOwner of the provider's organization only. Soft-archives the provider (sets `status` to `ARCHIVED`) so historical references remain resolvable. Supports optimistic concurrency via `If-Match`.",
1388
1402
  inputSchema: toZod({
1389
1403
  "type": "object",
1390
1404
  "properties": {
@@ -1429,7 +1443,7 @@ export const MCP_TOOLS = {
1429
1443
  }),
1430
1444
  },
1431
1445
  "DeleteProviderVersion": {
1432
- description: "Delete a verification version\n\nSoft-archives a version on the `draft`, `ai`, or `rejected` branch — sets `status` to `ARCHIVED` so history is preserved. Trunk (live) and `in_review` versions cannot be archived this way; archive the parent verification instead. Supports optimistic concurrency via `If-Match`.",
1446
+ description: "Delete a provider version\n\nSoft-archives a version on the `draft`, `ai`, or `rejected` branch — sets `status` to `ARCHIVED` so history is preserved. Trunk (live) and `in_review` versions cannot be archived this way; archive the parent provider instead. Supports optimistic concurrency via `If-Match`.",
1433
1447
  inputSchema: toZod({
1434
1448
  "type": "object",
1435
1449
  "properties": {
@@ -1521,7 +1535,7 @@ export const MCP_TOOLS = {
1521
1535
  }),
1522
1536
  },
1523
1537
  "DiscoverResolve": {
1524
- description: "Resolve a website + data points to verification providers\n\nProvider resolution: given a website and the data points the user wants their users to prove, return the smallest set of existing providers whose data points COVER the selection (greedy combine), the `requiredContext` inputs the consumer must supply, and any `uncovered` points no provider exposes yet (`needsCreation` is true then — author a provider with the agent tools). When the chosen providers can be fused into one (same start page, no clashing page scripts) `merge.mergeable` is true.\n\nAgent flow: pick a website (`GET /discover/data-points` lists what's verifiable there), confirm the data points with the user, call this to get the `providers` + `requiredContext`, collect those context values from the user, then `POST /verifications/sessions` with `providers` (pass every id) and `context` to mint a verification.",
1538
+ description: "Resolve a website + data points to providers\n\nProvider resolution: given a website and the data points the user wants their users to prove, return the smallest set of existing providers whose data points COVER the selection (greedy combine), the `requiredContext` inputs the consumer must supply, and any `uncovered` points no provider exposes yet (`needsCreation` is true then — author a provider with the agent tools). When the chosen providers can be fused into one (same start page, no clashing page scripts) `merge.mergeable` is true.\n\nAgent flow: pick a website (`GET /discover/data-points` lists what's verifiable there), confirm the data points with the user, call this to get the `providers` + `requiredContext`, collect those context values from the user, then `POST /verifications/sessions` with `providers` (pass every id) and `context` to mint a verification.",
1525
1539
  inputSchema: toZod({
1526
1540
  "type": "object",
1527
1541
  "properties": {
@@ -1792,7 +1806,7 @@ export const MCP_TOOLS = {
1792
1806
  }),
1793
1807
  },
1794
1808
  "GetProvider": {
1795
- description: "Get a verification\n\nFetch a single Reclaim verification by id. Returns every field needed\nto integrate it into a flow: domain, the live version (initialUrl + requests),\nand the data points it can prove. Use `providers` first if you only\nhave a query string.\n\nReturns the verification record.\n\n**Canonical URL.** For SEO and stable sharing, the canonical\nHTML URL for a verification is `/providers/{providerId}/{slug}`\n(see `GetProviderBySlug`). HTML callers (`Accept: text/html`)\nthat hit this route — which omits the slug — receive a `301`\nredirect to the slugged URL. JSON callers\n(`Accept: application/json`) receive the body directly without\nredirect; the slug is on the response.\n",
1809
+ description: "Get a provider\n\nFetch a single Reclaim provider by id. Returns every field needed\nto integrate it into a flow: domain, the live version (initialUrl + requests),\nand the data points it can prove. Use `providers` first if you only\nhave a query string.\n\nReturns the provider record.\n\n**Canonical URL.** For SEO and stable sharing, the canonical\nHTML URL for a provider is `/providers/{providerId}/{slug}`\n(see `GetProviderBySlug`). HTML callers (`Accept: text/html`)\nthat hit this route — which omits the slug — receive a `301`\nredirect to the slugged URL. JSON callers\n(`Accept: application/json`) receive the body directly without\nredirect; the slug is on the response.\n",
1796
1810
  inputSchema: toZod({
1797
1811
  "type": "object",
1798
1812
  "properties": {
@@ -1827,7 +1841,7 @@ export const MCP_TOOLS = {
1827
1841
  }),
1828
1842
  },
1829
1843
  "GetProviderVersion": {
1830
- description: "Get a specific verification version",
1844
+ description: "Get a specific provider version",
1831
1845
  inputSchema: toZod({
1832
1846
  "type": "object",
1833
1847
  "properties": {
@@ -2165,7 +2179,7 @@ export const MCP_TOOLS = {
2165
2179
  }),
2166
2180
  },
2167
2181
  "ListProviderVersions": {
2168
- description: "List a verification's versions\n\nLists versions visible to the caller. Non-public branches (`ai`, `draft`, `in_review`, `rejected`) are only visible to members of the verification's owning organization.",
2182
+ description: "List a provider's versions\n\nLists versions visible to the caller. Non-public branches (`ai`, `draft`, `in_review`, `rejected`) are only visible to members of the provider's owning organization.",
2169
2183
  inputSchema: toZod({
2170
2184
  "type": "object",
2171
2185
  "properties": {
@@ -2320,7 +2334,7 @@ export const MCP_TOOLS = {
2320
2334
  }),
2321
2335
  },
2322
2336
  "Providers": {
2323
- description: "Search verifications\n\nSearch published Reclaim verifications by domain or data point. A verification is the recipe an end-user follows to prove something about themselves (e.g. \"I have N followers on Twitter\"). Returns a list of matches. Call this BEFORE `create_provider` — most popular domains already have a public verification you can reuse.\nCatalog page listing all verifications. Returns a paginated list of verifications the caller can see. Public verifications are visible to everyone; private verifications are only visible to org members.",
2337
+ description: "Search providers\n\nSearch published Reclaim providers by domain or data point. A provider is the recipe an end-user follows to prove something about themselves (e.g. \"I have N followers on Twitter\"). Returns a list of matches. Call this BEFORE `create_provider` — most popular domains already have a public provider you can reuse.\nCatalog page listing all providers. Returns a paginated list of providers the caller can see. Public providers are visible to everyone; private providers are only visible to org members.",
2324
2338
  inputSchema: toZod({
2325
2339
  "type": "object",
2326
2340
  "properties": {
@@ -2366,7 +2380,7 @@ export const MCP_TOOLS = {
2366
2380
  }),
2367
2381
  },
2368
2382
  "PublishProviderVersion": {
2369
- description: "Publish a draft version directly (PRIVATE providers only)\n\nPublishes a verification version on the `draft` branch directly to the live channel (trunk). Only allowed for PRIVATE providers — PUBLIC providers must use the review flow (`POST .../review` → admin decision). On success the version's branch transitions from `draft` to `null` (trunk/live).",
2383
+ description: "Publish a draft version directly (PRIVATE providers only)\n\nPublishes a provider version on the `draft` branch directly to the live channel (trunk). Only allowed for PRIVATE providers — PUBLIC providers must use the review flow (`POST .../review` → admin decision). On success the version's branch transitions from `draft` to `null` (trunk/live).",
2370
2384
  inputSchema: toZod({
2371
2385
  "type": "object",
2372
2386
  "properties": {
@@ -2615,7 +2629,7 @@ export const MCP_TOOLS = {
2615
2629
  }),
2616
2630
  },
2617
2631
  "SubmitProviderVersionForReview": {
2618
- description: "Submit a verification version for review\n\nSubmits a verification version (currently on the `draft` or `rejected` branch) for admin review. On success the version's branch transitions to `in_review`. Callable by any authenticated member of the verification's owning org. Meaningful only on `PUBLIC` verifications — on `PRIVATE` verifications new versions go to the live channel directly and never need review.",
2632
+ description: "Submit a provider version for review\n\nSubmits a provider version (currently on the `draft` or `rejected` branch) for admin review. On success the version's branch transitions to `in_review`. Callable by any authenticated member of the provider's owning org. Meaningful only on `PUBLIC` providers — on `PRIVATE` providers new versions go to the live channel directly and never need review.",
2619
2633
  inputSchema: toZod({
2620
2634
  "type": "object",
2621
2635
  "properties": {
@@ -2666,7 +2680,7 @@ export const MCP_TOOLS = {
2666
2680
  }),
2667
2681
  },
2668
2682
  "TransferProvider": {
2669
- description: "Transfer a verification to a different workspace\n\nAdmin-only. Moves the verification's `orgId` to the supplied workspace; preserves slug, versions, members. SQL function `builder.transfer_provider` enforces the admin gate and target existence atomically.",
2683
+ description: "Transfer a provider to a different workspace\n\nAdmin-only. Moves the provider's `orgId` to the supplied workspace; preserves slug, versions, members. SQL function `builder.transfer_provider` enforces the admin gate and target existence atomically.",
2670
2684
  inputSchema: toZod({
2671
2685
  "type": "object",
2672
2686
  "properties": {
@@ -2825,7 +2839,7 @@ export const MCP_TOOLS = {
2825
2839
  }),
2826
2840
  },
2827
2841
  "UpdateProvider": {
2828
- description: "Update a verification\n\nUpdate a Reclaim verification's metadata. Partial PATCH — pass only fields you want to change. The caller must be an active member of the owning organization. To change the verification's recipe (initialUrl, requests, …) cut a new version via the dashboard. Use `seo` to control how the verification appears in search results and social shares; omit a subfield to keep the server default.\nCaller must be a member of the verification's owning organization. `id`, `orgId`, and `slug` are immutable. Supports optimistic concurrency via `If-Match`.",
2842
+ description: "Update a provider\n\nUpdate a Reclaim provider's metadata. Partial PATCH — pass only fields you want to change. The caller must be an active member of the owning organization. To change the provider's recipe (initialUrl, requests, …) cut a new version via the dashboard. Use `seo` to control how the provider appears in search results and social shares; omit a subfield to keep the server default.\nCaller must be a member of the provider's owning organization. `id`, `orgId`, and `slug` are immutable. Supports optimistic concurrency via `If-Match`.",
2829
2843
  inputSchema: toZod({
2830
2844
  "type": "object",
2831
2845
  "properties": {
@@ -2962,7 +2976,7 @@ export const MCP_TOOLS = {
2962
2976
  }),
2963
2977
  },
2964
2978
  "UpdateProviderVersion": {
2965
- description: "Update a verification version\n\nMutable only while the version is on the `draft` or `rejected` branch. Versions on `in_review` or the live channel reject all writes here. Caller must be a member of the verification's owning organization. Supports optimistic concurrency via `If-Match`.",
2979
+ description: "Update a provider version\n\nMutable only while the version is on the `draft` or `rejected` branch. Versions on `in_review` or the live channel reject all writes here. Caller must be a member of the provider's owning organization. Supports optimistic concurrency via `If-Match`.",
2966
2980
  inputSchema: toZod({
2967
2981
  "type": "object",
2968
2982
  "properties": {
@@ -3004,13 +3018,27 @@ export const MCP_TOOLS = {
3004
3018
  "body": {
3005
3019
  "type": "object",
3006
3020
  "additionalProperties": false,
3007
- "description": "Partial update of a provider version. Mutable only while the version is on the `draft` or `rejected` branch; versions on `in_review` or the live channel reject all writes here. The semver does not change on update — `build_number` increments instead, giving callers a monotonic revision counter without touching the (major, minor, patch) PK. `verificationType` / `prompt` are not accepted; see `ProviderVersion` for why.",
3021
+ "description": "Partial update of a provider version. Mutable only while the version is on the `draft` or `rejected` branch; versions on `in_review` or the live channel reject all writes here. The semver does not change on update — `build_number` increments instead, giving callers a monotonic revision counter without touching the (major, minor, patch) PK. `verificationType` and `prompt` may be updated; omit either to carry the prior build's value forward.",
3008
3022
  "properties": {
3009
3023
  "initialUrl": {
3010
3024
  "type": "string",
3011
3025
  "format": "uri",
3012
3026
  "maxLength": 300
3013
3027
  },
3028
+ "verificationType": {
3029
+ "type": "string",
3030
+ "description": "How a provider version verifies a claim. `structured` uses the explicit match rules on this version (the `matches` / `allowedJsMatches` and related settings). `llm` defers the verification decision to a language model and requires `prompt` to be set.",
3031
+ "enum": [
3032
+ "llm",
3033
+ "structured"
3034
+ ]
3035
+ },
3036
+ "prompt": {
3037
+ "type": "string",
3038
+ "description": "Extraction prompt, used when `verificationType` is `llm`.",
3039
+ "minLength": 0,
3040
+ "maxLength": 1000000
3041
+ },
3014
3042
  "requests": {
3015
3043
  "type": "array",
3016
3044
  "minItems": 0,
package/src/mcp/server.ts CHANGED
@@ -9,16 +9,35 @@ export interface ToolDefinition {
9
9
  inputSchema: JSONSchema.ObjectSchema
10
10
  }
11
11
 
12
+ /**
13
+ * Passed to every tool handler alongside its parsed args. Long-running,
14
+ * human-in-the-loop tools (e.g. a browser login flow that waits for someone
15
+ * to sign in) should call `reportProgress` every few seconds while waiting.
16
+ * MCP clients that attach a progress token to their request (most desktop
17
+ * clients do) treat an incoming `notifications/progress` as proof the call
18
+ * is still alive and reset their own request timeout instead of giving up —
19
+ * the SDK's client-side default is 60s, far shorter than a human can be
20
+ * expected to take to log in. A no-op when the client didn't request
21
+ * progress tracking for this call.
22
+ */
23
+ export interface ToolContext {
24
+ reportProgress: (message: string) => Promise<void>
25
+ }
26
+
27
+ const NOOP_CONTEXT: ToolContext = { reportProgress: async() => {} }
28
+
12
29
  export interface RegisteredTool {
13
30
  definition: ToolDefinition
14
31
  /**
15
32
  * @param args Tool arguments
33
+ * @param ctx Progress-reporting context (see {@link ToolContext}).
34
+ * Optional so direct/test callers can omit it.
16
35
  * @returns A JSON stringifiable content to be returned to the llm
17
36
  * @throws Any error encountered during tool execution will be caught and
18
37
  * returned as a response using
19
38
  * [remapErrorAsResponse](./src/mcp/remap_errors.ts).
20
39
  */
21
- handler: (args: unknown) => Promise<unknown>
40
+ handler: (args: unknown, ctx?: ToolContext) => Promise<unknown>
22
41
  }
23
42
 
24
43
  /**
@@ -30,14 +49,14 @@ export interface RegisteredTool {
30
49
  */
31
50
  export function defineTool<TArgs>(
32
51
  definition: ToolDefinition,
33
- handler: (args: TArgs) => Promise<unknown>,
52
+ handler: (args: TArgs, ctx: ToolContext) => Promise<unknown>,
34
53
  ): RegisteredTool {
35
54
  const schema = toZod(definition.inputSchema)
36
55
  return {
37
56
  definition,
38
- handler: async(raw) => {
57
+ handler: async(raw, ctx = NOOP_CONTEXT) => {
39
58
  const args = schema.parse(raw) as TArgs
40
- return await handler(args)
59
+ return await handler(args, ctx)
41
60
  },
42
61
  }
43
62
  }
@@ -55,13 +74,31 @@ export function registerToolList(server: McpServer, tools: RegisteredTool[]) {
55
74
  description: tool.definition.description,
56
75
  inputSchema: toZod(tool.definition.inputSchema),
57
76
  },
58
- async(args) => {
77
+ async(args, extra) => {
59
78
  try {
79
+ const progressToken = extra?._meta?.progressToken
80
+ let progress = 0
81
+ const ctx: ToolContext = {
82
+ reportProgress: async(message) => {
83
+ if(progressToken === undefined) {
84
+ return
85
+ }
86
+
87
+ progress += 1
88
+ await extra.sendNotification({
89
+ method: 'notifications/progress',
90
+ params: { progressToken, progress, message },
91
+ }).catch(() => {})
92
+ },
93
+ }
94
+
60
95
  return {
61
96
  content: [
62
97
  {
63
98
  type: 'text' as const,
64
- text: JSON.stringify(await tool.handler(args), null, 2),
99
+ text: JSON.stringify(
100
+ await tool.handler(args, ctx), null, 2,
101
+ ),
65
102
  },
66
103
  ],
67
104
  }
@@ -129,7 +129,7 @@ export function createVersionFromCaptureTool(
129
129
  {
130
130
  name: 'create_provider_version_from_capture',
131
131
  description:
132
- 'Persist drafted ReclaimProvider(s) as a verification version. ' +
132
+ 'Persist drafted ReclaimProvider(s) as a provider version. ' +
133
133
  'Call this after run_proof succeeds, passing the `provider` ' +
134
134
  'object(s) from propose_provider VERBATIM. This tool builds the ' +
135
135
  'body (the requests[] array, webSettings, etc.) for you — do NOT ' +
@@ -160,7 +160,7 @@ export function createVersionFromCaptureTool(
160
160
  properties: {
161
161
  providerId: {
162
162
  type: 'string',
163
- description: 'UUID of the verification to publish the version on.',
163
+ description: 'UUID of the provider to publish the version on.',
164
164
  },
165
165
  provider: PROVIDER_SCHEMA,
166
166
  providers: {
@@ -79,11 +79,18 @@ export async function captureFirebaseTokenViaCdp(opts: {
79
79
  loginUrl?: string
80
80
  timeoutMs?: number
81
81
  pollMs?: number
82
+ /** Called every ~10s while waiting for the human to finish signing in, so
83
+ * MCP clients that track progress on this call can reset their own
84
+ * request timeout instead of giving up on a login that's still in
85
+ * flight (see `ToolContext` in `mcp/server.ts`). */
86
+ onProgress?: (message: string) => Promise<void>
82
87
  }): Promise<CdpLoginResult> {
83
88
  const loginUrl =
84
89
  opts.loginUrl || oldLoginUrl() || DEFAULT_LOGIN_URL
85
90
  const timeoutMs = opts.timeoutMs ?? 180_000
86
91
  const pollMs = opts.pollMs ?? 1000
92
+ const onProgress = opts.onProgress ?? (async() => {})
93
+ const progressIntervalMs = 10_000
87
94
 
88
95
  const cfg = new ConfigStore().read()?.browser_agent
89
96
  const mode = cfg?.attachMode ?? 'dedicated'
@@ -114,7 +121,9 @@ export async function captureFirebaseTokenViaCdp(opts: {
114
121
  try {
115
122
  const session = await handles.attachToTab(targetId)
116
123
  const { Runtime } = session
117
- const deadline = Date.now() + timeoutMs
124
+ const startedAt = Date.now()
125
+ const deadline = startedAt + timeoutMs
126
+ let lastProgressAt = startedAt
118
127
  while(Date.now() < deadline) {
119
128
  const r = await Runtime.evaluate({
120
129
  expression: EXTRACT_TOKEN_JS,
@@ -126,6 +135,16 @@ export async function captureFirebaseTokenViaCdp(opts: {
126
135
  return { token: value, closedBrowser: launchedByUs }
127
136
  }
128
137
 
138
+ const now = Date.now()
139
+ if(now - lastProgressAt >= progressIntervalMs) {
140
+ lastProgressAt = now
141
+ const remainingSec = Math.round((deadline - now) / 1000)
142
+ await onProgress(
143
+ `Still waiting for sign-in at ${loginUrl} ` +
144
+ `(${remainingSec}s left before this call times out).`,
145
+ )
146
+ }
147
+
129
148
  await new Promise((resolve) => setTimeout(resolve, pollMs))
130
149
  }
131
150
 
@@ -1,5 +1,9 @@
1
1
  import { agentDisabled } from '../../consts.ts'
2
- import { defineTool, type RegisteredTool } from '../../mcp/server.ts'
2
+ import {
3
+ defineTool,
4
+ type RegisteredTool,
5
+ type ToolContext,
6
+ } from '../../mcp/server.ts'
3
7
  import type { OldIdentity, ReclaimOldClient } from '../client.ts'
4
8
  import { persistIdentity, toEthUid } from '../identity.ts'
5
9
  import { captureFirebaseTokenViaCdp } from '../login-cdp.ts'
@@ -83,7 +87,7 @@ export function authenticateTool(client: ReclaimOldClient): RegisteredTool {
83
87
  },
84
88
  },
85
89
  },
86
- async({ token, ethAddress, loginUrl, timeoutMs, useBrowser }) => {
90
+ async({ token, ethAddress, loginUrl, timeoutMs, useBrowser }, ctx: ToolContext) => {
87
91
  if(token && ethAddress) {
88
92
  throw new Error(
89
93
  'Provide only ONE of `token` or `ethAddress`, not both.',
@@ -136,7 +140,11 @@ export function authenticateTool(client: ReclaimOldClient): RegisteredTool {
136
140
  )
137
141
  }
138
142
 
139
- const result = await captureFirebaseTokenViaCdp({ loginUrl, timeoutMs })
143
+ const result = await captureFirebaseTokenViaCdp({
144
+ loginUrl,
145
+ timeoutMs,
146
+ onProgress: ctx.reportProgress,
147
+ })
140
148
  const identity: OldIdentity = { kind: 'bearer', token: result.token }
141
149
  await validate(identity)
142
150
  persistIdentity(identity)
@@ -38,7 +38,7 @@ export function resolveAttestorUrl(override?: string): string {
38
38
  )
39
39
  }
40
40
 
41
- /** Map a verification's provider recipe straight to the attestor's HTTP
41
+ /** Map a provider's recipe straight to the attestor's HTTP
42
42
  * params — never synthesized from captured traffic. */
43
43
  export function buildHttpParams(
44
44
  provider: ReclaimProvider,