@hanna84/mcp-writing 3.5.5 → 3.6.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.
- package/CHANGELOG.md +14 -0
- package/README.md +27 -0
- package/package.json +1 -1
- package/src/tools/search.js +27 -10
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,23 @@ 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
|
+
#### [v3.6.1](https://github.com/hannasdev/mcp-writing/compare/v3.6.0...v3.6.1)
|
|
8
|
+
|
|
9
|
+
- chore(deps): Bump fast-uri in the npm_and_yarn group across 1 directory [`#188`](https://github.com/hannasdev/mcp-writing/pull/188)
|
|
10
|
+
|
|
11
|
+
#### [v3.6.0](https://github.com/hannasdev/mcp-writing/compare/v3.5.5...v3.6.0)
|
|
12
|
+
|
|
13
|
+
> 14 May 2026
|
|
14
|
+
|
|
15
|
+
- feat(search): normalize metadata-read tool response envelopes [`#189`](https://github.com/hannasdev/mcp-writing/pull/189)
|
|
16
|
+
- Release 3.6.0 [`696eddd`](https://github.com/hannasdev/mcp-writing/commit/696edddbce11bf71c0ccb1cd255da1044c69f793)
|
|
17
|
+
|
|
7
18
|
#### [v3.5.5](https://github.com/hannasdev/mcp-writing/compare/v3.5.4...v3.5.5)
|
|
8
19
|
|
|
20
|
+
> 9 May 2026
|
|
21
|
+
|
|
9
22
|
- fix: suppress epigraph titles and refine review replies [`#187`](https://github.com/hannasdev/mcp-writing/pull/187)
|
|
23
|
+
- Release 3.5.5 [`26aa8f8`](https://github.com/hannasdev/mcp-writing/commit/26aa8f88d44dc49b0ee4c44a65b00274de73641c)
|
|
10
24
|
|
|
11
25
|
#### [v3.5.4](https://github.com/hannasdev/mcp-writing/compare/v3.5.3...v3.5.4)
|
|
12
26
|
|
package/README.md
CHANGED
|
@@ -86,12 +86,39 @@ Safe parsing pattern:
|
|
|
86
86
|
|
|
87
87
|
```js
|
|
88
88
|
const parsed = JSON.parse(toolText);
|
|
89
|
+
if (parsed.ok === false) throw new Error(parsed.error?.message ?? "tool error");
|
|
89
90
|
const scenes = parsed.results ?? [];
|
|
90
91
|
const totalCount = parsed.total_count ?? scenes.length;
|
|
91
92
|
const warning = parsed.warning ?? null;
|
|
92
93
|
const nextStep = parsed.next_step ?? null;
|
|
93
94
|
```
|
|
94
95
|
|
|
96
|
+
### `get_character_sheet`, `get_place_sheet`, `list_scene_references`, `get_relationship_arc` response-shape standardization
|
|
97
|
+
|
|
98
|
+
These metadata-read tools now return structured envelopes instead of flat objects or raw arrays.
|
|
99
|
+
|
|
100
|
+
- `get_character_sheet` and `get_place_sheet`: previously returned a flat object of field values; now return `{ results: [row], total_count: 1, next_step }`.
|
|
101
|
+
- `list_scene_references`: previously returned `{ references, scene_id, project_id }`; now returns `{ results, total_count, scene_id, project_id }`.
|
|
102
|
+
- `get_relationship_arc`: previously returned a raw JSON array; now returns `{ results, total_count, from_character, to_character }`.
|
|
103
|
+
|
|
104
|
+
Safe parsing pattern for sheet tools:
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
const parsed = JSON.parse(toolText);
|
|
108
|
+
if (parsed.ok === false) throw new Error(parsed.error?.message ?? "tool error");
|
|
109
|
+
const sheet = parsed.results?.[0] ?? {};
|
|
110
|
+
const nextStep = parsed.next_step ?? null;
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Safe parsing pattern for list/arc tools:
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
const parsed = JSON.parse(toolText);
|
|
117
|
+
if (parsed.ok === false) throw new Error(parsed.error?.message ?? "tool error");
|
|
118
|
+
const items = parsed.results ?? [];
|
|
119
|
+
const totalCount = parsed.total_count ?? items.length;
|
|
120
|
+
```
|
|
121
|
+
|
|
95
122
|
## Usage scenarios
|
|
96
123
|
|
|
97
124
|
### 1) Continuity pass before sending chapters to beta readers
|
package/package.json
CHANGED
package/src/tools/search.js
CHANGED
|
@@ -371,7 +371,7 @@ export function registerSearchTools(s, {
|
|
|
371
371
|
// ---- get_character_sheet -------------------------------------------------
|
|
372
372
|
s.tool(
|
|
373
373
|
"get_character_sheet",
|
|
374
|
-
"Get full character details: role, arc_summary, traits, the canonical sheet content, and any adjacent support notes when the character uses a folder-based layout. Use this when the reasoning task needs the character's canonical profile rather than only their scene progression.",
|
|
374
|
+
"Get full character details: role, arc_summary, traits, the canonical sheet content, and any adjacent support notes when the character uses a folder-based layout. Use this when the reasoning task needs the character's canonical profile rather than only their scene progression. Response shape note: returns a structured envelope (`results`, `total_count`) with one result row.",
|
|
375
375
|
{
|
|
376
376
|
character_id: z.string().describe("The character_id to look up (e.g. 'char-sebastian'). Use list_characters to find valid IDs."),
|
|
377
377
|
},
|
|
@@ -402,9 +402,17 @@ export function registerSearchTools(s, {
|
|
|
402
402
|
traits,
|
|
403
403
|
notes: notes || undefined,
|
|
404
404
|
supporting_notes: supportingNotes.length ? supportingNotes : undefined,
|
|
405
|
-
next_step: "Use get_arc with this character_id to trace scene-level progression, then open specific scenes with get_scene_prose when prose evidence is needed.",
|
|
406
405
|
};
|
|
407
|
-
return {
|
|
406
|
+
return {
|
|
407
|
+
content: [{
|
|
408
|
+
type: "text",
|
|
409
|
+
text: JSON.stringify({
|
|
410
|
+
results: [result],
|
|
411
|
+
total_count: 1,
|
|
412
|
+
next_step: "Use get_arc with this character_id to trace scene-level progression, then open specific scenes with get_scene_prose when prose evidence is needed.",
|
|
413
|
+
}, null, 2),
|
|
414
|
+
}],
|
|
415
|
+
};
|
|
408
416
|
}
|
|
409
417
|
);
|
|
410
418
|
|
|
@@ -444,7 +452,7 @@ export function registerSearchTools(s, {
|
|
|
444
452
|
// ---- get_place_sheet -----------------------------------------------------
|
|
445
453
|
s.tool(
|
|
446
454
|
"get_place_sheet",
|
|
447
|
-
"Get full place details: associated_characters, tags, the canonical sheet content, and any adjacent support notes when the place uses a folder-based layout. Use this when the current scene or question makes the place itself materially relevant.",
|
|
455
|
+
"Get full place details: associated_characters, tags, the canonical sheet content, and any adjacent support notes when the place uses a folder-based layout. Use this when the current scene or question makes the place itself materially relevant. Response shape note: returns a structured envelope (`results`, `total_count`) with one result row.",
|
|
448
456
|
{
|
|
449
457
|
place_id: z.string().describe("The place_id to look up (e.g. 'place-harbor-district'). Use list_places to find valid IDs."),
|
|
450
458
|
},
|
|
@@ -480,9 +488,17 @@ export function registerSearchTools(s, {
|
|
|
480
488
|
tags: tags.length ? tags : undefined,
|
|
481
489
|
notes: notes || undefined,
|
|
482
490
|
supporting_notes: supportingNotes.length ? supportingNotes : undefined,
|
|
483
|
-
next_step: "Use find_scenes with related filters to locate scenes where this place matters, then open targeted scenes with get_scene_prose when prose context is needed.",
|
|
484
491
|
};
|
|
485
|
-
return {
|
|
492
|
+
return {
|
|
493
|
+
content: [{
|
|
494
|
+
type: "text",
|
|
495
|
+
text: JSON.stringify({
|
|
496
|
+
results: [result],
|
|
497
|
+
total_count: 1,
|
|
498
|
+
next_step: "Use find_scenes with related filters to locate scenes where this place matters, then open targeted scenes with get_scene_prose when prose context is needed.",
|
|
499
|
+
}, null, 2),
|
|
500
|
+
}],
|
|
501
|
+
};
|
|
486
502
|
}
|
|
487
503
|
);
|
|
488
504
|
|
|
@@ -635,7 +651,7 @@ export function registerSearchTools(s, {
|
|
|
635
651
|
// ---- list_scene_references -----------------------------------------------
|
|
636
652
|
s.tool(
|
|
637
653
|
"list_scene_references",
|
|
638
|
-
"List direct reference documents linked from a scene via metadata (for example, reference_ids). Returns only one-hop scene -> reference links and does not recursively traverse related references. If scene IDs are reused across projects, omitting project_id returns CONFLICT with candidate project_ids.",
|
|
654
|
+
"List direct reference documents linked from a scene via metadata (for example, reference_ids). Returns only one-hop scene -> reference links and does not recursively traverse related references. If scene IDs are reused across projects, omitting project_id returns CONFLICT with candidate project_ids. Response shape note: returns a structured envelope (`results`, `total_count`) plus the resolved `scene_id` and `project_id` context.",
|
|
639
655
|
{
|
|
640
656
|
scene_id: z.string().describe("Scene ID to inspect."),
|
|
641
657
|
project_id: z.string().optional().describe("Optional project ID to disambiguate duplicate scene IDs across projects."),
|
|
@@ -714,9 +730,10 @@ export function registerSearchTools(s, {
|
|
|
714
730
|
content: [{
|
|
715
731
|
type: "text",
|
|
716
732
|
text: JSON.stringify({
|
|
733
|
+
results: references,
|
|
734
|
+
total_count: references.length,
|
|
717
735
|
scene_id: scene.scene_id,
|
|
718
736
|
project_id: scene.project_id,
|
|
719
|
-
references,
|
|
720
737
|
}, null, 2),
|
|
721
738
|
}],
|
|
722
739
|
};
|
|
@@ -869,7 +886,7 @@ export function registerSearchTools(s, {
|
|
|
869
886
|
// ---- get_relationship_arc ------------------------------------------------
|
|
870
887
|
s.tool(
|
|
871
888
|
"get_relationship_arc",
|
|
872
|
-
"Show how the relationship between two characters evolves across scenes, in order. Uses explicitly recorded relationship entries — returns
|
|
889
|
+
"Show how the relationship between two characters evolves across scenes, in order. Uses explicitly recorded relationship entries — returns a NO_RESULTS error if no entries exist yet. Use list_characters to get character_id values. Response shape note: returns a structured envelope { results, total_count, from_character, to_character }.",
|
|
873
890
|
{
|
|
874
891
|
from_character: z.string().describe("character_id of the first character (e.g. 'char-sebastian')."),
|
|
875
892
|
to_character: z.string().describe("character_id of the second character (e.g. 'char-mira-nystrom')."),
|
|
@@ -892,7 +909,7 @@ export function registerSearchTools(s, {
|
|
|
892
909
|
if (rows.length === 0) {
|
|
893
910
|
return errorResponse("NO_RESULTS", `No relationship data found between '${from_character}' and '${to_character}'.`);
|
|
894
911
|
}
|
|
895
|
-
return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
|
|
912
|
+
return { content: [{ type: "text", text: JSON.stringify({ results: rows, total_count: rows.length, from_character, to_character }, null, 2) }] };
|
|
896
913
|
}
|
|
897
914
|
);
|
|
898
915
|
|