runbios-mcp 0.2.1-dev.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +363 -0
- package/dist/api-client.d.ts +147 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +806 -0
- package/dist/api-client.js.map +1 -0
- package/dist/auth.d.ts +3 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +11 -0
- package/dist/auth.js.map +1 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -0
- package/dist/deployment-contract.d.ts +78 -0
- package/dist/deployment-contract.d.ts.map +1 -0
- package/dist/deployment-contract.js +155 -0
- package/dist/deployment-contract.js.map +1 -0
- package/dist/gpu-priorities.d.ts +25 -0
- package/dist/gpu-priorities.d.ts.map +1 -0
- package/dist/gpu-priorities.js +61 -0
- package/dist/gpu-priorities.js.map +1 -0
- package/dist/http/app.d.ts +14 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +140 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/audit.d.ts +23 -0
- package/dist/http/audit.d.ts.map +1 -0
- package/dist/http/audit.js +41 -0
- package/dist/http/audit.js.map +1 -0
- package/dist/http/config.d.ts +23 -0
- package/dist/http/config.d.ts.map +1 -0
- package/dist/http/config.js +70 -0
- package/dist/http/config.js.map +1 -0
- package/dist/http/consent.d.ts +18 -0
- package/dist/http/consent.d.ts.map +1 -0
- package/dist/http/consent.js +148 -0
- package/dist/http/consent.js.map +1 -0
- package/dist/http/internal-auth.d.ts +22 -0
- package/dist/http/internal-auth.d.ts.map +1 -0
- package/dist/http/internal-auth.js +73 -0
- package/dist/http/internal-auth.js.map +1 -0
- package/dist/http/main.d.ts +3 -0
- package/dist/http/main.d.ts.map +1 -0
- package/dist/http/main.js +65 -0
- package/dist/http/main.js.map +1 -0
- package/dist/http/mcp-handler.d.ts +12 -0
- package/dist/http/mcp-handler.d.ts.map +1 -0
- package/dist/http/mcp-handler.js +103 -0
- package/dist/http/mcp-handler.js.map +1 -0
- package/dist/http/metadata.d.ts +6 -0
- package/dist/http/metadata.d.ts.map +1 -0
- package/dist/http/metadata.js +32 -0
- package/dist/http/metadata.js.map +1 -0
- package/dist/http/oauth.d.ts +56 -0
- package/dist/http/oauth.d.ts.map +1 -0
- package/dist/http/oauth.js +486 -0
- package/dist/http/oauth.js.map +1 -0
- package/dist/http/serviceHostGuard.d.ts +31 -0
- package/dist/http/serviceHostGuard.d.ts.map +1 -0
- package/dist/http/serviceHostGuard.js +68 -0
- package/dist/http/serviceHostGuard.js.map +1 -0
- package/dist/http/store.d.ts +150 -0
- package/dist/http/store.d.ts.map +1 -0
- package/dist/http/store.js +366 -0
- package/dist/http/store.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/inference-contract.d.ts +29 -0
- package/dist/inference-contract.d.ts.map +1 -0
- package/dist/inference-contract.js +112 -0
- package/dist/inference-contract.js.map +1 -0
- package/dist/redaction.d.ts +74 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +316 -0
- package/dist/redaction.js.map +1 -0
- package/dist/server.d.ts +63 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +2282 -0
- package/dist/server.js.map +1 -0
- package/dist/training-contract.d.ts +107 -0
- package/dist/training-contract.d.ts.map +1 -0
- package/dist/training-contract.js +141 -0
- package/dist/training-contract.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# Run BiOS MCP server
|
|
2
|
+
|
|
3
|
+
The MCP server exposes the authenticated Run BiOS training and model-serving
|
|
4
|
+
control planes to MCP-compatible automation clients. This build registers **42
|
|
5
|
+
tools**; `get_platform_guide` lists all of them and a test fails if that
|
|
6
|
+
inventory drifts from what is actually registered, so the count in the guide is
|
|
7
|
+
always the count on the wire (`tools/list`).
|
|
8
|
+
|
|
9
|
+
PRE-LAUNCH: while the coming-soon gates are on (the default — see
|
|
10
|
+
`src/config.ts`), the three creation tools (`create_training_job`,
|
|
11
|
+
`upload_dataset`, `import_huggingface_dataset`) are not registered at all, so
|
|
12
|
+
the wire count is **39** and the guide lists only the live tools. Read and
|
|
13
|
+
lifecycle tools are unaffected. Set `RUNBIOS_TRAINING_COMING_SOON=false` /
|
|
14
|
+
`RUNBIOS_DATASETS_COMING_SOON=false` at launch and the registrations return.
|
|
15
|
+
|
|
16
|
+
Set these environment variables in the MCP client configuration:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"command": "npx",
|
|
21
|
+
"args": ["-y", "runbios-mcp"],
|
|
22
|
+
"env": {
|
|
23
|
+
"RUNBIOS_API_KEY": "bios-...",
|
|
24
|
+
"RUNBIOS_BASE_URL": "https://api.runbios.ai",
|
|
25
|
+
"RUNBIOS_WORKSPACE_ID": "workspace-id",
|
|
26
|
+
"RUNBIOS_INFERENCE_KEY": "sk-bios-deployment-key"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`RUNBIOS_*` is the canonical environment prefix. API keys start with `bios-`;
|
|
32
|
+
legacy `usf-` keys are still accepted.
|
|
33
|
+
The legacy `BIOS_*` variable names are still accepted as fallbacks.
|
|
34
|
+
|
|
35
|
+
`https://api.runbios.ai` is the canonical production hostname. Use
|
|
36
|
+
`https://api-dev.runbios.ai` explicitly during dev.
|
|
37
|
+
|
|
38
|
+
For interactive JWT authentication, omit `RUNBIOS_API_KEY` and set
|
|
39
|
+
`RUNBIOS_ACCESS_TOKEN`. The server never guesses credential type from a prefix:
|
|
40
|
+
API keys use `X-API-Key`, while access tokens use `Authorization: Bearer`.
|
|
41
|
+
|
|
42
|
+
`chat_with_inference` reads the deployment key only from
|
|
43
|
+
`RUNBIOS_INFERENCE_KEY`; a model cannot place it in a tool argument or expose it
|
|
44
|
+
in a transcript. Set `RUNBIOS_INFERENCE_BASE_URL=https://api-dev.runbios.ai`
|
|
45
|
+
explicitly for dev. The tool validates function schemas and matching tool
|
|
46
|
+
response messages, propagates MCP cancellation to the HTTP request, makes one
|
|
47
|
+
non-streaming OpenAI-compatible request, and never retries a dispatched
|
|
48
|
+
inference POST automatically. `RUNBIOS_INFERENCE_TIMEOUT_MS` defaults to
|
|
49
|
+
`900000` (15 minutes). Its idempotency header is propagated, but replay/dedup is
|
|
50
|
+
not claimed without an explicit acknowledgement from the endpoint.
|
|
51
|
+
|
|
52
|
+
## What the platform derives (never a tool input)
|
|
53
|
+
|
|
54
|
+
An MCP client acts on the schema it is given, so the schema must not offer a
|
|
55
|
+
choice the control plane will reject. These serving settings are SERVER-DERIVED
|
|
56
|
+
and immutable, and are therefore absent from `create_inference` /
|
|
57
|
+
`preflight_inference`:
|
|
58
|
+
|
|
59
|
+
- `serving_mode` (`full` / `adapter` / `merged`) — from the verified
|
|
60
|
+
checkpoint's lineage; a catalog base model is served whole.
|
|
61
|
+
- `model_task` — from the resolved model: `chat` for an instruct or
|
|
62
|
+
vision-language model, `completion` for a base model with no chat template,
|
|
63
|
+
`embedding`/`rerank` for those architectures. Every model in the current
|
|
64
|
+
catalog resolves to `chat`, so there is nothing to select.
|
|
65
|
+
- `supports_images` — from the model's own config, never a caller claim.
|
|
66
|
+
|
|
67
|
+
They are reported back on the preflight/create/status responses, which is where
|
|
68
|
+
a caller reads them. Copying `canonical_request` from preflight into create
|
|
69
|
+
still works: the echoed derived fields are dropped rather than rejected.
|
|
70
|
+
|
|
71
|
+
`context_length` is the one window a caller may narrow. The policy the server
|
|
72
|
+
enforces is: default `min(native max, 262144)`, falling back to `32768` when the
|
|
73
|
+
native window is unknown (a model with a smaller native window keeps its own),
|
|
74
|
+
and the hard ceiling is always the model's OWN native max. No static JSON Schema
|
|
75
|
+
can express a per-model ceiling, so the tool advertises a `1024`-`1048576` sanity
|
|
76
|
+
range and its description names the real authority —
|
|
77
|
+
`model.native_max_context` from `preflight_inference`. An out-of-range value is
|
|
78
|
+
rejected with the whole policy in the message, not a bare bounds error.
|
|
79
|
+
|
|
80
|
+
`max_seq_length` (training) follows the same rule: the platform default is
|
|
81
|
+
`2048`, the hard ceiling is the base model's OWN window, and the authoritative
|
|
82
|
+
field bounds live in `get_training_capabilities` (`max_length`), so the tool
|
|
83
|
+
advertises the contract's `128` floor and the same `1048576` sanity ceiling
|
|
84
|
+
instead of the meaningless `10000000` it used to. `chat_with_inference`'s
|
|
85
|
+
`max_tokens` is bounded the same way — the real ceiling is the served window
|
|
86
|
+
minus the prompt.
|
|
87
|
+
|
|
88
|
+
## Spend consent is bounded by what the platform can charge
|
|
89
|
+
|
|
90
|
+
`max_price_hour_cents` is the one field that authorizes money: the maximum
|
|
91
|
+
accepted TOTAL hourly price for the complete GPU count. The control plane has no
|
|
92
|
+
ceiling of its own — it only refuses a cap BELOW the primary placement's
|
|
93
|
+
verified price — so `z.number().int()` alone advertised `9007199254740991`, i.e.
|
|
94
|
+
told an agent that consenting to nine quadrillion cents an hour is a legal
|
|
95
|
+
request. All five money-bearing tools (`preflight_inference`, `create_inference`,
|
|
96
|
+
`update_inference_policy`, `preflight_training_job`, `create_training_job`) now
|
|
97
|
+
cap it at **100000 cents ($1,000/hour)**: the dearest configuration the GPU
|
|
98
|
+
catalog can book is 8 x B300 at 8280 cents/hour, and the beta serving cap
|
|
99
|
+
(models up to ~130B total parameters) keeps a deployment inside that ladder, so
|
|
100
|
+
the bound has an order of magnitude of headroom and still reads as a limit
|
|
101
|
+
rather than a blank cheque. It is a sanity bound on consent, never a price
|
|
102
|
+
quote: `get_gpu_pricing` and the preflight response carry the real numbers, and
|
|
103
|
+
the rejection message says all of this.
|
|
104
|
+
|
|
105
|
+
## Error text an agent can act on
|
|
106
|
+
|
|
107
|
+
- A capacity rejection stays the structured compact JSON below.
|
|
108
|
+
- A model id outside the Run BiOS catalog answers `MODEL_NOT_HOSTED` and names every
|
|
109
|
+
route to a valid id (`list_models`, `search_models`, ids already returned by
|
|
110
|
+
`list_inferences`/`list_training_jobs`, the console Models page) plus what to
|
|
111
|
+
do when none of them answer — recovery advice never dead-ends on one tool.
|
|
112
|
+
- A model-resolution failure answers `MODEL_NOT_RESOLVED` /
|
|
113
|
+
`MODEL_REVISION_UNRESOLVED` in the platform's own terms. The upstream mirror's
|
|
114
|
+
transport error is never forwarded: an agent cannot act on a vendor's 404, and
|
|
115
|
+
the vendor is not the answer to "why can this not be sized".
|
|
116
|
+
- A workspace-scoped read with no workspace context answers
|
|
117
|
+
`WORKSPACE_CONTEXT_REQUIRED` and says the fix is configuration
|
|
118
|
+
(`RUNBIOS_WORKSPACE_ID` or a workspace-bound API key), not a retry.
|
|
119
|
+
- An unknown id answers a structured `<THING>_NOT_FOUND`
|
|
120
|
+
(`INFERENCE_NOT_FOUND`, `TRAINING_JOB_NOT_FOUND`, `CHECKPOINT_NOT_FOUND`,
|
|
121
|
+
`DATASET_NOT_FOUND`, `INTEGRATION_NOT_FOUND`, `BOOKING_NOT_FOUND`, or
|
|
122
|
+
`RESOURCE_NOT_FOUND` for a path with no id) carrying the id, the resource, the
|
|
123
|
+
upstream code, and the read that hands out valid ids — never the bare sentence
|
|
124
|
+
("Deployment not found") an agent would have to string-match. A 404 on a static
|
|
125
|
+
endpoint under the same prefix (`/api/inference/gpu-options`) is never reported
|
|
126
|
+
as an id that does not exist.
|
|
127
|
+
- FastAPI-shaped bodies (`{"detail": ...}`) are read as well as
|
|
128
|
+
`{"error":{"message"}}`, and a response with no message at all reports the
|
|
129
|
+
status, the path, and what to do — never a bare `BIOS API error 400`.
|
|
130
|
+
- No tool result is ever a bare `null`.
|
|
131
|
+
- `get_inference_status` adds a derived `status_summary` (phase, `count x SKU`
|
|
132
|
+
GPU, `endpoint_usable`, queue/GPU state) built only from fields in the same
|
|
133
|
+
response, so a field that is null before the pod reports is not read as "no
|
|
134
|
+
GPU" or "no phase".
|
|
135
|
+
- `list_supported_architectures` labels its rows as manifest provenance. Which
|
|
136
|
+
build published a row is registry provenance, never the image the platform is
|
|
137
|
+
running, and the control plane no longer publishes it at all: the public
|
|
138
|
+
projection drops `image_tag`, `bios_version` and `manifest_sha256` at the
|
|
139
|
+
boundary. Internal build references are additionally removed **by content**
|
|
140
|
+
from every string of every scope, not by field name: dropping the `image_tag`
|
|
141
|
+
key alone left the identical `<registry>/<engine>@sha256:<digest>` in the
|
|
142
|
+
free-text `notes` of 194 training-scope rows, and a rule that matches only a
|
|
143
|
+
key is defeated the moment the value moves. `manifest_provenance` reports how
|
|
144
|
+
many references were redacted and how many rows came from a published manifest
|
|
145
|
+
— counted from `source`, the one provenance field that survives the
|
|
146
|
+
projection. It publishes no build-version or digest counts, because after the
|
|
147
|
+
projection those could only ever report 0, and a standing 0 reads as "the
|
|
148
|
+
registry records no build" rather than "this response cannot see one".
|
|
149
|
+
|
|
150
|
+
## Safe inference workflow
|
|
151
|
+
|
|
152
|
+
1. `get_inference_gpu_options` returns inference-fit configurations joined to
|
|
153
|
+
authoritative deployment prices and stock. Prefer the MODEL-ADDRESSED form
|
|
154
|
+
(`model=<catalog_model_id>` — an id from `list_models`/`search_models`,
|
|
155
|
+
optionally `revision`): the server resolves the model facts (vision-aware)
|
|
156
|
+
and answers with computed
|
|
157
|
+
`min_gpus`, `valid_counts`, and `bookable_counts` — the exact minimums the
|
|
158
|
+
create gate enforces, so client-supplied facts can never understate a
|
|
159
|
+
minimum. `unknown` is not treated as available; never offer a count below
|
|
160
|
+
`min_gpus` or outside `bookable_counts`.
|
|
161
|
+
2. `preflight_inference` validates the exact source, model, GPU, queue, price
|
|
162
|
+
cap, and wallet-hold terms without creating a deployment or mutating funds.
|
|
163
|
+
3. `create_inference` uses the same request contract, self-validates the
|
|
164
|
+
selection against the model-addressed minimums before submitting, and
|
|
165
|
+
follows BOOK-BEFORE-REVEAL: a non-queued create answers `202` with a
|
|
166
|
+
booking handle while the GPU is secured on real capacity (30-40 s typical); the
|
|
167
|
+
tool polls the handle and returns the full payload — including the ONE-TIME
|
|
168
|
+
`inference_key` — only after the GPU is confirmed. A definitive miss
|
|
169
|
+
returns the standard `CAPACITY_UNAVAILABLE` stock-miss error with fresh
|
|
170
|
+
alternatives and NO deployment exists (nothing charged). A selection that
|
|
171
|
+
can never run at all is refused up front with a permanent code instead
|
|
172
|
+
(see below). Queueing is opt-in, a maximum
|
|
173
|
+
total hourly price is explicit, and a GPU alternative is never selected
|
|
174
|
+
without approval. `gpu_priorities` backups (1-5 ranked choices) may be
|
|
175
|
+
supplied on ANY create — they form the after-start replacement ladder;
|
|
176
|
+
the queue itself stays opt-in.
|
|
177
|
+
4. `get_inference_booking` polls a booking handle from a long-running
|
|
178
|
+
`create_inference` (pending → revealed payload → or the standard 409).
|
|
179
|
+
5. `get_inference_status` reports durable queue state, provisioning/loading,
|
|
180
|
+
endpoint readiness, price cap, notification summary, and wallet
|
|
181
|
+
authorization. An endpoint is live only when the returned status is
|
|
182
|
+
`running`. `queued_capacity` means waiting for stock at zero charge —
|
|
183
|
+
after a deployment has ever been booked, GPU loss never ends in a
|
|
184
|
+
capacity failure; it waits and auto-resumes.
|
|
185
|
+
6. `get_inference_notifications` reports durable lifecycle-email delivery,
|
|
186
|
+
bounded retries, and operator-visible dead letters.
|
|
187
|
+
7. `update_inference_policy`, `stop_inference`, `resume_inference`,
|
|
188
|
+
`restart_inference`, and `delete_inference` manage the lifecycle.
|
|
189
|
+
|
|
190
|
+
## Standard GPU rejection contract
|
|
191
|
+
|
|
192
|
+
Training and inference speak ONE GPU rejection shape, with a `code` that says
|
|
193
|
+
whether waiting can ever help. The MCP transport serializes every code in the
|
|
194
|
+
family as compact JSON in the tool-error text, even when zero alternatives
|
|
195
|
+
exist, so an agent can act on it directly.
|
|
196
|
+
|
|
197
|
+
| `reason` | `code` | HTTP | `queue_offered` |
|
|
198
|
+
| --- | --- | --- | --- |
|
|
199
|
+
| `insufficient_stock` | `CAPACITY_UNAVAILABLE` | 409 | `true` |
|
|
200
|
+
| `model_too_large` | `GPU_TYPE_TOO_SMALL` | 400 | `false` |
|
|
201
|
+
| `below_model_minimum` | `GPU_COUNT_BELOW_MINIMUM` | 400 | `false` |
|
|
202
|
+
| `invalid_gpu_count` | `GPU_COUNT_INVALID` | 400 | `false` |
|
|
203
|
+
| `gpu_unsupported` | `GPU_TYPE_UNSUPPORTED` | 400 | `false` |
|
|
204
|
+
|
|
205
|
+
`CAPACITY_UNAVAILABLE` is the ONLY transient one: the GPU is not free right
|
|
206
|
+
now, so retrying later or joining the queue can succeed (training also echoes
|
|
207
|
+
the deprecated `SELECTED_GPU_UNAVAILABLE` as `legacy_code` for one release).
|
|
208
|
+
The four `400` codes are permanent facts about the request itself, so the queue
|
|
209
|
+
is never offered for them and resending the same `gpu_type`/`gpu_count` can
|
|
210
|
+
never succeed. Branch on `retryable_as_submitted` (or `queue_offered`) rather
|
|
211
|
+
than on the message text.
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"code": "GPU_TYPE_TOO_SMALL",
|
|
216
|
+
"status": 400,
|
|
217
|
+
"reason": "model_too_large",
|
|
218
|
+
"message": "This model does not fit on L40_48GB, at any GPU count.",
|
|
219
|
+
"recoverable": true,
|
|
220
|
+
"retryable_as_submitted": false,
|
|
221
|
+
"instruction": "This GPU cannot run the request as submitted, and free capacity will never change that. Retry the same call with gpu_type and gpu_count set to one of available_gpus, never below that entry's min_gpus. Do not set allow_capacity_queue, and do not send the same gpu_type and gpu_count again: neither waiting nor the queue can make this request fit.",
|
|
222
|
+
"available_gpus": [
|
|
223
|
+
{ "gpu_type": "H100_80GB", "gpu_count": 2, "min_gpus": 2, "valid_counts": [2, 4, 8],
|
|
224
|
+
"tier": "secure", "price_hour_cents": 598, "available_count": 6 }
|
|
225
|
+
],
|
|
226
|
+
"minimum_requirement": {
|
|
227
|
+
"selected_gpu_min": 2, "selected_valid_counts": [2, 4, 8],
|
|
228
|
+
"per_type": [{ "gpu_type": "H100_80GB", "min_gpus": 2, "valid_counts": [2, 4, 8] }]
|
|
229
|
+
},
|
|
230
|
+
"selected": { "gpu_type": "L40_48GB", "gpu_count": 2, "availability_status": "available" },
|
|
231
|
+
"queue_offered": false,
|
|
232
|
+
"queue_eligible": false,
|
|
233
|
+
"gpu_priorities_entry": 1,
|
|
234
|
+
"checked_at": "2026-07-19T00:00:00Z"
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`available_alternatives` is the deprecated alias of `available_gpus`, and
|
|
239
|
+
`gpu_priorities_entry` is the 1-based rank of the refused `gpu_priorities`
|
|
240
|
+
entry when the rejection belongs to one. A stock miss carries the same shape
|
|
241
|
+
with `code: "CAPACITY_UNAVAILABLE"`, `queue_offered: true`,
|
|
242
|
+
`retryable_as_submitted: true`, and an `instruction` that does offer
|
|
243
|
+
`allow_capacity_queue=true`.
|
|
244
|
+
|
|
245
|
+
A `503 ADMISSION_UNAVAILABLE` is a transient market outage, never a capacity
|
|
246
|
+
verdict. Retry shortly, and never conclude out-of-stock from it. See
|
|
247
|
+
`get_platform_guide` topic `capacity_errors` for the recovery playbook.
|
|
248
|
+
|
|
249
|
+
Models are selectable only from the platform's own catalog: `list_models`
|
|
250
|
+
and `search_models` read the hosted registry, and `create_training_job`,
|
|
251
|
+
`preflight_training_job`, `create_inference`, and `preflight_inference`
|
|
252
|
+
validate the model id against it, answering `MODEL_NOT_HOSTED` — never a
|
|
253
|
+
live Hugging Face lookup. Catalog models are pre-mirrored, so raw access
|
|
254
|
+
tokens are intentionally excluded from MCP tool inputs and no integration is
|
|
255
|
+
needed for models; Hugging Face integrations remain for dataset imports via
|
|
256
|
+
`import_huggingface_dataset`.
|
|
257
|
+
|
|
258
|
+
`create_training_job` and `resume_training_job` accept an optional
|
|
259
|
+
`idempotency_key`. Reuse the same value after a timeout so the API replays the
|
|
260
|
+
original acknowledgement instead of creating another job, wallet hold, queue
|
|
261
|
+
entry, or GPU request. The MCP server generates a fresh key when it is omitted.
|
|
262
|
+
|
|
263
|
+
## Training lifecycle (book-before-reveal)
|
|
264
|
+
|
|
265
|
+
Like inference, `create_training_job` books a GPU before it reveals a job.
|
|
266
|
+
The call blocks while the ranked ladder is booked (~40s typical); a job id and
|
|
267
|
+
the "training started" email exist only once a real pod is secured. Read
|
|
268
|
+
`status` on the response and from `get_training_status`:
|
|
269
|
+
|
|
270
|
+
- `booked` — a GPU is secured (booked == secured); the job then provisions,
|
|
271
|
+
downloads, and runs on its own. Money is captured only after this point.
|
|
272
|
+
- `securing` — still booking at the deadline; the async tail continues.
|
|
273
|
+
Poll `get_training_status` until `booked`/`running` (or a terminal state).
|
|
274
|
+
Nothing is charged while securing.
|
|
275
|
+
- `queued` — returned only with explicit queue consent; waits for stock at
|
|
276
|
+
zero charge and books via the same path.
|
|
277
|
+
- A booking-time stock miss returns the standard `CAPACITY_UNAVAILABLE`
|
|
278
|
+
(409) with fresh `available_gpus` and **no job exists** (nothing charged);
|
|
279
|
+
a selection that can never run returns one of the permanent `400` codes
|
|
280
|
+
above instead, and no queue is offered for it. A transient
|
|
281
|
+
`503 ADMISSION_UNAVAILABLE` is a retry, never out-of-stock, and the platform
|
|
282
|
+
never auto-substitutes a GPU.
|
|
283
|
+
|
|
284
|
+
If a started job loses its pod, it rests at `interrupted` — a self-heal state
|
|
285
|
+
distinct from `failed` in which billing is already stopped. Call
|
|
286
|
+
`resume_training_job` to continue from the last checkpoint; resume re-books on
|
|
287
|
+
secured capacity before reporting resumed, so the same guarantees apply.
|
|
288
|
+
|
|
289
|
+
Training and deployment share one authoritative GPU-capacity broker. Server
|
|
290
|
+
priority is evaluated first. Within an equal-priority class, the broker rotates
|
|
291
|
+
across users and preserves FIFO order within each user's own requests. This
|
|
292
|
+
anti-starvation policy prevents one user's large backlog from monopolizing GPU
|
|
293
|
+
capacity; consequently, a newer request from another user can run before an
|
|
294
|
+
older request in a busy user's backlog.
|
|
295
|
+
|
|
296
|
+
For each eligible request, the broker selects the lowest-ranked approved GPU
|
|
297
|
+
choice that is live, has enough cards, and remains within the accepted total
|
|
298
|
+
hourly price cap. Thus an older request's third choice can receive capacity
|
|
299
|
+
before a newer request's first choice. Unknown/stale provider inventory issues
|
|
300
|
+
no grant. Every vendor mutation requires the broker's short-lived fenced grant,
|
|
301
|
+
and atomic reservations prevent training and deployment replicas from booking
|
|
302
|
+
the same cards.
|
|
303
|
+
|
|
304
|
+
If capacity disappears before the vendor mutation, a queued request releases
|
|
305
|
+
the grant and returns to its original global position. Expired worker leases
|
|
306
|
+
are fenced and recovered with a new grant identity. Ambiguous provider results
|
|
307
|
+
retain the same immutable provisioning request and grant fence until
|
|
308
|
+
reconciliation proves success or definitive failure, so MCP clients should
|
|
309
|
+
retry with the same idempotency key rather than submit a replacement request.
|
|
310
|
+
|
|
311
|
+
## What never leaves the platform
|
|
312
|
+
|
|
313
|
+
An MCP client is a program the customer runs, so every tool result is a
|
|
314
|
+
customer-facing response. Two facts never appear in one:
|
|
315
|
+
|
|
316
|
+
- **Container build identity** — any image name, version, tag, digest or path.
|
|
317
|
+
`list_supported_architectures` used to ship the engine image reference that
|
|
318
|
+
published each registry row (`image_tag`, `bios_version`, `manifest_sha256`,
|
|
319
|
+
and the same tag again inside the free-text `notes`), and
|
|
320
|
+
`get_training_capabilities` shipped `image_compatibility`, the engine image
|
|
321
|
+
VERSION range the contract was written against. Both are removed at the tool
|
|
322
|
+
boundary (`src/redaction.ts`), by CONTENT as well as by key — a key-only rule
|
|
323
|
+
is defeated the moment the same value moves one field over, which is exactly
|
|
324
|
+
what `notes` did. Architecture support is unchanged and still authoritative:
|
|
325
|
+
the deployment and training gates read those same rows, and the response says
|
|
326
|
+
so. Engine text that reaches an agent as an error (upstream bodies, serving
|
|
327
|
+
exceptions, training log lines) goes through the same content rule.
|
|
328
|
+
- **Which supplier backs a placement** — the control plane already answers the
|
|
329
|
+
neutral `bios-cloud` / `global` placement labels, and the tools echo those
|
|
330
|
+
unchanged. Tool text describes what was bought ("the GPU is secured on real
|
|
331
|
+
capacity"), never who sold it.
|
|
332
|
+
|
|
333
|
+
## Hosted (remote) connector — implemented, not planned
|
|
334
|
+
|
|
335
|
+
Besides the stdio entry (`dist/index.js`), this package ships the hosted
|
|
336
|
+
Streamable HTTP connector used by clients that only accept remote MCP servers
|
|
337
|
+
(`dist/http/main.js`, ECS service `bios-mcp`, default port 7152). It is built and
|
|
338
|
+
deployed, not a roadmap item:
|
|
339
|
+
|
|
340
|
+
- `POST {PUBLIC_BASE_URL}/mcp` — stateless Streamable HTTP endpoint. `GET` and
|
|
341
|
+
`DELETE` answer `405`; an unauthenticated call answers `401` with the
|
|
342
|
+
RFC 9728 `WWW-Authenticate: Bearer resource_metadata=...` challenge.
|
|
343
|
+
- OAuth 2.1 authorization server with dynamic client registration and PKCE,
|
|
344
|
+
advertised at `/.well-known/oauth-protected-resource`,
|
|
345
|
+
`/.well-known/oauth-authorization-server`, and
|
|
346
|
+
`/.well-known/openid-configuration` (each also under `/mcp`).
|
|
347
|
+
- Consent is granted in the console (`CONSOLE_BASE_URL`); every request
|
|
348
|
+
re-validates the bearer token and re-checks scopes and workspace membership
|
|
349
|
+
through the internal api-gateway.
|
|
350
|
+
- `chat_with_inference` is intentionally unavailable on the hosted connection:
|
|
351
|
+
a deployment's serving key is shown exactly once at create time and is never
|
|
352
|
+
retrievable, so the tool fails with that explanation instead of a
|
|
353
|
+
local-environment hint that does not apply.
|
|
354
|
+
|
|
355
|
+
Any documentation still describing the hosted connector as "planned" is stale.
|
|
356
|
+
|
|
357
|
+
## Verification
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
npm test
|
|
361
|
+
npm run typecheck
|
|
362
|
+
npm run build
|
|
363
|
+
```
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE transient GPU rejection code: the GPU is not in stock right now.
|
|
3
|
+
* Stock changes, so retrying later or joining the capacity queue is a real
|
|
4
|
+
* answer here (and ONLY here).
|
|
5
|
+
*/
|
|
6
|
+
export declare const CAPACITY_UNAVAILABLE_CODE = "CAPACITY_UNAVAILABLE";
|
|
7
|
+
/**
|
|
8
|
+
* Deprecated training-side alias of CAPACITY_UNAVAILABLE, still echoed for one
|
|
9
|
+
* release. It means the same transient thing.
|
|
10
|
+
*/
|
|
11
|
+
export declare const LEGACY_CAPACITY_UNAVAILABLE_CODE = "SELECTED_GPU_UNAVAILABLE";
|
|
12
|
+
/**
|
|
13
|
+
* Permanent GPU rejection codes. Each one is a fixed fact about the request
|
|
14
|
+
* itself: the model does not fit on that GPU, the GPU count is below what the
|
|
15
|
+
* model needs, the count cannot be used for this model, or that GPU cannot run
|
|
16
|
+
* the model at all. Free stock never changes any of them, so waiting and the
|
|
17
|
+
* capacity queue can never turn one of these into a booking. Callers MUST NOT
|
|
18
|
+
* offer the queue for these.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PERMANENT_GPU_CODES: readonly ["GPU_TYPE_TOO_SMALL", "GPU_COUNT_BELOW_MINIMUM", "GPU_COUNT_INVALID", "GPU_TYPE_UNSUPPORTED"];
|
|
21
|
+
/** Resolve the machine code for a rejection reason. */
|
|
22
|
+
export declare function gpuRejectionCodeFor(reason?: string): string;
|
|
23
|
+
/** True when this code can never be fixed by waiting for stock. */
|
|
24
|
+
export declare function isPermanentGpuCode(code: unknown): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* HTTP status for a rejection reason: 409 for a stock miss (a retry can resolve
|
|
27
|
+
* it), 400 for a permanent rejection the request itself has to change.
|
|
28
|
+
*/
|
|
29
|
+
export declare function gpuRejectionStatusFor(reason?: string): number;
|
|
30
|
+
/** Every code whose body carries the standard GPU rejection fields. */
|
|
31
|
+
export declare function isGpuRejectionCode(code: unknown): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The recovery instruction handed to the calling agent. It branches on whether
|
|
34
|
+
* the rejection is permanent, because "wait for stock" is actively harmful
|
|
35
|
+
* advice on a request that can never book: the agent would park a deployment
|
|
36
|
+
* that no amount of capacity can satisfy.
|
|
37
|
+
*/
|
|
38
|
+
export declare function gpuRejectionInstruction(opts: {
|
|
39
|
+
permanent: boolean;
|
|
40
|
+
hasAlternatives: boolean;
|
|
41
|
+
/** How to name the retry, e.g. "create_inference". Defaults to "the same call". */
|
|
42
|
+
call?: string;
|
|
43
|
+
}): string;
|
|
44
|
+
export interface ApiOpts {
|
|
45
|
+
method?: string;
|
|
46
|
+
body?: unknown;
|
|
47
|
+
params?: Record<string, string | undefined>;
|
|
48
|
+
formData?: FormData;
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
export interface BiosClientOptions {
|
|
52
|
+
/** Control-plane base URL; no trailing slash. */
|
|
53
|
+
baseUrl: string;
|
|
54
|
+
/** Control-plane auth headers: {"X-API-Key": k} or {"Authorization": "Bearer ..."}. */
|
|
55
|
+
authHeaders: Record<string, string>;
|
|
56
|
+
orgId?: string;
|
|
57
|
+
workspaceId?: string;
|
|
58
|
+
/** Base URL for inference calls; defaults to baseUrl. */
|
|
59
|
+
inferenceBaseUrl?: string;
|
|
60
|
+
/** Authorization header value for inference calls (may be empty). */
|
|
61
|
+
inferenceAuthHeader?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Fallback Authorization header for /v1/chat/completions when no dedicated
|
|
64
|
+
* deployment key (inferenceAuthHeader) is set. A workspace platform key with
|
|
65
|
+
* serverless scope authenticates model-ID-routed serverless catalog calls;
|
|
66
|
+
* the unified /v1 gateway routes by the request's `model`. Dedicated
|
|
67
|
+
* deployment keys still take precedence when present.
|
|
68
|
+
*/
|
|
69
|
+
serverlessAuthHeader?: string;
|
|
70
|
+
/** Inference request timeout in milliseconds; defaults to 900000. */
|
|
71
|
+
inferenceTimeoutMs?: number;
|
|
72
|
+
/** User-Agent header; defaults to "bios-mcp/<VERSION>". */
|
|
73
|
+
userAgent?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Format a failed control-plane response as error text an agent can act on.
|
|
77
|
+
*
|
|
78
|
+
* Four shapes come out of here:
|
|
79
|
+
* 1. Capacity-class rejections stay the structured compact JSON of the
|
|
80
|
+
* standard capacity contract (book-first §3) — unchanged.
|
|
81
|
+
* 2. A missing-workspace 400 becomes a structured, self-recoverable error
|
|
82
|
+
* naming the exact fix, because "Workspace context required" alone does
|
|
83
|
+
* not tell an agent where workspace context comes from.
|
|
84
|
+
* 3. A 404 becomes a structured `<THING>_NOT_FOUND` naming the id, the
|
|
85
|
+
* resource, and the read that hands out valid ids — the same shape as
|
|
86
|
+
* MODEL_NOT_HOSTED, so an unknown id never has to be string-matched out
|
|
87
|
+
* of a bare sentence.
|
|
88
|
+
* 4. Everything else keeps the API's own prose (so a real message is never
|
|
89
|
+
* paraphrased), and a response with NO message gets the status, the path,
|
|
90
|
+
* and what to do about it instead of a bare error code.
|
|
91
|
+
*
|
|
92
|
+
* CUSTOMER BOUNDARY: whichever shape comes out, it is redacted before it is
|
|
93
|
+
* returned. Every one of the four carries text the platform did not author —
|
|
94
|
+
* an upstream message, a proxy body, an engine exception — so build identity
|
|
95
|
+
* (image name/tag/digest/version) can ride any of them out to an MCP client.
|
|
96
|
+
* The redaction is applied HERE rather than at the single throw site so the
|
|
97
|
+
* exported helper cannot be reused later without it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function formatApiError(status: number, text: string, path: string): string;
|
|
100
|
+
export declare class BiosClient {
|
|
101
|
+
private readonly baseUrl;
|
|
102
|
+
private readonly authHeaders;
|
|
103
|
+
private readonly orgId;
|
|
104
|
+
private readonly workspaceId;
|
|
105
|
+
private readonly inferenceBaseUrl;
|
|
106
|
+
private readonly inferenceAuthHeader;
|
|
107
|
+
private readonly serverlessAuthHeader;
|
|
108
|
+
private readonly inferenceTimeoutMs;
|
|
109
|
+
private readonly userAgent;
|
|
110
|
+
constructor(opts: BiosClientOptions);
|
|
111
|
+
/**
|
|
112
|
+
* Tri-state probe against the public model registry (model-service):
|
|
113
|
+
* "hosted" — the exact author/name id is a verified catalog model;
|
|
114
|
+
* "not_hosted" — the registry definitively answered 404; "unknown" — the
|
|
115
|
+
* registry could not answer (network/5xx). Callers must treat "unknown" as
|
|
116
|
+
* advisory only and let the server-side gate enforce: never fail closed.
|
|
117
|
+
*/
|
|
118
|
+
modelRegistryStatus(modelId: string): Promise<"hosted" | "not_hosted" | "unknown">;
|
|
119
|
+
api<T = unknown>(path: string, opts?: ApiOpts): Promise<T>;
|
|
120
|
+
/**
|
|
121
|
+
* Select the Authorization header for /v1/chat/completions. A dedicated
|
|
122
|
+
* deployment key (inferenceAuthHeader) wins when present; otherwise a
|
|
123
|
+
* serverless-scoped platform key (serverlessAuthHeader) authenticates
|
|
124
|
+
* model-ID-routed serverless catalog calls on the unified endpoint.
|
|
125
|
+
*/
|
|
126
|
+
private resolveInferenceAuth;
|
|
127
|
+
inferenceApi(body: Record<string, unknown>, idempotencyKey?: string, requestId?: string, callerSignal?: AbortSignal): Promise<unknown>;
|
|
128
|
+
/**
|
|
129
|
+
* POST a streaming /v1/chat/completions request, consume the SSE, and
|
|
130
|
+
* aggregate the deltas into ONE OpenAI-shaped completion. Content and
|
|
131
|
+
* reasoning_content are concatenated in arrival order (so reasoning surfaces
|
|
132
|
+
* as produced), tool-call fragments are reassembled by index, and the final
|
|
133
|
+
* usage row (requested via stream_options.include_usage) is attached. The
|
|
134
|
+
* MCP transport returns a single tool result, so streaming is used for
|
|
135
|
+
* on-the-wire liveness/real-TTFT parity while the caller receives the
|
|
136
|
+
* assembled message.
|
|
137
|
+
*/
|
|
138
|
+
streamInferenceApi(body: Record<string, unknown>, idempotencyKey?: string, requestId?: string, callerSignal?: AbortSignal): Promise<unknown>;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parse the OpenAI chat-completion SSE from `stream` and fold its deltas into a
|
|
142
|
+
* single non-streaming-shaped response. Content and reasoning_content are
|
|
143
|
+
* concatenated in arrival order; tool-call fragments are merged by index;
|
|
144
|
+
* finish_reason, role, id, model, and the final usage row are captured.
|
|
145
|
+
*/
|
|
146
|
+
export declare function aggregateChatStream(stream: ReadableStream<Uint8Array>): Promise<Record<string, unknown>>;
|
|
147
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|