Skip to main content

How to Design OpenAPI Contracts That Guide AI Agents

· 22 min read
Adrian Escutia
La Rebelion Founder

What you will learn

  • How to enrich an OpenAPI contract for progressive tool discovery.
  • How to declare effects, intent, and planning facts for AI agents.
  • How to model business state and human-established facts.
  • How to audit, simulate, and serve the capability graph.
  • How to verify agent behavior in VS Code.

Model Context Protocol (MCP) servers give AI agents access to real systems. When an MCP server exposes every operation of an Application Programming Interface (API) at once, the agent spends its limited context on tool definitions it never uses. It picks the wrong tool, or it claims a capability is unavailable when the right tool exists. HAPI turns an OpenAPI 3.x contract into an MCP server. The HAPI Capability Graph (HCG) adds a deterministic map of business state to that server. OrcA is a Visual Studio Code (VS Code) extension and a specialised harness for MCP and API work. It loads tools progressively, shows the capability graph in a Context tab, and records every exchange in a Traffic view. Clooney, an AI teammate built on Clawne Me, relies on the same harness pattern to work with your APIs.

This guide shows you how to enrich an existing OpenAPI contract so that HAPI, OrcA, and Clooney find the right tool at the right moment without bloating the context. You write discoverable operation metadata, declare effects and intent, and model business state with planning facts. You then declare the facts that only people can establish, describe failure outcomes, audit and simulate the graph, serve it with progressive discovery, and verify the behavior in OrcA. At the end of this guide, you have a contract that produces focused tool discovery, explicit plans, and clear clarification questions for any MCP client.

Key Takeaways​

This section answers the main question of this guide in five statements.

  • Sending every tool definition upfront wastes context and lowers tool-selection accuracy; progressive discovery loads tools as the conversation needs them.
  • Verb-first operationId values and summaries in the language of your users drive tool search.
  • x-hapi-intent, x-hapi-effects, and x-readOnlyHint control ranking and confirmations.
  • x-hapi-planning, x-hapi-client-facts, and x-hapi-outcome turn the contract into plans that ask for missing choices instead of guessing them.
  • hapi graph audit exits with code 1 on gaps, so you can gate contract changes in CI.

Prerequisites​

Before you begin, you need:

  • A valid OpenAPI 3.x contract for an API you own or maintain.
  • The HAPI Command Line Interface (CLI) installed with the Capability Graph plugin. Run hapi --version and hapi graph --help to confirm both commands respond.
  • VS Code with the OrcA extension installed.
  • A chat model configured in OrcA, either through an API key for a supported provider or through a VS Code language model.
  • Working knowledge of YAML and of OpenAPI paths, operations, and responses.
note

The examples use a fictional clinic appointments API with the operations listSpecialties, searchProfessionals, getAvailability, bookAppointment, listPatientAppointments, cancelAppointment, and rescheduleAppointment. Replace the names and facts with the ones from your own domain.

Understand How OrcA Loads Tools Progressively​

This section explains how OrcA decides which tool definitions reach the model, so that each metadata choice in the next sections has a clear purpose.

A Large Language Model (LLM) reads every tool definition you send with each request. Twenty operations with detailed request schemas can consume thousands of tokens before the user says a word, and tool selection accuracy drops as the list grows. OrcA avoids this cost with progressive tool discovery:

  • Upfront tools stay small. OrcA sends a single search_tools tool plus the tools a server marks as always loaded, such as capability_context and capability_plan.
  • A compact catalog lists what exists. The search_tools description names every tool that is not loaded yet, with a one-line purpose for small catalogs. The model knows a booking tool exists before it needs it.
  • Tools load as the conversation moves. Before the model answers each message, OrcA searches your server with that message and loads the matching tools. The model can also call search_tools with a query or with exact tool names.
  • The capability graph adds context. On servers with capability planning, OrcA asks capability_context with each message, shows the result in the Context tab, and loads the tools that the result points to.
  • Loaded tools stay loaded. OrcA appends discovered tools to the conversation and never reorders them, so provider prompt caches keep working. New Chat clears them.

