Generates patterns, templates, and guides for @outfitter/* packages. Covers transport-agnostic handler systems, Result types, error taxonomy, and package APIs. Use when working with @outfitter/*, Result types, Handler contract, error taxonomy, or when Result, Handler, ValidationError, NotFoundError, OutfitterError, or package names like contracts, cli, mcp, schema, tui, daemon, config, logging are mentioned.
Generates patterns and templates for building transport-agnostic handlers and typed errors with the @outfitter/* package ecosystem.
npx claudepluginhub outfitter-dev/outfitterThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Your trail map for building with @outfitter/* packages. This guide covers the patterns, the templates, and—just as importantly—the *why* behind it all.
We kept solving the same problems across projects: config loading, error handling, CLI output modes, MCP server boilerplate. Every tool needed the same foundation. So we extracted it.
The patterns assume you're building tools that agents will consume—structured output, typed errors, predictable behavior. Humans benefit too; agents just make the stakes clearer.
When an AI agent calls your CLI or MCP tool, it needs:
The stack enforces these properties by design, not discipline.
Traditional error handling (throw/catch) loses context, breaks type safety, and makes control flow unpredictable. We treat errors as first-class data:
const error = NotFoundError.create("user", "user-123");
error._tag; // "NotFoundError" — for pattern matching
error.category; // "not_found" — maps to exit code 2, HTTP 404
error.message; // Human-readable
error.toJSON(); // Serializes cleanly for agents
Ten categories cover all failure modes. Each maps to exit codes (CLI) and HTTP status (API/MCP). Agents can make retry decisions without parsing error strings.
Tests define behavior; implementations follow. The workflow:
The test proves the behavior exists. A failing test proves it doesn't—yet.
Types, schemas, and contracts have exactly one source:
| Concern | Source of Truth | Derives |
|---|---|---|
| Input validation | Zod schema | TypeScript types, JSON Schema |
| Error categories | ErrorCategory type | Exit codes, HTTP status |
| CLI flag names | Handler input types | MCP tool parameters |
If two things must stay in sync, one derives from the other.
Bun APIs before npm packages—faster, zero-dependency:
| Need | Use |
|---|---|
| Hashing | Bun.hash() |
| Globbing | Bun.Glob |
| Semver | Bun.semver |
| Shell | Bun.$ |
| SQLite | bun:sqlite |
| UUID v7 | Bun.randomUUIDv7() |
Handlers are pure functions returning Result<T, E>. CLI and MCP are thin adapters over the same logic. Write the handler once, expose it everywhere.
type Handler<TInput, TOutput, TError extends OutfitterError> = (
input: TInput,
ctx: HandlerContext
) => Promise<Result<TOutput, TError>>;
This buys you:
Dependencies flow one direction: Foundation → Runtime → Tooling.
┌─────────────────────────────────────────────────────────────────┐
│ TOOLING TIER │
│ @outfitter/testing @outfitter/tooling │
└─────────────────────────────────────────────────────────────────┘
▲
┌─────────────────────────────────────────────────────────────────┐
│ RUNTIME TIER │
│ @outfitter/cli @outfitter/mcp @outfitter/daemon │
│ @outfitter/config @outfitter/logging @outfitter/file-ops @outfitter/tui │
│ @outfitter/state @outfitter/index @outfitter/schema │
└─────────────────────────────────────────────────────────────────┘
▲
┌─────────────────────────────────────────────────────────────────┐
│ FOUNDATION TIER │
│ @outfitter/contracts @outfitter/types │
└─────────────────────────────────────────────────────────────────┘
| Package | What It Does | Reach For When... |
|---|---|---|
@outfitter/contracts | Result types, errors, Handler contract | Always. This is the foundation. |
@outfitter/types | Type utilities, collection helpers | You need type manipulation |
@outfitter/cli | Commands, output modes, formatting | Building CLI applications |
@outfitter/mcp | Server framework, tool registration | Building AI agent tools |
@outfitter/config | XDG paths, config loading | You have configuration |
@outfitter/logging | Structured logging, redaction | You need logging (you do) |
@outfitter/daemon | Lifecycle, IPC, health checks | Building background services |
@outfitter/file-ops | Atomic writes, locking, secure paths | File operations that matter |
@outfitter/state | Pagination, cursor state | Paginated data |
@outfitter/index | SQLite FTS5, WAL mode, BM25 ranking | Full-text search indexing |
@outfitter/schema | Schema introspection, surface maps, drift detection | CLI/MCP parity docs and CI drift checks |
@outfitter/tui | Terminal UI rendering primitives and prompts | Rich terminal UX (tables, trees, prompts, streaming) |
@outfitter/testing | Test harnesses, fixtures | Testing (always) |
@outfitter/tooling | oxlint, TypeScript, Lefthook presets | Project setup (dev dependency) |
Five things to know when building with the Outfitter stack. For the complete design process with templates, see guides/architecture.md.
Before writing code, understand:
For each domain operation:
Handler<Input, Output, Error1 | Error2>const CreateUserInputSchema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
interface User {
id: string;
email: string;
name: string;
}
const createUser: Handler<unknown, User, ValidationError | ConflictError>;
Ten categories. Memorize the exit codes—you'll use them.
| Category | Exit | HTTP | Class | When |
|---|---|---|---|---|
validation | 1 | 400 | ValidationError | Bad input, schema failures |
not_found | 2 | 404 | NotFoundError | Resource doesn't exist |
conflict | 3 | 409 | AlreadyExistsError | Resource already exists |
conflict | 3 | 409 | ConflictError | Version mismatch, concurrent modification |
permission | 4 | 403 | PermissionError | Forbidden action |
timeout | 5 | 504 | TimeoutError | Took too long |
rate_limit | 6 | 429 | RateLimitError | Too many requests |
network | 7 | 502 | NetworkError | Connection failures |
internal | 8 | 500 | InternalError | Bugs, unexpected errors |
auth | 9 | 401 | AuthError | Authentication required |
cancelled | 130 | 499 | CancelledError | User hit Ctrl+C |
Start with @outfitter/contracts. Always.
@outfitter/cli@outfitter/mcp@outfitter/config (paths) + @outfitter/file-ops (safety)Decide:
Deeper dives into specific topics.
| Guide | What's Covered | Location |
|---|---|---|
| Getting Started | First handler, CLI + MCP adapters | guides/getting-started.md |
| Architecture Design | 5-step process, templates, constraints | guides/architecture.md |
When you need the details, not just the overview.
| Pattern | What's Covered | Location |
|---|---|---|
| Handler Contract | Input, context, Result | patterns/handler.md |
| Error Taxonomy | 10 categories, exit/HTTP mapping | patterns/errors.md |
| Result Utilities | Creating, checking, transforming | patterns/results.md |
| CLI Patterns | Commands, output modes, pagination | patterns/cli.md |
| MCP Patterns | Tools, resources, prompts | patterns/mcp.md |
| Daemon Patterns | Lifecycle, IPC, health checks | patterns/daemon.md |
| File Operations | Atomic writes, locking, paths | patterns/file-ops.md |
| Logging | Structured logging, redaction | patterns/logging.md |
| Testing | Harnesses, fixtures, mocks | patterns/testing.md |
| Converting Code | Migrating to the stack | patterns/conversion.md |
| Schema Introspection | Manifest generation, surface maps, drift detection | patterns/schema.md |
Copy, paste, customize.
| Template | For | Location |
|---|---|---|
| Handler | Transport-agnostic business logic | templates/handler.md |
| Handler Test | Testing handlers with Bun | templates/handler-test.md |
| CLI Command | Commander.js wrapper | templates/cli-command.md |
| MCP Tool | Zod-schema tool definition | templates/mcp-tool.md |
| Daemon Service | Background service with IPC | templates/daemon-service.md |
# Foundation first
bun add @outfitter/contracts
# Then what you need
bun add @outfitter/cli # CLI apps
bun add @outfitter/mcp # MCP servers
bun add @outfitter/logging # Structured logging
bun add @outfitter/config # XDG-compliant config
bun add @outfitter/index # Full-text search
# Dev dependencies
bun add -D @outfitter/testing @outfitter/tooling
New here? Start with guides/getting-started.md.
Use canonical command forms when referencing repo maintenance workflows:
outfitter repo check <docs|exports|readme|registry|changeset|tree|boundary-invocations>outfitter repo sync docsoutfitter repo export docsDo not use removed legacy aliases such as outfitter docs <sync|check|export>,
outfitter repo docs-sync, or outfitter repo check-exports.
(input, ctx) => ResultcreateValidator@outfitter/config)console.log (use ctx.logger)Something not working? Use debug-outfitter for systematic investigation with structured reports. Common issues:
await on async handlersstart(), add .describe() to schema fieldsexitWithError(), not process.exit()If the issue is in Outfitter itself, use outfitter-issue to file a bug.
You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.