JavaRush /Курси /ChatGPT Apps /Інтеграція ChatGPT App у наявний продукт і міграції SDK/M...

Інтеграція ChatGPT App у наявний продукт і міграції SDK/MCP

ChatGPT Apps
Рівень 20 , Лекція 2
Відкрита

1. Навіщо взагалі говорити про інтеграцію та міграції

Досі ми здебільшого проєктували API та інструменти так, як нам зручно. У реальному житті майже завжди навпаки: у вас уже є:

  • моноліт або набір мікросервісів;
  • REST/GraphQL API;
  • бізнес‑логіка, що роками працює в продакшні.

І тут раптом з’являється завдання: «Підʼєднайте наш продукт до ChatGPT через Apps SDK і MCP».

Переписати все під «ідеальний MCP‑сервер» — не варіант. Потрібно акуратно «вдягнути» поверх наявної системи тонкий шар, який перекладе мову вашого бекенду на мову ChatGPT: інструменти, ресурси та схеми.

Друга проблема — продукт живий. Схеми й API змінюються. У звичайному фронтенді ви принаймні одразу отримаєте помилку TypeScript, щойно зміните поле. У світі LLM‑Apps усе підступніше: модель і далі впевнено надсилатиме старий формат, інструмент падатиме. А замість красивого «падіння» збірки ви отримаєте:

  • помилки під час виконання на MCP‑сервері;
  • галюцинації на кшталт «ну я приблизно здогадався, що ви хотіли від цього поля»;
  • неприємні інциденти, пов’язані з якістю.

Тому в цій лекції ми дивимося на шар MCP + Apps як на:

  • адаптер до наявного бекенду;
  • контракт, який потрібно підтримувати роками;
  • об’єкт міграцій: версій, анотацій, scopes і SDK.

2. Архітектура інтеграції: MCP як адаптер над наявним бекендом

Базова картина

Нагадаємо стек, але тепер — уже крізь призму продакшну:

flowchart LR
  U[Користувач у ChatGPT] --> G[Модель ChatGPT]
  G -->|викликає App| W["Віджет (Apps SDK, Next.js)"]
  G -->|tools.call| MCP[MCP‑сервер / Gateway]
  MCP --> S1["Gift Service (ваш наявний сервіс)"]
  MCP --> S2["Commerce Service (замовлення, ACP)"]

ChatGPT спілкується з вашим світом не напряму, а через MCP‑протокол: список tools/resources, виклики tools/call і стримінг подій.

MCP‑сервер у цій схемі — це рівно той самий адаптер. Він знає і про ChatGPT (JSON‑RPC, інструменти), і про ваші сервіси (REST/DB/черги) та перекладає одне в інше.

MCP як Gateway/Adapter

Класична постановка: у вас уже є Gift Service з REST‑ендпойнтами:

// Приклад наявного REST API
GET  /api/gifts/recommendations?budget=100&occasion=birthday
POST /api/orders

Замість того щоб писати нову бізнес‑логіку, MCP‑шар просто загортає це в Tool:

// mcp/tools/recommendGifts.ts
import { z } from "zod";
import { server } from "./mcpServer"; // умовний інстанс SDK

const recommendGiftsInput = z.object({
  occasion: z.string(),
  budgetUsd: z.number().int().positive(),
});

server.registerTool({
  name: "recommend_gifts",
  description: "Підбирає ідеї подарунків у межах бюджету",
  inputSchema: recommendGiftsInput,
  async execute(args) {
    const { occasion, budgetUsd } = recommendGiftsInput.parse(args);
    const res = await fetch(
      `https://api.myapp.com/gifts/recommendations?budget=${budgetUsd}&occasion=${occasion}`,
    );
    return res.json(); // важливо: повертаємо JSON, зручний і для моделі, і для віджета
  },
});

Уся логіка добору подарунків лишається всередині вашого наявного сервісу. MCP‑шар — це «тонкий перекладач» з мови ChatGPT на мову ваших API.

Іноді MCP‑шар ще й маршрутизує запити до кількох бекенд‑сервісів. У такому разі він перетворюється на повноцінний MCP Gateway. Його роль ви розбиратимете глибше в модулі про продакшн і мережу.

