@contentrain/mcp 1.11.0 → 2.0.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.
Files changed (40) hide show
  1. package/dist/{apply-manager-tRqDkzBV.mjs → apply-manager-a5l93F4O.mjs} +5 -5
  2. package/dist/apply-manager-a5l93F4O.mjs.map +1 -0
  3. package/dist/content-manager-BgxF3dHu.d.mts.map +1 -1
  4. package/dist/{context-CrS-IvVm.mjs → context-DjglDPvj.mjs} +2 -2
  5. package/dist/{context-CrS-IvVm.mjs.map → context-DjglDPvj.mjs.map} +1 -1
  6. package/dist/core/apply-manager.mjs +4 -4
  7. package/dist/core/content-manager.mjs +1 -1
  8. package/dist/core/context.mjs +2 -2
  9. package/dist/core/doctor.mjs +2 -2
  10. package/dist/core/model-manager.d.mts +13 -4
  11. package/dist/core/model-manager.d.mts.map +1 -1
  12. package/dist/core/model-manager.mjs +1 -1
  13. package/dist/core/ops/index.mjs +1 -1
  14. package/dist/core/validator/index.d.mts.map +1 -1
  15. package/dist/core/validator/index.mjs +2 -2
  16. package/dist/{doctor-BRPXxKhx.mjs → doctor-pw8EWRFR.mjs} +2 -2
  17. package/dist/{doctor-BRPXxKhx.mjs.map → doctor-pw8EWRFR.mjs.map} +1 -1
  18. package/dist/git/transaction.mjs +3 -3
  19. package/dist/index.mjs +9 -9
  20. package/dist/{local-DqmDeNxI.mjs → local-EJTxd5MR.mjs} +4 -4
  21. package/dist/{local-DqmDeNxI.mjs.map → local-EJTxd5MR.mjs.map} +1 -1
  22. package/dist/{model-manager-SiesTJrS.mjs → model-manager-DP2CZiMT.mjs} +92 -9
  23. package/dist/model-manager-DP2CZiMT.mjs.map +1 -0
  24. package/dist/{ops-B422KP_S.mjs → ops-CGKIKwDc.mjs} +2 -1
  25. package/dist/ops-CGKIKwDc.mjs.map +1 -0
  26. package/dist/providers/local/index.mjs +5 -5
  27. package/dist/server/http/index.mjs +9 -9
  28. package/dist/{server-DSEl2wzT.mjs → server-DKbnhtvV.mjs} +62 -18
  29. package/dist/server-DKbnhtvV.mjs.map +1 -0
  30. package/dist/server.mjs +9 -9
  31. package/dist/{transaction-BHOsiEn1.mjs → transaction-DC_dNON9.mjs} +2 -2
  32. package/dist/{transaction-BHOsiEn1.mjs.map → transaction-DC_dNON9.mjs.map} +1 -1
  33. package/dist/{validator-Ck0puppQ.mjs → validator-D5VncJ8M.mjs} +100 -82
  34. package/dist/validator-D5VncJ8M.mjs.map +1 -0
  35. package/package.json +2 -2
  36. package/dist/apply-manager-tRqDkzBV.mjs.map +0 -1
  37. package/dist/model-manager-SiesTJrS.mjs.map +0 -1
  38. package/dist/ops-B422KP_S.mjs.map +0 -1
  39. package/dist/server-DSEl2wzT.mjs.map +0 -1
  40. package/dist/validator-Ck0puppQ.mjs.map +0 -1
@@ -1,23 +1,23 @@
1
- import { i as planContentDelete, n as planModelDelete, r as planContentSave, t as planModelSave } from "./ops-B422KP_S.mjs";
1
+ import { i as planContentDelete, n as planModelDelete, r as planContentSave, t as planModelSave } from "./ops-CGKIKwDc.mjs";
2
2
  import { n as contentFilePath, t as contentDirPath } from "./paths-CmVw5Cw2.mjs";
3
3
  import { a as readJson, n as ensureDir, r as pathExists, s as writeJson, t as contentrainDir } from "./fs-DLbVB-Ek.mjs";
