Chapters now carry many Locations (new Tags-style entity with cross-referencing) instead of a single free-text Setting field, with a Locations tab on the novel for browsing them and seeing every chapter set at each one. Also surfaces the distinct characters appearing in a chapter's beats, linked, under the beat/word count on the outline tab.
72 lines
3.6 KiB
C#
72 lines
3.6 KiB
C#
using System.ComponentModel;
|
|
using ModelContextProtocol.Protocol;
|
|
using ModelContextProtocol.Server;
|
|
|
|
namespace Novelly.Mcp.Tools;
|
|
|
|
[McpServerToolType]
|
|
public static class ManuscriptTools
|
|
{
|
|
[McpServerTool(Name = "list_chapters")]
|
|
[Description("List a novel's chapters in manuscript order, with beat and word counts.")]
|
|
public static Task<CallToolResult> ListChapters(
|
|
NovelApiClient api,
|
|
[Description("The novel's id.")] Guid novelId,
|
|
CancellationToken ct) =>
|
|
api.GetAsync($"/api/novels/{novelId}/chapters", ct);
|
|
|
|
[McpServerTool(Name = "get_chapter")]
|
|
[Description("Read one chapter in full: its outline (beats) and its drafted prose.")]
|
|
public static Task<CallToolResult> GetChapter(
|
|
NovelApiClient api,
|
|
[Description("The chapter's id.")] Guid chapterId,
|
|
CancellationToken ct) =>
|
|
api.GetAsync($"/api/chapters/{chapterId}", ct);
|
|
|
|
[McpServerTool(Name = "create_chapter")]
|
|
[Description("Add a chapter to a novel. It goes at the end of the manuscript unless you supply a number.")]
|
|
public static Task<CallToolResult> CreateChapter(
|
|
NovelApiClient api,
|
|
[Description("The novel's id.")] Guid novelId,
|
|
[Description("Chapter title.")] string title,
|
|
CancellationToken ct,
|
|
[Description("Position in the manuscript, 1-based.")] int? number = null,
|
|
[Description("The chapter's outline summary paragraph.")] string? summary = null,
|
|
[Description("Where and when the chapter takes place. Unknown locations are created.")] string[]? locations = null,
|
|
[Description("Planned, Outlined, Drafted, Revised or Final.")] string? status = null,
|
|
[Description("Target length in words.")] int? targetWordCount = null,
|
|
[Description("The chapter's drafted text, in markdown, if you are writing it now.")] string? prose = null,
|
|
[Description("Tags for cross-referencing. Unknown tags are created.")] string[]? tags = null) =>
|
|
api.PostAsync($"/api/novels/{novelId}/chapters", new
|
|
{
|
|
title,
|
|
number,
|
|
summary,
|
|
locations,
|
|
status = status ?? "Planned",
|
|
targetWordCount,
|
|
prose,
|
|
tags
|
|
}, ct);
|
|
|
|
[McpServerTool(Name = "update_chapter")]
|
|
[Description("Revise a chapter's title, number, summary, locations, notes, status "
|
|
+ "or drafted prose. Use 'prose' to write or replace the chapter's draft text in "
|
|
+ "markdown; the word count is recomputed automatically.")]
|
|
public static Task<CallToolResult> UpdateChapter(
|
|
NovelApiClient api,
|
|
[Description("The chapter's id.")] Guid chapterId,
|
|
CancellationToken ct,
|
|
[Description("New title.")] string? title = null,
|
|
[Description("Position in the manuscript.")] int? number = null,
|
|
[Description("The chapter's outline summary paragraph.")] string? summary = null,
|
|
[Description("Where and when the chapter takes place. Replaces the existing locations. Unknown locations are created.")] string[]? locations = null,
|
|
[Description("Anything else worth recording.")] string? notes = null,
|
|
[Description("Planned, Outlined, Drafted, Revised or Final.")] string? status = null,
|
|
[Description("Target length in words.")] int? targetWordCount = null,
|
|
[Description("The chapter's drafted text, in markdown.")] string? prose = null,
|
|
[Description("Tags for cross-referencing. Replaces the existing tags.")] string[]? tags = null) =>
|
|
api.PatchAsync($"/api/chapters/{chapterId}",
|
|
new { title, number, summary, locations, notes, status, targetWordCount, prose, tags }, ct);
|
|
}
|