@everfur/cli 0.2.0 → 0.4.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.
package/README.md CHANGED
@@ -14,9 +14,14 @@ template) includes a 2 KB loader and gets the chat in an iframe on Everfur's ori
14
14
  a Shopify theme or app gets the Everfur app's `Everfur chat` block, with Shopify vouching for the
15
15
  signed-in customer so no backend mints. `--embedding in-page|frame` overrides the detected embedding.
16
16
  A capability the web cannot render (photo in the page, anything but chat in the frame) is reported as
17
- blocked by the platform, beside the steps for the rest. `everfur verify --origin https://your.page`
18
- adds the checks a web integration needs: the embed allowlist for that origin, the CORS exposure a
19
- browser depends on, the tenant's session policy, and the loader of the key's environment.
17
+ blocked by the platform, beside the steps for the rest. For a page that includes the loader,
18
+ `everfur verify --origin https://your.page --embedding frame` adds the checks a framed page needs: the embed
19
+ allowlist for that origin, the CORS exposure a browser depends on, the tenant's session policy, and the loader
20
+ of the key's environment. An in-page integration calls the API from its own origin, so it passes `--origin` too, with
21
+ `--embedding in-page`: the preflight is of a widget request with the headers the SDK sends, the exposure and the
22
+ session policy are checked, and the embed fence and the loader, which it never uses, are reported `N/A` rather than
23
+ failed. Only a named `--embedding in-page` does that: with none named every check runs, and a project whose page
24
+ includes the Everfur loader is checked as the frame it embeds.
20
25
 
21
26
  **The generated surface matches your own theme when the plan can read it, and says so when it cannot.** The
22
27
  plan reads, statically and never by executing anything, a Tailwind v4 `@theme` block, a `tailwind.config.{js,ts}`
@@ -37,14 +42,22 @@ config that runs code when it loads) is low: no theme is written, and the step n
37
42
  offers the manual `theme={{ ... }}` snippet. Over a remote MCP pass the config or CSS text as `theme_source`. The
38
43
  tenant's console branding is merged with the emitted theme per field (a locked field keeps the console's value;
39
44
  the console fills what the theme leaves unset); the plan holds no branding credential and never changes it.
45
+ Branding v2 (SDK 0.9.0): a success, warning or info colour and a heading stack the theme declares by name
46
+ (`--success`, `--color-warning`, `--font-heading`, `--font-display`, Tailwind v3 `colors.info` or
47
+ `fontFamily.heading`) are written too, as `successColor`, `warningColor`, `infoColor` (with their dark twins) and
48
+ `headingFontFamily`; a theme that declares none gets none. The logo and the chat's assistant avatar are uploaded in
49
+ the partner console, the only images the SDK shows, so a logo read from the project is named as the image to upload
50
+ rather than written. Custom fonts apply in-page only: the hosted frame keeps Everfur's fonts.
40
51
 
41
52
  **The chat and records mounts match the Everfur app, and the host seams are generated with them.** Chat has the
42
53
  header pet switcher (it appears by itself once two or more pets are registered), quick prompts, citations, thumbs
43
54
  feedback, the urgency banner and the history drawer built in. On an emergency or schedule-soon reply the banner's
44
55
  Find a vet pill opens the Everfur vet visit entry when Everfur has it on for your client, else calls `onFindVet`, else
45
- does not render: every in-page chat mount passes `onFindVet={findVet}`, which opens a maps search until you replace
46
- it with your own vet finder. A frame takes no `onFindVet`, so there the pill appears only while Everfur has its vet
47
- visit entry on for your client. The SDK bundles no picker and no
56
+ does not render. No generated mount passes `onFindVet`, so the plan writes no vet finder of its own that sends the
57
+ member to a page outside your app; the commented `onFindVet` line beside each in-page chat mount is for an app with
58
+ its own in-app vet finder (on React Native, passing it also replaces the Everfur vet entry on the check-in card). A
59
+ frame takes no `onFindVet`,
60
+ so there the pill appears only while Everfur has its vet visit entry on for your client. The SDK bundles no picker and no
48
61
  clipboard module, so an EXPO plan mounts `<EverfurChat attachments={{ pick: pickChatPhoto }}
49
62
  onCopy={copyText} />` and writes both helpers into `everfur/EverfurRoot.tsx`, the module the app mounts from its