4
4
  import { a as writeMetaEntries, i as writeMeta, r as readMeta } from "./meta-manager-CJUiTgP2.mjs";
5
5
  import { n as readVocabulary, t as readConfig } from "./config-oxxgznz7.mjs";
6
- import { S as writeContent, _ as resolveMdFilePath, a as fieldDefZodSchema, c as validateModelDefinition, d as listContent, g as resolveLocaleStrategy, h as resolveJsonFilePath, l as writeModel, m as resolveContentDir, n as checkReferences, o as listModels, r as countEntries, s as readModel, u as deleteContent } from "./model-manager-SiesTJrS.mjs";
7
- import { n as readContext } from "./context-CrS-IvVm.mjs";
6
+ import { S as writeContent, _ as resolveMdFilePath, a as fieldDefZodSchema, c as validateModelDefinition, d as listContent, g as resolveLocaleStrategy, h as resolveJsonFilePath, l as writeModel, m as resolveContentDir, n as checkReferences, o as listModels, r as countEntries, s as readModel, u as deleteContent } from "./model-manager-DP2CZiMT.mjs";
7
+ import { n as readContext } from "./context-DjglDPvj.mjs";
8
8
  import { a as deleteRemoteBranch, c as pruneMergedRemoteBranches, i as cleanupMergedBranches, n as checkBranchHealth, s as listRemoteCrBranches } from "./branch-lifecycle-BAfgSQBv.mjs";
9
- import { i as mergeBranch, n as createTransaction, r as ensureContentBranch, t as buildBranchName } from "./transaction-BHOsiEn1.mjs";
10
- import { n as isMerged, t as LocalProvider } from "./local-DqmDeNxI.mjs";
9
+ import { i as mergeBranch, n as createTransaction, r as ensureContentBranch, t as buildBranchName } from "./transaction-DC_dNON9.mjs";
10
+ import { n as isMerged, t as LocalProvider } from "./local-EJTxd5MR.mjs";
11
11
  import { n as isToolAvailable, t as TOOL_REQUIREMENTS } from "./availability-sWWRz2W0.mjs";
12
12
  import { t as detectStack } from "./detect-wVSI9VuY.mjs";
13
13
  import { t as TOOL_ANNOTATIONS } from "./annotations-D3tlsF38.mjs";
14
14
  import { n as listTemplates, t as getTemplate } from "./templates-D6EllixQ.mjs";
15
- import { t as validateProject } from "./validator-Ck0puppQ.mjs";
15
+ import { t as validateProject } from "./validator-D5VncJ8M.mjs";
16
16
  import { t as OverlayReader } from "./overlay-reader-BOb105gS.mjs";
17
17
  import { t as buildGraph } from "./graph-builder-CRUX_8mA.mjs";
18
18
  import { n as scanSummary, t as scanCandidates } from "./scanner-VOwrKLGC.mjs";
19
- import { n as applyExtract, r as applyReuse } from "./apply-manager-tRqDkzBV.mjs";
20
- import { t as runDoctor } from "./doctor-BRPXxKhx.mjs";
19
+ import { n as applyExtract, r as applyReuse } from "./apply-manager-a5l93F4O.mjs";
20
+ import { t as runDoctor } from "./doctor-pw8EWRFR.mjs";
21
21
  import { join } from "node:path";
22
22
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23
23
  import { CONTENTRAIN_BRANCH } from "@contentrain/types";
@@ -830,7 +830,7 @@ function registerModelTools(server, provider, projectRoot) {
830
830
  }],
831
831
  isError: true
832
832
  };