Monolith-integrated MCP vs Sidecar MCP

Є два базові варіанти, куди цей MCP‑шар можна «під’єднати».

Якщо описати це словами, матимемо таку картину:

Варіант Опис Де живе MCP‑код
Monolith-integrated Усе в одному Next.js/Node‑сервісі В API‑роутах Next.js або Express
Sidecar MCP Окремий контейнер/сервіс, що спілкується з API Окремий Node/Go‑застосунок

У невеликих проєктах часто достатньо першого варіанта: Next.js‑застосунок, розгортання на Vercel, там само маршрут /mcp або /api/mcp, і MCP‑сервер живе поруч із рештою API.

Приклад (сильно спрощений):

// app/api/mcp/route.ts (Next.js 16)
import { NextRequest } from "next/server";
import { mcpHandler } from "@/mcp/server";

export async function POST(req: NextRequest) {
  const body = await req.json();
  const response = await mcpHandler.handle(body); // запит JSON-RPC
  return new Response(JSON.stringify(response), {
    headers: { "content-type": "application/json" },
  });
}

У зрілішій архітектурі, де є кілька доменних сервісів (Gift, Commerce, Analytics), MCP‑шар зручніше винести в окремий Gateway‑сервіс. Він прийматиме MCP‑трафік від ChatGPT і вже сам маршрутизуватиме виклики до різних бекендів за іменем інструмента.

Важливо пам’ятати: з точки зору ChatGPT і Apps SDK це все одно один MCP‑сервер. Де саме він працює — усередині моноліту чи як окремий мікросервіс — уже ваша архітектурна турбота.

З архітектурою MCP‑шару розібралися. Він може жити всередині моноліту або бути окремим Gateway. Далі постає питання: що саме цей шар приймає і що віддає? І тут на сцену виходять схеми та контракти.

3. Single Source of Truth: схеми, типи і контрактні тести

Якщо у вас є внутрішні DTO, зовнішні REST‑контракти і ще MCP‑схеми для інструментів, спокуса «намалювати схеми на око» величезна. Результат передбачуваний:

  • змінюєте поле в бекенді та забуваєте оновити schema інструмента;
  • модель продовжує надсилати старий формат;
  • отримуєте веселий «зоопарк» помилок під час виконання.

Нормальний шлях — зробити одну точку істини для структури даних і використовувати її всюди. У світі TypeScript це дуже зручно робити через Zod або подібні бібліотеки, які MCP SDK уміє конвертувати в JSON Schema.

Загальна Zod‑схема для GiftGenius

Припустімо, ваш Gift‑сервіс у нашому навчальному GiftGenius уже використовує Zod для валідації вхідних даних:

// domain/gifts.ts
import { z } from "zod";

export const giftRecommendationInputSchema = z.object({
  occasion: z.string().describe("Привід: birthday, wedding тощо."),
  budgetUsd: z.number().int().positive(),
  recipientProfile: z.string().describe("Короткий опис людини"),
});

export type GiftRecommendationInput = z.infer<
  typeof giftRecommendationInputSchema
>;

Ця ж схема використовується:

  • у REST‑ендпойнті (для перевірки тіла запиту);
  • в MCP‑інструменті (як inputSchema);
  • у тестах (як основа для фікстур).

Підключаємо схему до MCP‑інструмента

// mcp/tools/recommendGifts.ts
import { giftRecommendationInputSchema } from "@/domain/gifts";
import { server } from "../mcpServer";

server.registerTool({
  name: "recommend_gifts",
  description: "Підбір подарунків за профілем і бюджетом",
  inputSchema: giftRecommendationInputSchema,
  async execute(args) {
    const input = giftRecommendationInputSchema.parse(args);

    const res = await fetch("https://api.myapp.com/gifts/recommendations", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(input),
    });

    return res.json();
  },
});

SDK сам сконвертує Zod‑схему в JSON Schema, яку ChatGPT побачить у tools/list. Це розв’язує одразу дві проблеми:

  • типи аргументів інструмента і коду жорстко пов’язані;
  • у разі зміни схеми компілятор TypeScript змусить вас оновити й обробник.

Контрактні тести для MCP ↔ backend

