@ebitex/content-mcp 0.1.0 → 0.1.74

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,102 +1,102 @@
1
- # @ebitex/content-mcp
2
-
3
- An [MCP](https://modelcontextprotocol.io) server for **ebitex Content**. Point an AI agent at it and
4
- it can build a content model end to end — Contracts and their fields, Templates, Components, the
5
- Experience tree — and publish it.
6
-
7
- It runs locally, over `stdio`, authenticated by a Content management key you mint yourself. Nothing
8
- is hosted by ebitex, so your write-capable key never leaves your machine.
9
-
10
- ## Configure it
11
-
12
- ```json
13
- {
14
- "mcpServers": {
15
- "ebitex-content": {
16
- "command": "npx",
17
- "args": ["-y", "@ebitex/content-mcp"],
18
- "env": { "EBITEX_CONTENT_MANAGEMENT_KEY": "frm_live_..." }
19
- }
20
- }
21
- }
22
- ```
23
-
24
- | Variable | |
25
- |---|---|
26
- | `EBITEX_CONTENT_MANAGEMENT_KEY` | **Required.** Mint one in Composer under Settings → Management keys |
27
- | `EBITEX_CONTENT_API_BASE` | Defaults to `https://api.ebitex.io` |
28
- | `EBITEX_CONTENT_MCP_READ_ONLY` | Set to `1` to hide every write tool regardless of the key's scopes |
29
- | `EBITEX_CONTENT_TARGET_MANAGEMENT_KEY` | A second key, for `content_transfer` to import into |
30
-
31
- ## Mint the key deliberately
32
-
33
- A management key is bound to **one organization and one authoring environment**, and acts as **one
34
- role**. That binding is the real safety boundary here, and it is the one this server cannot supply
35
- for you:
36
-
37
- - **Bind it to a non-production authoring environment.** This server has no way to tell which of
38
- your environments is production — that is your own topology, and a management key deliberately
39
- cannot see it. A heuristic would be worse than nothing, because it would be believed.
40
- - **Leave the authoring scope off unless the agent needs to write.** Without it, the write tools are
41
- not merely refused; they are not offered at all.
42
- - **Give it a role with the access it needs and no more.** Branch access rules and workflow gating
43
- apply to a key exactly as they do to a person.
44
-
45
- On startup the server prints, to stderr, which organization and environment the key reached and
46
- which role it acts as. Read it once; if it is not what you expected, stop.
47
-
48
- ## The tools
49
-
50
- Eight, shaped around what an agent does rather than around REST routes.
51
-
52
- | | |
53
- |---|---|
54
- | `content_describe` | What can exist here: every field type with its **own settings schema**, plus this environment's Contracts and Templates |
55
- | `content_find` | Locate entities by kind, search or external id |
56
- | `content_get` | Read one entity in full |
57
- | `content_write` | Create or update one entity |
58
- | `content_delete` | Delete one, behind an explicit confirmation |
59
- | `content_publish` | Plan, read the blockers, then publish |
60
- | `content_operation_status` | Poll a job |
61
- | `content_transfer` | Move a whole model between environments, atomically |
62
-
63
- Which of them appear depends on your key's scopes: a read-scoped key gets a server with no write
64
- tools at all, rather than tools that always fail.
65
-
66
- ## Why it cannot drift
67
-
68
- The server encodes **no knowledge of the content model**. It does not know what field types exist,
69
- what a Contract may contain, or what makes a document valid. All three are answered at runtime:
70
- `content_describe` returns each field type's own declared schema, and every write is validated by the
71
- server that stores it. There is nothing here to fall out of step, because nothing is duplicated.
72
-
73
- That also shapes how errors work. A refusal is relayed **whole**, with its field paths intact and no
74
- summarizing, because for an agent the error is the control loop: try, read the error, fix the one
75
- field it names, call again.
76
-
77
- ## Resources
78
-
79
- Alongside the tools, the server offers the Content documentation as MCP resources — start with
80
- `ebitex-content://help/agent-authoring`, which explains what Contracts, Templates, Presentations,
81
- Adapters and the Experience tree are for, and the order to create them in.
82
-
83
- ## Embedding it
84
-
85
- ```ts
86
- import { buildServer, readConfig } from '@ebitex/content-mcp'
87
-
88
- const { server, banner } = await buildServer({ config: readConfig() })
89
- console.error(banner) // never stdout — on stdio, that stream is the protocol
90
- ```
91
-
92
- ## A note on `zod`
93
-
94
- This package pins `zod` to `^3.25.76` rather than `^4`, even though `@modelcontextprotocol/sdk`
95
- accepts either. The reason is type identity, not capability: the SDK's schema types are written
96
- against whichever `zod` resolves for *it*, and a second copy at a different major produces a type
97
- mismatch on every tool registration. Pinning to the version the SDK resolves keeps one `zod` in the
98
- tree. This matters at build time only; at runtime the SDK handles both.
99
-
100
- ## Licence
101
-
102
- UNLICENSED — © ebitex software LLC.
1
+ # @ebitex/content-mcp
2
+
3
+ An [MCP](https://modelcontextprotocol.io) server for **ebitex Content**. Point an AI agent at it and
4
+ it can build a content model end to end — Contracts and their fields, Templates, Components, the
5
+ Experience tree — and publish it.
6
+
7
+ It runs locally, over `stdio`, authenticated by a Content management key you mint yourself. Nothing
8
+ is hosted by ebitex, so your write-capable key never leaves your machine.
9
+
10
+ ## Configure it
11
+
12
+ ```json
13
+ {
14
+ "mcpServers": {
15
+ "ebitex-content": {
16
+ "command": "npx",
17
+ "args": ["-y", "@ebitex/content-mcp"],
18
+ "env": { "EBITEX_CONTENT_MANAGEMENT_KEY": "frm_live_..." }
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ | Variable | |
25
+ |---|---|
26
+ | `EBITEX_CONTENT_MANAGEMENT_KEY` | **Required.** Mint one in Composer under Settings → Management keys |
27
+ | `EBITEX_CONTENT_API_BASE` | Defaults to `https://api.ebitex.io` |
28
+ | `EBITEX_CONTENT_MCP_READ_ONLY` | Set to `1` to hide every write tool regardless of the key's scopes |
29
+ | `EBITEX_CONTENT_TARGET_MANAGEMENT_KEY` | A second key, for `content_transfer` to import into |
30
+
31
+ ## Mint the key deliberately
32
+
33
+ A management key is bound to **one organization and one authoring environment**, and acts as **one
34
+ role**. That binding is the real safety boundary here, and it is the one this server cannot supply
35
+ for you:
36
+
37
+ - **Bind it to a non-production authoring environment.** This server has no way to tell which of
38
+ your environments is production — that is your own topology, and a management key deliberately
39
+ cannot see it. A heuristic would be worse than nothing, because it would be believed.
40
+ - **Leave the authoring scope off unless the agent needs to write.** Without it, the write tools are
41
+ not merely refused; they are not offered at all.
42
+ - **Give it a role with the access it needs and no more.** Branch access rules and workflow gating
43
+ apply to a key exactly as they do to a person.
44
+
45
+ On startup the server prints, to stderr, which organization and environment the key reached and
46
+ which role it acts as. Read it once; if it is not what you expected, stop.
47
+
48
+ ## The tools
49
+
50
+ Eight, shaped around what an agent does rather than around REST routes.
51
+
52
+ | | |
53
+ |---|---|
54
+ | `content_describe` | What can exist here: every field type with its **own settings schema**, plus this environment's Contracts and Templates |
55
+ | `content_find` | Locate entities by kind, search or external id |
56
+ | `content_get` | Read one entity in full |
57
+ | `content_write` | Create or update one entity |
58
+ | `content_delete` | Delete one, behind an explicit confirmation |
59
+ | `content_publish` | Plan, read the blockers, then publish |
60
+ | `content_operation_status` | Poll a job |
61
+ | `content_transfer` | Move a whole model between environments, atomically |
62
+
63
+ Which of them appear depends on your key's scopes: a read-scoped key gets a server with no write
64
+ tools at all, rather than tools that always fail.
65
+
66
+ ## Why it cannot drift
67
+
68
+ The server encodes **no knowledge of the content model**. It does not know what field types exist,
69
+ what a Contract may contain, or what makes a document valid. All three are answered at runtime:
70
+ `content_describe` returns each field type's own declared schema, and every write is validated by the
71
+ server that stores it. There is nothing here to fall out of step, because nothing is duplicated.
72
+
73
+ That also shapes how errors work. A refusal is relayed **whole**, with its field paths intact and no
74
+ summarizing, because for an agent the error is the control loop: try, read the error, fix the one
75
+ field it names, call again.
76
+
77
+ ## Resources
78
+
79
+ Alongside the tools, the server offers the Content documentation as MCP resources — start with
80
+ `ebitex-content://help/agent-authoring`, which explains what Contracts, Templates, Presentations,
81
+ Adapters and the Experience tree are for, and the order to create them in.
82
+
83
+ ## Embedding it
84
+
85
+ ```ts
86
+ import { buildServer, readConfig } from '@ebitex/content-mcp'
87
+
88
+ const { server, banner } = await buildServer({ config: readConfig() })
89
+ console.error(banner) // never stdout — on stdio, that stream is the protocol
90
+ ```
91
+
92
+ ## A note on `zod`
93
+
94
+ This package pins `zod` to `^3.25.76` rather than `^4`, even though `@modelcontextprotocol/sdk`
95
+ accepts either. The reason is type identity, not capability: the SDK's schema types are written
96
+ against whichever `zod` resolves for *it*, and a second copy at a different major produces a type
97
+ mismatch on every tool registration. Pinning to the version the SDK resolves keeps one `zod` in the
98
+ tree. This matters at build time only; at runtime the SDK handles both.
99
+
100
+ ## Licence
101
+
102
+ UNLICENSED — © ebitex software LLC.
package/dist/main.js CHANGED
@@ -30,6 +30,22 @@ main().catch((error) => {
30
30
  else {
31
31
  console.error(error);
32
32
  }
33
- process.exit(1);
33
+ // `process.exitCode`, never `process.exit()`. Found on Windows against the real published
34
+ // package: `process.exit()` here tears the process down while the `fetch` to `/whoami` still has
35
+ // live libuv handles, which aborts with
36
+ //
37
+ // Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src/win/async.c, line 76
38
+ //
39
+ // and an exit code of 127 rather than 1 — conventionally "command not found", which is actively
40
+ // misleading for a launcher. The message does still print first, so the failure looks like a
41
+ // crash *after* correctly reporting itself.
42
+ //
43
+ // Setting the code and letting the loop drain exits cleanly in well under a second: undici's
44
+ // keep-alive socket is unref'd, so it holds nothing open. The startup path that throws BEFORE any
45
+ // request (a missing key) was always fine, which is what localizes this to "a fetch happened".
46
+ //
47
+ // Note no test here can catch a regression: the assertion is Windows-only (`src/win/async.c`) and
48
+ // CI runs Ubuntu, where `process.exit()` would exit 1 quietly. The guard is this comment.
49
+ process.exitCode = 1;
34
50
  });
35
51
  //# sourceMappingURL=main.js.map
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,EAAE,CAAA;IAC9C,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAErB,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAA;AAClD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,8FAA8F;IAC9F,0FAA0F;IAC1F,qDAAqD;IACrD,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;QAClE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,WAAW,EAAE,CAAA;IAC9C,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAErB,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAA;AAClD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,8FAA8F;IAC9F,0FAA0F;IAC1F,qDAAqD;IACrD,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,YAAY,YAAY,EAAE,CAAC;QAClE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;IAED,0FAA0F;IAC1F,iGAAiG;IACjG,wCAAwC;IACxC,EAAE;IACF,0FAA0F;IAC1F,EAAE;IACF,gGAAgG;IAChG,6FAA6F;IAC7F,4CAA4C;IAC5C,EAAE;IACF,6FAA6F;IAC7F,kGAAkG;IAClG,+FAA+F;IAC/F,EAAE;IACF,kGAAkG;IAClG,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACtB,CAAC,CAAC,CAAA"}