833
- const errors = validateModel(input);
833
+ const { errors, warnings: schemaWarnings } = validateModel(input);
834
834
  if (errors.length > 0) return {
835
835
  content: [{
836
836
  type: "text",
@@ -924,6 +924,7 @@ function registerModelTools(server, provider, projectRoot) {
924
924
  valid: true,
925
925
  errors: []
926
926
  },
927
+ ...schemaWarnings.length > 0 ? { schema_warnings: schemaWarnings } : {},
927
928
  git: {
928
929
  branch,
929
930
  action: workflowAction,
@@ -934,7 +935,7 @@ function registerModelTools(server, provider, projectRoot) {
934
935
  content_path: contentPath + "/",
935
936
  ...displayPath ? { example_file: displayPath } : {},
936
937
  ...importSnippet ? { import_snippet: importSnippet } : {},
937
- next_steps: ["Add content with contentrain_content_save"]
938
+ next_steps: [...schemaWarnings.length > 0 ? ["REVIEW: see schema_warnings — some declared constraints are not enforced by MCP"] : [], "Add content with contentrain_content_save"]
938
939
  }, null, 2)
939
940
  }] };
940
941
  });
@@ -1133,6 +1134,19 @@ function registerContentTools(server, provider, projectRoot) {
1133
1134
  };
1134
1135
  }
1135
1136
  const entryIds = plan.result.map((r) => r.id ?? r.slug ?? r.locale).filter((v) => Boolean(v));
1137
+ const validationResult = await validateProject(new OverlayReader(provider, plan.changes), { model: input.model });
1138
+ const blocking = validationResult.issues.filter((i) => i.severity === "error" && touchesSavedEntries(i, plan.result));
1139
+ if (blocking.length > 0) return {
1140
+ content: [{
1141
+ type: "text",
1142
+ text: JSON.stringify({
1143
+ error: "Validation failed — nothing was written.",
1144
+ issues: blocking.map(describeIssue),
1145
+ hint: "Fix the values and call contentrain_content_save again. Nothing was committed, so there is no branch to clean up."
1146
+ }, null, 2)
1147
+ }],
1148
+ isError: true
1149
+ };
1136
1150
  const branch = buildBranchName("content", input.model);
1137
1151
  const message = `[contentrain] content: ${input.model}`;
1138
1152
  const contextPayload = {
@@ -1163,8 +1177,8 @@ function registerContentTools(server, provider, projectRoot) {
1163
1177
  isError: true
1164
1178
  };
1165
1179
  }
1166
- const validationResult = await validateProject(new OverlayReader(provider, plan.changes), { model: input.model });
1167
1180
  const allAdvisories = plan.result.flatMap((r) => r.advisories ?? []);
1181
+ const warnings = validationResult.issues.filter((i) => i.severity === "warning" && touchesSavedEntries(i, plan.result)).map((i) => `${i.field ?? i.locale}: ${i.message}`);
1168
1182
  return { content: [{
1169
1183
  type: "text",
1170
1184
  text: JSON.stringify({
@@ -1181,14 +1195,15 @@ function registerContentTools(server, provider, projectRoot) {
1181
1195
  advisories: allAdvisories,
1182
1196
  advisory_note: "Save succeeded. Review these warnings and consider consolidating duplicate values."
1183
1197
  } : {},
1198
+ ...warnings.length > 0 ? { warnings } : {},
1184
1199
  validation: {
1185
- valid: validationResult.valid,
1186
- errors: validationResult.issues.filter((i) => i.severity === "error").map((i) => i.message)
1200
+ valid: true,
1201
+ errors: []
1187
1202
  },
1188
1203
  context_updated: true,
1189
1204
  next_steps: [
1190
1205
  ...allAdvisories.length > 0 ? ["ADVISORY: Duplicate values detected — review advisories above"] : [],
1191
- ...!validationResult.valid ? ["WARNING: Content has validation errors — run contentrain_validate for details"] : [],
1206
+ ...warnings.length > 0 ? ["REVIEW: see warnings above — the save was not blocked by them"] : [],
1192
1207
  ...model.kind === "collection" ? ["Use contentrain_content_list to verify", "Add more entries or publish"] : ["Use contentrain_content_list to verify"]
1193
1208
  ]
1194
1209
  }, null, 2)
@@ -1345,6 +1360,35 @@ function registerContentTools(server, provider, projectRoot) {
1345
1360
  }
1346
1361
  });
1347
1362
  }
