---
title: Towards AI Friendly Web APIs
description: 'MCP and CLIs made APIs usable for agents by adding metadata, workflows, discovery, and better errors. Maybe the next step is to move this logic back into web APIs and OpenAPI specs.'
date: 2026-05-05
updated: 2026-05-06
tags:
  - ai
  - agents
  - api
  - openapi
  - developer-experience
---

## TL;DR

Ask your AI agent:

```text
Make API AI-friendly https://chaliy.name/blog/towards-ai-friendly-web-apis/
```

---

First, we moved to adapters. MCP was a way to wrap existing systems into tools that agents can understand. Add metadata, schemas, resources, prompts, discovery. Make API-shaped things look like agent-shaped things.

Then community started moving to CLIs and skills. This also makes sense. Agents understand shell super well. CLIs usually have better semantics than raw APIs, better help, better errors, and much better composition.

In both cases we are solving same root problem: most existing APIs are not very well suited for agents.

Maybe now is good moment to go back to APIs themselves. Not to kill MCP. Not to kill CLIs. But to make web APIs more AI-friendly, so adapters do not need to re-implement all domain semantics by hand.

![AI agent calling a web API](agent-calls-apis.svg)

## Why We Wrapped APIs

Existing OpenAPI specs were usually not enough. They described paths, parameters, request bodies, responses. Good for generating SDKs. Good for docs. Not enough for agent that needs to understand what to do next.

MCP and CLIs gave us few missing things:

1. **Good metadata.** What operation does, what inputs are expected, what values are possible.
2. **Better granularity.** Higher-level operations instead of many tiny resource updates.
3. **Discoverability.** Agent can list tools, read descriptions, run help, inspect examples.
4. **Business errors.** Not just `409 CONFLICT`, but "this resource is already started, stop it first or use restart."
5. **Human language.** Names and explanations closer to how people talk about the domain.

That is useful. But most of this is not inherently MCP or CLI thing.

This is API design thing.

## Models Got Better

Two or three years ago models were worse at reading specs, keeping constraints in head, and recovering from failed calls.

Today story is different.

Agents can read more context, reason better about data shapes, and retry with more useful changes. This makes richer API contracts more valuable than before.

So maybe OpenAPI spec can become the source of truth for agent-facing semantics.

Not because MCP is bad. Not because CLIs are bad. I love CLIs. But duplicated business metadata rots super fast. If API says one thing, CLI says another, and MCP says third thing, agent will eventually pick the wrong one.

![AI-friendly OpenAPI as source of truth for MCP, CLI, SDK, and direct agent calls](ai-friendly-api-source-of-truth.svg)

## What Makes Web API AI Friendly

Here is the checklist I am starting to use.

![Six principles for making web APIs more AI-friendly](ai-friendly-api-principles.svg)

### 1. Emphasize Semantic Operations

Be less fanatical about pure resource state modifications.

I would rather have this:

```http
POST /resources/{resourceId}/start
```

than this:

```http
PUT /resources/{resourceId}
Content-Type: application/json

{
  "status": "started"
}
```

"Start resource" is a business operation. It has preconditions. It may allocate something. It may trigger workflow. It may fail because resource is paused, locked, already running, missing configuration, or user has no quota.

For agent, operation shape matters. It maps directly to tool calling. It gives semantic handle to reason about.

Put good `operationId` on it too:

```yaml
operationId: startResource
summary: Start resource
description: |
  Starts a stopped resource and schedules runtime allocation.
  Use this operation when user asks to start, run, wake up, or resume work.
```

### 2. Make OpenAPI Spec Self-Sufficient

Most OpenAPI specs say:

```yaml
resourceId:
  type: string
```

Thanks. Agent still has no idea where to get it.

Better:

```yaml
resourceId:
  type: string
  description: |
    Stable internal identifier of the resource.
    If user provides resource number like RES-1842, first call findResourceByNumber.
    Do not ask user for this value unless lookup returns multiple resources.
```

Add acceptable values. Add validation rules. Add examples that match real domain. Add references to lookup operations. Say when to use field and when not to use it.

The key point: agent should be able to do preflight thinking before request.

### 3. Embed Domain Assumptions

Endpoint descriptions and model descriptions are good place for domain knowledge.

Not novels. Not full manual. But enough ubiquitous language so agent understands the world.

```yaml
Resource:
  description: |
    A Resource is a provisioned runtime unit owned by workspace.
    Users identify it by number, for example RES-1842.
    Internal services identify it by id.

    Lifecycle:
    created -> configured -> started -> stopped -> archived.
    Archived resources cannot be started again.
```

OpenAPI has models. This is one place where OpenAPI is stronger than flat tools or CLI help. Use it. Explain lifecycle, boundaries, relationships, canonical names, and what should not be done.

### 4. Give Actionable Errors

