Как создать MCP-сервер: пример на Python и TypeScriptMCP-сервер: что это простыми словами и как подключить

Как создать MCP-сервер: пример на Python и TypeScript

Коротко: MCP-сервер — это небольшая программа на официальном SDK, которая объявляет инструменты: имя, описание и параметры. На Python рабочий сервер занимает десяток строк с библиотекой FastMCP, на TypeScript — чуть больше. Начинают с локального сервера (stdio), проверяют его в MCP Inspector, подключают к Claude Code, а когда нужно отдать сервер другим людям — переводят на HTTP. Ниже — весь путь и грабли, на которые мы наступили, запуская 25 удалённых серверов в продакшене.

Сначала решите: нужен ли свой сервер

Для популярных сервисов — GitHub, Figma, Notion, браузер, поиск — готовые серверы уже есть, см. лучшие MCP-серверы. Свой пишут, когда модели нужен доступ к вашему: внутреннему API, базе, 1С, CRM, скриптам команды. Если вы только разбираетесь, что такое MCP, начните с обзора MCP-серверов.

Пример на Python (FastMCP)

Установите SDK — удобнее всего через uv:

uv init my-mcp && cd my-mcp
uv add "mcp[cli]"

Файл server.py:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("orders")

@mcp.tool()
def order_status(order_id: str) -> str:
    """Статус заказа по номеру. Используй, когда пользователь спрашивает, где его заказ."""
    # здесь — запрос к вашему API или базе
    return f"Заказ {order_id}: передан в доставку"

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

Вот и весь сервер. FastMCP сам превращает сигнатуру функции в схему параметров, а docstring — в описание инструмента. Именно описание модель читает, решая, звать ли инструмент, поэтому пишите в нём не «что делает функция», а когда её использовать.

Пример на TypeScript

npm init -y
npm install @modelcontextprotocol/sdk zod

Файл server.tspackage.json нужен "type": "module"):

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: 'orders', version: '1.0.0' });

server.registerTool(
  'order_status',
  {
    description:
      'Статус заказа по номеру. Используй, когда пользователь спрашивает, где его заказ.',
    inputSchema: { orderId: z.string().describe('Номер заказа') },
  },
  async ({ orderId }) => ({
    content: [{ type: 'text', text: `Заказ ${orderId}: передан в доставку` }],
  }),
);

await server.connect(new StdioServerTransport());

В stdio-сервере нельзя писать в stdout

Локальный сервер общается с клиентом через стандартный вывод, и любой print() или console.log() ломает протокол — клиент получит мусор вместо JSON. Логи пишите в stderr (console.error, logging в Python по умолчанию пишет туда же).

Проверка в MCP Inspector

Прежде чем подключать сервер к клиенту, проверьте его в Inspector — официальном отладчике с веб-интерфейсом: там видно список инструментов, их схемы, и каждый можно вызвать руками.

# Python
uv run mcp dev server.py

# TypeScript и любой другой сервер
npx @modelcontextprotocol/inspector npx tsx server.ts

Подключение к Claude Code и другим клиентам

Локальный сервер подключается командой запуска. В Claude Code всё, что после --, — это команда:

claude mcp add orders -- uv run --directory /path/to/my-mcp server.py
claude mcp add orders-ts -- npx tsx /path/to/server.ts

В клиентах с mcp.json — Cursor, LM Studio и других — та же команда записывается полями command и args. Где лежит конфиг у каждого: Claude, Cursor, LM Studio, Codex, OpenCode.

Из локального в удалённый: HTTP

Локальный сервер работает только у того, кто его запустил. Чтобы подключаться по ссылке — из claude.ai, с другой машины, всей командой, — сервер переводят на транспорт Streamable HTTP. В FastMCP это одна строка:

mcp.run(transport="streamable-http")

В TypeScript вместо StdioServerTransport используется StreamableHTTPServerTransport внутри вашего HTTP-сервера (Express, Fastify). Вместе с HTTP появляются вопросы, которых у локального сервера не было:

ВопросЧто решить
Кто вызываетВход: OAuth 2.1, как описано в спецификации MCP, или ключ в заголовке Authorization. Claude.ai и Cursor умеют OAuth, LM Studio и CI удобнее с ключом — разумно поддержать оба
СостояниеСервер без сессий (stateless) проще масштабировать: каждый запрос самодостаточен
Долгие вызовыМинутные операции держат соединение открытым — нужна защита от таймаутов (см. ниже)
РасходыЕсли инструмент стоит денег, резервируйте стоимость до вызова внешнего API, а не после