1363
+ /** Trim a validation issue to the fields that locate it, dropping empty ones. */
1364
+ function describeIssue(issue) {
1365
+ const out = {};
1366
+ if (issue.entry) out["entry"] = issue.entry;
1367
+ if (issue.slug) out["slug"] = issue.slug;
1368
+ if (issue.locale) out["locale"] = issue.locale;
1369
+ if (issue.field) out["field"] = issue.field;
1370
+ out["message"] = issue.message;
1371
+ return out;
1372
+ }
1373
+ /**
1374
+ * Does a validation issue belong to one of the entries this save is writing?
1375
+ *
1376
+ * The write gate blocks on errors, but `validateProject` inspects the whole model.
1377
+ * Without this filter, a pre-existing bad entry somewhere else in the model would
1378
+ * block an unrelated — and perfectly valid — save, leaving the caller stuck behind
1379
+ * a problem they did not create and may not be able to fix.
1380
+ *
1381
+ * Matching is by identity: entry ID for collections, slug for documents, and
1382
+ * locale alone for singletons and dictionaries, which have one record per locale.
1383
+ */
1384
+ function touchesSavedEntries(issue, results) {
1385
+ return results.some((r) => {
1386
+ if (issue.locale && r.locale && issue.locale !== r.locale) return false;
1387
+ if (r.id) return issue.entry === r.id;
1388
+ if (r.slug) return issue.slug === r.slug;
1389
+ return !issue.entry && !issue.slug;
1390
+ });
1391
+ }
1348
1392
  //#endregion
1349
1393
  //#region src/tools/workflow.ts
1350
1394
  /**
@@ -1391,9 +1435,9 @@ async function resolveMergeBranch(git, input) {
1391
1435
  }))).toSorted((left, right) => right.t - left.t)[0].b };
1392
1436
  }
1393
1437
  function registerWorkflowTools(server, provider, projectRoot) {
1394
- server.tool("contentrain_validate", "Validate project content against model schemas. Detects required field violations, type mismatches, broken relations, secret leaks, i18n parity issues, and more. If fix:true, auto-fixes structural issues (canonical sort, orphan meta, missing locale files) — do NOT manually edit .contentrain/ files.", {
1438
+ server.tool("contentrain_validate", "Validate project content against model schemas. Detects required field violations, type mismatches, broken relations, secret leaks, i18n parity issues, and more. If fix:true, auto-fixes structural issues (canonical sort, orphan meta, missing locale files, stray non-i18n meta layout) — do NOT manually edit .contentrain/ files.", {
1395
1439
  model: z.string().optional().describe("Model ID to validate (omit for all models)"),
1396
- fix: z.boolean().optional().describe("Auto-fix structural issues (canonical sort, orphan meta, missing locale files). Default: false")
1440
+ fix: z.boolean().optional().describe("Auto-fix structural issues (canonical sort, orphan meta, missing locale files, stray non-i18n meta layout). Default: false")
1397
1441
  }, TOOL_ANNOTATIONS["contentrain_validate"], async (input) => {
1398
1442
  if (input.fix && !projectRoot) return capabilityError("contentrain_validate", "localWorktree");
1399
1443
  if (!await readConfig(provider)) return {
@@ -2557,7 +2601,7 @@ function registerMediaTools(server, provider, _projectRoot) {
2557
2601
  }
2558
2602
  //#endregion
2559
2603
  //#region package.json
2560
- var version = "1.11.0";
2604
+ var version = "2.0.1";
2561
2605
  //#endregion
2562
2606
  //#region src/server.ts
2563
2607
  /**
@@ -2627,4 +2671,4 @@ function resolveServerContext(input) {
2627
2671
  //#endregion
2628
2672
  export { createServer as n, DEFAULT_INSTRUCTIONS as t };
2629
2673
 
2630
- //# sourceMappingURL=server-DSEl2wzT.mjs.map
2674
+ //# sourceMappingURL=server-DKbnhtvV.mjs.map