50
63
  own entry (`app/_layout.tsx` in an Expo Router app, `App.tsx` otherwise): in an Expo app `pickChatPhoto` opens the system
@@ -70,24 +83,52 @@ because it asked for one) and always carries the size when the picker reports it
70
83
  records upload, so no image picker is wired there. The system document UI is not the media library and needs no
71
84
  permission on either platform, so unlike the chat photo picker there is nothing to request before it opens. On the web the
72
85
  browser is the picker and the speech engine: photo attachments and voice dictation are on by default, and
73
- `attachments={false}` and `dictation={false}` turn them off. Records is the app's screen flow: `<EverfurRecords>`
74
- opens on the pet's dashboard and owns its own stack (the hardware back key pops it), every screen is exported by
75
- name for your own navigator, the mount passes `petName={selectedPet.name}` for headings such as
76
- `{petName}'s lab trends` (and `onCopy={copyText}` on React Native, for the share sheet), and
77
- `initialScreen={{ name: 'request', requestId }}` opens a request directly, which is what the generated
78
- notification router's `record_request` handler says to do. Lab markers, share links, the record PDF link, fact
86
+ `attachments={false}` and `dictation={false}` turn them off. On React Native records is the app's screen flow:
87
+ `<EverfurRecords>` opens on the pet's dashboard and owns its own stack (the hardware back key pops it), and
88
+ `initialScreen={{ name: 'request', requestId }}` opens a request directly, which is what the generated notification
89
+ router's `record_request` handler says to do. On the web records is a page, the shape of Everfur's own web records
90
+ page: the generated `EverfurRecordsSurface` renders `EverfurRecordsPage` and opens what it links to (the full record,
91
+ the timeline, an upload) in the records flow in the same place, with no route added to your app; the page holds no clinic
92
+ request of its own, so `records-clinic` mounts beside it. Every screen is exported by name for your own navigator,
93
+ and the plan's `mount-records-screens` step names them per platform. The mount passes `petName={selectedPet.name}` for
94
+ headings such as `{petName}'s lab trends` (and `onCopy={copyText}` on React Native, for the share sheet). An Expo
95
+ records mount also passes `onOpenUrl={openRecordUrl}`, which handles the share sheet the way the Everfur app does: a
96
+ share link goes to the OS share sheet, and the record PDF is downloaded into the app's cache (`expo-file-system`),
97
+ shared as a file (`expo-sharing`) and deleted, so neither opens a browser. A bare React Native mount passes none, and
98
+ the SDK's default opens both in the system browser until you pass your own. Lab markers, share links, the record PDF link, fact
79
99
  edits and the signature gallery need the platform flag `partner.records_full_depth_enabled`; while it is off the
80
100
  SDK hides them.
81
101
 
