@thatopen/services 0.18.0 → 0.19.0

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.
@@ -2,7 +2,15 @@ export interface Base {
2
2
  _id: ObjectId;
3
3
  createdAt: Date;
4
4
  updatedAt?: Date;
5
- creatingUser: ObjectId;
5
+ /**
6
+ * The human user who performed the action (audit trail). This used to be
7
+ * declared as a required `creatingUser`, a field the API has never
8
+ * returned — the server sends `createdBy`, optionally. Matching the wire
9
+ * is what a type is for.
10
+ */
11
+ createdBy?: ObjectId;
12
+ /** The human user who last updated the entity (audit trail). */
13
+ lastUpdatedBy?: ObjectId;
6
14
  creatingToken?: ObjectId;
7
15
  archived?: boolean;
8
16
  }
@@ -1,5 +1,7 @@
1
- export type NotificationCategoryDto = 'automation' | 'invitation';
2
- export type NotificationTypeDto = 'automation.run.started' | 'automation.run.finished' | 'invitation.added' | 'invitation.accepted' | 'project.role_changed';
1
+ export type NotificationCategoryDto = 'automation' | 'invitation' | 'file';
2
+ export type NotificationTypeDto = 'automation.run.started' | 'automation.run.finished' | 'invitation.added' | 'invitation.accepted' | 'project.role_changed' | 'project.transfer.proposed'
3
+ /** A file version landed in a project the recipient belongs to. */
4
+ | 'file.version_added';
3
5
  /**
4
6
  * One notification as the API returns it.
5
7
  *
@@ -51,8 +51,8 @@ Steps 0–2 are identical for both; they diverge only at `create` (step 3) and h
51
51
 
52
52
  - **Node.js ≥ 18** and **npm** — verify with `node -v`.
53
53
  - A **That Open Platform API token**. The user creates it themselves:
54
- → **https://platform.thatopen.com/dashboard → Account → Tokens → create → copy.**
55
- (The Data overview shows the same Tokens card; both paths work.)
54
+ → **https://platform.thatopen.com/dashboard/data → API Tokens → Add → copy.**
55
+ (Account Settings — avatar menu → Account Settings → API Tokens — shows the same card.)
56
56
  Ask the user to paste it. **Never print it back, never write it into a file, never commit it.**
57
57
 
58
58
  ## 1. Install the CLI
@@ -64,7 +64,7 @@ npm install -g @thatopen/services@latest
64
64
  ## 2. Log in (do this BEFORE creating the project)
65
65
 
66
66
  Get the token from the platform dashboard — **https://platform.thatopen.com/dashboard**
67
- → **Account → Tokens** → create → copy (the Data overview shows the same card). Ask the
67
+ → **Data → API Tokens** → Add → copy (Account Settings shows the same card). Ask the
68
68
  user to paste it, then run:
69
69
 
70
70
  ```bash
@@ -81,6 +81,22 @@ first: the next step's install pulls the private `@thatopen-platform/*-beta` pac
81
81
  `create` uses your logged-in token to write an authenticated `.npmrc` so that install can
82
82
  resolve them. No npm account or manual npm token is involved.
83
83
 
84
+ ### The two credentials, side by side (misusing them is the classic first failure)
85
+
86
+ | | **Platform API token** | **User session JWT** |
87
+ |---|---|---|
88
+ | Where it comes from | Dashboard → Data → API Tokens | The platform session (Auth0); inside an app, `PlatformClient.fromPlatformContext()` |
89
+ | Lifetime | Long-lived until revoked | Hours |
90
+ | Client | `EngineServicesClient` | `PlatformClient` (extends the other, so it can do both) |
91
+ | Can | Files, folders, versions, components, executions — anything with an explicit `projectId` | All of that **plus** the JWT-only routes: `listProjects`, `getProject`, `getProjectData`, `checkPermission`, notifications |
92
+ | Cannot | The JWT-only routes above — they identify a *person*, a token identifies a *token* | Run unattended: it expires with the session |
93
+ | How it travels | `Authorization: Bearer` (the CLI does this for you); the `?accessToken=` query form is legacy | `Authorization: Bearer` |
94
+
95
+ The failure mode this table prevents: building a service on an API token and discovering
96
+ that `checkPermission` or `listProjects` answers 401 — not because of a bug, but because
97
+ those questions are only answerable about a person. If a feature needs them unattended,
98
+ that is a platform gap to report, not a header to keep fiddling with.
99
+
84
100
  ## 3. Scaffold a beta project
85
101
 
86
102
  **[app]**
@@ -190,7 +206,11 @@ Copy this skeleton. It is the boot of an app that works.
190
206
  async function main() {
191
207
  const client = PlatformClient.fromPlatformContext();
192
208
 
193
- // 1 ── built-ins registered, UI shell up
209
+ // 1 ── built-ins registered, UI shell up. This is the FULL globals set a
210
+ // built-in may ask for: OBC, OBF, BUI, THREE, FRAGS, MARKERJS (plus
211
+ // CUI only if your app still uses the deprecated @thatopen/ui-obc).
212
+ // Only OBC and BUI are strictly required, but a built-in that needs
213
+ // a missing one fails at init, so pass everything you import.
194
214
  const { components } = await client.setup<OBC.Components>(
195
215
  { OBC, OBF, BUI, THREE, FRAGS, MARKERJS },
196
216
  { uuid: UIManager.uuid },
@@ -278,7 +298,7 @@ system: the platform relays messages between app tabs and external tools.
278
298
  To command the app from outside (a script, an MCP server, your own tool):
279
299
 
280
300
  1. The user creates a **platform token** with the **API** permission
281
- (dashboard → Account → Tokens). Same secrecy rules as always.
301
+ (dashboard → Data → API Tokens). Same secrecy rules as always.
282
302
  2. Connect with `socket.io-client` and subscribe, then publish:
283
303
 
284
304
  ```bash
@@ -288,7 +308,13 @@ npm i socket.io-client # in the external tool's own project, not the app
288
308
  ```js
289
309
  import { io } from "socket.io-client";
290
310
 
291
- const socket = io(`https://platform.thatopen.com?accessToken=${process.env.THATOPEN_TOKEN}`);
311
+ // WebSocket-only matters: the default long-polling handshake spreads several
312
+ // HTTP requests across the platform's load-balanced instances and can die
313
+ // before it upgrades. The token rides the auth payload, not the URL.
314
+ const socket = io("https://platform.thatopen.com", {
315
+ transports: ["websocket"],
316
+ auth: { accessToken: process.env.THATOPEN_TOKEN },
317
+ });
292
318
  // While iterating with `thatopen serve`, OMIT appId (the dev channel is keyed
293
319
  // to the account); pass the appId once the app is published.
294
320
  socket.emit("channelSubscribe", { projectId, appId, kind: "cli" });
@@ -323,6 +349,32 @@ is the reply. The full walkthrough (typed contracts, collaboration events,
323
349
  presence) is `node_modules/@thatopen/services/src/core/examples/channel.ts`,
324
350
  indexed in the client API index you already loaded.
325
351
 
352
+ ## 5c. The desktop MCP server: `thatopen mcp`
353
+
354
+ The raw socket above is for tools you write. For an LLM on the desktop (Claude
355
+ Desktop, Claude Code, anything that speaks MCP), the CLI ships the same bridge
356
+ as a ready-made MCP server over stdio:
357
+
358
+ ```json
359
+ {
360
+ "mcpServers": {
361
+ "thatopen-platform": {
362
+ "command": "npx",
363
+ "args": ["-y", "@thatopen/services", "mcp"]
364
+ }
365
+ }
366
+ }
367
+ ```
368
+
369
+ Credentials come from `thatopen login` (`~/.thatopen/config.json`); to inject
370
+ them instead, set `THATOPEN_TOKEN` and `THATOPEN_API_URL` in the server's
371
+ `env`. Two tools: `platform-status` (can the platform be reached, is the
372
+ subscription alive) and `send-app-command` (publish a command to a running
373
+ app — `ping`, `get-loaded-models`, or whatever the app declares — and return
374
+ its reply). The project is named per call, by id or dashboard URL, so one MCP
375
+ session can drive whichever project the user has open; while iterating with
376
+ `thatopen serve`, `app_id` is omitted, same rule as the raw socket.
377
+
326
378
  ## 6. Publish (when ready)
327
379
 
328
380
  ```bash
@@ -0,0 +1,41 @@
1
+ ---
2
+ description: "Start an MCP server over stdio that lets an LLM on the desktop (Claude Desktop, Claude Code, any MCP client) drive a running That Open app through the platform channel: send it commands and read the replies."
3
+ ---
4
+
5
+ ## thatopen mcp
6
+
7
+ Runs an MCP (Model Context Protocol) server on stdio. Register it in an MCP
8
+ client's config and the LLM can command a running That Open app through the
9
+ platform channel — the same relay the app's `src/setups/channel.ts` listens
10
+ to, as one external channel of kind `mcp`.
11
+
12
+ **Usage (in the MCP client's config, not a terminal):**
13
+
14
+ ```json
15
+ {
16
+ "mcpServers": {
17
+ "thatopen-platform": {
18
+ "command": "npx",
19
+ "args": ["-y", "@thatopen/services", "mcp"]
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ **Credentials:** resolved like every other CLI command — the project's
26
+ `.thatopen`, then `~/.thatopen/config.json` from `thatopen login`. Or inject
27
+ `THATOPEN_TOKEN` and `THATOPEN_API_URL` through the MCP server's `env`.
28
+
29
+ **Tools exposed:**
30
+
31
+ - `platform-status` — which API the server points at, whether credentials were
32
+ found, and whether the channel subscription works. The first call to make
33
+ when another tool fails.
34
+ - `send-app-command` — publish a command (`ping`, `get-loaded-models`, or
35
+ whatever the app declares in `AppCommands`) to a running app and return its
36
+ reply. Takes the project per call (id or dashboard URL); `app_id` is omitted
37
+ while the app runs under `thatopen serve` and passed once it is published.
38
+
39
+ **The app must be open.** `delivered: 0` means no tab of that app has the
40
+ channel joined — the LLM is told to ask the user to open or reload the app
41
+ tab, the same rule as the raw socket in the quickstart's section 5b.
@@ -11,6 +11,10 @@
11
11
  "path": "docs/cli/login.md",
12
12
  "description": "Authenticate with the ThatOpen platform by validating an access token against the API. Saves credentials globally (~/.thatopen/config.json) by default, or locally to the project's .thatopen file with --local. Local config takes priority over global."
13
13
  },
14
+ {
15
+ "path": "docs/cli/mcp.md",
16
+ "description": "Start an MCP server over stdio that lets an LLM on the desktop (Claude Desktop, Claude Code, any MCP client) drive a running That Open app through the platform channel: send it commands and read the replies."
17
+ },
14
18
  {
15
19
  "path": "docs/cli/publish.md",
16
20
  "description": "Build and upload the project to the platform. Automatically detects app vs cloud component from the local .thatopen config. On first publish, saves the assigned item ID back to .thatopen so subsequent runs update the same item instead of creating a new one."
@@ -8,7 +8,7 @@ here rather than in each of them.
8
8
  Say this to the user, and wait:
9
9
 
10
10
  > "To log in I need a That Open Platform **access token**. You create it yourself:
11
- > **https://platform.thatopen.com/dashboard → Account → Tokens → create → copy** (the Data overview shows the same card), then paste it
11
+ > **https://platform.thatopen.com/dashboard/data → API Tokens → Add → copy** (Account Settings shows the same card), then paste it
12
12
  > here."
13
13
 
14
14
  **Never print it back, never write it into a file, never commit it.** Not into a script, not into a
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  "access": "public"
16
16
  },
17
17
  "private": false,
18
- "version": "0.18.0",
18
+ "version": "0.19.0",
19
19
  "main": "dist/index.cjs.js",
20
20
  "module": "dist/index.es.js",
21
21
  "types": "dist/index.d.ts",
@@ -48,6 +48,7 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@changesets/cli": "^2.27.7",
51
+ "@modelcontextprotocol/sdk": "^1.32.1",
51
52
  "@thatopen/components": "~3.4.0",
52
53
  "@thatopen/components-front": "~3.4.0",
53
54
  "@thatopen/fragments": "~3.4.0",
@@ -70,7 +71,8 @@
70
71
  "vite": "^5.2.0",
71
72
  "vite-plugin-dts": "^3.9.1",
72
73
  "vite-plugin-eslint": "^1.8.1",
73
- "vitest": "^4.1.4"
74
+ "vitest": "^4.1.4",
75
+ "zod": "^3.25.76"
74
76
  },
75
77
  "dependencies": {
76
78
  "@types/geojson": "7946.0.16",
@@ -30,7 +30,9 @@ export interface CDENamingSchema {
30
30
  separator: string;
31
31
  segments: CDENamingSegment[];
32
32
  }
33
- export interface CDEFile extends Omit<Item, 'creatingUser'> {
33
+ // `Base` now declares `createdBy` itself (the field the API actually
34
+ // returns); this narrows it to the string the CDE panels render.
35
+ export interface CDEFile extends Omit<Item, 'createdBy'> {
34
36
  createdBy: string;
35
37
  metadata: Record<string, CDEMetadataValue>;
36
38
  versions?: ItemVersion[];
@@ -40,7 +40,7 @@ Apps run inside the That Open Platform within a project, not as standalone websi
40
40
  To develop locally:
41
41
 
42
42
  1. Run `npm run dev` — watches source with esbuild and serves the bundle on port 4000.
43
- 2. Open your project on the platform and click the debug button.
43
+ 2. Open your project on the platform, click the Local App icon, then Get Started.
44
44
  3. Live reload is enabled — save a file to rebuild automatically.
45
45
 
46
46
  **Important**: use `npm run dev` (it runs `thatopen serve` under the hood). Do NOT run