Контрактні тести тут — не страшне слово, а кілька цілком приземлених перевірок.

Найпростіший unit/contract‑тест може виглядати так:

// tests/mcp/recommendGifts.contract.test.ts
import { giftRecommendationInputSchema } from "@/domain/gifts";

test("приклад запиту відповідає схемі інструмента", () => {
  const sample = {
    occasion: "birthday",
    budgetUsd: 150,
    recipientProfile: "колега, любить гаджети",
  };

  expect(() => giftRecommendationInputSchema.parse(sample)).not.toThrow();
});

Такий тест не гарантує, що «весь світ прекрасний». Але принаймні він ловить розсинхрон між очікуваннями бекенду й MCP‑шару, якщо ви змінили схему та забули оновити фікстури.

Далі цей самий підхід легко розширюється на:

  • замокані відповіді зовнішніх API (Stripe, CMS);
  • прогін MCP‑клієнта проти реального MCP‑сервера в тестовому середовищі.

4. Стратегії версіонування tools і resources

Схеми рано чи пізно змінюються. Головне — не робити це в дусі «я просто перейменую поле, що може піти не так». У LLM‑світі так легко зламати не лише збірку, а й поведінку моделі: старі промпти, збережені діалоги та golden‑кейси продовжать очікувати старого контракту.

Адитивні vs breaking‑зміни

Умовно зміни можна поділити на дві категорії.

Адитивні зміни — ви щось додаєте, але нікого не ламаєте:

  • нове необов’язкове поле у відповіді;
  • новий необов’язковий аргумент зі значенням за замовчуванням;
  • додаткові значення enum, до яких UI і модель можуть ставитися нейтрально.

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

Breaking‑зміни — ви ламаєте наявні очікування:

  • робите поле обов’язковим, хоча його раніше не було;
  • змінюєте тип (рядок → об’єкт);
  • змінюєте сенс аргументів (бюджет у USD → бюджет у локальній валюті, але при цьому не змінюєте назви полів).

У таких випадках єдиний безпечний шлях — заводити нову версію інструмента.

Патерн Tool_v2

Класичний патерн: у вас був recommend_gifts, і ви хочете суттєво змінити схему. Старий інструмент ви не чіпаєте. Натомість створюєте новий — recommend_gifts_v2.

// v1
const recommendGiftsInput_v1 = z.object({
  occasion: z.string(),
  budgetUsd: z.number().int().positive(),
});

// v2: підтримка валют і фільтрів доставки
const recommendGiftsInput_v2 = z.object({
  occasion: z.string(),
  maxPrice: z.number().int().positive(),
  currency: z.enum(["USD", "EUR", "GBP"]),
  deliverByDate: z.string().optional(); // ISO-рядок
});

server.registerTool({
  name: "recommend_gifts",
  description: "DEPRECATED: використовуйте recommend_gifts_v2",
  inputSchema: recommendGiftsInput_v1,
  async execute(args) { /* стара логіка */ },
});

server.registerTool({
  name: "recommend_gifts_v2",
  description:
    "Підбір подарунків за бюджетом, валютою і дедлайном доставки",
  inputSchema: recommendGiftsInput_v2,
  async execute(args) { /* нова логіка */ },
});

Модель і старі промпти/агенти продовжать використовувати recommend_gifts, доки ви їх не оновите. Нові сценарії ви вже пишете під recommend_gifts_v2.

Після періоду міграції:

  • golden‑кейси й агенти перемкнені на v2;
  • метрики показують, що v1 майже не викликається;

можна почати акуратно згортати v1 (наприклад, спершу сховати його зі списку інструментів у dev/staging, а потім — у проді).

Версіонування ресурсів

Tools — не єдине, що потребує версій. Якщо у вас є ресурси (resources) — наприклад, статичний каталог подарунків, — їх теж краще версіонувати.

Популярні варіанти:

  • версію «зашити» в ім’я ресурсу: gift_catalog.v1.json, gift_catalog.v2.json;
  • або передавати версію в URI/параметрі: /api/catalog?version=1.

Сенс той самий: не підміняти дані «під ногами» в уже запущених сценаріях, а давати їм явно зафіксовану версію каталогу.

