@contentrain/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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Contentrain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,274 @@
1
+ # @contentrain/mcp
2
+
3
+ MCP (Model Context Protocol) server for [Contentrain](https://contentrain.io) CMS content management. Enables AI agents (Claude, Codex, Cursor, etc.) and humans to create, read, update, and delete Contentrain models, content, and assets — with automatic git branch synchronization.
4
+
5
+ ## Why?
6
+
7
+ Contentrain is a git-based headless CMS. This MCP server lets AI assistants directly manage your Contentrain content through a standardized protocol, so you can say things like:
8
+
9
+ - *"Create a blog post model with title, excerpt, and cover image fields"*
10
+ - *"Add a new blog post in English and Turkish"*
11
+ - *"List all FAQ entries and update the second one"*
12
+
13
+ All changes are committed and pushed to your git repository automatically.
14
+
15
+ ## Prerequisites
16
+
17
+ - A Contentrain project already set up via the [Contentrain Web App](https://app.contentrain.io)
18
+ - The project's GitHub repository cloned locally
19
+ - Node.js >= 18
20
+
21
+ > **Important:** Contentrain projects must be initialized through the Web App first. The Web App creates the repository structure, the `contentrain` branch, models, and environment configuration. This MCP server operates on an existing Contentrain project — it does not replace the initial setup.
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Install
26
+
27
+ ```bash
28
+ # From npm (when published)
29
+ npm install -g @contentrain/mcp
30
+
31
+ # Or from source
32
+ git clone https://github.com/Contentrain/contentrain-mcp.git
33
+ cd contentrain-mcp
34
+ pnpm install && pnpm build
35
+ ```
36
+
37
+ ### 2. Configure your MCP client
38
+
39
+ Add to your MCP client configuration:
40
+
41
+ **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "contentrain": {
47
+ "command": "contentrain-mcp",
48
+ "env": {
49
+ "CONTENTRAIN_REPO_PATH": "/path/to/your/contentrain-project",
50
+ "CONTENTRAIN_BRANCH": "contentrain"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ **Cursor** (`.cursor/mcp.json` in your project root):
58
+
59
+ ```json
60
+ {
61
+ "mcpServers": {
62
+ "contentrain": {
63
+ "command": "contentrain-mcp",
64
+ "env": {
65
+ "CONTENTRAIN_REPO_PATH": "/path/to/your/contentrain-project",
66
+ "CONTENTRAIN_BRANCH": "contentrain"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ **Claude Code** (`.mcp.json` in your project root):
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "contentrain": {
79
+ "command": "contentrain-mcp",
80
+ "env": {
81
+ "CONTENTRAIN_REPO_PATH": "/path/to/your/contentrain-project",
82
+ "CONTENTRAIN_BRANCH": "contentrain"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ > If installed from source instead of globally, replace `"command": "contentrain-mcp"` with `"command": "node"` and add `"args": ["/absolute/path/to/contentrain-mcp/dist/index.mjs"]`.
90
+
91
+ ### 3. Start using
92
+
93
+ Once configured, your AI assistant has access to 14 tools. Here's an example conversation:
94
+
95
+ ```
96
+ You: List all my models
97
+ Agent: → calls contentrain_list_models
98
+ You have 3 models: blog (MD, localized), faq (JSON), authors (JSON)
99
+
100
+ You: Show me the blog model schema
101
+ Agent: → calls contentrain_describe_model { modelId: "blog" }
102
+ Blog model has fields: title (string, required), description (string),
103
+ category (one-to-one → blogcategories), imagesrc (media), author (one-to-one → authors)
104
+
105
+ You: Create a new blog post titled "Getting Started with Contentrain"
106
+ Agent: → calls contentrain_create_content {
107
+ modelId: "blog",
108
+ data: { title: "Getting Started with Contentrain", slug: "getting-started-with-contentrain", description: "..." },
109
+ locale: "en",
110
+ status: "draft",
111
+ content: "# Getting Started\n\nWelcome to Contentrain..."
112
+ }
113
+ Created entry e7f3a1b9c0d2 in blog model (draft).
114
+ Committed and pushed to contentrain branch.
115
+
116
+ You: Now publish it
117
+ Agent: → calls contentrain_update_content {
118
+ modelId: "blog",
119
+ entryId: "e7f3a1b9c0d2",
120
+ data: { status: "publish" },
121
+ locale: "en"
122
+ }
123
+ Updated and published.
124
+ ```
125
+
126
+ ## Environments
127
+
128
+ Contentrain uses **git branches** to manage environments. Each environment maps to a branch:
129
+
130
+ | Environment | Branch | Description |
131
+ |-------------|--------|-------------|
132
+ | Default | `contentrain` | Main content branch (created automatically) |
133
+ | Staging | `contentrain-staging` | Preview/staging environment |
134
+ | Production | `contentrain-production` | Production environment |
135
+
136
+ Environments are created via the Contentrain Web App. The branch naming follows the pattern `contentrain-{environment-name}`.
137
+
138
+ ### Targeting a specific environment
139
+
140
+ Set the `CONTENTRAIN_BRANCH` environment variable to the target environment's branch:
141
+
142
+ ```json
143
+ {
144
+ "mcpServers": {
145
+ "contentrain": {
146
+ "command": "contentrain-mcp",
147
+ "env": {
148
+ "CONTENTRAIN_REPO_PATH": "/path/to/project",
149
+ "CONTENTRAIN_BRANCH": "contentrain-staging"
150
+ }
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ ### Multiple environments simultaneously
157
+
158
+ You can register multiple MCP server instances — one per environment:
159
+
160
+ ```json
161
+ {
162
+ "mcpServers": {
163
+ "contentrain-default": {
164
+ "command": "contentrain-mcp",
165
+ "env": {
166
+ "CONTENTRAIN_REPO_PATH": "/path/to/project",
167
+ "CONTENTRAIN_BRANCH": "contentrain"
168
+ }
169
+ },
170
+ "contentrain-staging": {
171
+ "command": "contentrain-mcp",
172
+ "env": {
173
+ "CONTENTRAIN_REPO_PATH": "/path/to/project",
174
+ "CONTENTRAIN_BRANCH": "contentrain-staging"
175
+ }
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ ## Available Tools
182
+
183
+ ### Model Management
184
+
185
+ | Tool | Description |
186
+ |------|-------------|
187
+ | `contentrain_list_models` | List all models with metadata |
188
+ | `contentrain_describe_model` | Get full schema with field definitions |
189
+ | `contentrain_create_model` | Create a new model (JSON, MD, or MDX) |
190
+ | `contentrain_add_field` | Add a field to an existing model |
191
+ | `contentrain_delete_model` | Delete a model and all its content |
192
+
193
+ ### Content Management
194
+
195
+ | Tool | Description |
196
+ |------|-------------|
197
+ | `contentrain_list_content` | List all entries for a model |
198
+ | `contentrain_get_content` | Get a single entry by ID |
199
+ | `contentrain_create_content` | Create a new entry with validation |
200
+ | `contentrain_update_content` | Update specific fields of an entry |
201
+ | `contentrain_delete_content` | Delete an entry (all locales + markdown) |
202
+ | `contentrain_validate` | Dry-run validation against model schema |
203
+
204
+ ### Asset Management
205
+
206
+ | Tool | Description |
207
+ |------|-------------|
208
+ | `contentrain_list_assets` | List all registered assets |
209
+ | `contentrain_register_asset` | Register an existing file as an asset |
210
+ | `contentrain_deregister_asset` | Remove an asset from the registry |
211
+
212
+ ## Environment Variables
213
+
214
+ | Variable | Default | Description |
215
+ |----------|---------|-------------|
216
+ | `CONTENTRAIN_REPO_PATH` | `process.cwd()` | Path to the git repository |
217
+ | `CONTENTRAIN_BRANCH` | `contentrain` | Target environment branch |
218
+ | `CONTENTRAIN_REMOTE` | `origin` | Git remote name |
219
+ | `CONTENTRAIN_DIR` | `contentrain` | Contentrain directory name |
220
+ | `CONTENTRAIN_DRY_RUN` | `false` | Skip git operations (local changes only) |
221
+ | `CONTENTRAIN_AUTHOR_NAME` | — | Git commit author name |
222
+ | `CONTENTRAIN_AUTHOR_EMAIL` | — | Git commit author email |
223
+
224
+ ## Programmatic Usage
225
+
226
+ You can also use the writer directly in your own code:
227
+
228
+ ```typescript
229
+ import { ContentrainWriter } from '@contentrain/mcp'
230
+
231
+ const writer = new ContentrainWriter({
232
+ repoPath: '/path/to/your/project',
233
+ branch: 'contentrain',
234
+ })
235
+
236
+ // Read operations (no git transaction needed)
237
+ const models = await writer.model.list()
238
+ const entries = await writer.content.list('blog-posts')
239
+
240
+ // Write operations (uses git worktree + commit + push)
241
+ await writer.transaction(async (tx) => {
242
+ await tx.content.create('blog-posts', {
243
+ title: 'Hello World',
244
+ excerpt: 'My first post',
245
+ }, { status: 'draft' })
246
+ }, { message: 'add new blog post' })
247
+ ```
248
+
249
+ ## How Git Sync Works
250
+
251
+ Every write operation runs inside a **transaction**:
252
+
253
+ 1. Creates an isolated git worktree from the target branch
254
+ 2. Applies all changes inside the worktree
255
+ 3. Commits and pushes to the target branch
256
+ 4. If the push is rejected (remote advanced), performs a **structural 3-way merge** — diffing JSON entries by their `ID` field instead of text lines — then retries (up to 3 attempts)
257
+ 5. Cleans up the worktree
258
+
259
+ This means concurrent writes from multiple agents or the Contentrain web app won't conflict.
260
+
261
+ ## Development
262
+
263
+ ```bash
264
+ pnpm install # Install dependencies
265
+ pnpm dev # Watch mode build
266
+ pnpm test # Run tests (watch mode)
267
+ pnpm test:run # Run tests once
268
+ pnpm lint # Lint with oxlint
269
+ pnpm build # Production build
270
+ ```
271
+
272
+ ## License
273
+
274
+ MIT
@@ -0,0 +1,75 @@
1
+ import { access, mkdir, readdir, rm, unlink } from "node:fs/promises";
2
+ //#region \0rolldown/runtime.js
3
+ var __defProp = Object.defineProperty;
4
+ var __exportAll = (all, no_symbols) => {
5
+ let target = {};
6
+ for (var name in all) __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true
9
+ });
10
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
11
+ return target;
12
+ };
13
+ //#endregion
14
+ //#region src/util/fs.ts
15
+ var fs_exports = /* @__PURE__ */ __exportAll({
16
+ ensureDir: () => ensureDir,
17
+ fileExists: () => fileExists,
18
+ listJsonFiles: () => listJsonFiles,
19
+ removeDir: () => removeDir,
20
+ removeFile: () => removeFile
21
+ });
22
+ /**
23
+ * Ensure a directory exists, creating it recursively if needed.
24
+ */
25
+ async function ensureDir(dirPath) {
26
+ await mkdir(dirPath, { recursive: true });
27
+ }
28
+ /**
29
+ * Check if a file exists.
30
+ */
31
+ async function fileExists(filePath) {
32
+ try {
33
+ await access(filePath);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+ /**
40
+ * Remove a file if it exists.
41
+ */
42
+ async function removeFile(filePath) {
43
+ try {
44
+ await unlink(filePath);
45
+ } catch (err) {
46
+ if (err instanceof Error && "code" in err && err.code !== "ENOENT") throw err;
47
+ }
48
+ }
49
+ /**
50
+ * Remove a directory recursively.
51
+ */
52
+ async function removeDir(dirPath) {
53
+ try {
54
+ await rm(dirPath, {
55
+ recursive: true,
56
+ force: true
57
+ });
58
+ } catch (err) {
59
+ if (err instanceof Error && "code" in err && err.code !== "ENOENT") throw err;
60
+ }
61
+ }
62
+ /**
63
+ * List JSON files in a directory (returns filenames without extension).
64
+ */
65
+ async function listJsonFiles(dirPath) {
66
+ try {
67
+ return (await readdir(dirPath)).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
68
+ } catch {
69
+ return [];
70
+ }
71
+ }
72
+ //#endregion
73
+ export { removeDir as a, listJsonFiles as i, fileExists as n, removeFile as o, fs_exports as r, ensureDir as t };
74
+
75
+ //# sourceMappingURL=fs-D83-Ii_y.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs-D83-Ii_y.mjs","names":[],"sources":["../src/util/fs.ts"],"sourcesContent":["import { mkdir, access, unlink, readdir, rm } from 'node:fs/promises'\n\n/**\n * Ensure a directory exists, creating it recursively if needed.\n */\nexport async function ensureDir(dirPath: string): Promise<void> {\n await mkdir(dirPath, { recursive: true })\n}\n\n/**\n * Check if a file exists.\n */\nexport async function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath)\n return true\n }\n catch {\n return false\n }\n}\n\n/**\n * Remove a file if it exists.\n */\nexport async function removeFile(filePath: string): Promise<void> {\n try {\n await unlink(filePath)\n }\n catch (err: unknown) {\n if (err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err\n }\n }\n}\n\n/**\n * Remove a directory recursively.\n */\nexport async function removeDir(dirPath: string): Promise<void> {\n try {\n await rm(dirPath, { recursive: true, force: true })\n }\n catch (err: unknown) {\n if (err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err\n }\n }\n}\n\n/**\n * List JSON files in a directory (returns filenames without extension).\n */\nexport async function listJsonFiles(dirPath: string): Promise<string[]> {\n try {\n const files = await readdir(dirPath)\n return files\n .filter(f => f.endsWith('.json'))\n .map(f => f.replace('.json', ''))\n }\n catch {\n return []\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAKA,eAAsB,UAAU,SAAgC;AAC9D,OAAM,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;;;;;AAM3C,eAAsB,WAAW,UAAoC;AACnE,KAAI;AACF,QAAM,OAAO,SAAS;AACtB,SAAO;SAEH;AACJ,SAAO;;;;;;AAOX,eAAsB,WAAW,UAAiC;AAChE,KAAI;AACF,QAAM,OAAO,SAAS;UAEjB,KAAc;AACnB,MAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,SACnF,OAAM;;;;;;AAQZ,eAAsB,UAAU,SAAgC;AAC9D,KAAI;AACF,QAAM,GAAG,SAAS;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;UAE9C,KAAc;AACnB,MAAI,eAAe,SAAS,UAAU,OAAQ,IAA8B,SAAS,SACnF,OAAM;;;;;;AAQZ,eAAsB,cAAc,SAAoC;AACtE,KAAI;AAEF,UADc,MAAM,QAAQ,QAAQ,EAEjC,QAAO,MAAK,EAAE,SAAS,QAAQ,CAAC,CAChC,KAAI,MAAK,EAAE,QAAQ,SAAS,GAAG,CAAC;SAE/B;AACJ,SAAO,EAAE"}
@@ -0,0 +1,272 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+
3
+ //#region src/types/index.d.ts
4
+ interface ModelMetadata {
5
+ name: string;
6
+ description?: string;
7
+ modelId: string;
8
+ path?: string;
9
+ localization: boolean;
10
+ type: 'JSON' | 'MD' | 'MDX';
11
+ createdBy?: string;
12
+ isServerless: boolean;
13
+ }
14
+ interface FieldDefinition {
15
+ name: string;
16
+ description?: string;
17
+ fieldId: string;
18
+ modelId: string;
19
+ componentId: ComponentId;
20
+ fieldType: FieldType;
21
+ options: FieldOptions;
22
+ validations: FieldValidations;
23
+ system: boolean;
24
+ defaultField: boolean;
25
+ }
26
+ type FieldType = 'string' | 'number' | 'boolean' | 'array' | 'date' | 'media' | 'relation';
27
+ type ComponentId = 'single-line-text' | 'multi-line-text' | 'email' | 'url' | 'slug' | 'color' | 'json' | 'md-editor' | 'rich-text-editor' | 'integer' | 'decimal' | 'rating' | 'percent' | 'phone-number' | 'checkbox' | 'switch' | 'date' | 'date-time' | 'media' | 'one-to-one' | 'one-to-many';
28
+ interface FieldOptions {
29
+ 'title-field'?: {
30
+ value: boolean;
31
+ };
32
+ 'default-value'?: {
33
+ value: boolean;
34
+ form?: Record<string, {
35
+ value: unknown;
36
+ }>;
37
+ };
38
+ reference?: {
39
+ value: boolean;
40
+ form?: {
41
+ reference: {
42
+ value: string;
43
+ };
44
+ };
45
+ };
46
+ [key: string]: unknown;
47
+ }
48
+ interface FieldValidations {
49
+ 'required-field'?: {
50
+ value: boolean;
51
+ };
52
+ 'unique-field'?: {
53
+ value: boolean;
54
+ };
55
+ [key: string]: unknown;
56
+ }
57
+ interface ContentEntry {
58
+ ID: string;
59
+ createdAt: string;
60
+ updatedAt: string;
61
+ status: 'draft' | 'publish' | 'changed';
62
+ scheduled?: boolean;
63
+ [key: string]: unknown;
64
+ }
65
+ interface MDContentEntry extends ContentEntry {
66
+ slug: string;
67
+ path?: string;
68
+ }
69
+ interface AssetEntry {
70
+ path: string;
71
+ mimetype: string;
72
+ size: number;
73
+ alt?: string;
74
+ meta?: {
75
+ user?: {
76
+ name: string;
77
+ email: string;
78
+ avatar?: string;
79
+ };
80
+ createdAt?: string;
81
+ updatedAt?: string;
82
+ };
83
+ }
84
+ interface ContentrainConfig {
85
+ assetsPath: string;
86
+ }
87
+ interface WriterConfig {
88
+ repoPath: string;
89
+ branch: string;
90
+ contentrainDir?: string;
91
+ remote?: string;
92
+ author?: {
93
+ name: string;
94
+ email: string;
95
+ };
96
+ commitPrefix?: string;
97
+ retryAttempts?: number;
98
+ dryRun?: boolean;
99
+ }
100
+ interface WorktreeContext {
101
+ path: string;
102
+ branch: string;
103
+ id: string;
104
+ }
105
+ interface TransactionOptions {
106
+ message?: string;
107
+ }
108
+ interface ValidationResult {
109
+ valid: boolean;
110
+ errors: ValidationFieldError[];
111
+ }
112
+ interface ValidationFieldError {
113
+ fieldId: string;
114
+ message: string;
115
+ rule: string;
116
+ }
117
+ interface ToolResult {
118
+ success: boolean;
119
+ data?: unknown;
120
+ error?: string;
121
+ }
122
+ //#endregion
123
+ //#region src/server.d.ts
124
+ declare function createServer(config: WriterConfig): McpServer;
125
+ //#endregion
126
+ //#region src/core/model/manager.d.ts
127
+ declare class ModelManager {
128
+ private basePath;
129
+ constructor(basePath: string);
130
+ private get modelsDir();
131
+ private get metadataPath();
132
+ list(): Promise<ModelMetadata[]>;
133
+ get(modelId: string): Promise<ModelMetadata>;
134
+ getFields(modelId: string): Promise<FieldDefinition[]>;
135
+ describe(modelId: string): Promise<{
136
+ model: ModelMetadata;
137
+ fields: FieldDefinition[];
138
+ customFields: FieldDefinition[];
139
+ }>;
140
+ create(input: {
141
+ name: string;
142
+ description?: string;
143
+ modelId: string;
144
+ localization: boolean;
145
+ type: 'JSON' | 'MD' | 'MDX';
146
+ path?: string;
147
+ fields?: Omit<FieldDefinition, 'modelId' | 'system' | 'defaultField'>[];
148
+ }): Promise<ModelMetadata>;
149
+ addField(modelId: string, field: Omit<FieldDefinition, 'modelId' | 'system' | 'defaultField'>): Promise<FieldDefinition>;
150
+ removeField(modelId: string, fieldId: string): Promise<void>;
151
+ delete(modelId: string): Promise<void>;
152
+ }
153
+ //#endregion
154
+ //#region src/core/content/manager.d.ts
155
+ declare class ContentManager {
156
+ private basePath;
157
+ private repoPath;
158
+ private modelManager;
159
+ private markdownSync;
160
+ constructor(basePath: string, repoPath: string);
161
+ private contentDir;
162
+ private contentFilePath;
163
+ list(modelId: string, locale?: string): Promise<ContentEntry[]>;
164
+ get(modelId: string, entryId: string, locale?: string): Promise<ContentEntry>;
165
+ getLocales(modelId: string): Promise<string[]>;
166
+ create(modelId: string, data: Record<string, unknown>, options?: {
167
+ locale?: string;
168
+ id?: string;
169
+ status?: 'draft' | 'publish';
170
+ content?: string;
171
+ }): Promise<string>;
172
+ update(modelId: string, entryId: string, data: Record<string, unknown>, options?: {
173
+ locale?: string;
174
+ content?: string;
175
+ }): Promise<ContentEntry>;
176
+ delete(modelId: string, entryId: string): Promise<void>;
177
+ private resolveContentPath;
178
+ private createLocalized;
179
+ private appendEntry;
180
+ private deleteFromFile;
181
+ }
182
+ //#endregion
183
+ //#region src/core/asset/manager.d.ts
184
+ declare class AssetManager {
185
+ private basePath;
186
+ private repoPath;
187
+ private config;
188
+ private assetsPath;
189
+ constructor(basePath: string, repoPath: string, config: WriterConfig);
190
+ list(): Promise<AssetEntry[]>;
191
+ get(assetPath: string): Promise<AssetEntry | undefined>;
192
+ register(input: {
193
+ path: string;
194
+ mimetype: string;
195
+ alt?: string;
196
+ }): Promise<AssetEntry>;
197
+ deregister(assetPath: string): Promise<void>;
198
+ update(assetPath: string, updates: {
199
+ alt?: string;
200
+ mimetype?: string;
201
+ }): Promise<AssetEntry>;
202
+ }
203
+ //#endregion
204
+ //#region src/core/validation/validator.d.ts
205
+ declare class Validator {
206
+ private basePath;
207
+ private modelManager;
208
+ constructor(basePath: string);
209
+ /**
210
+ * Validate content data against model field definitions.
211
+ */
212
+ validate(modelId: string, data: Record<string, unknown>, options?: {
213
+ isUpdate?: boolean;
214
+ }): Promise<ValidationResult>;
215
+ /**
216
+ * Validate ID format.
217
+ */
218
+ validateId(id: string): ValidationResult;
219
+ /**
220
+ * Validate status value.
221
+ */
222
+ validateStatus(status: string): ValidationResult;
223
+ private checkRelationIntegrity;
224
+ private checkAssetExists;
225
+ private checkUniqueness;
226
+ }
227
+ //#endregion
228
+ //#region src/core/writer.d.ts
229
+ /**
230
+ * Main orchestrator — coordinates all managers and git sync.
231
+ *
232
+ * Three modes:
233
+ * 1. Read mode: reads from repoPath directly (no git sync)
234
+ * 2. Transaction mode: creates a worktree, applies changes, commits + pushes
235
+ * 3. Dry-run mode: copies contentrain dir to temp, applies changes there, then discards
236
+ */
237
+ declare class ContentrainWriter {
238
+ private config;
239
+ private contentrainDir;
240
+ readonly model: ModelManager;
241
+ readonly content: ContentManager;
242
+ readonly asset: AssetManager;
243
+ readonly validator: Validator;
244
+ private gitSync;
245
+ constructor(config: WriterConfig);
246
+ /**
247
+ * Execute operations in a git-synced transaction.
248
+ * Creates worktree → runs operations → validates → commits → pushes.
249
+ * In dry-run mode, copies to a temp directory and discards after execution.
250
+ */
251
+ transaction<T>(fn: (managers: TransactionManagers) => Promise<T>, options?: {
252
+ message?: string;
253
+ }): Promise<T>;
254
+ /**
255
+ * Dry-run: copy contentrain dir to temp, run operations there, then discard.
256
+ * The original repository is never modified.
257
+ */
258
+ private dryRunTransaction;
259
+ /**
260
+ * Create managers for a specific repo path (used in transactions).
261
+ */
262
+ private createManagers;
263
+ }
264
+ interface TransactionManagers {
265
+ model: ModelManager;
266
+ content: ContentManager;
267
+ asset: AssetManager;
268
+ validator: Validator;
269
+ }
270
+ //#endregion
271
+ export { AssetEntry, ComponentId, ContentEntry, ContentrainConfig, ContentrainWriter, FieldDefinition, FieldOptions, FieldType, FieldValidations, MDContentEntry, ModelMetadata, ToolResult, TransactionOptions, ValidationFieldError, ValidationResult, WorktreeContext, WriterConfig, createServer };
272
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/index.ts","../src/server.ts","../src/core/model/manager.ts","../src/core/content/manager.ts","../src/core/asset/manager.ts","../src/core/validation/validator.ts","../src/core/writer.ts"],"mappings":"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"}