Master the MCP Protocol: Core Concepts, Implementation Details, and Best‑Practice Applications

This article provides a systematic deep‑dive into the Model Context Protocol (MCP), explaining its purpose, architecture, core capabilities, data and transport layers, and step‑by‑step guides for building both local and remote MCP servers with Python, while also covering security, permission, and best‑practice recommendations for real‑world AI applications.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
Master the MCP Protocol: Core Concepts, Implementation Details, and Best‑Practice Applications

Why MCP Is Needed

Large language models can understand and generate text but cannot directly access real‑time data, files, databases, or business systems. To give a model the ability to search the web, read files, or call APIs, the surrounding AI application must provide the appropriate tools.

Without a unified protocol, each AI application would need custom integration code for every external system, leading to an M×N explosion of integration points as the number of apps (M) and services (N) grows.

MCP (Model Context Protocol) is an open protocol that standardizes how AI applications connect to external tools, data, and prompt templates.

MCP does not replace the model or perform inference; it only provides a uniform layer for tool discovery, invocation, and result return.

What MCP Actually Is

The full name is Model Context Protocol, initiated by Anthropic and now an open standard for AI ecosystems. It can be thought of as the USB‑C of AI‑to‑external‑service connections: as long as both client and server follow the same message format, they can discover capabilities, invoke tools, and return results.

What MCP Is Not

It is not a large model and does not perform training or inference.

It is not a full‑featured agent framework; it does not handle task planning or autonomous loops.

It does not implement any specific tool – it only defines how tools are described, discovered, and invoked.

It is not an authentication system; identity, authorization, and approval must be handled by the Host, Server, and underlying infrastructure.

It is distinct from Function Calling – Function Calling expresses a single tool call, while MCP standardizes the whole discovery‑to‑execution pipeline.

Three Core Capability Types

Tools : executable functions that can read data or cause side effects (e.g., get_weather, book_flight).

Resources : read‑only contextual data provided by the application (e.g., file contents, database schemas, user profiles).

Prompts : reusable prompt templates that the user can select (e.g., code‑review template, incident‑response template).

Resources provide context, Tools provide executable ability, Prompts provide reusable interaction entry points.

Basic MCP Architecture

MCP follows a client‑server model with three roles:

MCP Host : the AI‑capable application that manages sessions, calls the model, aggregates context, controls permissions, and coordinates tool calls (e.g., Claude Code, VS Code extensions).

MCP Client : an internal component of the Host that establishes a session with a specific Server, negotiates version and capabilities, and sends requests.

MCP Server : a service (local or remote) that exposes Tools, Resources, and Prompts.

Each Host can connect to multiple Servers, typically creating a dedicated Client per Server.

Design implications:

Capability isolation : each Server only exposes the data and tools it owns.

Security isolation : Servers do not see each other’s data or the full conversation unless explicitly shared.

Protocol Layers and Transport

Data Layer

JSON‑RPC 2.0 request/response/notification messages.

Lifecycle management.

Version negotiation and capability advertisement.

Definitions for Tools, Resources, Prompts.

Logging and progress notifications.

All messages use JSON‑RPC 2.0. Requests that expect a response use the request‑response pattern; fire‑and‑forget actions can use notifications.

Transport Layer

stdio : the Client spawns the Server as a subprocess and exchanges messages via standard input/output – ideal for local file‑system, development tools, or local databases.

Streamable HTTP : HTTP POST/GET with optional Server‑Sent Events for streaming – used for SaaS services, cloud databases, or internal enterprise services.

Older HTTP + SSE is deprecated; SSE remains an optional streaming mode within Streamable HTTP.

Building and Connecting an MCP Server – Example: Weather Service

Environment Setup

Requires Python 3.10+. Install uv and create a virtual environment:

curl -LsSf https://astral.sh/uv/install.sh | sh
uv init weather
cd weather
uv venv
source .venv/bin/activate
uv add "mcp[cli]>=1.27,<2" httpx
touch weather.py

Weather Server Code (Python)

The example uses the stable 1.x MCP SDK. Three functions are decorated with @mcp.tool() so the SDK can generate the tool schema.

import json, logging, httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather")

