4Geeks chosen to deliver AI education in the Bahamas alongside Harvard, Oxford, and Columbia.See more
Image: 4geeks logo big
SIGN IN
12 min read

What Is an MCP Server? Why It's Not an API Endpoint

An MCP server exposes a company's tools and the workflows between them to any AI agent. Definition, code, three server patterns, and one enterprise example.

An MCP server is a program that exposes a company's tools, data, and the workflows that connect them to any AI agent through one open protocol, so the agent can act without a custom integration or task-specific training. Anthropic released the Model Context Protocol as an open standard in November 2024 and donated it to the Linux Foundation's Agentic AI Foundation in December 2025. Any AI application that speaks the protocol (Claude, ChatGPT, Gemini, Cursor, VS Code, and hundreds of others) can connect to any server built for it, and there are more than 10,000 public servers to choose from.

You will hear it called the USB-C port for AI. That analogy explains the client side well and undersells the server side badly. This article is about the server side.

What is an MCP server for?

Before MCP, connecting an agent to a system meant two pieces of custom work: an integration written for that specific agent, and a playbook telling the agent when and how to use it. Ten systems times five agents meant fifty integrations and fifty playbooks, and every model update could break any of them.

An MCP server collapses that to one piece of work. You describe your tools once, in a form the model reads at runtime, and every compatible agent gets the same capabilities with the same instructions. The server is the one place where "how you work with our system" lives.

That is the purpose. Not "let the model call a function." Functions were already callable. The purpose is to move the knowledge of how to operate a system out of prompts, training data, and per-agent config files, and into the system itself.

Is an MCP server just an API?

No, but the confusion is reasonable because the simplest MCP server looks exactly like one.

An API endpoint returns data and assumes the caller already knows what to do with it. The API has no opinion about what you should call next, what a bad input looks like in practice, or which of its 200 endpoints matter for the job you are doing. All of that knowledge lives in the developer's head or in documentation the model never reads.

An MCP server flips that. It publishes three things the model reads directly:

  • Tools: actions with a name, a natural-language description, and a typed input schema.
  • Resources: data the agent can read, addressed by URI.
  • Prompts: reusable instruction templates the server hands to the client.

The tool description is not a comment for humans. It is the primary way the model decides whether to call the tool, what to pass, and what to do with the result. A server can also tell the model what to do next after a call succeeds or fails. That is where the workflow knowledge goes, and it is what an API has no place for.

REST APIFunction callingMCP server
Who reads the descriptionDevelopersThe model, per appThe model, any app
Where the "what next" livesYour codeYour promptThe server
Adding a new agentNew integrationNew prompt and tool defsZero work
TransportHTTPIn-processstdio or HTTP (JSON-RPC 2.0)
StandardPer vendorPer vendorOpen, shared

Two questions people ask here, answered plainly:

Is an MCP server a real server? Sometimes. A local server runs as a child process on your machine and talks over stdin/stdout. A remote server runs on a host and talks over HTTP. Both are "servers" in the protocol sense: they respond to requests from a client.

What is the difference between MCP and an MCP server? MCP is the protocol, the set of rules for how clients and servers talk. An MCP server is a program that implements those rules to expose one system.

The simplest MCP server

Here is the version everyone builds first: one tool, send_email, using the official Python SDK.

python
from mcp.server.fastmcp import FastMCP
 
mcp = FastMCP("email")
 
