@stellar-agent-services/dev-mcp 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -2
  3. package/dist/docs.d.ts +35 -0
  4. package/dist/docs.d.ts.map +1 -0
  5. package/dist/docs.js +92 -0
  6. package/dist/docs.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +6 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/server.d.ts +14 -0
  12. package/dist/server.d.ts.map +1 -0
  13. package/dist/server.js +125 -0
  14. package/dist/server.js.map +1 -0
  15. package/package.json +40 -4
  16. package/repository/AGENTS.md +162 -0
  17. package/repository/CONTRIBUTING.md +95 -0
  18. package/repository/IMPLEMENTATION_PLAN.md +560 -0
  19. package/repository/README.md +80 -0
  20. package/repository/SECURITY.md +22 -0
  21. package/repository/apps/wallet/README.md +78 -0
  22. package/repository/docs/client-integration/README-AI.md +495 -0
  23. package/repository/docs/client-integration/README.md +138 -0
  24. package/repository/docs/dx-improvements.md +670 -0
  25. package/repository/docs/erc-compatibility.md +163 -0
  26. package/repository/docs/job-recovery.md +201 -0
  27. package/repository/docs/payment-client.md +290 -0
  28. package/repository/docs/provider-setup-guide.md +348 -0
  29. package/repository/docs/provider-standard.md +728 -0
  30. package/repository/docs/recovery/README-AI.md +288 -0
  31. package/repository/docs/recovery/README.md +119 -0
  32. package/repository/docs/sow-closeout.md +67 -0
  33. package/repository/docs/standard/README-AI.md +614 -0
  34. package/repository/docs/standard/README.md +108 -0
  35. package/repository/docs/verification.md +504 -0
  36. package/repository/examples/paid-service/README.md +33 -0
  37. package/repository/examples/python-upstream/README.md +44 -0
  38. package/repository/llms.txt +65 -0
  39. package/repository/packages/create-stellar-service/README.md +143 -0
  40. package/repository/packages/dev-mcp/README.md +51 -0
  41. package/repository/packages/payment-client/README.md +98 -0
  42. package/repository/packages/protocol/README.md +15 -0
  43. package/repository/packages/protocol/schemas/manifest.schema.json +272 -0
  44. package/repository/packages/protocol/test-vectors/README.md +45 -0
  45. package/repository/packages/provider-kit/README.md +509 -0
  46. package/repository/packages/provider-kit/schemas/gateway-config.schema.json +140 -0
  47. package/repository/packages/verifier/README.md +131 -0
  48. package/repository/plugins/stellar-agent-services/README.md +31 -0
