search_manuscript
Search across manuscript chapters using substring matching. Input a query and optional file pattern to find specific text in your book.
Instructions
Substring search across manuscript/*.md.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| file_glob | No | *.md |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/storywright_mcp/workflow.py:639-653 (handler)Core implementation of search_manuscript: reads manuscript/ directory files matching a glob, performs case-insensitive substring search, returns file:line hits (up to 80, truncated).
def search_manuscript(query: str, file_glob: str = "*.md") -> str: proj, _ = require_project() root = proj.base_path / "manuscript" if not root.exists(): return "No manuscript/ directory." hits: list[str] = [] q_lower = query.lower() for path in sorted(root.glob(file_glob)): text = path.read_text(encoding="utf-8", errors="replace") for i, line in enumerate(text.splitlines(), 1): if q_lower in line.lower(): hits.append(f"{path.name}:{i}:{line.strip()[:200]}") if not hits: return f"No matches for `{query}`." return "\n".join(hits[:80]) + ("\n… truncated …" if len(hits) > 80 else "") - src/storywright_mcp/app.py:317-323 (registration)Registers search_manuscript as a FastMCP tool, delegates to workflow.search_manuscript.
@mcp.tool() async def search_manuscript(query: str, file_glob: str = "*.md") -> str: """Substring search across manuscript/*.md.""" try: return workflow.search_manuscript(query, file_glob) except ValueError as e: return str(e) - Uses require_project() from .session to get the project config and resolve the manuscript directory path.
proj, _ = require_project() root = proj.base_path / "manuscript"