@everfur/cli 0.1.0 → 0.2.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 +295 -19
- package/dist/{chunk-67H43MI6.js → chunk-4UB26D67.js} +295 -510
- package/dist/chunk-FC23PLWG.js +161 -0
- package/dist/chunk-KQICUK54.js +12852 -0
- package/dist/chunk-SJUEHHU6.js +893 -0
- package/dist/chunk-XMIERMOW.js +2512 -0
- package/dist/everfur-mcp-container.d.ts +2 -0
- package/dist/everfur-mcp-container.js +60 -0
- package/dist/everfur-mcp-hosted.d.ts +2 -0
- package/dist/everfur-mcp-hosted.js +28 -0
- package/dist/everfur-mcp-http.d.ts +2 -0
- package/dist/everfur-mcp-http.js +21 -0
- package/dist/everfur-mcp.js +11 -365
- package/dist/everfur.js +4 -3
- package/dist/index.d.ts +875 -41
- package/dist/index.js +2 -21
- package/package.json +5 -4
- package/schema/diagnosis.v1.schema.json +41 -0
- package/schema/plan.v1.schema.json +90 -4
- package/schema/verification.v1.schema.json +7 -3
- package/contract/contract-public.json +0 -1335
- package/dist/chunk-KQZ6ZXL7.js +0 -3994
package/README.md
CHANGED
|
@@ -3,33 +3,278 @@
|
|
|
3
3
|
Plan, verify, and diagnose an [Everfur](https://everfur.com) pet-health integration from the terminal,
|
|
4
4
|
and serve the same tools to your coding agent over MCP.
|
|
5
5
|
|
|
6
|
-
Everfur lets partner apps embed veterinary chat, health records, and pet-state surfaces via
|
|
7
|
-
`@everfur/sdk` (React Native
|
|
6
|
+
Everfur lets partner apps and pages embed veterinary chat, health records, and pet-state surfaces via
|
|
7
|
+
`@everfur/sdk` (React Native and Expo; the web through `@everfur/sdk/web` and a hosted frame; Flutter
|
|
8
|
+
through `everfur_sdk`). This CLI is the integration companion: it reads your project, plans the
|
|
8
9
|
integration, exercises the live wire, and explains what it finds.
|
|
9
10
|
|
|
11
|
+
**On the web the plan follows what the project is.** A React tree (react-dom in `package.json`) embeds the
|
|
12
|
+
SDK's components in the page and installs the package; any other page (Vue, Svelte, a static page, a CMS
|
|
13
|
+
template) includes a 2 KB loader and gets the chat in an iframe on Everfur's origin, installing nothing;
|
|
14
|
+
a Shopify theme or app gets the Everfur app's `Everfur chat` block, with Shopify vouching for the
|
|
15
|
+
signed-in customer so no backend mints. `--embedding in-page|frame` overrides the detected embedding.
|
|
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.
|
|
20
|
+
|
|
21
|
+
**The generated surface matches your own theme when the plan can read it, and says so when it cannot.** The
|
|
22
|
+
plan reads, statically and never by executing anything, a Tailwind v4 `@theme` block, a `tailwind.config.{js,ts}`
|
|
23
|
+
(`theme.extend.colors`, `fontFamily`, `borderRadius`, with `hsl(var(--x))` resolved through your CSS), `:root`
|
|
24
|
+
custom properties (`--primary`, `--brand`, `--background`, `--radius`, `--font-sans` and their common spellings,
|
|
25
|
+
in a stylesheet or a static page's `<style>`), or a React Native theme or colours file (a react-navigation
|
|
26
|
+
`Theme`, the Expo template's `constants/Colors.ts`). What maps unambiguously is written as the SDK's `theme` prop
|
|
27
|
+
(`primaryColor`, `accentColor`, the dark variants, `brightness`, `surfaceStyle`, `radiusScale`, `fontFamily`, every
|
|
28
|
+
colour as `#RRGGBB`), each field citing the file and line it came from, and the plan's `match-theme` step names
|
|
29
|
+
the source. A React Native theme file's exact `fontFamily: '<Name>'` literals become the SDK's per-weight `fonts`
|
|
30
|
+
map (`regular`, `medium`, `bold`: family names only, mapped by the weight word in each name, as in `Azeret-Regular`
|
|
31
|
+
or `Inter_500Medium`; the one plain family a file names stands in for the regular face and is marked
|
|
32
|
+
`REPLACE: verify`); the app registers the faces itself, the SDK bundles no font and synthesizes no weight on a named
|
|
33
|
+
face, and a family the file does not name is never written. The frame's init never carries the map, since a family
|
|
34
|
+
the page registered does not exist on Everfur's origin. A primary and a background read as literals from one source is high confidence; part of the picture
|
|
35
|
+
is medium, and every field is then marked `REPLACE: verify`; anything less (nothing found, a computed value, a
|
|
36
|
+
config that runs code when it loads) is low: no theme is written, and the step names the exact missing facts and
|
|
37
|
+
offers the manual `theme={{ ... }}` snippet. Over a remote MCP pass the config or CSS text as `theme_source`. The
|
|
38
|
+
tenant's console branding is merged with the emitted theme per field (a locked field keeps the console's value;
|
|
39
|
+
the console fills what the theme leaves unset); the plan holds no branding credential and never changes it.
|
|
40
|
+
|
|
41
|
+
**The chat and records mounts match the Everfur app, and the host seams are generated with them.** Chat has the
|
|
42
|
+
header pet switcher (it appears by itself once two or more pets are registered), quick prompts, citations, thumbs
|
|
43
|
+
feedback, the urgency banner and the history drawer built in. On an emergency or schedule-soon reply the banner's
|
|
44
|
+
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
|
|
48
|
+
clipboard module, so an EXPO plan mounts `<EverfurChat attachments={{ pick: pickChatPhoto }}
|
|
49
|
+
onCopy={copyText} />` and writes both helpers into `everfur/EverfurRoot.tsx`, the module the app mounts from its
|
|
50
|
+
own entry (`app/_layout.tsx` in an Expo Router app, `App.tsx` otherwise): in an Expo app `pickChatPhoto` opens the system
|
|
51
|
+
picker with `expo-image-picker` (`launchImageLibraryAsync({ mediaTypes: ['images'], quality: 0.85, selectionLimit:
|
|
52
|
+
1 })`, `launchCameraAsync({ quality: 0.85 })` for the camera) and never calls `requestMediaLibraryPermissionsAsync`
|
|
53
|
+
first, because a pre-request that is denied once disables the picker for good, and `copyText` is
|
|
54
|
+
`expo-clipboard`'s `setStringAsync`, and `pickRecordsDocument` opens the system document UI with
|
|
55
|
+
`expo-document-picker` for the records upload; the plan's `install-host-modules` step names the three modules as
|
|
56
|
+
optional to the SDK, one sentence each. A bare React Native app passes **neither** prop and imports neither package: React
|
|
57
|
+
Native ships no picker and no clipboard, so anything emitted as live code would be a stub, and a prop wired to a
|
|
58
|
+
stub renders an attachment control that attaches nothing and a Copy control that copies nothing, while without the
|
|
59
|
+
props the SDK renders neither control. The same helpers are emitted there as commented recipes naming
|
|
60
|
+
`react-native-image-picker`, `@react-native-clipboard/clipboard` and `@react-native-documents/picker` (the current
|
|
61
|
+
name: `react-native-document-picker` is deprecated), each between its own named markers, beside the commented
|
|
62
|
+
`attachments`, `onCopy` and `onPickDocument` lines in the mounts; each recipe and its prop lines name each other, so
|
|
63
|
+
both halves are uncommented together once the packages are installed. `tests/contract/plan-compiles.test.ts`
|
|
64
|
+
compiles that uncommented result, so a recipe is held to the same standard as the code around it.
|
|
65
|
+
|
|
66
|
+
**What the records upload accepts** is read from the SDK, not assumed: `application/pdf` compared exactly, at most
|
|
67
|
+
25 MiB, and a handle with no `sizeBytes` is refused as oversize, so the generated picker asks the document UI for
|
|
68
|
+
PDFs, passes the type the OS reported (falling back to the file extension, never claiming `application/pdf` merely
|
|
69
|
+
because it asked for one) and always carries the size when the picker reports it. Photos are not an accepted
|
|
70
|
+
records upload, so no image picker is wired there. The system document UI is not the media library and needs no
|
|
71
|
+
permission on either platform, so unlike the chat photo picker there is nothing to request before it opens. On the web the
|
|
72
|
+
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
|
|
79
|
+
edits and the signature gallery need the platform flag `partner.records_full_depth_enabled`; while it is off the
|
|
80
|
+
SDK hides them.
|
|
81
|
+
|
|
82
|
+
**Every plan that writes code names its environment.** `--environment staging|production` (or
|
|
83
|
+
`environment` over MCP) is required and never assumed; generated files read the publishable key and base
|
|
84
|
+
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
|
|
89
|
+
`'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
|
|
91
|
+
with the minted session (Televet mints a handoff only on a sandbox tenant, and a surface Everfur has not
|
|
92
|
+
switched on is reported `GATED`, not failed); `--project-root <path>` adds offline checks for a secret key
|
|
93
|
+
in source or client build output and for `'use client'`. `everfur diagnose --bundle` prints one support
|
|
94
|
+
document with no credential value in it.
|
|
95
|
+
|
|
10
96
|
## The fastest path: let your agent do it
|
|
11
97
|
|
|
12
|
-
|
|
13
|
-
|
|
98
|
+
### Hosted MCP: nothing to install
|
|
99
|
+
|
|
100
|
+
Everfur runs the MCP server for you, so there is no key, token, variable or file to set. Today it is
|
|
101
|
+
reachable at one endpoint:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
https://mcp-staging.everfur.com/mcp
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
That is the staging deployment, and it is where your sandbox tenant lives while you build. **The production
|
|
108
|
+
endpoint `https://mcp.everfur.com/mcp` is not available yet:** the hostname does not resolve, so a client
|
|
109
|
+
pointed at it fails to connect rather than falling back to anything. Use the staging URL until Everfur tells
|
|
110
|
+
you production is live.
|
|
111
|
+
|
|
112
|
+
Add the URL to your editor as a remote (Streamable HTTP) MCP server. You paste the URL yourself; there is
|
|
113
|
+
nothing to search for in a connector directory. Adding it is the whole configuration step:
|
|
114
|
+
|
|
115
|
+
- **Claude Code:** `claude mcp add --transport http everfur https://mcp-staging.everfur.com/mcp`
|
|
116
|
+
- **Cursor:** `.cursor/mcp.json` with `{"mcpServers":{"everfur":{"url":"https://mcp-staging.everfur.com/mcp"}}}`
|
|
117
|
+
- **VS Code:** `.vscode/mcp.json` with `{"servers":{"everfur":{"type":"http","url":"https://mcp-staging.everfur.com/mcp"}}}`
|
|
118
|
+
- **Codex:** a streamable HTTP server entry with that URL in `~/.codex/config.toml` (`[mcp_servers.everfur]`, `url`)
|
|
119
|
+
- **claude.ai:** add it as a **custom connector** and paste the URL into the dialog. Click by click,
|
|
120
|
+
including what the sign-in asks and how to tell it worked:
|
|
121
|
+
[docs/ADD_THE_EVERFUR_CONNECTOR_IN_CLAUDE.md](docs/ADD_THE_EVERFUR_CONNECTOR_IN_CLAUDE.md)
|
|
122
|
+
|
|
123
|
+
There is no key to type because the server authenticates you instead. On first connect it answers with a
|
|
124
|
+
`401` naming its OAuth resource metadata, your client registers itself automatically (dynamic client
|
|
125
|
+
registration, RFC 7591), and you are sent to the Everfur partner console to sign in and approve. From then
|
|
126
|
+
on every tool runs as the tenant of that console session.
|
|
127
|
+
|
|
128
|
+
The tools are the same five as below, plus eight that read your own tenant, with these differences that follow
|
|
129
|
+
from where the server runs:
|
|
130
|
+
|
|
131
|
+
1. It cannot read your disk. Describe the project with `project_details` (dependencies, package manager and
|
|
132
|
+
file-presence markers, never source files) and, to match your theme, pass `theme_source` (the text of your
|
|
133
|
+
Tailwind config or the CSS that declares your colours; read statically, never executed). `project_root` and
|
|
134
|
+
`contract_path` are not offered there (sent anyway, they are refused with `LOCAL_PATH_NOT_READABLE`), and
|
|
135
|
+
the offline project checks of `everfur_verify_integration` and `everfur_diagnose` are not available there.
|
|
136
|
+
An omitted `environment` plans for the deployment your tenant is on.
|
|
137
|
+
2. `everfur_verify_integration` runs with a short-lived session the Everfur API mints for a synthetic test user
|
|
138
|
+
(`everfur-verify-...`) of your **sandbox** tenant on each call. Signed in as a live tenant, the session step
|
|
139
|
+
is reported `GATED` and the wire exercise does not run. `everfur_send_test_message` refuses a live tenant, as
|
|
140
|
+
it does everywhere.
|
|
141
|
+
3. The hosted service holds no secret key, no docs key and no per-tenant key. On every request it verifies
|
|
142
|
+
your editor's sign-in with the Everfur API and resolves your tenant's publishable key from that answer;
|
|
143
|
+
the contract is served to the signed-in partner. The Everfur API re-checks on every such call that the
|
|
144
|
+
signed-in user is still an admin of an active partner tenant, and the service re-asks it at most every
|
|
145
|
+
60 seconds, so a removed admin or a deactivated tenant loses access within a minute.
|
|
146
|
+
4. Eight read-only tools answer questions about your tenant, as the partner console does:
|
|
147
|
+
`everfur_get_tenant_status` (plan, features, mode, embed origins, entitlements, your publishable key's id
|
|
148
|
+
and prefix, Televet return keys and booking lead), `everfur_list_webhook_endpoints`,
|
|
149
|
+
`everfur_list_webhook_deliveries`, `everfur_get_webhook_delivery`, `everfur_list_events`,
|
|
150
|
+
`everfur_get_usage_summary`, `everfur_get_records_status_summary` and `everfur_search_docs` (the error
|
|
151
|
+
registry, capabilities and guide of your contract). None returns a signing secret, a key value or a
|
|
152
|
+
member's content. The routes behind them are specified in
|
|
153
|
+
[docs/HOSTED_TENANT_API.md](docs/HOSTED_TENANT_API.md).
|
|
154
|
+
|
|
155
|
+
Everything below is for running the server yourself, which is what you want when the agent runs beside the
|
|
156
|
+
project (so `project_root` works) or when your organisation keeps its own MCP process.
|
|
157
|
+
|
|
158
|
+
### Local MCP: the same block for every editor
|
|
159
|
+
|
|
160
|
+
The Everfur MCP server ships with this package (and standalone as `@everfur/mcp`). Add this one block to
|
|
161
|
+
your agent's MCP configuration. It is the same block for Claude Code, Cursor and VS Code; only the file it
|
|
162
|
+
goes in differs:
|
|
163
|
+
|
|
164
|
+
- **Claude Code:** `.mcp.json` at the project root (or `claude mcp add-json everfur '<the everfur entry>'`
|
|
165
|
+
for your user scope).
|
|
166
|
+
- **Cursor:** `.cursor/mcp.json` in the project (or `~/.cursor/mcp.json` for every project).
|
|
167
|
+
- **VS Code:** `.vscode/mcp.json` in the workspace, which wraps the same `everfur` entry under a `servers`
|
|
168
|
+
key instead of `mcpServers`.
|
|
14
169
|
|
|
15
170
|
```json
|
|
16
171
|
{
|
|
17
172
|
"mcpServers": {
|
|
18
173
|
"everfur": {
|
|
19
174
|
"command": "npx",
|
|
20
|
-
"args": ["-y", "@everfur/mcp"],
|
|
175
|
+
"args": ["-y", "-p", "@everfur/mcp", "everfur-mcp"],
|
|
21
176
|
"env": {
|
|
22
|
-
"EVERFUR_API_BASE_URL": "https://api.everfur.com/api/v1",
|
|
23
|
-
"EVERFUR_PARTNER_KEY": "
|
|
177
|
+
"EVERFUR_API_BASE_URL": "https://api-staging.everfur.com/api/v1",
|
|
178
|
+
"EVERFUR_PARTNER_KEY": "pk_live_your_sandbox_tenant_key",
|
|
179
|
+
"EVERFUR_DOCS_KEY": "dk_partner_your_docs_key",
|
|
180
|
+
"EVERFUR_SESSION_TOKEN": "a_short_lived_session_your_backend_minted"
|
|
24
181
|
}
|
|
25
182
|
}
|
|
26
183
|
}
|
|
27
184
|
}
|
|
28
185
|
```
|
|
29
186
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
187
|
+
The four values, and where they come from:
|
|
188
|
+
|
|
189
|
+
| Variable | What it is |
|
|
190
|
+
|---|---|
|
|
191
|
+
| `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`. |
|
|
192
|
+
| `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`. |
|
|
193
|
+
| `EVERFUR_DOCS_KEY` | A read-only docs key (`dk_partner_...`) that opens `GET /partners/contract` and nothing else. |
|
|
194
|
+
| `EVERFUR_SESSION_TOKEN` | A short-lived session your backend minted for a test user. Only `everfur_verify_integration` reads it. |
|
|
195
|
+
|
|
196
|
+
The secret key (`sk_partner_...`) never appears in an MCP configuration: the server never reads it. Going
|
|
197
|
+
live changes every value in the block: the production base URL, and the production tenant's own publishable
|
|
198
|
+
key and docs key (its secret key changes on your backend at the same time). Production keys are issued only
|
|
199
|
+
after `everfur_verify_integration` passes on the sandbox tenant.
|
|
200
|
+
|
|
201
|
+
`EVERFUR_DOCS_KEY` is required. The Everfur contract (the error registry, capability packs, credential
|
|
202
|
+
shapes and the decision trees the planner walks) is not included in this package: the tools fetch it from
|
|
203
|
+
`GET /partners/contract` on `EVERFUR_API_BASE_URL`, presenting a partner docs key (`dk_partner_...`), and
|
|
204
|
+
cache it on disk (see [Credentials](#credentials)). A partner admin mints a docs key with
|
|
205
|
+
`POST /api/v1/partners/docs-keys` (list with `GET`, revoke with `DELETE /api/v1/partners/docs-keys/{key_id}`);
|
|
206
|
+
the value is returned once. A docs key authenticates the contract route and no other route. Without it,
|
|
207
|
+
`everfur_plan_integration`, `everfur_explain_error`, the `everfur://contract/*` and `everfur://guide*`
|
|
208
|
+
resources, and the prompt completions refuse with `CONTRACT_CREDENTIAL_MISSING`. The partner secret key and
|
|
209
|
+
the publishable key are not accepted for the contract; do not put the secret key in an MCP configuration.
|
|
210
|
+
|
|
211
|
+
`EVERFUR_SESSION_TOKEN` is for `everfur_verify_integration`. It is a short-lived session your backend minted for a
|
|
212
|
+
test user (the same mint your session route performs); the tool reads the entitlement verdict and the surfaces with
|
|
213
|
+
it, and mints nothing itself. The server never reads `EVERFUR_PARTNER_SECRET_KEY`, even when it is set: the secret
|
|
214
|
+
key stays on your backend. Sessions expire, so mint a fresh one before verifying; without one the session step is
|
|
215
|
+
skipped and names the variable.
|
|
216
|
+
|
|
217
|
+
Then ask your agent to add Everfur to your app. The `everfur_plan_integration` tool reads your project
|
|
218
|
+
(the framework and package manager are detected, never asked) and refuses to guess the facts only you can
|
|
219
|
+
answer, naming each one:
|
|
220
|
+
|
|
221
|
+
1. **Which capabilities**, chosen from the contract's list (`chat`, `records`, `televet`, `events`, ...). The
|
|
222
|
+
refusal lists the ids the loaded contract can plan, and `all` names every one of them; a name the contract
|
|
223
|
+
does not list is refused with `UNKNOWN_OPTION`, never taken as free text. The plan holds no credential, so
|
|
224
|
+
`everfur verify` is the fail-closed check of what your tenant actually has.
|
|
225
|
+
2. **How your users sign in**: `partner-user-ref` (your backend mints a session for your own user id),
|
|
226
|
+
`shopify-app-proxy` (a Shopify storefront vouches for the signed-in customer) or `anonymous`.
|
|
227
|
+
3. **Which environment** the generated configuration targets (`staging` or `production`).
|
|
228
|
+
4. **The language of the backend that mints sessions**, asked for `partner-user-ref`, because that is
|
|
229
|
+
the one mode where the plan generates a session route for you, and for `events`, because the webhook
|
|
230
|
+
receiver it generates (the server-side copy of every event about your members, for your own handling;
|
|
231
|
+
the members' push and email are sent by Everfur through the `notifications` lane) is written in it. A
|
|
232
|
+
Node backend gets the files; any other backend gets the recipe.
|
|
233
|
+
|
|
234
|
+
That is three questions for an anonymous integration and four for `partner-user-ref`. Once answered, the
|
|
235
|
+
plan's steps include the complete integration file, ready to write.
|
|
236
|
+
|
|
237
|
+
The explicit `everfur-mcp` command is intentional: `@everfur/mcp` also ships `everfur-mcp-http` for a
|
|
238
|
+
server deployment, so bare `npx @everfur/mcp` can no longer safely guess which executable you mean.
|
|
239
|
+
|
|
240
|
+
### Run it on your own server
|
|
241
|
+
|
|
242
|
+
For a partner-operated cloud/server deployment, run the guarded Streamable HTTP entry point behind TLS:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
export EVERFUR_API_BASE_URL="https://api-staging.everfur.com/api/v1"
|
|
246
|
+
# No deployed environment accepts pk_test_: use the pk_live_ key of a tenant created as a sandbox.
|
|
247
|
+
export EVERFUR_PARTNER_KEY="pk_live_..."
|
|
248
|
+
export EVERFUR_DOCS_KEY="dk_partner_..."
|
|
249
|
+
# For verification only: a short-lived session your backend minted. The server never reads the secret key.
|
|
250
|
+
export EVERFUR_SESSION_TOKEN="..."
|
|
251
|
+
export EVERFUR_MCP_HTTP_ACCESS_TOKEN="$(openssl rand -base64 48)"
|
|
252
|
+
export EVERFUR_MCP_HTTP_HOST="0.0.0.0"
|
|
253
|
+
export EVERFUR_MCP_HTTP_ALLOWED_HOSTS="mcp-staging.partner.example"
|
|
254
|
+
npx -y -p @everfur/mcp everfur-mcp-http
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
It serves `POST /mcp` on port `3100` by default. Put a TLS reverse proxy in front of it and inject both
|
|
258
|
+
the Everfur credentials and `EVERFUR_MCP_HTTP_ACCESS_TOKEN` from your server-side secret manager. Do not
|
|
259
|
+
put either secret in an agent prompt, a browser, or a client bundle. The HTTP process is stateless and can
|
|
260
|
+
scale behind a load balancer. It does **not** replace a platform OAuth service: use this mode when one
|
|
261
|
+
partner owns the deployment and its credentials.
|
|
262
|
+
|
|
263
|
+
For release gates, reverse-proxy requirements, negative tests, and the staged production rollout, use the
|
|
264
|
+
[remote MCP staging and production runbook](docs/REMOTE_MCP_STAGING_PRODUCTION_RUNBOOK.md).
|
|
265
|
+
|
|
266
|
+
Five tools are served: `everfur_plan_integration`, `everfur_verify_integration`, `everfur_diagnose`,
|
|
267
|
+
`everfur_explain_error`, and `everfur_send_test_message`. Each is a body of the CLI, so the two surfaces
|
|
268
|
+
answer the same question the same way. `everfur_explain_error` is the one to reach for when a response
|
|
269
|
+
carries an error code: it needs no project, reads the registry from the fetched contract, and refuses a code
|
|
270
|
+
it does not recognise instead of explaining it from the shape of the string.
|
|
271
|
+
|
|
272
|
+
Every tool declares an `outputSchema` and returns `structuredContent`. A failed call carries one envelope
|
|
273
|
+
whatever the tool: `error.code`, `error.retryable`, `error.next_action` (a closed vocabulary),
|
|
274
|
+
`error.request_id`, `error.docs_path` and `error.details`. The server also serves resources (the fetched
|
|
275
|
+
contract under `everfur://contract/*` and an integration guide rendered from its decision trees under
|
|
276
|
+
`everfur://guide/*`, both requiring the partner docs key; the tool refusal codes and the JSON Schemas,
|
|
277
|
+
served offline) and three prompts: `integrate-web`, `integrate-react-native` and `diagnose-integration`.
|
|
33
278
|
|
|
34
279
|
## Install the CLI
|
|
35
280
|
|
|
@@ -44,7 +289,8 @@ npx -y -p @everfur/cli everfur plan
|
|
|
44
289
|
```
|
|
45
290
|
|
|
46
291
|
Plain `npx @everfur/cli` does not work, and the error it gives ("could not determine executable to
|
|
47
|
-
run") does not explain why: this package ships
|
|
292
|
+
run") does not explain why: this package ships four binaries, `everfur`, `everfur-mcp`,
|
|
293
|
+
`everfur-mcp-http` and `everfur-mcp-hosted` (the entry Everfur runs itself), and npx only
|
|
48
294
|
picks a binary on its own when there is exactly one, or when one is named after the package. `-p` names
|
|
49
295
|
the package and the word after it names the binary.
|
|
50
296
|
|
|
@@ -55,7 +301,7 @@ the package and the word after it names the binary.
|
|
|
55
301
|
| `everfur plan` | Produce an integration plan for the project in the current directory. |
|
|
56
302
|
| `everfur verify` | Exercise the wire: mint a session, read the verdict, assert it is not empty. |
|
|
57
303
|
| `everfur diagnose` | Explain why each capability is or is not available to this caller. |
|
|
58
|
-
| `everfur explain-error` | Explain an API error code: cause, retryability, and next action. |
|
|
304
|
+
| `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`. |
|
|
59
305
|
| `everfur contract validate` | Validate a contract-public projection: envelope shape, then digest. |
|
|
60
306
|
| `everfur sandbox create` | Provision a partner secret key (Everfur-internal; requires an admin token). |
|
|
61
307
|
|
|
@@ -76,7 +322,7 @@ CI is invited to branch on these. An existing code never changes meaning.
|
|
|
76
322
|
| `6` | REMOTE_FAILURE | The API could not be reached, timed out, or answered with a typed error. |
|
|
77
323
|
| `7` | VERIFICATION_FAILED | `verify` ran and at least one hard assertion failed. |
|
|
78
324
|
| `8` | SECRET_UNCLAIMABLE | `sandbox create` minted a key but no claim flow exists to hand it over safely. |
|
|
79
|
-
| `9` | BLOCKED |
|
|
325
|
+
| `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. |
|
|
80
326
|
|
|
81
327
|
---
|
|
82
328
|
|
|
@@ -90,15 +336,39 @@ but a sandbox tenant.
|
|
|
90
336
|
## Credentials
|
|
91
337
|
|
|
92
338
|
Configuration comes from environment variables only. No `.env` file is read.
|
|
93
|
-
No command prints, logs, or writes a credential;
|
|
94
|
-
secret (and requires `EVERFUR_ADMIN_TOKEN`),
|
|
339
|
+
No command prints, logs, or writes a credential; four commands DO handle them in memory: `everfur sandbox create` mints a partner
|
|
340
|
+
secret (and requires `EVERFUR_ADMIN_TOKEN`), `everfur verify` reads your keys to exercise the wire, and
|
|
341
|
+
`everfur plan` and `everfur explain-error` present your partner docs key to fetch the contract.
|
|
342
|
+
|
|
343
|
+
**The contract is fetched, not bundled.** `everfur plan`, `everfur explain-error`, `everfur diagnose --bundle`
|
|
344
|
+
and the MCP server read the Everfur contract from `GET /partners/contract` on `EVERFUR_API_BASE_URL`, sending
|
|
345
|
+
`EVERFUR_DOCS_KEY` in the `x-everfur-docs-key` header. They never send `EVERFUR_PARTNER_SECRET_KEY` there, and
|
|
346
|
+
the route refuses it. The response is checked against the
|
|
347
|
+
contract envelope schema and its digest before it is used. It is cached under `EVERFUR_CACHE_DIR`, else
|
|
348
|
+
`$XDG_CACHE_HOME/everfur/contract`, else `~/.cache/everfur/contract`, keyed by the API base URL and the contract
|
|
349
|
+
digest; later runs send `If-None-Match` and reuse the cached copy on a `304`. When the API cannot be reached, a
|
|
350
|
+
cached copy is used and labelled as not revalidated; when the API rejects the key, the cache is not used. A
|
|
351
|
+
projection file named with `--contract`, `contract_path` or `EVERFUR_CONTRACT_PUBLIC` is read from disk instead,
|
|
352
|
+
with no network call and no credential.
|
|
95
353
|
|
|
96
354
|
| Variable | Used for |
|
|
97
355
|
|---|---|
|
|
98
356
|
| `EVERFUR_API_BASE_URL` | Everything that touches the network. No default host. |
|
|
99
|
-
| `EVERFUR_PARTNER_KEY` | Your publishable key (`pk_live_...`). Identifies your tenant. |
|
|
100
|
-
| `EVERFUR_PARTNER_SECRET_KEY` | Session minting, server side only. Never ship it in an app. |
|
|
357
|
+
| `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_`. |
|
|
358
|
+
| `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. |
|
|
359
|
+
| `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. |
|
|
360
|
+
| `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`. |
|
|
361
|
+
| `EVERFUR_CACHE_DIR` | Optional directory for the cached contract. |
|
|
362
|
+
| `EVERFUR_CONTRACT_PUBLIC` | Optional path to a contract-public projection file, used instead of fetching the contract. |
|
|
101
363
|
| `EVERFUR_ADMIN_TOKEN` | Everfur-internal administration. Partners do not need this. |
|
|
364
|
+
| `EVERFUR_MCP_HTTP_ACCESS_TOKEN` | Required to run the self-hosted HTTP MCP; protect the endpoint from its clients. |
|
|
365
|
+
| `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. |
|
|
366
|
+
| `EVERFUR_MCP_HTTP_ALLOWED_HOSTS` | Required for any non-loopback bind; comma-separated public hostnames only. |
|
|
367
|
+
| `EVERFUR_MCP_HTTP_ALLOWED_ORIGINS` | Optional browser-origin allowlist. Origins are rejected unless explicitly listed. |
|
|
368
|
+
| `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. |
|
|
369
|
+
| `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. |
|
|
370
|
+
| `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. |
|
|
371
|
+
| `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. |
|
|
102
372
|
|
|
103
373
|
**Getting keys:** Everfur keys are currently provisioned by the Everfur team during partner onboarding;
|
|
104
374
|
there is no self-serve signup yet. Contact [support@everfur.com](mailto:support@everfur.com) to get set
|
|
@@ -106,8 +376,14 @@ up. The publishable key is designed to ship inside your app; the secret key is n
|
|
|
106
376
|
|
|
107
377
|
## Documentation
|
|
108
378
|
|
|
109
|
-
The
|
|
110
|
-
[
|
|
379
|
+
The integration guide, API reference, error catalog and testing guide are at
|
|
380
|
+
[everfur.com/docs](https://everfur.com/docs): adoption paths, the HTTP contract, API keys, the launch
|
|
381
|
+
checklist and troubleshooting.
|
|
382
|
+
|
|
383
|
+
This page used to link to `github.com/Everfur-Health/everfur-sdk-rn`, which is a PRIVATE repository: the
|
|
384
|
+
link returned 404 for every partner who clicked it, since a partner is outside Everfur by definition. If
|
|
385
|
+
a doc you need is not on the site yet, ask [support@everfur.com](mailto:support@everfur.com) rather than
|
|
386
|
+
looking for a repository you cannot open.
|
|
111
387
|
|
|
112
388
|
## License
|
|
113
389
|
|