diff --git a/.changeset/skills-platform-self-hosted-deploy-mcp-connect.md b/.changeset/skills-platform-self-hosted-deploy-mcp-connect.md new file mode 100644 index 00000000000..214ef36f00f --- /dev/null +++ b/.changeset/skills-platform-self-hosted-deploy-mcp-connect.md @@ -0,0 +1,10 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): the platform skill teaches `os environments use-runtime` and `os mcp connect` + +Clause-②: no +Metadata-protocol: none + +`objectstack-platform`'s operations reference gains two rows and two clauses. `os environments use-runtime --token ` makes a self-hosted runtime the deploy target: the key is tried on the runtime first (`--no-verify` skips that), kept in `~/.objectstack/cloud.json` (0600) across cloud login and logout, and `--forget` or `os environments switch` targets the cloud again. The `os deploy` row says how it deploys to that runtime (build, dry-run plan, inline install, live on `hotLoaded`, `--confirm-access-upgrade`), and the `os package install` row says it falls back to that runtime with no `-u`. `os mcp connect` registers an environment's MCP server with Claude Code, `--url` for a self-hosted or local server (named `objectstack`), `--name`, `--print`, and stores no token. Paid by cutting restatement in the same file and two rows that misstated `os login` and `os register`. Two new eval cases cover the commands. The CLI behaviour already shipped; this release only teaches it. diff --git a/content/docs/ai/connect-mcp.mdx b/content/docs/ai/connect-mcp.mdx index c6ecce6a62b..e681ab1beca 100644 --- a/content/docs/ai/connect-mcp.mdx +++ b/content/docs/ai/connect-mcp.mdx @@ -22,7 +22,23 @@ disappears with it, along with both its Setup and Account navigation entries. Se Interactive clients use OAuth — each deployment is its own OAuth 2.1 authorization server, so there are no admin-minted credentials to pass around. -The first tool call opens a browser login and you connect **as yourself**: +The first tool call opens a browser login and you connect **as yourself**. + +With the ObjectStack CLI, [`os mcp connect`](/docs/deployment/cli#os-mcp-connect) +runs the `claude mcp add` line for you. With no flags it registers your +ObjectOS Cloud environment, picked as `os deploy` picks it, under that +environment's name; `--url` takes +a self-hosted or local server's base URL and registers it as `objectstack`. Claude +Code is the only client it runs (`--client claude-code`); `--print` prints the +endpoint and the line and runs nothing, for any other client. It stores no token: +the client runs the server's own sign-in. + +```bash +os mcp connect # your ObjectOS Cloud environment +os mcp connect --url http://localhost:3000 # a self-hosted or local server +``` + +Or type the line yourself: ```bash # local dev server diff --git a/content/docs/deployment/cli.mdx b/content/docs/deployment/cli.mdx index c4c34375c18..305e05d95ce 100644 --- a/content/docs/deployment/cli.mdx +++ b/content/docs/deployment/cli.mdx @@ -3127,6 +3127,7 @@ spells them, so one consumer reads both commands. | `os environments create` | Provision a new environment. With `--activate` (the default) the new environment becomes the active one, recorded in `~/.objectstack/cloud.json` as well when the control plane it talked to is the one `os cloud login` recorded — so [`os package publish --install`](#os-package-publish) can install into it with no `switch` in between | | `os environments switch ` | Set the active environment for later CLI calls. Recorded in `~/.objectstack/cloud.json` as well when the control plane it talked to is the one `os cloud login` recorded, so [`os package publish --install`](#os-package-publish) can use it | | `os environments bind ` | Bind a compiled local artifact to an existing environment | +| `os environments use-runtime --token ` | Make a self-hosted runtime the deploy target instead of an ObjectOS Cloud environment — see [Deploy to a self-hosted runtime](#deploy-to-a-self-hosted-runtime) | #### Create an environment from a local artifact @@ -3153,12 +3154,53 @@ os environments bind --artifact ./dist/objectstack.json --build is reserved for the server-side reseed endpoint; use it only when that endpoint is available in your deployment. +#### Deploy to a self-hosted runtime + +`os environments use-runtime` remembers a runtime you operate — ObjectStack's +Docker image, Community or Enterprise — as the deploy target, so +[`os deploy`](#to-a-self-hosted-runtime), the `d` key in [`os dev`](#os-dev) and +[`os package install`](#os-package-install) stop asking for its URL and key. + +```bash +os environments use-runtime http://localhost:8080 --token osk_… +OS_RUNTIME_TOKEN=osk_… os environments use-runtime https://objectos.example.com +os environments use-runtime --forget # deploy to ObjectOS Cloud again +``` + +The key is an API key the runtime issued (its **Setup → API Keys**); `-t, --token` +reads `OS_RUNTIME_TOKEN`. The URL is the runtime's http(s) base URL, with no query +or fragment. Before anything is written, the key is tried on the runtime's +install-local endpoint (`GET /api/v1/marketplace/install-local`, which answers +only an authenticated caller): a key the runtime rejects (`401` / `403`), a +runtime that does not serve the endpoint (`404`), or one that cannot be reached +is refused, naming why, and nothing is recorded. `--no-verify` records it anyway, +for a container that is not up yet. + +The URL and key are stored in `~/.objectstack/cloud.json` (mode `0600`), the file +`os cloud login` writes, as their own record: no cloud request reads the key, and +it is sent only to that runtime's URL. `os cloud logout` keeps the record, and so +does a new `os cloud login`. + +Whichever of `use-runtime` and `os environments switch` ran last is the target +`os deploy` uses. `os environments switch `, when it records the environment +in `cloud.json` (see the table above), makes ObjectOS Cloud the target again and +keeps the remembered runtime, so `os package install` still falls back to it. +`--forget` drops the record. + +| Flag | Env equivalent | Purpose | +|---|---|---| +| `url` (positional) | — | Base URL of the self-hosted runtime (e.g. `http://localhost:8080`) | +| `-t, --token ` | `OS_RUNTIME_TOKEN` | An API key the runtime issued; sent only to this runtime | +| `--no-verify` | — | Record the runtime without trying the key on it first | +| `--forget` | — | Forget the remembered runtime; ObjectOS Cloud is the deploy target again. Takes no `url` | + ### Deploy #### `os deploy` Puts the app in the current directory on your ObjectOS Cloud environment, with -no flags. It builds the app (the compile [`os build`](#os-compile) runs), +no flags (or on a [self-hosted runtime](#to-a-self-hosted-runtime) you made the +target). It builds the app (the compile [`os build`](#os-compile) runs), publishes it as `-dev.` — a UTC semver pre-release, so deploying again never needs a version bump — installs that version into your organization's environment, and waits until the environment @@ -3196,10 +3238,90 @@ is its production, so a deploy is always a deliberate act. | `--wait-timeout ` | — | How long to wait for the environment to serve the new version (default `600`) | | `-s, --server ` | `OS_CLOUD_URL` | Control-plane URL. Default `https://cloud.objectos.ai`, or the URL recorded by `os cloud login` | | `-t, --token ` | `OS_CLOUD_API_KEY` | Bearer token; `$OS_TOKEN` and `~/.objectstack/cloud.json` are the fallbacks | +| `--confirm-access-upgrade ` | — | Self-hosted target only: confirm the upgrade's access findings with the finding-set token the runtime's refusal printed (as [`os package install`](#os-package-install)). Refused when the target is ObjectOS Cloud | `os package publish` stays the command for a **release**: it publishes the version the manifest declares, with `--install` optional. +##### To a self-hosted runtime + +When [`os environments use-runtime`](#deploy-to-a-self-hosted-runtime) made a +self-hosted runtime the active target, `os deploy` with no flags deploys there +instead; `--env` still names an ObjectOS Cloud environment and deploys to it. +There is no catalog and no version to mint: the build is sent inline to the +runtime's install-local endpoint by [`os package install`](#os-package-install), +run in the same process with the remembered URL and key, so its refusals and +answers are the ones printed. + +1. **Build** — the same compile as above. A failed build prints its errors, and + nothing is installed. +2. **Dry run** — the runtime's upgrade plan of the installed version against this + build is printed; nothing is installed. A runtime that refuses it, or does not + serve install-local, stops the deploy with exit `1`. A runtime that predates the + dry run ignores it and installs: the command says so and exits `1`. +3. **Install** — with `--confirm-access-upgrade` passed through. The endpoint's + refusals (an access upgrade to confirm, a package the running kernel cannot + register, …) stop the deploy with exit `1`. +4. **Live** — install-local registers the package in the running kernel before it + answers, so the answer is the verdict: `hotLoaded` prints `Live: … now serves …` + and exits `0`. + +The `d` key in [`os dev`](#os-dev) runs the same `os deploy`, so it deploys to the +runtime too and asks for no cloud login. + +### MCP + +#### `os mcp connect` + +Registers your environment's MCP server with your MCP client in one command. The +client then runs the environment's own OAuth: on first use it follows the +server's OAuth discovery, registers itself, and your browser shows the +environment's sign-in and consent. + +```bash +os mcp connect +os mcp connect --print # print the endpoint and the `claude mcp add` line, run nothing +os mcp connect --env env_abc123 # your account reaches several environments +os mcp connect --url http://localhost:3000 # a self-hosted or local server +``` + +The ObjectOS Cloud environment is resolved exactly as [`os deploy`](#os-deploy) +resolves it: `--env`, then the active environment for this control plane, then +your organization's only environment; none or several is refused, naming +`--env`. A self-hosted runtime `os environments use-runtime` remembered is not +consulted; name it with `--url`. Its MCP URL is the environment's address, read +from the control plane, plus `/api/v1/mcp`, and the server is registered under +the environment's own name (its slug, else its display name, else its id, +lower-cased with every run of other characters turned into one `-`), so several +environments can be registered side by side. `--url` skips ObjectOS Cloud: it +takes the base URL of a self-hosted or local server (the command adds +`/api/v1/mcp` itself and refuses a URL that already ends in it) and registers it +as `objectstack`, the name `os dev`'s banner gives it. `--name` overrides either +name. + +For Claude Code — the only `--client` value, and the default — the command runs +`claude mcp add --transport http NAME URL`. `--print` prints the endpoint and that +line and runs nothing, which is also how to connect any other client: add the +endpoint as a remote HTTP MCP server in its settings. Then, in Claude Code, run +`/mcp` and authenticate the server. If `claude` is not on your `PATH`, or +`claude mcp add` fails (for a name already registered, `claude mcp remove NAME` or +pick another `--name`), the command exits non-zero. + +**No token is stored.** The command hands the client the URL alone and writes no +token into a client config. `-t, --token` (or the `os cloud login` session) is +used only to find the environment and is never handed to the client. To revoke +the grant, use the environment's console: **Account → Connect an AI Assistant**. + +| Flag | Env equivalent | Purpose | +|---|---|---| +| `--env ` | `OS_ENVIRONMENT_ID` | Environment to connect to (default: the active one for this control plane, then your organization's only one) | +| `--url ` | — | Base URL of a self-hosted or local server; skips ObjectOS Cloud | +| `--name ` | — | Server name in the client: letters, digits, `-` and `_` (default: the environment's name; `objectstack` with `--url`) | +| `--client ` | — | `claude-code`, the default and only value | +| `--print` | — | Print the endpoint and the command that registers it; run nothing | +| `-s, --server ` | `OS_CLOUD_URL` | Control-plane URL. Default `https://cloud.objectos.ai`, or the URL recorded by `os cloud login` | +| `-t, --token ` | `OS_CLOUD_API_KEY` | Bearer token, used only to find the environment; `$OS_TOKEN` and `~/.objectstack/cloud.json` are the fallbacks | + ### Packages The two commands that move a compiled app onto a platform. They target @@ -3386,9 +3508,9 @@ write `-u URL` instead. | Flag | Env equivalent | Purpose | |---|---|---| | `package` (positional, required) | — | Package manifest id (`com.acme.crm`) **or** a path to a compiled artifact JSON | -| `-u, --url ` | `OS_RUNTIME_URL` | Base URL of the runtime to install **into** (default `http://localhost:3000`) | +| `-u, --url ` | `OS_RUNTIME_URL` | Base URL of the runtime to install **into** (default: the runtime [`os environments use-runtime`](#deploy-to-a-self-hosted-runtime) remembered, else `http://localhost:3000`) | | `-v, --version ` | — | Version to install in catalog mode (default `latest`). Air-gapped mode takes the version from the artifact | -| `-t, --token ` | `OS_RUNTIME_TOKEN` | A token the target runtime issued (an API key); instead of `--email` / `--password` | +| `-t, --token ` | `OS_RUNTIME_TOKEN` | A token the target runtime issued (an API key); instead of `--email` / `--password`. Default: the key `os environments use-runtime` remembered, when the URL is that runtime's and no `--email` / `--password` is given | | `--email ` | `OS_RUNTIME_EMAIL` | Account email on the target runtime | | `--password ` | `OS_RUNTIME_PASSWORD` | Account password on the target runtime | | `--confirm-global-uniques` | — | Affirm this app's installation-wide `unique` constraints are genuinely platform-wide — see below | diff --git a/content/docs/deployment/publish-and-preview.mdx b/content/docs/deployment/publish-and-preview.mdx index 68c41eea643..c0d5950b6ec 100644 --- a/content/docs/deployment/publish-and-preview.mdx +++ b/content/docs/deployment/publish-and-preview.mdx @@ -209,6 +209,18 @@ login. Uninstall (`DELETE /api/v1/marketplace/install-local/:manifestId`) remove the cached manifest and withdraws the app from the running kernel; its answer says whether any part stays loaded until the next restart. +**Deploying your working copy to a runtime you operate, again and again?** +Remember it once with +[`os environments use-runtime --token `](/docs/deployment/cli#deploy-to-a-self-hosted-runtime) +(an API key that runtime issued). [`os deploy`](/docs/deployment/cli#to-a-self-hosted-runtime) +— and the `d` key in `os dev`, which runs it — then deploys there with no URL or +key to type: it builds, prints the runtime's upgrade plan from a dry run, +installs the build inline, and reports the app live once the running kernel has +loaded it (`hotLoaded`). `os package install` uses the same runtime when `-u` / +`-t` are not given. `os environments use-runtime --forget`, or an +`os environments switch ` recorded for your `os cloud login` control plane, +makes ObjectOS Cloud the target again. + For the full command reference — every flag, every subcommand — see the [CLI reference](/docs/deployment/cli). diff --git a/scripts/check-skills-token-ratchet.mjs b/scripts/check-skills-token-ratchet.mjs index 0e7c4c1337c..90ac7ce9bee 100644 --- a/scripts/check-skills-token-ratchet.mjs +++ b/scripts/check-skills-token-ratchet.mjs @@ -1062,7 +1062,25 @@ export const CEILINGS = new Map([ // the image pitfall row); the remainder, +179 measured, raises the row, // pinned at the measurement. Maintainer ruling 2026-10-27 on the decision card 「skills 修复卡的教学和 eval 都撞 token 天 // 花板,怎么付?」, tap A, verbatim and untranslated: 「A:先削重复,余额按实测提高」. - ['skills/objectstack-platform/references/operations.md', 3287], + // 3287 -> 3340: `os environments use-runtime` (a self-hosted runtime as the + // deploy target: key tried first, `--no-verify`, kept in cloud.json across + // cloud login/logout, `--forget` / `os environments switch` back to the + // cloud), the self-hosted `os deploy` sequence and `--confirm-access-upgrade`, + // `os package install`'s fallback with no `-u`, and `os mcp connect`. Paid + // first by deleting restatement in the same file (-594 bytes): the `os dev` + // row's "idempotent, never overwrites" clause (home: the same row, whose + // "on an empty DB only" now reads "with no login yet", as `os dev` seeds); Verify your work's bare-`field` sentence (home: its + // intro) and its `os lint` parenthesis (homes: the `os lint` row and the + // rule of thumb); the Production port row's list of what a shifted port + // breaks and "Don't rely on the 3000 default" (home: Production guidance); + // Testing's "a full bootstrap is cheap" (home: its intro); and two rows that + // misstated `os login` / `os register` (home: content/docs/deployment/cli.mdx + // § Authentication). 13140 -> 13360 bytes (+220 net: -594 cut, +814 added) + // -> ceil(13360 / 4) = 3340; headroom was 2 tokens, so the remainder, +53 + // measured, raises the row, pinned at the measurement. Same maintainer + // ruling, verbatim and untranslated: 「A:先削重复,余额按实测提高」 (ruling + // record 6513306068 on #27847). + ['skills/objectstack-platform/references/operations.md', 3340], ['skills/objectstack-platform/references/plugin-development.md', 3137], ['skills/objectstack-upgrade/references/examples-upgrade.md', 1197], diff --git a/skills/objectstack-platform/evals/config-plugins-ops.json b/skills/objectstack-platform/evals/config-plugins-ops.json index 2e631f8bbfd..a8867114b2c 100644 --- a/skills/objectstack-platform/evals/config-plugins-ops.json +++ b/skills/objectstack-platform/evals/config-plugins-ops.json @@ -347,6 +347,36 @@ "requires: ['storage', 'settings']" ] } + }, + { + "id": 21, + "prompt": "We run ObjectStack's Docker image on our own server at https://objectos.example.com. How do I deploy my app there from the project without typing its URL and key every time?", + "expected_output": "Remember the runtime once: `os environments use-runtime https://objectos.example.com --token `, with an API key that runtime issued. The key is tried on the runtime before anything is recorded (`--no-verify` skips that), then kept in `~/.objectstack/cloud.json` (0600), across cloud login and logout. From then on `os deploy` (or `d` in `os dev`) deploys there: it builds, prints the runtime's dry-run upgrade plan, installs the build inline, and reports it live when the runtime answers `hotLoaded`; `--confirm-access-upgrade ` confirms the access findings a refusal printed. `os package install` falls back to that runtime when `-u` is not given. `os environments use-runtime --forget` or `os environments switch ` makes ObjectOS Cloud the target again.", + "files": [], + "assertions": { + "must_contain": [ + "os environments use-runtime", + "--token" + ], + "must_not_contain": [ + "os deploy --url" + ] + } + }, + { + "id": 22, + "prompt": "Connect Claude Code to our ObjectOS Cloud environment's MCP server, and to my local `os dev` server too.", + "expected_output": "Run `os mcp connect`: it runs `claude mcp add --transport http ` for the environment `os deploy` would pick on ObjectOS Cloud (`--env `, else the active one, else your organization's only one), named after that environment. For the local server, `os mcp connect --url http://localhost:3000` registers it as `objectstack`. `--name` overrides the name; `--print` prints the endpoint and the line and runs nothing, which is how to connect another client. No token is stored: in Claude Code run `/mcp` and authenticate, and the server's own sign-in and consent open in the browser.", + "files": [], + "assertions": { + "must_contain": [ + "os mcp connect", + "--url" + ], + "must_not_contain": [ + "--client cursor" + ] + } } ] } diff --git a/skills/objectstack-platform/references/operations.md b/skills/objectstack-platform/references/operations.md index ad321ecaa60..074cb0e53b3 100644 --- a/skills/objectstack-platform/references/operations.md +++ b/skills/objectstack-platform/references/operations.md @@ -5,7 +5,7 @@ | Command | What it does | |:--------|:-------------| | `os init` | Scaffold a new project (alternative to `npx create-objectstack`) | -| `os dev` | Start the dev server with hot metadata reload. `--seed-admin` (default **on** for plain `os dev`) seeds a loginable dev admin **in-process via the runtime** (env vars `OS_SEED_ADMIN*`) on an **empty** DB only — idempotent, never overwrites an existing account (default `admin@objectos.ai` / `admin123`; override with `--admin-email` / `--admin-password`; disable with `--no-seed-admin`). `--fresh` = ephemeral clean OS_HOME/DB, implies `--seed-admin`. | +| `os dev` | Start the dev server with hot metadata reload. `--seed-admin` (default **on** for plain `os dev`) seeds a loginable dev admin **in-process via the runtime** (env vars `OS_SEED_ADMIN*`) with **no login** yet (default `admin@objectos.ai` / `admin123`; override with `--admin-email` / `--admin-password`; disable with `--no-seed-admin`). `--fresh` = ephemeral clean OS_HOME/DB, implies `--seed-admin`. | | `os dev --ui` | Also mount the bundled Console portal at `/_console/` (there is no separate `os studio` command) | | `os validate` | Validate `objectstack.config.ts` — see [Verify your work](#verify-your-work) | | `os lint` | Style/convention lint on metadata files | @@ -40,15 +40,14 @@ os build # the same three gates, plus emits dist/objectstack.json 2. **CEL / predicate validation (ADR-0032)** — every `visible` / `disabled` / `requiredWhen` / validation rule / flow condition / sharing rule is parsed for CEL syntax *and* checked that each `record.` reference exists on - the target object. A bare `field` (missing `record.`) fails here. + the target object. 3. **Widget-binding integrity (ADR-0021)** — every dashboard widget's `dataset` / `dimensions` / `values` resolves to a declared dataset/field. Both exit non-zero with a located, corrective message. **Rule of thumb: never report a metadata change as done until `os validate` -passes.** (`os lint` is a *separate* style/convention pass and does not -replace `os validate`.) +passes.** ## Ports & networking @@ -63,12 +62,11 @@ Port resolution is the same for `os dev` and `os start` (both spawn `os serve`): | Mode | If the resolved port is busy | |:-----|:-----------------------------| | **Dev** (`os dev`, or `NODE_ENV=development`) | Auto-hops to the next free port (up to +100) so several example apps run side-by-side. The startup banner shows the *actual* bound port. | -| **Production** (`os start`) | **Fails loudly and exits 1.** It never silently drifts — a shifted port breaks reverse-proxy upstreams, better-auth callback URLs, and CORS trusted-origins as opaque 403/502s. | +| **Production** (`os start`) | **Fails loudly and exits 1.** It never silently drifts. | **Production guidance:** -- **Pin the port explicitly** — `PORT=8080 os start` (or `--port 8080`). Don't - rely on the `3000` default; it collides easily on shared hosts. +- **Pin the port explicitly** — `PORT=8080 os start` (or `--port 8080`). - **Keep these in sync when you change the port** (mismatch ⇒ better-auth `Invalid origin` 403 / CORS failures): - reverse-proxy upstream (`nginx`/`caddy`) @@ -91,13 +89,13 @@ Port resolution is the same for `os dev` and `os start` (both spawn `os serve`): | Command | What it does | |:--------|:-------------| -| `os login` / `logout` / `whoami` | Auth against the ObjectStack cloud control plane | | `os environments list` / `create` / `switch` | Manage cloud environments (prod/staging/dev) | -| `os register` | Register the local stack as a deployable target | +| `os environments use-runtime --token ` | Deploy to a self-hosted runtime instead: the key (one it issued) is tried first (`--no-verify` skips), then kept in `~/.objectstack/cloud.json` (0600) across cloud login/logout. `--forget` or `os environments switch` targets the cloud again | | `os cloud login` / `logout` / `whoami` | Cloud auth subcommands (these three only — there are no `os cloud logs/metrics/status`) | -| `os deploy` (or `npm run deploy`, or `d` in `os dev`) | See an edit on your org's ObjectOS Cloud environment: build, publish `-dev.`, install, wait until served. Environment: `--env `, else the one `os environments switch` recorded (kept across `os cloud login`), else your organization's only one. `--no-wait`; `--wait-timeout ` (600) | +| `os deploy` (or `npm run deploy`, or `d` in `os dev`) | See an edit on your org's ObjectOS Cloud environment: build, publish `-dev.`, install, wait until served. Environment: `--env `, else the one `os environments switch` recorded (kept across `os cloud login`), else your organization's only one. `--no-wait`; `--wait-timeout ` (600). A `use-runtime` target, no `--env`: build, dry-run plan, install, live on `hotLoaded`; `--confirm-access-upgrade ` | | `os package publish [dist/objectstack.json] [--env … --install --visibility org]` | Upload the compiled artifact as a versioned package to the cloud catalog (ADR-0008 P3). Listing file, README, review and every screen the customer's administrator meets: [Marketplace handbook](https://objectstack.ai/docs/deployment/marketplace-handbook) | -| `os package install [--version │ -u/--url http://localhost:3000]` | Install a package into a **running** runtime. Catalog mode (by manifest id) or air-gapped local-artifact mode. Auths as a **target runtime** user (`-t/--token` or `--email/--password`; env `OS_RUNTIME_TOKEN` or `OS_RUNTIME_EMAIL`/`OS_RUNTIME_PASSWORD`), not the cloud login | +| `os package install [--version │ -u/--url]` | Install a package into a **running** runtime. Catalog mode (by manifest id) or air-gapped local-artifact mode. Auths as a **target runtime** user (`-t/--token` or `--email/--password`; env `OS_RUNTIME_TOKEN` or `OS_RUNTIME_EMAIL`/`OS_RUNTIME_PASSWORD`), not the cloud login. No `-u`: the `use-runtime` runtime and, with no `-t`/`--email`, its key; else `http://localhost:3000` | +| `os mcp connect` | Register the cloud environment's MCP server (picked as `os deploy` picks it) with Claude Code (`claude mcp add`); `--url ` for a self-hosted or local server (named `objectstack`); `--name`; `--print` runs nothing (other clients). Stores no token: the client runs the server's own OAuth | **`os deploy` exit 1:** a refusal before any write publishes nothing: log in (`os cloud login`), pick an environment (`--env`; none: create one in the ObjectOS Cloud console), or fix the local build or `manifest.version`. `could not build …: `: the environment's build failed, it keeps serving the previous one; fix and deploy again. `Timed out after …`: the install is recorded and a failing build reads as pending for minutes; deploy again with a longer `--wait-timeout`, or see the build in the ObjectOS Cloud console. @@ -142,8 +140,7 @@ CLI would auto-register (`use()` is synchronous and chainable): config's default export). The vitest shape: [Testing Plugins](./plugin-development.md#testing-plugins); the other imports: [Using Plugins](./plugin-development.md#using-plugins). -- **Reset between tests:** create a fresh `LiteKernel` per test — with the - in-memory driver a full bootstrap is cheap, and there is no `reset()`. +- **Reset between tests:** create a fresh `LiteKernel` per test, and there is no `reset()`. - **HTTP-level tests:** mount `createHonoApp({ kernel })` from `@objectstack/hono` and drive it with `app.request(...)` / `fetch`.