82
- **Every plan that writes code names its environment.** `--environment staging|production` (or
102
+ **The vet visit runs inside your app.** A plan that mounts `televet` installs `@everfur/sdk@^0.9.0`, a release
103
+ whose `VetVisitButton`, `useVetVisit().open()` and `openVisit()`, and every vet entry inside chat open the visit in
104
+ the app by default, and writes the two things that default needs: `registerEverfurDaily(...)` once at load, from
105
+ `@everfur/sdk/televet/call` over `@daily-co/react-native-daily-js` (React Native) or from
106
+ `@everfur/sdk/web/televet/call` over `import('@daily-co/daily-js')` (the web), and one `<EverfurTelevetHost />`
107
+ inside the provider, where every visit opens: a full-screen modal on React Native, a dialog in the SDK's own DOM
108
+ subtree on the web. The button passes no `returnTo` and no `prefer`, the file registers no presenter and reads no
109
+ return URL, and the chat mount gets the member's state as `vetUsState`. On React Native the plan installs the Daily
110
+ modules the consumer app runs and the native setup (the Expo config plugin and a clean prebuild, or the bare
111
+ manifest and Info.plist edits), and the notifications file opens a tapped visit in the app through the same hook.
112
+ The booking lists vets only for the member's US state and the SDK never reads a device location, so the file's
113
+ `YOUR_USER_US_STATE` reaches the button, the chat and the notifications bridge, and `everfur verify --project-root`
114
+ fails while it is still `null` as shipped. The hosted visit in the system browser or a new tab is the SDK's explicit
115
+ `prefer="redirect"`, which no generated file passes. A plan without `televet` installs the same `@everfur/sdk@^0.9.0`
116
+ and none of the call.
117
+
118
+ <!-- Owner-approved 2026-10-01 (production sandbox plan, PC-1/PC-2): what `test` and the key prefixes mean. -->
119
+ **Every plan that writes code names its environment.** `--environment test|staging|production` (or
83
120
  `environment` over MCP) is required and never assumed; generated files read the publishable key and base
84
121
  URL from the framework's own variables (`NEXT_PUBLIC_`, `VITE_`, `EXPO_PUBLIC_`) and the plan writes the
85
- names into `.env.example`. Test mode is not a third deployment: it is your **sandbox tenant on the staging
86
- API** (`https://api-staging.everfur.com/api/v1`), issued an ordinary `pk_live_` key, whose entitlements
87
- report `livemode: false`. No deployed environment accepts a `pk_test_` key. `--environment test` is
88
- accepted and resolves to that same staging deployment. The web framework is detected from the project: the Next.js App Router gets a
122
+ names into `.env.example`. `test` is your **sandbox tenant on the production API**
123
+ (`https://api.everfur.com/api/v1`), issued a `pk_test_` key, whose entitlements report `livemode: false`;
124
+ `staging` is your sandbox tenant on the staging API (`https://api-staging.everfur.com/api/v1`), for early
125
+ access; `production` is your live tenant and its `pk_live_` key. The key prefix names the tenant mode: a
126
+ sandbox created before `pk_test_` keys were issued may still carry `pk_live_`. The web framework is detected from the project: the Next.js App Router gets a
89
127
  `'use client'` surface and a Route Handler, the Pages Router a component and an API route, Vite with React
90
- reads `import.meta.env`. `everfur verify --surfaces chat,records,televet,theme` exercises each surface
128
+ reads `import.meta.env` and gets `server/dev-server.ts`, a development server for its session route (a Vite build has
129
+ no server, so the plan also says where the route goes in production). A signed-in React Native app gets the same server
130
+ on its own route, with the URL that reaches it from the iOS simulator, the Android emulator and a device, and its
131
+ server-only names in `server/.env.example`, never in the app's env file. `everfur verify --surfaces chat,records,televet,theme` exercises each surface
91
132
  with the minted session (Televet mints a handoff only on a sandbox tenant, and a surface Everfur has not
92
133
  switched on is reported `GATED`, not failed); `--project-root <path>` adds offline checks for a secret key
93
134
  in source or client build output and for `'use client'`. `everfur diagnose --bundle` prints one support
@@ -95,20 +136,40 @@ document with no credential value in it.
95
136
 
96
137
  ## The fastest path: let your agent do it
97
138
 
98
- ### Hosted MCP: nothing to configure
139
+ ### Hosted MCP: nothing to install
99
140
 
100
- Everfur runs the MCP server for you at `https://mcp.everfur.com/mcp` (staging, where your sandbox tenant
101
- lives while you build: `https://mcp-staging.everfur.com/mcp`). Add that URL to your editor as a remote
102
- (Streamable HTTP) MCP server. When the editor connects, it sends you to the Everfur partner console to sign
103
- in; from then on every tool runs as your tenant. There is no key, token, variable or file to set.
141
+ Everfur runs the MCP server for you, so there is no key, token, variable or file to set. It is reachable at
142
+ two endpoints, one per deployment:
143
+
144
+ ```
145
+ https://mcp.everfur.com/mcp production: your sandbox (test) tenant and your live tenant
146
+ https://mcp-staging.everfur.com/mcp staging: an early-access sandbox tenant on the staging API
147
+ ```
148
+
149
+ Use the one for the deployment your tenant is on. A client pointed at the other one signs in to a different
150
+ deployment and does not fall back to anything.
151
+
152
+ Add the URL to your editor as a remote (Streamable HTTP) MCP server. You paste the URL yourself; there is
153
+ nothing to search for in a connector directory. Adding it is the whole configuration step:
104
154
 
105
155
  - **Claude Code:** `claude mcp add --transport http everfur https://mcp.everfur.com/mcp`
106
156
  - **Cursor:** `.cursor/mcp.json` with `{"mcpServers":{"everfur":{"url":"https://mcp.everfur.com/mcp"}}}`
107
157
  - **VS Code:** `.vscode/mcp.json` with `{"servers":{"everfur":{"type":"http","url":"https://mcp.everfur.com/mcp"}}}`
108
158
  - **Codex:** a streamable HTTP server entry with that URL in `~/.codex/config.toml` (`[mcp_servers.everfur]`, `url`)
109
- - **claude.ai:** a custom connector with that URL. Click by click, including what the sign-in asks and how to
110
- tell it worked:
111
- [docs/ADD_THE_EVERFUR_CONNECTOR_IN_CLAUDE.md](docs/ADD_THE_EVERFUR_CONNECTOR_IN_CLAUDE.md)
159
+ - **claude.ai:** add it as a **custom connector** at <https://claude.ai/customize/connectors> (**+**, then
160
+ **Add custom connector**): name it `Everfur` and paste the URL exactly, all lowercase, `https://`, `/mcp` on
161
+ the end, no trailing slash. Leave **Request headers** empty: Everfur never wants a key there. Click
162
+ **Connect** and sign in to the partner console. The approval page names Claude as the application asking
163
+ and shows the address Claude will be sent back to, `https://claude.ai/api/mcp/auth_callback` or
164
+ `https://claude.com/api/mcp/auth_callback`; anything else is not Everfur's consent page. Connecting does not
165
+ switch it on for every conversation: in a chat, **+**, then **Connectors**, then toggle **Everfur**. To check
166
+ it worked, ask Claude to explain the Everfur error code `authExpired`: an answer through
167
+ `everfur_explain_error` means the connection itself is good.
168
+
169
+ There is no key to type because the server authenticates you instead. On first connect it answers with a
170
+ `401` naming its OAuth resource metadata, your client registers itself automatically (dynamic client
171
+ registration, RFC 7591), and you are sent to the Everfur partner console to sign in and approve. From then
172
+ on every tool runs as the tenant of that console session.
112
173
 
