본문으로 건너뛰기 MCP (Model Context Protocol) Complete Guide

MCP (Model Context Protocol) Complete Guide

MCP (Model Context Protocol) Complete Guide

이 글의 핵심

MCP (Model Context Protocol) is an open protocol that allows AI models to communicate with external resources — file systems, databases, web APIs — in a standardized way. Supported by Claude Desktop, Cursor, VS Code, and more.

What is MCP?

MCP (Model Context Protocol) is an open-source protocol Anthropic published in late 2024. It lets AI models communicate with external resources — file systems, databases, web APIs, dev tools — in a standardized way.

Client (Claude Desktop, Cursor, VS Code)
    ↕ MCP Protocol
Server (file system, DB, web API, Git, Slack...)

Why MCP?

Before MCP, every AI app had to write its own integration code for every external tool. MCP provides a USB-like standard interface: write an MCP server once and reuse it across all compatible clients.

Before MCPWith MCP
Each app writes its own API integrationWrite MCP server once → all clients use it
Model changes require code rewritesWorks regardless of model or client
Security/auth reimplemented repeatedlyHandled consistently on the server side

MCP Architecture

┌─────────────────────────────────────────┐
│             MCP Client                  │
│  (Claude Desktop / Cursor / VS Code)    │
└────────────────┬────────────────────────┘
                 │ JSON-RPC over stdio / SSE
┌────────────────▼────────────────────────┐
│              MCP Server                 │
│  ┌──────────┐  ┌──────────┐  ┌───────┐ │
│  │  Tools   │  │Resources │  │Prompts│ │
│  └──────────┘  └──────────┘  └───────┘ │
└────────────────┬────────────────────────┘

     ┌───────────┼───────────┐
     ▼           ▼           ▼
  File system  Database   Web API

3 Core Concepts

  • Tools: Functions the AI can call (similar to Function Calling)
  • Resources: Data the AI can read (files, DB records, etc.)
  • Prompts: Reusable prompt templates

Quick Start: Python MCP Server

Installation

pip install mcp
# or using uv (recommended)
pip install uv
uv add mcp

Your First MCP Server

The list_tools function is implemented below. It handles the core logic described above:

# server.py
from mcp.server import Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import mcp.types as types

# Create server instance
server = Server("my-first-server")

@server.list_tools()
async def list_tools() -> list[Tool]:
    """Return list of available tools"""
    return [
        Tool(
            name="calculate",
            description="Evaluates a mathematical expression",
            inputSchema={
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "Expression to evaluate (e.g. 2 + 3 * 4)"
                    }
                },
                "required": ["expression"]
            }
        ),
        Tool(
            name="get_current_time",
            description="Returns the current time",
            inputSchema={
                "type": "object",
                "properties": {}
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    """Handle tool calls"""
    if name == "calculate":
        expression = arguments.get("expression", "")
        try:
            # Safe eval (use a proper sandbox in production)
            result = eval(expression, {"__builtins__": {}}, {})
            return [TextContent(type="text", text=f"{expression} = {result}")]
        except Exception as e:
            return [TextContent(type="text", text=f"Error: {str(e)}")]
    
    elif name == "get_current_time":
        from datetime import datetime
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        return [TextContent(type="text", text=f"Current time: {now}")]
    
    raise ValueError(f"Unknown tool: {name}")

async def main():
    # Run in stdio mode (for local Claude Desktop connections)
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="my-first-server",
                server_version="1.0.0",
                capabilities=server.get_capabilities(
                    notification_options=None,
                    experimental_capabilities={}
                )
            )
        )

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

Real-World Example: File System MCP Server

The list_tools function is implemented below. It handles the core logic described above:

# file_server.py
from mcp.server import Server
from mcp.server.models import InitializationOptions
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
from pathlib import Path
import os

server = Server("file-system-server")
ALLOWED_DIR = Path(os.environ.get("ALLOWED_DIR", "/tmp/mcp-files"))
ALLOWED_DIR.mkdir(exist_ok=True)

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="read_file",
            description="Read file contents",
            inputSchema={
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "File path"}
                },
                "required": ["path"]
            }
        ),
        Tool(
            name="write_file",
            description="Write content to a file",
            inputSchema={
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "File path"},
                    "content": {"type": "string", "description": "Content to write"}
                },
                "required": ["path", "content"]
            }
        ),
        Tool(
            name="list_files",
            description="List files in a directory",
            inputSchema={
                "type": "object",
                "properties": {
                    "directory": {"type": "string", "description": "Directory path (defaults to base dir)"}
                }
            }
        )
    ]

def validate_path(path_str: str) -> Path:
    """Security: only allow paths within the permitted directory"""
    path = (ALLOWED_DIR / path_str).resolve()
    if not str(path).startswith(str(ALLOWED_DIR)):
        raise ValueError("Access denied: path is outside the allowed directory")
    return path

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "read_file":
        path = validate_path(arguments["path"])
        if not path.exists():
            return [TextContent(type="text", text=f"Error: file not found: {path}")]
        content = path.read_text(encoding="utf-8")
        return [TextContent(type="text", text=content)]
    
    elif name == "write_file":
        path = validate_path(arguments["path"])
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(arguments["content"], encoding="utf-8")
        return [TextContent(type="text", text=f"File saved: {path.name}")]
    
    elif name == "list_files":
        dir_path = validate_path(arguments.get("directory", "."))
        if not dir_path.is_dir():
            return [TextContent(type="text", text="Not a directory")]
        
        files = []
        for item in sorted(dir_path.iterdir()):
            if item.is_dir():
                files.append(f"📁 {item.name}/")
            else:
                size = item.stat().st_size
                files.append(f"📄 {item.name} ({size:,} bytes)")
        
        result = "\n".join(files) if files else "No files found"
        return [TextContent(type="text", text=result)]
    
    raise ValueError(f"Unknown tool: {name}")

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream,
            InitializationOptions(server_name="file-system-server", server_version="1.0.0",
                capabilities=server.get_capabilities(notification_options=None, experimental_capabilities={})))

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

