Internative Logo

How to Build an MCP Server in 2026: Complete Guide + Koordex Case Study

How to Build an MCP Server in 2026: Complete Guide + Koordex Case Study

How to Build an MCP Server in 2026: Complete Guide + Koordex Case Study

TL;DR: Model Context Protocol (MCP) is the 2026 standard for exposing tools to LLMs. You can build a working MCP server in about 50 lines of Python: define tool functions, declare them, run the server. Claude, Cursor and custom agents then discover and call your tools through one interface. This guide covers what MCP is, how it differs from a REST API, the full build, the architecture decisions you will hit, and how we run 40+ enterprise tools through MCP in Koordex.

If you are building anything agentic in 2026 — autonomous workflows, AI assistants over internal systems, custom Claude integrations — you will write MCP servers. MCP went from "interesting Anthropic protocol" in late 2024 to "the way LLMs talk to tools" by mid-2026.

By the end of this guide you will have running code, the production patterns we use at scale, and the avoidance list for the most common mistakes.

What Is an MCP Server?

The Model Context Protocol is an open standard, introduced by Anthropic in late 2024, that defines how AI applications connect to external data sources, tools and services. An MCP server is a lightweight process that exposes specific capabilities — database access, file reads, API calls, code execution — to any connected AI client.

Think of MCP as a universal adapter, a "USB-C for AI." Before it, every AI integration was a bespoke engineering project: connecting Claude, GPT or Gemini to your internal knowledge base meant writing custom code for each model-and-data-source combination.

Concretely, before MCP:

  • Each LLM SDK had its own tool definition format
  • Switching from OpenAI to Anthropic required rewriting tool schemas
  • Tool implementations lived inside the AI application, not as services
  • Scaling past 5-10 tools became unmanageable

And with MCP:

  • Tools are defined once, in MCP-compatible servers
  • Any MCP-compatible client (Claude, Cursor, custom agents) can use them
  • Discovery (list_tools), invocation (call_tool) and result format are standard
  • Tools become services — independently deployed, governed, audited

By mid-2026 MCP has been adopted across the industry. Anthropic, OpenAI, Google DeepMind, Microsoft and hundreds of enterprise software vendors ship or support MCP-compatible tooling.

How MCP Works: Hosts, Clients and Servers

MCP follows a client-server architecture with three components.

An MCP Host is the application that contains or orchestrates the AI model — Claude Code, an internal developer chat tool, or a custom agent your team built.

An MCP Client is the component inside the host that speaks the protocol and manages server connections on the model's behalf.

An MCP Server is the process that exposes capabilities to any connected client.

Each server declares what it can do using three primitives: tools (callable functions the model can invoke), resources (data the model can read) and prompts (reusable templates for common workflows). The model discovers and uses these at runtime, with no model-specific customization.

When a user sends a request, the model determines it needs external data. The client routes the call to the right server, which executes the operation and returns the result. The model receives the data in context and generates a grounded answer.

This flow works over local stdio (same machine) or remote HTTP with Server-Sent Events, which is what lets MCP servers run as shared enterprise services accessible to any model or agent in your organization.

MCP vs API: What's the Difference?

This is the question engineering teams ask first: "We already have REST APIs — why do we need MCP?"

The short answer: APIs are for programs; MCP is for AI agents.

REST APIs require a developer to know in advance which endpoint to call and what parameters to pass. The application logic is hardcoded. MCP lets the model discover available tools at runtime and decide dynamically how and when to use them. The model is the caller, not application code.

Across the dimensions that matter:

  • Caller — REST API: application code; MCP: AI model or agent
  • Discovery — REST API: external docs or OpenAPI spec; MCP: runtime capability declaration
  • Authentication — REST API: per-API credentials; MCP: centralized via the host
  • Composability — REST API: manual orchestration; MCP: model-driven and dynamic
  • Error handling — REST API: application logic; MCP: delegated to model reasoning