113
174
  The tools are the same five as below, plus eight that read your own tenant, with these differences that follow
114
175
  from where the server runs:
@@ -134,8 +195,7 @@ from where the server runs:
134
195
  `everfur_list_webhook_deliveries`, `everfur_get_webhook_delivery`, `everfur_list_events`,
135
196
  `everfur_get_usage_summary`, `everfur_get_records_status_summary` and `everfur_search_docs` (the error
136
197
  registry, capabilities and guide of your contract). None returns a signing secret, a key value or a
137
- member's content. The routes behind them are specified in
138
- [docs/HOSTED_TENANT_API.md](docs/HOSTED_TENANT_API.md).
198
+ member's content.
139
199
 
140
200
  Everything below is for running the server yourself, which is what you want when the agent runs beside the
141
201
  project (so `project_root` works) or when your organisation keeps its own MCP process.
@@ -173,8 +233,8 @@ The four values, and where they come from:
173
233
 
174
234
  | Variable | What it is |
175
235
  |---|---|
176
- | `EVERFUR_API_BASE_URL` | The staging API while you build. Your test tenant is a **sandbox tenant on staging**; there is no separate test deployment. Going live changes this to `https://api.everfur.com/api/v1`. |
177
- | `EVERFUR_PARTNER_KEY` | The sandbox tenant's publishable key. It is a `pk_live_` key (no deployed environment accepts `pk_test_`); what makes the tenant a sandbox is the tenant, and its entitlements report `livemode: false`. |
236
+ | `EVERFUR_API_BASE_URL` | The API of your sandbox tenant while you build: `https://api.everfur.com/api/v1` for a test (production sandbox) tenant, the staging API for an early-access one. Going live uses `https://api.everfur.com/api/v1`. |
237
+ | `EVERFUR_PARTNER_KEY` | The sandbox tenant's publishable key, `pk_test_...` (a sandbox created before `pk_test_` keys may still carry `pk_live_`); its entitlements report `livemode: false`. |
178
238
  | `EVERFUR_DOCS_KEY` | A read-only docs key (`dk_partner_...`) that opens `GET /partners/contract` and nothing else. |