Міграції без даунтайму

Типовий цикл міграції інструмента:

  1. Додаєте нову версію інструмента (_v2) паралельно до старої.
  2. Оновлюєте App/агентів/system‑prompt так, щоб вони використовували нову версію.
  3. Проганяєте golden‑кейси і LLM‑eval для обох варіантів та переконуєтесь, що для критичних сценаріїв якість не впала.
  4. Спостерігаєте метрики використання v1 і v2 (а також помилки).
  5. Після того як трафік на v1 близький до нуля, починаєте його деактивувати.

Такий підхід добре працює і для міграцій схем, і для оновлень SDK/протоколу, і для змін в Auth. Ми розібрали, як еволюціонують самі інструменти й ресурси — через v1/v2 та акуратні адитивні зміни. Друга велика частина контракту — автентифікація й авторизація: OAuth, scopes і .well-known. Вони теж живуть роками й потребують обережних міграцій.

5. Еволюція автентифікації: .well-known, scopes і наявний OAuth

Якщо ваш продукт уже живе у світі OAuth 2.1/OpenID Connect, інтеграція з ChatGPT через MCP — це не «ще один логін». Це новий клієнт, який має спілкуватися з вашим Authorization Server за спільними правилами.

MCP і .well-known/oauth-protected-resource

Про повний OAuth 2.1/OpenID Connect і налаштування Auth Server ми докладно говоримо в окремому модулі курсу (див. модуль про автентифікацію). Тут нас цікавить лише прикладний аспект: як MCP‑ресурс повідомляє ChatGPT, що він захищений OAuthʼом, і як запустити процес прив’язування облікового запису.

Стандартний патерн для захищених MCP‑ресурсів:

  • ваш MCP‑сервер експонує спеціальний ендпойнт /.well-known/oauth-protected-resource;
  • у відповіді він повідомляє, який це ресурс і якими AS (Authorization Server) він захищений;
  • у разі 401 на MCP‑виклику сервер повертає заголовок WWW-Authenticate з посиланням на цей .well-known, і ChatGPT сам запускає OAuth‑процес («Link account»).

Мінімальний приклад на Express:

// mcp-auth/.well-known.ts
import express from "express";
const app = express();

app.get("/.well-known/oauth-protected-resource", (_req, res) => {
  res.json({
    resource: "https://mcp.myapp.com",
    authorization_servers: [
      "https://auth.myapp.com/.well-known/openid-configuration",
    ],
  });
});

app.listen(3000);

І обробник 401 з підказкою для клієнта:

res
  .status(401)
  .set(
    "WWW-Authenticate",
    'Bearer resource_metadata="https://mcp.myapp.com/.well-known/oauth-protected-resource"',
  )
  .end();

ChatGPT, побачивши цей заголовок, розуміє, до якого AS іти і як запускати OAuth‑процес для вашого MCP‑ресурсу.

Scopes і міграції авторизації

Scopes — ще одне джерело міграцій. Ми вже докладно розбирали це в модулі про Auth, але в контексті інтеграції та міграцій важливі кілька моментів.

Уявіть, що GiftGenius спочатку вмів лише читати каталог (gifts.read), а згодом ви додали gifts.write для створення замовлень. Вам потрібно:

  • додати новий scope у конфігурацію клієнта (ChatGPT App);
  • оновити MCP‑сервер, щоб він вимагав цей scope лише для інструментів, які справді щось змінюють;
  • описати зміни в .well-known, якщо це потрібно.

З погляду UX користувач під час наступної спроби скористатися новим функціоналом може побачити запит «розширити права» для ChatGPT‑застосунку. Ви не хочете, щоб це траплялося посеред уже поточного діалогу — без попередження. Тому такі зміни потрібно:

  • анонсувати (release notes, документація);
  • протестувати на staging із тестовим AS;
  • поєднати з оновленням описів інструментів (destructiveHint тощо), щоб модель усвідомлено викликала «небезпечні» tools.

6. Метадані й анотації: hint‑шар поверх контракту

Auth‑шар відповідає на запитання, хто і що може робити через ваш App. Але навіть за коректних токенів і scopes важливо й те, як модель викликатиме ваші інструменти та як вона пояснюватиме дії користувачеві. Тут у справу вступає додатковий hint‑шар: метадані й анотації.

