Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/skills-platform-self-hosted-deploy-mcp-connect.md
Original file line number Diff line number Diff line change
@@ -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 <url> --token <key>` 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.
18 changes: 17 additions & 1 deletion content/docs/ai/connect-mcp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
128 changes: 125 additions & 3 deletions content/docs/deployment/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <id>` | 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 <id>` | Bind a compiled local artifact to an existing environment |
| `os environments use-runtime <url> --token <key>` | 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

Expand All @@ -3153,12 +3154,53 @@ os environments bind <environment-id> --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 <id>`, 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 <key>` | `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 `<manifest.version>-dev.<YYYYMMDDHHMMSS>` — 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
Expand Down Expand Up @@ -3196,10 +3238,90 @@ is its production, so a deploy is always a deliberate act.
| `--wait-timeout <seconds>` | — | How long to wait for the environment to serve the new version (default `600`) |
| `-s, --server <url>` | `OS_CLOUD_URL` | Control-plane URL. Default `https://cloud.objectos.ai`, or the URL recorded by `os cloud login` |
| `-t, --token <key>` | `OS_CLOUD_API_KEY` | Bearer token; `$OS_TOKEN` and `~/.objectstack/cloud.json` are the fallbacks |
| `--confirm-access-upgrade <token>` | — | 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 <id>` | `OS_ENVIRONMENT_ID` | Environment to connect to (default: the active one for this control plane, then your organization's only one) |
| `--url <url>` | — | Base URL of a self-hosted or local server; skips ObjectOS Cloud |
| `--name <name>` | — | Server name in the client: letters, digits, `-` and `_` (default: the environment's name; `objectstack` with `--url`) |
| `--client <client>` | — | `claude-code`, the default and only value |
| `--print` | — | Print the endpoint and the command that registers it; run nothing |
| `-s, --server <url>` | `OS_CLOUD_URL` | Control-plane URL. Default `https://cloud.objectos.ai`, or the URL recorded by `os cloud login` |
| `-t, --token <key>` | `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
Expand Down Expand Up @@ -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 <url>` | `OS_RUNTIME_URL` | Base URL of the runtime to install **into** (default `http://localhost:3000`) |
| `-u, --url <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 <semver>` | — | Version to install in catalog mode (default `latest`). Air-gapped mode takes the version from the artifact |
| `-t, --token <token>` | `OS_RUNTIME_TOKEN` | A token the target runtime issued (an API key); instead of `--email` / `--password` |
| `-t, --token <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 <email>` | `OS_RUNTIME_EMAIL` | Account email on the target runtime |
| `--password <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 |
Expand Down
12 changes: 12 additions & 0 deletions content/docs/deployment/publish-and-preview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <url> --token <key>`](/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 <id>` 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).

Expand Down
20 changes: 19 additions & 1 deletion scripts/check-skills-token-ratchet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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],

Expand Down
30 changes: 30 additions & 0 deletions skills/objectstack-platform/evals/config-plugins-ops.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 <key>`, 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 <token>` 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 <id>` 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 <name> <url>` for the environment `os deploy` would pick on ObjectOS Cloud (`--env <id>`, 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"
]
}
}
]
}
Loading
Loading