@hanna84/mcp-writing 1.9.3 → 1.9.4

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 (3) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +8 -629
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,11 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ #### [v1.9.4](https://github.com/hannasdev/mcp-writing.git
8
+ /compare/v1.9.3...v1.9.4)
9
+
10
+ - docs: split README into focused topic guides [`#56`](https://github.com/hannasdev/mcp-writing.git
11
+ /pull/56)
12
+
7
13
  #### [v1.9.3](https://github.com/hannasdev/mcp-writing.git
8
14
  /compare/v1.9.2...v1.9.3)
9
15
 
16
+ > 20 April 2026
17
+
10
18
  - docs: add data ownership model section to README [`#52`](https://github.com/hannasdev/mcp-writing.git
11
19
  /pull/52)
20
+ - Release 1.9.3 [`7b2b94a`](https://github.com/hannasdev/mcp-writing.git
21
+ /commit/7b2b94a4ad77488cab1a6215b001d925f9e7cdc5)
12
22
 
13
23
  #### [v1.9.2](https://github.com/hannasdev/mcp-writing.git
14
24
  /compare/v1.9.1...v1.9.2)
package/README.md CHANGED
@@ -20,294 +20,17 @@ Instead of feeding an entire manuscript to an AI and hoping it fits in the conte
20
20
  - AI-assisted editing workflows where you want targeted context retrieval instead of full-manuscript prompting.
21
21
  - Projects that need traceable, reversible edits with metadata that stays synchronized as drafts evolve.
22
22
 
23
- ## Prerequisites
23
+ ## Documentation
24
24
 
