Проблема, которую Решает MCP

Каждый ИИ-ассистент — Claude, Copilot, Cursor, любой другой — в конечном счёте упирается в одну и ту же стену: модель знает много, но не может ничего сделать за пределами своего контекстного окна. Она не может прочитать вашу базу данных, запросить внутренний API или узнать, который сейчас час — если только кто-то явно не построил этот мост.

До MCP каждая команда, создававшая инструмент на базе ИИ, должна была создавать собственные интеграции с нуля. Хотите, чтобы Claude искал в вашем Notion? Напишите кастомную интеграцию. Хотите, чтобы он запрашивал Postgres? Напишите ещё одну. Каждая интеграция была одноразовой, хрупкой и не поддающейся повторному использованию.

Model Context Protocol (MCP) — ответ Anthropic: открытый стандарт, определяющий единый универсальный интерфейс между ИИ-хостами и внешним миром. Создайте MCP-сервер один раз — и любой совместимый клиент: Claude Desktop, Claude Code, Cursor, ваше собственное приложение — сможет использовать его немедленно.

Думайте об этом как о HTTP для инструментов ИИ. HTTP не изобрёл веб, но дал каждому браузеру и серверу общий язык. MCP делает то же самое для ИИ и сервисов, с которыми ему нужно взаимодействовать.


Архитектура из Трёх Частей

MCP имеет три роли:

Хост — Приложение, с которым взаимодействует пользователь. Claude Desktop, Claude Code, Cursor или ваше собственное приложение. Хост содержит MCP-клиент, который управляет соединениями.

Клиент — Живёт внутри хоста. Поддерживает соединение 1:1 с MCP-сервером, управляет согласованием протокола и маршрутизирует запросы модели на сервер и ответы обратно.

Сервер — Внешний процесс (локальный или удалённый), который предоставляет возможности модели. Именно эту часть вы создаёте. Сервер может быть таким простым, как один файл Python.

┌──────────────────────────────────┐
             Host                 
  ┌──────────┐   ┌─────────────┐  
     LLM    │◄──│ MCP Client    
  └──────────┘   └──────┬──────┘  
└─────────────────────────┼────────┘
                            MCP Protocol
              ┌───────────▼──────────┐
                    MCP Server      
                (your code / tool)  
              └──────────────────────┘

Что Могут Предоставлять Серверы

MCP-сервер может предоставлять три типа возможностей:

Инструменты

Функции, которые модель может вызывать — самый распространённый и полезный тип. Модель сама решает, когда вызвать инструмент на основе разговора, получает результат и включает его в свой ответ.

@mcp.tool()
def query_database(sql: str) -> str:
    """Выполняет SQL-запрос только для чтения и возвращает результаты в формате JSON"""
    ...

Ресурсы

Структурированные данные, которые модель может читать — файлы, содержимое баз данных, ответы API. Ресурсы идентифицируются URI и читаются по требованию.

@mcp.resource("logs://app/recent")
def get_recent_logs() -> str:
    """Возвращает последние 100 строк лога приложения"""
    ...

Промпты

Многократно используемые шаблоны промптов, которые хост может показывать пользователям. Полезны для стандартизации типовых рабочих процессов.

@mcp.prompt()
def code_review(code: str) -> str:
    return f"Проверь этот код на наличие ошибок, проблем безопасности и стиля:\n\n{code}"

Транспорт: Как Общаются Клиенты и Серверы

MCP поддерживает два транспортных механизма:

stdio — Сервер запускается как подпроцесс хоста. Общение происходит через stdin/stdout. Это стандартный подход для локальных инструментов — без настройки сети, просто и быстро.

HTTP + SSE — Сервер работает как HTTP-сервис. Клиент отправляет запросы по HTTP и получает потоковые ответы через Server-Sent Events. Используется для удалённых серверов или когда нужен постоянный сетевой сервис.

Для локальной разработки и личных инструментов stdio почти всегда является правильным выбором.


Создание Первого MCP-Сервера на Python

Установите SDK:

pip install mcp

Ниже — полный рабочий MCP-сервер, предоставляющий несколько полезных инструментов: запрос текущего времени, чтение файла и базовый веб-поиск через API DuckDuckGo:

# server.py
from datetime import datetime
from pathlib import Path
import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("dev-tools")


@mcp.tool()
def current_datetime() -> str:
    """Возвращает текущие дату и время."""
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")


@mcp.tool()
def read_file(path: str) -> str:
    """
    Читает файл из файловой системы и возвращает его содержимое.

    Args:
        path: Абсолютный или относительный путь к файлу.
    """
    try:
        return Path(path).read_text(encoding="utf-8")
    except FileNotFoundError:
        return f"Ошибка: файл не найден: {path}"
    except Exception as e:
        return f"Ошибка при чтении файла: {e}"


@mcp.tool()
def web_search(query: str, max_results: int = 5) -> str:
    """
    Ищет в интернете через DuckDuckGo и возвращает сводку результатов.

    Args:
        query: Поисковый запрос.
        max_results: Количество возвращаемых результатов (по умолчанию 5).
    """
    url = "https://api.duckduckgo.com/"
    params = {"q": query, "format": "json", "no_redirect": 1}
    response = httpx.get(url, params=params, timeout=10)
    data = response.json()

    results = []
    for topic in data.get("RelatedTopics", [])[:max_results]:
        if "Text" in topic and "FirstURL" in topic:
            results.append(f"- {topic['Text']}\n  {topic['FirstURL']}")

    if not results:
        return f"Результаты не найдены для: {query}"
    return "\n".join(results)