Building an MCP Server with TypeScript

npm install @modelcontextprotocol/sdk

Import the required modules and set up the dependencies:

// server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import fetch from "node-fetch";

const server = new Server(
  { name: "weather-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// Define tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_weather",
      description: 'Get current weather for a city. MCP (Model Context Protocol) Complete Guide에 대한 완전한 가이드입니다. 실전 예제와 함께 핵심 개념부터 고급 활용까지 다룹니다.',
      inputSchema: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "City name (in English)",
          },
        },
        required: ["city"],
      },
    },
  ],
}));

// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "get_weather") {
    const city = args?.city as string;
    const apiKey = process.env.OPENWEATHER_API_KEY;

    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
    );
    const data = (await response.json()) as any;

    if (data.cod !== 200) {
      return {
        content: [{ type: "text", text: `Error: ${data.message}` }],
      };
    }

    return {
      content: [
        {
          type: "text",
          text: `${city} weather: ${data.weather[0].description}, ${data.main.temp}°C (feels like ${data.main.feels_like}°C)`,
        },
      ],
    };
  }

  throw new Error(`Unknown tool: ${name}`);
});

// Start server
const transport = new StdioServerTransport();
await server.connect(transport);

Connecting to Claude Desktop

Configuration file:

// ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
// %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
  "mcpServers": {
    "my-calculator": {
      "command": "python",
      "args": ["/path/to/server.py"],
      "env": {}
    },
    "file-system": {
      "command": "python",
      "args": ["/path/to/file_server.py"],
      "env": {
        "ALLOWED_DIR": "/Users/username/Documents"
      }
    },
    "weather": {
      "command": "node",
      "args": ["/path/to/weather-server/build/index.js"],
      "env": {
        "OPENWEATHER_API_KEY": "your-api-key-here"
      }
    }
  }
}

Restart Claude Desktop and a 🔧 tool icon will appear at the bottom of the chat window.


Using Resources and Prompts

Resources (Reading Data)

The list_resources function is implemented below. It handles the core logic described above:

from mcp.types import Resource, TextResourceContents

@server.list_resources()
async def list_resources() -> list[Resource]:
    return [
        Resource(
            uri="file:///config.json",
            name="Config file",
            description="Application configuration",
            mimeType="application/json"
        )
    ]

@server.read_resource()
async def read_resource(uri: str) -> str | bytes:
    if uri == "file:///config.json":
        return '{"version": "1.0", "debug": false}'
    raise ValueError(f"Unknown resource: {uri}")

Prompts (Reusable Templates)

The list_prompts function is implemented below. It handles the core logic described above:

from mcp.types import Prompt, PromptArgument, GetPromptResult, PromptMessage

@server.list_prompts()
async def list_prompts() -> list[Prompt]:
    return [
        Prompt(
            name="code_review",
            description="Code review request prompt",
            arguments=[
                PromptArgument(name="language", description="Programming language", required=True),
                PromptArgument(name="focus", description="Review focus area", required=False)
            ]
        )
    ]

@server.get_prompt()
async def get_prompt(name: str, arguments: dict | None) -> GetPromptResult:
    if name == "code_review":
        language = arguments.get("language", "code") if arguments else "code"
        focus = arguments.get("focus", "overall quality") if arguments else "overall quality"
        
        return GetPromptResult(
            description=f"{language} code review",
            messages=[
                PromptMessage(
                    role="user",
                    content=TextContent(
                        type="text",
                        text=f"""Please review the following {language} code with a focus on {focus}:
                        
1. Code quality and readability
2. Potential bugs and errors
3. Performance optimization opportunities
4. Security vulnerabilities
5. Improvement suggestions (with code examples)"""
                    )
                )
            ]
        )
    raise ValueError(f"Unknown prompt: {name}")

Official MCP servers from Anthropic and the community:

Run the following commands:

# Filesystem server (official)
npx -y @modelcontextprotocol/server-filesystem /path/to/directory

# SQLite server (official)
npx -y @modelcontextprotocol/server-sqlite /path/to/database.db

# GitHub server (official)
npx -y @modelcontextprotocol/server-github

# PostgreSQL server
npx -y @modelcontextprotocol/server-postgres postgresql://user:pass@localhost/db

# Slack server
npx -y @modelcontextprotocol/server-slack

# Brave Search server
npx -y @modelcontextprotocol/server-brave-search

Conclusion

MCP is rapidly becoming core infrastructure for the AI agent ecosystem. Learning it now gives you a significant edge in AI agent development.

Next steps:


Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. MCP (Model Context Protocol) is Anthropic’s open standard for connecting AI to external tools. Learn how to build and co…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers MCP, Claude, AI, Agent, Python, TypeScript, Tools, Anthropic.