mc8yp 2.1.0 → 2.2.1

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
@@ -1,25 +1,135 @@
1
- # mc8yp - Cumulocity IoT MCP Server
1
+ # mc8yp - Full Cumulocity API Access for AI Agents
2
2
 
3
3
  ![Version](https://img.shields.io/npm/v/mc8yp)
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
+ mc8yp is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that gives AI agents access to the **full Cumulocity API surface** through a compact code-mode interface.
8
8
 
9
- **Two Deployment Modes:**
9
+ It supports the two bundled Cumulocity API families exposed by this project:
10
10
 
11
- - **CLI Mode**: Run locally with `pnpm dlx mc8yp` for development and testing with AI agents like Claude Desktop. Uses your system's secure keyring to store credentials.
12
- - **Microservice Mode**: Deploy as a Cumulocity microservice for production use. The MCP endpoint (`/mcp`) integrates with Cumulocity's agents manager, using the service user's permissions automatically.
11
+ - **Core API**
12
+ - **DTM API**
13
13
 
14
- ## Installation
14
+ Instead of limiting agents to a small fixed set of prebuilt tools, mc8yp gives them broad access to Cumulocity through two code-mode tools:
15
15
 
16
- ### CLI Mode (Local Development & Testing)
16
+ - `query` — inspect the bundled Core + DTM OpenAPI specs
17
+ - `execute` — call the live Cumulocity API
18
+
19
+ The result is an MCP integration where agents can work across the broader Cumulocity platform, while operators still keep **fine-grained control** over what is actually allowed at runtime.
20
+
21
+ mc8yp is available in two modes:
22
+
23
+ - **Cumulocity microservice mode** for production use with [AI Agent Manager](https://cumulocity.com/docs/ai/aim-introduction/)
24
+ - **CLI mode** for local debugging, testing, and development
25
+
26
+ ## Why mc8yp
27
+
28
+ ### Full API power for agents
29
+
30
+ mc8yp is built to give agents access to the **complete Cumulocity API surface available through the bundled Core and DTM specs**, instead of a tiny curated subset of actions.
31
+
32
+ That means agents are not blocked just because a specific endpoint was never wrapped as a custom MCP tool.
33
+
34
+ ### Built for AI Agent Manager first
35
+
36
+ The primary production deployment model is **Cumulocity microservice mode**.
37
+
38
+ Deploy mc8yp as a Cumulocity microservice and expose `/mcp` to **AI Agent Manager**, so agents can use broad Cumulocity API capabilities inside the platform.
39
+
40
+ ### Full power, controlled access
41
+
42
+ Broad capability does **not** have to mean unrestricted access.
43
+
44
+ mc8yp lets you constrain live API usage with:
45
+
46
+ - **restrictions** to deny specific methods or paths
47
+ - **allow rules** to define an allow-list
48
+ - **bundled OpenAPI disablement** for selected API families
49
+ - **sandboxed execution** and a tenant-host network boundary
50
+ - normal **Cumulocity permissions** from the authenticated user or service user
51
+
52
+ This makes setups like these possible:
53
+
54
+ - **read-only agents**
55
+ - **non-destructive production agents**
56
+ - agents limited to **inventory**, **alarms**, or other selected API families
57
+ - agents allowed to write only to a small approved set of endpoints
58
+
59
+ ### Token efficiency comes from the small MCP surface
60
+
61
+ The agent gets broad API reach without requiring a huge fixed tool inventory. Instead of many endpoint-specific tools, mc8yp keeps the MCP surface compact and lets the model reason over the bundled OpenAPI specs.
62
+
63
+ ## How it works
64
+
65
+ 1. The agent uses `query` to inspect the bundled Cumulocity OpenAPI specs.
66
+ 2. The agent decides which Core or DTM endpoint it needs.
67
+ 3. The agent uses `execute` to call the live Cumulocity API.
68
+ 4. mc8yp enforces configured restrictions and allow rules before sending the request.
69
+
70
+ ## Deployment Modes
71
+
72
+ ### 1. Cumulocity Microservice Mode (recommended)
73
+
74
+ Designed for deployment inside **Cumulocity IoT**.
75
+
76
+ In this mode, mc8yp exposes an HTTP MCP endpoint at `/mcp` and is intended for use with **AI Agent Manager**.
77
+
78
+ - deploy through Cumulocity microservice packaging
79
+ - integrate with [AI Agent Manager](https://cumulocity.com/docs/ai/aim-introduction/)
80
+ - use the service user's permissions automatically
81
+ - configure per-connection MCP policy with restrictions, allow rules, and bundled OpenAPI disablement
82
+
83
+ ### 2. CLI Mode (local development)
84
+
85
+ CLI mode is ideal for:
86
+
87
+ - local debugging
88
+ - testing agent prompts and workflows
89
+ - validating access-policy setups before deployment
90
+ - working with MCP clients such as Claude Desktop
91
+
92
+ Credentials are stored in your operating system's secure credential manager.
93
+
94
+ ## Quick Start: AI Agent Manager / Microservice
95
+
96
+ 1. Download the latest release package from [GitHub Releases](https://github.com/schplitt/mc8yp/releases)
97
+ 2. Upload the `.zip` in **Application Management**
98
+ 3. Subscribe the application in your tenant
99
+ 4. Connect your agent workflow to:
100
+
101
+ ```txt
102
+ https://<tenant>.cumulocity.com/service/mc8yp-server/mcp
103
+ ```
104
+
105
+ No extra tenant credential setup is required in microservice mode. The microservice uses Cumulocity's deployment environment and request authentication model.
106
+
107
+ ### Example: production-safe read-only microservice connection
108
+
109
+ You can expose broad API knowledge to the agent while allowing only safe read access at runtime.
110
+
111
+ Example MCP endpoint configuration patterns:
112
+
113
+ ```txt
114
+ /mcp?allow=GET:/inventory/**&allow=GET:/alarm/**&allow=GET:/measurement/**
115
+ ```
116
+
117
+ Or with headers:
118
+
119
+ ```http
120
+ POST /mcp HTTP/1.1
121
+ mc8yp-allow: GET:/inventory/**
122
+ mc8yp-allow: GET:/alarm/**
123
+ mc8yp-allow: GET:/measurement/**
124
+ ```
125
+
126
+ ## Quick Start: Local CLI
17
127
 
18
128
  ```sh
19
129
  # Run directly (recommended)
20
130
  pnpm dlx mc8yp
21
131
 
22
- # Pick a specific core OpenAPI snapshot for query
132
+ # Pick a specific bundled OpenAPI build for query
23
133
  pnpm dlx mc8yp --spec 2025
24
134
 
25
135
  # Or install globally
@@ -27,61 +137,30 @@ npm install -g mc8yp
27
137
  mc8yp
28
138
  ```
29
139
 
30
- **Credential Storage:**
31
- Credentials are stored using your operating system's secure credential manager:
140
+ ### Credential Storage
141
+
142
+ The interactive `mc8yp creds add` flow uses masked password input and stores credentials in your operating system's secure credential manager.
32
143
 
33
144
  - **macOS**: Keychain
34
145
  - **Windows**: Credential Vault
35
146
  - **Linux**: Secret Service API (libsecret)
36
147
 
37
- ### Microservice Mode (Production Deployment)
38
-
39
- Designed **exclusively for deployment on Cumulocity IoT**. The server exposes an HTTP endpoint at `/mcp` that integrates with Cumulocity's agents manager, automatically using the service user's credentials and permissions.
40
-
41
- 1. Download the latest release package from [GitHub Releases](https://github.com/schplitt/mc8yp/releases)
42
- 2. Upload the `.zip` to Cumulocity via **Application Management**
43
- 3. Subscribe to the application in your tenant
44
- 4. The MCP server will be available at: `https://<tenant>.cumulocity.com/service/mc8yp-server/mcp`
45
-
46
- No additional credential configuration needed — the microservice uses Cumulocity's built-in service user authentication.
47
-
48
- ## Usage
49
-
50
- ### Managing Credentials (CLI)
148
+ ### Managing Credentials
51
149
 
52
150
  ```sh
53
- # Add credentials (prompts for tenant URL, username, password)
151
+ # Add credentials (prompts for tenant URL, username, and a masked password)
54
152
  pnpm dlx mc8yp creds add
55
153
 
56
154
  # List stored credentials
57
155
  pnpm dlx mc8yp creds list
58
156
 
59
- # Remove credentials
157
+ # Remove stored credentials
60
158
  pnpm dlx mc8yp creds remove
61
159
  ```
62
160
 
63
- ### Selecting The Core OpenAPI Snapshot (CLI)
161
+ ### Connecting a Local MCP Client
64
162
 
65
- Use `--spec` or `-s` to choose which bundled core OpenAPI snapshot the `query` tool exposes.
66
-
67
- Supported values are `release`, `2026`, `2025`, and `2024`.
68
-
69
- ```sh
70
- # Default: latest bundled release snapshot
71
- mc8yp
72
-
73
- # Explicitly use the 2025 core OpenAPI snapshot
74
- mc8yp --spec 2025
75
-
76
- # Short form
77
- mc8yp -s 2024
78
- ```
79
-
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.
81
-
82
- ### Connecting to AI Agents
83
-
84
- For Claude Desktop or any MCP client, add to your MCP configuration:
163
+ For Claude Desktop or any MCP client, add:
85
164
 
86
165
  ```json
87
166
  {
@@ -95,7 +174,7 @@ For Claude Desktop or any MCP client, add to your MCP configuration:
95
174
  }
96
175
  ```
97
176
 
98
- With restrictions (see [API Restrictions](#api-restrictions)):
177
+ Example with read-only access rules:
99
178
 
100
179
  ```json
101
180
  {
@@ -103,19 +182,50 @@ With restrictions (see [API Restrictions](#api-restrictions)):
103
182
  "mc8yp": {
104
183
  "type": "stdio",
105
184
  "command": "pnpm",
106
- "args": ["dlx", "mc8yp", "-r", "/alarm/**", "-r", "DELETE:/inventory/**"]
185
+ "args": [
186
+ "dlx",
187
+ "mc8yp",
188
+ "-a",
189
+ "GET:/inventory/**",
190
+ "-a",
191
+ "GET:/alarm/**",
192
+ "-a",
193
+ "GET:/measurement/**"
194
+ ]
107
195
  }
108
196
  }
109
197
  }
110
198
  ```
111
199
 
200
+ ## Bundled OpenAPI Coverage
201
+
202
+ The `query` tool exposes the bundled OpenAPI snapshots included by this project:
203
+
204
+ - **Core** snapshots: `release`, `2026`, `2025`, and `2024`
205
+ - **DTM** snapshot: bundled alongside each supported core build
206
+
207
+ In CLI mode, use `--spec` or `-s` to choose which bundled **core** OpenAPI snapshot `query` exposes:
208
+
209
+ ```sh
210
+ # Default: latest bundled release build
211
+ mc8yp
212
+
213
+ # Explicitly use the 2025 bundled build
214
+ mc8yp --spec 2025
215
+
216
+ # Short form
217
+ mc8yp -s 2024
218
+ ```
219
+
220
+ This only changes the bundled OpenAPI data that `query` sees. The `execute` tool still calls the live Cumulocity API of the selected tenant or deployed service environment.
221
+
112
222
  ## Tools & Prompts
113
223
 
114
224
  ### Tools
115
225
 
116
226
  | Tool | Description |
117
227
  | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
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`. |
228
+ | `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
229
  | `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
230
  | `list-credentials` | _(CLI mode only)_ List stored credentials from your system keyring. |
121
231
 
@@ -124,7 +234,13 @@ Both code-mode tools run in a sandboxed runtime ([secure-exec](https://github.co
124
234
  - `query` returns JSON text for easier inspection of OpenAPI data.
125
235
  - `execute` returns the successful function result in [Toon format](https://github.com/nicepkg/toon). If execution is blocked or fails, it returns a plain text message instead.
126
236
 
127
- ### Execute Input Shape
237
+ ### Prompts
238
+
239
+ | Prompt | Description |
240
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
241
+ | `code-mode-guide` | Full reference for the `query` and `execute` tools, including available types, examples, and access-policy info for the current connection. |
242
+
243
+ ## Execute Input Shape
128
244
 
129
245
  The `execute` tool expects an async function expression, not module source with `export default`.
130
246
 
@@ -152,12 +268,6 @@ async () => {
152
268
  }
153
269
  ```
154
270
 
155
- ### Prompts
156
-
157
- | Prompt | Description |
158
- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
159
- | `code-mode-guide` | Full reference for the `query` and `execute` tools, including available types, examples, and access-policy info for the current connection. |
160
-
161
271
  ## API Access Policy
162
272
 
163
273
  mc8yp supports two per-connection rule types:
@@ -167,6 +277,8 @@ mc8yp supports two per-connection rule types:
167
277
 
168
278
  If both apply to the same operation, **restrictions take priority**.
169
279
 
280
+ This is what makes it possible to expose broad API capability while still keeping an agent in a **read-only** or otherwise **non-destructive** operating mode.
281
+
170
282
  Example: allowing `/inventory/**` but restricting `/inventory/managedObjects` still blocks `/inventory/managedObjects`.
171
283
 
172
284
  Both rule types use the same syntax.
@@ -237,6 +349,7 @@ Supported wildcards:
237
349
  - `/inventory/**` already matches `/inventory` itself, so you do **not** need both `/inventory` and `/inventory/**`
238
350
  - `/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
351
  - `*:/inventory/**` is allowed and means the same thing as `/inventory/**`
352
+ - 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
353
  - Rule patterns may not contain empty segments (`//`), `.` or `..` segments, query strings, or fragments
241
354
 
242
355
  ### CLI Mode
@@ -252,9 +365,6 @@ mc8yp -r "/inventory/**"
252
365
  # Block deletes on inventory and all alarm access
253
366
  mc8yp -r "DELETE:/inventory/**" -r "/alarm/**"
254
367
 
255
- # Block everything under user management
256
- mc8yp --restriction "/user/**"
257
-
258
368
  # Same thing using the long alias
259
369
  mc8yp --restrict "/user/**"
260
370
  ```
@@ -272,31 +382,61 @@ mc8yp --allow "GET:/inventory/**" --allowed "POST:/alarm/**"
272
382
  mc8yp -a "/inventory/**" -r "/inventory/managedObjects"
273
383
  ```
274
384
 
385
+ 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:
386
+
387
+ ```sh
388
+ # Disable bundled DTM APIs for execute policy on this CLI connection
389
+ mc8yp -d dtm
390
+
391
+ # Disable multiple bundled specs if more are added in the future
392
+ mc8yp -d dtm -d core
393
+ ```
394
+
275
395
  ### Microservice Mode (HTTP)
276
396
 
277
397
  Pass restrictions as `restriction`, `restrict`, or `r` query parameters on the MCP endpoint URL.
278
398
  Pass allow rules as `allowed`, `allow`, or `a` query parameters.
399
+ To forbid bundled OpenAPI parts for execute policy, pass the `openapi-disabled` query parameter.
400
+ You can also send project-scoped HTTP headers to avoid conflicts with well-known headers:
279
401
 
280
- ```
402
+ - `mc8yp-restriction` for deny rules
403
+ - `mc8yp-allow` for allow-list rules
404
+ - `mc8yp-openapi-disabled` for bundled OpenAPI part disablement
405
+
406
+ 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.
407
+
408
+ ```txt
281
409
  /mcp?restriction=/inventory/**&restrict=DELETE:/alarm/**
282
410
  /mcp?r=/inventory/**&r=DELETE:/alarm/**
283
411
  /mcp?allow=/inventory/**&allowed=POST:/alarm/**
284
- /mcp?a=/inventory/**&r=/inventory/managedObjects
412
+ /mcp?openapi-disabled=dtm
413
+ /mcp?openapi-disabled=dtm&openapi-disabled=core
414
+ ```
415
+
416
+ ```http
417
+ POST /mcp HTTP/1.1
418
+ Authorization: Bearer <token>
419
+ mc8yp-restriction: /inventory/**
420
+ mc8yp-restriction: DELETE:/alarm/**
421
+ mc8yp-allow: GET:/measurement/**
422
+ mc8yp-openapi-disabled: dtm
285
423
  ```
286
424
 
287
425
  ### How Access Policy Works
288
426
 
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.
427
+ 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
428
 
291
429
  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
430
 
293
- 3. **Network boundary**: The secure-exec permission layer independently restricts network access to the configured tenant host. Other network operations are denied.
431
+ 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.
432
+
433
+ 4. **Network boundary**: The secure-exec permission layer independently restricts network access to the configured tenant host. Other network operations are denied.
294
434
 
295
435
  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
436
 
297
437
  ## Build And Packaging
298
438
 
299
- The repository bundles multiple core OpenAPI snapshots for CLI use and builds one microservice server bundle per snapshot version.
439
+ The repository bundles multiple OpenAPI specs for CLI use and builds one microservice server bundle per configured build version.
300
440
 
301
441
  ### Build Outputs
302
442
 
@@ -305,7 +445,7 @@ The repository bundles multiple core OpenAPI snapshots for CLI use and builds on
305
445
  - CLI bundle in `dist/`
306
446
  - Versioned server bundles in `.output/release/`, `.output/2026/`, `.output/2025/`, and `.output/2024/`
307
447
 
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.
448
+ 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
449
 
310
450
  ### Release Packaging
311
451
 
@@ -321,10 +461,10 @@ pnpm package:microservices
321
461
 
322
462
  That command creates one zip per bundled server variant in the repository root, for example:
323
463
 
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`
464
+ - `mc8yp-core-release-dtm-v1.2.3.zip`
465
+ - `mc8yp-core-2026-dtm-v1.2.3.zip`
466
+ - `mc8yp-core-2025-dtm-v1.2.3.zip`
467
+ - `mc8yp-core-2024-dtm-v1.2.3.zip`
328
468
 
329
469
  The GitHub release workflow uses that packaging command when building tagged releases.
330
470
 
@@ -354,7 +494,7 @@ pnpm test:run
354
494
  pnpm test:bench
355
495
  ```
356
496
 
357
- ### Run Locally
497
+ ### Run Locally From Source
358
498
 
359
499
  Build first, then point your MCP client at the compiled CLI:
360
500
 
@@ -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();