@mcp.tool()
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email from the company account to one recipient."""
    # call your email provider here
    return f"Sent to {to}"
 
if __name__ == "__main__":
    mcp.run()

That is a complete, working server. Point Claude Desktop or Cursor at it and the model can send email.

It is also the version that proves the misconception. This server is a wrapped function. Every company has a hundred of these already: create_ticket, lookup_customer, post_to_slack. Wrapping them as MCP tools is useful and takes an afternoon. It is not where the value is.

Where the real value is: abstracting the interaction, not the function

Take three tools that depend on each other: find_contact, draft_email, send_email. Expose them as bare functions and the agent has to guess the order, guess that send_email needs a draft ID and not a body, and guess what to do when find_contact returns two matches. Most of the time it guesses right. The rest of the time you get a Slack message at 2am.

The usual fix is to write that knowledge down somewhere the agent reads it: an AGENT.md file, a skill file, a system prompt, or in the extreme case, post-training the model on your workflows. Every one of those is a second copy of the truth, maintained separately from the server, and specific to one agent.

The MCP answer is to put the knowledge in the server:

python
@mcp.tool()
def find_contact(query: str) -> dict:
    """Find a contact by name or email. Returns one match, or a list of
    candidates if the query is ambiguous. If you get candidates, ask the
    user to pick one before calling draft_email."""
    ...
 
@mcp.tool()
def draft_email(contact_id: str, intent: str) -> dict:
    """Create a draft addressed to contact_id. Returns draft_id and the
    rendered text. Show the text to the user for approval, then call
    send_email with the draft_id. Never send without approval."""
    ...
 
@mcp.tool()
def send_email(draft_id: str) -> str:
    """Send an approved draft. Fails if the draft was never shown to the
    user. On failure, call draft_email again rather than retrying."""
    ...

Same three functions. Now the sequence, the ambiguity handling, the approval gate, and the retry rule all travel with the server. Any agent that connects gets them for free. No skill file, no AGENT.md, no post-training. When the workflow changes, you change it in one place and every agent picks it up on the next connection.

That is the real definition of a good MCP server: it abstracts the interactions between functions, not just the functions. A department's tooling is fifty functions and two hundred rules about how they fit together. The rules are the hard part, and the server is the right home for them.

Why plugins didn't solve this

OpenAI tried a version of this in March 2023 with ChatGPT plugins. A plugin was an OpenAPI spec plus a manifest with a description_for_model field. The workflow knowledge went into that description and into whatever instructions the plugin author could fit in the manifest.

It was a thick, platform-specific layer. It only worked in ChatGPT, the spec format was OpenAI's, and no other vendor adopted it. Anthropic never did. OpenAI deprecated plugins in 2024 in favor of GPTs, added MCP support to its Agents SDK in March 2025, and shipped full MCP tool support in ChatGPT in September 2025.

The difference is where the layer sits. Plugins put it in the client platform, defined by one company. MCP puts it in the server, defined by an open spec, readable by every client. That is why plugins ended up as a ChatGPT feature and MCP ended up as the standard.

Three MCP servers worth studying

Each of these earns its place for one interaction it abstracts, not for the number of tools it exposes.

A code host server (GitHub)

GitHub ships an official MCP server. A naive version would expose create_branch, commit_file, and open_pull_request as three unrelated tools and let the agent figure out that a PR needs a branch, a branch needs a base, and a base needs to exist. The useful version describes each tool in terms of the one before it: the branch tool tells the agent which base it used, the commit tool returns the branch it landed on, and the PR tool refuses a branch with no commits. The agent never has to hold the git model in its head. The server holds it.

What to copy: return the identifiers the next step needs, and say in the description which step that is.

A database server (PostgreSQL)

Every database server you will find has a query tool. The good ones also have list_schemas and describe_table, and the query description says to call them first. That one sentence in a docstring is the difference between an agent that guesses users.email exists and an agent that reads the schema, finds accounts.contact_email, and writes the right query on the first try. Read-only by default, with a separate tool for writes, is the other pattern worth copying: the agent cannot drop a table by accident because the tool that could do it does not answer to the same name.

What to copy: discovery tools before action tools, and separate names for reads and writes.

A booking or calendar server

Availability, hold, confirm. Three tools, one rule: you cannot confirm what you never held, and a hold expires. A server that encodes the rule in the tool contract (confirm takes a hold_id, not a time slot) makes double-booking impossible from the agent side regardless of which model is calling. A server that exposes book(time) and hopes for the best is the version that ends up with two meetings in one room.

What to copy: make the invalid sequence impossible to express, not just discouraged.

An enterprise example: one server for a whole department

At 4Geeks, agents run the marketing website. The site has 606 live URLs in English and Spanish, and scheduled agent sessions work on it around the clock, clearing validation issues, fixing broken links, and writing page copy. Two different vendors' chat agents do this work, claude.ai and Grok, with no AGENT.md, no skill file, and no orchestration framework. One MCP server, the same for both.

Here is how the server exposes a department's tooling so that any agent can operate it cold.

It starts with a contract, not a tool list. The first call on any session is bootstrap_agent, which returns the current conventions as a versioned document. The agent does not need to be told the rules before connecting. It asks the server, and the server answers with today's version.

Every response carries the next step. Each tool returns a next_actions array alongside its result: which tool to call next, with which arguments, and under what condition. The tool list is the set of nodes. next_actions is the set of edges. The workflow lives in the responses, so there is nothing for a client-side file to describe.

Gates stop the agent before it can do damage. Every mutating tool requires an agent_session_id and a written report of at least 80 characters explaining the change, or it returns report_too_short before touching anything. Destructive tools require an explicit confirm: true. Expensive diagnostics on a recently scanned page return a cost gate instead of re-running. Calling a tool without naming the site returns multi_site_domain_required with the list of valid sites. The agent does not have to know any of this in advance. It finds out at the exact moment it matters.

The server verifies its own work. After a fix is written, the server re-validates the page and marks the issue verified_gone only if it is actually absent. "The agent said it was done" is not a state the system recognizes.

The numbers from one five-day window, September 2 to 6, 2026: 229 issues resolved and verified gone, across 118 entries on 101 URLs, with activity in 23 of the 24 hours of the day. Ten of the 229 were later reopened, a 4.4% rate. The full breakdown, including the four times the server stopped an agent mid-task, is in How We Run Our Website With AI Agents and One MCP Server.

The point for this article is not the numbers. It is that a department's entire operating knowledge (fifty-odd tools, the order they go in, what needs approval, what costs money, what is irreversible) fits inside one server, and any agent that can read a JSON response inherits all of it on its first call.

How do you build an MCP server?

Four steps, and the first one is not code.

  1. Write down the interactions, not the functions. List the tools, then list every rule about how they relate: what comes first, what needs a human, what returns an ID another tool consumes, what fails and what to do when it does. The rules are the product.
  2. Pick an SDK. Official SDKs exist for Python, TypeScript, Java, Kotlin, C#, Go, Ruby, Rust, and Swift. Python's FastMCP and the TypeScript SDK are the two most people start with.
  3. Put the rules in the tool contracts. Descriptions that name the next step. Input schemas that require the ID from the previous step. Return values that include what the next call needs. A next_actions field if you want to go further than descriptions.
  4. Connect a client and watch it fail. Claude Desktop, Cursor, and VS Code all take a local server config. Give the agent a real task with no instructions and see where it guesses wrong. Every wrong guess is a missing rule in the server, not a missing line in a prompt.

If you want to do step 2 through 4 with your hands on a keyboard, the interactive exercise Building an MCP Server walks through a working server from an empty folder.

Download the MCP Server Standard guide

An 8-pattern implementation guide, written to be fed straight to your coding agent.

Frequently Asked Questions