This is the most fun part of agents: they try to recover.

Most APIs do not help with recovery at all.

```json
{
  "code": "RESOURCE_NOT_READY"
}
```

Okay. What now?

Better:

```json
{
  "type": "https://api.example.com/problems/resource-not-ready",
  "title": "Resource is not ready",
  "detail": "Resource RES-1842 is still configuring and cannot be started.",
  "currentState": "configuring",
  "retryAfterSeconds": 45,
  "allowedActions": [
    {
      "operationId": "getResource",
      "reason": "Check current lifecycle state before retrying."
    }
  ]
}
```

Use RFC 9457 as base shape. Add domain extension fields. Include violated rules, possible values, retry information, and operations that can fix the issue.

For agents this is difference between "I failed" and "I know what to do next."

### 5. Build Navigation Into APIs

Remember hypermedia links? Same here.

Agents can follow links. Agents can discover relations from links. Agents can use canonical URLs and UI URLs to explain things back to humans.

```json
{
  "id": "rsc_9x82",
  "number": "RES-1842",
  "displayName": "Payments worker",
  "state": "stopped",
  "_links": {
    "self": {
      "href": "https://api.example.com/resources/rsc_9x82"
    },
    "ui": {
      "href": "https://app.example.com/resources/RES-1842"
    },
    "start": {
      "href": "https://api.example.com/resources/rsc_9x82/start",
      "method": "POST",
      "operationId": "startResource"
    }
  }
}
```

Also expose metadata endpoints: allowed regions, lifecycle transitions, current user capabilities, supported import formats, known labels.

Do not force agent to hallucinate enum values from docs page. Make values fetchable.

### 6. Include Human Representations

Agents talk to humans. APIs should tell agents which fields humans understand.

In many systems humans say "number" and backend says "id." Humans say "Payments worker" and backend says `rsc_9x82`. Humans expect link to UI, not raw JSON.

Make this explicit.

```yaml
Resource:
  properties:
    id:
      type: string
      description: Internal stable id. Do not show to end users unless debugging.
    number:
      type: string
      description: Human-facing identifier. Use this when asking user to confirm resource.
    displayName:
      type: string
      description: Human-facing display name. Safe to use in messages and summaries.
```

If agent needs to say "I started Payments worker (RES-1842)" it needs to know what "Payments worker" and "RES-1842" mean.

Human representation is part of API contract now.

## Granularity Still Matters

Most web APIs are too granular for agents.

A good CLI command may do five API calls. A good MCP tool may hide lookup, validation, operation, and formatting behind one function.

API can do this too when action is common enough:

```http
POST /resources/start-by-number
```

```json
{
  "number": "RES-1842"
}
```

Another option is workflow metadata. Keep primitive API operations, but describe sequence:

1. find resource by number
2. check current state
3. call start
4. poll operation status
5. return human summary

I do not know yet which one is better. Probably both. Endpoint for most common business action, workflow spec for composable multi-step processes.

The important thing is not to force every agent client to rediscover same sequence.

## What This Changes

MCP and CLIs should not be the only place where agent-friendly semantics live.

If API returns actionable error, CLI can just print it. MCP server can pass it through. If OpenAPI spec knows that `number` is human-facing identifier, agent clients, MCP generator, SDK docs, and CLI help can all reuse same truth.

This is directionally better.

Less duplication. Less drift. More boring infrastructure.

## Related Work

This idea is not new. We just have new pressure from agents.

[OpenAPI Links](https://learn.openapis.org/specification/links.html) already describe relationships between responses and follow-up operations.

[Arazzo](https://spec.openapis.org/arazzo/latest.html) describes API workflows in human-readable and machine-readable way. Super relevant, because many agent tasks are not one endpoint call.

[RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457) gives standard shape for HTTP API errors. It does not solve business recovery by itself, but it gives place to put structured problem type, detail, and extension fields.

[OpenAPI Overlays](https://spec.openapis.org/overlay/latest.html) can help maintain public, internal, partner, or agent-optimized views of same API.

[APIs.json](https://apisjson.org/) is about API discovery. For agents, directories of specs, docs, pricing, and terms matter a lot.

And older hypermedia ideas are suddenly interesting again. People joked about HATEOAS for years, but agents can actually use links.

Funny. We may end up rediscovering boring web architecture because agents need to know what can happen next.

## Back To Roots

I am generally seeing "make APIs more AI-friendly" as way to move past MCP versus CLI fight.

Not by replacing both tomorrow. That is not realistic and not even desirable.

But by moving the important logic back to the root: the web API and its machine-readable contract.

MCP server should be adapter. CLI should be adapter. SDK should be adapter. Agent should be able to use adapter when it helps, and use API directly when it is good enough.

Good APIs were always supposed to be self-describing, navigable, and meaningful.

Agents just made our old shortcuts too expensive.
