Головна / Статті

MCP для початківців: створення сервера та клієнта, а потім їх використання в корпоративних проектах.

MCP для початківців: проблема N x M, основні ідеї у простих словах, сервер та клієнт допомоги на TypeScript у Node.js, а також перехід до безпечних розгортань через HTTP.

Видавець: REACTAPP.TOP14 хв читання3185 слів

Джерела: MCP Tools Inside Enterprise Applications: A Beginner-Friendly Deep Dive

Кожен, хто нещодавно працював з AI-агентами, стикався з MCP — Model Context Protocol. Він використовується в Claude Desktop, у плагінах IDE, у внутрішніх корпоративних проектах „AI-порталів“ та все частіше у комерційному програмному забезпеченні.

Це посібник для початківців, який не передбачає попереднього досвіду роботи з MCP. Він починається з проблеми, яку вирішує протокол, розглядає його архітектуру, створює справжній сервер та клієнт на TypeScript у середовищі Node.js, а потім розглядає аспекти безпеки, управління та можливості моніторингу, які використовують компанії у своїх рішеннях. Діаграми представлені у вигляді звичайного тексту ASCII, тож їх можна без проблем вставити до нотаток чи документації до коду.

1. Проблема, яку вирішує MCP

До появи MCP, щоб змусити агента виконати якусь конкретну дію, наприклад прочитати запис у CRM, відкрити заявку в ITSM чи здійснити запит до сховища даних, було лише одне рішення: написати спеціальний, одноразовий код інтеграції для цього агента та цієї системи.

Це ще можна терпіти, коли є кілька агентів (бот для підтримки, асистент з програмування, внутрішній помічник з інформацією) та кілька систем (Salesforce, Jira, база даних HR, склад). У результаті утворюється купа вручну створених з’єднувачів.