Your contract feeds every step of this flow. Operation names and summaries drive search, intent and effects drive ranking and confirmation, and planning facts drive context and plans. Contracts with weak metadata still work, but the agent searches longer and asks fewer precise questions.

Write Discoverable Operation Names and Summaries​

This section shows you how to write operation identifiers, summaries, and descriptions that search ranks correctly and that fit in the compact catalog.

HAPI search matches tool names, descriptions, and arguments. OrcA shows the first sentence of each description in the catalog, truncated to about 80 characters. Write each operation for both readers: the search index and the model.

  1. Give every operation a stable, verb-first operationId.

    paths:
    /appointments:
    post:
    operationId: bookAppointment

    The operationId becomes the tool name. Stable names keep conversations, saved Traffic sessions, and plans valid across releases.

  2. Start each summary with the action and the business object in the language your users write in.

          summary: Book an appointment for the patient
    description: |
    Books a confirmed appointment in the selected professional's free slot.
    Use the `slotId` returned by getAvailability; a constructed value returns `400`.

    The first sentence of the description becomes the catalog line. Keep it short, specific, and free of internal jargon.

  3. Describe every parameter and request property with the source of its value.

          parameters:
    - name: professionalId
    in: query
    required: true
    description: Identifier from a searchProfessionals result row chosen by the patient.
    schema:
    type: string

    Parameter descriptions tell the model where a value comes from, which prevents invented identifiers.

warning

Search in HAPI matches words, not meaning across languages. A contract written in Spanish does not match an English request such as "book an appointment" through search alone. Write summaries in the language of your users, or add the user-facing terms to the description. OrcA's catalog of tool names reduces the impact, but focused wording still produces better rankings.

Declare Intent, Effects, and Tool Hints​

This section shows you how to tell the graph when an operation is the right choice, what it changes, and how MCP clients such as OrcA decide when to ask for confirmation.

  1. Add x-hapi-capability and x-hapi-intent to every operation.

          x-hapi-capability:
    - Healthcare/Appointments/Booking
    x-hapi-intent:
    use-when:
    - The patient selected a professional and a free slot and confirmed the details.
    avoid-when:
    - The patient only wants to see free slots or existing appointments.

    x-hapi-capability places the operation in a business taxonomy. use-when and avoid-when give the graph the evidence it needs to separate similar operations, such as booking and rescheduling.

  2. Add x-hapi-effects and x-hapi-prerequisites.

          x-hapi-effects:
    mode: create
    side-effecting: true
    destructive: false
    idempotent: false
    requires-confirmation: true
    x-hapi-prerequisites:
    - The patient identity, the professional, and the slot must be verified.

    The graph uses effects to add gates such as mutation-confirmation. Prerequisites explain readiness to people and to the model. They do not create machine state; planning facts do.

  3. Set MCP tool hints explicitly when the HTTP method does not describe the behavior.

      /professionals/search:
    post:
    operationId: searchProfessionals
    x-readOnlyHint: true
    x-destructiveHint: false
    x-idempotentHint: true

    HAPI derives tool annotations from the x-readOnlyHint, x-destructiveHint, x-idempotentHint, and x-openWorldHint extensions. Without them, HAPI marks GET operations as read-only and every POST, PUT, PATCH, and DELETE operation as destructive. OrcA's default confirmation policy asks before every tool that is not read-only, so a search implemented as POST asks for confirmation on every call unless you set x-readOnlyHint: true.

Model Business State with Planning Facts​

This section shows you how to describe what each operation requires and changes, so that capability_context and capability_plan return useful goals and routes.

