Проблема, которую Решает 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, вскоре и на многих других.
Паттерн, который он открывает, мощный: вместо того чтобы дообучать модель для знания ваших конкретных систем, вы даёте модели возможность запрашивать в режиме реального времени. Модель остаётся общей; инструменты делают её специфичной для вашего контекста. Это разделение чище, проще в обслуживании и легче поддаётся аудиту, чем попытка встроить предметные знания в веса.
Ресурсы
- Официальная документация MCP
- Python SDK для MCP на GitHub
- Реестр серверов MCP — эталонные реализации для GitHub, Slack, Postgres, файловой системы и других
- Руководство по MCP для Claude Desktop