@@ -0,0 +1,670 @@
1
+ # Developer experience improvements
2
+
3
+ What each phase of the developer-experience work changed for people who build paid AI services with this
4
+ repository, why, and how it was checked. A phase is added here when it lands.
5
+
6
+ | Phase | Change | Status |
7
+ | --- | --- | --- |
8
+ | 1 | [`provider-kit try`](#phase-1-provider-kit-try): the first paid call in one command | Done |
9
+ | 2 | [`provider-kit init --openapi`](#phase-2-provider-kit-init---openapi): the gateway config from an OpenAPI document | Done |
10
+ | 3 | [`npm create stellar-service`, and how agents see you](#phase-3-npm-create-stellar-service-and-how-agents-see-you): a ready project in one command, and what search sees | Released in 0.1.0 |
11
+ | 4 | [From nothing to live: `dev`, `launch`, `status`](#phase-4-from-nothing-to-live-dev-launch-status): no wallet until launch, one approval page, verification without a hand-off | Released in 0.1.0 |
12
+ | 5 | [Mainnet, and the checks before a release](#phase-5-mainnet-and-the-checks-before-a-release): a mainnet project, CI that rehearses the npm publish, daily live checks, releases from a tag | Released in 0.1.0 |
13
+
14
+ Release status was checked on 2026-11-06. The sections below retain their implementation-time validation and
15
+ limits; [readiness evidence](../evidence/readiness.md) records newer checks. Live SOW evidence and the independent
16
+ pilot remain outstanding; see [SOW closeout](sow-closeout.md).
17
+
18
+ Before these phases, the owner's transactions (the USDC trustline, 8004 registration, validation requests) moved
19
+ from a key in the CLI to the developer's own wallet, approved on a page the CLI opens
20
+ ([provider-kit README](../packages/provider-kit/README.md#approving-in-your-wallet)).
21
+
22
+ ## Phase 1: `provider-kit try`
23
+
24
+ ### Why
25
+
26
+ A provider wants to see one paid call work before anyone else pays. That used to need a second wallet:
27
+
28
+ ```bash
29
+ npx stellar-pay wallet create
30
+ npx stellar-pay wallet fund --usdc 2
31
+ MANIFEST=http://localhost:8787/.well-known/stellar-agent-services.json
32
+ npx stellar-pay pay $MANIFEST text-stats '{"text":"Hello"}' --dev
33
+ ```
34
+
35
+ Now it is one command, with no wallet to make or fund:
36
+
37
+ ```bash
38
+ provider-kit try text-stats
39
+ ```
40
+
41
+ ### What it does
42
+
43
+ 1. **Finds the provider:** `--manifest <url>`, or the `baseUrl` of `--config`. Without either, `provider.config.json`
44
+ in the current directory, else `http://localhost:8787`. Only `baseUrl` is read from a config, so the terminal
45
+ needs none of the provider's other settings (`PAY_TO`, upstream keys).
46
+ 2. **Picks the service and input:** the service named, or the only one. The input is the argument (JSON, or a file
47
+ with `@`), or the service's first example. With several services and none named, it lists them with prices.
48
+ 3. **Quotes:** an unpaid create-job and its `402` challenge, checked against the manifest [P7]. Nothing is signed.
49
+ 4. **Asks:** it shows the exact price, payee, network and charge policy, and asks `Pay 0.01 USDC in test money?
50
+ [Y/n]`. `--yes` skips the question, for scripts and coding agents.
51
+ 5. **Funds a throwaway payer:** a new key in memory gets test XLM from Friendbot, then one transaction adds its USDC
52
+ trustline and buys exactly the price on the testnet DEX.
53
+ 6. **Pays** through the payment client, once. It follows the job (`200`, or `202` and polling) and confirms the
54
+ `transfer` to `payTo` on chain [J16].
55
+ 7. **Reports** the result, the payment link, and `payTo received 0.01 USDC`. Exit 0 only when the job succeeded and
56
+ its payment is confirmed.
57
+
58
+ ```text
59
+ Pay for one job at Stellar Agent Services example (localhost:8787)
60
+
61
+ Service text-stats: Counts the words, sentences and characters in a text.
62
+ Input {"text":"Hello world."} (its example)
63
+ Price 0.01 USDC, charged only if the work succeeds
64
+ Pay to GDJBKS6DUSE2G2UG4PPI2M5ABD43T73AHGEIOWHWSSCWXLN3LOYVXA3O
65
+ From GDN3DXCMEMNADAXWSSXDTAHQ5V4EQA6JZWJL25Q5TIAISIS77LYX43AW, a throwaway test account
66
+ Network Stellar Testnet: test money only
67
+
68
+ ✓ Test XLM from Friendbot
69
+ ✓ Test USDC from the testnet DEX: 0.01 USDC
70
+ ✓ text-stats succeeded: 10.2 s from payment to result
71
+ ✓ Paid 0.01 USDC, confirmed on chain: https://stellar.expert/explorer/testnet/tx/0051061ae6bd372d6563880793350cf15796eecb603096360ade5bb10097cda9
72
+
73
+ {
74
+ "words": 2,
75
+ "sentences": 1,
76
+ "characters": 12
77
+ }
78
+
79
+ GDJB…XA3O (payTo) received 0.01 USDC.
80
+ ```
81
+
82
+ ### How it stays safe
83
+
84
+ - **No key is ever stored.** The payer's key lives in memory for one command and is never written anywhere
85
+ ([AGENTS.md](../AGENTS.md) rule 1). It holds test money only.
86
+ - **Only the payment client signs a payment** (rule 5), after the exact terms were shown and approved [P7]. Its
87
+ spending policy allows exactly one payment: the manifest's price, to the manifest's `payTo`, on testnet.
88
+ - **Testnet only.** A provider on Mainnet is refused before anything is signed (rule 6).
89
+ - **Fail closed.** If the answer to the paid request is lost, it says the payment may have gone through and never
90
+ calls it unpaid. The provider decides it from the chain [J13]. A failed purchase of test USDC stops everything
91
+ before the provider is paid.
92
+ - **Provider text can't take over the terminal.** Names, descriptions, errors and output are shown without control
93
+ characters or direction overrides.
94
+
95
+ ### Changed files
96
+
97
+ | File | Change |
98
+ | --- | --- |
99
+ | `packages/provider-kit/src/cli/try.ts` | New: the command |
100
+ | `packages/provider-kit/src/cli/owner-chain.ts` | `buyTestUsdc()` (testnet only), `formatUsdc()`. `describeResult()` names the operation that failed, not the first one, and explains the DEX's answers. |
101
+ | `packages/provider-kit/src/gateway/config.ts` | `gatewayManifestUrl()`: a config's manifest URL from `baseUrl` alone |
102
+ | `packages/provider-kit/src/cli/main.ts`, `shared.ts`, `init.ts` | The `try` command and `--yes`; `init` names `try` as the next step |
103
+ | `packages/payment-client/src/client.ts` | `PaymentUncertainError.reason`: what happened, without the "recover" advice that doesn't apply to `try` |
104
+ | `packages/provider-kit/package.json`, `tsconfig.json`, `package-lock.json` | Provider Kit depends on the payment client (one lockfile line) |
105
+ | `packages/provider-kit/Dockerfile` | The image ships the payment client, which the CLI loads |
106
+ | `packages/provider-kit/test/try.test.ts` | New: 16 tests, offline, with a real provider |
107
+ | `packages/provider-kit/test/approval.test.ts`, `test/support/owner.ts` | The failing-operation message, USDC formatting; the fake chain buys test USDC |
108
+ | Docs | [Setup guide §6](provider-setup-guide.md#6-make-a-paid-testnet-request), [provider-kit README](../packages/provider-kit/README.md#trying-a-paid-call) (the `reconcile` row is back inside the CLI table), [AGENTS.md](../AGENTS.md) §5, [README-AI §13](standard/README-AI.md#13-testing-and-conformance), the two example READMEs, three skills, the implementation plan, `llms-full.txt` |
109
+
110
+ ### How it was checked
111
+
112
+ - **Tests:** `npm run typecheck` and `npm test` pass: 876 tests, 16 of them new. They cover the paid flow against a
113
+ real provider with a fake facilitator, a declined prompt, scripts without `--yes`, input from JSON and files,
114
+ service listing, Mainnet refusal, refused input, a failed DEX purchase, an unreachable provider, a failed job, a
115
+ rejected payment, a lost answer after paying, `--config` with `env:` values, and the real `buyTestUsdc()`
116
+ transaction.
117
+ - **On testnet**, against `examples/paid-service`:
118
+ - `text-stats` with its example, `--yes`: exit 0 in 24 s,
119
+ [settlement](https://stellar.expert/explorer/testnet/tx/0051061ae6bd372d6563880793350cf15796eecb603096360ade5bb10097cda9).
120
+ Horizon showed `payTo` holding 0.0100000 USDC, and the fee paid by the facilitator.
121
+ - `delayed-report` (upfront, `202` and polling) in a real terminal, approved with Enter:
122
+ [settlement](https://stellar.expert/explorer/testnet/tx/43015a6a913109ac710d376563a68ca09be9051f12569ac00e251481d0ec351d).
123
+ - Declined with `n`: nothing funded or paid, exit 1.
124
+ - Refused input, no provider listening, and no terminal without `--yes`: each stops with its reason.
125
+ - **Docker image:** the daemon wasn't available, so the runtime stage was rebuilt by hand. `npm ci --omit=dev` with
126
+ the edited lockfile links the payment client, and the CLI runs with only the files the Dockerfile copies.
127
+
128
+ ### Limits
129
+
130
+ - The test USDC comes from the testnet DEX. When it has none for sale, `try` stops before paying and points to
131
+ `stellar-pay` with a test wallet funded at [faucet.circle.com](https://faucet.circle.com).
132
+ - Each run makes a new testnet account through Friendbot.
133
+ - The journal lives in memory, so `try` can't recover a job after it exits. The provider settles any uncertain
134
+ payment by itself, and `provider-kit reconcile --dry-run` shows where it stands.
135
+
136
+ ## Phase 2: `provider-kit init --openapi`
137
+
138
+ ### Why
139
+
140
+ Gateway mode makes an existing API paid with a config file, but every service's input and output schema had to be
141
+ written by hand, in JSON Schema. Most APIs already describe themselves: FastAPI serves an OpenAPI document at
142
+ `/openapi.json`, and Django (drf-spectacular), Spring, NestJS and others can write one.
143
+
144
+ Before:
145
+
146
+ ```bash
147
+ provider-kit init --upstream http://127.0.0.1:5000/run # one placeholder service
148
+ # then write each service's name, description, inputSchema and outputSchema by hand
149
+ ```
150
+
151
+ After:
152
+
153
+ ```bash
154
+ provider-kit init --openapi http://127.0.0.1:8000/openapi.json
155
+ ```
156
+
157
+ ### What it does
158
+
159
+ 1. **Reads the document:** OpenAPI 3.0, 3.1 or 3.2, JSON or YAML, from a file or a URL (up to 10 MB). Swagger 2.0
160
+ is refused with a pointer to converting it.
161
+ 2. **Picks the operations the gateway can call:** a `POST` that takes a JSON object (or no body) and answers `2xx`
162
+ with a JSON object, with no path parameters and no required query, header or cookie parameters. Every other
163
+ operation is listed with the reason: `GET`, a path parameter, a required query parameter, a file upload, a form
164
+ body, an array or image answer, no content, deprecated, more than 25 services.
165
+ 3. **Converts schemas** to what the standard asks [S4]–[S6]: JSON Schema 2020-12, the body's own schema at the root
166
+ with `"type": "object"` first, and the components it uses under `$defs`, recursion included. OpenAPI 3.0's
167
+ `nullable`, `example` and boolean `exclusiveMinimum`/`exclusiveMaximum` are rewritten. `readOnly` properties
168
+ leave the input and `writeOnly` ones the output. `x-` extensions, `xml`, `discriminator` and `externalDocs` are
169
+ dropped.
170
+ 4. **Fills in the rest from the document:** the provider's name and description from `info`. For each service: a
171
+ name from the path (`/v1/text/summarize` is `text-summarize`), a title from the summary, the description, tags,
172
+ and an example when the document has an input and output that match their schemas [S9].
173
+ 5. **Finds the API:** `--upstream`, else the operation's, path's or document's server (with its variables'
174
+ defaults), else the address the document was read from. A relative server in a file needs `--upstream`.
175
+ 6. **Keeps the API's credentials out of the file:** an API key header, bearer or basic becomes an `upstream.headers`
176
+ entry read from the environment, such as `"X-API-Key": "env:UPSTREAM_X_API_KEY"`. Credentials that can't be a
177
+ fixed header (OAuth2, cookies) get a warning.
178
+ 7. **Checks each service as `serve` would:** `defineService()` and a manifest with it must pass. One that fails, such
179
+ as a Python-only regular expression or a schema over 32 KiB, is skipped with the problem, so the config always
180
+ loads. Then the written file itself is loaded as `serve` loads it.
181
+ 8. **Says what's next:** prices to set, descriptions to write, environment variables to export, then `serve` and
182
+ `provider-kit try`.
183
+
184
+ ```text
185
+ Read Text Tools API 1.4.0 (OpenAPI 3.1.0) from http://127.0.0.1:8000/openapi.json
186
+
187
+ Wrote provider.config.json with 4 services:
188
+ summarize POST http://127.0.0.1:8000/summarize
189
+ translate POST http://127.0.0.1:8000/v1/translate with X-API-Key
190
+ classify-tree POST http://127.0.0.1:8000/classify/tree
191
+ random-fact POST http://127.0.0.1:8000/random-fact
192
+
193
+ Skipped 7 operations:
194
+ GET /health the gateway calls the API with POST
195
+ POST /documents/{document_id}/summarize it has a path parameter ({document_id}); the gateway calls one fixed URL
196
+ POST /search it needs the query parameter "q"; the gateway sends only a JSON body
197
+ POST /upload it takes multipart/form-data, not JSON
198
+ POST /languages it answers with an array, not a JSON object
199
+ POST /render it answers with image/png, not JSON
200
+ POST /v0/summarize it is deprecated
201
+
202
+ ✓ The config passes Provider Standard v1, checked as provider-kit serve checks it.
203
+
204
+ Next:
205
+ 1. Review provider.config.json: each service's price (0.01 USDC for now), charge and description, which agents choose by.
206
+ Write one for classify-tree: the document gives only a summary, or nothing.
207
+ 2. export UPSTREAM_X_API_KEY=... the API's key, sent as X-API-Key
208
+ 3. PAY_TO=G... provider-kit serve --config provider.config.json
209
+ 4. provider-kit try summarize one paid testnet call, from another terminal
210
+ ```
211
+
212
+ ### What it doesn't decide
213
+
214
+ The price (`0.01`), charge policy (`upfront`), expected duration (5 s) and capacity are the same first guesses as the
215
+ starter config's. The output says to set them. It never overwrites a file, and with no callable operation it writes
216
+ nothing.
217
+
218
+ ### Changed files
219
+
220
+ | File | Change |
221
+ | --- | --- |
222
+ | `packages/provider-kit/src/gateway/openapi.ts` | New: reading the document, choosing operations, converting schemas, credentials, checking each service |
223
+ | `packages/provider-kit/src/cli/init.ts` | `--openapi`, the summary and next steps. The starter config is written as before, from the same first guesses. |
224
+ | `packages/provider-kit/src/cli/main.ts`, `shared.ts` | The `--openapi` flag and its help |
225
+ | `packages/provider-kit/package.json`, `package-lock.json` | `yaml` 2.9.1 (ISC, no dependencies), pinned. The lockfile adds that entry only. |
226
+ | `packages/provider-kit/test/openapi.test.ts` | New: 26 tests |
227
+ | `packages/provider-kit/test/fixtures/openapi/` | A real FastAPI document and the app that generates it (FastAPI 0.115.12, Pydantic 2.14.1), and an OpenAPI 3.0 YAML document in drf-spectacular style |
228
+ | Docs | [Setup guide §3a](provider-setup-guide.md#3a-gateway-an-existing-api), [provider-kit README](../packages/provider-kit/README.md#from-an-openapi-document), [AGENTS.md](../AGENTS.md) §2, the wrap-existing-api skill, the implementation plan, `llms-full.txt` |
229
+
230
+ ### How it was checked
231
+
232
+ - **Tests:** `npm run typecheck` and `npm test` pass: 902 tests, 26 of them new. They cover the FastAPI document
233
+ (services, skipped operations, `$defs`, recursion, examples, the API key), the OpenAPI 3.0 YAML rewrites, every
234
+ reason to skip, the 25-service limit, names, any-type answers, credential alternatives, server resolution,
235
+ reading files and URLs, and the CLI: output, no overwriting, nothing written when nothing is callable, and the
236
+ unchanged starter config. One test serves the generated config and makes paid calls through the gateway to a
237
+ stand-in API, which receives the right path, body and key.
238
+ - **The whole flow, live:** the FastAPI fixture app ran under uvicorn. `init --openapi
239
+ http://127.0.0.1:8000/openapi.json` found the API's address by itself, then `serve` started on the generated
240
+ config, and `provider-kit try` paid on testnet:
241
+ - `summarize` with its example: [settlement](https://stellar.expert/explorer/testnet/tx/6943f507e559a91a7d91feb1f9bc8932a150de4c129e722217c77333482b0863)
242
+ - `translate`, through FastAPI's API key check, with the key from `UPSTREAM_X_API_KEY`:
243
+ [settlement](https://stellar.expert/explorer/testnet/tx/6d968b968594ebca0dd71d3ce0d4a356bb9917a553266776d333fe81c295ccee)
244
+ - **A third-party document:** the public Swagger Petstore (`petstore3.swagger.io`, OpenAPI 3.0.4) gave 3 services
245
+ and 16 skipped operations with their reasons. Its relative server was resolved against the document's URL, and
246
+ OAuth2 got its warning.
247
+ - **Docker image:** the runtime install was rebuilt by hand again: `npm ci --omit=dev` installs `yaml`, and `init
248
+ --openapi` reads a YAML document with only the files the image ships.
249
+
250
+ ### Limits
251
+
252
+ - External `$ref`s (`other.yaml#/Thing`) aren't followed: such an operation is skipped and asks for a bundled
253
+ document.
254
+ - Endpoints that take path or query parameters, forms or files can't be gateway services. The way to offer one is a
255
+ small JSON route in front of it, as the wrap-existing-api skill says.
256
+ - A `readOnly` or `writeOnly` property is found when it is marked on the property itself, not inside a referenced
257
+ component.
258
+
259
+ ## Phase 3: `npm create stellar-service`, and how agents see you
260
+
261
+ ### Why
262
+
263
+ Starting a provider took a clone, a build, an alias, `init`, a config edited by hand, an exported `PAY_TO`, and a
264
+ Dockerfile of your own. Now it is one command:
265
+
266
+ ```bash
267
+ npm create stellar-service # from a clone until the packages are on npm:
268
+ # node packages/create-stellar-service/dist/index.js
269
+ ```
270
+
271
+ And a provider could describe every service well and still not be found. Search (stellar-agent-search's
272
+ `find_agent`, and 8004 explorers) matches a query against the registered name and description only, never the
273
+ manifest's services, and nothing said so. `provider-kit check` now shows it.
274
+
275
+ ### `npm create stellar-service`
276
+
277
+ 1. **Asks at most four questions:** the folder; what to make (wrap an API you run, a new TypeScript service, or a
278
+ new Python service); for an API, its OpenAPI document or the URL a paid call goes to; and one sentence on what
279
+ it offers, the words search matches. Flags answer all of them, for scripts and coding agents. Without a terminal
280
+ it asks nothing.
281
+ 2. **Reads the OpenAPI document first,** so a mistyped URL or a stopped API is caught before anything is written. On
282
+ a terminal it asks again.
283
+ 3. **Gets the kit:** from a clone, it packs the three packages into the project's `vendor/` folder, so the project
284
+ installs and builds by itself, in Docker too. Once published, the project depends on the released version. Either
285
+ way the project gets the repository's npm `overrides`, which keep one copy of the Stellar SDK.
286
+ 4. **Makes the kind asked for:**
287
+ - **wrap:** `provider-kit init --openapi` (or the starter config, for one URL). Then the config reads
288
+ `PUBLIC_ORIGIN`, `PAY_TO` and `JOB_DB_PATH` from `.env`, so the same file runs on a laptop and in a container.
289
+ An API key init found goes in `.env`, empty, for you to fill.
290
+ - **typescript:** `src/server.ts` and an example service to replace, `src/services/summarize.ts`.
291
+ - **python:** a FastAPI app, `app.py`, with a gateway config that matches its models, and one container that
292
+ runs both (`start.sh`).
293
+ 5. **Writes the rest:** `.env` and `.env.example`, a `Dockerfile` with the job database on a volume at `/data`, a
294
+ README, and scripts: `dev`, `try`, `check`, `wallet`, `start`, and `register` for gateways.
295
+ 6. **Sets up the account clients pay,** if you say yes: `provider-kit trustline` opens its approval page, your wallet
296
+ adds the USDC trustline, and the address goes in `.env` as `PAY_TO`.
297
+
298
+ ```text
299
+ Creating text-tools: a gateway in front of your API, paid in USDC on Stellar testnet
300
+
301
+ ✓ Packed the Provider Kit from your clone into vendor/
302
+ ✓ Installed packages (npm install)
303
+
304
+ Read Text Tools API 1.4.0 (OpenAPI 3.1.0) from http://127.0.0.1:8000/openapi.json
305
+
306
+ Wrote provider.config.json with 4 services:
307
+ summarize POST http://127.0.0.1:8000/summarize
308
+ translate POST http://127.0.0.1:8000/v1/translate with X-API-Key
309
+ classify-tree POST http://127.0.0.1:8000/classify/tree
310
+ random-fact POST http://127.0.0.1:8000/random-fact
311
+
312
+ Skipped 7 operations:
313
+ GET /health the gateway calls the API with POST
314
+ …
315
+
316
+ ✓ The config passes Provider Standard v1, checked as provider-kit serve checks it.
317
+
318
+ ✓ Wrote provider.config.json, .env, a Dockerfile and a README
319
+
320
+ ✓ Created text-tools in /home/you/text-tools
321
+
322
+ Next:
323
+ cd text-tools
324
+ npm run wallet sets up the account clients pay: put the address it prints in .env as PAY_TO
325
+ set UPSTREAM_X_API_KEY in .env the gateway sends it to your API
326
+ npm run dev starts the gateway at http://localhost:8787
327
+ npm run try pays for one call with test money, from another terminal
328
+ npm run check checks it, and shows how agents see it
329
+ ```
330
+
331
+ ### How agents see you (`provider-kit check`)
332
+
333
+ `check` now ends with the provider as agents meet it. These hints never fail the check:
334
+
335
+ ```text
336
+ How agents see you
337
+ Search matches only this name and description, which provider-kit register publishes:
338
+ Text Tools API
339
+ Summaries, translations and labels for English text.
340
+ ! Search reads no service names, titles or tags, so agents searching for "summarize", "translate",
341
+ "classify", "tree", "random" or "fact" won't find you. Put those words in provider.description.
342
+ ! translate: a 7-word description, no example
343
+ ! classify-tree: a 3-word description, no example
344
+ ! random-fact: a 9-word description
345
+ An agent picks a service by its description: what it does, when to use it, its limits, with no price or
346
+ URL [§3.4]. An example shows it what to send, and provider-kit try pays with it.
347
+ · Ranking counts reviews from distinct paying clients only. Until the first, search lists you as new and
348
+ unrated.
349
+ · Not registered yet: provider-kit register publishes this name and description.
350
+ ```
351
+
352
+ - **Matching** is stellar-agent-search 0.1.0's own: accents and case folded, its stopwords dropped, its crude
353
+ stem, then a substring test on the name and description. A word from a service's name, title or tags that this
354
+ misses is listed. So "summarize" is flagged when the description only says "Summaries", as a real search would
355
+ miss it.
356
+ - **Once registered,** it shows the name and description the registry has (what search shows today), says when
357
+ they are older than the manifest, and prints the stellar-agent-search command that shows the live profile.
358
+ - **Each service:** the init placeholder, a description under 12 words, a price or URL in it [§3.4], or no example.
359
+
360
+ ### How it stays safe
361
+
362
+ - **No secret key, anywhere.** The scaffolder never asks for one, makes one or writes one ([AGENTS.md](../AGENTS.md)
363
+ rule 1). The payout account is set up in the developer's own wallet, on `provider-kit trustline`'s approval page,
364
+ and only its public address is saved. `.env` is kept out of git.
365
+ - **It never pays.** `npm run try` pays later, when the developer runs it, from a throwaway account with test money.
366
+ - **Testnet only** (rule 6). Every project starts on `stellar:testnet`.
367
+ - **Nothing is overwritten.** It refuses a folder with files in it. A cancelled question writes nothing.
368
+ - **Provider text stays text.** The registered name and description are shown without control characters or
369
+ direction overrides, as `try` shows provider text.
370
+
371
+ ### Changed files
372
+
373
+ | File | Change |
374
+ | --- | --- |
375
+ | `packages/create-stellar-service/` | New package: `src/main.ts` (questions, flags, the run), `src/files.ts` (what each kind writes), `src/kit.ts` (clone or npm), `templates/`, README |
376
+ | `packages/create-stellar-service/test/create.test.ts` | New: 24 tests |
377
+ | `packages/provider-kit/src/cli/discovery.ts` | New: how agents see you |
378
+ | `packages/provider-kit/src/cli/check.ts` | Prints it, with the registration file it read |
379
+ | `packages/provider-kit/src/registration.ts` | `inspectRegistration()`: `checkRegistration()` and the file it read. `checkRegistration()` is unchanged. |
380
+ | `packages/provider-kit/src/cli/shared.ts`, `try.ts` | `plain()` moved to `shared.ts`, for both |
381
+ | `packages/provider-kit/src/gateway/openapi.ts`, `cli/init.ts` | The provider description placeholder is exported, so `check` can spot it |
382
+ | `packages/provider-kit/test/discovery.test.ts`, `test/validation-request.test.ts` | New: 8 tests, and 1 for `inspectRegistration()` |
383
+ | `package.json`, `tsconfig.json`, `package-lock.json` | The new workspace. The lockfile adds its two entries only. |
384
+ | Docs | [Setup guide](provider-setup-guide.md) §1, §5 and §8, [provider-kit README](../packages/provider-kit/README.md#how-agents-see-you), [AGENTS.md](../AGENTS.md) §1–§3 and §5, the root README, both build skills, the implementation plan, `llms.txt`, `llms-full.txt` |
385
+
386
+ ### How it was checked
387
+
388
+ - **Tests:** `npm run typecheck` and `npm test` pass: 935 tests, 33 of them new. The scaffolder's run every kind
389
+ with npm and provider-kit replaced: the files, packing and installing, init's output cut before its own next
390
+ steps, the config made to read `.env`, the fallback starter, the payout account set up or left for later, every
391
+ refused call, the questions on a terminal, a document that can't be read, and cancelling. Two tests tie the
392
+ scaffolder to the kit: one compiles the TypeScript template against the Provider Kit's types, and one checks
393
+ that the real `provider-kit init` still ends its report where the scaffolder cuts it.
394
+ - **Live, from a clone, each kind on testnet:** scaffold, `npm run dev`, `npm run check`, `npm run try`:
395
+ - typescript: [settlement](https://stellar.expert/explorer/testnet/tx/c76aa0500d929685d3fb3e1a1a1ccb22feb1c68bb18a1ea351aa93d956687ba5)
396
+ - python, the generated FastAPI app under uvicorn:
397
+ [settlement](https://stellar.expert/explorer/testnet/tx/b656271975e2ee0394f54efa909388a8b13b88bfd64fc03479a0860a6c223170).
398
+ Its config matches what `init --openapi` reads from the app's own document, with `additionalProperties: false`
399
+ added.
400
+ - wrap, the Phase 2 FastAPI fixture: `translate` passed the API's key check with the key from `.env`:
401
+ [settlement](https://stellar.expert/explorer/testnet/tx/2b184ace578e7ca752810e8447cbc83d639af1c09ffc9e453c0788c75a9a07f3)
402
+ - The questions, answered in a real terminal (a pseudo-terminal).
403
+ - **Docker:** the daemon wasn't available, so each Dockerfile's steps ran by hand in an empty folder. `npm ci`
404
+ installs from the lockfile and `vendor/`, and each image's command serves with an HTTPS origin and the job
405
+ database in `data/`. For the Python image, `start.sh` stops the gateway and exits 1 when the API dies, and stops
406
+ both and exits 0 on a stop signal.
407
+ - **Found and fixed on the way:** the TypeScript template didn't compile (an `interface` isn't a JSON object type),
408
+ which the compile test now catches. A venv made by the scaffolder got x86_64 packages when Node ran under Rosetta,
409
+ so the Python packages are now installed in the developer's own shell. macOS's bash 3.2 has no `wait -n`, so
410
+ `start.sh` is plain POSIX `sh`.
411
+ - `npm run build`, both Svelte checks and `npm run llms:check` pass.
412
+
413
+ ### Limits
414
+
415
+ - **Until the npm publish,** it runs from a clone, and `vendor/` holds the kit as it was then: a `git pull` in the
416
+ clone doesn't update it. The npm path (`npm create stellar-service`) is written but untested until then. Projects
417
+ made any other way need the same `overrides` once the kit is published, or they install two Stellar SDKs.
418
+ - **Setting up the payout account** needs a person and a browser wallet. A coding agent passes `--no-wallet`, or
419
+ asks first.
420
+ - **wrap in Docker:** 127.0.0.1 is the container, so each `upstream.url` must point at an address it reaches.
421
+ - **python:** the scaffolder doesn't make `.venv`. The next steps give the two commands.
422
+ - **How agents see you** follows stellar-agent-search 0.1.0's matching. If search changes, the hints may drift.
423
+ It doesn't read reviews: it states the ranking policy, and the live command shows the real profile.
424
+ - **Not done:** stellar-agent-search's provider guide (`build-a-discoverable-agent.md`) still tells providers to
425
+ register with their own key script. That change belongs to that repository.
426
+
427
+ ## Phase 4: from nothing to live: `dev`, `launch`, `status`
428
+
429
+ ### Why
430
+
431
+ Phases 1–3 made the first paid call quick. Going live still took most of the work, and three separate wallet
432
+ sessions:
433
+
434
+ ```bash
435
+ npm run wallet # 1st wallet session: the USDC trustline, before anything runs
436
+ npm run dev && npm run try # two terminals
437
+ # deploy it yourself: HTTPS, a volume, the environment
438
+ PUBLIC_ORIGIN=https://… npm run register # 2nd wallet session, then "deploy it again" with the agent id
439
+ provider-kit request-validation --validator G… # 3rd wallet session, then send the verifier the hash yourself
440
+ # and on testnet, stellar-agent-search couldn't find the provider unless the searcher knew its id
441
+ ```
442
+
443
+ Now:
444
+
445
+ ```bash
446
+ npm run dev # no wallet: a throwaway testnet account is paid until launch
447
+ npm run try # starts the service for the call when it isn't running
448
+ npm run launch # one page in your wallet: trustline, registration, verification request; then Fly.io
449
+ npm run status # where it stands, and the one command to run next (--json for agents)
450
+ ```
451
+
452
+ ### What changed
453
+
454
+ 1. **`provider-kit dev`** runs a provider on this computer: a gateway config in its own process, or any provider by its
455
+ command (`dev -- node dist/server.js`). It reads `.env`. With `PAY_TO` empty, it makes a **throwaway payout
456
+ account**: a new testnet key in memory gets test XLM from Friendbot and signs the account's USDC trustline, then
457
+ the key is dropped. Only the public address reaches the provider and `.env` (as `PAY_TO` and `DEV_PAY_TO`), and what
458
+ it receives is test money nobody can spend. `--with '<command>'` starts the API a gateway calls and waits until it
459
+ takes connections. `--tunnel` adds a temporary public HTTPS address through a Cloudflare quick tunnel.
460
+ 2. **`provider-kit launch`** takes the provider live:
461
+ - The public address comes first: `--url`, `fly.toml`'s app (`https://<app>.fly.dev`) or an `https://`
462
+ `PUBLIC_ORIGIN`. So the service is registered before its first deploy and deployed once, with its agent id.
463
+ - It builds the manifest that address will serve and checks it: from the gateway config, from the provider's own
464
+ command started with `PUBLIC_ORIGIN` set (`--start`), or from a deployed provider (`--manifest`).
465
+ - One approval page, one wallet: test XLM, the USDC trustline [P2], `register_with_uri` (or `set_agent_uri` when the
466
+ manifest moved) [I1]–[I3], and `validation_request`, which is built once the new agent id is known.
467
+ - It saves `PAY_TO` and `AGENT_ID` in `.env`, the agent id where the gateway config reads it, and the public
468
+ settings in `fly.toml`'s `[env]`. A new agent's id is saved even when a later step stops, so running `launch`
469
+ again never registers a second agent. Run again with everything in place, it signs nothing.
470
+ - With `fly.toml` and a yes, `flyctl` creates the app the first time, the API's key is staged as a Fly secret from
471
+ `.env`, `fly deploy` runs with one machine, and `launch` waits for the public manifest and checks [I1]–[I3] from
472
+ outside.
473
+ 3. **`provider-kit status`** reads this computer, the chain and the public address: running here, `payTo`, paid
474
+ once, registered, live, verified. It names the one command to run next, and finds the public manifest from the
475
+ registration itself when nothing else names it. `--json` for scripts and coding agents.
476
+ 4. **`check`:** `--fix` writes the service words search would miss into a gateway config's `provider.description`,
477
+ and `--json` prints the result. A provider on this computer that isn't registered yet passes, with a note that
478
+ `launch` registers it.
479
+ 5. **`try`:** when nothing answers, it starts the provider for the call and stops it after: the gateway config in its
480
+ own process (with a throwaway `payTo` when `PAY_TO` is empty), or `--start '<command>'`. `--json` prints the
481
+ outcome on standard output and the steps on standard error.
482
+ 6. **`npm create stellar-service`:** no wallet question; a price question (`--price`); `fly.toml`; the scripts `dev`,
483
+ `try`, `check`, `status`, `launch` and `deploy` (`wallet` and `register` are gone); gateway configs read the agent
484
+ id as `env:AGENT_ID`. The Python kind runs its API with `uv` when it is installed, so there is no virtual
485
+ environment to make, and `npm run dev` starts the API and the gateway together. Every Dockerfile, and the kit's
486
+ gateway image, gives `/data` to the `node` user when it starts as root, so a host's new volume works as it comes.
487
+ 7. **`provider-kit init`** guesses `on-success` for a call expected to take seconds, instead of `upfront`, and
488
+ `--json` prints what it wrote and skipped.
489
+ 8. **A gateway config's `provider.agentId`** may read an unset variable (`"env:AGENT_ID"` before launch): the provider
490
+ just isn't registered yet.
491
+ 9. **`verifier watch`** answers validation requests with no hand-off ([verification.md](verification.md#self-serve-verification-verifier-watch)).
492
+ It reads the Validation Registry's list of requests naming its key, finds each request's document in its
493
+ `validation_request` event (or, once the RPC no longer has it, the agent's registration) and checks only a manifest
494
+ URL the agent's registration lists [I1]. Then it runs C1–C7 with its test payer, serves the receipt and badges over
495
+ HTTP, and answers with `validation_response`, signed by the Stellar CLI keychain. A provider that isn't live yet is
496
+ checked again for 24 hours, so `launch` can ask before the deploy. Its state survives restarts, and a request is
497
+ never answered twice. `/requests/<request hash>.json` shows where a request stands.
498
+ 10. **stellar-agent-search finds testnet providers** (in that repository, for its 0.2.0 release). With
499
+ `STELLAR_NETWORK=testnet` and nothing else, it reads the newest 50 agents straight from the Identity contract
500
+ (ids are given in order, so `total_agents` names the window; `REGISTRY_SCAN_LIMIT` changes it), with honest
501
+ coverage and short caches, and never mixes in mainnet's explorer. A search there also matches a provider's
502
+ services: when the registration lists one `stellar-agent-services` manifest that is live and bound to the agent
503
+ [I1]–[I3], their names, titles, descriptions and tags count, and each result says which field matched. `check`'s
504
+ "How agents see you" follows it: on testnet the missing service words are a note, on mainnet, where the explorer
505
+ lists no services, still a warning; and it prints the 0.2.0 command that shows the live profile.
506
+
507
+ ### How it stays safe
508
+
509
+ - **No key is kept.** The throwaway payout account's key lives in memory for one transaction, its own trustline,
510
+ and is never written; the provider, like any provider, knows only the public address ([AGENTS.md](../AGENTS.md)
511
+ rule 1, [SECURITY.md](../SECURITY.md)). It is never made on Mainnet: there, an empty `PAY_TO` stops `dev`.
512
+ - **The owner signs, once, in their wallet.** `launch` builds each transaction when it is due, and the page checks
513
+ that what the wallet signed is that very transaction (as in Phase 3's approval page). A provider's own account is
514
+ never asked to validate it.
515
+ - **Nothing twice.** A registration that landed is saved before anything else can stop the run. A re-run reads the
516
+ chain: a registration that names the manifest isn't sent again, and a verifier that was asked isn't asked again.
517
+ - **Fail closed.** `launch` refuses an `http://` address, a quick tunnel's (it changes every run), an `AGENT_ID` that
518
+ doesn't exist, and a `PAY_TO` that isn't the agent's wallet [I3], before anything is signed.
519
+ - **Secrets stay secret.** `fly.toml` gets public values only. The API's key goes to Fly's secrets through `flyctl`'s
520
+ standard input, never on a command line.
521
+
522
+ ### Changed files
523
+
524
+ | File | Change |
525
+ | --- | --- |
526
+ | `packages/provider-kit/src/cli/dev.ts`, `dev-account.ts` | New: `provider-kit dev`, and the throwaway payout account |
527
+ | `packages/provider-kit/src/cli/launch.ts`, `fly.ts`, `validators.ts` | New: `provider-kit launch`, `fly.toml` and `flyctl`, the default verifier |
528
+ | `packages/provider-kit/src/cli/status.ts` | New: `provider-kit status` |
529
+ | `packages/provider-kit/src/cli/env-file.ts`, `processes.ts`, `tunnel.ts` | New: `.env` reading and writing, the provider's own command, the quick tunnel |
530
+ | `packages/provider-kit/src/cli/serve.ts` | `serveGateway()`, shared by `serve`, `dev` and `try` |
531
+ | `packages/provider-kit/src/cli/check.ts`, `discovery.ts` | `--fix`, `--json`, `describedWithServices()`; an unregistered local provider passes |
532
+ | `packages/provider-kit/src/cli/try.ts` | Starts the provider when nothing answers; `--start`, `--json` |
533
+ | `packages/provider-kit/src/cli/owner-steps.ts` | A validation request step prepared when it is due |
534
+ | `packages/provider-kit/src/registration.ts` | `agentValidations()`: an agent's validation requests |
535
+ | `packages/provider-kit/src/gateway/config.ts`, `openapi.ts`, `schemas/gateway-config.schema.json` | An unset `env:` agent id; the `baseUrl` override; `on-success` first guess |
536
+ | `packages/provider-kit/src/cli/main.ts`, `shared.ts`, `init.ts` | The commands, their flags and usage; `init`'s next steps |
537
+ | `packages/provider-kit/Dockerfile` | The `/data` hand-over; the missing `create-stellar-service` workspace manifest |
538
+ | `packages/create-stellar-service/` | No wallet question, `--price`, `fly.toml`, the new scripts, `uv`, the Dockerfiles' `/data` hand-over |
539
+ | `packages/verifier/src/watch.ts`, `watch-state.ts`, `requests.ts`, `cli/watch.ts`, `cli/serve.ts` | New: `verifier watch`, its state, how a request becomes a manifest URL, and the receipts it serves |
540
+ | `packages/verifier/src/registry.ts`, `chain.ts`, `json.ts`, `schemas.ts`, `cli/` | The validator's request list, the payer's balance, the shared `data:` URI reader, the request schema, the command |
541
+ | `packages/provider-kit/src/cli/request-validation.ts` | Says that nothing needs sending: a watching verifier finds the request |
542
+ | `packages/provider-kit/src/cli/discovery.ts` | Search as stellar-agent-search 0.2.0 does it: services on testnet, the registration's services, the 0.2.0 command |
543
+ | stellar-agent-search (its own repository) | The registry scan for testnet, matching manifest services, `match.fields` in results, docs and its changelog |
544
+ | Tests | `dev.test.ts`, `launch.test.ts`, `status.test.ts`, `check.test.ts`, `tools.test.ts`, the verifier's `watch.test.ts` and `requests.test.ts` (new); `try`, `openapi`, `create` and the registry fakes |
545
+ | Docs | The [setup guide](provider-setup-guide.md) (a quick start, then §5–§10 rewritten), [verification.md](verification.md) (self-serve verification), the provider-kit, create-stellar-service and verifier READMEs, [AGENTS.md](../AGENTS.md), the root README, [SECURITY.md](../SECURITY.md), README-AI §2 and §13, three skills, both examples, `llms-full.txt` |
546
+
547
+ ### How it was checked
548
+
549
+ - **Tests:** `npm run typecheck` and `npm test` pass: 1,020 tests. `launch` is tested end to end against a fake
550
+ chain, registry, wallet and `flyctl`: one page from a throwaway `PAY_TO` to agent 42 with its validation request; a
551
+ re-run that signs nothing; a page closed after registering, then a re-run that asks only for the validation; a
552
+ provider's own command; the deploy, the staged secret and the live check; a gateway on 127.0.0.1 that isn't
553
+ deployed; and every refusal. `verifier watch` has 50 tests with a fake registry, RPC, Stellar CLI and provider: a
554
+ request found, checked, served and answered; one not live yet, retried, then answered with its failure; a URL the
555
+ registration doesn't list, never checked; a restart in the middle; and no second answer, which mutation checks
556
+ confirmed the tests guard.
557
+ - **Live, on testnet,** a Python project made from the clone: `npm run dev` made the throwaway account (Friendbot,
558
+ then its trustline), started the API with `uv` and waited for it, then served the gateway. `npm run status` and
559
+ `npm run check` read the running provider, Horizon and the registry, and `npm run launch -- --dry-run` planned a
560
+ new registration against the testnet registry. Ctrl+C stopped the gateway and the API together.
561
+ - **Not run here:** a paid `try` and a live `launch` (they spend test money and register a test agent: they wait
562
+ for the maintainer's go), a Fly.io deploy (no Fly account), the Docker images (no Docker daemon) and `--tunnel` (no
563
+ `cloudflared`). Those parts are tested with fakes only.
564
+
565
+ ### Limits
566
+
567
+ - **The default verifier is empty** (`src/cli/validators.ts`) until a verifier runs `verifier watch` with a known
568
+ account. Until then, `launch --validator <G…>` or `STELLAR_VALIDATOR` names one, and without either no validation is
569
+ requested.
570
+ - **Deploying is Fly.io only.** Elsewhere, `launch --url <https origin> --no-deploy` registers the address and prints
571
+ the settings to deploy with.
572
+ - **A hosted gateway**, where `launch` would upload the config and get an address without deploying anything, is not
573
+ built: whoever ran it would hold the API's key and the job database.
574
+ - **`launch --start`** runs the provider's command here for a few seconds, so its startup checks (such as the
575
+ facilitator's) must pass on this computer.
576
+ - **Search reaches the newest 50 testnet agents** (and any one by id). Of today's newest 50, 35 registrations can't be
577
+ read, as they point at `http://`, `localhost` or pages that are gone. The kit's printed search command needs the
578
+ stellar-agent-search 0.2.0 release; until then it runs from a checkout of that repository
579
+ (`STELLAR_NETWORK=testnet node dist/index.js profile <id>`).
580
+ - **`verifier watch` has no container image**: it signs through the Stellar CLI keychain, which would have to be
581
+ installed in the image. Its README describes running it on a small server. It hasn't run against testnet here.
582
+
583
+ ## Phase 5: mainnet, and the checks before a release
584
+
585
+ ### Why
586
+
587
+ The packages are about to go to npm, for testnet and mainnet. Until now every project started on testnet, and nothing
588
+ checked what an npm user would get: the packed packages, a project made by the published scaffolder, or the mainnet
589
+ services the packages name.
590
+
591
+ ### What changed
592
+
593
+ - **`create-stellar-service --network mainnet`** makes a project that takes real USDC: OpenZeppelin's facilitator,
594
+ which wants an API key (`FACILITATOR_AUTH`, sent by the TypeScript template and by a gateway config's
595
+ `facilitatorHeaders`), a Soroban RPC in `.env`, and no `try` script. `launch` writes the network to `fly.toml`,
596
+ stages the key and the RPC as Fly secrets, and won't call a deploy live while it takes payments on another network
597
+ than its registration. `dev` and `status` point to `check` and `launch` on mainnet. A facilitator that refuses the key
598
+ says so.
599
+ - **CI** ([CONTRIBUTING.md](../CONTRIBUTING.md#what-ci-checks)) makes a project of each kind on each network, then
600
+ installs, builds and starts it and runs the verifier on it. It checks what every package would publish, and rehearses
601
+ the publish on a throwaway registry: the packages installed and used from it, and projects the published scaffolder
602
+ makes.
603
+ - **Daily live checks** read what the packages take for granted on both networks, pay one testnet call with test
604
+ money, and, with the `MAINNET_FACILITATOR_AUTH` secret, start a mainnet project against the real facilitator.
605
+ - **Releases** ([CONTRIBUTING.md](../CONTRIBUTING.md#releasing)): a version tag publishes the npm packages with
606
+ provenance, the gateway image at `ghcr.io/algorialabs/provider-kit`, and a GitHub release.
607
+
608
+ ### What the checks found
609
+
610
+ - **A kit provider could fail the verifier's C7 at random.** `listen()` read each request body with
611
+ `Readable.toWeb()`, which starts reading at once and stops when nobody reads. A body answered before it was read
612
+ (C6's oversized probe, a `404`) stayed half-read on its kept-alive connection, and the next request on it, in C7's
613
+ burst, was reset: in 3 runs of 4 here. The body is now read only when the router reads it, and Node drops the rest
614
+ after the answer.
615
+ - **The MCP server, installed from npm, would have crashed on start**: it read `llms.txt` from the clone it was built
616
+ in. Its package now carries the documents (`scripts/pack-docs.mjs`, run before each pack), read when there is no
617
+ clone.
618
+ - **Receipts would have named version 0.0.0** after a release: the verifier's version, in its User-Agent and in every
619
+ receipt, was typed in the code. The verifier and the MCP server now read their own `package.json`.
620
+ - **Every package would have published its TypeScript build cache** (`dist/.tsbuildinfo`, about 80 kB), and none its
621
+ license text. Each package now has a `LICENSE` and `engines.node`.
622
+
623
+ ### How it stays safe
624
+
625
+ - No check signs or pays on mainnet, and none holds a Stellar key. The pull-request checks use no secret: the
626
+ facilitator and the verifier's chain lookup are stand-ins on the runner.
627
+ - The only payment is the daily testnet call, from a throwaway account with test XLM and test USDC.
628
+ - The rehearsal publishes to a registry on the runner, with npm settings of its own, so nothing reaches npm.
629
+ - The publish refuses a private package, a tag that doesn't match the version, and packages at different versions.
630
+ npm itself would skip a private workspace and report success.
631
+ - Release builds use no dependency cache, and the release's third-party actions are pinned to commits.
632
+
633
+ ### Changed files
634
+
635
+ | File | Change |
636
+ | --- | --- |
637
+ | `.github/workflows/ci.yml`, `live.yml`, `release.yml` | The pull-request checks; the daily live checks; the release |
638
+ | `scripts/scaffold-check.mjs`, `packages.mjs`, `live-check.mjs` | A new project end to end; packing, versions, the rehearsal and the publish; the live networks |
639
+ | `packages/provider-kit/src/http/node-server.ts`, `test/node-server.test.ts` | The request body, read when the router reads it, and its regression test |
640
+ | `packages/dev-mcp/src/server.ts`, `scripts/pack-docs.mjs` | The documents packed with the package, and its version |
641
+ | `packages/verifier/src/tool.ts` | Its version, from its `package.json` |
642
+ | `packages/*/package.json`, `packages/*/LICENSE` | `engines.node`, no build cache, the license text |
643
+ | `packages/create-stellar-service/`, `packages/provider-kit/src/cli/`, `src/payments/facilitator.ts` | `--network mainnet`; `launch`, `dev` and `status` on mainnet; the facilitator's `401` |
644
+ | Docs | [CONTRIBUTING.md](../CONTRIBUTING.md), [AGENTS.md](../AGENTS.md), README-AI §2, §7 and §15, the five skills, the provider-kit, create-stellar-service and dev-mcp READMEs, the setup guide's mainnet section, `llms.txt` |
645
+
646
+ ### How it was checked
647
+
648
+ - `npm run typecheck` and `npm test` pass: 1,032 tests. The new test fails without the fix: the request after a
649
+ `413` hangs, or is reset.
650
+ - `npm run scaffold:check` passed here for all three kinds on both networks, and `npm run packages:rehearse` passed:
651
+ six packages published to Verdaccio, installed and used, and three projects from the published scaffolder.
652
+ - `npm run live:check` passed on both networks: x402.org's and OpenZeppelin's facilitators (the latter without a key:
653
+ it answers `401`), both RPCs, Horizon, the USDC contracts and the 8004 registries.
654
+ - actionlint passes the three workflows.
655
+ - **Not run here:** the workflows themselves (they run once pushed), the paid testnet call (it spends test money, so it
656
+ waits for the maintainer's go), the mainnet facilitator with a key (no key here), and the Docker builds (no Docker
657
+ daemon here; CI builds them).
658
+
659
+ ### Limits
660
+
661
+ - **Two Stellar SDKs in a plain install.** `@trionlabs/stellar8004` 0.0.11 asks for the Stellar SDK 15 and the kit
662
+ uses 16, so npm installs a second SDK for it. Everything works: only strings and numbers pass between the two (a
663
+ rehearsal of a plain install read the testnet registry through both). The cost is 18 MB and the second copy's
664
+ axios 1.15.0, which `npm audit` flags. Projects made by `create-stellar-service` carry an override that keeps one
665
+ copy, and `scaffold:check` fails a project with two. Someone installing the kit from npm by hand should add it
666
+ ([provider-kit README](../packages/provider-kit/README.md)). The rehearsal warns while this holds. A `@trionlabs/stellar8004` release that accepts SDK 16 would end it. Its main
667
+ branch moved to SDK 17 on 2026-10-10, unpublished, which would still be a second copy: x402 needs 16.
668
+ - **The first release, 0.1.0, publishes with a token,** since npm can only trust the workflow for a package that
669
+ exists. Trusted publishing replaces it after that ([CONTRIBUTING.md](../CONTRIBUTING.md#releasing)).
670
+ - **The mainnet start check needs a key.** Without the `MAINNET_FACILITATOR_AUTH` secret it is skipped.