A fact is a lowercase predicate that names verified business state, such as professional.selected or appointment.booked. A transition is an operation that requires some facts, produces others, and optionally removes facts. The planner searches transitions from the facts that are true now to the goal facts the user wants.

  1. List the business states of your domain before you write metadata.

    OperationRequiresProducesRemoves
    searchProfessionalsnoneprofessionals.consultednone
    getAvailabilityprofessional.selectedavailability.consultednone
    bookAppointmentpatient.identity.verified, professional.selected, slot.selected, appointment.details.confirmedappointment.bookednone
    cancelAppointmentpatient.identity.verified, appointment.selected, cancellation.confirmedappointment.cancelledappointment.booked

    The table exposes gaps early. A mutation that needs a choice must have an operation or a person that establishes that choice.

  2. Add x-hapi-planning to each operation.

          x-hapi-planning:
    requires:
    - predicate: patient.identity.verified
    - predicate: professional.selected
    - predicate: slot.selected
    - predicate: appointment.details.confirmed
    produces:
    - predicate: appointment.booked
    cost: 1

    Every planning entry needs at least one produced fact and a finite, nonnegative cost. A lower cost makes the planner prefer that transition when several routes exist.

  3. Keep searches separate from selections.

      /professionals/search:
    post:
    operationId: searchProfessionals
    x-hapi-planning:
    produces:
    - predicate: professionals.consulted
    cost: 1

    A search produces professionals.consulted. It never produces professional.selected, because a person makes that choice. This boundary makes the plan pause and ask the user instead of picking the first result.

Follow these rules for every fact:

  • Use business state, such as appointment.booked, not commands such as bookAppointment.
  • Avoid wishes and guesses, such as patient.wants.appointment or a date the user did not confirm.
  • Add a cross-domain requirement only when a real business rule demands it.

Declare Facts That Only People Can Establish​

This section shows you how to document the facts that your API never produces, so that the planner and OrcA know who must establish them.

Selections, confirmations, and identity checks happen in the conversation, not in the API. Without a declaration, the graph treats these facts as orphan requirements and reports booking goals as unreachable.

  1. Compile and audit the contract.

    $ hapi graph compile ./clinic.openapi.yaml --out clinic.graph.json
    $ hapi graph audit clinic.graph.json

    The audit output lists the gaps:

    Orphan requirements (required, never produced, not client-verified):
    ✗ appointment.details.confirmed required by bookAppointment
    ✗ professional.selected required by bookAppointment, getAvailability
    ✗ slot.selected required by bookAppointment, rescheduleAppointment
    ...
    Reachability
    ✗ appointment.booked: clarification-required (goal-unreachable, missing-planning-facts)
    Result: gaps found
  2. Declare the client facts at the root of the contract.

    x-hapi-client-facts:
    - predicate: patient.identity.verified
    description: The phone number comes from the chat channel, not from typed text.
    - predicate: professional.selected
    description: The patient picked one professional from the search results.
    - predicate: slot.selected
    description: The patient picked one free time slot.
    - predicate: appointment.details.confirmed
    description: The patient confirmed the appointment summary.

    Each description states who establishes the fact and how. The model reads these descriptions in context results and asks the right question at the right step.

  3. Compile and audit again.

    $ hapi graph compile ./clinic.openapi.yaml --out clinic.graph.json
    $ hapi graph audit clinic.graph.json

    The audit now lists the facts under Client-verified facts, every outcome shows planned, and the result is clean. The command exits with code 1 when gaps remain, so you can add it to a Continuous Integration (CI) pipeline.

Describe Failure Outcomes for Re-Planning​

This section shows you how to declare what a failed response means for business state, so that a client can re-plan instead of retrying blindly.

  1. Add x-hapi-outcome to each non-success response that forces a new decision.

          responses:
    "201":
    description: Appointment booked.
    "409":
    description: The slot was taken by another patient.
    x-hapi-outcome:
    produces:
    - predicate: slot.unavailable
    removes:
    - predicate: slot.selected

    The status key accepts a code, an NXX range, or default, but never a 2xx status. The operation must also declare x-hapi-planning.

  2. Describe the next step in the response description.

    A clear description, such as "The slot was taken by another patient; ask the patient to pick another slot," turns a raw error into a question the agent can ask. The planner never plans through an outcome, but plan steps list outcomes so that clients know which alternatives exist.

Audit, Simulate, and Visualize the Graph​