BEFORE MCP — the N x M integration problem

  ┌───────────┐        ┌─────────────┐
  │  Agent A  │───────▶│  CRM API    │  (custom connector #1)
  └───────────┘        └─────────────┘
  ┌───────────┐        ┌─────────────┐
  │  Agent A  │───────▶│  Ticketing  │  (custom connector #2)
  └───────────┘        └─────────────┘
  ┌───────────┐        ┌─────────────┐
  │  Agent B  │───────▶│  CRM API    │  (custom connector #3 -
  └───────────┘        └─────────────┘   yes, AGAIN, for a different agent)
  ┌───────────┐        ┌─────────────┐
  │  Agent B  │───────▶│  Data       │  (custom connector #4)
  └───────────┘        │  Warehouse  │
                       └─────────────┘
  N agents x M systems = N x M custom, non-reusable integrations.
  Every new agent re-implements auth, retries, schemas, error handling.

USB вирішив аналогічну проблему з апаратними периферійними пристроями, а ODBC та JDBC — із базами даних: занадто багато спеціалізованих конекторів та відсутній спільний стандарт. MCP є відповіддю Anthropic, опублікованою як відкритий протокол, а не лише функція Claude, тож будь-яке ШІ-застосування та будь-який бекенд можуть використовувати спільну мову.

AFTER MCP — one protocol, many servers, many clients

  ┌───────────┐                          ┌───────────────────┐
  │  Agent A  │──┐                   ┌─▶│  MCP Server: CRM   │
  └───────────┘  │    ┌───────────┐  │   └───────────────────┘
                 ├───▶│    MCP   │───┤  ┌───────────────────┐
  ┌───────────┐  │    │  (shared  │  ├─▶│ MCP Server: Ticket │
  │  Agent B  │──┘    │  protocol)│  │   └───────────────────┘
  └───────────┘       └───────────┘  │  ┌──────────────────┐
                                     └─▶│ MCP Server: DW   │
                                        └──────────────────┘
  Any MCP-compatible agent can now talk to any MCP server.
  Build the connector once, reuse it everywhere.

Суть можна описати одним реченням. MCP надає AI-додаткам єдиний спосіб знаходити інструменти, викликати їх, читати дані та використовувати шаблони запитів, тож інтеграцію потрібно писати лише один раз, і будь-який відповідний клієнт може її повторно використовувати.

2. Основні концепції простою мовою

Чотири терміни використовуються досить неформально, тому їх уточнюють ще до написання коду.

  • Хост. AI-заснований додаток, з яким працює кінцевий користувач, наприклад внутрішній чат-асистент, плагін IDE чи помічник з підтримкою, і саме він містить LLM.
  • Klient MCP. Компонент усередині хоста, який керує єдиним з’єднанням із окремим сервером MCP. Хост, який взаємодіє з трьома бекендами, зазвичай має три клієнти — по одному на кожен сервер.
  • Сервер MCP. Легка програма, яка розкриває можливості певної системи (інструменти, дані, запити) відповідно до специфікації MCP. Як розробник корпоративних рішень, зазвичай саме її ви створюєте.
  • Транспорт. Канал, через який взаємодіють клієнт та сервер. Існують два основні варіанти: stdio, у якому клієнт запускає сервер як локальний дочірній процес та обмінюється повідомленнями через стандартний вхід та вихід (зручний для локальної розробки та десктопних додатків), та Streamable HTTP, у якому сервер знаходиться віддалено за протоколом HTTPS (зазвичай використовується для спільних, централізовано керованих серверів).
  • У своїй основі всі повідомлення використовують JSON-RPC 2.0 — простий та добре усталений формат для запитів, відповідей та сповіщень. SDK генерує його за вас, але розуміння цього формату пояснює, чому інструменти для налагодження MCP схожі на інструменти API, які ви вже використовуєте: вони містять ідентифікатор запиту, назву методу, параметри та результат або помилку.

    Що може запропонувати сервер: три примітиви

    • Інструменти — це функції, які можна викликати, щоб дозволити моделі діяти або отримувати обчислені результати; сама модель вирішує, коли їх викликати, залежно від запиту користувача. Приклади: create_support_ticket, get_employee_details.
    • Ресурси — це лише для читання дані, які хост може додати як контекст, наприклад файли чи записи. Додаток або користувач вирішує, що додати. Приклади: стаття з бази знань, PDF-документ з правилами, файл журналу.
  • Інструкції — це повторно використовувані, параметризовані шаблони команд, які вибирає користувач, подібно до команд з слешем. Приклад: „Узагальніть цей інцидент для аналізу після його виникнення“.
  • У більшості корпоративних інтеграцій інструменти виконують близько 90% важливої роботи, тому їх створюють першими.

    3. Архітектура: три шари разом

    ┌─────────────────────────── HOST APPLICATION ───────────────────────────┐
    │   e.g. an internal AI assistant, IDE plugin, support copilot           │
    │                                                                        │
    │   ┌───────────────┐        ┌───────────────┐       ┌───────────────┐   │
    │   │  MCP Client 1 │        │  MCP Client 2 │       │  MCP Client 3 │   │
    │   └───────┬───────┘        └───────┬───────┘       └───────┬───────┘   │
    └───────────┼────────────────────────┼───────────────────────┼───────────┘
                │ JSON-RPC over          │ JSON-RPC over         │ JSON-RPC over
                │ stdio / HTTPS          │ stdio / HTTPS         │ stdio / HTTPS
                ▼                        ▼                       ▼
       ┌──────────────────┐     ┌──────────────────┐     ┌─────────────────┐
       │   MCP Server     │     │   MCP Server     │     │   MCP Server    │
       │  wraps HR system │     │  wraps Ticketing │     │  wraps Data     │
       │  (tools: lookup, │     │  (tools: create, │     │  Warehouse      │
       │   update)        │     │   status, close) │     │  (tools: query) │
       └──────────────────┘     └──────────────────┘     └─────────────────┘
    

    Кожен сервер MCP має бути невеликим та спеціалізованим, з окремим сервером для кожної системи бекенду, без єдиного величезного сервера, який би охоплював усе. Цей підхід запозичений з мікросервісів: невеликий радіус впливу, незалежне розгортання, незалежне масштабування та відповідальність команди, яка керує відповідним бекендом.

    4. Створення першого сервера MCP (Node.js та TypeScript)

    Прикладом є сервер MCP IT Helpdesk із двома інструментами: один для створення заявки на підтримку, інший — для перевірки статусу заявки. Він відображає дуже поширену корпоративну практику — інтеграцію внутрішнього API для обробки заявок або ITSM.

    4.1 Налаштування проекту

    mkdir helpdesk-mcp-server && cd helpdesk-mcp-server
    npm init -y
    npm install @modelcontextprotocol/sdk zod
    npm install -D typescript tsx @types/node
    npx tsc --init
    

    @modelcontextprotocol/sdk — це офіційний пакет. Він керує передачею даних у форматі JSON-RPC, транспортними механізмами та примітивними елементами, тому до сирого формату JSON-RPC ніколи не доводиться втручатися вручну. zod — це бібліотека для перевірки схем. Вхідні дані інструментів MCP перевіряються за схемою ще до того, як запуститься код обробника, що має велике значення в корпоративних середовищах, адже некоректні чи зловмисні запити блокуються на початковому етапі.

    4.2 Код сервера

    // src/server.ts
    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
    import { z } from "zod";
    
    // --- A stand-in for a real internal ticketing API client ---
    // In a real enterprise server this would call your ITSM system
    // (ServiceNow, Jira Service Management, Zendesk, an internal API, etc.)
    const ticketStore = new Map<string, { status: string; subject: string }>();
    let nextId = 1000;
    // 1. Create the server instance.
    //    "name" and "version" identify this server to any client that connects.
    const server = new McpServer({
      name: "helpdesk-mcp-server",
      version: "1.0.0",
    });
    // 2. Register a tool: create_support_ticket
    server.registerTool(
      "create_support_ticket",
      {
        title: "Create Support Ticket",
        description:
          "Creates a new IT helpdesk ticket for the requesting employee.",
        inputSchema: {
          subject: z.string().describe("Short summary of the issue"),
          priority: z.enum(["low", "medium", "high", "urgent"]),
          employeeId: z.string().describe("Requesting employee's ID"),
        },
        outputSchema: {
          ticketId: z.string(),
          status: z.string(),
        },
      },
      async ({ subject, priority, employeeId }) => {
        const ticketId = `TCK-${nextId++}`;
        ticketStore.set(ticketId, { status: "open", subject });
        const output = { ticketId, status: "open" };
        // MCP tool results return a "content" array (what a human/LLM reads)
        // and, optionally, "structuredContent" (typed data other code can use).
        return {
          content: [
            {
              type: "text",
              text: `Created ticket ${ticketId} (priority: ${priority}) for employee ${employeeId}.`,
            },
          ],
          structuredContent: output,
        };
      }
    );
    // 3. Register a second tool: get_ticket_status
    server.registerTool(
      "get_ticket_status",
      {
        title: "Get Ticket Status",
        description: "Looks up the current status of an existing support ticket.",
        inputSchema: {
          ticketId: z.string(),
        },
        outputSchema: {
          status: z.string(),
        },
      },
      async ({ ticketId }) => {
        const ticket = ticketStore.get(ticketId);
        if (!ticket) {
          // Returning isError lets the model know the call failed
          // WITHOUT crashing the whole conversation.
          return {
            content: [{ type: "text", text: `No ticket found with ID ${ticketId}.` }],
            isError: true,
          };
        }
        return {
          content: [{ type: "text", text: `Ticket ${ticketId} is currently "${ticket.status}".` }],
          structuredContent: { status: ticket.status },
        };
      }
    );
    // 4. Wire the server to a transport and start listening.
    //    stdio is perfect for local development and desktop-hosted tools.
    const transport = new StdioServerTransport();
    await server.connect(transport);
    

    4.3 Що робить кожна частина

    • Екземпляр McpServer. Цей об’єкт є серцем сервера. Він відстежує кожен зареєстрований інструмент, ресурс та команду, а також обробляє процедуру MCP handshake (домовленість щодо можливостей) під час підключення клієнта. Список інструментів ніколи не створюється вручну, оскільки SDK формує його на основі викликів registerTool.
    • inputSchema з Zod. Це більше, ніж просто документація. SDK використовує його для створення JSON Schema, яке вказує клієнту та LLM, які аргументи є дійсними, а також для перевірки кожного виклику після його отримання. Клієнт, який надсилає priority: „banana“, відхиляється ще до того, як досягнеться обробник. Для корпорацій така перевірка на основі схеми є справжнім засобом безпеки, а не просто зручністю.
  • outputSchema та structuredContent. Інструмент може повертати огляд, зрозумілий людині (content), а також об’єкт, зрозумілий машині (structuredContent). Текст відображається у записі чату, тоді як програма-запитувач може обробити структуровані дані для формування інтерфейсу користувача чи виконання подальшої логіки.
  • isError: true. MCP передбачає окреме поняття помилки на рівні інструменту, відмінне від помилки на рівні протоколу. Це означає, що операція зазнала невдачі, але розмова має продовжуватися, тож модель може прочитати помилку та вирішити, чи спробувати ще раз, запитати користувача чи скористатися іншим інструментом. Ви не хочете, щоб уся сесія агента зупинилася через відсутність певного ідентифікатора замовлення.
  • Транспорт (StdioServerTransport). Частина, яка переміщує байти. Для локальних інструментів, таких як IDE розробника чи десктопний асистент, стандартний варіант stdio є найпростішим, оскільки хост запускає сервер як дочірній процес. Усе, що працює централізовано в корпорації, використовуватиме транспорт на основі HTTP, про який йдеться у розділі 6.
  • 4.4 Запуск

    npx tsx src/server.ts
    

    Сам по собі сервер нічого не відображає, оскільки чекає на підключення клієнта через stdin та stdout. Далі йде клієнт.

    5. Клієнт у межах корпоративного додатку

    Початківці часто створюють сервер, а потім цікавляться, що насправді з ним взаємодіє. Ось мінімальний клієнт, уявлений як частина внутрішнього сервера Node.js, який працює з ШІ-асистентом компанії.

    // src/client.ts
    import { Client } from "@modelcontextprotocol/sdk/client/index.js";
    import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
    
    async function main() {
      // 1. Describe how to launch the server. Here we spawn it as a
      //    local subprocess - in production you'd more commonly point
      //    this at a remote HTTP-based server instead (see Section 6).
      const transport = new StdioClientTransport({
        command: "npx",
        args: ["tsx", "src/server.ts"],
      });
      // 2. Create a client and connect. This performs the MCP
      //    handshake and capability negotiation automatically.
      const client = new Client({ name: "internal-ai-assistant", version: "1.0.0" });
      await client.connect(transport);
      // 3. Discover what tools this server offers - this is the same
      //    mechanism an LLM uses to "learn" what it can do.
      const { tools } = await client.listTools();
      console.log("Available tools:", tools.map((t) => t.name));
      // 4. Call a tool, just like the LLM would.
      const result = await client.callTool({
        name: "create_support_ticket",
        arguments: {
          subject: "VPN keeps disconnecting",
          priority: "high",
          employeeId: "E-4821",
        },
      });
      console.log(result.content);
      await client.close();
    }
    main();
    

    Чому це має значення з концептуальної точки зору

    Центральною ідеєю є відкриття. Клієнт викликає listTools() та отримує каталог, придатний для читання машиною, з усім, що пропонує сервер, кожен інструмент має свою схему. Саме це робить MCP динамічним: хост, який працює за допомогою LLM, не потребує інструментів, вбудованих у його запит. Під час підключення він запитує сервер, що той може робити, і відповідь стає частиною інформації, яку модель отримує про те, що вона може робити під час цієї розмови.

    Саме на цьому механізмі ґрунтується корпоративне управління. Якщо певний інструмент не повинен бути доступним для певного користувача чи середовища, найкращим способом контролю є виключити його з того, що повертає listTools(), а не сподіватися, що модель сама вирішить його не використовувати.

    6. Перехід від локального прототипу до впровадження в компанії

    Налаштування stdio підходить для навчання та локальних інструментів розробників, але воно не відповідає способу, яким більшість компаній керує інфраструктурою: централізоване розміщення, автентифікація, облік діяльності та можливість одночасного доступу з боку багатьох додатків-хостів. Саме тут на допомогу приходять транспорт Streamable HTTP та API-шлюз.

    Ось той самий сервер допомоги, адаптований для роботи через HTTP та розташований позаду звичайного додатку Express:

    // src/httpServer.ts
    import express from "express";
    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
    
    const app = express();
    app.use(express.json());
    app.post("/mcp", async (req, res) => {
      // In a real enterprise deployment, authentication middleware would
      // run BEFORE this point - verifying a bearer token, checking scopes,
      // and attaching the caller's identity to the request.
      const server = buildHelpdeskServer(); // same registerTool calls as before
      const transport = new StreamableHTTPServerTransport({
        sessionIdGenerator: undefined, // stateless mode: simplest to scale horizontally
      });
      res.on("close", () => transport.close());
      await server.connect(transport);
      await transport.handleRequest(req, res, req.body);
    });
    app.listen(3000, () => console.log("MCP server listening on :3000"));
    

    Суттєва зміна стосується не стільки коду, скільки місця розташування відповідальності. Три аспекти, які були неявними у stdio, стають чіткими корпоративними проблемами через HTTP.

    1. Аутентифікація відбувається перед сервером, зазвичай у середовищі проміжного програмування або шлюзі, що дозволяє серверу вважати, що req вже містить перевірену ідентичність.
    2. Безстановість — це свідоме рішення. З sessionIdGenerator: undefined транспорт не зберігає сесію для кожного з’єднання, тож будь-яка інстанція за балансувальником навантаження може відповісти на будь-який запит, і його можна масштабувати, як будь-який безстановий API.
    3. Оперативність моніторингу більше не є необов’язковою. Кожне викликання інструменту є справжнім мережевим запитом, тому його слід логувати, відстежувати та обмежувати швидкість, як і будь-який внутрішній API-запит.

    7. Чек-лист критеріїв для корпоративних проектів

    Запуск сервера MCP — це лише п’ята частина роботи. Решта чотири п’ятих полягає у забезпеченні його безпеки для роботи з продуктивними системами, які містять справжні дані співробітників чи клієнтів, і саме цей етап виглядає так.

    • Аутентифікація та авторизація. Використовуйте OAuth2 або OIDC на шлюзі. Пов’язуйте ідентичність користувача чи облікового запису сервісу з конкретними дозволами, щоб не кожен інструмент був доступний для використання кожним користувачем.
    • Проектування інструментів із мінімальними привілеями. Віддавайте перевагу вузькоспрямованим інструментам, таким як get_ticket_status, замість широкоспрямованих, як run_sql_query. Інструмент, який виконує одну чітко визначену функцію, значно простіший у розумінні та аудиті.
  • Перевірка вхідних даних. Розглядайте перевірку схеми, як і у випадку з Zod вище, як справжню межу безпеки, а не просто зручність перевірки типів.
  • Участь людини під час руйнівних дій. Для будь-яких незворотних дій, таких як видалення запису, надсилання електронного листа за межі системи чи схвалення платежу, необхідно отримати пряме підтвердження від користувача перед тим, як інструмент виконає дію.
  • Журналування аудиту. Фіксуйте, хто викликав кожен інструмент, з якими аргументами та з яким результатом. Саме це дозволяє пізніше пояснити дію, виконану за допомогою ШІ, що є обов’язковою вимогою у регульованих сферах, таких як охорона здоров’я та фінанси.
  • Обмеження частоти та таймаути. Застосовуйте ті самі механізми контролю, що й до іншого внутрішнього трафіку API, щоб агент, застряглий у циклі, не міг засипати серверний бекенд запитами.
  • Ідемпотентність. Робіть інструменти з побічними ефектами безпечними для двократного виклику з однаковим вхідним даним, оскільки агенти іноді повторюють спроби.
  • Версіонування. Коли змінюються схеми інструментів, свідомо створюйте їх версії, щоб старіші системи не починали працювати некоректно.
  • 8. Де це використовується у реальних компаніях

    • ІТ та внутрішня підтримка. Приклад служби підтримки: створення та пошук заявок, скидання паролів, перевірка статусу системи.
    • Помічники у продажах та CRM. Отримання історії облікових записів та складання листів-підтримок на основі реальних даних CRM, а не припущень моделі.
    • Асистенти з обробки даних та аналітики. Набір інструментів для запитів з обмеженими правами доступу над складом замість необмеженого SQL.
    • Робочі процеси у сфері охорони здоров’я. Інструменти, які шукають вільні часи для прийому, перевіряють наявність страховки чи виявляють проблеми у координації догляду, завжди працюють за суворими правилами доступу та обліку операцій через високу конфіденційність даних. Тут принципи використання вузькоспеціалізованих інструментів, участі людини у процесі та повного обліку операцій, описані у розділі 7, часто є вимогою до дотримання стандартів.
    • Інструменти для розробників. Асистенти IDE, які звертаються до внутрішньої документації, запускають процеси CI чи перевіряють, хто знаходиться на чергуванні для певного сервісу.

    Що є спільним у всьому цьому: MCP не робить модель розумнішою. Змінюється те, що ШІ отримує контрольований, підлягаючий аудиту доступ до реальних систем та більше не змушений вигадувати правдоподібні відповіді.

    9. Підводні камені, з якими стикаються початківці

    • Один величезний сервер „роби все“. Розділення за допомогою системи бекенду чи домену. Це відповідає принципам хороших мікросервісів та зменшує масштаб поширення проблем.
    • Пропуск полів description. Модель сильно покладається на описи інструментів та параметрів, щоб вирішити, коли та як щось викликати. Нечіткі описи призводять до використання неправильного інструменту або правильного інструменту з неправильними аргументами.
    • Забування поля isError. Необроблена виняток, викинута обробником інструменту, може зруйнувати всю клієнтську з’єднаність, тоді як моделі слід повідомити про невдачу та спробувати щось інше.
    • Використання stdio у продакшені. Stdio підходить для локальної розробки та настільних серверів. Продакшен з кількома користувачами вимагає використання HTTP-транспорту за допомогою справжнього шлюзу.
  • Відсутня історія аудиту. Якщо ви не можете вказати, хто попросив ШІ діяти та що воно зробило, тримайте його подалі від продуктивних даних.
  • 10. Коротка версія

    Якщо прибрати всі модні терміни, основна ідея MCP залишається дуже простою: один спільний метод для додатків ШІ для пошуку та виклику інструментів, отримання даних та повторного використання шаблонів запитів, що означає, що робота з інтеграцією, виконана один раз, слугує всім клієнтам, які дотримуються стандартів. Для корпорацій саме ця стандартизація дозволяє перетворити агентів ШІ з ефектних демонстрацій на інструменти, які можна керувати, захищати та використовувати в продакшені поруч з усіма іншими системами.

    Для тих, хто тільки починає, розумний підхід полягає у наступному:

    1. Створіть невеликий сервер MCP локально за допомогою stdio, як у прикладі служби підтримки.
    2. Додайте клієнта, який викликає ці інструменти, щоб весь процес був видимим.
  • Розмістіть сервер за протоколом HTTP із автентифікацією або за допомогою шлюза спереду.
  • Лише після цього переходьте до журналізації подій, обмежень за частотою та людського схвалення для будь-яких деструктивних дій.
  • Почніть з малого та локального рівня, поступово впроваджуючи корпоративні вимоги. Спроба створити ідеальний сервер для продакшну вже з першого дня — це набагато ненадійніший шлях до мети.