Контракт (schema) каже, що інструмент приймає і що повертає. Метадані й анотації допомагають моделі зрозуміти, як і коли його викликати. Це стає особливо важливим, коли ви еволюціонуєте App: додаєте нові destructive‑дії, змінюєте UI, вводите інтеграції із зовнішнім світом.

_meta["openai/widgetDescription"] і widgetCSP

В Apps SDK і MCP‑описах є спецполе _meta, куди OpenAI додає свої розширення протоколу. Наприклад:

  • _meta["openai/widgetDescription"] — короткий опис того, що показує ваш віджет; модель може використовувати його, щоб не «переказувати» UI і правильно анонсувати App;
  • _meta["openai/widgetCSP"] — декларація CSP‑доменів, які потрібні вашому віджету (для fetch/зображень/скриптів).

Коли ви змінюєте UI (наприклад, додаєте новий крок оформлення замовлення), корисно оновити widgetDescription, щоб модель і далі коректно пояснювала користувачеві, що відбувається.

Анотації інструментів (readOnlyHint, destructiveHint, openWorldHint)

Анотації — прості булеві прапорці, які помітно впливають на UX і безпеку:

  • readOnlyHint: true — інструмент нічого не змінює (читання). Модель може викликати його без зайвих підтверджень.
  • destructiveHint: true — інструмент може щось видалити/змінити. ChatGPT проситиме явне підтвердження.
  • openWorldHint: true — інструмент публікує дані назовні або може повернути «дуже багато всього», що вимагатиме підсумовування.

Приклад дескриптора інструмента з анотаціями:

server.registerTool({
  name: "delete_saved_gift",
  description: "Видаляє збережений подарунок користувача",
  inputSchema: z.object({ giftId: z.string() }),
  annotations: {
    readOnlyHint: false,
    destructiveHint: true,
    openWorldHint: false,
  },
  async execute({ giftId }) {
    // ...видаляємо подарунок
  },
});

Під час міграції, коли ви додаєте нові «небезпечні» інструменти, анотації — ваші друзі. Вони допомагають ChatGPT не виконувати їх потай і підштовхують до обережнішої поведінки.

Важливо розуміти: анотації — це не захист «по‑справжньому». Вони впливають лише на поведінку клієнта і моделі. Справжню безпеку, як і раніше, забезпечує ваш сервер (Auth, scopes, валідація).

7. Міграції SDK і MCP‑специфікацій

MCP і Apps SDK активно розвиваються: з’являються нові поля в capabilities, нові типи повідомлень, нові _meta/annotations. Документація чесно попереджає: «станом на 2025 рік» — і нам із цим жити.

Тому міграції версій SDK і специфікації — нормальна частина життя App, а не рідкісна подія «колись потім».

Типовий процес оновлення

Здоровий сценарій оновлення приблизно такий:

  1. Читаєте changelog нової версії Apps SDK/MCP SDK. Позначаєте всі потенційно breaking‑зміни.
  2. Оновлюєте залежності в dev/staging‑оточенні, не чіпаючи продакшн.
  3. Проганяєте MCP Inspector / Jam чи інший клієнт:
    • перевіряєте handshake;
    • tools/list / resources/list;
    • кілька тестових tools/call.
  4. Оновлюєте описи інструментів і _meta згідно з новими можливостями:
    • наприклад, додаєте нові annotations або widgetDescription.
  5. Проганяєте golden‑кейси і LLM‑eval, про які говорили в попередніх лекціях, щоб упевнитися: поведінка App з точки зору якості не зіпсувалася.
  6. Лише після цього викочуєте оновлення в продакшн, за можливості використовуючи canary/feature‑flag на підмножині трафіку.

Приклад: додаємо openWorldHint у новій версії SDK

Припустімо, нова версія Apps SDK додала підтримку openWorldHint, і ви вирішили позначити ним інструмент search_public_reviews, який звертається до зовнішніх відгуків і може повернути багато шуму.

