mc8yp 2.0.1 → 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 +158 -46
- package/dist/{add-C-dXxz3k.mjs → add-BecAtXuv.mjs} +12 -4
- package/dist/cli.mjs +9029 -864
- package/dist/{creds-CpDDNTs5.mjs → creds-CmsPhr1t.mjs} +2 -2
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|

|
|
5
5
|

|
|
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
|
|
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
|
|
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
|
|
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
|
|
77
|
+
# Default: latest bundled release build
|
|
71
78
|
mc8yp
|
|
72
79
|
|
|
73
|
-
# Explicitly use the 2025
|
|
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`
|
|
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
|
|
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
|
|
|
@@ -154,39 +161,97 @@ async () => {
|
|
|
154
161
|
|
|
155
162
|
### Prompts
|
|
156
163
|
|
|
157
|
-
| Prompt | Description
|
|
158
|
-
| ----------------- |
|
|
159
|
-
| `code-mode-guide` | Full reference for the `query` and `execute` tools, including available types, examples, and
|
|
164
|
+
| Prompt | Description |
|
|
165
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `code-mode-guide` | Full reference for the `query` and `execute` tools, including available types, examples, and access-policy info for the current connection. |
|
|
167
|
+
|
|
168
|
+
## API Access Policy
|
|
169
|
+
|
|
170
|
+
mc8yp supports two per-connection rule types:
|
|
171
|
+
|
|
172
|
+
- **Restrictions** — deny rules that block matching API operations
|
|
173
|
+
- **Allow rules** — allow-list rules that permit matching API operations and block everything else when at least one allow rule is configured
|
|
174
|
+
|
|
175
|
+
If both apply to the same operation, **restrictions take priority**.
|
|
176
|
+
|
|
177
|
+
Example: allowing `/inventory/**` but restricting `/inventory/managedObjects` still blocks `/inventory/managedObjects`.
|
|
178
|
+
|
|
179
|
+
Both rule types use the same syntax.
|
|
180
|
+
|
|
181
|
+
### Restrictions
|
|
160
182
|
|
|
161
|
-
|
|
183
|
+
Restrictions are deny rules that block specific API operations.
|
|
162
184
|
|
|
163
|
-
|
|
185
|
+
### Allow Rules
|
|
186
|
+
|
|
187
|
+
Allow rules are the inverse of restrictions. They define what is permitted. When one or more allow rules are configured, any operation that does not match at least one allow rule is blocked.
|
|
164
188
|
|
|
165
189
|
### Rule Format
|
|
166
190
|
|
|
191
|
+
A restriction or allow rule can be written in either of these forms:
|
|
192
|
+
|
|
193
|
+
```txt
|
|
194
|
+
<path-pattern>
|
|
195
|
+
<method>:<path-pattern>
|
|
167
196
|
```
|
|
168
|
-
[METHOD:]<path-pattern>
|
|
169
|
-
```
|
|
170
197
|
|
|
171
|
-
- **Without a method prefix** —
|
|
172
|
-
- **With a method prefix** —
|
|
173
|
-
- **
|
|
174
|
-
-
|
|
198
|
+
- **Without a method prefix** — matches all HTTP methods for matching paths
|
|
199
|
+
- **With a method prefix** — matches only that method (for example `GET:`, `DELETE:`, `POST:`)
|
|
200
|
+
- **The `:` separator is only present when a method prefix is provided**
|
|
201
|
+
- **Supported methods** — `DELETE`, `GET`, `HEAD`, `OPTIONS`, `PATCH`, `POST`, `PUT`, `TRACE`, or `*`
|
|
202
|
+
- Method names are case-insensitive when parsed (`get:/inventory/**` becomes `GET:/inventory/**`)
|
|
203
|
+
|
|
204
|
+
### Path Pattern Syntax
|
|
205
|
+
|
|
206
|
+
Patterns are matched against the request **pathname**.
|
|
207
|
+
|
|
208
|
+
- Query strings and fragments are **not allowed in rule patterns**
|
|
209
|
+
- Incoming request query strings are ignored for matching, so `/inventory/**` also matches requests such as `/inventory?pageSize=5`
|
|
210
|
+
- Patterns must start with `/`
|
|
211
|
+
- Matching is path-segment aware: `/` separates segments
|
|
212
|
+
|
|
213
|
+
Supported wildcards:
|
|
214
|
+
|
|
215
|
+
- `*` — wildcard **inside a single path segment**. It matches any characters except `/`
|
|
216
|
+
- `**` — recursive wildcard across **zero or more whole path segments**. `**` must be its own complete segment
|
|
217
|
+
|
|
218
|
+
### Path Pattern Examples
|
|
219
|
+
|
|
220
|
+
| Pattern | Matches | Does Not Match |
|
|
221
|
+
| --------------------- | ----------------------------------------------------------- | ------------------------------------------ |
|
|
222
|
+
| `/inventory` | `/inventory` | `/inventory/managedObjects` |
|
|
223
|
+
| `/inventory/**` | `/inventory`, `/inventory/managedObjects`, `/inventory/x/y` | `/alarm/alarms` |
|
|
224
|
+
| `/i*` | `/inventory`, `/identity`, `/i` | `/inventory/managedObjects` |
|
|
225
|
+
| `/i*/**` | `/inventory`, `/inventory/managedObjects`, `/identity/x` | `/alarm/alarms` |
|
|
226
|
+
| `/inventory/m*` | `/inventory/managedObjects`, `/inventory/measurements` | `/inventory/events`, `/inventory/m/x` |
|
|
227
|
+
| `/inventory/*/child` | `/inventory/device-1/child`, `/inventory/x/child` | `/inventory/child`, `/inventory/a/b/child` |
|
|
228
|
+
| `/inventory/**/child` | `/inventory/child`, `/inventory/a/b/child` | `/inventory/a/b/sibling` |
|
|
175
229
|
|
|
176
|
-
### Examples
|
|
230
|
+
### Common Rule Examples
|
|
177
231
|
|
|
178
|
-
| Rule | Effect
|
|
179
|
-
| -------------------------------- |
|
|
180
|
-
| `/inventory/**` | Block all methods on all inventory
|
|
181
|
-
| `DELETE:/inventory/**` | Block only DELETE on inventory
|
|
182
|
-
| `/alarm/alarms` | Block all methods on the exact path `/alarm/alarms` |
|
|
183
|
-
| `GET:/measurement/measurements` | Block only GET on measurements
|
|
184
|
-
| `POST:/inventory/managedObjects` | Block creating new managed objects
|
|
185
|
-
| `/
|
|
232
|
+
| Rule | Restriction Effect | Allow-list Effect |
|
|
233
|
+
| -------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------- |
|
|
234
|
+
| `/inventory/**` | Block all methods on `/inventory` and everything below it | Permit all methods on `/inventory` and everything below it |
|
|
235
|
+
| `DELETE:/inventory/**` | Block only DELETE on `/inventory` and everything below it | Permit only DELETE on `/inventory` and everything below it |
|
|
236
|
+
| `/alarm/alarms` | Block all methods on the exact path `/alarm/alarms` | Permit all methods on the exact path `/alarm/alarms` |
|
|
237
|
+
| `GET:/measurement/measurements` | Block only GET on the exact path `/measurement/measurements` | Permit only GET on the exact path `/measurement/measurements` |
|
|
238
|
+
| `POST:/inventory/managedObjects` | Block creating new managed objects | Permit creating new managed objects |
|
|
239
|
+
| `/i*/**` | Block all routes whose first path segment starts with `i` | Permit all routes whose first path segment starts with `i` |
|
|
240
|
+
| `/user/**` | Block all user management paths | Permit all user management paths |
|
|
241
|
+
|
|
242
|
+
### Important Notes
|
|
243
|
+
|
|
244
|
+
- `/inventory/**` already matches `/inventory` itself, so you do **not** need both `/inventory` and `/inventory/**`
|
|
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
|
|
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
|
|
248
|
+
- Rule patterns may not contain empty segments (`//`), `.` or `..` segments, query strings, or fragments
|
|
186
249
|
|
|
187
250
|
### CLI Mode
|
|
188
251
|
|
|
189
|
-
Pass restrictions as CLI arguments.
|
|
252
|
+
Pass restrictions and allow rules as CLI arguments.
|
|
253
|
+
|
|
254
|
+
Repeat `-r`, `--restrict`, or `--restriction` for deny rules:
|
|
190
255
|
|
|
191
256
|
```sh
|
|
192
257
|
# Block all inventory access
|
|
@@ -195,31 +260,78 @@ mc8yp -r "/inventory/**"
|
|
|
195
260
|
# Block deletes on inventory and all alarm access
|
|
196
261
|
mc8yp -r "DELETE:/inventory/**" -r "/alarm/**"
|
|
197
262
|
|
|
198
|
-
#
|
|
199
|
-
mc8yp --
|
|
263
|
+
# Same thing using the long alias
|
|
264
|
+
mc8yp --restrict "/user/**"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Repeat `-a`, `--allow`, or `--allowed` for allow rules:
|
|
268
|
+
|
|
269
|
+
```sh
|
|
270
|
+
# Only permit inventory access
|
|
271
|
+
mc8yp -a "/inventory/**"
|
|
272
|
+
|
|
273
|
+
# Permit GET inventory access and POST alarms
|
|
274
|
+
mc8yp --allow "GET:/inventory/**" --allowed "POST:/alarm/**"
|
|
275
|
+
|
|
276
|
+
# Allow inventory broadly, but still block one path with a restriction
|
|
277
|
+
mc8yp -a "/inventory/**" -r "/inventory/managedObjects"
|
|
278
|
+
```
|
|
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
|
|
200
288
|
```
|
|
201
289
|
|
|
202
290
|
### Microservice Mode (HTTP)
|
|
203
291
|
|
|
204
|
-
Pass restrictions as `restriction` query parameters on the MCP endpoint URL
|
|
292
|
+
Pass restrictions as `restriction`, `restrict`, or `r` query parameters on the MCP endpoint URL.
|
|
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
|
|
205
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.
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
/mcp?restriction=/inventory/**&restrict=DELETE:/alarm/**
|
|
305
|
+
/mcp?r=/inventory/**&r=DELETE:/alarm/**
|
|
306
|
+
/mcp?allow=/inventory/**&allowed=POST:/alarm/**
|
|
307
|
+
/mcp?openapi-disabled=dtm
|
|
308
|
+
/mcp?openapi-disabled=dtm&openapi-disabled=core
|
|
206
309
|
```
|
|
207
|
-
|
|
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
|
|
208
318
|
```
|
|
209
319
|
|
|
210
|
-
### How
|
|
320
|
+
### How Access Policy Works
|
|
321
|
+
|
|
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.
|
|
211
323
|
|
|
212
|
-
|
|
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.
|
|
213
325
|
|
|
214
|
-
|
|
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.
|
|
215
327
|
|
|
216
|
-
|
|
328
|
+
4. **Network boundary**: The secure-exec permission layer independently restricts network access to the configured tenant host. Other network operations are denied.
|
|
217
329
|
|
|
218
|
-
When an `execute` request is blocked by MCP
|
|
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.
|
|
219
331
|
|
|
220
332
|
## Build And Packaging
|
|
221
333
|
|
|
222
|
-
The repository bundles multiple
|
|
334
|
+
The repository bundles multiple OpenAPI specs for CLI use and builds one microservice server bundle per configured build version.
|
|
223
335
|
|
|
224
336
|
### Build Outputs
|
|
225
337
|
|
|
@@ -228,7 +340,7 @@ The repository bundles multiple core OpenAPI snapshots for CLI use and builds on
|
|
|
228
340
|
- CLI bundle in `dist/`
|
|
229
341
|
- Versioned server bundles in `.output/release/`, `.output/2026/`, `.output/2025/`, and `.output/2024/`
|
|
230
342
|
|
|
231
|
-
The
|
|
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.
|
|
232
344
|
|
|
233
345
|
### Release Packaging
|
|
234
346
|
|
|
@@ -244,10 +356,10 @@ pnpm package:microservices
|
|
|
244
356
|
|
|
245
357
|
That command creates one zip per bundled server variant in the repository root, for example:
|
|
246
358
|
|
|
247
|
-
- `mc8yp-release-v1.2.3.zip`
|
|
248
|
-
- `mc8yp-2026-v1.2.3.zip`
|
|
249
|
-
- `mc8yp-2025-v1.2.3.zip`
|
|
250
|
-
- `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`
|
|
251
363
|
|
|
252
364
|
The GitHub release workflow uses that packaging command when building tagged releases.
|
|
253
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
|
|
24
|
-
|
|
25
|
-
|
|
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();
|