One schema, three surfaces
my approach wrt to the agentic backend is to cover cli/api/mcp27 with a single openapi15 schema that is used to generate all three¶
His example is this repository. A capability is a named action with a contract and handler. A contract defines the shared input, output, failures, and metadata. Effect Schema supplies types and runtime checks for these values.¶
The code makes the direction more precise:¶
- A capability contract21 owns Effect schemas.¶
- HttpApi, OpenAPI, CLI, and MCP derive from that contract.¶
- CLI and MCP do not consume the generated OpenAPI JSON.¶
Each interface for calling the action, or surface, shares the same behavior. defineContract owns input, output, failure, description, and annotations. implement binds one handler.¶
A projection builds an interface from a capability.¶
One contract feeds the projections
Contract (Effect schemas + one handler)
├─ toHttpApi ── HTTP + OpenAPI
├─ toCommand ── CLI
└─ toToolkit ── MCP tools - to-http-api.ts derives HttpApi endpoints, the handler Layer, and OpenAPI.¶
- to-command.ts derives flags or positional inputs, schema decoding17, and the command handler.¶
- to-toolkit.ts derives tool schemas, annotations, and the toolkit handler Layer.¶
Where the CLI composes them
apps/cli/src/surfaces.ts composes the projections. command.ts maps registered capabilities to commands. It also prints the generated OpenAPI document.¶
Source: apps/cli/src/surfaces.ts at a646f7ffd00273d85fbe1d905d9cdb194fdc0f60, lines 21–25.¶
export const http = toHttpApi("RatStack", capabilities);
export const tools = toToolkit(capabilities);
export const codeMode = toCodeMode(capabilities);Each call receives the same capability list.¶
One HttpApi definition supplies the HTTP implementation and its OpenAPI document. The broader shared definition is the capability contract. This split avoids three independently maintained schemas.¶
See MCP27, OpenAPI15, and code mode21 for each interface.¶
See how we do it with Effect9 for the whole path.¶
Sources
- Joel Hooks on schema-driven CLI/API/MCP. Primary source. Joel Hooks on schema-driven CLI/API/MCP Accessed 2026-10-02.
- packages/capability/src/contract.ts. GitHub. packages/capability/src/contract.ts Accessed 2026-10-02.
- packages/capability/src/to-http-api.ts. GitHub. packages/capability/src/to-http-api.ts Accessed 2026-10-02.
- packages/capability/src/to-command.ts. GitHub. packages/capability/src/to-command.ts Accessed 2026-10-02.
- packages/capability/src/to-toolkit.ts. GitHub. packages/capability/src/to-toolkit.ts Accessed 2026-10-02.
- apps/cli/src/surfaces.ts. GitHub. apps/cli/src/surfaces.ts Accessed 2026-10-02.
- apps/cli/src/command.ts. GitHub. apps/cli/src/command.ts Accessed 2026-10-02.
Last changed 2026-10-07 in f1a00f2. Source on GitHub. Change log.
Linked from
- Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
Do not spam or retry through another surface when the result is uncertain.
- Agent rules for rat-stack (AGENTS.md). What you may change, which commands to run, and which changes need approval.
- Auth. Email and password sign-in through Better Auth, with the signed-in person provided to each RPC call.
The public sign-in surface is gated by AUTH_ENABLED, which defaults to false.
- Build and run rat-stack (README.md). What is in the repo, how the example works, and how to run it.
Bind a server-side handler, then project the capability onto every agent surface.
- CAFE names the Cloudflare, Alchemy, Foldkit and Effect stack. CAFE is a named stack: Cloudflare runs the app, Alchemy declares it, Foldkit draws the UI, and Effect does the typed work.
The largest difference is the API surface.
- Change log. What changed in the files served here, newest first.
One schema, three surfaces — edited: feat(learn): teach cards 63 to 82 with plain lines, diagrams and snippets
- Effect + Alchemy peer patterns. Source-grounded patterns from repos on nearby Effect and Alchemy pins.
Encode the capability-only surface rule in a test or type-aware lint rule.
- Effect keeps the surfaces on one contract. Schema owns values, projections build interfaces, and Layers supply implementations.
One schema, three surfaces: project the contract instead of maintaining three schemas.¶
- Feature flags. Typed feature flags with owners, removal dates, and stable subject rollouts.
A live operator write surface and listFlagChanges remain future work.¶
- Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
- Infrastructure as effects. Sam Goodwin relates application operations to infrastructure requirements in one typed program.
Each surface derives its input and failure definitions from the same schemas.¶
- learn. Present Effect, Alchemy, XState, and application-design concepts during real work when the operator enables learn mode. Use for adaptive explanations and local learning progress.
Read the public deck through either surface:¶
- MCP is another surface. MCP lists and calls schema-typed tools; rat-stack projects the same handlers instead of adding MCP-only behavior.
In rat-stack, MCP projects the shared handler.¶
- One capability, every surface. A schema-typed contract and one handler feed every interface rat-stack keeps.
Here, a surface is an interface for calling the action.
- OpenAPI describes the refusal too. The generated HTTP contract declares inputs, outputs, and the bodyless 400 returned when decoding fails.
See generating surfaces for the document’s source.¶
- Oxlint rule limits. How the current lint rules draw their syntax boundaries.
- Purpose and boundaries of rat-stack (VISION.md). What this starter is for and what a useful copy should keep.
sh is the teaching surface.
- Rat King binds agents into one encrypted network. Rat King plans an encrypted AT Protocol agent network built on rat-stack.
Every surface projects the same contract.
- Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.
One schema, three surfaces — A capability contract feeds CLI, HTTP, and MCP; OpenAPI is generated output, not their input.
- Schema projections: 2026-09-18 history. Historical Effect rc.115 proposal and implementation receipts from 2026-09-18; use the one-capability-every-surface lore page for the current pattern.
Link context
The interesting question is whether one Effect Schema definition can project into a CLI, a REST API with OpenAPI, an MCP server, and a code-mode surface, and what Executor and Kody teach about the last two.
- Schemas define the boundary. Define accepted data with Schema, decode unknown input before dispatch, and encode results through the declared contract.
See also: one schema, three surfaces, OpenAPI refusals, services, and testing.¶
- Source change history (log.md). The generated change log as Markdown.
One schema, three surfaces — edited: feat(learn): teach cards 63 to 82 with plain lines, diagrams and snippets
- use-a-prompt. Discover and read published rat-stack prompts before starting a task with one.
Call listPrompts through an available capability surface.¶
Unlinked mentions (46)
- A clean playground → A service is a named interface for a job.
- A Layer as constructor → A service is the named interface callers use for a job.¶
- A service captures its construction dependencies → A service is a named interface.
- add-a-capability → Do not write separate business logic for each interface.¶
- add-a-lifecycle-machine → A service is a named interface for one job.
- add-a-store → A service is a named interface for one job.
- Agent rules for rat-stack (AGENTS.md) → Errors are part of the interface.
- Analytics → A service is a named interface for one job.¶
- Build and run rat-stack (README.md) → The host validates the input schema and runs the same handler used by every other surface.
- Capabilities → A service is a named interface for one job.
- Cartridges → A service is the named interface callers use for that job.
- Chris Tate → Generative UI is an interface assembled by a model from supplied components.¶
- Code snippets quote a pinned source → A port is an application-owned interface.
- Configuration is a supplied, validated dependency → A service is a named interface.
- Database → A service is a named interface for one job.¶
- Dependency injection is the reason to choose Effect → A service gives callers a typed interface.
- Devtools → A service is a named interface for one job.¶
- Effect + Alchemy peer patterns → Capability fence: test or lint that every surface, including toRpc, originates from the capability registry; make the test prove its guarded paths exist and contain files.
- Effect 4 study: September 2026 → t3code splits Services/ (tag + shape interface) from Layers/ (implementation) per module.
- Effect basics → A service is a named interface for work.
- Effect keeps the surfaces on one contract → A projection builds an interface from a capability, a named action with a contract and handler.
- Engine-neutral tokens keep adapters replaceable → A highlighter port is an interface for highlighting code, independent of the chosen highlighting tool.
- Fence → A service is a named interface for one job.¶
- HATEOAS → Roy Fielding names it as one of REST’s four uniform-interface constraints in his dissertation, chapter 5, section 5.1.5.
- Hexagonal architecture → A port is an interface for an application job.
- HTTP success needs a status check and a decoder → An adapter implements an application interface for a provider.¶
- Interest signup → Call it through MCP, POST /api/joinInterest, or the command-line interface (CLI).
- keep-or-cut → A projection builds a callable interface from capabilities.¶
- Layer composition makes dependencies explicit → A service is a named interface.
- learn → If they are absent from MCP, report that feedback sign-in is unavailable on that surface.
- Learn mode presents concepts at useful depths → A service is a named interface for one job.¶
- learn-alchemy → A service is a named interface for one job.
- learn-rat-stack → A service is a named interface for one job.
- One capability, every surface → Here, a surface is an interface for calling the action.
- One prompt serves people and agents → A projection builds a callable interface from capabilities.¶
- Purpose and boundaries of rat-stack (VISION.md) → Public GitHub is a steal-the-ideas surface, not a supported product.
- rat-stack → Pieces: A service is an interface for one job.
- rat-stack-mode → md: changing an interface or user-visible behavior.¶
- Run Effect at the integration boundary → A service is a named interface.
- Schema projections: 2026-09-18 history → MCP surface: execute (code mode), skills (server-side guides for the model), tools search / describe / call, plus artifacts.
- Schemas define the boundary → Derive the TypeScript type from the schema rather than maintaining a second interface.
- ship → CLI: command-line interface, a program called through terminal commands.¶
- Structure an Effect codebase by domain → A service is a named interface for that work.
- test-behaviour → An example test fits one interface between parts, such as a service over the real filesystem.
- Tests prove behavior through supplied services → A service is a named interface.
- uncomplect → CLI means command-line interface.