Most MCP servers wrap existing APIs under the hood. The Jira MCP server calls the Jira REST API; the GitHub MCP server calls the GitHub REST API. MCP is a routing layer on top of your existing API surface, not a replacement for it.

Build an MCP Server in 50 Lines

We will build a server that exposes three tools: get current weather, query a SQL database, send a Slack message.

Setup

pip install mcp

Server code: weather_db_slack_server.py

from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import mcp.types as types

server = Server("internative-tools")


@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="get_weather",
            description="Get current weather for a city",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "City name"}
                },
                "required": ["city"],
            },
        ),
        types.Tool(
            name="query_db",
            description="Run a read-only SQL query against the analytics warehouse",
            inputSchema={
                "type": "object",
                "properties": {
                    "sql": {"type": "string", "description": "SELECT-only SQL"}
                },
                "required": ["sql"],
            },
        ),
        types.Tool(
            name="send_slack",
            description="Send a message to a Slack channel",
            inputSchema={
                "type": "object",
                "properties": {
                    "channel": {"type": "string"},
                    "message": {"type": "string"},
                },
                "required": ["channel", "message"],
            },
        ),
    ]


@server.call_tool()
async def handle_call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == "get_weather":
        # In real code: call OpenWeatherMap or similar
        result = f"Weather in {arguments['city']}: 18°C, partly cloudy"
    elif name == "query_db":
        # In real code: SQLAlchemy with read-only credentials + SQL validator
        result = f"Query result: [3 rows] {arguments['sql'][:60]}..."
    elif name == "send_slack":
        # In real code: Slack SDK with audit logging
        result = f"Sent to {arguments['channel']}: {arguments['message'][:50]}..."
    else:
        result = f"Unknown tool: {name}"
    return [types.TextContent(type="text", text=result)]


async def main():
    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="internative-tools",
                server_version="0.1.0",
                capabilities=server.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )


if __name__ == "__main__":
    import asyncio

    asyncio.run(main())

That is a working MCP server. Any MCP-compatible client can now connect to it, call list_tools() to discover the three tools, and call call_tool("get_weather", {"city": "Istanbul"}) to get a response.

Connect from Claude Desktop

In the Claude Desktop config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "internative-tools": {
      "command": "python",
      "args": ["/absolute/path/to/weather_db_slack_server.py"]
    }
  }
}

Restart Claude Desktop. Your three tools now appear in any conversation, and Claude can call them on its own.

Connect from a custom agent (LangGraph)

from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "internative-tools": {
        "command": "python",
        "args": ["/path/to/weather_db_slack_server.py"],
        "transport": "stdio",
    }
})

tools = await client.get_tools()
# tools is now a list of LangChain Tool objects you can bind to any LLM

The same server works across Claude, Cursor, custom Python agents and custom Node agents. Write once, use everywhere.

Architecture Decisions You'll Hit

Transport: stdio vs HTTP vs SSE

  • stdio (this tutorial): the server runs as a subprocess. Best for desktop integrations (Claude Desktop, Cursor) or co-located services.
  • HTTP: the server is a web service. Best for centralized tool servers used by multiple agents and clients.
  • SSE (Server-Sent Events): streaming. Best for long-running tools that produce incremental output.

For Koordex in production we use HTTP transport, because our tool servers run as Kubernetes deployments serving many agent instances.

Tool granularity

Wrong: one MCP server with 50 unrelated tools. Right: three to five servers, each grouped by domain — database tools, communication tools, file tools.

The reason is operational: easier permissions, easier audit, easier deployment lifecycle.

Permission model

MCP itself does not enforce permissions — the client decides what to expose. For production:

  • Wrap your server in an auth layer (HTTP transport plus JWT or mTLS)
  • Filter the list_tools response per caller identity
  • Validate call_tool arguments against the caller's permission set
  • Log every tool invocation for audit

Tool implementation standards

