create_book_project
Initialize a new book project with title, genre, and author details, automatically loading it into the writing pipeline for multi-agent book creation.
Instructions
Create a new book project under book_projects/ (or STORYWRIGHT_PROJECTS_ROOT). Loads the new project.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_name | Yes | ||
| book_title | Yes | ||
| genre | No | ||
| authors | No | ||
| projects_root | No | ||
| third_agents | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/storywright_mcp/workflow.py:40-76 (handler)The actual implementation of create_book_project. Creates directories (book_projects/<slug>, briefs, reports, manuscript), initializes a ProjectConfig with provided parameters (defaulting third_agents to ['comedy']), saves config and an empty ContinuityLog, writes default brief templates, and binds the project to the session.
def create_book_project( project_name: str, book_title: str, genre: str = "", authors: list[str] | None = None, projects_root: str | None = None, third_agents: list[str] | None = None, ) -> str: root = Path(projects_root).resolve() if projects_root else get_settings().projects_root.resolve() base_path = root / "book_projects" / project_name.strip().replace(" ", "-").lower() base_path.mkdir(parents=True, exist_ok=True) (base_path / "briefs").mkdir(exist_ok=True) (base_path / "reports").mkdir(exist_ok=True) (base_path / "manuscript").mkdir(exist_ok=True) agents = ["comedy"] if third_agents is None else list(third_agents) proj = ProjectConfig( name=book_title, genre=genre, authors=authors or [], base_path=base_path, third_agents=agents, ) proj.save() cont = ContinuityLog() cont.save(base_path) (base_path / "briefs" / "writer_brief.md").write_text(get_default_writer_brief(), encoding="utf-8") (base_path / "briefs" / "editor_brief.md").write_text(get_default_editor_brief(), encoding="utf-8") (base_path / "briefs" / "comedy_brief.md").write_text(get_default_comedy_brief(), encoding="utf-8") bind_project(base_path) return ( f"Created **{book_title}** at `{base_path}`.\n" f"Third-pass agents: {', '.join(agents)}\n\n" f"Next: `load_book_project(\"{base_path}\")` from another cwd, or continue — project is loaded." ) - src/storywright_mcp/app.py:26-38 (registration)MCP tool registration via @mcp.tool() decorator. The async function create_book_project is exposed as an MCP tool and delegates to workflow.create_book_project.
@mcp.tool() async def create_book_project( project_name: str, book_title: str, genre: str = "", authors: list[str] | None = None, projects_root: str | None = None, third_agents: list[str] | None = None, ) -> str: """Create a new book project under book_projects/ (or STORYWRIGHT_PROJECTS_ROOT). Loads the new project.""" return workflow.create_book_project( project_name, book_title, genre, authors, projects_root, third_agents ) - ProjectConfig dataclass schema — the data model used to store book project configuration (name, genre, authors, chapters, characters, etc.) that is created by create_book_project.
@dataclass class ProjectConfig: name: str genre: str = "" authors: list[str] = field(default_factory=list) chapters: list[Chapter] = field(default_factory=list) characters: list[Character] = field(default_factory=list) death_schedule: list[DeathEntry] = field(default_factory=list) running_gags: list[RunningGag] = field(default_factory=list) base_path: Path = field(default_factory=Path.cwd) third_agents: list[str] = field(default_factory=list) has_deaths: bool = True - src/storywright_mcp/config.py:8-37 (helper)Settings class with projects_root defaulting to current working directory. Used by create_book_project to determine where to create the project folder.
class Settings(BaseSettings): model_config = SettingsConfigDict( env_prefix="STORYWRIGHT_", env_file=".env", env_nested_delimiter="__", extra="ignore", ) # Projects live under `{projects_root}/book_projects/<slug>/` projects_root: Path = Path.cwd() # Persist last loaded project path here (optional UX) state_dir: Path = Path.home() / ".storywright" # Model id for writer/editor/third-pass API calls (override per deployment) anthropic_model: str = "claude-sonnet-4-20250514" # Prior approved prose budget for writer prompts (controls context size) prior_chapters_max_words: int = 12000 prior_chapters_max_count: int = 8 # Anthropic API transient failures anthropic_max_retries: int = 2 anthropic_retry_delay_seconds: float = 2.0 _settings: Settings | None = None def get_settings() -> Settings: global _settings if _settings is None: _settings = Settings() return _settings - Helper functions get_default_writer_brief(), get_default_editor_brief(), and get_default_comedy_brief() that return the default brief templates written to the new project's briefs/ directory.
def get_default_writer_brief() -> str: """Get the default writer brief template.""" return WRITER_BRIEF_TEMPLATE def get_default_editor_brief() -> str: """Get the default editor brief template.""" return EDITOR_BRIEF_TEMPLATE def get_default_comedy_brief() -> str: """Get the default comedy brief template.""" return COMEDY_BRIEF_TEMPLATE