# MCP vs API: what actually changes when an agent is the client

> An MCP server is usually a layer over an API, built for a model that picks tools at runtime. How they differ in discovery, auth and cost, and which you need.

Source: https://cotlet.dev/blog/mcp-vs-api

Published: 2026-10-10

An MCP server is usually a layer over an API, built for a model that picks tools at runtime. How they differ in discovery, auth and cost, and which you need.

Cotlet team

Book a slot in my salon app

That app has no API.

MCP vs API is not a choice between two rival technologies. An API is an interface for code you write; an MCP server is an interface for an AI model that reads a list of tools at runtime and decides which one to call. In practice an MCP server is almost always a thin layer on top of an API, so the real question is who your client is: a model or a script.

Most explanations stop at “MCP is like USB-C for AI”. That line comes from the [official MCP introduction](https://modelcontextprotocol.io/docs/getting-started/intro) and it is fine as far as it goes. This post covers what it leaves out: discovery, schemas, auth, what tools cost in context tokens, and when plain REST is still the better tool.

## What is an MCP server?

The Model Context Protocol is an open standard Anthropic [introduced on November 25, 2024](https://www.anthropic.com/news/model-context-protocol). The [specification](https://modelcontextprotocol.io/specification/latest) (revision 2026-07-28, current as of October 2026) defines three roles:

- **Host:** the AI application, such as Claude Code, Cursor or ChatGPT.
- **Client:** the connector inside the host that talks to one server.
- **Server:** the program that offers capabilities.

A server can offer **tools** (functions the model can call), **resources** (data for context) and **prompts** (templates for the user). Messages are JSON-RPC 2.0\. For most people wiring up an agent, “MCP server” means “a list of tools my agent can call”, and that is the part this post compares with an API.

## MCP vs API: the short comparison

| | REST API | MCP server |
| ------------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
| Who calls it | Code you wrote | A model, through its host |
| Who decides what to call | You, at write time | The model, at runtime |
| How you learn what exists | Docs or an OpenAPI file you read in advance | tools/list returns names, descriptions and JSON Schemas |
| Request shape | Any HTTP method, path and body | JSON-RPC: tools/list, tools/call |
| Auth | Whatever the vendor chose: API keys, OAuth, sessions | Spec defines OAuth 2.1 for HTTP servers; local servers read credentials from the environment |
| Errors | HTTP status codes your code handles | Tool errors written for the model to read and recover from |
| Cost of existing | Nothing until you call it | Tool definitions take context tokens on every turn |
| Best for | Cron jobs, pipelines, backends, anything deterministic | Open-ended tasks where the steps are not known up front |

The rest of this post goes row by row through the ones that matter when you build.

## Who is the client: a model or your code

With a REST API, every call is a decision you made while writing code. You read the docs, picked `POST /invoices`, mapped the fields and handled the errors. If the vendor adds an endpoint, nothing changes until you change your code.

With MCP, the decision moves to runtime. The [tools section of the spec](https://modelcontextprotocol.io/specification/2026-07-28/server/tools) says it directly: tools are “model-controlled, meaning that the language model can discover and invoke tools automatically based on its contextual understanding and the user’s prompts.”

That is the real difference, and it has a consequence the spec is honest about. If a model chooses the action, someone should be able to stop it.

_Model Context Protocol specification, Tools page, revision 2026-07-28, captured October 2026._

Your script does exactly what you wrote. Your agent does what it decides, so confirmation prompts for writes are part of the design, not an afterthought.

## Discovery and schemas

An API’s capabilities live in documentation. Some vendors publish an OpenAPI file; many publish a web page and nothing machine-readable.

An MCP server describes itself. The client sends `tools/list` and gets back each tool’s `name`, `description` and an `inputSchema`, which the spec says MUST be a valid JSON Schema object. A tool can also declare an `outputSchema`, and then the server MUST return structured results that match it ([spec, Tools](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)).

Two practical notes:

- **The description is the documentation.** The model picks a tool from its name and description. “Get data” is a bad tool. “Members of a group with name, role and join date, newest first” is a good one.
- **The list can change.** A server that declares `listChanged` notifies clients when tools are added, so your agent can pick up new capabilities without a code change. Your script never will, and for a script that is a feature.

One more correction to older articles that call MCP stateful and APIs stateless: in the current spec, MCP has “no protocol-level session”, and a server that needs state across calls should return an explicit handle, like a cart ID, that the model passes back.

## Auth: OAuth 2.1 vs API keys

REST APIs authenticate however the vendor decided. API keys in a header are the most common, then OAuth, then session cookies for internal APIs.

MCP standardizes this for remote servers. The [authorization spec](https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization) says authorization is optional, but HTTP servers that support it SHOULD follow OAuth 2.1 with:

- **Protected Resource Metadata (RFC 9728),** so the client can find the authorization server on its own after a 401.
- **PKCE and resource indicators (RFC 8707),** so a token is bound to one server.
- **Client ID Metadata Documents** as the preferred way to register a client, with Dynamic Client Registration kept only for backwards compatibility.
- **No token passthrough.** A server “MUST NOT accept or transit any other tokens”.

Local servers that run over stdio are told to skip all of this and read credentials from the environment, which is why most local setups ask you for an API key in a config file.

In practice many hosted servers accept both. [GitHub’s MCP server](https://github.com/github/github-mcp-server), for example, supports OAuth for its remote server and a personal access token as a bearer header. Both are still a credential for GitHub’s own APIs underneath.

## The cost nobody puts in the comparison: context tokens

An API you do not call costs nothing. An MCP server you connected costs tokens on every request, because the host loads tool definitions into the model’s context.

Anthropic’s engineers measured it. A five-server setup (GitHub, Slack, Sentry, Grafana, Splunk) had “58 tools consuming approximately 55K tokens before the conversation even starts”, and they have “seen tool definitions consume 134K tokens before optimization” ([Anthropic, Advanced tool use, November 2025](https://www.anthropic.com/engineering/advanced-tool-use)). Their tool search approach cut that by 85%.

Intermediate results cost too. In a separate post, moving data from Google Drive to Salesforce through direct tool calls took about 150,000 tokens; having the agent write code against the tools instead brought it to 2,000 ([Anthropic, Code execution with MCP, November 2025](https://www.anthropic.com/engineering/code-execution-with-mcp)).

What this means for you:

- Connect the servers you use, not every server you found.
- Prefer servers with a few well-described tools over one tool per endpoint.
- Bulk data movement is a job for code. If the task is “copy 5,000 rows”, a script calling the API is cheaper and more predictable than a model calling a tool 5,000 times.

## Why an MCP server is almost always a layer over an API

Look inside most MCP servers and you find an API client. The server translates `tools/call` into HTTP requests, then translates the response into something a model can read. GitHub’s server says it “can use many of the GitHub APIs”.

That is why a vendor with no API rarely ships MCP: there is nothing underneath to wrap. We wrote about that gap in [why your agent says an app has no API](https://cotlet.dev/blog/why-your-agent-says-no-api), and why vendors end up there is its own post on [why apps have no API](https://cotlet.dev/blog/why-apps-have-no-api).

It also means that if you need both, building the API first is the simpler order. The MCP layer adds descriptions, schemas and model-friendly errors; the API does the work.

### A worked example: one action, two interfaces

Cotlet is built this way. Take [Skool](https://cotlet.dev/integrations/skool-api-mcp-server), which has no public API (checked 10 October 2026). The planned `list_members` action is defined once, with one JSON Schema for its inputs: `group` (required), `joined_since`, `role`, `limit` and `cursor`.

Your agent sees it as an MCP tool with that schema and a one-line description, and calls it when you ask “who joined my community this week?”. Your script calls the same action as a REST endpoint:

_Cotlet API docs, REST section, October 2026\. Example request._

Same key, same input, same output shape. The errors are shared too: when a session expires, both return `AUTH_EXPIRED` with a link where you sign in again, written so an agent can tell you what to do. The full list is in the [API docs](https://cotlet.dev/docs), and the reasoning behind shipping both is in [MCP for your agent, REST for your scripts](https://cotlet.dev/blog/mcp-and-rest-one-key).

## When plain REST is the better choice

MCP earns its overhead when a model needs to choose. When nothing needs choosing, it is overhead. Use the API directly when:

- **The job runs on a schedule.** A nightly export or a monthly invoice run has fixed steps. A cron job is cheaper, faster and fails the same way every time.
- **It is a pipeline step.** ETL, webhooks, syncing two systems: deterministic input, deterministic output.
- **Volume is high.** Every tool call goes through a model turn. Thousands of records belong in a loop.
- **You need exact control over errors and retries.** Your code reads HTTP status codes and retries on its own terms.
- **The vendor has a good official API.** Use it. A wrapper, ours included, adds a dependency you do not need.

And sometimes neither is right. If you do the task twice a year, doing it by hand is fine. If you already use Zapier and the vendor’s Zapier app covers your action, that is the shortest path.

## Which one do you need? A decision rule

Ask these in order:

1. **Will a person describe the task in words and expect it done?** Yes: you want MCP, so your agent can pick the tools.
2. **Are the steps fixed and known before it runs?** Yes: you want the API, called from a script or workflow tool.
3. **Do you need both?** Common: an agent for ad-hoc requests, a script for the weekly batch. Look for one integration that exposes both, with one schema, so they never drift apart.
4. **Does the app have an official API with the actions you need?** Use it, and add the vendor’s MCP server if one exists.
5. **No API, or not the actions you need?** Then neither exists yet, and that is a different problem.

Where the server runs matters too. That trade-off has its own post on [remote vs local MCP servers](https://cotlet.dev/blog/remote-mcp-server), and the risks of giving an agent tools are covered in [MCP server security](https://cotlet.dev/blog/mcp-server-security).

## Where Cotlet fits

Cotlet is for step 5: apps with no API, or one too narrow for the job. Every integration is designed as an API first with an MCP server on top, so your agent gets tools and your scripts get REST endpoints, one key for both. You sign in to each app yourself, once, in a browser on our side; we keep the encrypted session, never your password. We never solve CAPTCHAs or bypass MFA, and we do not keep the data that passes through. [How it works](https://cotlet.dev/how-it-works) walks through the setup.

Access is granted per account, and nothing is charged until your access is active. If your app is in the [catalog](https://cotlet.dev/integrations), [request access](https://cotlet.dev/signup) and pick it.

[All posts](https://cotlet.dev/blog)

## Want your app first?

Pick it when you sign up. The most-picked apps open first.

[Request access](https://cotlet.dev/signup)
