# Свой MCP-сервер на TypeScript

Тот же сервер каталога на TypeScript SDK v2: stdio и Streamable HTTP

> Страница сайта MCP Docs — руководство (https://mcpdocs.ru/svoy-server/typescript/). Тип: Руководство. Код проверен: Node.js 22, @modelcontextprotocol/server 2.3.1, @modelcontextprotocol/client 2.3.1 — по stdio и по HTTP. Ревизия спецификации 2026-07-28, сверено 13.10.2026.

TypeScript SDK второй версии разделён на пакеты: `@modelcontextprotocol/server` для серверов и `@modelcontextprotocol/client` для клиентов. Прежний пакет `@modelcontextprotocol/sdk` — это первая версия, её поддерживают в отдельной ветке.

_TypeScript SDK v2: фабрика createServer регистрирует возможности; serveStdio обслуживает локальный запуск, createMcpHandler — HTTP._

## Проект

Нужен Node.js 20 или новее. SDK поставляется только как ES-модули.

_Создание проекта_

```bash
mkdir catalog && cd catalog
npm init -y
npm pkg set type=module
npm install @modelcontextprotocol/server @modelcontextprotocol/node zod tsx
mkdir src
```

`tsx` запускает TypeScript без сборки. Схемы параметров пишутся на Zod 4 (`zod/v4`); подходят и другие библиотеки со стандартом Standard Schema.

## Код сервера

_src/server.ts_

```typescript
// MCP-сервер «Каталог» на TypeScript SDK v2: инструмент, ресурс, шаблон запроса
import { createServer as createHttpServer } from 'node:http';
import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import { localhostHostValidation, localhostOriginValidation, toNodeHandler } from '@modelcontextprotocol/node';
import * as z from 'zod/v4';

const PRODUCTS = [
    { sku: 'A-100', name: 'Стол обеденный', price: 18900, stock: 4 },
    { sku: 'A-200', name: 'Стул мягкий', price: 5400, stock: 0 },
    { sku: 'B-310', name: 'Полка настенная', price: 2300, stock: 17 }
];

function createServer(): McpServer {
    const server = new McpServer({ name: 'catalog', version: '1.0.0' });

    server.registerTool(
        'find_products',
        {
            description: 'Найти товары по части названия. price — цена в рублях, stock — остаток.',
            inputSchema: z.object({
                query: z.string().min(1),
                in_stock_only: z.boolean().default(false)
            })
        },
        async ({ query, in_stock_only }) => {
            const q = query.toLowerCase();
            const found = PRODUCTS.filter(p => p.name.toLowerCase().includes(q) && (p.stock > 0 || !in_stock_only));
            return {
                content: [{ type: 'text', text: JSON.stringify(found) }],
                structuredContent: { result: found }
            };
        }
    );

    server.registerResource('categories', 'catalog://categories',
        { title: 'Разделы каталога', mimeType: 'text/plain' },
        async uri => ({ contents: [{ uri: uri.href, text: 'Столы\nСтулья\nПолки' }] })
    );

    server.registerPrompt(
        'compare',
        {
            description: 'Сравнить два товара по артикулу',
            argsSchema: z.object({ first_sku: z.string(), second_sku: z.string() })
        },
        ({ first_sku, second_sku }) => ({
            messages: [{
                role: 'user',
                content: { type: 'text', text: `Сравни товары ${first_sku} и ${second_sku}: цена, наличие, назначение.` }
            }]
        })
    );

    return server;
}

if (process.argv.includes('--http')) {
    // Удалённый режим: Streamable HTTP на http://127.0.0.1:3000/mcp
    const handler = toNodeHandler(createMcpHandler(createServer));
    const validateHost = localhostHostValidation();
    const validateOrigin = localhostOriginValidation();
    createHttpServer((req, res) => {
        if (!validateHost(req, res) || !validateOrigin(req, res)) return;
        void handler(req, res);
    }).listen(3000, '127.0.0.1');
    console.error('catalog: http://127.0.0.1:3000/mcp');
} else {
    // Локальный режим: stdio. В stdout писать нельзя — только в stderr
    void serveStdio(createServer);
}
```

- `createServer` — фабрика: по HTTP она вызывается на каждый запрос, поэтому сервер ничего не хранит между запросами. Пулы соединений и кэши создавайте вне фабрики.
- `registerTool` принимает имя, описание со схемой Zod и обработчик. SDK проверит аргументы до вызова обработчика и вернёт модели понятную ошибку.
- Для HTTP перед обработчиком стоят проверки `Host` и `Origin`: на локальном адресе они защищают от DNS rebinding — когда вредная страница в браузере обращается к вашему серверу на `127.0.0.1`.

> По stdio стандартный вывод — канал протокола. Один `console.log` ломает обмен: пишите журнал через `console.error`.

## Запуск и проверка

_Запуск_

```bash
npx tsx src/server.ts            # stdio
npx tsx src/server.ts --http     # http://127.0.0.1:3000/mcp
npx @modelcontextprotocol/inspector npx tsx src/server.ts
```

Клиент для проверки ставится отдельным пакетом `@modelcontextprotocol/client`:

_src/client.ts_

```typescript
// Клиент: запускает сервер как дочерний процесс и вызывает инструмент
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';

const client = new Client({ name: 'catalog-check', version: '1.0.0' });
await client.connect(new StdioClientTransport({ command: 'npx', args: ['tsx', 'src/server.ts'] }));

const { tools } = await client.listTools();
console.log(tools.map(t => t.name));

const result = await client.callTool({ name: 'find_products', arguments: { query: 'стол' } });
console.log(result.structuredContent);

await client.close();
```

_Вывод_

```text
[ 'find_products' ]
{
  result: [ { sku: 'A-100', name: 'Стол обеденный', price: 18900, stock: 4 } ]
}
```

## Подключение к клиенту

Команда запуска для клиентов — `npx tsx /абсолютный/путь/src/server.ts`; для рабочего варианта соберите проект в JavaScript и запускайте `node dist/server.js`. Форматы настроек клиентов — в разделе [«Подключение»](https://mcpdocs.ru/podklyuchenie/).

Авторизация по HTTP в TypeScript SDK передаётся в фабрику через `authInfo`: токен проверяет промежуточный обработчик до SDK. Подход и требования одинаковы для обоих языков — в руководстве [«Авторизация»](https://mcpdocs.ru/svoy-server/avtorizatsiya/).

> Готовые серверы — в [каталоге MCP-серверов](https://mcpprotocol.ru/katalog/) справочника. Свой удалённый сервер можно проверить на соответствие ревизии 2026-07-28 [бесплатной проверкой](https://mcpprotocol.ru/mcp-check/).