# Helper to fetch JSON with timeout and error handling
async def get_json(url: str, *, headers: dict[str, str] | None = None, params: dict[str, str] | None = None) -> dict[str, any] | None:
    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.get(url, headers=headers, params=params)
            response.raise_for_status()
            return response.json()
    except (httpx.HTTPError, json.JSONDecodeError) as exc:
        logging.warning("Weather API request failed: %s", exc)
        return None

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Query active weather alerts for a US state (e.g., "CA")."""
    headers = {"User-Agent": "weather-mcp/1.0", "Accept": "application/geo+json"}
    data = await get_json(f"https://api.weather.gov/alerts/active/area/{state.upper()}", headers=headers)
    if not data or "features" not in data:
        return "Unable to fetch alerts at this time."
    features = data["features"]
    if not features:
        return f"{state.upper()} currently has no active alerts."
    return "

---

".join(format_alert(item) for item in features[:10])

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get a short‑term forecast for a US coordinate pair."""
    headers = {"User-Agent": "weather-mcp/1.0", "Accept": "application/geo+json"}
    points = await get_json(f"https://api.weather.gov/points/{latitude},{longitude}", headers=headers)
    if not points:
        return "Unable to retrieve grid information for this location."
    forecast_url = points.get("properties", {}).get("forecast")
    if not forecast_url:
        return "Weather service did not return a forecast URL."
    forecast = await get_json(forecast_url, headers=headers)
    if not forecast:
        return "Unable to fetch detailed forecast."
    periods = forecast.get("properties", {}).get("periods", [])[:5]
    if not periods:
        return "No forecast data available for this location."
    results = []
    for p in periods:
        results.append("
".join([
            f"{p.get('name', 'Unknown')}:",
            f"Temperature: {p.get('temperature')}°{p.get('temperatureUnit', '')}",
            f"Wind: {p.get('windSpeed', 'Unknown')} {p.get('windDirection', '')}",
            f"Forecast: {p.get('detailedForecast', 'Unknown')}"
        ]))
    return "

---

".join(results)

@mcp.tool()
async def get_global_forecast(city: str) -> str:
    """Query current weather and a three‑day forecast for a global city (e.g., "Shanghai")."""
    data = await get_json(f"https://wttr.in/{city}", params={"format": "j1"})
    if not data:
        return f"Unable to fetch weather for {city}. Check the city name and try again."
    current = data.get("current_condition", [{}])[0]
    description = current.get("weatherDesc", [{}])[0].get("value", "Unknown")
    result = [
        f"Location: {city}",
        f"Current weather: {description}",
        f"Temperature: {current.get('temp_C', 'N/A')}°C",
        f"Feels like: {current.get('FeelsLikeC', 'N/A')}°C",
        f"Humidity: {current.get('humidity', 'N/A')}%",
        "",
        "Future weather:"
    ]
    for day in data.get("weather", [])[:3]:
        result.append(f"- {day.get('date', 'Unknown')}: {day.get('mintempC', 'N/A')}~{day.get('maxtempC', 'N/A')}°C")
    return "
".join(result)

if __name__ == "__main__":
    mcp.run(transport="stdio")

The three @mcp.tool() functions become MCP Tools that the Host can invoke: get_alerts: query US state weather alerts. get_forecast: fetch a short‑term forecast by latitude/longitude. get_global_forecast: fetch global city weather and a three‑day summary.

Connecting the Server

Create a .mcp.json file in the project root:

{
  "mcpServers": {
    "weather": {
      "type": "stdio",
      "command": "uv",
      "args": ["--directory", "${WEATHER_MCP_DIR}", "run", "weather.py"]
    }
  }
}

Set the environment variable WEATHER_MCP_DIR to the absolute path of the weather project, then start Claude Code. The command /mcp shows the connection status.

Remote Server Example: GitHub

Add a remote HTTP server via the CLI:

claude mcp add --transport http github https://api.githubcopilot.com/mcp/

OAuth authentication is recommended; if a token is used, store it in an environment variable rather than hard‑coding.

Configuration Scopes

local (default): visible only to the current user and project.

project : shared among project members via .mcp.json.

user : available to all projects of the current user.

Scope precedence: local > project > user.

How MCP Works – End‑to‑End Call Flow

Host creates a Client and sends an initialize request (JSON‑RPC) to negotiate protocol version and exchange capabilities.

After initialization, Host requests the list of available tools ( tools/list).

When a user asks a question (e.g., “What’s the weather in Hangzhou tomorrow?”), the model decides a tool is needed and generates a tools/call request with the chosen tool name and arguments.

Host checks permissions, possibly asks the user for confirmation, then routes the call to the appropriate Client.

Client forwards the tools/call request to the Server, which executes the real logic and returns a structured result.

Host injects the result back into the model’s context so the model can produce a final answer or continue a multi‑step plan.

Best‑Practice Guidelines for MCP Development and Use

Design Clear Tool Interfaces

Use explicit verbs (e.g., get_order, create_issue).

Provide concise, accurate descriptions and JSON‑Schema for parameters.

Keep each tool focused on a single responsibility.

Define stable output schemas when possible.

Separate Read‑Only and Write Operations

