ISSUE Nº 001
2-COLOR RISO
PINK / BLUE

Field guide · Rev A · Shaping APIs for agents

Endpoints are not tools.

Your API speaks REST — resources, verbs, cursors, status codes. Agents speak intent. Everything interesting about the Model Context Protocol lives in the translation between the two. This is a field guide to that translation: where naive conversion breaks, and four ways to do it properly.

32
REST endpoints
28
Schemas
18
Curated actions
5 +long tail
Intent tools published
Plate 00 · The shaping pipelinepulled in 2 passes
The shaping pipeline Eight REST endpoints flow through a shaping stage and converge into three intent tools plus a dynamic long-tail meta-tool. SHAPE · GUARD · TEST GET/restaurants GET/restaurants/{id}/menu POST/cart/items POST/orders GET/orders/{id}/tracking POST/orders/{id}/issue GET/promotions GET/profile · +24 more… order_food() search → menu → cart → order ⚑ APPROVAL REQUIRED · WRITE track_and_resolve() tracking → issue → refund path explore_menu() menu + promos, shaped response search_tools() · invoke() — long tail
Fig. 00 — The shaping pipeline, live request flow · write path · ⚑ human gate
The 1:1 fallacySHT 01 / 07

Naive conversion is where
everyone starts — and stalls.

Point a generator at an OpenAPI spec, mint one tool per endpoint, ship it. The result compiles but doesn't work: agents pick wrong tools, drown their own context, and fumble multi-call chains. Four structural reasons why.

F·01

Tool count explosion

32 endpoints become 32 tools. Selection accuracy degrades sharply past ~15–20 tools — every definition competes for the model's attention on every single turn.

F·02

Wrong abstraction level

The user wants "order dinner." The API offers POST /cart/items then POST /orders. The agent must plan a four-call chain, thread IDs through it, and not lose state halfway.

F·03

Resource-oriented naming

getOrderTracking describes a route, not a reason. Agents choose tools by name and description — REST semantics carry almost no signal about when to call.

F·04

Docs written for humans

OpenAPI summary fields are terse, missing, or assume a developer reading in context. No examples, no "use when / don't use when." Agents fly blind.

100% 0 TOOLS EXPOSED → TASK ACCURACY ~15 · TOOL BUDGET SHAPED SET LIVES HERE 1:1 PASSTHROUGH

The tool budget is real, and it's small.

Every tool definition is paid for in context tokens and attention on every turn — before the agent does anything. Past a budget of roughly fifteen tools, selection errors and token burn compound each other.

So the design question inverts. Not "how do I expose my API?" but "what is the smallest set of tools that covers real intents?"

Fig. 01 — schematic, illustrative curve

Anatomy of a shaped toolSHT 02 / 07

One endpoint, four operations
on the way to becoming a tool.

Even before composing intents, a single endpoint needs surgery. Watch the same operation cross the shaping band — the highlight cycles through each transformation.

Before · openapi.yamlmachine-shaped
GET /restaurants:
  operationId: listRestaurants
  summary: List restaurants.
  parameters:
    - lat            # number, required
    - lng            # number, required
    - cuisine        # string enum, 41 values
    - page_cursor    # opaque string
    - include_meta   # boolean
  responses:
    200: RestaurantList
         # 47 fields · ~52 KB typical
After · tool definitionagent-shaped
{
  "name": "search_restaurants",
  "description": "Find restaurants near a place.
     Use when the user names a craving or an
     area. Not for reorders → reorder_last().",
  "input": {
    "near":    "string — address or place name",
    "craving": "string — cuisine or dish, optional"
  },
  "returns": "top 5 · name, rating, eta,
              price_band  // ~0.4 KB
  "guards": "read · safe · no approval"
}

Intent naming. Rename from route-derived identifiers to verb-first intent names, and attach a safety classification the runtime can enforce.

Response shaping, elaboratedSHT 03 / 07

The response is
the context bill.

A tool response isn't data delivery — it's a deposit into the agent's working memory, re-read on every turn that follows. APIs return everything a UI might render someday. An agent needs exactly enough to choose its next action. Shape for the decision, not the data.

GET /restaurants · one record
name "Mission Chinese Food"✓ decision
id r-001 · travels with its label✓ chaining
rating 4.3✓ decision
eta_minutes 25–35✓ decision
price_band $$ · computed from 3 fields✓ derived
image_urls[12]18.2 KB
review_excerpts[50]14.6 KB
geo_polygon6.1 KB
opening_hours_full3.2 KB
partner_metadata2.8 KB
i18n_names[14]2.1 KB
delivery_zones1.9 KB
tax_config0.9 KB
internal_flags0.4 KB
seo_slug · analytics_tags · …+28 fields
47 fields · ~52 KB→ 5 fields · 0.4 KB
1 : 130

tokens the agent needed vs tokens it was sent — to scale

shaped · 1 cell ≈ 100 tknaive overhead · 129 cells

Responses persist. By the last step of a six-call task the agent is re-reading every earlier payload on every turn — ~78,000 tokens of JSON carried before it decides anything. Shaped: ~600.

And it's not just cost. Attention is finite: the answer to "which restaurant?" is one line drowning under forty-six the agent will never use. Payload noise is selection error.

Plate 03 · The drill-down contract3 print tiers
AGENT earns detail search_restaurants() top-5 summary · 0.4 KB TIER 1 · DEFAULT answers most turns get_restaurant_details(id) full record · 2 KB · on request TIER 2 · EARNED only when deciding needs it resource: raw record 52 KB · audit & debug only TIER 3 · OFF HOT PATH never auto-loaded
Fig. 03 — the drill-down contractsummary by default · detail on demand · raw off the loop
T·01

Field allowlist

