Marcio Cunha

Model Context Protocol (MCP) in Practice: Building Servers and Tools for Autonomous Agents

Discover how Anthropic's Model Context Protocol (MCP) eliminates AI integration fragmentation by creating a universal standard for connecting autonomous agents to real data and tools. Learn to design, build, and secure robust MCP servers with practical Python implementations.

Marcio Cunha14 min
Also available in:EspañolPortuguês
Summary
  • The Model Context Protocol acts as a universal standard that decouples artificial intelligence clients from data and tool providers, eliminating fragile custom adapters.
  • The protocol architecture relies on four pillars including hosts, clients, servers, and transports like stdio for local environments and server-sent events for cloud setups.
  • Developers can build functional MCP servers in Python using official software development kits to expose structured resources and validated database tools.
  • Security measures such as strict input validation, least privilege permissions, and human-in-the-loop approvals are essential at the server perimeter.
  • Integrating MCP servers into development environments and cloud pipelines requires minimal configuration, enabling seamless collaboration across autonomous agents.

The Fragmentation Labyrinth: Why Our Agents Need a Standard Protocol

Building autonomous agents and Large Language Model (LLM) systems, which are advanced computer programs that process human language and make decisions, has become the vanguard of modern software engineering. However, any system architect who has tried connecting a model like Claude or GPT-4 to legacy databases, enterprise APIs (application programming interfaces, which let different software programs talk to each other), file systems, and developer tools knows the pain of fragmentation. Historically, each AI provider created its own abstraction for function calling and plugins, requiring developers to write custom, fragile adapters for every ecosystem. The result was an unsustainable tight coupling where business logic was locked into proprietary SDKs (software development kits, which are pre-written packages of code that help build applications), and API changes broke entire agent workflows overnight.

This is precisely the structural problem that the Model Context Protocol (MCP), introduced by Anthropic, sets out to solve once and for all. Think of MCP as the USB-C equivalent for artificial intelligence: an open standard that decouples AI clients (Hosts and Clients) from data and tool providers (Servers). Instead of building ad-hoc integrations for every IDE (integrated development environment, which is software programmers use to write code), chat application, or agent framework, developers can now create a single standardized MCP server that instantly connects to any compatible client. This paradigm shift transforms isolated agents into collaborative systems capable of navigating complex infrastructures securely and deterministically.

Anatomy of the MCP Architecture: Host, Client, Server, and Transports

To design robust systems using MCP, understanding its internal topology is essential. The architecture consists of four fundamental pillars communicating through standardized transport protocols:

  • MCP Host: The main application initiating the AI session (e.g., the Claude Desktop app, Cursor IDE, or a custom agent pipeline). The Host manages security, user approvals, and the client lifecycle.
  • MCP Client: The component inside the Host that maintains a direct 1-to-1 connection with the MCP server. It negotiates capabilities, translates requests, and manages session state.
  • MCP Server: A lightweight process exposing data and capabilities via the MCP protocol. It encapsulates business logic for accessing databases, REST APIs, file systems, or terminal tools.
  • Transports: The underlying communication channels. MCP natively supports stdio (standard input/output, which lets programs pass text messages directly back and forth locally on the same machine, ideal for IDE integrations) and Server-Sent Events / SSE (a technology that lets servers push real-time updates over regular HTTP web connections, enabling cloud scalability).

The table below summarizes the architectural characteristics of the transport media supported by the protocol:

Evaluation Criteriastdio (Standard Input/Output)SSE (Server-Sent Events)
Ideal Use CaseLocal tools, IDEs, personal automation scripts.Remote services, cloud microservices, shared tools.
Setup ComplexityLow (managed directly by the parent process).Medium/High (requires HTTP authentication, load balancing, and TLS).
LatencyMinimal (communication via IPC/local pipes).Low to Moderate (dependent on TCP/HTTP network).
Security IsolationExecuted within local user permission context.Requires rigorous authentication layers (OAuth, mTLS).

Building a Robust MCP Server from Scratch in Python

Let's put theory into practice by building a functional MCP server in Python using the official SDK. This server will provide tools to interact with a relational database and expose static configuration resources to the agent.

import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
import mcp.types as types

