# One schema, three surfaces

> For agents: start with the [agent guide](https://ratstack.sh/llms.txt). Every page is Markdown by default; add `Accept: text/html` for HTML.

Free workshop: [how to burn a trillion tokens and get good results](/tokenmaxx#interested).

[Joel writes in a reply](https://x.com/joelhooks/status/2106018556496457773):

> my approach wrt to the agentic backend is to cover cli/api/mcp with a single openapi schema that is used to generate all three

His example is this repository. The code makes the direction more precise:

- A capability contract owns Effect schemas.
- HttpApi, OpenAPI, CLI, and MCP derive from that contract.
- CLI and MCP do not consume the generated OpenAPI JSON.

A **surface** is an invocation interface over shared behavior. [`defineContract`](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/contract.ts) owns input, output, failure, description, and annotations. `implement` binds one handler.

## One contract feeds the projections

```text
Contract (Effect schemas + one handler)
├─ toHttpApi ── HTTP + OpenAPI
├─ toCommand ── CLI
└─ toToolkit ── MCP tools
```

**What to notice:** HttpApi is one projection, not the source of the CLI and MCP projections.

- [to-http-api.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-http-api.ts) derives HttpApi endpoints, the handler Layer, and OpenAPI.
- [to-command.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-command.ts) derives flags or positional inputs, schema decoding, and the command handler.
- [to-toolkit.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-toolkit.ts) derives tool schemas, annotations, and the toolkit handler Layer.

## Where the CLI composes them

[`apps/cli/src/surfaces.ts`](https://github.com/joelhooks/rat-stack/blob/main/apps/cli/src/surfaces.ts) composes the projections. [`command.ts`](https://github.com/joelhooks/rat-stack/blob/main/apps/cli/src/command.ts) maps registered capabilities to commands. It also prints the generated OpenAPI document.

Source: [`apps/cli/src/surfaces.ts` at `445b1a20b51a5492f8e586daeb9306e8011b7673`, lines 21–25](https://github.com/joelhooks/rat-stack/blob/445b1a20b51a5492f8e586daeb9306e8011b7673/apps/cli/src/surfaces.ts#L21-L25).

```ts
export const http = toHttpApi("RatStack", capabilities);

export const tools = toToolkit(capabilities);

export const codeMode = toCodeMode(capabilities);
```

**What to notice:** 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 [MCP](/lore/mcp-is-another-surface), [OpenAPI](/lore/openapi-describes-the-refusal-too), and [code mode](/lore/one-program-can-replace-several-tool-calls) for each surface.

See [how we do it with Effect](/lore/how-we-do-it-with-effect) for the whole path.

## Sources

1. [Joel Hooks on schema-driven CLI/API/MCP](https://x.com/joelhooks/status/2106018556496457773)
   Primary source. Joel Hooks on schema-driven CLI/API/MCP Accessed 2026-10-02.

2. [packages/capability/src/contract.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/contract.ts)
   GitHub. packages/capability/src/contract.ts Accessed 2026-10-02.

3. [packages/capability/src/to-http-api.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-http-api.ts)
   GitHub. packages/capability/src/to-http-api.ts Accessed 2026-10-02.

4. [packages/capability/src/to-command.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-command.ts)
   GitHub. packages/capability/src/to-command.ts Accessed 2026-10-02.

5. [packages/capability/src/to-toolkit.ts](https://github.com/joelhooks/rat-stack/blob/main/packages/capability/src/to-toolkit.ts)
   GitHub. packages/capability/src/to-toolkit.ts Accessed 2026-10-02.

6. [apps/cli/src/surfaces.ts](https://github.com/joelhooks/rat-stack/blob/main/apps/cli/src/surfaces.ts)
   GitHub. apps/cli/src/surfaces.ts Accessed 2026-10-02.

7. [apps/cli/src/command.ts](https://github.com/joelhooks/rat-stack/blob/main/apps/cli/src/command.ts)
   GitHub. apps/cli/src/command.ts Accessed 2026-10-02.

## Lore on this page

- [MCP is another surface](/lore/mcp-is-another-surface)
- [OpenAPI describes the refusal too](/lore/openapi-describes-the-refusal-too)
- [Capabilities](/systems/capabilities)
- [Schemas define the boundary](/lore/schemas-define-the-boundary)

## Linked from

- Agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms.txt)

- AGENTS.md → What you may change, which commands to run, and which changes need approval. → [Read page](https://ratstack.sh/AGENTS.md)

- An MCP your users want → Rhys Sullivan's practical guide gives rat-stack a checklist for useful MCP tools. → [Read page](https://ratstack.sh/lore/an-mcp-your-users-want)

- Capabilities → Domain behaviors on generated surfaces share one schema-typed contract and one handler. → [Read page](https://ratstack.sh/systems/capabilities)

- Change log → What changed in the files served here, newest first. → [Read page](https://ratstack.sh/log)

- Effect + Alchemy peer patterns → Source-grounded patterns from repos on nearby Effect and Alchemy pins. → [Read page](https://ratstack.sh/resources/same-version-repos.svx)

- Effect keeps the surfaces on one contract → Schema owns values, projections build interfaces, and Layers supply implementations. → [Read page](https://ratstack.sh/lore/how-we-do-it-with-effect)

- Full agent guide → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/llms-full.txt)

- HATEOAS → Links in a representation tell its reader what to do next; rat-stack applies that idea to agent surfaces. → [Read page](https://ratstack.sh/lore/hateoas)

- Hexagonal architecture → Ports name the application's jobs; adapters connect them to the outside world. → [Read page](https://ratstack.sh/lore/hexagonal-architecture)

- Hosted agent front door → Discovery documents, HTTP, MCP, A2A, and bounded code mode expose the public reference to agents. → [Read page](https://ratstack.sh/systems/agent-front-door)

- log.md → The generated change log as Markdown. → [Read page](https://ratstack.sh/log.md)

- MCP is another surface → MCP lists and calls schema-typed tools; rat-stack projects the same handlers instead of adding MCP-only behavior. → [Read page](https://ratstack.sh/lore/mcp-is-another-surface)

- One capability, every surface → A schema-typed contract and one handler feed every interface rat-stack keeps. → [Read page](https://ratstack.sh/lore/one-capability-every-surface)

- OpenAPI describes the refusal too → The generated HTTP contract declares inputs, outputs, and the bodyless 400 returned when decoding fails. → [Read page](https://ratstack.sh/lore/openapi-describes-the-refusal-too)

- Oxlint rule limits → How the current lint rules draw their syntax boundaries. → [Read page](https://ratstack.sh/resources/lint-rule-limits.svx)

- Rat Stack lore | rat-stack → Short, source-grounded notes on the ideas and decisions behind rat-stack. → [Read page](https://ratstack.sh/lore)

- rat-stack → An Effect stack so pure (aspirational) Kit Langton will blush. → [Read page](https://ratstack.sh/)

- README.md → What is in the repo, how the example works, and how to run it. → [Read page](https://ratstack.sh/README.md)

- 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. → [Read page](https://ratstack.sh/resources/schema-projections-and-code-mode.svx)

- Tests that earn their place → A test earns its place by covering behavior the existing suite does not prove. → [Read page](https://ratstack.sh/lore/tests-that-earn-their-place)

- VISION.md → What this starter is for and what a useful copy should keep. → [Read page](https://ratstack.sh/VISION.md)

## Unlinked mentions

- [AGENTS.md](https://ratstack.sh/AGENTS.md) → One contract, every surface.

- [Capabilities](https://ratstack.sh/systems/capabilities) → No hand-rolled surface.

- [Effect + Alchemy peer patterns](https://ratstack.sh/resources/same-version-repos.svx) → Fence: move rat-stack’s capability-only surface rule from prose into a test or type-aware lint rule, and include a check that the guard cannot go vacuous.

- [One capability, every surface](https://ratstack.sh/lore/one-capability-every-surface) → To add one, define a contract, implement it beside the data or infrastructure it owns, register it in capabilities, then check each surface you keep.

- [rat-stack](https://ratstack.sh/) → 115 proposal and implementation receipts from 2026-09-18; use the one-capability-every-surface lore page for the current pattern.

- [README.md](https://ratstack.sh/README.md) → \&lt;name\&gt;(input), which the host validates against that capability’s input schema and runs through the same handler as every other surface.

- [Schema projections: 2026-09-18 history](https://ratstack.sh/resources/schema-projections-and-code-mode.svx) → MCP surface: execute (code mode), skills (server-side guides for the model), tools search / describe / call, plus artifacts.

- [VISION.md](https://ratstack.sh/VISION.md) → Public GitHub is a steal-the-ideas surface, not a supported product.