This section shows you how to test plans before any agent calls your API.

  1. Simulate a goal without verified facts.

    $ hapi graph simulate clinic.graph.json --initial-fact patient.identity.verified --goal-fact appointment.booked

    The output reports Outcome: clarification-required with the diagnostic missing-planning-facts. This result is correct: the patient has not chosen a professional or a slot yet.

  2. Simulate the same goal with every required fact and gate.

    $ hapi graph simulate clinic.graph.json \
    --initial-fact patient.identity.verified \
    --initial-fact professional.selected \
    --initial-fact slot.selected \
    --initial-fact appointment.details.confirmed \
    --goal-fact appointment.booked \
    --satisfied-gate mutation-confirmation \
    --eligible-operation bookAppointment

    The output reports Outcome: planned with one step, operation:bookAppointment. The --eligible-operation option simulates an authorized principal. Without it, the plan reports the unsatisfied operation-authorization gate.

  3. Generate a visual review for domain experts.

    $ hapi graph visualize clinic.graph.json \
    --initial-fact patient.identity.verified \
    --goal-fact appointment.booked \
    --out clinic-flow.html

    Open clinic-flow.html in a browser. The Business State Flow shows which facts each operation requires, produces, and removes. Review it with the people who own the business process; an unexpected gap is contract feedback, not a planner error.

Serve the API with Progressive Discovery​

This section shows you how to choose the HAPI flags that match the level of guidance you need.

  1. Serve with deferred tool search only.

    $ hapi serve --specs ./clinic.openapi.yaml --deferred-tool-search --port 3000

    HAPI marks operation tools as deferred and adds a tool_search tool. OrcA detects it and enables progressive discovery. Use this level when you want smaller context without planning metadata.

  2. Serve with the capability graph and planning.

    $ hapi serve --specs ./clinic.openapi.yaml --capability-graph --capability-planning --port 3000

    --capability-graph compiles the graph and implies deferred search with graph-aware ranking. --capability-planning adds the read-only capability_context and capability_plan tools. Neither tool calls your API, stores state, or grants authorization.

  3. Serve an existing backend in headless mode.

    $ hapi serve --specs ./clinic.openapi.yaml --headless --url https://api.example.com --capability-graph --capability-planning --port 3000

    Headless mode exposes only the MCP endpoint and forwards calls to the backend at --url. Add --dev --capability-trace during development to emit redacted graph diagnostics.

note

Without these flags, HAPI serves every tool upfront, as before. OrcA then sends every definition to the model unless you set orca.chat.toolDiscovery to always, which applies OrcA's local search to any server.

Connect the Server in OrcA​

This section shows you how to add your server to OrcA and confirm that progressive discovery and the capability graph are active.

  1. Open the MCP Servers view in the OrcA activity bar.

  2. Add the server.

    • For a contract in your workspace, start it with HAPI from OrcA so that it appears as a local run.
    • For a running server, add an external server with a name such as clinic, the URL http://localhost:3000/mcp, and any required headers. OrcA stores header values in the VS Code secret storage.
  3. Connect the server and hover over it.

    The tooltip shows a Discovery line, for example "Progressive tool discovery: tool_search (8 of 11 tools deferred) · Capability graph: capability_context, capability_plan". Deferred tools carry a deferred marker in the tool list.

  4. Open the Harness tab in the OrcA view.

    The preview shows the tokens of the tools sent upfront compared with all definitions, per server. Use this number to measure the context you save.

  5. Review the discovery settings.

    SettingDefaultPurpose
    orca.chat.toolDiscoveryautoauto defers tools on servers with search, always searches every server, off sends every tool.
    orca.chat.toolSearchLimit5Maximum tools returned per server for each search.
    orca.chat.toolPrefetchtrueSearches and asks capability_context with each message before the model answers.
    orca.chat.confirmToolsmutatingAsks before tools that are not read-only.

Verify the Behavior in Chat, Context, and Traffic​

