RETURN_TO_BLOG
AI & Automation 14 min

MCP (Model Context Protocol) — One Standard to Connect AI to All Your Company's Tools

MCP (Model Context Protocol) is an open standard for communication between AI applications and external tools and data — published by Anthropic in November 2024 and adopted within months by OpenAI, Google DeepMind and Microsoft. It solves the N×M problem: instead of building a separate integration between every AI application and every tool (4 apps × 4 tools = 16 integrations), you build one adapter per side (4 + 4 = 8). If your tools need to serve multiple AI applications (Claude, ChatGPT, Cursor, your own agent) — MCP is the right choice. If you are building a single application with a handful of functions — plain function calling is enough, and MCP would be over-engineering.

The complete guide to MCP: what problem the protocol solves, how the host–client–server architecture works, how it differs from function calling and OpenAPI, how to build your own MCP server in Python and TypeScript, which ready-made servers to plug into your business tools, and how to secure the deployment against tool poisoning.

Your company has an AI agent in customer support, an assistant in Slack and a copilot for the sales team. Each of them needs access to the CRM, the database and the calendar. Without a standard you write nine integrations — and when the CRM changes its API, you patch three applications at once. That exact pain is what MCP was created to remove.

The adoption numbers speak for themselves: over 5,800 public MCP servers in the registry, ~97 million SDK downloads per month and backing from the four biggest AI players. In 2026 MCP stopped being a curiosity — it became an architectural decision every company deploying agents has to consider. This article explains how the protocol works, when to use it and how to deploy it safely.

What problem does MCP solve?

/// THE N×M PROBLEM AND HOW MCP SOLVES IT

Without MCP: 16 integrations. With MCP: 8 adapters.

Without MCPN × M
Claude
ChatGPT
Cursor
Your app
╳ ╳ ╳ ╳ ╳ ╳ ╳ ╳
4 × 4 = 16 separate integrations
Slack
PostgreSQL
GitHub
Google Drive
Every app integrates every tool separately. An API change means fixing all of them.
With MCPN + M
Claude
ChatGPT
Cursor
Your app
↓ ↓ ↓ ↓
MCP
one shared protocol
↓ ↓ ↓ ↓
Slack
PostgreSQL
GitHub
Google Drive
Each tool gets one MCP server. Each app speaks one protocol — 4 + 4 = 8 adapters.
5,800+
READY-MADE MCP SERVERS
97M
MONTHLY SDK DOWNLOADS
4
BIG TECH BEHIND IT ANTHROPIC · OPENAI · GOOGLE · MS

Before MCP, every AI-to-tool integration was bespoke. LangChain had its "tools", OpenAI had its "functions", every framework had its own definition format and its own error handling. The result: a fragmented ecosystem, non-portable integrations, and every new AI application in the company starting from zero.

MCP standardises three things:

  • Tool discovery — the AI application asks the server "what can you do?" and receives a list of tools with descriptions and parameter schemas; nothing is hardcoded
  • Invocation — one request and response format (JSON-RPC 2.0) regardless of whether the tool is a database, an API or a file system
  • Context — beyond tools, a server can expose resources (files, records) and prompt templates that the AI application fetches in a unified way

The analogy that stuck: MCP is USB-C for AI. A laptop manufacturer does not build a separate port for every device — one standard serves them all. In the same way, an AI application with an MCP client can use any MCP server, no matter who wrote it.

How MCP works — host, client, server

The MCP architecture has three roles:

RoleWhat it doesExample
HostThe AI application the user talks toClaude Desktop, Cursor, your agent
MCP clientHost component — maintains a 1:1 connection with a serverBuilt into the host, you do not write it
MCP serverExposes tools, resources and prompts for one systemPostgres server, Slack server, your own

An MCP server exposes three kinds of objects (the primitives):

  • Tools — functions the model can call: "find a customer in the CRM", "send a message", "run a SQL query"; the equivalent of function calling
  • Resources — read-only data the host can pull into context: a file, a database record, a document; like GET in REST
  • Prompts — ready-made prompt templates with parameters that the server recommends for its tools; used less often, but valuable in complex systems

Communication runs over JSON-RPC 2.0 on one of two transports:

TransportHow it worksWhen to use
stdioThe host launches the server as a local process, communicating via stdin/stdoutLocal tools: file system, local database, CLI — 90% of cases
Streamable HTTPThe server runs remotely as an HTTP endpoint, supports streaming and multiple clientsA server shared by a team, SaaS, production

The practical rule: build and test on stdio, switch to Streamable HTTP only when more than one person or machine needs the server. Note: the older SSE transport was removed from the spec in March 2025 in favour of Streamable HTTP — if a tutorial shows SSE, it is outdated.

This is what connecting MCP servers to Claude Desktop looks like — a single config file:

claude_desktop_config.json
{  "mcpServers": {    "filesystem": {      "command": "npx",      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/company/documents"]    },    "postgres": {      "command": "npx",      "args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://readonly_user:password@localhost/crm"]    }  }}

After restarting the app, Claude sees both servers' tools and decides on its own when to use them. Note the "readonly_user" — that is not an accident; we will come back to it in the security section.

MCP vs function calling vs OpenAPI — which to choose?

This is the most common question and the biggest source of confusion. All three approaches solve the same problem — the model picks an action, the runtime executes it — but at different levels:

CriterionFunction callingOpenAPI toolsMCP
What it isA model vendor's API featureAuto-generating tools from a REST specAn open discovery and invocation protocol
ScopeOne applicationExisting HTTP APIsMany apps × many tools
Tool discoveryNone — defined in codeFrom the OAS 3.1 specDynamic — the runtime asks the server
PortabilityLow (vendor format)MediumHigh — any MCP host
Implementation costLowestLow with an existing APIMedium — you run a server
Best forOne app, a few functions, low latencyGoverned corporate REST APIsAn ecosystem of many agents and tools

The decision in three questions:

  1. 1.Building a single application with at most a dozen functions? Function calling — simplest, fastest, no extra infrastructure (I covered this in the article on tool calling in production)
  2. 2.Have a mature REST API with an OpenAPI spec you want to expose to agents? OpenAPI tools — do not build an MCP server just to wrap an existing API that only one application consumes
  3. 3.More than one AI application needs the same tools — or you want to tap into the ecosystem of ready-made servers? MCP — the cost of running a server pays off with the second client

An important nuance: these approaches are not mutually exclusive. An MCP host still uses the model's function calling under the hood — MCP standardises the layer above: where tools come from and how they are discovered.

How to build your own MCP server in Python

The fastest route is FastMCP — a high-level SDK where a tool is a decorated function. Example: a server giving an agent access to the company order database:

orders_mcp_server.py
from fastmcp import FastMCPimport osimport psycopg2mcp = FastMCP("orders-server")def get_db():    return psycopg2.connect(        host="localhost", dbname="shop",        user="readonly_agent", password=os.environ["DB_PASS"]    )@mcp.tool()def find_order(order_id: str) -> dict:    """Returns the status, amount and date of an order by its number."""    with get_db() as conn, conn.cursor() as cur:        cur.execute(            "SELECT status, total, created_at FROM orders WHERE id = %s",            (order_id,)        )        row = cur.fetchone()    if row is None:        return {"error": "Order not found: " + order_id}    return {"status": row[0], "total": float(row[1]), "created_at": str(row[2])}@mcp.tool()def orders_summary(days: int = 7) -> dict:    """Summary of orders from the last N days: count and total revenue."""    if days < 1 or days > 90:        return {"error": "days must be in the range 1-90"}    with get_db() as conn, conn.cursor() as cur:        cur.execute(            "SELECT count(*), coalesce(sum(total),0) FROM orders "            "WHERE created_at > now() - make_interval(days := %s)",            (days,)        )        count, total = cur.fetchone()    return {"orders": count, "revenue": float(total), "period_days": days}if __name__ == "__main__":    mcp.run()Three things that make a real difference here:- **Docstrings are part of the product** — they are how the model learns when to use a tool; write them like instructions for a new employee, not like comments for a programmer- **Range validation in code** — the model will eventually pass days=10000; the server must reject it, not execute it- **The readonly_agent account** — permissions are enforced by the database, not the prompt; even a successful prompt injection cannot run an UPDATE through an account that only has SELECT

Testing before connecting an agent: "npx @modelcontextprotocol/inspector python orders_mcp_server.py" opens the MCP Inspector web interface, where you can call every tool manually and see the raw responses. It is the Postman of MCP — use it before every integration.

An MCP server in TypeScript

For JS/TS teams, the official SDK looks like this — a warehouse stock server:

inventory_mcp_server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";import { z } from "zod";const server = new McpServer({ name: "inventory", version: "1.0.0" });server.tool(  "check_stock",  "Checks the stock level of a product by SKU. Returns quantity and location.",  { sku: z.string().regex(/^[A-Z0-9-]{4,20}$/) },  async ({ sku }) => {    const res = await fetch("https://api.company.com/v1/stock/" + sku, {      headers: { Authorization: "Bearer " + process.env.WMS_TOKEN }    });    if (!res.ok) {      return { content: [{ type: "text", text: "Product not found: " + sku }] };    }    const data = await res.json();    return { content: [{ type: "text", text: JSON.stringify(data) }] };  });const transport = new StdioServerTransport();await server.connect(transport);

Differences from Python: parameter validation is done by Zod (the regex schema on SKU rejects junk input before it touches your API), and logs must go to console.error — stdout is owned by the protocol and any console.log will break the connection. Language choice: Python/FastMCP for fast iteration and ML integrations, TypeScript for long-lived services where the type safety pays off.

Ready-made MCP servers for business tools

Before writing your own server, check the registry — most popular systems already have one:

  • Data and databases: PostgreSQL, MongoDB, SQLite, Redis — queries with access control at the database-role level
  • Communication: Slack (reading channels, sending messages), Gmail, Microsoft Teams
  • Documents: Google Drive, Notion, Confluence, file system restricted to specified directories
  • Developer: GitHub (issues, PRs, code review), GitLab, Sentry, Docker
  • Business: Stripe (payments and invoices), HubSpot, Salesforce, Linear, Jira
  • Web: Brave Search, Puppeteer/Playwright (browser automation), Firecrawl (scraping)

The decision rule: take a ready-made server when it covers your use case completely and comes from a trusted source (the official vendor or the modelcontextprotocol repository). Write your own when you need business logic (e.g. "find customers at churn risk"), aggregation of several systems into one tool, or hard permission constraints that a generic server does not enforce.

MCP security — a new attack surface

/// SECURITY RISKS OF THE MCP ECOSYSTEM

4 main MCP attacks — and 3 defense rules

01CRITICAL
Tool poisoning
Malicious instructions in the tool description
The model reads the description, the user never sees it — a hidden command steers the agent
02HIGH
Rug pull
Server changes a tool definition after the audit
Trusted version 1.0 turns malicious in 1.1 — without pinning you will not notice
03HIGH
Confused deputy
Server acts with broader privileges than the user
The agent performs an action the user has no right to — one OAuth misconfiguration is enough
04MEDIUM
Credential leakage
API keys and tokens in configuration files
MCP configs hold credentials — an attack tells the agent to read and exfiltrate them
Tool allowlist
Deny by default — approve per tool
Version pinning
Freeze definitions after the audit
Least privilege + HTTPS
Scoped tokens, OAuth, zero plaintext

MCP adds a class of risk to AI applications that plain function calling did not have — because tools now come from external sources. This directly extends the topics from the prompt injection article (#39):

  • Tool poisoning — an attacker plants malicious instructions in a tool description; the model reads and follows them while the user never sees them, because the UI shows only the tool name
  • Rug pull — a server that passed your audit quietly changes its tool definitions in a later version; trust from installation day does not last forever
  • Confused deputy — the MCP server acts with broader privileges than the user invoking it; one OAuth misconfiguration and the agent does things the user is not allowed to do
  • Credential leakage — MCP configuration files contain API keys and passwords; tool poisoning can instruct the agent to read and exfiltrate them (CVE-2025-6514 demonstrated OAuth token interception via a malicious authorisation endpoint)

Defense rules for deployment:

  1. 1.Allowlist instead of blocklist — the host denies tool invocations by default; you approve individually the ones the agent actually needs
  2. 2.Pin server versions — after the audit, freeze the version (exact package hash, not "latest"); an update means re-reviewing tool definitions
  3. 3.Least privilege at the source system level — read-only database accounts, scoped API keys, OAuth with minimal scopes; permissions are enforced by the system, not the prompt
  4. 4.Trusted server sources only — official vendor repositories; an MCP server from a random GitHub repo carries the same risk as installing an unknown npm package with access to your CRM
  5. 5.HTTPS everywhere — a significant share of audited MCP servers use plaintext HTTP, exposing tokens to interception; no TLS exceptions in production
  6. 6.Human-in-the-loop for irreversible actions — sending, paying and deleting require human confirmation, exactly as in the defense layers from article #39

Deploying MCP in your company — where to start

  1. 1.Inventory your AI applications and tools: how many hosts (Claude, Cursor, your own agents) and how many systems to connect — if the result is 1×N, stay with function calling
  2. 2.Start with ready-made servers in read-only mode: filesystem on a designated directory and Postgres on a readonly account — zero code, immediate value
  3. 3.Test every server in MCP Inspector before connecting it to a host
  4. 4.Write your first own server for one business process with 2–4 tools — do not build a "server for everything" on day one
  5. 5.Treat docstrings and tool descriptions like prompts: test whether the model picks the right tool and iterate on the wording
  6. 6.Validate parameters in server code (ranges, regex, types) — the model will eventually pass an absurd value
  7. 7.Keep all credentials in environment variables or a vault — never in a config committed to the repo
  8. 8.Set a tool allowlist on the host and pin server versions
  9. 9.Log every tool invocation (who, what, with which parameters, result) — audit and debugging in one
  10. 10.Move to Streamable HTTP with OAuth only when the server must be shared — local stdio is safer by default

Key takeaways

MCP solves a real problem — the N×M integration explosion — and has rare industry-wide consensus behind it: Anthropic, OpenAI, Google and Microsoft. Choose it when more than one AI application uses the same tools; for a single application, stay with function calling. Getting started is cheap: ready-made servers connect in an hour, your own takes 1–3 days with FastMCP. Take security seriously from day one: a tool allowlist, version pinning, least privilege on source-system accounts and trusted server sources only — because an MCP server is a new attack vector carrying your systems' permissions.

---

I help companies design and deploy tool architecture for AI agents — from the function calling vs MCP decision, through building custom MCP servers with proper permissions, to secure production deployment with monitoring. Get in touch — I start with a free 30-minute analysis of your use case.

/// AUTHOR
Paweł Wiszniewski – AI & Web Engineer

Paweł Wiszniewski

SEO & GEO Specialist & AI Engineer

SEO/GEO specialist (10 years) and AI engineer (3 years). I build search visibility, AI systems and automations that reduce costs and improve operational efficiency.

Signal received?

Terminate
Silence

Initiate protocol. Establish connection. Let's build something loud.

> WAITING_FOR_INPUT...