179
239
  | `EVERFUR_SESSION_TOKEN` | A short-lived session your backend minted for a test user. Only `everfur_verify_integration` reads it. |
180
240
 
@@ -183,8 +243,10 @@ live changes every value in the block: the production base URL, and the producti
183
243
  key and docs key (its secret key changes on your backend at the same time). Production keys are issued only
184
244
  after `everfur_verify_integration` passes on the sandbox tenant.
185
245
 
186
- `EVERFUR_DOCS_KEY` is required. The Everfur contract (the error registry, capability packs, credential
187
- shapes and the decision trees the planner walks) is not included in this package: the tools fetch it from
246
+ `EVERFUR_DOCS_KEY` is required here, for the server you run yourself. The Everfur-hosted connector above
247
+ needs no docs key: it signs in with OAuth and is served the contract as your tenant. The Everfur contract
248
+ (the error registry, capability packs, credential shapes and the decision trees the planner walks) is not
249
+ included in this package: the tools fetch it from
188
250
  `GET /partners/contract` on `EVERFUR_API_BASE_URL`, presenting a partner docs key (`dk_partner_...`), and
189
251
  cache it on disk (see [Credentials](#credentials)). A partner admin mints a docs key with
190
252
  `POST /api/v1/partners/docs-keys` (list with `GET`, revoke with `DELETE /api/v1/partners/docs-keys/{key_id}`);
@@ -217,7 +279,12 @@ answer, naming each one:
217
279
  Node backend gets the files; any other backend gets the recipe.
218
280
 
219
281
  That is three questions for an anonymous integration and four for `partner-user-ref`. Once answered, the
220
- plan's steps include the complete integration file, ready to write.
282
+ plan's steps include the complete integration file, ready to write. Over MCP the tool first returns a small
283
+ summary with a `plan_id` and a number of `pages` (the goal, the detected facts, each step's id, title, one-line
284
+ intent and page, and the checklist); the same call with `plan_id` and `page` returns that page's steps in full,
285
+ commands and file bodies included, and page 1 also the detection, warnings and checklist in full. `steps`
286
+ (step ids) in place of `page` still returns those steps, a page at a time. `everfur plan` prints the whole plan at
287
+ once.
221
288
 
222
289
  The explicit `everfur-mcp` command is intentional: `@everfur/mcp` also ships `everfur-mcp-http` for a
223
290
  server deployment, so bare `npx @everfur/mcp` can no longer safely guess which executable you mean.
@@ -228,7 +295,7 @@ For a partner-operated cloud/server deployment, run the guarded Streamable HTTP
228
295
 
229
296
  ```bash
230
297
  export EVERFUR_API_BASE_URL="https://api-staging.everfur.com/api/v1"
231
- # No deployed environment accepts pk_test_: use the pk_live_ key of a tenant created as a sandbox.
298
+ # The publishable key of a tenant created as a sandbox (pk_test_..., or pk_live_... on an older staging sandbox).
232
299
  export EVERFUR_PARTNER_KEY="pk_live_..."
233
300
  export EVERFUR_DOCS_KEY="dk_partner_..."
234
301
  # For verification only: a short-lived session your backend minted. The server never reads the secret key.
@@ -245,14 +312,14 @@ put either secret in an agent prompt, a browser, or a client bundle. The HTTP pr
245
312
  scale behind a load balancer. It does **not** replace a platform OAuth service: use this mode when one
246
313
  partner owns the deployment and its credentials.
247
314
 
248
- For release gates, reverse-proxy requirements, negative tests, and the staged production rollout, use the
249
- [remote MCP staging and production runbook](docs/REMOTE_MCP_STAGING_PRODUCTION_RUNBOOK.md).
250
-
251
315
  Five tools are served: `everfur_plan_integration`, `everfur_verify_integration`, `everfur_diagnose`,
252
316
  `everfur_explain_error`, and `everfur_send_test_message`. Each is a body of the CLI, so the two surfaces
253
317
  answer the same question the same way. `everfur_explain_error` is the one to reach for when a response
254
- carries an error code: it needs no project, reads the registry from the fetched contract, and refuses a code
255
- it does not recognise instead of explaining it from the shape of the string.
318
+ carries an error code: it needs no project, reads the registry from the fetched contract, answers offline the SDK's
319
+ own codes (`EverfurError` and its closed sub-enums such as `AttachmentErrorCode` and `VetVisitErrorReason`), the
320
+ diagnostics reason codes, the CLI's own error codes and the `error.code` these tools return (`VERIFICATION_FAILED`,
321
+ `PLAN_CHANGED` and the rest), and refuses a code it does not recognise instead of explaining it from the shape of the
322
+ string. `everfur explain-error` gives the same answers.
256
323
 
257
324
  Every tool declares an `outputSchema` and returns `structuredContent`. A failed call carries one envelope
258
325
  whatever the tool: `error.code`, `error.retryable`, `error.next_action` (a closed vocabulary),
@@ -275,7 +342,7 @@ npx -y -p @everfur/cli everfur plan
275
342
 
276
343
  Plain `npx @everfur/cli` does not work, and the error it gives ("could not determine executable to
277
344
  run") does not explain why: this package ships four binaries, `everfur`, `everfur-mcp`,
278
- `everfur-mcp-http` and `everfur-mcp-hosted` (the entry Everfur runs at `mcp.everfur.com`), and npx only
345
+ `everfur-mcp-http` and `everfur-mcp-hosted` (the entry Everfur runs itself), and npx only
279
346
  picks a binary on its own when there is exactly one, or when one is named after the package. `-p` names
280
347
  the package and the word after it names the binary.
281
348
 
@@ -288,7 +355,7 @@ the package and the word after it names the binary.
288
355
  | `everfur diagnose` | Explain why each capability is or is not available to this caller. |
289
356
  | `everfur explain-error` | Explain an API error code: cause, retryability, and next action. Reads the registry from `--contract <file>` when given, otherwise from the contract fetched with `EVERFUR_DOCS_KEY`. |
290
357
  | `everfur contract validate` | Validate a contract-public projection: envelope shape, then digest. |
291
- | `everfur sandbox create` | Provision a partner secret key (Everfur-internal; requires an admin token). |
358
+ | `everfur sandbox create` | Everfur-internal (requires an admin token): mints a partner secret key and, because no claim flow exists to hand it over safely, revokes it again and exits `8`. |
292
359
 
293
360
  `--json` on any command emits a machine-readable document on stdout, which is what you want in CI.
294
361
 
@@ -300,7 +367,7 @@ CI is invited to branch on these. An existing code never changes meaning.
300
367
  |---|---|---|
301
368
  | `0` | OK | The command produced a complete answer. |
302
369
  | `1` | INTERNAL_ERROR | A bug in the CLI, or an unreadable filesystem. |
303
- | `2` | USAGE_ERROR | Malformed invocation: unknown command or flag, missing flag value. |
370
+ | `2` | USAGE_ERROR | Malformed invocation: unknown command or flag, missing flag value, or an argument value the command does not recognise (for example `explain-error <unknown code>`). |
304
371
  | `3` | UNDERSPECIFIED | The CLI refused to guess. Required facts were missing; supply them and retry. |
305
372
  | `4` | INVALID_CONTRACT | A named artifact was missing, unparseable, or failed schema validation. |
306
373
  | `5` | DIGEST_MISMATCH | A loaded artifact was well shaped but its digest did not match its content. |
@@ -308,6 +375,7 @@ CI is invited to branch on these. An existing code never changes meaning.
308
375
  | `7` | VERIFICATION_FAILED | `verify` ran and at least one hard assertion failed. |
309
376
  | `8` | SECRET_UNCLAIMABLE | `sandbox create` minted a key but no claim flow exists to hand it over safely. |
310
377
  | `9` | BLOCKED | Nothing you asked for can be built today: the contract blocks every capability you named, or the SDK ships no entry point for this project shape. |
378
+ | `10` | CONTRACT_CREDENTIAL_MISSING | The command needs the Everfur contract and has none: no `--contract` file, no `EVERFUR_CONTRACT_PUBLIC`, and no `EVERFUR_DOCS_KEY` to fetch it with. The error says where a docs key comes from, and that the Everfur-hosted MCP connector (OAuth) needs none. |
311
379
 
312
380
  ---
313
381
 
@@ -339,7 +407,7 @@ with no network call and no credential.
339
407
  | Variable | Used for |
340
408
  |---|---|
341
409
  | `EVERFUR_API_BASE_URL` | Everything that touches the network. No default host. |
342
- | `EVERFUR_PARTNER_KEY` | Your publishable key (`pk_live_...`). Identifies your tenant. A sandbox tenant is issued the same `pk_live_` form; no deployed environment accepts `pk_test_`. |
410
+ | `EVERFUR_PARTNER_KEY` | Your publishable key (`pk_test_...` or `pk_live_...`). Identifies your tenant. The prefix names the tenant mode: `pk_test_` for a sandbox tenant, `pk_live_` for a live one; a sandbox created before `pk_test_` keys may still carry `pk_live_`. |
343
411
  | `EVERFUR_PARTNER_SECRET_KEY` | Session minting, server side only. Never ship it in an app, and never put it in an MCP server's environment. `everfur verify` mints with it locally only when `EVERFUR_SESSION_TOKEN` is not set. |
344
412
  | `EVERFUR_SESSION_TOKEN` | A short-lived session your backend minted. `everfur verify` and `everfur_verify_integration` use it instead of minting (the MCP tool verifies only this way); `everfur diagnose` uses it for the signed path. |
345
413
  | `EVERFUR_DOCS_KEY` | Your partner docs key (`dk_partner_...`). Fetches the contract; accepted by no other route. Minted with `POST /api/v1/partners/docs-keys`. |
@@ -350,10 +418,11 @@ with no network call and no credential.
350
418
  | `EVERFUR_MCP_HTTP_HOST` / `PORT` / `PATH` | Optional HTTP listener configuration; defaults to `127.0.0.1:3100/mcp`. `EVERFUR_MCP_HTTP_PORT` is also the hosted service's port. |
351
419
  | `EVERFUR_MCP_HTTP_ALLOWED_HOSTS` | Required for any non-loopback bind; comma-separated public hostnames only. |
352
420
  | `EVERFUR_MCP_HTTP_ALLOWED_ORIGINS` | Optional browser-origin allowlist. Origins are rejected unless explicitly listed. |
353
- | `MCP_PUBLIC_URL` | Everfur-internal: the FULL endpoint URL of the hosted service (`everfur-mcp-hosted`), path included (`https://mcp.everfur.com/mcp`), exactly as a partner pastes it into Claude. It is the OAuth resource every token names and the base of every 401's metadata pointer; a bare origin is refused at start. With `EVERFUR_API_BASE_URL`, `EVERFUR_MCP_HTTP_PORT` and `MCP_TRUSTED_PROXY_HOPS` it is the hosted task's whole environment. Partners do not set this. |
421
+ | `MCP_PUBLIC_URL` | Everfur-internal: the FULL endpoint URL of the hosted service (`everfur-mcp-hosted`), path included (`https://mcp-staging.everfur.com/mcp` on the staging deployment), exactly as a partner pastes it into Claude. It is the OAuth resource every token names and the base of every 401's metadata pointer; a bare origin is refused at start. With `EVERFUR_API_BASE_URL`, `EVERFUR_MCP_HTTP_PORT` and `MCP_TRUSTED_PROXY_HOPS` it is the hosted task's whole environment. Partners do not set this. |
354
422
  | `MCP_ALLOWED_HOSTS` | Everfur-internal, optional: extra `Host` values the hosted service accepts beside its own. `MCP_PORT` is read only when `EVERFUR_MCP_HTTP_PORT` is unset. Partners do not set these. |
355
- | `MCP_ALLOWED_ORIGINS` | Everfur-internal, optional: extra `Origin` values the hosted service's MCP endpoint accepts beside `https://claude.ai` and `https://claude.com`; any other `Origin` is refused with `403`. A browser page at an accepted origin gets its CORS preflight answered and can read the `401` challenge. Partners do not set this. |
423
+ | `MCP_ALLOWED_ORIGINS` | Everfur-internal, optional: extra `Origin` values the hosted service's MCP endpoint accepts beside `https://claude.ai`, `https://claude.com` and `https://chatgpt.com`; any other `Origin` is refused with `403`. A browser page at an accepted origin gets its CORS preflight answered and can read the `401` challenge. Partners do not set this. |
356
424
  | `MCP_TRUSTED_PROXY_HOPS` | Everfur-internal, optional: how many proxies appended to `X-Forwarded-For` (`1` behind the load balancer). Keys the hosted service's per-client budget of unauthenticated failures; `0` (default) uses the socket address. Partners do not set this. |
425
+ | `OPENAI_APPS_CHALLENGE_TOKEN` | Everfur-internal, optional: the domain verification token OpenAI's plugin portal generates. Set, the hosted service serves it as the whole plain-text body of `/.well-known/openai-apps-challenge` on its own host; unset or blank, that path is a `404`. Never logged. Partners do not set this. |
357
426
 
358
427
  **Getting keys:** Everfur keys are currently provisioned by the Everfur team during partner onboarding;
359
428
  there is no self-serve signup yet. Contact [support@everfur.com](mailto:support@everfur.com) to get set
@@ -361,8 +430,14 @@ up. The publishable key is designed to ship inside your app; the secret key is n
361
430
 
362
431
  ## Documentation
363
432
 
364
- The full integration guide, API reference, error catalog, and testing guide ship with the SDK:
365
- [`@everfur/sdk` docs](https://github.com/Everfur-Health/everfur-sdk-rn/tree/main/docs/partner-integration).
433
+ The integration guide, API reference, error catalog and testing guide are at
434
+ [sdk.everfur.com/v1/docs](https://sdk.everfur.com/v1/docs/index.html): adoption paths, the HTTP contract, API
435
+ keys, the launch checklist and troubleshooting.
436
+
437
+ This page used to link to `github.com/Everfur-Health/everfur-sdk-rn`, which is a PRIVATE repository: the
438
+ link returned 404 for every partner who clicked it, since a partner is outside Everfur by definition. If
439
+ a doc you need is not on the site yet, ask [support@everfur.com](mailto:support@everfur.com) rather than
440
+ looking for a repository you cannot open.
366
441
 
367
442
  ## License
368
443