Грабли из продакшена

Мы держим в работе 25 удалённых MCP-серверов — поиск, генерацию изображений, видео, музыки и речи. Вот чего нет в кратких инструкциях, но что стоило нам инцидентов.

1. Имя инструмента длиннее, чем кажется

Многие клиенты и модели склеивают имя сервера и имя инструмента — например, seedream-image-generation_seedream-multi-image-to-batch-image. OpenAI-совместимые API проверяют такое имя по правилу ^[a-zA-Z0-9_-]{1,64}$ и при нарушении отклоняют весь запрос, а не один инструмент. Держите имена короткими, латиницей, без точек и пробелов, и проверяйте длину вместе с префиксом сервера.

2. Молчание дольше 5 минут обрывает вызов

Генерация видео не отправляет ничего до готового результата. У популярных HTTP-клиентов на Node.js таймауты заголовков и тела по умолчанию — 300 секунд, и ровно через пять минут клиент обрывает соединение, хотя сервер продолжает работу, сохраняет файл и списывает оплату. Лечится это комментариями-keepalive в SSE-потоке (строка вида : keepalive раз в 20 секунд): парсер событий их отбрасывает, но таймеры соединения сбрасываются.

3. Без правильного 401 клиент не найдёт OAuth

Чтобы Claude или Cursor сами предложили вход, сервер на запрос без токена должен ответить 401 с заголовком WWW-Authenticate: Bearer resource_metadata="…", указывающим на /.well-known/oauth-protected-resource, а тот — на сервер авторизации с динамической регистрацией клиентов и PKCE. Пропустите заголовок — и клиент просто покажет «ошибка подключения».

4. Описание — это промпт

Модель выбирает инструмент только по описанию. Размытое «работает с заказами» приводит к лишним вызовам или к тому, что инструмент не вызывают вовсе. Пишите: когда вызывать, когда не вызывать, что вернётся и что делать при ошибке. И возвращайте ошибки текстом с подсказкой — модель прочитает и исправит параметры сама.

Если свой сервер нужен ради поиска или генерации

Для этих задач писать сервер с нуля не обязательно: готовые удалённые серверы подключаются ссылкой — см. MCP для поиска и генерацию изображений и видео. А о том, как собрать из серверов агента, — в главе ИИ-агенты и MCP.

Частые вопросы

Как создать свой MCP-сервер?
Возьмите официальный SDK — Python (FastMCP) или TypeScript — и объявите инструменты: имя, описание и параметры. На Python достаточно функции с декоратором @mcp.tool() и вызова mcp.run(). Проверьте сервер в MCP Inspector (mcp dev server.py) и подключите к клиенту, например claude mcp add имя -- uv run server.py.
На каком языке писать MCP-сервер?
Официальные SDK есть для Python, TypeScript, Java, Kotlin, C#, Go и других языков. Для быстрого старта удобнее Python с FastMCP; если сервер будет частью Node.js-бэкенда — TypeScript. Протокол один, клиенту язык сервера не важен.
Чем локальный MCP-сервер отличается от удалённого?
Локальный запускается клиентом как процесс на вашем компьютере и общается через stdio — ему доступны ваши файлы, но работает он только у вас. Удалённый работает по HTTP (Streamable HTTP), подключается ссылкой с любого устройства и из веб-версии Claude, но требует хостинга и авторизации.
Как отладить MCP-сервер?
Через MCP Inspector: npx @modelcontextprotocol/inspector с командой запуска сервера, для Python — mcp dev server.py. Inspector показывает инструменты и схемы и позволяет вызвать каждый вручную. В локальном сервере логи пишите в stderr: stdout занят протоколом.
Как сделать авторизацию в удалённом MCP-сервере?
Спецификация MCP описывает OAuth 2.1: сервер на запрос без токена отвечает 401 с заголовком WWW-Authenticate, указывающим на метаданные защищённого ресурса, а сервер авторизации поддерживает PKCE и динамическую регистрацию клиентов. Для клиентов без OAuth удобно дополнительно принимать ключ в заголовке Authorization: Bearer.

Попробуйте сами

Соберите свой MCP-сервер за минуту

Отметьте нужные инструменты — ссылки коннекторов и mcp.json соберутся сами. Подключается к Claude, Cursor, LM Studio, Codex и OpenCode.

Собрать MCP-сервер
Оцените свой опыт