# Initialize the main MCP Server instance
app = Server('enterprise-data-server')

@app.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri='config://system/env',
            name='System Environment Variables',
            description='Current production environment settings',
            mimeType='application/json'
        )
    ]

@app.read_resource()
async def read_resource(uri: str) -> str:
    if uri == 'config://system/env':
        return '{"environment": "production", "region": "us-east-1", "debug": false}'
    raise ValueError(f'Resource not found: {uri}')

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name='execute_sql_query',
            description='Executes a safe read-only SQL query on the metrics database.',
            inputSchema={
                'type': 'object',
                'properties': {
                    'query': {
                        'type': 'string',
                        'description': 'The SELECT SQL query to execute.'
                    }
                },
                'required': ['query']
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == 'execute_sql_query':
        query = arguments.get('query', '')
        if not query.lower().strip().startswith('select'):
            raise ValueError('Only SELECT operations are allowed for security reasons.')
        
        # Database execution simulation
        mock_result = f'[MOCK RESULT FOR]: {query}\n- Row 1: id=101, status=active\n- Row 2: id=102, status=active'
        return [types.TextContent(type='text', text=mock_result)]
    
    raise ValueError(f'Unknown tool: {name}')

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(
            read_stream,
            write_stream,
            app.create_initialization_options()
        )

if __name__ == '__main__':
    asyncio.run(main())

The code above demonstrates the protocol's clarity. Through clean decorators (special markers in Python that modify how functions behave), we exposed a readable resource and a validated database tool. The connected agent doesn't need to know the database connection details; it simply interacts with the JSON schema (a structured format that defines how data must look) declared in the tool.

Security First: Sandboxing and Preventing Arbitrary Execution

Connecting language models to code execution tools and databases opens a vast array of possibilities, but it also introduces critical security vectors, such as Indirect Prompt Injection and malicious command execution. As architects, we cannot blindly trust LLM outputs. MCP is designed on the premise that security must be enforced at the Server perimeter, never delegated to the model.

When designing enterprise MCP servers, strictly follow these security guidelines:

  1. Rigorous Input Validation (Input Schemas): Utilize strict validation based on JSON Schema. Reject any parameters outside expected patterns before invoking business logic.
  2. Principle of Least Privilege: Database credentials or APIs used by MCP servers must have strictly restricted permissions (read-only when possible, no access to sensitive tables).
  3. Process Isolation (Sandboxing): When using stdio transport, run the MCP server process within isolated Docker containers or restricted virtual environments to prevent unauthorized host file system access.
  4. Human-in-the-Loop (HITL): For destructive tools (such as deleting records, sending emails, or modifying infrastructure), the MCP Host must require explicit user approval before dispatching final execution.

Real-World Integrations: From Claude Desktop to Production Pipelines

The true beauty of MCP lies in its immediate interoperability. Once your MCP server is built and tested, integrating it into your existing ecosystem requires only configuring a JSON file in your chosen Host. For instance, to register our Python server in Claude Desktop, we add the following configuration to the claude_desktop_config.json file:

{
  "mcpServers": {
    "enterprise-metrics": {
      "command": "python",
      "args": [
        "/path/to/your/mcp_server.py"
      ]
    }
  }
}

Beyond traditional chat clients like Claude Desktop, the MCP architecture shines brightly in integrated development environments like Cursor and in cloud-based autonomous agent pipelines built on frameworks like LangGraph or AutoGen. In advanced enterprise architectures, MCP servers are packaged as microservices accessed via SSE, secured by API gateways with mTLS (mutual Transport Layer Security, a secure protocol where both the client and server verify each other's digital certificates) and OAuth2 authentication, allowing entire fleets of autonomous agents to collaborate on complex software engineering and data analysis workflows without friction.

The Horizon of the Open and Interoperable Agentic Web

The advent of the Model Context Protocol marks a turning point in AI-driven software engineering. We are moving away from the era of proprietary silos and entering an ecosystem where autonomous agents can navigate, interact, and modify the digital world in a standardized and secure manner. For developers and architects, mastering the creation of MCP servers and tools is no longer an optional differentiator, but an essential skill to build the next generation of intelligent applications. The future of the web is agentic, interoperable, and built on open protocols.