Кроки виглядають так:

  • оновлюєте SDK і типи;
  • дописуєте annotations.openWorldHint = true у дескриптор інструмента;
  • оновлюєте system‑prompt, щоб агент явно пояснював користувачеві, що зараз буде запит у зовнішній світ;
  • проганяєте safety‑golden‑кейси (особливо на питання про приватність/PII), щоб упевнитися, що модель не стала надмірно «балакучою».

Ми обговорили загальний процес оновлення SDK і анотацій. Тепер погляньмо на все це в одному конкретному сценарії — еволюції інструмента recommend_gifts.

8. Міні‑кейс: еволюція recommend_gifts у GiftGenius

Давайте зберемо все разом на конкретному сценарії.

Початкова версія

Базовий інструмент виглядав так:

const recommendGiftsInput_v1 = z.object({
  occasion: z.string(),
  budgetUsd: z.number().int().positive(),
  recipientProfile: z.string(),
});

server.registerTool({
  name: "recommend_gifts",
  description: "Підбирає ідеї подарунків у USD",
  inputSchema: recommendGiftsInput_v1,
  async execute(args) {
    const input = recommendGiftsInput_v1.parse(args);
    return giftService.recommend(input); // внутрішня функція
  },
});

Усе чудово, доки у вас лише користувачі зі США й одна валюта.

Нові бізнес‑вимоги: мультивалюта і дедлайн

Продуктова команда приходить із новими вимогами:

  • потрібно підтримати EUR/GBP;
  • потрібно враховувати дедлайн доставки (не показувати подарунки, які прийдуть за місяць, якщо день народження за три дні);
  • бажано додавати у відповідь оцінку часу доставки.

Наївний підхід: просто змінюємо поля.

  • budgetUsd перейменовуємо на maxPrice;
  • додаємо currency;
  • у відповідь додаємо deliveryEstimateDays.

Що піде не так?

Старі промпти (зокрема golden‑кейси й опис у system‑prompt) і збережені діалоги продовжують надсилати budgetUsd. Модель не знає, що цього поля більше немає. MCP‑шар почне падати під час спроби parse. Поведінка ChatGPT App раптово ламається в реальних користувачів.

Правильний шлях:

  1. Додаємо нову схему і новий інструмент _v2.
const recommendGiftsInput_v2 = z.object({
  occasion: z.string(),
  maxPrice: z.number().int().positive(),
  currency: z.enum(["USD", "EUR", "GBP"]),
  recipientProfile: z.string(),
  deliverByDate: z.string().optional(),
});

server.registerTool({
  name: "recommend_gifts_v2",
  description:
    "Підбір подарунків із урахуванням валюти та бажаної дати доставки",
  inputSchema: recommendGiftsInput_v2,
  async execute(args) {
    const input = recommendGiftsInput_v2.parse(args);
    return giftService.recommendV2(input); // нова логіка
  },
});
  1. Залишаємо recommend_gifts як є, додавши в description позначку DEPRECATED.
  2. Оновлюємо system‑prompt і описи App так, щоб модель віддавала перевагу recommend_gifts_v2 (можна прямо вказати це в інструкціях).
  3. Оновлюємо віджет GiftGenius, щоб він розумів новий формат відповіді: поле deliveryEstimateDays тощо.
  4. Проганяємо golden‑кейси для типових сценаріїв (підбір подарунків до певної дати) через LLM‑eval.

Тести й спостережуваність

Кілька тестів, які варто мати:

Контракт‑тест для нового входу:

test("v2 приймає сценарій з EUR і дедлайном", () => {
  const sample = {
    occasion: "birthday",
    maxPrice: 100,
    currency: "EUR",
    recipientProfile: "колега",
    deliverByDate: "2025-12-24",
  };

  expect(() => recommendGiftsInput_v2.parse(sample)).not.toThrow();
});

Спостереження в проді:

  • метрика частки викликів recommend_gifts_v2 порівняно з recommend_gifts;
  • error‑rate по v1 (очікуємо, що він не зростає);
  • LLM‑eval‑score по golden‑кейсах до/після міграції (за результатами попередніх лекцій ви вже знаєте, як це робити).

Коли v2 «перемагає» і за якістю, і за метриками використання, можна акуратно планувати деактивацію v1.

