AI Agent Integrations PRO: MCPПервый MCP-сервер: scaffold, инициализация и возможностиScaffold MCP-сервера: структура проекта и зависимости

Scaffold MCP-сервера: структура проекта и зависимости

Уроки курсаScaffold MCP-сервера: структура проекта и зависимости

Точка входа: минимальный код инициализации сервера

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


import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

const server = new Server(
  { name: 'my-server', version: '0.1.0' },
  { capabilities: {} }
);

const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Server running on stdio');

Разберём, что здесь происходит. new Server(...) — это точка сборки сервера. Первый аргумент — идентификационные данные: name и version будут видны клиенту при установке соединения. Второй аргумент — объект capabilities, в котором позже вы объявите, что именно умеет сервер: инструменты, ресурсы, подсказки. Пока он пустой — и это нормально, сервер всё равно запустится. StdioServerTransport — адаптер, который говорит серверу читать входящие сообщения из stdin и писать ответы в stdout. Это стандартный транспорт для локальных серверов, которые запускаются как дочерние процессы хоста. await server.connect(transport) — вот здесь сервер реально «стартует». Без этого вызова экземпляр Server существует в памяти, но не слушает никакого ввода. await обязателен: без него Node.js не будет ждать завершения асинхронной инициализации и процесс завершится немедленно. console.error(...) — вывод идёт именно в stderr, а не в stdout, потому что stdout занят протоколом MCP. Если писать диагностику в stdout, сообщения перемешаются с JSON-RPC трафиком и сломают парсинг на стороне клиента. ---

Python


from mcp.server import Server
from mcp.server.stdio import stdio_server

app = Server('my-server')

if __name__ == '__main__':
    import asyncio

    async def main():
        async with stdio_server() as (read, write):
            await app.run(read, write, app.create_initialization_options())

    asyncio.run(main())

Логика та же, но Python-версия SDK использует context manager вместо явного объекта транспорта. Server('my-server') — создаёт экземпляр сервера с именем. Версию и capabilities можно уточнить позже через create_initialization_options(). stdio_server() — асинхронный context manager, который открывает stdin/stdout как пару потоков (read, write) и корректно закрывает их при выходе. Вся логика работы сервера происходит внутри блока async with. app.run(read, write, app.create_initialization_options()) — запускает event loop сервера: читает входящие JSON-RPC сообщения из read, обрабатывает их и пишет ответы в write. create_initialization_options() формирует стандартный набор параметров инициализации, включая capabilities. Обёртка if __name__ == '__main__': здесь не просто привычка — она важна, если позже вы захотите импортировать app из другого модуля, не запуская сервер автоматически. Оба варианта после запуска (npm run start или python main.py) не выводят ничего в консоль и просто ждут входящих сообщений. Это ожидаемое поведение: сервер готов к работе и слушает транспорт.