runbios-mcp
v0.6.1
Published
Run BiOS MCP server — train and deploy AI models from MCP-compatible automation clients
Readme
Run BiOS MCP server
The MCP server exposes the authenticated Run BiOS training and model-serving
control planes to MCP-compatible automation clients. This build registers 126
tools when every surface is open; get_platform_guide derives its inventory
from what is actually registered so its count matches the wire (tools/list).
PRE-LAUNCH: while the coming-soon gates are on (the default — see
src/config.ts), the three creation tools (create_training_job,
upload_dataset, import_huggingface_dataset) are not registered, and read and
lifecycle tools, including dataset mapping/revalidation/source/integration
maintenance, remain available. A production organization holding the training
beta grant opens both creation gates for its own MCP session. Local API-key mode
resolves the bound organization automatically through key introspection, so
RUNBIOS_ORG_ID is optional; otherwise set
RUNBIOS_TRAINING_COMING_SOON=false / RUNBIOS_DATASETS_COMING_SOON=false at
launch and the registrations return.
THE LOOP GATE: the Conscious Loop is offered only where the
platform has opened it. This package does not decide that for itself: at start it asks the API it is pointed at, with your key
(GET /api/loop/models). Where the API answers 403 LOOP_COMING_SOON, or has
no loop backend at all (503 capability_unavailable, or a 404), none of its 59
loop_ tools is registered — reads included — the platform guide does not
mention the loop, and introspect_api_key leaves the loop tools and SDK methods
out of what a key holding the loop scopes may call (those scopes read
"Conscious Loop: not available on this platform yet"). A key without the
loop:read scope (403 INSUFFICIENT_SCOPE) gets no loop_ tools either: it
could not call one. Give the key loop:read (and loop:write to change
anything) and restart the server. Where the API offers the loop to the key,
every loop_ tool is registered with no variable set. Any other answer (a
server error, no answer within two seconds) registers them too, and each loop
call is then answered by the API with its own code. WHERE THE LOOP IS SWITCHED
OFF the API still answers the five routes a member needs to stop paying: list
and read pipelines, switch one off, delete one, cancel a training. So where the
models probe answers 403 LOOP_COMING_SOON, the package asks once more,
GET /api/loop/pipelines?limit=1; when that answers 200 the five tools of those
routes are registered (loop_list_pipelines, loop_get_pipeline,
loop_update_pipeline with enabled: false only, loop_delete_pipeline,
loop_cancel_training_run), each description saying the loop is switched off,
and the guide names those five and nothing else of the loop; any other answer
keeps the family hidden. RUNBIOS_LOOP_COMING_SOON
overrides the probe: false registers the tools, any other value hides them,
and nothing is asked. The loop gate has this one name: unlike training and datasets it has no
legacy BIOS_* spelling, and the training beta grant does not open it. The
hosted server reads the gate from its task definition instead of asking; its
task definitions open it in every environment.
So the wire count is 126 with everything open, 123 with training and datasets gated, and 64 where all three gates are closed (69 where the API says the loop is switched off: its five stop-paying tools stay).
Set these environment variables in the MCP client configuration:
{
"command": "npx",
"args": ["-y", "runbios-mcp"],
"env": {
"RUNBIOS_API_KEY": "bios-...",
"RUNBIOS_BASE_URL": "https://api.runbios.ai",
"RUNBIOS_WORKSPACE_ID": "workspace-id",
"RUNBIOS_INFERENCE_KEY": "sk-bios-deployment-key"
}
}RUNBIOS_* is the canonical environment prefix. Platform API keys default to bios-; dashboard-issued custom and provider-shaped prefixes work too, and legacy usf- keys remain valid.
The legacy BIOS_* variable names are still accepted as fallbacks.
https://api.runbios.ai is the canonical production hostname. Use
https://api-dev.runbios.ai explicitly during dev.
For interactive JWT authentication, omit RUNBIOS_API_KEY and set
RUNBIOS_ACCESS_TOKEN. The server never guesses credential type from a prefix:
API keys use X-API-Key, while access tokens use Authorization: Bearer.
The inference tools use RUNBIOS_INFERENCE_KEY for a dedicated deployment, or
a workspace RUNBIOS_API_KEY whose scopes allow serverless or dedicated
invocation through the unified /v1 gateway;
no serving credential is accepted as a tool argument or returned in
model-visible text. chat_with_inference and message_with_inference work on
models that serve the respective OpenAI-chat or Anthropic-Messages dialect.
complete_with_inference, embed_with_inference, and
rerank_with_inference require a dedicated deployment that advertises the
matching task: the serverless pool has no completions/embeddings/rerank
routes. Choose by the server's declared model task, not a guess from the name.
Set RUNBIOS_INFERENCE_BASE_URL=https://api-dev.runbios.ai explicitly in dev.
The chat tool validates function schemas and matching tool responses. Chat
streaming assembles deltas; completions and Messages streaming preserve ordered
SSE events in one MCP result (up to 2 MB/4096 events; use an SDK for longer
streams). MCP cancellation propagates to HTTP, an error or response echoing the
configured key is suppressed, and a dispatched inference POST is never retried
automatically. RUNBIOS_INFERENCE_TIMEOUT_MS defaults to 900000 (15 minutes).
Caller-provided idempotency headers are forwarded without promising server-side
replay/deduplication.
Conscious Loop
What it is
The Conscious Loop keeps improving a model from an app's conversations and
the user's feedback. A pipeline is one use case. The app's conversations
are saved to it as samples, the user says how each answer did, and when enough
samples have feedback a training starts: the model learns from them. Every
training is tested before it can go live: on questions from the pipeline's own
samples that the model never trained on (the automatic test), and on the
user's benchmarks if they add any. A training that does better, or that a
member approves, becomes the next version: v1, v2, v3 and on, with no limit.
The live version is the version the app gets answers from right now; it
answers to the model name loop-<pipeline ID>, and each new version takes
that name over. For 30 days after a new version goes live a member can go back
to the one before.
The pipeline ID (e.g. support-replies) is made from the pipeline's name
when it is created and never changes; the pipeline's Connect page shows it.
The sample ID comes back in the reply's X-Loop-Sample-Id header, and
feedback names it; it is missing only when the platform is not recording calls
just then, and then nothing was saved. A sample ID does not prove the pipeline
ID is right: if no pipeline has that ID (misspelt, or the pipeline was
deleted), the conversation is not saved under it. If recording is switched on
for the model that answered, the call is still recorded the way that model
records every call, and feedback on it is then accepted; when nothing records
it, feedback on it is refused (first "not recorded yet", then "not found").
loop_stats counts such calls under capture (reason no_pipeline), and
loop_list_pipelines shows each pipeline's ID. The pipeline ID is read in
lowercase.
The three steps, from an assistant
- Send the app's conversations.
loop_chatasks a model with the pipeline ID in theX-Loop-Pipelineheader and saves the conversation as a sample: it returnsanswerandsample_id. It is a normal, billed model call. Before the pipeline's first version passmodel(the model the app uses now); left out, it asksloop-<pipeline ID>, and while no version is live it answers with a plain sentence saying so. - Say how each answer did.
loop_add_signalon thesample_id:good(learned as it is),bad(not learned), orcorrectedwith the right answer incorrection(the right answer is learned instead). Feedback sent the instant the answer arrives can reach the platform before the conversation is saved (409 SAMPLE_NOT_YET_RECORDED):loop_add_signalwaits 2 seconds and sends it again by itself, up to 6 tries, as the SDKs do. Without feedback, if automatic scoring is on, the judge scores it: the judge is an AI that compares each answer with the right answer, and where nobody gave one, as here, it scores the answer on its instructions alone. 0.8 or more is learned like good. - Use the improved model. Leave
modelout ofloop_chatonce a version is live, or callloop-<pipeline ID>from any OpenAI-compatible client: it always answers with the live version.
loop_get_pipeline and loop_list_pipeline_attempts say how each training
went and what each version learned; get_platform_guide lists every loop tool
in the order a user takes them.
Workspace serverless observability
A platform API key or hosted OAuth grant with analytics:read may use
get_serverless_limits, get_serverless_usage_overview,
get_serverless_usage_by_model, list_serverless_requests,
get_serverless_usage_timeseries, get_serverless_usage_daily and
get_serverless_savings. The gateway resolves current workspace access from
the credential and stamps it on the request: no tool takes an organization or
workspace override, serving key, or per-key usage selector. serverless scope
alone authorizes inference, not analytics. The saved RPM/spend settings are
read-only; key creation/listing, key-level usage, org-wide totals, and all limit
writes stay in the JWT-authenticated console.
Raw request metrics and the per-request log have about seven days of retained
detail; get_serverless_usage_daily reads the workspace's full-history daily
counters over up to 92 UTC days, counting admitted failures as well as success.
The saved workspace RPM cap is not a claim about effective org-tier RPM or
remaining headroom. Incomplete responses error rather than presenting zero
usage or an unset limit.
What the platform derives (never a tool input)
An MCP client acts on the schema it is given, so the schema must not offer a
choice the control plane will reject. These serving settings are SERVER-DERIVED
and immutable, and are therefore absent from create_inference /
preflight_inference:
serving_mode(full/adapter/merged) — from the verified checkpoint's lineage; a catalog base model is served whole.model_task— from the resolved model:chatfor an instruct or vision-language model,completionfor a base model with no chat template,embedding/rerankfor those architectures. Every model in the current catalog resolves tochat, so there is nothing to select.supports_images— from the model's own config, never a caller claim.
They are reported back on the preflight/create/status responses, which is where
a caller reads them. Copying canonical_request from preflight into create
still works: the echoed derived fields are dropped rather than rejected.
context_length is the one window a caller may narrow. The policy the server
enforces is: default min(native max, 262144), falling back to 32768 when the
native window is unknown (a model with a smaller native window keeps its own),
and the hard ceiling is always the model's OWN native max. No static JSON Schema
can express a per-model ceiling, so the tool advertises a 1024-1048576 sanity
range and its description names the real authority —
model.native_max_context from preflight_inference. An out-of-range value is
rejected with the whole policy in the message, not a bare bounds error.
max_seq_length (training) follows the same rule: the platform default is
2048, the hard ceiling is the base model's OWN window, and the authoritative
field bounds live in get_training_capabilities (max_length), so the tool
advertises the contract's 128 floor and the same 1048576 sanity ceiling
instead of the meaningless 10000000 it used to. chat_with_inference's
max_tokens is bounded the same way — the real ceiling is the served window
minus the prompt.
Spend consent is bounded by what the platform can charge
max_price_hour_cents is the one field that authorizes money: the maximum
accepted TOTAL hourly price for the complete GPU count. The control plane has no
ceiling of its own — it only refuses a cap BELOW the primary placement's
verified price — so z.number().int() alone advertised 9007199254740991, i.e.
told an agent that consenting to nine quadrillion cents an hour is a legal
request. All five money-bearing tools (preflight_inference, create_inference,
update_inference_policy, preflight_training_job, create_training_job) now
cap it at 100000 cents ($1,000/hour): the dearest configuration the GPU
catalog can book is 8 x B300 at 8280 cents/hour, and the beta serving cap
(models up to ~130B total parameters) keeps a deployment inside that ladder, so
the bound has an order of magnitude of headroom and still reads as a limit
rather than a blank cheque. It is a sanity bound on consent, never a price
quote: get_gpu_pricing and the preflight response carry the real numbers, and
the rejection message says all of this.
Error text an agent can act on
- A capacity rejection stays the structured compact JSON below.
- A model id outside the Run BiOS catalog answers
MODEL_NOT_HOSTEDand names every route to a valid id (list_models,search_models, ids already returned bylist_inferences/list_training_jobs, the console Models page) plus what to do when none of them answer — recovery advice never dead-ends on one tool. - A model-resolution failure answers
MODEL_NOT_RESOLVED/MODEL_REVISION_UNRESOLVEDin the platform's own terms. The upstream mirror's transport error is never forwarded: an agent cannot act on a vendor's 404, and the vendor is not the answer to "why can this not be sized". - A workspace-scoped read with no workspace context answers
WORKSPACE_CONTEXT_REQUIREDand says the fix is configuration (RUNBIOS_WORKSPACE_IDor a workspace-bound API key), not a retry. - An unknown id answers a structured
<THING>_NOT_FOUND(INFERENCE_NOT_FOUND,TRAINING_JOB_NOT_FOUND,CHECKPOINT_NOT_FOUND,DATASET_NOT_FOUND,INTEGRATION_NOT_FOUND,BOOKING_NOT_FOUND, orRESOURCE_NOT_FOUNDfor a path with no id) carrying the id, the resource, the upstream code, and the read that hands out valid ids — never the bare sentence ("Deployment not found") an agent would have to string-match. A 404 on a static endpoint under the same prefix (/api/inference/gpu-options) is never reported as an id that does not exist. - FastAPI-shaped bodies (
{"detail": ...}) are read as well as{"error":{"message"}}, and a response with no message at all reports the status, the path, and what to do — never a bareBIOS API error 400. - No tool result is ever a bare
null. get_inference_statusadds a derivedstatus_summary(phase,count x SKUGPU,endpoint_usable, queue/GPU state) built only from fields in the same response, so a field that is null before the pod reports is not read as "no GPU" or "no phase".list_supported_architectureslabels its rows as manifest provenance. Which build published a row is registry provenance, never the image the platform is running, and the control plane no longer publishes it at all: the public projection dropsimage_tag,bios_versionandmanifest_sha256at the boundary. Internal build references are additionally removed by content from every string of every scope, not by field name: dropping theimage_tagkey alone left the identical<registry>/<engine>@sha256:<digest>in the free-textnotesof 194 training-scope rows, and a rule that matches only a key is defeated the moment the value moves.manifest_provenancereports how many references were redacted and how many rows came from a published manifest — counted fromsource, the one provenance field that survives the projection. It publishes no build-version or digest counts, because after the projection those could only ever report 0, and a standing 0 reads as "the registry records no build" rather than "this response cannot see one".
Safe inference workflow
get_inference_gpu_optionsreturns inference-fit configurations joined to authoritative deployment prices and stock. Prefer the MODEL-ADDRESSED form (model=<catalog_model_id>— an id fromlist_models/search_models, optionallyrevision): the server resolves the model facts (vision-aware) and answers with computedmin_gpus,valid_counts, andbookable_counts— the exact minimums the create gate enforces, so client-supplied facts can never understate a minimum.unknownis not treated as available; never offer a count belowmin_gpusor outsidebookable_counts.preflight_inferencevalidates the exact source, model, GPU, queue, price cap, and wallet-hold terms without creating a deployment or mutating funds.create_inferenceuses the same request contract, self-validates the selection against the model-addressed minimums before submitting, and follows BOOK-BEFORE-REVEAL: a non-queued create answers202with a booking handle while the GPU is secured on real capacity (30-40 s typical); the tool polls the handle and returns the deployment payload only after the GPU is confirmed. The API's one-timeinference_keyis deliberately withheld from MCP tool content because that text is model-visible; MCP inference uses the workspace key plus deployment name/id. Create through the console or a direct SDK/API call when an external standalone key is required. A definitive miss returns the standardCAPACITY_UNAVAILABLEstock-miss error with fresh alternatives and NO deployment exists (nothing charged). A selection that can never run at all is refused up front with a permanent code instead (see below). Queueing is opt-in, a maximum total hourly price is explicit, and a GPU alternative is never selected without approval.gpu_prioritiesbackups (1-5 ranked choices) may be supplied on ANY create — they form the after-start replacement ladder; the queue itself stays opt-in.get_inference_bookingpolls a booking handle from a long-runningcreate_inference(pending → revealed payload → or the standard 409).get_inference_statusreports durable queue state, provisioning/loading, endpoint readiness, price cap, notification summary, and wallet authorization. An endpoint is live only when the returned status isrunning.queued_capacitymeans waiting for stock at zero charge — after a deployment has ever been booked, GPU loss never ends in a capacity failure; it waits and auto-resumes.get_inference_notificationsreports durable lifecycle-email delivery, bounded retries, and operator-visible dead letters.update_inference_policy,stop_inference,resume_inference,restart_inference, anddelete_inferencemanage the lifecycle.
Standard GPU rejection contract
Training and inference speak ONE GPU rejection shape, with a code that says
whether waiting can ever help. The MCP transport serializes every code in the
family as compact JSON in the tool-error text, even when zero alternatives
exist, so an agent can act on it directly.
| reason | code | HTTP | queue_offered |
| --- | --- | --- | --- |
| insufficient_stock | CAPACITY_UNAVAILABLE | 409 | true |
| model_too_large | GPU_TYPE_TOO_SMALL | 400 | false |
| below_model_minimum | GPU_COUNT_BELOW_MINIMUM | 400 | false |
| invalid_gpu_count | GPU_COUNT_INVALID | 400 | false |
| gpu_unsupported | GPU_TYPE_UNSUPPORTED | 400 | false |
CAPACITY_UNAVAILABLE is the ONLY transient one: the GPU is not free right
now, so retrying later or joining the queue can succeed (training also echoes
the deprecated SELECTED_GPU_UNAVAILABLE as legacy_code for one release).
The four 400 codes are permanent facts about the request itself, so the queue
is never offered for them and resending the same gpu_type/gpu_count can
never succeed. Branch on retryable_as_submitted (or queue_offered) rather
than on the message text.
{
"code": "GPU_TYPE_TOO_SMALL",
"status": 400,
"reason": "model_too_large",
"message": "This model does not fit on L40_48GB, at any GPU count.",
"recoverable": true,
"retryable_as_submitted": false,
"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.",
"available_gpus": [
{ "gpu_type": "H100_80GB", "gpu_count": 2, "min_gpus": 2, "valid_counts": [2, 4, 8],
"tier": "secure", "price_hour_cents": 598, "available_count": 6 }
],
"minimum_requirement": {
"selected_gpu_min": 2, "selected_valid_counts": [2, 4, 8],
"per_type": [{ "gpu_type": "H100_80GB", "min_gpus": 2, "valid_counts": [2, 4, 8] }]
},
"selected": { "gpu_type": "L40_48GB", "gpu_count": 2, "availability_status": "available" },
"queue_offered": false,
"queue_eligible": false,
"gpu_priorities_entry": 1,
"checked_at": "2026-07-19T00:00:00Z"
}available_alternatives is the deprecated alias of available_gpus, and
gpu_priorities_entry is the 1-based rank of the refused gpu_priorities
entry when the rejection belongs to one. A stock miss carries the same shape
with code: "CAPACITY_UNAVAILABLE", queue_offered: true,
retryable_as_submitted: true, and an instruction that does offer
allow_capacity_queue=true.
A 503 ADMISSION_UNAVAILABLE is a transient market outage, never a capacity
verdict. Retry shortly, and never conclude out-of-stock from it. See
get_platform_guide topic capacity_errors for the recovery playbook.
Models are selectable only from the platform's own catalog: list_models
and search_models read the hosted registry, and create_training_job,
preflight_training_job, create_inference, and preflight_inference
validate the model id against it, answering MODEL_NOT_HOSTED — never a
live Hugging Face lookup. Catalog models are pre-mirrored, so raw access
tokens are intentionally excluded from MCP tool inputs and no integration is
needed for models; Hugging Face integrations remain for dataset imports via
import_huggingface_dataset.
list_inference_models is a different read: it uses /v1/models and the
connection's selected inference credential to list the serverless pool and
workspace deployments it can actually invoke. get_inference_model retrieves
one exact id through the same scope and workspace boundary, including
author/name model ids. A partially readable list names
usf_unreachable_sources; never infer an unreadable source has no models.
The registry's list_models / search_models tools remain the source for
trainable/deployable base models, not the invocable roster. Neither new tool
accepts a credential argument or emits credential fields in its result.
create_training_job and resume_training_job accept an optional
idempotency_key. Reuse the same value after a timeout so the API replays the
original acknowledgement instead of creating another job, wallet hold, queue
entry, or GPU request. The MCP server generates a fresh key when it is omitted.
Training lifecycle (book-before-reveal)
dataset_ids is the ordered optimization set; eval_dataset_ids is a separate
held-out set and cannot be combined with eval_split_ratio. Dataset sampling,
row caps, source order, mixing weights, and the fixed seed must be identical in
preflight and create. They are frozen when the job is created—an active job does
not accept another dataset; launch a new job to change its data contract.
Like inference, create_training_job books a GPU before it reveals a job.
The call blocks while the ranked ladder is booked (~40s typical); a job id and
the "training started" email exist only once a real pod is secured. Read
status on the response and from get_training_status:
booked— a GPU is secured (booked == secured); the job then provisions, downloads, and runs on its own. Money is captured only after this point.securing— still booking at the deadline; the async tail continues. Pollget_training_statusuntilbooked/running(or a terminal state). Nothing is charged while securing.queued— returned only with explicit queue consent; waits for stock at zero charge and books via the same path.- A booking-time stock miss returns the standard
CAPACITY_UNAVAILABLE(409) with freshavailable_gpusand no job exists (nothing charged); a selection that can never run returns one of the permanent400codes above instead, and no queue is offered for it. A transient503 ADMISSION_UNAVAILABLEis a retry, never out-of-stock, and the platform never auto-substitutes a GPU.
If a started job loses its pod, it rests at interrupted — a self-heal state
distinct from failed in which billing is already stopped. Call
resume_training_job to continue from the last checkpoint; resume re-books on
secured capacity before reporting resumed, so the same guarantees apply.
Training and deployment share one authoritative GPU-capacity broker. Server priority is evaluated first. Within an equal-priority class, the broker rotates across users and preserves FIFO order within each user's own requests. This anti-starvation policy prevents one user's large backlog from monopolizing GPU capacity; consequently, a newer request from another user can run before an older request in a busy user's backlog.
For each eligible request, the broker selects the lowest-ranked approved GPU choice that is live, has enough cards, and remains within the accepted total hourly price cap. Thus an older request's third choice can receive capacity before a newer request's first choice. Unknown/stale provider inventory issues no grant. Every vendor mutation requires the broker's short-lived fenced grant, and atomic reservations prevent training and deployment replicas from booking the same cards.
If capacity disappears before the vendor mutation, a queued request releases the grant and returns to its original global position. Expired worker leases are fenced and recovered with a new grant identity. Ambiguous provider results retain the same immutable provisioning request and grant fence until reconciliation proves success or definitive failure, so MCP clients should retry with the same idempotency key rather than submit a replacement request.
What never leaves the platform
An MCP client is a program the customer runs, so every tool result is a customer-facing response. Two facts never appear in one:
- Container build identity — any image name, version, tag, digest or path.
list_supported_architecturesused to ship the engine image reference that published each registry row (image_tag,bios_version,manifest_sha256, and the same tag again inside the free-textnotes), andget_training_capabilitiesshippedimage_compatibility, the engine image VERSION range the contract was written against. Both are removed at the tool boundary (src/redaction.ts), by CONTENT as well as by key — a key-only rule is defeated the moment the same value moves one field over, which is exactly whatnotesdid. Architecture support is unchanged and still authoritative: the deployment and training gates read those same rows, and the response says so. Engine text that reaches an agent as an error (upstream bodies, serving exceptions, training log lines) goes through the same content rule. - Which supplier backs a placement — the control plane already answers the
neutral
bios-cloud/globalplacement labels, and the tools echo those unchanged. Tool text describes what was bought ("the GPU is secured on real capacity"), never who sold it.
Hosted (remote) connector — implemented, not planned
Besides the stdio entry (dist/index.js), this package ships the hosted
Streamable HTTP connector used by clients that only accept remote MCP servers
(dist/http/main.js, ECS service bios-mcp, default port 7152). It is built and
deployed, not a roadmap item:
POST {PUBLIC_BASE_URL}/mcp— stateless Streamable HTTP endpoint.GETandDELETEanswer405; an unauthenticated call answers401with the RFC 9728WWW-Authenticate: Bearer resource_metadata=...challenge.- OAuth 2.1 authorization server with dynamic client registration and PKCE,
advertised at
/.well-known/oauth-protected-resource,/.well-known/oauth-authorization-server, and/.well-known/openid-configuration(each also under/mcp). - Consent is granted in the console (
CONSOLE_BASE_URL); every request re-validates the bearer token and re-checks scopes and workspace membership through the internal api-gateway.tools/listis filtered to that grant and the organization’s active release entitlements, so unavailable capabilities are absent rather than advertised and rejected later. chat_with_inferenceis available when the OAuth grant carriesserverlessfor a pool model ordeployments:read/deployments:writefor a workspace deployment. The gateway resolves the grant, checks the current workspace membership and scopes, and forwards through an internally attested serving route. The deployment's one-time serving key is never retrieved or returned to the model.upload_datasetremains unavailable on hosted MCP because the hosted process cannot read a file from the client's machine; use the console for local uploads.complete_with_inference,embed_with_inference,rerank_with_inferenceandmessage_with_inferenceare withheld from hostedtools/listuntil OAuth-bound routes for those tasks are implemented and tested; stdio may use the configured inference credential now.
Any documentation still describing the hosted connector as "planned" is stale.
Verification
npm test
npm run typecheck
npm run build