@mcp.resource("env://system")
def system_info() -> str:
    """Возвращает базовую информацию о системе."""
    import platform
    return (
        f"ОС: {platform.system()} {platform.release()}\n"
        f"Python: {platform.python_version()}\n"
        f"Архитектура: {platform.machine()}"
    )


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

Запустите напрямую, чтобы убедиться, что запускается без ошибок:

python server.py

Подключение к Claude Desktop

Claude Desktop читает конфигурацию MCP-сервера из JSON-файла:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Добавьте свой сервер:

{
  "mcpServers": {
    "dev-tools": {
      "command": "python",
      "args": ["/абсолютный/путь/к/server.py"]
    }
  }
}

Перезапустите Claude Desktop. В интерфейсе чата появится небольшой значок розетки, подтверждающий подключение сервера. Теперь вы можете просить Claude использовать любой из ваших инструментов прямо в разговоре:

«Который сейчас час?» «Прочитай файл /home/user/notes.txt и сделай его краткое изложение.» «Найди в интернете последние новости о Rust 2025.»

Claude самостоятельно решает, когда вызывать каждый инструмент. Не нужно просить об этом явно — он воспользуется нужным инструментом, когда контекст этого потребует.


Подключение к Claude Code

В Claude Code (этом CLI) MCP-серверы настраиваются на уровне проекта или глобально. Добавьте сервер в конфигурацию проекта:

claude mcp add dev-tools python /абсолютный/путь/к/server.py

Или отредактируйте .claude/settings.json напрямую:

{
  "mcpServers": {
    "dev-tools": {
      "command": "python",
      "args": ["/абсолютный/путь/к/server.py"]
    }
  }
}

Более Реалистичный Пример: Инструменты для Базы Данных

Вот пример, ближе к продакшену — MCP-сервер, предоставляющий доступ только для чтения к базе данных SQLite:

# db_server.py
import sqlite3
import json
from mcp.server.fastmcp import FastMCP

DB_PATH = "app.db"
mcp = FastMCP("database")


@mcp.tool()
def list_tables() -> str:
    """Перечисляет все таблицы в базе данных."""
    with sqlite3.connect(DB_PATH) as conn:
        tables = conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
        ).fetchall()
    return json.dumps([t[0] for t in tables])


@mcp.tool()
def describe_table(table: str) -> str:
    """
    Возвращает схему таблицы.

    Args:
        table: Имя таблицы.
    """
    with sqlite3.connect(DB_PATH) as conn:
        cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
    return json.dumps([
        {"name": c[1], "type": c[2], "nullable": not c[3], "pk": bool(c[5])}
        for c in cols
    ], indent=2)


@mcp.tool()
def query(sql: str) -> str:
    """
    Выполняет SQL-запрос SELECT только для чтения.

    Args:
        sql: Оператор SELECT. INSERT/UPDATE/DELETE отклоняются.
    """
    sql_stripped = sql.strip().upper()
    if not sql_stripped.startswith("SELECT"):
        return "Ошибка: разрешены только запросы SELECT."

    with sqlite3.connect(DB_PATH) as conn:
        conn.row_factory = sqlite3.Row
        rows = conn.execute(sql).fetchall()

    return json.dumps([dict(r) for r in rows], indent=2, default=str)


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

Подключив это к Claude, вы можете вести разговор на естественном языке о своей базе данных:

«Какие у нас есть таблицы?» «Покажи последние 10 заказов на сумму свыше 5000 рублей.» «Сколько пользователей регистрировалось каждый месяц в этом году?»

Claude переводит ваш запрос в SQL, вызывает инструмент query и представляет результаты — вам не нужно писать запрос самостоятельно.


Соображения Безопасности

MCP-серверы запускаются с теми же правами, что и запускающий их процесс. Несколько базовых правил:

  • Открывайте только необходимое. Не создавайте общий инструмент run_shell_command, если у вас нет очень веской причины.
  • Валидируйте входные данные. Относитесь к аргументам инструментов как к любым другим внешним данным — они приходят от LLM, которым могут управлять пользователи.
  • Применяйте только чтение там, где это уместно. Пример с базой данных выше отклоняет всё, что не является SELECT.
  • Будьте осторожны с доступом к файлам. Если создаёте инструмент read_file, рассмотрите возможность ограничить его конкретным каталогом вместо того, чтобы принимать произвольные пути.

Общая Картина

MCP ещё молод, но кривая его принятия крутая. За несколько месяцев с момента запуска появились сотни серверов — для GitHub, Slack, Postgres, Kubernetes, веб-браузеров, векторных баз данных и других систем. Поскольку протокол открытый, любой инструмент, который вы создадите, работает на всех совместимых хостах — сегодня Claude Desktop и Claude Code, вскоре и на многих других.

Паттерн, который он открывает, мощный: вместо того чтобы дообучать модель для знания ваших конкретных систем, вы даёте модели возможность запрашивать в режиме реального времени. Модель остаётся общей; инструменты делают её специфичной для вашего контекста. Это разделение чище, проще в обслуживании и легче поддаётся аудиту, чем попытка встроить предметные знания в веса.


Ресурсы