25
- - **Node.js 22.6.0 or later** (required for SQLite support via `--experimental-sqlite` flag)
26
- - **npm 8.0.0 or later**
27
- - **Git** (for edit snapshots and version history)
28
-
29
- Verify your setup:
30
-
31
- ```sh
32
- node --version # should be v22.6.0 or later
33
- npm --version # should be 8.0.0 or later
34
- git --version # should be installed
35
- ```
36
-
37
- ## Permission Contract (Recommended For All Users)
38
-
39
- To keep MCP write tools reliable across local runs, Docker, and AI agents, use this contract:
40
-
41
- 1. The same non-root user should own and write the sync directory.
42
- 2. Containerized runs should use host UID/GID, not root.
43
- 3. If ownership drifts (for example root-owned files), repair once on host and continue.
44
-
45
- Repair commands (host):
46
-
47
- ```sh
48
- sudo chown -R "$(id -u):$(id -g)" /path/to/sync-dir
49
- find /path/to/sync-dir -type d -exec chmod u+rwx {} +
50
- find /path/to/sync-dir -type f -exec chmod u+rw {} +
51
- ```
52
-
53
- You can also inspect ownership/writability status at runtime via `get_runtime_config`.
54
-
55
- ## First-time setup path (recommended)
56
-
57
- If this is your first time, follow these steps in order:
58
-
59
- 1. Start with **Quick start with Scrivener** (or use **Running with Docker** if that is your preferred setup).
60
- 2. Start the server.
61
- 3. Verify the server (`/healthz` and `/sse`).
62
- 4. Run `import_scrivener_sync` with `dry_run: true` first to preview what will happen.
63
- 5. Run it again with `dry_run: false` to write files. Keep `auto_sync: true` (default) so your scenes are indexed immediately.
64
-
65
- Once this is working, you can come back to:
66
-
67
- - **Advanced: Native sync format** for custom project layouts
68
- - **Reference: Available tools** for the full tool catalog
69
- - **Appendix: Real-world usage scenarios** for workflow ideas
70
-
71
- ## Quick start with Scrivener
72
-
73
- If you write in [Scrivener](https://www.literatureandlatte.com/scrivener), this gives you the smoothest path to get started.
74
-
75
- ### 1. Export from Scrivener
76
-
77
- In Scrivener, go to **File → Sync → With External Folder**. Set the format to **plain text** (`.txt`) and choose an output folder, for example `~/my-novel-txt/`.
78
-
79
- Only `Draft/` is imported automatically.
80
-
81
- ### 2. Start mcp-writing
82
-
83
- ```sh
84
- WRITING_SYNC_DIR=/path/to/sync-dir DB_PATH=./writing.db npm start
85
- ```
86
-
87
- You should see:
88
-
89
- ```sh
90
- Listening on port 3000
91
- Sync dir: /path/to/sync-dir
92
- Database: ./writing.db
93
- ```
94
-
95
- ### 3. Verify the server
96
-
97
- - Open `http://localhost:3000/healthz` and confirm it returns OK.
98
- - Open `http://localhost:3000/sse` and confirm it opens an SSE stream.
99
-
100
- ### 4. Import Draft scenes through MCP (recommended)
101
-
102
- From your MCP client, call `import_scrivener_sync` with:
103
-
104
- ```json
105
- {
106
- "source_dir": "/Users/yourname/my-novel-txt",
107
- "project_id": "my-novel",
108
- "dry_run": true
109
- }
110
- ```
111
-
112
- > **Note:** use a full absolute path for `source_dir`. Shell shortcuts like `~` are not expanded by Node.js.
113
-
114
- If the preview looks right, run it again with writes enabled:
115
-
116
- ```json
117
- {
118
- "source_dir": "/Users/yourname/my-novel-txt",
119
- "project_id": "my-novel",
120
- "dry_run": false,
121
- "auto_sync": true
122
- }
123
- ```
124
-
125
- The importer:
126
-
127
- - Converts `Draft/` files to scene sidecars (`.meta.yaml`) with generated `scene_id`, `title`, `timeline_position`, `external_source`, `external_id`, and carried `save_the_cat_beat` where applicable.
128
- - Skips beat-marker files (`-Setup-`, `-Catalyst-`, etc.), chapter-intro files, epigraphs, and trashed files.
129
- - Reconciles updates by stable Scrivener binder ID (`[123]` in filenames) so reorder/move operations map to existing scenes.
130
-
131
- Non-draft content is not inferred from `Notes/`. Put it directly into the target sync dir using the `world/` folder conventions described below.
132
-
133
- ### 5. Optional: CLI fallback import
134
-
135
- If you prefer to run the import from the command line, use:
136
-
137
- ```sh
138
- node scripts/import.js ~/my-novel-txt /path/to/sync-dir --project my-novel
139
- ```
140
-
141
- Then call `sync` once.
142
-
143
- ### 6. Lint your metadata (optional)
144
-
145
- ```sh
146
- node scripts/lint-metadata.mjs --sync-dir /path/to/sync-dir
147
- ```
148
-
149
- This exits with a non-zero code if it finds errors. Warnings (for example `UNKNOWN_KEY`) are informational.
150
-
151
- ---
152
-
153
- ## Advanced: Native sync format
154
-
155
- For projects not starting from a Scrivener export, place plain `.md` files in the sync folder directly. Metadata lives in a YAML frontmatter block.
156
-
157
- ### Scene file example
158
-
159
- ```markdown
160
- ---
161
- scene_id: p1-ch2-sc3
162
- title: The Arrival
163
- part: 1
164
- chapter: 2
165
- characters: [elena, marcus]
166
- places: [harbor-district]
167
- logline: Elena arrives at the harbor and meets Marcus for the first time.
168
- save_the_cat_beat: Setup
169
- pov: elena
170
- timeline_position: 4
171
- story_time: "Day 1, morning"
172
- tags: [first-meeting, tension]
173
- ---
174
-
175
- Prose starts here...
176
- ```
177
-
178
- Alternatively, metadata can live in a sidecar file named `<scene-file>.meta.yaml` alongside the prose file — useful for keeping the prose file clean.
179
-
180
- ### Project structure
181
-
182
- ```bash
183
- /sync-root/
184
- /universes/
185
- /my-series/
186
- /world/
187
- characters/elena/sheet.md
188
- characters/elena/arc.md
189
- places/harbor-district/sheet.md
190
- reference/vampire-biology.md
191
- /book-1/
192
- /part-1/chapter-1/scene-001.md
193
- /projects/
194
- /standalone-novel/
195
- /world/
196
- characters/
197
- places/
198
- reference/
199
- /part-1/chapter-1/scene-001.md
200
- ```
201
-
202
- Character/place folders use one canonical sheet file for entity indexing:
203
-
204
- - `world/characters/<slug>/sheet.md` or `sheet.txt`
205
- - `world/places/<slug>/sheet.md` or `sheet.txt`
206
-
207
- Additional files in the same folder are treated as support notes, not separate entities.
208
-
209
- Universe-level characters and places are shared across all books in that universe. Standalone projects are fully isolated.
210
-
211
- ### Scaffolding templates
212
-
213
- Yes, a template is helpful here. It keeps the canonical metadata fields consistent, lowers the friction of adding a new character or place, and reduces the chance that a file is created in the right folder but missing the fields needed for indexing.
214
-
215
- Use the scaffold script to create a canonical character or place folder:
216
-
217
- ```sh
218
- npm run new:entity -- --sync-dir /path/to/sync-root --kind character --scope universe --universe my-series --name "Mira Nystrom"
219
- ```
220
-
221
- ```sh
222
- npm run new:entity -- --sync-dir /path/to/sync-root --kind place --scope project --project my-series/book-1 --name "University Hospital"
223
- ```
224
-
225
- This creates:
226
-
227
- - `world/characters/<slug>/sheet.md` plus `arc.md` for character arcs
228
- - `world/places/<slug>/sheet.md` for places
229
- - `sheet.meta.yaml` with the required entity ID and starter fields
230
-
231
- Generated Markdown follows one formatting contract so scaffolded files are predictable to edit:
232
-
233
- - the first line is a top-level title (`# Name`)
234
- - every heading is followed by a blank line
235
- - every generated `.md` file ends with a trailing blank line
236
-
237
- Use `--dry-run` to preview the path without writing files.
238
-
239
- You can also create canonical sheets directly through the MCP server with `create_character_sheet` and `create_place_sheet`, then move your existing raw notes into the generated folders.
240
-
241
- Recommended workflow:
242
-
243
- 1. Scaffold the entity folder and canonical sheet.
244
- 2. Fill in the metadata fields you care about first.
245
- 3. For characters, fill in `arc.md` as a separate arc-analysis document.
246
- 4. Add any other nearby notes like `relationships.md` or `history.md` as needed.
247
- 5. Run `sync` to index the new entity.
248
-
249
- ---
250
-
251
- ## Data ownership model
252
-
253
- Two separate rulesets apply depending on whether a file lives under `scenes/` (import-managed) or `world/` (human/agent-managed). Mixing writers outside these rules risks silent data loss.
254
-
255
- ### scenes/ — import-managed files
256
-
257
- The importer is the authoritative writer for Scrivener-imported prose; any edits to `scenes/**/*.md` (manual or via tools) will be overwritten on re-import. Sidecars are shared but with clearly partitioned fields.
258
-
259
- | File | Writer | Fields written | Behavior on re-import |
260
- |---|---|---|---|
261
- | `scenes/**/*.md` | **Scrivener / importer** | Full prose content | **Unconditionally overwritten.** Never edit `.md` files directly — changes will be lost on the next import. |
262
- | `scenes/**/*.meta.yaml` | **Importer** (Scrivener fields) + **AI agent** (enrichment fields) | Importer writes: `scene_id`, `external_source`, `external_id` (always); `title`, `timeline_position`, `save_the_cat_beat` (from Scrivener metadata, also writable by agents via `update_scene_metadata`) | Importer spreads existing sidecar first, then overlays only its fields. All other fields (logline, status, tags, characters, notes, flags, …) are preserved across re-imports. |
263
-
264
- **Rule:** write AI-side fields via the appropriate tool — never touch the Scrivener-controlled fields manually or the importer will overwrite them.
265
- - `update_scene_metadata` supports: `logline`, `status`, `tags`, `characters`, `places`, `pov`, `part`, `chapter`, `timeline_position`, `story_time`, `save_the_cat_beat`, `title`.
266
- - `flag_scene` appends accumulating continuity/review notes (free-text `flags` list).
267
- - `enrich_scene` re-derives lightweight metadata from the current prose and clears staleness.
268
- - `metadata_stale` is a SQLite-only flag set automatically by sync when prose changes — it is not a sidecar field and cannot be written by tools.
269
-
270
- ### sync — read-only with respect to files
271
-
272
- `sync` reads files and writes only to SQLite. It never touches `.md` prose. The one exception is auto-migration: if a `.md` file has YAML frontmatter but no sidecar yet, sync will create the `.meta.yaml` from the frontmatter (one-time, non-destructive). After that, the sidecar is the source of truth and frontmatter is ignored.
273
-
274
- | Operation | Reads | Writes |
275
- |---|---|---|
276
- | Indexing pass | `scenes/**/*.md`, `scenes/**/*.meta.yaml`, `world/**/*.md`, `world/**/*.meta.yaml` | SQLite only |
277
- | Frontmatter auto-migration | Any `.md`/`.txt` file (frontmatter block) | Corresponding `.meta.yaml` (created once if missing, for any file type including `world/**`) |
278
-
279
- `sync` never overwrites an existing sidecar and never touches a `.md` prose file.
280
-
281
- ### world/ — human/agent-managed files
282
-
283
- The importer never reads or writes anything under `world/`. These files are fully owned by humans and the AI agent and are safe to edit at any time without import risk.
284
-
285
- | File | Writer | Description |
286
- |---|---|---|
287
- | `world/characters/<slug>/sheet.md` | **Human** (after creation) | Canonical character sheet prose. `create_character_sheet` writes this file once on first setup; after that it is human-owned and no tool modifies it. |
288
- | `world/characters/<slug>/*.md` | **Human or AI agent** | Arc notes, relationship docs, history. Add and edit freely. |
289
- | `world/characters/<slug>/sheet.meta.yaml` | **AI agent** | Character metadata (`name`, `role`, `arc_summary`, `first_appearance`, `traits`). Written by `create_character_sheet`, `update_character_sheet`. |
290
- | `world/places/<slug>/sheet.md` | **Human** (after creation) | Canonical place sheet prose. `create_place_sheet` writes this file once on first setup; after that it is human-owned and no tool modifies it. |
291
- | `world/places/<slug>/sheet.meta.yaml` | **AI agent** | Place metadata (`name`, `associated_characters`, `tags`). Written by `create_place_sheet`, `update_place_sheet`. |
292
- | `world/reference/**/*.md` | **Human** | Free-form reference notes (world rules, timelines, etc.). Never indexed as entities. |
293
-
294
- **Rule:** all character and place changes that should survive forever — backstory, relationships, traits, arc notes — belong in `world/`. This content is never at risk from a Scrivener re-import.
295
-
296
- ### Summary
297
-
298
- | What you want to change | Where to make the change |
25
+ | Guide | Description |
299
26
  |---|---|
300
- | Prose wording | Scrivener → re-import |
301
- | Scene logline, status, tags, beat analysis | `scenes/*.meta.yaml` via AI tools |
302
- | Character traits, backstory, relationships | `world/characters/<slug>/` files |
303
- | Place descriptions and lore | `world/places/<slug>/` files |
304
- | Shared world rules, timelines, reference | `world/reference/` files |
305
-
306
- ---
307
-
308
- ## Appendix: Real-world usage scenarios
27
+ | [docs/setup.md](docs/setup.md) | Prerequisites, first-time setup, Scrivener import, native sync format |
28
+ | [docs/docker.md](docs/docker.md) | Docker Compose, OpenClaw integration, SSH hardening |
29
+ | [docs/data-ownership.md](docs/data-ownership.md) | Which tools write which files, import safety rules |
30
+ | [docs/tools.md](docs/tools.md) | Full tool reference auto-generated from source |
31
+ | [docs/development.md](docs/development.md) | Running locally, tests, environment variables, troubleshooting |
309
32
 
310
- The tool list is useful as reference. These example workflows show how people actually use `mcp-writing` while drafting and revising.
33
+ ## Usage scenarios
311
34
 
312
35
  ### 1) Continuity pass before sending chapters to beta readers
313
36
 
@@ -353,349 +76,5 @@ Goal: let AI propose prose edits without losing control of your draft.
353
76
 
354
77
  Outcome: you get AI speed with explicit approval and recoverable history for every applied change.
355
78
 
356
- ---
357
-
358
- ## Reference: Available tools
359
-
360
- The full tool reference — with every parameter, type, and description — is auto-generated from source and lives at **[docs/tools.md](docs/tools.md)**.
361
-
362
- To regenerate after editing tool definitions in `index.js`:
363
-
364
- ```bash
365
- npm run docs
366
- ```
367
-
368
- ---
369
-
370
- ## Running with Docker
371
-
372
- ```yaml
373
- # docker-compose.yml snippet
374
- writing-mcp:
375
- build: .
376
- user: "${OPENCLAW_UID:-1000}:${OPENCLAW_GID:-1000}"
377
- environment:
378
- WRITING_SYNC_DIR: /sync
379
- DB_PATH: /data/writing.db
380
- HTTP_PORT: "3000"
381
- OWNERSHIP_GUARD_MODE: "${OWNERSHIP_GUARD_MODE:-warn}"
382
- GIT_SSH_COMMAND: "ssh -i /ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/ssh/known_hosts"
383
- volumes:
384
- - ${OPENCLAW_WORKSPACE_DIR:?run scripts/setup-openclaw-env.sh first}/sync:/sync
385
- - ${OPENCLAW_SSH_DIR:?run scripts/setup-openclaw-env.sh first}:/ssh:ro
386
- - writing-mcp-data:/data
387
- healthcheck:
388
- test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
389
- interval: 30s
390
- timeout: 5s
391
- retries: 5
392
-
393
- volumes:
394
- writing-mcp-data:
395
- ```
396
-
397
- Recommended: start from `docker-compose.example.yml` and generate `.env` with machine-specific values:
398
-
399
- ```sh
400
- sh scripts/setup-openclaw-env.sh
401
- ```
402
-
403
- That script writes `OPENCLAW_UID`, `OPENCLAW_GID`, `OPENCLAW_WORKSPACE_DIR`, and `OPENCLAW_SSH_DIR` to `.env`.
404
- Running Compose without these values is unsupported and may create invalid mount definitions.
405
- It also normalizes `OWNERSHIP_GUARD_MODE` to `warn` or `fail` and preserves an existing valid value when rerun.
406
-
407
- Then register in your OpenClaw config:
408
-
409
- ```json
410
- "mcp": {
411
- "servers": {
412
- "writing": { "url": "http://writing-mcp:3000/sse" }
413
- }
414
- }
415
- ```
416
-
417
- <details>
418
- <summary>Advanced OpenClaw / Docker integration notes</summary>
419
-
420
- ### OpenClaw / Docker integration notes
421
-
422
- When `mcp-writing` runs behind OpenClaw (or any Docker MCP gateway), these details prevent common runtime failures.
423
-
424
- #### Required environment and mounts
425
-
426
- - Set `WRITING_SYNC_DIR=/sync`
427
- - Set `DB_PATH=/data/writing.db`
428
- - Set `OWNERSHIP_GUARD_MODE=warn` (or `fail` to block startup on ownership drift)
429
- - Mount your manuscript sync repo to `/sync`
430
- - Mount a persistent path for SQLite data at `/data`
431
- - Mount SSH materials read-only at `/ssh` and use `GIT_SSH_COMMAND` with `/ssh` paths
432
-
433
- Debug/test-only runtime override knobs:
434
-
435
- - `RUNTIME_UID_OVERRIDE` — test helper to simulate runtime UID during ownership diagnostics
436
- - `ALLOW_RUNTIME_UID_OVERRIDE=1` — explicitly enables the override outside `NODE_ENV=test`
437
-
438
- Do not set these in normal production or desktop deployments.
439
-
440
- If `/sync` contains raw Scrivener external-sync output, run the importer once before normal `sync` usage:
441
-
442
- ```sh
443
- node scripts/import.js /path/to/scrivener-export /sync --project my-novel
444
- ```
445
-
446
- `sync` indexes files that already contain scene metadata. It does not convert Scrivener `Draft/` filenames into scene sidecars by itself.
447
-
448
- #### Git ownership trust for mounted repos
449
-
450
- If host and container ownership differ, git can fail with:
451
-
452
- - `fatal: detected dubious ownership in repository`
453
-
454
- Mark the mounted repo path as safe in the container image:
455
-
456
- ```sh
457
- git config --system --add safe.directory /sync
458
- ```
459
-
460
- #### SSH transport hardening
461
-
462
- For private remotes, mount SSH materials read-only and enforce strict host checks:
463
-
464
- - Auth key for fetch/pull/push
465
- - `known_hosts` with GitHub host key
466
- - `StrictHostKeyChecking=yes`
467
-
468
- Example:
469
-
470
- ```sh
471
- export GIT_SSH_COMMAND="ssh -i /ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=/ssh/known_hosts"
472
- ```
473
-
474
- #### Separate auth and signing keys
475
-
476
- Use dedicated keys for transport and signing:
477
-
478
- - Auth key: repository transport (`fetch` / `pull` / `push`)
479
- - Signing key: commit/tag signatures
480
-
481
- Recommended git config:
482
-
483
- ```sh
484
- git config gpg.format ssh
485
- git config user.signingkey /root/.ssh/id_ed25519_signing
486
- git config commit.gpgsign true
487
- git config pull.ff only
488
- ```
489
-
490
- #### Git identity and GitHub email privacy
491
-
492
- If GitHub email privacy is enabled, pushes can fail unless `user.email` is a GitHub noreply address:
493
-
494
- ```sh
495
- git config user.name "Edda"
496
- git config user.email "<id>+<username>@users.noreply.github.com"
497
- ```
498
-
499
- #### Branch safety for automation
500
-
501
- For bot-driven edits, prefer branch-per-change flow:
502
-
503
- - Push to `edda/*` or `bot/*`
504
- - Merge via pull request
505
- - Protect `main` from direct automation pushes
506
-
507
- #### Quick validation
508
-
509
- ```sh
510
- ssh -T git@github.com
511
- git -C /sync fetch origin
512
- git -C /sync pull --ff-only
513
- ```
514
-
515
- Then create and push a signed smoke commit on a temporary branch.
516
-
517
- </details>
518
-
519
- ## Running locally
520
-
521
- ```sh
522
- npm install
523
- WRITING_SYNC_DIR=./my-manuscript DB_PATH=./writing.db npm start
524
- ```
525
-
526
- The `npm start` script automatically includes the `--experimental-sqlite` flag needed for SQLite support in Node.js 22+.
527
-
528
- ## Verify your setup
529
-
530
- After starting the server, test that it's working:
531
-
532
- ```sh
533
- # In a new terminal
534
- curl http://localhost:3000/healthz
535
- # Should return: ok
536
- ```
537
-
538
- Then test the MCP endpoint:
539
-
540
- ```sh
541
- curl http://localhost:3000/sse
542
- # Should return a stream endpoint: /message?sessionId=<id>
543
- ```
544
-
545
- If both return successfully, the server is ready to use.
546
-
547
- ## Development
548
-
549
- ```sh
550
- npm install
551
- npm test # unit + integration tests
552
- npm run test:unit # unit tests only (no server required)
553
- npm run lint:metadata # lint metadata in WRITING_SYNC_DIR or ./sync
554
- ```
555
-
556
- Unit tests use an in-memory SQLite database and temporary directories — no server needed. Integration tests generate a fixture sync tree at runtime in temporary directories, spawn a real server on port 3099, and verify all MCP tools end-to-end.
557
-
558
- For real projects, keep your manuscript sync folder outside this tool repository and point `WRITING_SYNC_DIR` at that external path.
559
-
560
- Maintainers: see `MAINTAINERS.md` for release and operational setup notes, and `AGENT.md` for persistent workflow conventions and release/recovery guidance.
561
-
562
- ## Troubleshooting
563
-
564
- ### "Module not found: sqlite" or "Database support not available"
565
-
566
- Your Node.js version is too old, or SQLite support was not started with the required flag.
567
-
568
- Fix:
569
-
570
- 1. Run `node --version` and confirm v22.6.0 or newer.
571
- 2. Upgrade Node.js if needed.
572
- 3. Restart with `npm start` (the script already includes `--experimental-sqlite`).
573
-
574
- ### "EADDRINUSE: address already in use :::3000"
575
-
576
- Port 3000 is already in use.
577
-
578
- Fix: start on a different port.
579
-
580
- ```sh
581
- HTTP_PORT=3001 WRITING_SYNC_DIR=./my-manuscript DB_PATH=./writing.db npm start
582
- ```
583
-
584
- Then update your MCP client config to use `http://localhost:3001/sse`.
585
-
586
- ### "ENOENT: no such file or directory, open './writing.db'"
587
-
588
- The directory for `DB_PATH` does not exist.
589
-
590
- Fix: create the directory first.
591
-
592
- ```sh
593
- mkdir -p $(dirname ./writing.db) # if using a subdirectory
594
- WRITING_SYNC_DIR=./my-manuscript DB_PATH=./writing.db npm start
595
- ```
596
-
597
- Or use an absolute path:
598
-
599
- ```sh
600
- WRITING_SYNC_DIR=~/my-manuscript DB_PATH=~/writing-data/writing.db npm start
601
- ```
602
-
603
- ### "Sync dir not found: ./my-manuscript"
604
-
605
- The `WRITING_SYNC_DIR` path does not exist.
606
-
607
- Fix: create it (or point to an existing sync folder).
608
-
609
- ```sh
610
- mkdir -p ./my-manuscript/projects/my-novel
611
- WRITING_SYNC_DIR=./my-manuscript DB_PATH=./writing.db npm start
612
- ```
613
-
614
- ### "Import failed: unrecognized format"
615
-
616
- Scrivener export is not plain text (`.txt`) or folder layout is unexpected.
617
-
618
- Fix:
619
-
620
- 1. In Scrivener, re-export with **File → Sync → With External Folder**
621
- 2. Ensure the format is set to **Plain text** (not RTF or .docx)
622
- 3. Verify the export folder has a `Draft/` subdirectory with `.txt` files
623
- 4. Try the import again: `node scripts/import.js ~/my-novel-txt /path/to/sync-dir --project my-novel`
624
-
625
- ### "OpenClaw can read tools, but scene indexing is empty or incomplete"
626
-
627
- You are likely running `sync` on raw Scrivener `Draft/` output that has not been imported yet.
628
-
629
- Fix:
630
-
631
- 1. Run importer once to create scene metadata sidecars:
632
-
633
- ```sh
634
- node scripts/import.js /path/to/scrivener-export /path/to/sync-dir --project my-novel
635
- ```
636
-
637
- 2. Restart the service (if needed), then call `sync` again.
638
-
639
- Note: importer behavior is Draft-aware (`<source>/Draft` if present, else source root), but plain `sync` only indexes already-normalized scene files.
640
-
641
- ### "Write access to repository denied" (or git push/pull fails in container)
642
-
643
- Your container can start and read files, but cannot write metadata, create snapshots, or push branches.
644
-
645
- Fix:
646
-
647
- 1. Check runtime diagnostics via `get_runtime_config`:
648
- - `sync_dir_writable` must be `true`
649
- - `runtime_warnings` should be empty for normal editing flows
650
- 2. Ensure `/sync` is mounted read-write (no `:ro`) and owned by the container user.
651
- 3. For mounted git repos with UID mismatch, mark safe directory:
652
-
653
- ```sh
654
- git config --system --add safe.directory /sync
655
- ```
656
-
657
- 4. Verify SSH key has write access to the remote and `known_hosts` is mounted.
658
- 5. Prefer branch-per-change workflow (`bot/*` or `edda/*`) if `main` is protected.
659
-
660
- ### "Blocked: file is root-owned" (EACCES / ownership drift)
661
-
662
- The runtime user can read but cannot overwrite prose files.
663
-
664
- Fix:
665
-
666
- 1. Repair host ownership once:
667
-
668
- ```sh
669
- sudo chown -R "$(id -u):$(id -g)" /path/to/sync-dir
670
- ```
671
-
672
- 2. Ensure container user mapping is set from `.env` (`OPENCLAW_UID` / `OPENCLAW_GID`).
673
- 3. Optionally set `OWNERSHIP_GUARD_MODE=fail` to catch mismatches at startup.
674
- 4. Re-check `get_runtime_config` and confirm ownership warnings are gone.
675
-
676
- ### Tests fail after updating Node.js
677
-
678
- Local install state may be stale after the Node.js change.
679
-
680
- Fix: reinstall dependencies.
681
-
682
- ```sh
683
- rm -rf node_modules package-lock.json
684
- npm install
685
- npm test
686
- ```
687
-
688
- ## Environment variables
689
-
690
- | Variable | Default | Description |
691
- | --- | --- | --- |
692
- | `WRITING_SYNC_DIR` | `./sync` | Path to the sync folder |
693
- | `DB_PATH` | `./writing.db` | Path to the SQLite index database |
694
- | `HTTP_PORT` | `3000` | Port for the MCP SSE endpoint |
695
- | `MAX_CHAPTER_SCENES` | `10` | Maximum scenes returned by `get_chapter_prose` |
696
- | `DEFAULT_METADATA_PAGE_SIZE` | `20` | Default page size for paginated tools |
697
- | `OWNERSHIP_GUARD_MODE` | `warn` | Startup ownership policy: `warn` logs drift, `fail` exits when sampled files are not owned by runtime user |
698
-
699
79
  ## License
700
-
701
80
  AGPL-3.0-only
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanna84/mcp-writing",
3
- "version": "1.9.3",
3
+ "version": "1.9.4",
4
4
  "description": "MCP service for AI-assisted reasoning and editing on long-form fiction projects",
5
5
  "type": "module",
6
6
  "main": "index.js",