@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 +102 -102
- package/dist/main.js +17 -1
- package/dist/main.js.map +1 -1
- package/dist/resources/helpCorpus.generated.js +21 -21
- package/dist/resources/helpCorpus.generated.js.map +1 -1
- package/dist/tools/register.d.ts.map +1 -1
- package/dist/tools/register.js +43 -4
- package/dist/tools/register.js.map +1 -1
- package/package.json +39 -39
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(
|
|
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,
|
|
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"}
|