@ebitex/content-mcp
v0.5.106
Published
An MCP server for ebitex Content: build and publish a content model from an AI agent.
Downloads
1,831
Readme
@ebitex/content-mcp
An MCP server for ebitex Content. Point an AI agent at it and it can build a content model end to end — Contracts and their fields, Templates, Components, the Experience tree — and publish it.
It runs locally, over stdio, authenticated by a Content management key you mint yourself. Nothing
is hosted by ebitex, so your write-capable key never leaves your machine.
Configure it
{
"mcpServers": {
"ebitex-content": {
"command": "npx",
"args": ["-y", "@ebitex/content-mcp"],
"env": { "EBITEX_CONTENT_MANAGEMENT_KEY": "frm_live_..." }
}
}
}| Variable | |
|---|---|
| EBITEX_CONTENT_MANAGEMENT_KEY | Required. Mint one in Content under Settings → API Keys → Management keys |
| EBITEX_CONTENT_API_BASE | Defaults to https://api.ebitex.io |
| EBITEX_CONTENT_MCP_READ_ONLY | Set to 1 to hide every write tool regardless of the key's scopes |
| EBITEX_CONTENT_TARGET_MANAGEMENT_KEY | A second key, for content_transfer to import into |
Mint the key deliberately
A management key is bound to one organization and one authoring environment, and acts as one role. That binding is the real safety boundary here, and it is the one this server cannot supply for you:
- Bind it to a non-production authoring environment. This server has no way to tell which of your environments is production — that is your own topology, and a management key deliberately cannot see it. A heuristic would be worse than nothing, because it would be believed.
- Leave the authoring scope off unless the agent needs to write. Without it, the write tools are not merely refused; they are not offered at all.
- Give it a role with the access it needs and no more. Branch access rules and workflow gating apply to a key exactly as they do to a person.
On startup the server prints, to stderr, which organization and environment the key reached and which role it acts as. Read it once; if it is not what you expected, stop.
The tools
Ten, shaped around what an agent does rather than around REST routes.
| | |
|---|---|
| content_whoami | Where this key is scoped: organization, environment, role, scopes, this server's version, and which tools were withheld and why. Needs no scope, so a key that can do almost nothing can still ask why |
| content_describe | What can exist here: every field type with its own settings schema, plus this environment's Contracts and Templates |
| content_find | Locate entities by kind, search or external id, with one paging contract across both listings |
| content_get | Read one entity in full |
| content_write | Create or update one entity |
| content_delete | Delete one, behind an explicit confirmation |
| content_publish | Plan, read the blockers, then publish |
| content_operation_status | Poll a job |
| content_transfer | Move a whole model between environments, atomically. identityMode picks how items are matched: externalId (the default, by natural key), preserve (by id), or fresh (everything new) |
| content_topology | Where this environment sits: the delivery environments a publish lands in, and its promotion and inheritance neighbours. One hop only |
Which of them appear depends on your key's scopes: a read-scoped key gets a server with no write
tools at all, rather than tools that always fail. content_whoami is the exception and always
appears — it reports which tools were withheld and what each would need, so an absent tool is
explained rather than merely missing.
Every result also names the organization and environment it was answered in, and an empty one says so explicitly. A key reads and writes exactly one authoring environment, so "no Contracts" always means "none here" — an agent that reads it as "none anywhere" creates duplicates.
Why it cannot drift
The server encodes no knowledge of the content model. It does not know what field types exist,
what a Contract may contain, or what makes a document valid. All three are answered at runtime:
content_describe returns each field type's own declared schema, and every write is validated by the
server that stores it. There is nothing here to fall out of step, because nothing is duplicated.
That also shapes how errors work. A refusal is relayed whole, with its field paths intact and no summarizing, because for an agent the error is the control loop: try, read the error, fix the one field it names, call again.
Resources
Alongside the tools, the server offers the Content documentation as MCP resources — start with
ebitex-content://help/agent-authoring, which explains what Contracts, Templates, Presentations,
Adapters and the Experience tree are for, and the order to create them in.
Embedding it
import { buildServer, readConfig } from '@ebitex/content-mcp'
const { server, banner } = await buildServer({ config: readConfig() })
console.error(banner) // never stdout — on stdio, that stream is the protocolA note on zod
This package pins zod to ^3.25.76 rather than ^4, even though @modelcontextprotocol/sdk
accepts either. The reason is type identity, not capability: the SDK's schema types are written
against whichever zod resolves for it, and a second copy at a different major produces a type
mismatch on every tool registration. Pinning to the version the SDK resolves keeps one zod in the
tree. This matters at build time only; at runtime the SDK handles both.
Licence
Apache-2.0 — © ebitex software LLC.
Full guide: developers.ebitex.io.
