@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.
- package/LICENSE +21 -0
- package/README.md +50 -2
- package/dist/docs.d.ts +35 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +92 -0
- package/dist/docs.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +125 -0
- package/dist/server.js.map +1 -0
- package/package.json +40 -4
- package/repository/AGENTS.md +162 -0
- package/repository/CONTRIBUTING.md +95 -0
- package/repository/IMPLEMENTATION_PLAN.md +560 -0
- package/repository/README.md +80 -0
- package/repository/SECURITY.md +22 -0
- package/repository/apps/wallet/README.md +78 -0
- package/repository/docs/client-integration/README-AI.md +495 -0
- package/repository/docs/client-integration/README.md +138 -0
- package/repository/docs/dx-improvements.md +670 -0
- package/repository/docs/erc-compatibility.md +163 -0
- package/repository/docs/job-recovery.md +201 -0
- package/repository/docs/payment-client.md +290 -0
- package/repository/docs/provider-setup-guide.md +348 -0
- package/repository/docs/provider-standard.md +728 -0
- package/repository/docs/recovery/README-AI.md +288 -0
- package/repository/docs/recovery/README.md +119 -0
- package/repository/docs/sow-closeout.md +67 -0
- package/repository/docs/standard/README-AI.md +614 -0
- package/repository/docs/standard/README.md +108 -0
- package/repository/docs/verification.md +504 -0
- package/repository/examples/paid-service/README.md +33 -0
- package/repository/examples/python-upstream/README.md +44 -0
- package/repository/llms.txt +65 -0
- package/repository/packages/create-stellar-service/README.md +143 -0
- package/repository/packages/dev-mcp/README.md +51 -0
- package/repository/packages/payment-client/README.md +98 -0
- package/repository/packages/protocol/README.md +15 -0
- package/repository/packages/protocol/schemas/manifest.schema.json +272 -0
- package/repository/packages/protocol/test-vectors/README.md +45 -0
- package/repository/packages/provider-kit/README.md +509 -0
- package/repository/packages/provider-kit/schemas/gateway-config.schema.json +140 -0
- package/repository/packages/verifier/README.md +131 -0
- 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.
|