@ebitex/content-mcp 0.1.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.
Files changed (50) hide show
  1. package/README.md +102 -0
  2. package/dist/client.d.ts +34 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +63 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/config.d.ts +42 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +62 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/errors.d.ts +43 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +82 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/index.d.ts +14 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +14 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/main.d.ts +3 -0
  19. package/dist/main.d.ts.map +1 -0
  20. package/dist/main.js +35 -0
  21. package/dist/main.js.map +1 -0
  22. package/dist/resources/helpCorpus.generated.d.ts +7 -0
  23. package/dist/resources/helpCorpus.generated.d.ts.map +1 -0
  24. package/dist/resources/helpCorpus.generated.js +112 -0
  25. package/dist/resources/helpCorpus.generated.js.map +1 -0
  26. package/dist/resources/register.d.ts +8 -0
  27. package/dist/resources/register.d.ts.map +1 -0
  28. package/dist/resources/register.js +73 -0
  29. package/dist/resources/register.js.map +1 -0
  30. package/dist/routes.d.ts +68 -0
  31. package/dist/routes.d.ts.map +1 -0
  32. package/dist/routes.js +173 -0
  33. package/dist/routes.js.map +1 -0
  34. package/dist/server.d.ts +32 -0
  35. package/dist/server.d.ts.map +1 -0
  36. package/dist/server.js +65 -0
  37. package/dist/server.js.map +1 -0
  38. package/dist/session.d.ts +45 -0
  39. package/dist/session.d.ts.map +1 -0
  40. package/dist/session.js +71 -0
  41. package/dist/session.js.map +1 -0
  42. package/dist/tools/register.d.ts +26 -0
  43. package/dist/tools/register.d.ts.map +1 -0
  44. package/dist/tools/register.js +212 -0
  45. package/dist/tools/register.js.map +1 -0
  46. package/dist/tools/result.d.ts +28 -0
  47. package/dist/tools/result.d.ts.map +1 -0
  48. package/dist/tools/result.js +26 -0
  49. package/dist/tools/result.js.map +1 -0
  50. package/package.json +39 -0