For every production tool:

  • Input validation: do not trust the LLM's argument formatting. Validate types, ranges, allowed values.
  • Idempotency: where possible, tool calls should be safe to retry. Add idempotency keys for mutating operations.
  • Timeouts: set them per tool. LLM-driven workflows can spam tool calls.
  • Rate limits: per-caller and per-tool limits prevent a runaway agent from causing an incident.
  • Audit logging: who called what tool, with what arguments, and what came back. JSON-line format.

Koordex Case Study: MCP at Production Scale

Koordex is Internative's AI operations layer. We use MCP servers to expose enterprise tools to autonomous agents safely.

The setup

A typical Koordex deployment has 40+ tools across five to eight MCP servers:

  • Database server (10-15 read tools): SELECT against the warehouse, dashboards, status checks
  • Communication server (5-8 tools): Slack post, email send, Teams message, ticket create
  • File system server (5-7 tools): read internal docs, write reports, list folders
  • Vendor APIs server (10-15 tools): Stripe queries, HubSpot lookups, Salesforce operations
  • Admin server (3-5 tools, restricted): user management, billing operations

Each server runs as a separate Kubernetes deployment. Agents discover tools via list_tools, filtered by their permission scope.

The permission pattern

Agent Identity → Policy Engine → Allowed Tools Filter → MCP Discovery

When an agent connects, our policy engine returns the subset of tools it may use. The agent sees only those in its list_tools response. Calls outside that scope are rejected at the gateway.

A customer support agent gets read tools plus ticket create, nothing more. An analyst agent gets read tools plus dashboard write, no mutating operations. An admin agent gets full access, with a human approval gate on destructive operations.

The audit pattern

Every tool call is logged:

{
  "timestamp": "2026-06-22T14:31:02Z",
  "agent_id": "support-agent-1",
  "session_id": "abc123",
  "tool": "query_db",
  "arguments": {"sql": "SELECT count(*) FROM tickets WHERE status='open'"},
  "result_summary": "Returned 1 row",
  "duration_ms": 145,
  "policy_decision": "allowed",
  "user_invoker": "team@internative.net"
}

That log feeds cost tracking (per tool, per agent, per session), the compliance audit trail, performance debugging, and anomaly detection when an agent starts making unusual tool sequences.

The cost pattern

MCP tool calls are not free — they are how LLM costs add up. Agentic workflows can chain 10-100 tool calls per user request. Without observability, the bill explodes.

Koordex caches identical tool calls within a session, read-only results across sessions on a 5-60 minute TTL, and tool argument fingerprints to detect redundant calls. On a real deployment, caching cut tool call volume by 40-60% on customer support workloads.

For the cost engineering detail, see LLM Cost Optimization: 7 Patterns.

MCP vs A2A: Which One Do You Need?

Google's Agent-to-Agent (A2A) protocol addresses a related but distinct problem: how AI agents communicate with each other. MCP governs model-to-tool connections — how an agent accesses resources and calls tools. A2A governs agent-to-agent delegation — how agents hand off subtasks to specialist agents.

In a mature agentic architecture you will likely need both. MCP handles the integration layer: reading from Postgres, calling a CRM API, running a code interpreter. A2A handles orchestration: a planner agent delegating a subtask to a specialist, which then uses MCP to do the work.

Neither replaces the other. For the orchestration patterns that sit above MCP, see our breakdown of agentic AI architecture in 2026.

Top MCP Servers to Know in 2026

The ecosystem has grown to thousands of servers. The most widely used in enterprise contexts:

  • Filesystem — controlled local file access with configurable read/write permissions
  • GitHub — repository reads, issue and PR management, code search
  • PostgreSQL / SQLite — read-only database queries with schema inspection
  • Slack — channel message retrieval, summaries, draft composition
  • Google Drive / Notion — knowledge base retrieval and document reads
  • Puppeteer / Playwright — browser automation for agents
  • Memory — persistent cross-session storage for long-running agents

Anthropic's official MCP repository maintains reference implementations, and vendors including Atlassian, Salesforce and SAP ship production-grade connectors for their platforms.

