mc8yp 2.1.0 → 2.2.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  ![License](https://img.shields.io/npm/l/mc8yp)
5
5
  ![Node Version](https://img.shields.io/node/v/mc8yp)
6
6
 
7
- A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that gives AI agents full access to the Cumulocity IoT platform through code execution. Instead of exposing dozens of fixed tools, the server provides two code-mode tools — `query` and `execute` — that let the agent write JavaScript to inspect the core OpenAPI spec and call any API endpoint.
7
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that gives AI agents full access to the Cumulocity IoT platform through code execution. Instead of exposing dozens of fixed tools, the server provides two code-mode tools — `query` and `execute` — that let the agent write JavaScript to inspect the bundled OpenAPI specs and call any API endpoint.
8
8
 
9
9
  **Two Deployment Modes:**
10
10
 
@@ -19,7 +19,7 @@ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that gi
19
19
  # Run directly (recommended)
20
20
  pnpm dlx mc8yp
21
21
 
22
- # Pick a specific core OpenAPI snapshot for query
22
+ # Pick a specific bundled OpenAPI build for query
23
23
  pnpm dlx mc8yp --spec 2025
24
24
 
25
25
  # Or install globally
@@ -28,7 +28,7 @@ mc8yp
28
28
  ```
29
29
 
30
30
  **Credential Storage:**
31
- Credentials are stored using your operating system's secure credential manager:
31
+ Credentials are stored using your operating system's secure credential manager. The interactive `mc8yp creds add` flow uses hidden password input so the secret is not echoed back in the terminal while you type it.
32
32
 
33
33
  - **macOS**: Keychain
34
34
  - **Windows**: Credential Vault
@@ -50,7 +50,7 @@ No additional credential configuration needed — the microservice uses Cumuloci
50
50
  ### Managing Credentials (CLI)
51
51
 
52
52
  ```sh
53
- # Add credentials (prompts for tenant URL, username, password)
53
+ # Add credentials (prompts for tenant URL, username, and a masked password)
54
54
  pnpm dlx mc8yp creds add
55
55
 
56
56
  # List stored credentials
@@ -60,24 +60,31 @@ pnpm dlx mc8yp creds list
60
60
  pnpm dlx mc8yp creds remove
61
61
  ```
62
62
 
63
- ### Selecting The Core OpenAPI Snapshot (CLI)
63
+ ### Selecting The Bundled OpenAPI Build (CLI)
64
64
 
65
- Use `--spec` or `-s` to choose which bundled core OpenAPI snapshot the `query` tool exposes.
65
+ Use `--spec` or `-s` to choose which bundled **core** OpenAPI snapshot the `query` tool exposes.
66
66
 
67
67
  Supported values are `release`, `2026`, `2025`, and `2024`.
68
68
 
69
+ This flag selects the bundled **core** API version only. The bundled **dtm** OpenAPI snapshot is currently fixed and is included alongside every supported core build.
70
+
71
+ Each bundled CLI build currently contains:
72
+
73
+ - the selected bundled **core** OpenAPI snapshot
74
+ - the bundled **dtm** OpenAPI snapshot
75
+
69
76
  ```sh
70
- # Default: latest bundled release snapshot
77
+ # Default: latest bundled release build
71
78
  mc8yp
72
79
 
73
- # Explicitly use the 2025 core OpenAPI snapshot
80
+ # Explicitly use the 2025 bundled build
74
81
  mc8yp --spec 2025
75
82
 
76
83
  # Short form
77
84
  mc8yp -s 2024
78
85
  ```
79
86
 
80
- This only affects the `query` tool's OpenAPI view. The `execute` tool still calls the live Cumulocity API of the selected tenant or deployed service environment.
87
+ This only affects the bundled OpenAPI data that `query` sees. The `execute` tool still calls the live Cumulocity API of the selected tenant or deployed service environment.
81
88
 
82
89
  ### Connecting to AI Agents
83
90
 
@@ -115,7 +122,7 @@ With restrictions (see [API Restrictions](#api-restrictions)):
115
122
 
116
123
  | Tool | Description |
117
124
  | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
118
- | `query` | Search and inspect the bundled Cumulocity core OpenAPI spec by running a JavaScript module. The spec is injected as a top-level `spec` binding. Export the result with `export default`. |
125
+ | `query` | Search and inspect the bundled OpenAPI specs by running a JavaScript function expression. The sandbox exposes `coreSpec`, `dtmSpec`, and `specsEnabled`, and it never hides bundled specs from the query surface. |
119
126
  | `execute` | Execute JavaScript against the live Cumulocity API. Provide an async JavaScript function expression. A top-level `cumulocity` binding provides `cumulocity.request({ method, path, body?, headers? })`. Return the final value from that function. |
120
127
  | `list-credentials` | _(CLI mode only)_ List stored credentials from your system keyring. |
121
128
 
@@ -237,6 +244,7 @@ Supported wildcards:
237
244
  - `/inventory/**` already matches `/inventory` itself, so you do **not** need both `/inventory` and `/inventory/**`
238
245
  - `/i**` is **not valid** because `**` must be its own segment. Use `/i*/**` if you want to match a first segment starting with `i` and everything below it
239
246
  - `*:/inventory/**` is allowed and means the same thing as `/inventory/**`
247
+ - Root paths across the bundled core and DTM specs are intentionally treated as disjoint, so path-based restriction and allow rules are enough for request enforcement
240
248
  - Rule patterns may not contain empty segments (`//`), `.` or `..` segments, query strings, or fragments
241
249
 
242
250
  ### CLI Mode
@@ -252,9 +260,6 @@ mc8yp -r "/inventory/**"
252
260
  # Block deletes on inventory and all alarm access
253
261
  mc8yp -r "DELETE:/inventory/**" -r "/alarm/**"
254
262
 
255
- # Block everything under user management
256
- mc8yp --restriction "/user/**"
257
-
258
263
  # Same thing using the long alias
259
264
  mc8yp --restrict "/user/**"
260
265
  ```
@@ -272,31 +277,61 @@ mc8yp --allow "GET:/inventory/**" --allowed "POST:/alarm/**"
272
277
  mc8yp -a "/inventory/**" -r "/inventory/managedObjects"
273
278
  ```
274
279
 
280
+ To forbid one or more bundled OpenAPI parts for execute policy, repeat `--disable-openapi` (or `-d`) in CLI mode. `query` still sees all bundled specs and can inspect `specsEnabled` to understand which spec families remain enabled for execute policy:
281
+
282
+ ```sh
283
+ # Disable bundled DTM APIs for execute policy on this CLI connection
284
+ mc8yp -d dtm
285
+
286
+ # Disable multiple bundled specs if more are added in the future
287
+ mc8yp -d dtm -d core
288
+ ```
289
+
275
290
  ### Microservice Mode (HTTP)
276
291
 
277
292
  Pass restrictions as `restriction`, `restrict`, or `r` query parameters on the MCP endpoint URL.
278
293
  Pass allow rules as `allowed`, `allow`, or `a` query parameters.
294
+ To forbid bundled OpenAPI parts for execute policy, pass the `openapi-disabled` query parameter.
295
+ You can also send project-scoped HTTP headers to avoid conflicts with well-known headers:
296
+
297
+ - `mc8yp-restriction` for deny rules
298
+ - `mc8yp-allow` for allow-list rules
299
+ - `mc8yp-openapi-disabled` for bundled OpenAPI part disablement
300
+
301
+ Both headers accept either repeated header instances or a comma-separated list of values. Query parameters and headers can be combined on the same connection.
279
302
 
280
303
  ```
281
304
  /mcp?restriction=/inventory/**&restrict=DELETE:/alarm/**
282
305
  /mcp?r=/inventory/**&r=DELETE:/alarm/**
283
306
  /mcp?allow=/inventory/**&allowed=POST:/alarm/**
284
- /mcp?a=/inventory/**&r=/inventory/managedObjects
307
+ /mcp?openapi-disabled=dtm
308
+ /mcp?openapi-disabled=dtm&openapi-disabled=core
309
+ ```
310
+
311
+ ```http
312
+ POST /mcp HTTP/1.1
313
+ Authorization: Bearer <token>
314
+ mc8yp-restriction: /inventory/**
315
+ mc8yp-restriction: DELETE:/alarm/**
316
+ mc8yp-allow: GET:/measurement/**
317
+ mc8yp-openapi-disabled: dtm
285
318
  ```
286
319
 
287
320
  ### How Access Policy Works
288
321
 
289
- 1. **Query visibility**: The `query` tool exposes the raw bundled OpenAPI snapshot for the current MCP connection. It does not annotate or filter operations based on restrictions or allow rules.
322
+ 1. **Query visibility**: The `query` tool exposes the raw bundled OpenAPI specs for the current MCP connection. Bundled specs are not hidden or rewritten, and the query sandbox exposes `specsEnabled` so the model can see which spec families are enabled for execute policy.
290
323
 
291
324
  2. **Sandbox request enforcement**: The `execute` tool checks restrictions and allow rules inside the generated sandbox request helper, where the actual HTTP method and normalized path are both available. Matching deny rules block first. If any allow rules are configured, requests must also match at least one allow rule.
292
325
 
293
- 3. **Network boundary**: The secure-exec permission layer independently restricts network access to the configured tenant host. Other network operations are denied.
326
+ 3. **Bundled spec disablement**: When the connection disables bundled OpenAPI parts such as `dtm`, `query` still shows all bundled specs, while the server expands that selection into additional restrictions so `execute` stays path-and-method based.
327
+
328
+ 4. **Network boundary**: The secure-exec permission layer independently restricts network access to the configured tenant host. Other network operations are denied.
294
329
 
295
330
  When an `execute` request is blocked by MCP connection policy, the tool returns explanatory text stating whether the operation was denied by a restriction or blocked because it is outside the configured allow list, no request was sent to Cumulocity, and retrying through the same connection will not help.
296
331
 
297
332
  ## Build And Packaging
298
333
 
299
- The repository bundles multiple core OpenAPI snapshots for CLI use and builds one microservice server bundle per snapshot version.
334
+ The repository bundles multiple OpenAPI specs for CLI use and builds one microservice server bundle per configured build version.
300
335
 
301
336
  ### Build Outputs
302
337
 
@@ -305,7 +340,7 @@ The repository bundles multiple core OpenAPI snapshots for CLI use and builds on
305
340
  - CLI bundle in `dist/`
306
341
  - Versioned server bundles in `.output/release/`, `.output/2026/`, `.output/2025/`, and `.output/2024/`
307
342
 
308
- The versions built are driven by [`openapi-versions.json`](openapi-versions.json). Each server bundle contains only its own `core-openapi/<version>.json` snapshot.
343
+ The build matrix is driven by [`openapi-builds.json`](openapi-builds.json). Core snapshots live under `openapi/core/`, DTM snapshots live under `openapi/dtm/`, and each server bundle contains the configured combination for that build.
309
344
 
310
345
  ### Release Packaging
311
346
 
@@ -321,10 +356,10 @@ pnpm package:microservices
321
356
 
322
357
  That command creates one zip per bundled server variant in the repository root, for example:
323
358
 
324
- - `mc8yp-release-v1.2.3.zip`
325
- - `mc8yp-2026-v1.2.3.zip`
326
- - `mc8yp-2025-v1.2.3.zip`
327
- - `mc8yp-2024-v1.2.3.zip`
359
+ - `mc8yp-core-release-dtm-v1.2.3.zip`
360
+ - `mc8yp-core-2026-dtm-v1.2.3.zip`
361
+ - `mc8yp-core-2025-dtm-v1.2.3.zip`
362
+ - `mc8yp-core-2024-dtm-v1.2.3.zip`
328
363
 
329
364
  The GitHub release workflow uses that packaging command when building tagged releases.
330
365
 
@@ -3,6 +3,7 @@ import { defineCommand } from "citty";
3
3
  import consola from "consola";
4
4
  import * as v from "valibot";
5
5
  import { exit } from "node:process";
6
+ import { cancel, isCancel, password } from "@clack/prompts";
6
7
  //#region src/cli/subcommands/subcommands/add.ts
7
8
  const command = defineCommand({
8
9
  meta: {
@@ -20,10 +21,17 @@ const command = defineCommand({
20
21
  type: "text",
21
22
  cancel: "reject"
22
23
  });
23
- const password = await consola.prompt("Password:", {
24
- type: "text",
25
- cancel: "reject"
24
+ const passwordPrompt = await password({
25
+ message: "Password:",
26
+ clearOnError: true,
27
+ validate: (value) => {
28
+ if (!value) return "Password is required.";
29
+ }
26
30
  });
31
+ if (isCancel(passwordPrompt)) {
32
+ cancel("Cancelled.");
33
+ exit();
34
+ }
27
35
  if ((await getStoredC8yAuth()).some((cred) => cred.tenantUrl === cleanTenantUrl(tenantUrl))) {
28
36
  if (!await consola.prompt("Credentials for this tenant already exist. Overwrite?", {
29
37
  type: "confirm",
@@ -36,7 +44,7 @@ const command = defineCommand({
36
44
  await setStoredC8yAuth({
37
45
  tenantUrl,
38
46
  user,
39
- password
47
+ password: passwordPrompt
40
48
  });
41
49
  consola.success("Credentials saved successfully!");
42
50
  exit();