Якщо спростити до трьох тез: (1) MCP — це тонкий адаптер, а не новий моноліт; (2) схеми, auth і анотації — це довгоживучий контракт між ChatGPT і вашим бекендом; його потрібно версіонувати й тестувати так само ретельно, як звичайні API; (3) будь‑які міграції SDK/специфікації — це нормальний інженерний процес зі staging, golden‑кейсами і спостережуваністю, а не «оновили пакет у п’ятницю ввечері». Якщо ви дивитиметеся на ChatGPT App крізь цю призму, інтеграції з наявним продуктом перестануть здаватися хаосом.

9. Типові помилки під час інтеграції та міграцій MCP/SDK

Помилка № 1: MCP як «новий бекенд», а не тонкий адаптер.
Іноді хочеться затягнути в MCP‑шар усю бізнес‑логіку: звернення до БД, доменні правила, розрахунки. Це перетворює MCP‑сервер на ще один моноліт, який важко синхронізувати з рештою бекенду. Значно здоровіше тримати MCP як Gateway/Adapter над наявними сервісами: уся доменна логіка живе там само, де й до ChatGPT, а MCP лише «перекладає» JSON туди‑назад.

Помилка № 2: Різні схеми для одного й того самого об’єкта.
Поширений антипатерн — мати три визначення «подарунка»: одне в БД, одне в REST‑API, одне в MCP‑інструменті, і всі трохи різні. У результаті ламаються статична типізація, контракти, тести й здоровий глузд. Використання єдиної схеми (Zod/TypeBox тощо) як Single Source of Truth і генерація JSON Schema для MCP значно зменшують цей ризик.

Помилка № 3: Неправильні міграції схем — «тиха» breaking‑зміна.
Перейменування поля або зміна його сенсу без зміни імені інструмента — шлях до прихованої регресії. Модель продовжить надсилати старий формат, інцидент проявиться лише в частини користувачів і далеко не відразу. У разі серйозних змін заводьте *_v2, залишайте стару версію працювати паралельно, використовуйте deprecation‑позначки й моніторинг.

Помилка № 4: Ігнорування Auth‑змін і scopes.
Додали новий інструмент із побічними ефектами, але забули оновити scopes і .well-known? Користувач може або отримати 401 посеред сценарію, або навпаки — ваш MCP почне виконувати destructive‑операції без адекватної авторизації. Плануйте міграції auth‑шару так само обережно, як міграції схем: через staging, тести й плавне розширення прав.

Помилка № 5: Невикористання анотацій (destructiveHint, readOnlyHint, openWorldHint).
Якщо не підказати моделі, які інструменти безпечні, а які потенційно небезпечні, вона може поводитися непередбачувано: просити підтвердження на безневинний get_catalog і без попередження виконувати видалення даних. Правильні анотації роблять поведінку передбачуваною для користувача та зменшують ризик інцидентів якості й безпеки.

Помилка № 6: Оновлення SDK у продакшні без прогону golden‑кейсів.
Нова версія SDK/специфікації може додавати поля, змінювати поведінку handshake або структуру повідомлень. Якщо просто «оновити залежності й розгорнути», ви ризикуєте зловити регрес якості (модель перестала викликати потрібний інструмент, змінилися формулювання помилок тощо). Спочатку — dev/staging, MCP Inspector, далі — golden‑кейси і LLM‑eval, і лише після цього — продакшн.

Помилка № 7: Жорстка прив’язка бізнес‑логіки до однієї версії інструмента.
Коли внутрішня логіка Gift Service напряму залежить від конкретного recommend_gifts, важко мігрувати на recommend_gifts_v2 без болю. Найкраща практика — мати внутрішній сервіс, який еволюціонує за своїми правилами, а інструменти *_v1, *_v2 — лише thin‑adapterʼи, що маплять старі й нові зовнішні контракти на спільні доменні структури.

Помилка № 8: Відсутність спостережуваності за версіями інструментів.
Якщо в логах і метриках ви не розрізняєте, який саме інструмент і яку версію було викликано, налагодження міграцій перетворюється на вгадування. Логуйте ім’я інструмента, версію схеми/SDK і ключові параметри — тоді будь‑які регресії легше прив’язати до конкретної зміни.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