For AI agents: the complete documentation index is available at https://scriptedalchemy.github.io/agent-bundle/llms.txt, the full documentation bundle is available at https://scriptedalchemy.github.io/agent-bundle/llms-full.txt, and this page is available as Markdown at https://scriptedalchemy.github.io/agent-bundle/examples/audiobook-curator.md.
Skip to main content
  • English
  • Audiobook Curator

    A complete plugin rather than a demonstration of one surface: a real media-management application assembled from React Server Component route modules, request context, durable state, MCP routes, and CLI routes. One build produces a generated stdio MCP server, an installable CLI, one Skill, and native Claude Code and Codex plugin artifacts. It is the reference consumer of the framework-owned package build.

    • Run from the repository root: pnpm example:audiobook
    • Package: @agent-bundle-example/audiobook-curator
    • Public dependencies: agent-bundle (workspace:*), @agent-bundle/runtime (workspace:*), @modelcontextprotocol/server, react, zod
    • Targets: claude, codex, with marketplace: true
    • Requires: Node.js 22.19 or later, plus ffprobe and ffmpeg on PATH
    • Source: examples/audiobook-curator

    Optional features call the foreign tools that supply their evidence: Audiobook Forge, Audiolocate in a selected Python environment, and whisper-cli with a selected model. Skipping them costs those features, not the build. This example has no hooks.

    What it proves

    • The route tree is the application. agent-bundle.config.ts declares identity, the Node runtime, and the two targets. File conventions discover the rest: 16 tool routes, one catalog resource, and one curation prompt under src/mcp/curator/. There is no route registry, no handwritten src/mcp/curator.ts, no per-operation server selector, and no routes setting. Every tool uses defineTool to keep its metadata, schemas, and handler in one definition.
    • One operation, one command. Every tool has a colocated <tool>.cli.ts projection, search_audible.cli.ts spells the tool as audible-search, --duration, and a comma-separated or repeatable --regions; prepare_audiobook.cli.ts spells outputRoot as --output; and convert_audiobook.cli.ts keeps its nested conversion contract whole behind --input, so the compiled graph carries exactly 16 CLI commands, each with the tool's own identity (tool:curator/<tool>). There is no src/cli/ directory and no audiobook-curator curator <tool> --input twin: the projection is the command. Every projection declares confirm: false because the plan-first commands gate their mutation on --apply, never on --yes.
    • Presentation is shared, not duplicated. src/components/ is one report library, and each tool module renders once for both surfaces: the MCP text content and the piped CLI Markdown are the same document, so a tool and its command cannot drift into two presenters.
    • Markdown blocks are JSX. The library's DataList and FileList primitives and the duplicate analysis's measured-files table author their Markdown through the runtime's MarkdownContent component, <ul>/<li>, <strong>, and a GFM <table>, instead of hand-concatenated strings, so escaping belongs to the renderer and piped CLI Markdown and MCP text content come from one output.
    • Request context is observed, not assumed. The conventional src/providers/library.ts probes ffmpeg -version and ffprobe -version per request and publishes tool availability with the probe time. The catalog resource reads it through await agent() and renders either the live context or an explicit unavailable state.
    • Durable state has a mounted identity. src/state.ts mounts the workspace-durable audiobook-curator/shelf state with three events. If state is not mounted, the read-only review_curation_shelf tool, shelf on the command line, returns an empty structured shelf and an explicit unavailable notice instead of failing.
    • Suspense becomes MCP progress. audit_library places its asynchronous analysis behind React Suspense with an Agent.Progress fallback, and that fallback alone is the progress surface: the generated projector sends it as notifications/progress to a client that passed a progress token, then replaces it with the completed analysis without changing the final structured receipt. No progress.report() call repeats the message.
    • The framework owns the package build. The config declares no bin or scripts field. The projected commands compile into dist/bin/audiobook-curator.mjs for package.json bin, and dist/index.js plus declarations serve exports. See CLI and library package entries.
    • package.json is the single version source. The resolved version flows into project context, artifact manifests, inspect output, dev status, and the agent-bundle/meta constant this plugin imports.

    Every command renders its tool's document

    Each command runs its tool's component. An interactive terminal draws the streamed Agent.Progress fallback in place; piped output is one final Markdown document, the same headline and report the MCP client receives as text content. --json selects machine output and emits one result-schema-validated JSON value followed by a newline: the canonical final Agent.Result value, never the Markdown presentation and never an intermediate Suspense fallback, and byte for byte the structuredContent of the tool call. Named --report and --receipt options remain optional where a command projects fields as flags; convert carries its optional receipt inside the JSON conversion object. A command that gets one still writes the receipt file when it succeeds. This is a behavior change from the retired src/cli/ tree, where every command that took a report or receipt path, thirteen of the sixteen, inventory --report and convert --receipt among them, required it: those commands now run without the path and write no report or receipt file, and their exit codes, --apply gating, and error output are unchanged either way. inspect, prepare, and shelf never took one.

    Working in the workspace

    pnpm --filter @agent-bundle-example/audiobook-curator build
    pnpm --filter @agent-bundle-example/audiobook-curator test
    pnpm --filter @agent-bundle-example/audiobook-curator test:routes
    pnpm --filter @agent-bundle-example/audiobook-curator typecheck

    pnpm check inside the package runs validate, build, typecheck, and both test pools. One agent-bundle build --output artifact produces everything: one composite plugin root at artifact/ carrying the Claude and Codex projections, each host's manifest in its own dotfolder over one shared Skill, bundled CLI script, and lifecycle-wrapped MCP server, plus the npm package beneath dist/.

    Start pnpm example:audiobook from the repository root for the visual development loop:

    1. Expand Application → MCP → curator → Tools and select search_audible.
    2. Enter a title such as Dune in the generated input editor, then select Run.
    3. Inspect Rendered first: it shows the actual Agent Document from the route's production RSC execution. Structured data, raw document, MCP/CLI projections, and Trace remain available as secondary tabs.
    4. Open Trace to see the invocation start and completion correlated under the run, then use Open route to return to this recorded result.
    5. Edit src/mcp/curator/tools/search_audible.tsx or a component it renders. After the rebuild reaches Idle, rerun the saved input and inspect the updated rendered result.

    The route is directly addressable at /routes/mcp/curator/tool/search_audible; append ?invocation=<id> to reopen one of this session's recorded results.

    To exercise the built CLI without packing a tarball, link the built bin from any writable directory already on PATH:

    cd examples/audiobook-curator
    ln -s "$(pwd)/dist/bin/audiobook-curator.mjs" ~/.local/bin/audiobook-curator
    audiobook-curator --help

    Running the server on stdio

    cd examples/audiobook-curator
    pnpm exec agent-bundle mcp run --server curator --target claude

    The command resolves the generated entry from the Claude target's MCP manifest, building a temporary artifact first; pass --artifact artifact to reuse the pnpm build output instead. Closing stdin exits 0 and Ctrl-C exits 130, and per-server state persists under .agent-bundle/mcp-run/claude/curator.

    Safety boundaries

    Sources are immutable and planning never mutates media. Conversion publishes to a separate destination; metadata and chapter replacement require an explicit apply, stage into a same-directory file, verify the staged result, and then rename atomically. JSON receipts refuse audio suffixes and collisions with media or evidence inputs. Network bodies, process output, traversal, and concurrency are bounded, and network work uses bounded attempts plus the caller's cancellation signal rather than hidden deadlines. Local media processes have no wall-clock deadline; caller cancellation and bounded stdout and stderr stay enforced.

    The curation Skill directs the agent to perform only the requested operations: inventory or audit alone does not require conversion or edition matching. For a mutation, it prepares the exact plan and evidence before seeking missing approval, and reuses approval only while those inputs remain unchanged. Every mutating tool call still requires apply: true; source immutability and human-reviewed edition selection remain intact.