Every tool carries a response contract — an architect-editable allowlist. What isn't listed doesn't ship. The response schema becomes a design surface, not an accident of the backend.

T·02

Computed fields

Distill for decisions: min/max delivery fees collapse into price_band; fifty review excerpts into one rating. The derived field is usually the only one the agent ever needed.

T·03

Top-N + continuation

Cap every list. Return more_available: true instead of page eleven. The agent asks for more only when the task genuinely demands it — which is almost never.

T·04

Label–ID pairing

Opaque IDs travel with their human names — r-001 · "Mission Chinese" — so the model chains identifiers it can anchor instead of hallucinating ones it can't.

T·05

Summary / detail split

Two tools, two costs. The cheap one answers most turns; the expensive one exists precisely so the cheap one can stay cheap. Never merge them back.

T·06

Raw escape hatch

The unshaped payload lives on as an MCP resource — reachable for audits, debugging, and distrustful architects, but never in the agent's loop by default.

★ The design rule

A response should contain exactly what the agent needs to choose its next action — plus a path to more. Everything else is a tax collected on every turn that follows.

The gap catalogSHT 04 / 07

Fifteen gaps between
a spec and a working agent.

The same fifteen, mapped onto the request path. Shape gaps live where tool definitions meet the agent. Behavior gaps live on the wire at runtime. Trust gaps are the plane underneath — they span every hop. Select a marker, or let it walk the path.

SHAPE · 01–05 BEHAVIOR · 06–11 AGENTTOOL SURFACE RUNTIMEUPSTREAM API context lives herenames · schemas · docs mcpify / gatewayyour REST estate request → ← response TRUST PLANE · 12–15 — SPANS EVERY HOP 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
01
tool surface · shape

Granularity

free

··
free  belongs in the open build layer gateway  belongs in the governance layer GAP 14 — verification — is the sleeper: nobody measures it.
Four ways to shapeSHT 05 / 07

Curate, facade,
compile, or defer.

Four architectures for turning N endpoints into a toolset an agent can actually drive. They're not rivals — they're layers. But one of them has to lead.

A

Curated 1:1

Select a subset, rename, rewrite descriptions, simplify schemas. One endpoint = one tool.

Transparent — architects can audit the mapping.

Chattiness survives; caps at ~15–20 tools.

Fit — small, read-heavy APIs

B

Capability facades

One tool per capability with an operation discriminator. Six tools for a 32-endpoint API.

Tiny tool count; scales to 500-endpoint estates.

Fat union schemas confuse mid-tier models; per-op safety gets awkward.

Fit — auto-fallback for huge APIs

C

Intent compiler

Compile task-level tools that orchestrate multiple endpoints server-side. The agent makes one call; IDs and state never leave the runtime.

Best task success; biggest token savings; safety attaches at the intent — how humans reason about risk.

Needs an orchestration runtime: partial failure, compensation, timeouts. Composites must be eval-tested.

Fit — transactional APIs · the default publish

D

Dynamic toolset

Three meta-tools — search, describe, invoke. The agent discovers what it needs, when it needs it.

Unbounded API size at near-zero context cost; invoke is a single governance chokepoint.

Extra round trips; weaker models fumble discovery; undramatic in demos.

Fit — the long tail behind the intents

AxisA · CuratedB · FacadeC · Intents ★D · Dynamic
Agent task success●●●●●
Token efficiency●●●●●●●●
Scales to huge APIs●●●●●●●
Auditability●●●●●●●
Governance attach pointper toolper capabilityper intent ★per invoke ★
Build costlowlowhighmedium
The layered answerSHT 06 / 07

Intents lead. The long tail
defers. Curation is the exit row.

The directions compose. Publish a small set of compiled intents as the face of the server, keep every primitive reachable through meta-tools, and leave a flat curated mode for architects who want to see every wire.

Layer 1 · default publishC

Compiled intents

5–8 task-level tools — order_food, track_and_resolve — orchestrated server-side. Each one is a testable task and a governable unit: place_order requires approval is exactly how risk owners think.

Layer 2 · long tailD

Dynamic discovery

search_tools / describe / invoke expose every curated primitive at near-zero context cost. Invoke doubles as the audit and policy chokepoint.

Layer 3 · escape hatchA

Flat curated mode

One switch flips the server to transparent 1:1 tools. A trust feature for skeptical architects — cheap to keep, disproportionately reassuring.

Build sequence

Now

Ship A + shape fixes

Curated tools with naming, schema diet, response shaping, guidance. Mostly built.

+ weeks

Add D meta-tools

Unlocks large-API prospects immediately; establishes the invoke chokepoint governance will use.

Quarter

Build the C runtime

Orchestration with failure semantics — behind a beta flag, gated on eval pass rates.

Never*

Facades (B)

*Unless real APIs prove too large even for curation + discovery. Don't build it first.

★ Why intents win strategically

An intent is a testable task — "order a meal under $30" either passes or fails. Compiled intents plus auto-generated evals produce the one trust metric nobody else can print: 12/12 agent tasks pass.

Beyond toolsSHT 07 / 07

Not everything
should be a tool.

MCP ships four primitives; almost every converter uses one. Classifying each capability into the right primitive — not just tool-or-skip — is instant, visible sophistication.

Tools

Actions with consequences

Reserved for operations that do something. Every tool costs context on every turn — spend the budget on verbs, not lookups.

Resources

Reference data, cached

Menus, promo lists, docs — served as resources, not read tools. The client caches them: get_menu stops costing a round trip.

Prompts

Workflows, user-invokable

Drafted workflows double as MCP prompts — "file a delivery complaint" becomes a pre-parameterized entry the user picks, not a chain the agent guesses.

Elicitation

Human gates, in-flow

Mid-workflow approvals and OTP steps without breaking the run. This is how place_order approval should actually work — a pause, not a dead end.