This section shows you how to confirm that the agent discovers tools as the conversation progresses and that plans ask for the right facts.

  1. Start a multi-step conversation in the Chat tab.

    Find a pediatrician in Springfield.

    A card titled Tools loaded for your request lists the tools OrcA loaded before the model answered, such as searchProfessionals.

  2. Move the conversation to the next step.

    Book the first one for next Tuesday afternoon.

    OrcA loads getAvailability and bookAppointment for this message. The model asks you to choose a slot and to confirm the details, because the plan requires slot.selected and appointment.details.confirmed. OrcA shows an inline confirmation before the booking call because the tool is not read-only.

  3. Open the Context tab.

    Under From Chat, each message lists the candidate operations, goal facts, required facts, and gates. Select Plan This Goal on appointment.booked, enter the facts you verified, one per line, and select Create Plan. The plan lists the steps, the missing facts, and the unsatisfied gates.

  4. Open the Traffic view.

    Each model round, search, context request, and tool call appears with its latency, size, tokens, and cost. Select a tool call to see the model round that caused it. Use Show in Traffic in the Context tab to jump to the exact capability_context exchange.

  5. Check the Activity tab.

    The tab records connections, new chats, and failures with a plain explanation of which service failed and why.

Troubleshoot Common Discovery Problems​

This section maps common symptoms to the contract change that fixes them.

SymptomLikely causeFix
The model says it has no tool for an action.The operation summary does not match the user's words or language.Rewrite the summary and description with user-facing terms, and confirm the tool appears in the catalog.
Search returns unrelated tools.Generic summaries such as "Process request".Use verb-first summaries and specific use-when statements.
Every search call asks for confirmation.A read-only operation uses POST without hints.Add x-readOnlyHint: true and x-destructiveHint: false.
Plans return clarification-required for every goal.Orphan requirements.Run hapi graph audit and declare x-hapi-client-facts.
The agent picks the first search result.A search produces a selection fact.Produce a consultation fact and declare the selection as a client fact.
The agent retries a failed booking.The failure response has no outcome.Add x-hapi-outcome and a description that names the next question.

Frequently Asked Questions​

This section answers the questions readers ask most often about this topic.

What Is MCP Context Bloat, and How Do I Avoid It?​

Context bloat happens when an MCP server sends every tool definition with every request, so the model spends its context on tools it never uses and picks the wrong one more often. Serve the contract with --capability-graph or --deferred-tool-search, and let a harness such as OrcA load tools progressively.

What Is Progressive Tool Discovery?​

Progressive tool discovery sends a small search tool and a few always-loaded tools instead of the full catalog. OrcA loads matching tools before the model answers each message, lists the names of the remaining tools so the model knows they exist, and appends discovered tools without reordering them, which keeps prompt caches valid.

What Are the X-Hapi Extensions in OpenAPI?​

They are OpenAPI extensions that HAPI compiles into its capability graph: x-hapi-capability and x-hapi-intent for discovery, x-hapi-effects and x-hapi-prerequisites for behavior, x-hapi-planning for state transitions, x-hapi-client-facts for facts people establish, x-hapi-outcome for failure responses, and x-hapi-event for webhooks.

Why Does capability_plan Always Return Clarification-Required?​

Usually because some required facts are never produced by an operation and are not declared as client facts. Run hapi graph audit to list these orphan requirements, then declare selections, confirmations, and identity checks in x-hapi-client-facts.

Why Does Every Search Tool Ask for Confirmation?​

HAPI marks every POST, PUT, PATCH, and DELETE operation as destructive unless the contract sets tool hints, and clients such as OrcA ask before any tool that is not read-only. Add x-readOnlyHint: true and x-destructiveHint: false to read-only searches that use POST.

Does HAPI Tool Search Work Across Languages?​

No. HAPI search matches words in tool names, descriptions, and arguments. Write summaries in the language of your users, or add their terms to descriptions; OrcA's catalog of tool names reduces, but does not remove, the impact of a language mismatch.

Conclusion​

You enriched an OpenAPI contract so that HAPI, OrcA, and Clooney find the right tool at the right moment. Verb-first operations and precise descriptions drive progressive discovery. Intent, effects, and tool hints separate similar operations and keep confirmations meaningful. Planning facts, client facts, and response outcomes turn the contract into a capability graph that asks for missing choices instead of guessing them. You audited and simulated the graph, served it with progressive discovery, and verified the behavior in OrcA's Chat, Context, Traffic, and Activity views.

Apply the same process to the next API in your portfolio. Run hapi graph audit in your CI pipeline to catch new gaps, and compare the token preview in the Harness tab before and after each change to measure the context you save.