Год назад я смотрел на MCP-серверы и думал — это для разработчиков уровня senior, с конфигами, протоколами, JSON-RPC. Сложно. Пусть кто-нибудь другой разберётся.
Потом я написал MCP сервер своими руками за вечер. На Python, с нуля, без особых мучений. Теперь Claude Code сам генерирует обложки для блога через мой сервер — я просто пишу промпт, остальное происходит автоматически.
В этом гайде — всё, что нужно, чтобы повторить это самому. Минимальный сервер на FastMCP, подключение к Claude Desktop и Cursor, отладка и типичные ошибки. И реальный кейс в конце — мой TypeScript сервер для kie.ai, который я использую каждый день.
Гайд для тех, кто уже работал с AI-инструментами (ChatGPT, Claude, Cursor) и хочет пойти дальше. Если ты впервые слышишь про API или CLI — начни с вайб-кодинга, там более мягкий вход.
Что понадобится
- Python 3.10+ (для примера на FastMCP) или Node.js 18+ (для TypeScript)
- Текстовый редактор или IDE
- Claude Desktop — бесплатно, скачать с claude.ai/download
- Опционально: Cursor, n8n для дополнительных подключений
- Время: 1-2 часа на первый сервер
- Стоимость: бесплатно (только Claude Desktop)
Что такое MCP и зачем вы захотите его сделать
MCP (Model Context Protocol) — это протокол, который позволяет AI-модели обращаться к внешним инструментам и данным. Стандарт выпустила Anthropic в конце 2024 года, и с тех пор под него написали тысячи серверов: для баз данных, GitHub, Slack, браузеров, файловых систем.
Простая аналогия: если обычный API — это телефон, через который ты сам звонишь сервису, то MCP — это когда AI звонит сам, когда ему нужно. Ты один раз описываешь инструмент («вот функция, вот что она делает»), и модель начинает использовать его в своих рассуждениях.
Зачем писать свой сервер, если готовых тысячи? Потому что твои данные — не чужие данные. База клиентов, внутренняя вики, корпоративная CRM, собственный API — это то, чего нет в публичном реестре. MCP-сервер даёт Claude прямой доступ к твоим данным, без копипасты и ручного контекста.
Я, например, написал сервер для генерации обложек через API kie.ai. Теперь в рамках одной сессии Claude Code запускает генерацию картинки, проверяет статус задачи и скачивает результат — всё само, без моего участия.
Архитектура за 5 минут: host, client, server — кто есть кто
Когда говорят «MCP-сервер», это немного сбивает с толку. На самом деле в системе три участника:
Host — приложение с AI-моделью. Claude Desktop, Cursor, n8n. То, чем пользуешься ты.
Client — встроенный в host компонент, который говорит по MCP-протоколу. Ты его не видишь, он внутри.
Server — твой код. Запускается как отдельный процесс, ждёт команд и возвращает результаты. Вот его и пишем.
Claude Desktop (host)
└─ MCP Client (встроен) ←→ твой MCP Server (отдельный процесс)
Важный момент: сервер общается с клиентом через stdio (стандартный ввод-вывод) — stdin и stdout. Никаких портов, никакого HTTP. Просто процесс, который читает JSON из stdin и пишет JSON в stdout. Это важно: если твой сервер печатает что-то лишнее в stdout — он сломает протокол.
Инфо
Есть и HTTP-транспорт (SSE/Streamable HTTP) — для серверов, которые запущены удалённо. Но для начала stdio проще: никакой настройки сети, сервер запускается и останавливается вместе с host-приложением.
Tools, Resources, Prompts — в чём разница и что использовать
MCP предлагает три типа примитивов. Я сам путался поначалу — вот как это работает на практике.
Tools — функции, которые AI вызывает для совершения действий. Написать файл, запросить API, выполнить поиск. Это самое часто используемое. Если не знаешь, что выбрать — бери Tools.
Resources — данные, которые AI читает как контекст. Файл, страница базы данных, результат запроса. Не «сделай что-то», а «дай мне информацию». Думай про это как про системный промпт как архитектуру: ты кормишь модель контекстом через стандартный механизм.
Prompts — шаблоны для быстрого вызова сценариев. Например, готовый промпт «проведи code review вот этого файла». Реже используется, но удобно для повторяющихся сценариев.
Для большинства случаев — Tools. Resources стоит добавлять, когда нужно предоставить AI доступ к конкретным данным, которые меняются.
Python или TypeScript: выбираем стек для MCP сервера
Оба варианта рабочие. Вот честное сравнение.
Python + FastMCP:
- Проще API.
@mcp.tool— один декоратор, и функция стала инструментом - Меньше бойлерплейта
- Хорошо, если уже знаешь Python
- FastMCP — это враппер над официальным Python SDK от Anthropic, добавляющий удобный синтаксис
TypeScript + MCP SDK:
- Официальный SDK Anthropic —
@modelcontextprotocol/sdk - Хорошо, если проект уже в Node.js экосистеме
- Чуть больше кода, зато типизация из коробки
- Идеально при вайб-кодинге — AI хорошо знает TypeScript паттерны
Для гайда я выбрал Python + FastMCP. Меньше кода, быстрее старт. TypeScript покажу на реальном кейсе в конце.
Пишем первый MCP сервер на Python с FastMCP
Установка
pip install fastmcpВсё. Одна зависимость.
Минимальный сервер
Создаём файл server.py:
from fastmcp import FastMCP
mcp = FastMCP("Мой первый сервер")
@mcp.tool
def get_weather(city: str) -> str:
"""Возвращает текущую погоду для города."""
# В реальном коде — вызов weather API
return f"В {city} сейчас 18°C, облачно"
if __name__ == "__main__":
mcp.run()Это рабочий MCP-сервер. Claude увидит инструмент get_weather, прочитает описание из docstring, и при необходимости вызовет его.
Запускаем:
python server.pyСервер стартует и ждёт соединения. Без вывода в консоль — это нормально, он общается через stdio.
Добавляем реальную логику
Сделаем что-то полезное — инструмент для поиска файлов:
from fastmcp import FastMCP
import os
from pathlib import Path
mcp = FastMCP("File Tools")
@mcp.tool
def find_files(directory: str, extension: str = ".py") -> list[str]:
"""
Ищет файлы с заданным расширением в директории.
Args:
directory: Путь к директории для поиска
extension: Расширение файлов (по умолчанию .py)
Returns:
Список путей к найденным файлам
"""
path = Path(directory)
if not path.exists():
return [f"Директория {directory} не существует"]
files = [str(f) for f in path.rglob(f"*{extension}")]
return files[:50] # Не больше 50 результатов
@mcp.tool
def read_file(filepath: str) -> str:
"""Читает содержимое файла и возвращает текст."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
except FileNotFoundError:
return f"Файл {filepath} не найден"
except Exception as e:
return f"Ошибка чтения: {str(e)}"
if __name__ == "__main__":
mcp.run()Несколько моментов, которые не очевидны с первого раза. Docstring — это буквально документация для AI: Claude читает его, чтобы понять когда и как вызывать функцию. Пиши туда что-то внятное, не "делает штуку". Аннотации типов FastMCP превращает в JSON Schema — благодаря им модель знает, что directory это строка, а не список. И главное: обрабатывай ошибки через return, а не raise. Если бросить исключение — сервер упадёт. Верни строку с описанием что пошло не так.
Сервер с внешним API
Теперь что-то, что реально делает HTTP-запросы:
from fastmcp import FastMCP
import httpx
mcp = FastMCP("GitHub Tools")
@mcp.tool
async def get_repo_info(owner: str, repo: str) -> dict:
"""
Получает информацию о GitHub репозитории.
Args:
owner: Владелец репозитория (логин пользователя или организации)
repo: Название репозитория
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.github.com/repos/{owner}/{repo}",
headers={"Accept": "application/vnd.github.v3+json"}
)
if response.status_code == 404:
return {"error": f"Репозиторий {owner}/{repo} не найден"}
data = response.json()
return {
"name": data["name"],
"description": data.get("description", ""),
"stars": data["stargazers_count"],
"language": data.get("language", ""),
"url": data["html_url"],
}
if __name__ == "__main__":
mcp.run()FastMCP поддерживает и async-функции — без дополнительных настроек.
Совет
Секреты (API-ключи, токены) передавай через переменные окружения, не хардкодь в коде. В сервере читай через os.environ.get("MY_API_KEY"). Как именно передать переменные в сервер — покажу в разделе про подключение.
Подключаем сервер к Claude Desktop
Claude Desktop — это десктопное приложение от Anthropic. Бесплатно, macOS и Windows. Скачать: claude.ai/download.
Конфиг
Конфиг MCP-серверов лежит в одном файле:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Открываем и добавляем:
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": ["/абсолютный/путь/к/server.py"],
"env": {
"MY_API_KEY": "sk-ваш-ключ-здесь"
}
}
}
}Важно: путь должен быть абсолютным. Относительные пути не работают — Claude Desktop запускается из другой директории.
После сохранения конфига — перезапускаем Claude Desktop. Без перезапуска изменения не применятся.
Если всё правильно, в новом чате появится иконка инструментов (молоток) рядом с полем ввода. Кликни — увидишь список доступных серверов и их инструментов.
Тест
Пишем в Claude Desktop:
Найди все .py файлы в /Users/pavel/projects
Claude должен вызвать find_files автоматически, если ты добавил сервер из предыдущего раздела.
Virtualenv
Если используешь виртуальное окружение (venv — изолированный Python со своими пакетами, чтобы зависимости разных проектов не конфликтовали) — указывай путь к интерпретатору внутри него:
{
"mcpServers": {
"my-tools": {
"command": "/путь/к/проекту/.venv/bin/python",
"args": ["/путь/к/проекту/server.py"]
}
}
}Подключаем к Cursor и n8n
Cursor
Cursor (AI-редактор кода) поддерживает MCP с версии 0.43. Настройка через Settings → MCP:
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": ["/абсолютный/путь/к/server.py"]
}
}
}Синтаксис тот же, что и для Claude Desktop. После добавления в Cursor Agent Mode инструменты становятся доступны автоматически.
n8n
n8n — платформа автоматизации, о которой я уже писал — n8n AI агент без кода. Там есть нода MCP Client. Подключение немного другое — n8n общается с сервером через SSE (Server-Sent Events), а не stdio.
Для поддержки SSE нужно запустить сервер с HTTP-транспортом:
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=8000)Затем в n8n указываешь URL сервера. Это уже для продвинутого сценария — если хочешь встроить MCP в автоматизированный пайплайн без десктопного приложения.
Отладка: MCP Inspector и типичные ошибки
Без этого раздела первые шаги — чистая рулетка. Пишешь код, всё выглядит правильно, а Claude молчит.
MCP Inspector
Официальный инструмент для тестирования серверов. Не нужно каждый раз запускать Claude Desktop.
npx @modelcontextprotocol/inspector python server.pyОткроется веб-интерфейс на http://localhost:5173. Там можно вызывать инструменты руками, смотреть запросы и ответы, проверять схему.
Запусти Inspector первым делом — убедись, что сервер вообще стартует и инструменты видны, прежде чем лезть в конфиг Claude Desktop.
Типичные ошибки
Сервер не появляется в Claude Desktop. Проверь: путь в конфиге абсолютный? Сделал перезапуск? Python доступен по тому пути, что указан?
Для диагностики смотри логи:
- macOS:
~/Library/Logs/Claude/mcp*.log - Windows:
%APPDATA%\Claude\logs\mcp*.log
Инструмент вызывается, но возвращает пустой результат.
FastMCP ожидает конкретные возвращаемые типы. Если функция вернула None — это ошибка. Всегда возвращай строку, число, список или словарь.
Сервер падает при запуске с ошибкой json decode error.
Что-то печатает в stdout при старте. Импорт библиотеки, отладочный print — всё, что попадает в stdout, ломает протокол. Убери все print() из кода сервера. Для логирования используй stderr:
import sys
print("Debug info", file=sys.stderr) # Это безопасноModuleNotFoundError при запуске из Claude Desktop.
Claude Desktop запускает сервер в своём окружении. Если FastMCP установлен в virtualenv, а в конфиге указан системный python — ничего не найдёт. Решение: указывай полный путь к python внутри venv.
Внимание
Никогда не используй print() для вывода в stdout внутри MCP-сервера. Stdout — это канал протокола. Любой лишний текст там сломает JSON-RPC и сервер перестанет работать. Только sys.stderr для логов.
Инструмент вызывается бесконечно.
Иногда Claude пытается вызвать инструмент снова и снова, если не получает понятного ответа. Убедись, что возвращаемые данные информативны. Пустая строка или {} — плохой ответ. Верни что-то осмысленное, даже если операция не дала результатов: "Файлы не найдены" лучше, чем [].
Включаем verbose логирование
В FastMCP можно включить детальное логирование:
import logging
logging.basicConfig(level=logging.DEBUG, stream=sys.stderr)
mcp = FastMCP("My Server")Все внутренние события FastMCP пойдут в stderr, и ты увидишь, что именно происходит при каждом запросе.
Реальный кейс: мой MCP сервер для генерации обложек
Ладно, теперь реальный случай — то, что я использую каждый день.
Для блога нейропоток нужны обложки к каждой статье. Раньше это был ручной процесс: открыть сайт, написать промпт, подождать, скачать. Теперь это делает Claude Code, пока я занимаюсь чем-то полезным.
Я написал MCP-сервер на TypeScript, который общается с API kie.ai (агрегатор AI-моделей для генерации изображений). Сервер называется kieai, вот упрощённая версия его ключевой части:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "kieai",
version: "3.0.0",
});
server.tool(
"generate_cover",
"Start image generation via kie.ai API. Returns taskId — use check_status to poll.",
{
prompt: z.string().describe("Image generation prompt in English"),
model: z.enum(["imagen4", "flux2-pro", "seedream"]).default("imagen4"),
aspectRatio: z.enum(["1:1", "16:9", "9:16"]).default("16:9"),
},
async ({ prompt, model, aspectRatio }) => {
const modelMap: Record<string, string> = {
"imagen4": "google/imagen4",
"flux2-pro": "flux2/pro-text-to-image",
"seedream": "seedream/seedream-v4-text-to-image",
};
const response = await apiRequest("/api/v1/jobs/createTask", {
method: "POST",
body: JSON.stringify({
model: modelMap[model],
input: { prompt, aspect_ratio: aspectRatio },
}),
});
const taskId = response?.data?.taskId;
return {
content: [{
type: "text",
text: `Task ID: ${taskId}\nNext: call check_status with taskId="${taskId}"`,
}],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);Обрати внимание на API сервера TypeScript SDK: server.tool(name, description, schema, handler). Схема — это Zod-объект (библиотека для описания и валидации типов в TypeScript) с описанием полей. Структурно похоже на FastMCP, только без декораторов (декоратор — это @что-то перед функцией, как @mcp.tool в Python).
Полный сервер сложнее — там ещё check_status (проверить статус асинхронной задачи) и download_image (скачать результат локально). Всё вместе даёт Claude Code полный цикл: запустить генерацию → подождать → скачать файл. Я просто пишу «сгенерируй обложку к статье про MCP», и через минуту файл лежит на диске.
Не «AI помогает» — а «AI делает».
Чем MCP отличается от обычного API?
Меня спрашивают об этом часто. Чем MCP отличается от просто REST API?
Главная разница — кто решает когда вызывать. С обычным API ты сам вызываешь нужный endpoint в нужный момент. С MCP это решение принимает модель: она видит задачу, выбирает инструмент и вызывает сама, без твоей команды.
Ещё: REST API нужно знать заранее — URL, параметры, документация. MCP-инструменты модель "видит" в начале сессии и сама понимает, что применимо к текущей задаче.
Результат: ты один раз описываешь инструмент — и он работает. Во всех сценариях, где модель решит, что он нужен. Не пишешь каждый раз "вызови вот этот API с вот этими параметрами".
Обычный API — это калькулятор. Ты нажимаешь кнопки сам, когда решил считать. MCP — это когда коллега сам достаёт калькулятор, когда видит что надо посчитать, и приносит тебе ответ.
Что дальше: публикация и реестр серверов
Написал полезный сервер? Можно поделиться.
Официальный реестр: github.com/modelcontextprotocol/servers — там репозитории с reference-серверами от Anthropic и официально поддерживаемые интеграции.
mcp.so — каталог сообщества с тысячами серверов. Там можно найти готовое и туда же опубликовать своё.
Smithery (smithery.ai) — ещё один каталог, с возможностью деплоить серверы как managed-сервис.
Если хочешь опубликовать свой сервер как пакет:
# Python
pip install build twine
python -m build
twine upload dist/*
# TypeScript — публикуем на npm
npm publishПосле публикации на npm (реестр пакетов для JavaScript/TypeScript) или PyPI (аналог для Python) другие пользователи смогут установить сервер одной командой.
Частые вопросы
Что такое MCP сервер простыми словами? MCP сервер — это отдельная программа, которая даёт AI-модели доступ к инструментам и данным. Ты описываешь функции (что они делают, какие принимают параметры), и модель начинает вызывать их самостоятельно, когда решает, что они нужны для задачи. Не ты управляешь вызовами — AI сам решает когда и что использовать.
Как написать MCP сервер на Python?
Установи FastMCP (pip install fastmcp), создай объект FastMCP, добавь функции с декоратором @mcp.tool и понятными docstring, вызови mcp.run() в конце. Минимальный рабочий сервер — 10-15 строк кода. Всё описано в разделе выше с готовыми примерами.
Как проверить, что MCP сервер работает?
Используй MCP Inspector: npx @modelcontextprotocol/inspector python server.py. Откроется веб-интерфейс, где можно вручную вызывать инструменты и видеть ответы. Не нужно подключать Claude Desktop — это первый шаг для диагностики.
Можно ли использовать MCP без навыков программирования? Для написания своего сервера — нужен базовый Python или TypeScript. Но использовать готовые MCP-серверы (а их тысячи) можно без кода: просто добавить конфиг в Claude Desktop. Если хочешь автоматизацию без кода совсем — n8n AI агент без кода — там уже есть MCP Client нода.
Начни с малого: одна функция, которую ты сам делаешь руками каждый день. Поиск по файлам, запрос к внутреннему API, парсинг какого-нибудь сайта. Один инструмент, двадцать строк кода. Дальше само разрастётся.
Если хочешь прокачать Claude Code дальше — читай про кастомные навыки и агентов Claude Code или про реальный рабочий пайплайн. А если нужно встроить MCP в автоматизацию без кода — n8n уже поддерживает MCP Client.
MCP — это не магия. Просто процесс, который читает JSON и пишет JSON. Двадцать строк кода — и Claude сам генерирует обложки, сам проверяет статус задачи, сам скачивает файл. Я всё ещё немного удивляюсь каждый раз, когда это работает.