Split queries, creations, updates, and deletions into distinct tools.

This improves model selection accuracy, enables fine‑grained approval policies, and reduces accidental side‑effects.

Tool annotations such as readOnlyHint, destructiveHint, idempotentHint can convey intent, but they are not security guarantees.

Require Human Confirmation for High‑Risk Actions

Deletion, file writes, email sending, order creation, production config changes, code merges, bulk modifications, etc., should prompt the user with tool name, key parameters, and impact.

Apply the Principle of Least Privilege

Grant only the permissions needed for each tool.

Prefer read‑only scopes and elevate only when necessary.

Restrict file‑system access, limit database accounts, and use short‑lived, narrowly‑scoped tokens.

Avoid sharing a single high‑privilege credential across many servers.

Secure Credential Management

Never hard‑code secrets in .mcp.json, source code, or command arguments.

Use environment variables, secret managers, or OS keychains.

Never log tokens, cookies, or full request bodies.

Remote servers must use HTTPS and preferably OAuth flows.

Servers must validate token audience, expiry, and scopes.

Be Cautious with Third‑Party Servers

Verify publisher trustworthiness, audit source code and dependencies.

Check launch commands for suspicious scripts.

Understand which files, networks, and credentials the server can access.

Prefer sandboxed or containerized execution.

Control Output Size and Context Cost

Support pagination, filtering, and result limits.

Return concise summaries by default; fetch details on demand.

Avoid redundant fields and unrelated metadata.

Prefer structured JSON output.

Set maximum lengths for logs, search results, and file contents.

Load only the tools needed for the current task.

Robust Error Handling

Set sensible timeouts for external calls.

Retry only idempotent operations.

Distinguish argument errors, permission errors, rate‑limit, and service failures.

Return clear, machine‑parseable error messages without stack traces or secrets.

Provide progress updates or async handling for long‑running tasks.

Logging, Monitoring, and Auditing

Record who initiated the call, which tool was used, target resource, whether confirmation was required, execution time, result status, and any permission or rate‑limit failures.

Avoid logging passwords, tokens, full private data, or raw tool output.

Relationship Between MCP, Function Calling, and Agents

Function Calling : how the model expresses a single tool call in a structured way; it lives between the model and the Host.

MCP : the protocol the Host uses to discover, connect, and invoke external capabilities.

Agent : the higher‑level planning component that decides which tools to call and orchestrates multi‑step workflows.

A typical flow: Agent/Host plans → Model emits Function Calling → Host uses MCP to locate and call the tool → Result returns to model for further reasoning.

Common Pitfalls and Troubleshooting

Server Connected but Model Doesn’t Call Tools

Check tool names, descriptions, and parameter schemas for clarity.

Ensure the tool matches the user’s intent.

Verify the Host actually exposes the tool to the model.

Avoid naming collisions or overly broad responsibilities.

Too many tools can confuse the model; prune unnecessary ones.

Local Server Fails to Start

Confirm the command exists in PATH and arguments point to the correct files.

Validate Python/Node version compatibility.

Make sure all dependencies are installed.

Ensure the server writes only protocol messages to stdout and logs to stderr.

Check for startup timeouts.

Remote Server Returns 401/403

Complete OAuth flow.

Refresh expired tokens.

Verify the token’s scope includes required permissions.

Check audience validation.

Confirm organizational policies allow the connection.

Tool Call Succeeds but Answer Quality Is Poor

Trim unnecessary output.

Return structured data instead of free‑form text.

Clearly label fields and units.

Set isError for failure cases.

Document tool applicability in the description.

Host should only inject the most relevant information back into the model.

Conclusion

MCP’s value lies in providing a uniform, composable connection layer for AI applications to reach external tools, data, and prompts. It separates three concerns:

Architecture : Host coordinates, Client communicates, Server provides capabilities.

Protocol : JSON‑RPC messages define semantics; transport can be stdio or Streamable HTTP.

Application : Models or Agents decide what to do; MCP handles the standardized execution; security policies enforce safe operation.

While MCP reduces integration effort, it does not automatically solve permission, security, or reliability concerns. Production‑ready deployments require clear tool design, least‑privilege access, human confirmation for risky actions, credential protection, output size control, and comprehensive logging and audit trails.

When many AI apps and services adopt a common protocol, developers no longer need to write custom glue code for each combination. That standardization is the core promise of MCP.

References

MCP official architecture documentation

MCP protocol version specifications

MCP Server core capabilities overview

MCP transport protocol details

MCP Server development tutorial

MCP security best‑practice guide

Claude Code MCP usage documentation

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonMCPbest practicessecurityModel Context ProtocolAI integrationtool calling
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.