@@ -0,0 +1,212 @@
1
+ import { z } from 'zod';
2
+ import { query } from '../client.js';
3
+ import { routesFor, withId, WRITABLE_KINDS } from '../routes.js';
4
+ import { SCOPE_AUTHORING, SCOPE_PUBLISH, SCOPE_READ, SCOPE_WRITE } from '../session.js';
5
+ import { fail, guard, ok } from './result.js';
6
+ const KIND = z.enum(WRITABLE_KINDS);
7
+ export function registerTools(server, context) {
8
+ const { client, session, target } = context;
9
+ // Everything below is gated on a scope, and an absent tool is the point: an agent that cannot see
10
+ // a tool will not plan around it, where a tool that is present and always 403s costs it a turn
11
+ // and a re-plan (§4).
12
+ const canRead = session.has(SCOPE_READ);
13
+ const canAuthor = session.has(SCOPE_AUTHORING);
14
+ const canPublish = session.has(SCOPE_PUBLISH);
15
+ const canTransfer = session.has(SCOPE_WRITE);
16
+ if (canRead) {
17
+ server.registerTool('content_describe', {
18
+ title: 'Describe what can exist here',
19
+ description: 'What this environment can hold: every field type with its own settings JSON Schema, ' +
20
+ 'and the Contracts and Templates that already exist. Call this first — it is where the ' +
21
+ 'shape of everything you are about to write comes from, and it is answered live by the ' +
22
+ 'server rather than from anything this tool knows.',
23
+ inputSchema: {
24
+ include: z
25
+ .array(z.enum(['fieldTypes', 'contracts', 'templates']))
26
+ .optional()
27
+ .describe('Defaults to all three.'),
28
+ },
29
+ annotations: { readOnlyHint: true, openWorldHint: true },
30
+ }, async ({ include }) => guard(async () => {
31
+ const wanted = new Set(include ?? ['fieldTypes', 'contracts', 'templates']);
32
+ const [fieldTypes, contracts, templates] = await Promise.all([
33
+ wanted.has('fieldTypes') ? client.request('GET', '/field-types') : undefined,
34
+ wanted.has('contracts') ? client.request('GET', '/contracts') : undefined,
35
+ wanted.has('templates') ? client.request('GET', '/templates') : undefined,
36
+ ]);
37
+ return ok({ fieldTypes, contracts, templates });
38
+ }));
39
+ server.registerTool('content_find', {
40
+ title: 'Find entities',
41
+ description: 'Locate entities in this environment. With no kind, returns a paged inventory of ' +
42
+ 'everything; with a kind, only that kind. Use it to check whether something already ' +
43
+ 'exists before creating a duplicate.',
44
+ inputSchema: {
45
+ kind: z.string().optional().describe('contract, template, component, adapter, stream, audience, categoryGroup, category, folder, node'),
46
+ search: z.string().optional().describe('Free-text search. Components only.'),
47
+ externalId: z.string().optional().describe('Exact match. Components only.'),
48
+ limit: z.number().int().min(1).max(200).optional(),
49
+ cursor: z.string().optional().describe('From a previous call\'s nextCursor.'),
50
+ },
51
+ annotations: { readOnlyHint: true, openWorldHint: true },
52
+ }, async ({ kind, search, externalId, limit, cursor }) => guard(async () => {
53
+ // Two listings, one tool, because both answer "what is already here" and an agent asking
54
+ // that should not have to know which of them can filter. The components listing is the
55
+ // only one that takes a query at all; /items is a flat inventory across every kind.
56
+ if (search !== undefined || externalId !== undefined) {
57
+ if (kind !== undefined && kind !== 'component') {
58
+ return fail('search and externalId filter components only; drop them, or pass kind: "component".');
59
+ }
60
+ return ok(await client.request('GET', `/components${query({ search, externalId })}`));
61
+ }
62
+ return ok(await client.request('GET', `/items${query({ kind, limit, cursor })}`));
63
+ }));
64
+ server.registerTool('content_get', {
65
+ title: 'Read one entity',
66
+ description: 'Read a single entity in full, by id. Not every kind supports this — folders, audiences, ' +
67
+ 'sites and taxonomy are read by listing them with content_find instead, and this tool ' +
68
+ 'will say so rather than guess at a route.',
69
+ inputSchema: { kind: KIND, id: z.string() },
70
+ annotations: { readOnlyHint: true, openWorldHint: true },
71
+ }, async ({ kind, id }) => guard(async () => {
72
+ const routes = routesFor(kind);
73
+ if (!routes.readOne) {
74
+ return fail(`${routes.label} has no single-entity read on this API. Use content_find` +
75
+ (routes.list ? ` (kind: "${kind}")` : '') +
76
+ ' instead.');
77
+ }
78
+ return ok(await client.request('GET', withId(routes.readOne, id)));
79
+ }));
80
+ }
81
+ if (canAuthor) {
82
+ server.registerTool('content_write', {
83
+ title: 'Create or update an entity',
84
+ description: 'Create (omit id) or update (supply id) one entity. The payload is whatever that kind\'s ' +
85
+ 'API takes — run content_describe first to learn it. The server validates what you send ' +
86
+ 'and refuses an invalid document naming the failing field, so a refusal tells you exactly ' +
87
+ 'what to change; correct it and call again.',
88
+ inputSchema: {
89
+ kind: KIND,
90
+ id: z.string().optional().describe('Omit to create.'),
91
+ payload: z.record(z.string(), z.unknown()).describe('The request body, as this kind\'s API defines it.'),
92
+ },
93
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
94
+ }, async ({ kind, id, payload }) => guard(async () => {
95
+ const routes = routesFor(kind);
96
+ if (!id) {
97
+ if (!routes.create) {
98
+ return fail(`${routes.label} cannot be created on its own; it is written by updating its owner.`);
99
+ }
100
+ return ok(await client.request('POST', routes.create, payload), session.contextLine());
101
+ }
102
+ if (!routes.update) {
103
+ return fail(`${routes.label} cannot be updated once created.`);
104
+ }
105
+ // PATCH for an Adapter, PUT for everything else. Read from the table rather than assumed
106
+ // uniform — see routes.ts's header for why that distinction is load-bearing.
107
+ return ok(await client.request(routes.update.method, withId(routes.update.path, id), payload), session.contextLine());
108
+ }));
109
+ server.registerTool('content_delete', {
110
+ title: 'Delete an entity',
111
+ description: 'Permanently delete one entity. Requires confirm: true. Content refuses to delete ' +
112
+ 'anything still in use and will name what depends on it, so a refusal here is the ' +
113
+ 'product protecting you rather than an error to work around.',
114
+ inputSchema: {
115
+ kind: KIND,
116
+ id: z.string(),
117
+ confirm: z.boolean().describe('Must be true. There is no undo.'),
118
+ },
119
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
120
+ }, async ({ kind, id, confirm }) => guard(async () => {
121
+ // Refused locally rather than sent. An unconfirmed delete is not a request the server
122
+ // should have to judge, and asking it would mean the confirmation could be satisfied by
123
+ // a retry that happened to succeed.
124
+ if (!confirm)
125
+ return fail('Not deleted. Pass confirm: true to delete this permanently.');
126
+ const routes = routesFor(kind);
127
+ if (!routes.remove)
128
+ return fail(`${routes.label} cannot be deleted through this API.`);
129
+ await client.request('DELETE', withId(routes.remove, id));
130
+ return ok(`Deleted ${routes.label} ${id}.`, session.contextLine());
131
+ }));
132
+ }
133
+ if (canPublish) {
134
+ server.registerTool('content_publish', {
135
+ title: 'Publish content',
136
+ description: 'Publishing is two calls, and deliberately so. Pass roots to get a plan: it resolves the ' +
137
+ 'whole dependency closure and reports blockers — an unresolved reference, a workflow ' +
138
+ 'state, a validation failure. Read the plan, then call again with items to publish them. ' +
139
+ 'Both phases are jobs: poll the returned operationId with content_operation_status.',
140
+ inputSchema: {
141
+ roots: z
142
+ .array(z.record(z.string(), z.unknown()))
143
+ .optional()
144
+ .describe('Plan phase: the entities to publish, each { kind, id }.'),
145
+ items: z
146
+ .array(z.record(z.string(), z.unknown()))
147
+ .optional()
148
+ .describe('Execute phase: the plan items to publish, taken from the finished plan.'),
149
+ deliveryEnvironmentId: z.string().optional().describe('Required only when several delivery targets are mapped.'),
150
+ },
151
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
152
+ }, async ({ roots, items, deliveryEnvironmentId }) => guard(async () => {
153
+ if ((roots === undefined) === (items === undefined)) {
154
+ return fail('Pass exactly one of roots (to plan) or items (to publish).');
155
+ }
156
+ const path = roots ? '/publish-plan' : '/publish';
157
+ const body = roots ? { roots, deliveryEnvironmentId } : { items, deliveryEnvironmentId };
158
+ return ok(await client.request('POST', path, body), session.contextLine());
159
+ }));
160
+ }
161
+ if (canRead) {
162
+ server.registerTool('content_operation_status', {
163
+ title: 'Check a job',
164
+ description: 'Poll a publish plan or publish job by its operationId. A finished job carries its ' +
165
+ 'result — for a plan, that result is the plan itself.',
166
+ inputSchema: { operationId: z.string() },
167
+ annotations: { readOnlyHint: true, openWorldHint: true },
168
+ }, async ({ operationId }) => guard(async () => ok(await client.request('GET', `/operations/${encodeURIComponent(operationId)}`))));
169
+ }
170
+ if (canTransfer) {
171
+ server.registerTool('content_transfer', {
172
+ title: 'Move a model between environments',
173
+ description: 'Export this environment (or a set of roots) as a bundle, and — when a target key is ' +
174
+ 'configured — import it into that target atomically. This is the one operation bundles ' +
175
+ 'are uniquely good at: forty per-entity writes are forty transactions, and one import is ' +
176
+ 'one. The bundle is produced and consumed by the server; nothing here builds it.',
177
+ inputSchema: {
178
+ roots: z.array(z.record(z.string(), z.unknown())).optional(),
179
+ wholeEnvironment: z.boolean().optional(),
180
+ apply: z
181
+ .boolean()
182
+ .optional()
183
+ .describe('Import into the target environment. Without it, the bundle is only returned.'),
184
+ identityMode: z.enum(['fresh', 'existing', 'externalId']).optional(),
185
+ },
186
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
187
+ }, async ({ roots, wholeEnvironment, apply, identityMode }) => guard(async () => {
188
+ const bundle = await client.request('POST', '/export', {
189
+ roots,
190
+ wholeEnvironment: wholeEnvironment ?? roots === undefined,
191
+ });
192
+ if (!apply)
193
+ return ok(bundle);
194
+ if (!target) {
195
+ return fail('No target environment is configured. Set EBITEX_CONTENT_TARGET_MANAGEMENT_KEY to a ' +
196
+ 'key bound to the environment this should import into.');
197
+ }
198
+ const mode = identityMode ?? 'externalId';
199
+ const plan = (await target.request('POST', '/import-plan', { bundle, identityMode: mode }));
200
+ // The plan's own items are echoed back as the selection. `items[]` selects what is
201
+ // written (spec #623), so sending the plan unchanged is "apply everything this plan
202
+ // describes" — and it is the plan, not this code, that decided what that is.
203
+ const result = await target.request('POST', '/import', {
204
+ bundle,
205
+ identityMode: mode,
206
+ items: (plan.items ?? []).map((item) => ({ kind: item.kind, id: item.id })),
207
+ });
208
+ return ok({ plan, result });
209
+ }));
210
+ }
211
+ }
212
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAgB,MAAM,eAAe,CAAA;AACrG,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAyB7C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;AAEnC,MAAM,UAAU,aAAa,CAAC,MAAiB,EAAE,OAAoB;IACnE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAE3C,kGAAkG;IAClG,+FAA+F;IAC/F,sBAAsB;IACtB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAE5C,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;YACE,KAAK,EAAE,8BAA8B;YACrC,WAAW,EACT,sFAAsF;gBACtF,wFAAwF;gBACxF,wFAAwF;gBACxF,mDAAmD;YACrD,WAAW,EAAE;gBACX,OAAO,EAAE,CAAC;qBACP,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;qBACvD,QAAQ,EAAE;qBACV,QAAQ,CAAC,wBAAwB,CAAC;aACtC;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACzD,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CACpB,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAA;YAC3E,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAC3D,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS;gBAC5E,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;gBACzE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;aAC1E,CAAC,CAAA;YAEF,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAA;QACjD,CAAC,CAAC,CACL,CAAA;QAED,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;YACE,KAAK,EAAE,eAAe;YACtB,WAAW,EACT,kFAAkF;gBAClF,qFAAqF;gBACrF,qCAAqC;YACvC,WAAW,EAAE;gBACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iGAAiG,CAAC;gBACvI,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;gBAC5E,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;gBAC3E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;gBAClD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;aAC9E;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACzD,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CACpD,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,yFAAyF;YACzF,uFAAuF;YACvF,oFAAoF;YACpF,IAAI,MAAM,KAAK,SAAS,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBACrD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC/C,OAAO,IAAI,CAAC,qFAAqF,CAAC,CAAA;gBACpG,CAAC;gBAED,OAAO,EAAE,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,KAAK,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YACvF,CAAC;YAED,OAAO,EAAE,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QACnF,CAAC,CAAC,CACL,CAAA;QAED,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;YACE,KAAK,EAAE,iBAAiB;YACxB,WAAW,EACT,0FAA0F;gBAC1F,uFAAuF;gBACvF,2CAA2C;YAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE;YAC3C,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACzD,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CACrB,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;YAC9B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO,IAAI,CACT,GAAG,MAAM,CAAC,KAAK,0DAA0D;oBACvE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzC,WAAW,CACd,CAAA;YACH,CAAC;YAED,OAAO,EAAE,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;QACpE,CAAC,CAAC,CACL,CAAA;IACH,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;YACE,KAAK,EAAE,4BAA4B;YACnC,WAAW,EACT,0FAA0F;gBAC1F,yFAAyF;gBACzF,2FAA2F;gBAC3F,4CAA4C;YAC9C,WAAW,EAAE;gBACX,IAAI,EAAE,IAAI;gBACV,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;gBACrD,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,mDAAmD,CAAC;aACzG;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE;SACxG,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAC9B,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;YAE9B,IAAI,CAAC,EAAE,EAAE,CAAC;gBACR,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBACnB,OAAO,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,qEAAqE,CAAC,CAAA;gBACnG,CAAC;gBAED,OAAO,EAAE,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;YACxF,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnB,OAAO,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,kCAAkC,CAAC,CAAA;YAChE,CAAC;YAED,yFAAyF;YACzF,6EAA6E;YAC7E,OAAO,EAAE,CACP,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,EACnF,OAAO,CAAC,WAAW,EAAE,CACtB,CAAA;QACH,CAAC,CAAC,CACL,CAAA;QAED,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;YACE,KAAK,EAAE,kBAAkB;YACzB,WAAW,EACT,mFAAmF;gBACnF,mFAAmF;gBACnF,6DAA6D;YAC/D,WAAW,EAAE;gBACX,IAAI,EAAE,IAAI;gBACV,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;gBACd,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;aACjE;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACvG,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAC9B,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,sFAAsF;YACtF,wFAAwF;YACxF,oCAAoC;YACpC,IAAI,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC,6DAA6D,CAAC,CAAA;YAExF,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;YAC9B,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,sCAAsC,CAAC,CAAA;YAEtF,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAA;YACzD,OAAO,EAAE,CAAC,WAAW,MAAM,CAAC,KAAK,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;QACpE,CAAC,CAAC,CACL,CAAA;IACH,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;YACE,KAAK,EAAE,iBAAiB;YACxB,WAAW,EACT,0FAA0F;gBAC1F,sFAAsF;gBACtF,0FAA0F;gBAC1F,oFAAoF;YACtF,WAAW,EAAE;gBACX,KAAK,EAAE,CAAC;qBACL,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;qBACxC,QAAQ,EAAE;qBACV,QAAQ,CAAC,yDAAyD,CAAC;gBACtE,KAAK,EAAE,CAAC;qBACL,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;qBACxC,QAAQ,EAAE;qBACV,QAAQ,CAAC,yEAAyE,CAAC;gBACtF,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;aACjH;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE;SACzG,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE,CAChD,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,EAAE,CAAC;gBACpD,OAAO,IAAI,CAAC,4DAA4D,CAAC,CAAA;YAC3E,CAAC;YAED,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,CAAA;YACjD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAA;YACxF,OAAO,EAAE,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAA;QAC5E,CAAC,CAAC,CACL,CAAA;IACH,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,CAAC,YAAY,CACjB,0BAA0B,EAC1B;YACE,KAAK,EAAE,aAAa;YACpB,WAAW,EACT,oFAAoF;gBACpF,sDAAsD;YACxD,WAAW,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE;YACxC,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;SACzD,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CACxB,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,eAAe,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,CACvG,CAAA;IACH,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;YACE,KAAK,EAAE,mCAAmC;YAC1C,WAAW,EACT,sFAAsF;gBACtF,wFAAwF;gBACxF,0FAA0F;gBAC1F,iFAAiF;YACnF,WAAW,EAAE;gBACX,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;gBAC5D,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;gBACxC,KAAK,EAAE,CAAC;qBACL,OAAO,EAAE;qBACT,QAAQ,EAAE;qBACV,QAAQ,CAAC,8EAA8E,CAAC;gBAC3F,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,EAAE;aACrE;YACD,WAAW,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE;SACxG,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CACzD,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE;gBACrD,KAAK;gBACL,gBAAgB,EAAE,gBAAgB,IAAI,KAAK,KAAK,SAAS;aAC1D,CAAC,CAAA;YAEF,IAAI,CAAC,KAAK;gBAAE,OAAO,EAAE,CAAC,MAAM,CAAC,CAAA;YAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,IAAI,CACT,qFAAqF;oBACnF,uDAAuD,CAC1D,CAAA;YACH,CAAC;YAED,MAAM,IAAI,GAAG,YAAY,IAAI,YAAY,CAAA;YACzC,MAAM,IAAI,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAEzF,CAAA;YAED,mFAAmF;YACnF,oFAAoF;YACpF,6EAA6E;YAC7E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE;gBACrD,MAAM;gBACN,YAAY,EAAE,IAAI;gBAClB,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;aAC5E,CAAC,CAAA;YAEF,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;QAC7B,CAAC,CAAC,CACL,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The shape every tool returns, and the one place a refusal is turned into text.
3
+ *
4
+ * MCP's own result type has an `isError` flag and a content array; what matters here is what goes
5
+ * into the array. SPEC.md's Business Logic §3 is the rule: a validation failure is the agent's
6
+ * control loop, so it is relayed whole, with its field paths intact, and never summarized. That is
7
+ * implemented by `ContentApiError.message` already carrying the whole body, so this module simply
8
+ * does not get in its way.
9
+ */
10
+ export type ToolResult = {
11
+ content: {
12
+ type: 'text';
13
+ text: string;
14
+ }[];
15
+ isError?: boolean;
16
+ [key: string]: unknown;
17
+ };
18
+ export declare function ok(value: unknown, context?: string): ToolResult;
19
+ export declare function fail(text: string): ToolResult;
20
+ /**
21
+ * Runs a tool body, turning any thrown error into a result rather than a transport-level failure.
22
+ *
23
+ * A thrown error reaches the agent as a protocol error rather than a tool result in some clients,
24
+ * which is a worse place for a message it is meant to act on — so everything lands in `isError`
25
+ * instead, where the text is unambiguously part of the conversation.
26
+ */
27
+ export declare function guard(run: () => Promise<ToolResult>): Promise<ToolResult>;
28
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB,CAAA;AAED,wBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAG/D;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAE7C;AAED;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAO/E"}
@@ -0,0 +1,26 @@
1
+ import { ContentApiError } from '../errors.js';
2
+ export function ok(value, context) {
3
+ const body = typeof value === 'string' ? value : JSON.stringify(value ?? null, null, 2);
4
+ return { content: [{ type: 'text', text: context ? `${context}\n${body}` : body }] };
5
+ }
6
+ export function fail(text) {
7
+ return { content: [{ type: 'text', text }], isError: true };
8
+ }
9
+ /**
10
+ * Runs a tool body, turning any thrown error into a result rather than a transport-level failure.
11
+ *
12
+ * A thrown error reaches the agent as a protocol error rather than a tool result in some clients,
13
+ * which is a worse place for a message it is meant to act on — so everything lands in `isError`
14
+ * instead, where the text is unambiguously part of the conversation.
15
+ */
16
+ export async function guard(run) {
17
+ try {
18
+ return await run();
19
+ }
20
+ catch (error) {
21
+ if (error instanceof ContentApiError)
22
+ return fail(error.message);
23
+ return fail(error instanceof Error ? error.message : String(error));
24
+ }
25
+ }
26
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/tools/result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAkB9C,MAAM,UAAU,EAAE,CAAC,KAAc,EAAE,OAAgB;IACjD,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACvF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAA;AACtF,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAA8B;IACxD,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,EAAE,CAAA;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,eAAe;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAChE,OAAO,IAAI,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IACrE,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@ebitex/content-mcp",
3
+ "version": "0.1.0",
4
+ "description": "An MCP server for ebitex Content: build and publish a content model from an AI agent.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "bin": {
11
+ "ebitex-content-mcp": "./dist/main.js"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "scripts": {
23
+ "prebuild": "node scripts/bundleHelp.mjs",
24
+ "build": "npm run prebuild && tsc -p tsconfig.build.json",
25
+ "lint": "oxlint",
26
+ "pretest": "node scripts/bundleHelp.mjs",
27
+ "test": "vitest run"
28
+ },
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.30.0",
31
+ "zod": "^3.25.76"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.0.0",
35
+ "oxlint": "^1.71.0",
36
+ "typescript": "~6.0.2",
37
+ "vitest": "^4.1.10"
38
+ }
39
+ }