When NOT to Use MCP

  • A single-purpose agent with one to three tools: write the tool calls directly, MCP is overkill.
  • Tool calls that need sub-50ms latency end to end: MCP transport adds 20-100ms.
  • Tools fully internal to one Python process: function calls beat protocol overhead.
  • A legacy stack that cannot run Python or Node 18+: the SDKs assume modern runtimes.

For everything else — multi-agent systems, cross-language teams, governance-heavy environments — MCP is the right abstraction.

The Three Most Common Mistakes

Mistake 1: building tool calls directly, without MCP, "to save complexity." It saves a day. It costs four to six weeks at month eight, when you add a second agent or switch frameworks.

Mistake 2: no permission layer. Default-open tool access is fine for a prototype. In production, an agent without permission boundaries is one prompt injection away from sending emails it should not.

Mistake 3: no audit log. When the agent does something surprising in production — and it will — you need to reconstruct what it did. Without a per-tool audit log, you are guessing.

Frequently Asked Questions

How do I make a basic MCP server?

Install the MCP SDK (pip install mcp for Python), declare your tools in a list_tools handler with a JSON schema for each one's inputs, implement a call_tool handler that runs the matching function, and start the server over stdio. That is roughly 50 lines for three tools, and the full working example is in the build section above.

How do I set up an MCP server with Claude Desktop?

Add an entry under mcpServers in claude_desktop_config.json, giving the command that launches your server and the absolute path to the script. On macOS that file lives at ~/Library/Application Support/Claude/claude_desktop_config.json. Restart Claude Desktop and the tools appear in every conversation.

How do I create a remote MCP server?

Use HTTP or SSE transport instead of stdio, so the server runs as a web service rather than a subprocess on the user's machine. Put an authentication layer in front of it — JWT or mTLS — and filter the list_tools response by caller identity, because a remote server is reachable by clients you do not control. This is the setup we use in production, where each server is a separate Kubernetes deployment.

How much does it cost to build an MCP server?

Building in-house typically means one senior engineer for about a month, which lands around $15,000-25,000 in loaded cost for a 3-6 week MVP. Starting from a framework or template cuts that to one to two weeks, roughly $8,000-15,000. Working with an agency runs $10,000-30,000 depending on complexity, and usually reaches a working MVP in under a week. The variable that moves the number most is your permission and audit requirements, not the tool count.

Does ChatGPT use MCP?

Yes. OpenAI announced MCP support in early 2025, and by 2026 both the ChatGPT desktop app and the OpenAI API support MCP server connections natively. What started as an Anthropic standard has become the cross-industry default, which is why one MCP server now serves every major model.

Is MCP a replacement for our REST API?

No. Most MCP servers call an existing REST API underneath — MCP is the layer that makes those capabilities discoverable and callable by a reasoning model. You keep your API for programs and add MCP for agents.

Six Questions Before You Build

  1. How many tools will agents call? Fewer than five: skip MCP, integrate directly. Five to 50: one server. More than 50: multiple servers grouped by domain.
  2. How many distinct agents or clients? One: MCP is overhead. Two or more: it saves work with every agent you add.
  3. What is your transport need? Desktop (Claude, Cursor): stdio. Production multi-agent: HTTP.
  4. What is your permission model? Open, internal tooling only: simple. Governed, multi-tenant or regulated: wrap MCP in an auth gateway from day one.
  5. What is your audit requirement? Light: a log file. Heavy: structured logs into a SIEM, plus a retention policy.
  6. Who owns operational responsibility? A single team: simple. Cross-team: one server per domain, owned by the domain team.

Related Reading

Next Step

If you are building MCP servers for production, or evaluating an AI operations layer, we run 30-minute architecture reviews. We will walk through your tool surface, permission needs and audit requirements, and tell you honestly whether Koordex fits or whether a self-build is the right call.

Our AI Integration & Automation practice covers the full lifecycle, from protocol selection through security modelling to production observability.

Contact: team@internative.net