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, withmarketplace: true - Requires: Node.js 22.19 or later, plus
ffprobeandffmpegonPATH - 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.tsdeclares identity, the Node runtime, the two targets, and the MCP-to-CLI projection. File conventions discover the rest: 16 tool routes, one catalog resource, and one curation prompt undersrc/mcp/curator/. There is no operation registry, no handwrittensrc/mcp/curator.ts, and no per-operation server selector. - One authored surface, two protocols.
routes.mcpCommandsprojects all 16 MCP tools asaudiobook-curator curator <tool>, so the compiled graph carries 32 CLI commands: the 16 authored ones undersrc/cli/plus the 16 projections. Projected tools take one optional--input '<JSON object>'; read-only tools run directly while mutation-capable tools require--yes. - Presentation is shared, not duplicated.
src/components/is one report library composed by both the MCP routes and the rendered CLI routes, so an MCP tool and its CLI counterpart cannot drift into two presenters. - Request context is observed, not assumed. The conventional
src/providers/library.tsprobesffmpeg -versionandffprobe -versionper request and publishes tool availability with the probe time. The catalog resource reads it throughawait agent()and renders either the live context or an explicit unavailable state. - Durable state has a mounted identity.
src/state.tsmounts the workspace-durableaudiobook-curator/shelfstate with three events. If state is not mounted, the read-only MCP tool and the renderedshelfCLI command both return an empty structured shelf and an explicit unavailable notice instead of failing. - Suspense becomes MCP progress.
audit_libraryreports progress through the request'scontext.progress, then places its asynchronous analysis behind ReactSuspensewith anAgent.Progressfallback. The generated projector streams the progress state and replaces it with the completed analysis without changing the final structured receipt. - The framework owns the package build. The config declares no
binorscriptsfield. The routedsrc/cli/commands compile intodist/bin/audiobook-curator.jsforpackage.jsonbin, anddist/index.jsplus declarations serveexports. See CLI and library package entries. package.jsonis the single version source. The config declares noplugin.version; the resolved version flows into project context, artifact manifests,inspectoutput, dev status, and theagent-bundle/metaconstant this plugin imports.
Rendered and plain CLI routes
Seven authored .tsx commands render Agent Documents — inventory, select, audible-search,
convert, audit, library-audit, and shelf. An interactive terminal updates their reported
progress in place; piped output is one final Markdown document. Nine compatibility commands
remain plain .ts routes.
Across both kinds, --json selects machine output and emits one result-schema-validated JSON
value followed by a newline. For a rendered command that value is the canonical final
Agent.Result value — never the Markdown presentation and never an intermediate Suspense
fallback — so a receipt consumer does not change when a command becomes rendered.
Working in the workspace
pnpm check inside the package runs validate, build, typecheck, and both test pools. One
agent-bundle build --output artifact produces everything: complete Claude and Codex outputs
beneath artifact/ — each host's plugin metadata, Skill, bundled CLI script, and
lifecycle-wrapped MCP server — plus the npm package beneath dist/.
To exercise the built CLI without packing a tarball, link the built bin from any writable
directory already on PATH:
Running the server on stdio
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.