JavaRush /Курсы /ChatGPT Apps /Формат сообщений MCP: requests, replies, notifications, t...

Формат сообщений MCP: requests, replies, notifications, tools/resources/prompts

ChatGPT Apps
6 уровень , 1 лекция
Открыта

1. MCP и JSON‑RPC: «скучный» фундамент, который надо понять один раз

В прошлой лекции мы говорили о том, зачем вообще нужен MCP и как он вписывается в стек Apps SDK. В этой лекции сузим фокус до самого «скучного» слоя — формата MCP‑сообщений, чтобы вы могли уверенно читать сырые JSON‑логи и понимать, что именно ChatGPT шлёт вашему серверу и что тот отвечает.

MCP использует JSON‑RPC 2.0 как транспорт данных: все запросы, ответы и уведомления — это обычные JSON‑объекты с предсказуемой схемой.

То есть, вместо «каждый сервис придумывает свой формат», есть базовый контракт:

  • у запроса есть обязательное поле jsonrpc (обычно "2.0"), уникальный id, строковое имя метода method и объект params с параметрами;
  • ответ связывается с запросом по id и содержит либо result, либо error;
  • уведомления (notifications) похожи на запросы, но без id, и ответа на них не будет.

Выглядит это примерно так:


{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/list",
  "params": {
    "cursor": null
  }
}

Это request. А ответ в случае успеха:

{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "tools": [],
    "nextCursor": null
  }
}

Если в голове всплыло «ну это же обычный RPC», — так и есть. MCP просто фиксирует, какие именно методы существуют (tools/list, tools/call, resources/list, prompts/list, …) и в каком формате они ожидают параметры и возвращают данные.

Важно почувствовать: JSON‑RPC — это каркас «запрос–ответ–уведомление». MCP — это «какие именно запросы бывают и что в них внутри».

2. Request: как MCP просит что‑то сделать

Начнём с запросов. Они всегда идут в сторону «кто‑то что‑то хочет сделать». Обычно это клиент → сервер (ChatGPT → ваш MCP‑сервер), но MCP допускает и обратные запросы, когда сервер просит клиента сделать sampling или elicitation. В этой лекции нас в первую очередь интересует классический вариант: клиент просит сервер.

У любого MCP‑request есть три ключевых поля:

  1. jsonrpc — версия протокола JSON‑RPC, обычно "2.0".
  2. id — идентификатор запроса; любой JSON‑тип, но на практике чаще всего число или строка. Главное, чтобы для активных запросов id были уникальны.
  3. method — строка вида "tools/list" или "tools/call". MCP специфицирует набор допустимых методов.

И есть объект params, где уже живут параметры конкретного метода.

Пример: запрос списка инструментов

Представим, что ChatGPT только что подключился к вашему MCP‑серверу и хочет узнать, какие tools он может вызвать. Он отправит запрос примерно такого вида:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "cursor": null
  }
}

Поле cursor нужно для пагинации — если инструментов много, сервер может отдавать их порциями.

Для нашего учебного приложения (подбор подарков) тут будет пока скучно: один‑два инструмента, но протокол остаётся тем же. Пока относитесь к этому как к интуитивному примеру; формальные структуры мы разберём позже в разделе про tools.

Пример: вызов инструмента (tools/call)

Теперь чуть интереснее. Предположим, у нас уже есть MCP‑tool suggest_gifts, который вы планируете реализовать в лекции про MCP‑сервер. Он ожидает параметры:

  • occasion — повод (Birthday, Wedding, …),
  • budget — число в долларах,
  • recipient — строка с описанием, кому дарим.

ChatGPT, решив воспользоваться этим инструментом, сформирует MCP‑запрос:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "suggest_gifts",
    "arguments": {
      "occasion": "birthday",
      "budget": 100,
      "recipient": "friend who loves board games"
    }
  }
}

Обратите внимание на несколько деталей.

Во‑первых, имя инструмента берётся из того, что вы объявили на стороне сервера (server.registerTool("suggest_gifts", …)). Во‑вторых, объект arguments должен соответствовать JSON Schema, которую вы приложите в описании инструмента.

Если GPT попытается отправить аргументы не по схеме (например, budget: "сто баксов"), сервер вправе вернуть ошибку на уровне протокола или бизнес‑логики в зависимости от реализации. Пока главное — уловить общую форму такого запроса; в разделе про tools ниже мы посмотрим на эти же сообщения уже более системно.

Requests для ресурсов и промптов

Аналогично выглядят запросы к ресурсам и промптам. Спецификация MCP определяет методы:

  • resources/list — перечислить доступные ресурсы;
  • resources/read (или resources/get) — прочитать конкретный ресурс по URI;
  • prompts/list — получить список доступных промптов;
  • prompts/get — получить текст конкретного промпта.

Пример запроса чтения ресурса с каталогом подарков:

{
  "jsonrpc": "2.0",
  "id": 15,
  "method": "resources/read",
  "params": {
    "uri": "mcp://gift-server/resources/gift_catalog"
  }
}

Пока можно просто запомнить две вещи. Во‑первых, для каждого примитива есть методы */list и */get/*/read. Во‑вторых, имя метода всегда живёт в строковом поле method, а всё содержимое — в объекте params.

3. Reply: как MCP отвечает — result и error

Ответ (reply) всегда связан с запросом по полю id. Это как correlationId во многих распределённых системах: вы смотрите в логи и видите, что запрос с id=7 получил ответ с id=7, значит это одна пара.

JSON‑RPC устанавливает простое правило: в ответе либо result, либо error, но не оба сразу. MCP поверх этого уточняет структуру result для разных методов (tools/list, tools/call и т.д.) и рекомендует коды ошибок.

Успешный ответ (result)

Посмотрим на пример успешного ответа на tools/call нашего suggest_gifts. Сервер всё отработал, нашёл подходящие подарки и возвращает список в поле result:

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Here are some gift ideas for your friend..."
      }
    ],
    "structuredContent": {
      "gifts": [
        { "name": "Board game: Catan", "price": 45 },
        { "name": "Dice set", "price": 20 }
      ]
    },
    "isError": false
  }
}

Здесь важно несколько моментов.

  • Во‑первых, content и structuredContent — это те самые части ответа MCP‑tools, которые вы уже видели в Apps SDK. Модель использует текст из content, а ваш виджет аккуратно отрисовывает данные из structuredContent.
  • Во‑вторых, флаг isError относится к бизнес‑результату. С точки зрения протокола всё прошло успешно: JSON валидный, метод существует, аргументы разобраны. Но бизнес‑логика может сказать: «я не нашёл ни одной идеи подарка, это с точки зрения UX считаем ошибкой». Тогда вы ставите isError: true и описываете проблему в content.
  • В‑третьих, MCP‑спецификация для разных методов (tools/list, tools/call, */list, */get) детально описывает, какие поля должны быть в result. Например, для tools/list сервер возвращает массив описаний инструментов с именами, заголовками, описаниями и JSON Schema входных аргументов.

Ошибочный ответ (error)

Если что‑то пошло не так на уровне протокола или сервера, вместо result возвращается объект error. У него обычно есть:

  • code — числовой код ошибки;
  • message — человекочитаемое описание;
  • data — опциональные дополнительные данные (stack trace, подробности, …).

Пример: модель вызвала несуществующий метод:

{
  "jsonrpc": "2.0",
  "id": 99,
  "error": {
    "code": -32601,
    "message": "Method not found: tools/col"
  }
}

Код -32601 — классический для JSON‑RPC «method not found».

Есть тонкая, но важная грань между двумя типами ошибок.

Протокольная ошибка — когда нарушены правила MCP/JSON‑RPC: неизвестный метод, неправильный тип в поле params, невалидный JSON. Тогда уместно возвращать error на верхнем уровне.

Бизнес‑ошибка — когда протокол соблюдён, но сама операция не удалась по доменной причине: пустой каталог, нет прав на конкретный ресурс, неверный бизнес‑идентификатор. Тогда MCP обычно рекомендует вернуть валидный result, но пометить его как isError: true и описать проблему в содержимом.

Это разделение очень помогает ChatGPT и инструментам отладки: глядя на логи, вы сразу видите, была ли это техническая поломка или осознанный отказ бизнес‑логики.

4. Notifications: однонаправленные сообщения

Уведомление (notification) — это «письмо без ожидания ответа». В JSON‑RPC уведомления выглядят как обычные запросы без поля id. Клиент не должен отправлять на них reply.

В MCP уведомления используются для событий: изменения списков tools/resources/prompts, прогресс долгих операций, лог‑сообщения и т.п.

Простейший пример, который вы точно встретите — уведомление о том, что список инструментов изменился. MCP‑спека для tools описывает capability listChanged и уведомление tools/list_changed, которое сервер отправляет, если набор доступных tools поменялся.

Уведомление может выглядеть так:

{
  "jsonrpc": "2.0",
  "method": "tools/list_changed",
  "params": {
    "reason": "New tool 'suggest_gift_cards' was added"
  }
}

Ответ на него не нужен. Клиент, получив такое уведомление, может решить: «ага, надо ещё раз позвать tools/list и обновить кэш инструментов».

Другие типичные уведомления MCP (мы о них подробно будем говорить в модуле про потоки и события):

  • события прогресса (notifications/progress) для долгих операций;
  • логи сервера (notifications/logging/message);
  • изменения ресурсов (resources/list_changed) и промптов (prompts/list_changed).

Пока важно одно: уведомление = запрос без id и без ожидаемого ответа. Если вы видите в логах JSON без id, это скорее всего notification.

Insight

Эксперементально установлено, что ChatGPT App игнорирует отправленные ему сообщения (MCP-notification). Однако, учитывая, что ChatGPT Apps только в самом начале своего развития, вероятность полной поддержки всех строн MCP-протокола в ближайшем будущем очень велика. Так что рекомендую все же изучить эту сторону MCP-протокола.

5. Как выглядят tools/resources/prompts в сообщениях

Теперь самое вкусное: как именно внутри MCP‑сообщений описаны те самые tools, resources и prompts, про которые мы столько говорим.

Tools: описание и вызов

На уровне протокола у tools есть два основных процесса:

  1. discovery — клиент узнаёт, какие инструменты есть;
  2. invocation — клиент вызывает конкретный инструмент.

Мы уже мельком видели tools/list и tools/call выше. Теперь посмотрим на них более системно: какие именно процессы они покрывают и что именно возвращается в result.

5.1.1. Список инструментов — tools/list

Мы уже видели request для tools/list. Посмотрим на структуру ответа. Спецификация MCP говорит: в result.tools должен вернуться массив объектов, каждый из которых описывает один инструмент. У инструмента обязательно есть:

  • name — уникальное имя, по которому потом будет вызываться tools/call;
  • title — короткий заголовок (это видит и человек, и модель);
  • description — более развёрнутое описание того, что делает tool, как будто вы объясняете это коллеге;
  • inputSchema — JSON Schema для аргументов инструмента.

Для нашего suggest_gifts ответ tools/list может выглядеть так (сильно упрощённо):

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "suggest_gifts",
        "title": "Gift ideas generator",
        "description": "Suggests gift ideas for a given occasion and budget.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "occasion": { "type": "string" },
            "budget": { "type": "number" },
            "recipient": { "type": "string" }
          },
          "required": ["occasion", "budget"]
        }
      }
    ],
    "nextCursor": null
  }
}

Если вы в Apps SDK уже писали inputSchema при регистрации инструмента, вы практически видели этот объект, только «сверху» — в виде TypeScript‑объекта. MCP просто передаёт его по протоколу клиенту.

5.1.2. Вызов инструмента — tools/call

Формат вызова мы уже затрагивали. MCP‑спецификация описывает, что params должен содержать:

  • name — имя инструмента;
  • arguments — объект, соответствующий inputSchema.

Например:

{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "suggest_gifts",
    "arguments": {
      "occasion": "wedding",
      "budget": 150,
      "recipient": "coworker from marketing"
    }
  }
}

А в ответ сервер отдаёт result с content, structuredContent и, опционально, _meta (например, с указанием openai/outputTemplate, если вы хотите связать этот tool с конкретным виджетом).

Эта связка tools/listtools/call — базовый цикл работы MCP‑tools: сначала discovery, потом использование.

Resources: данные с адресом

Resources в MCP — это любые куски данных, к которым клиент может обращаться по URI: файлы, записи БД, конфиги, каталоги и т.п.

У них стандартный набор операций:

  • resources/list — чтобы узнать, какие ресурсы есть;
  • resources/read — чтобы прочитать конкретный ресурс (или его часть).

Представим ресурс gift_catalog, который описывает базовый каталог подарков: категории, бренды, минимальные и максимальные цены. Сервер может объявить его с URI "mcp://gift-server/resources/gift_catalog".

Ответ на resources/list может быть таким (упрощённо):

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "resources": [
      {
        "uri": "mcp://gift-server/resources/gift_catalog",	//просто уникальная строка. mcp-не протокол.
        "name": "gift_catalog",
        "description": "Base catalog of gifts with categories and prices",
        "mimeType": "application/json"
      }
    ],
    "nextCursor": null
  }
}

А чтение ресурса — resources/read:

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/read",
  "params": {
    "uri": "mcp://gift-server/resources/gift_catalog"
  }
}

Ответ может содержать сам контент и метаданные:

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "contents": [
      {
        "uri": "mcp://gift-server/resources/gift_catalog",
        "mimeType": "application/json",
        "text": "{\"categories\":[\"boardgames\",\"books\"]}"
      }
    ]
  }
}

Главная идея: ресурс — это адресуемые данные, а tools — это операции. И то и другое MCP делает явным в протоколе.

Prompts: переиспользуемые шаблоны

Prompts — это «заготовленные подсказки» или шаблоны, которые сервер может предоставить клиенту. MCP трактует их как примитив, у которого есть:

  • имя;
  • человекочитаемый заголовок/описание;
  • содержимое (часто шаблон system‑промпта или набор few‑shot примеров).

И, предсказуемо, есть два метода:

  • prompts/list — узнать, какие промпты есть;
  • prompts/get — получить содержимое одного промпта.

Например, вы хотите задать особый стиль для генерации поздравительных сообщений вместе с подарком. Тогда в MCP‑сервере можно объявить prompt gift_congrats_style.

Ответ на prompts/list может выглядеть так:

{
  "jsonrpc": "2.0",
  "id": 10,
  "result": {
    "prompts": [
      {
        "name": "gift_congrats_style",
        "description": "Style guide for birthday congratulations in a friendly tone"
      }
    ]
  }
}

А prompts/get вернёт уже сам текст (или структурированное содержимое), которое клиент может затем передать в LLM как часть system‑prompt. Пример такого запроса и ответа:

{
  "jsonrpc": "2.0",
  "id": 11,
  "method": "prompts/get",
  "params": {
    "name": "gift_congrats_style"
  }
}
{
  "jsonrpc": "2.0",
  "id": 11,
  "result": {
    "prompt": {
      "name": "gift_congrats_style",
      "messages": [
        {
          "role": "system",
          "content": [
            {
              "type": "text",
              "text": "You are a friendly assistant that writes short, warm birthday congratulations..."
            }
          ]
        }
      ]
    }
  }
}

6. Как это связано с Apps SDK и нашим виджетом

Сейчас MCP‑JSON выглядит, возможно, всё ещё чуть громоздко. Давайте увяжем это с тем, что вы уже делали через Apps SDK.

Напомним, во фронтенде виджета у вас может быть код:

// внутри React-компонента в песочнице ChatGPT
async function fetchGifts() {
  const result = await window.openai.callTool("suggest_gifts", {
    occasion: "birthday",
    budget: 50,
    recipient: "friend who loves sci-fi"
  });

  console.log(result);
}

На уровне Apps SDK это удобная функция, которая:

  1. знает URL MCP‑сервера (из конфигурации приложения);
  2. умеет по имени suggest_gifts найти описание инструмента;
  3. упаковывает ваш вызов в MCP‑request tools/call;
  4. отправляет его по выбранному транспорту (HTTP/SSE);
  5. ждёт MCP‑reply, распаковывает result и отдаёт его вам как result в JavaScript.

Если нарисовать это в виде схемы, получится примерно так:

sequenceDiagram
    participant Widget
    participant AppsSDK as Apps SDK
    participant MCP as MCP-сервер

    Widget->>AppsSDK: window.openai.callTool("suggest_gifts", {...})
    AppsSDK->>MCP: JSON { id:7, method:"tools/call", params:{...} }
    MCP-->>AppsSDK: JSON { id:7, result:{ content, structuredContent } }
    AppsSDK-->>Widget: result (ToolOutput)
    Widget->>Widget: setState(toolOutput)

Понимание MCP‑формата даёт вам два шикарных навыка.

Во‑первых, вы можете адекватно смотреть на сырые MCP‑логи (например, в MCP Inspector, о котором будет отдельная лекция) и видеть: какой именно tools/call улетел, что в нём были за аргументы, что вернулось в result или error.

Во‑вторых, при проектировании инструментов и ресурсов вы можете думать не только в терминах TypeScript‑типов, но и в терминах MCP‑схем: как это будет выглядеть в JSON, насколько это удобно для других клиентов (например, агентов, которые тоже могут подключиться к вашему MCP‑серверу).

7. Мини‑практика: читаем и «чиним» MCP‑JSON

Чтобы MCP‑формат стал своим, лучше один раз руками разобрать по частям пару сообщений. Возьмём пример цельного диалога tools/listtools/call → результат.

Клиент хочет список инструментов

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}

Что мы видим:

  • это request (есть id);
  • метод tools/list, значит, речь идёт о discovery инструментов;
  • параметры пустые, без пагинации.

Сервер отвечает:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "suggest_gifts",
        "title": "Gift ideas generator",
        "description": "Suggests gift ideas",
        "inputSchema": { "type": "object", "properties": { "occasion": { "type": "string" } } }
      }
    ]
  }
}

Сразу видно, что это ответ на тот самый запрос (тот же id: 1), протокол прошёл успешно (result есть, error нет), и теперь клиент знает, что существует tool suggest_gifts.

Клиент вызывает инструмент

Дальше клиент делает tools/call:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "suggest_gifts",
    "arguments": {
      "occasion": "anniversary"
    }
  }
}

Если сервер ожидает ещё и budget, но модель не указала его, сервер может:

  • либо вернуть протокольную ошибку (например, error с кодом «invalid params»);
  • либо принять решение по умолчанию (например, использовать средний бюджет) и вернуть нормальный result.

В терминах, которые мы вводили выше, первый вариант — протокольная ошибка (error на верхнем уровне), второй — уже область бизнес‑логики: вы всё равно возвращаете валидный result и решаете, считать ли такую ситуацию бизнес‑ошибкой (isError: true) или нормальным поведением.

Ответ в случае ошибки аргументов мог бы выглядеть так:

{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32602,
    "message": "Missing required property 'budget' in arguments"
  }
}

Опять же, отличаем это от бизнес‑ошибки: протокол нарушен (аргументы не соответствуют схеме), поэтому тут уместен error.

Сломанный пример: ищем баг

Вот JSON, который иногда встречается у новичков:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "tool": "suggest_gifts",
    "args": {
      "occasion": "birthday",
      "budget": 100
    }
  }
}

На первый взгляд выглядит правдоподобно, но если вы сверите это со спецификацией MCP, то заметите, что поля tool и args не совпадают с ожидаемыми name и arguments.

Клиент/сервер MCP‑SDK, скорее всего, никогда не сгенерирует такой JSON, но если вы, не зная спецификации, интегрируетесь руками, то подобный баг вполне реален. Именно поэтому в курсе мы и разбираем протокол «вчистую», а не только обёртки SDK.

8. Типичные ошибки при работе с сообщениями MCP

Ошибка №1: смешение протокольных и бизнес‑ошибок.
Часто разработчики по привычке всё, что «пошло не так», заворачивают в верхнеуровневый error — и отсутствие ресурса, и неправильные аргументы, и падения базы. В контексте MCP полезно разделять: если JSON‑структура и схема вызова нарушены (не тот метод, не те поля, неверные типы), это повод вернуть error. Если же инструмент просто не смог выполнить доменную операцию (нет подарков для такого бюджета, пользователь не найден), лучше вернуть валидный result с isError: true и понятным сообщением в content. Тогда и ChatGPT‑модель, и отладчики смогут корректно отличать «сломался канал связи» от «сервер осознанно отказал».

Ошибка №2: игнорирование поля id и корреляции запросов.
Иногда в логах MCP‑сервера можно увидеть ручной вывод без id или с повторяющимися значениями id для разных активных запросов. В однопоточном hello‑world это ещё может как‑то жить, но как только у вас появляются параллельные вызовы или ретраи, становится сложно понять, какой ответ к какому запросу относится. JSON‑RPC специально требует уникальный id на время жизни запроса, и MCP полагается на это правило. Если вы используете официальные SDK, о id можно не думать, но как только вы пишете транспорт или логирование сами, не забывайте сохранять и выводить id — это первая вещь, по которой вы будете дебажить странные баги.

Ошибка №3: нестабильные структуры result для одного и того же метода.
Бывает соблазн «чуть‑чуть» менять формат ответа в зависимости от ситуации: иногда возвращать массив подарков, иногда — объект с одной строкой, иногда вообще только text без structuredContent. Модель, может быть, и переживёт такие фокусы, но ваши виджеты и любые другие клиенты MCP — вряд ли. MCP‑спека для каждого метода описывает предсказуемую структуру result; старайтесь её придерживаться. Если нужен другой формат, лучше объявите отдельный tool или версию, а не меняйте схему на лету.

Ошибка №4: избыточные или отсутствующие поля в params.
Типичная проблема кастомных реализаций — добавить в params то, чего MCP не ожидает, или забыть обязательное поле. Например, отправить toolName вместо name в tools/call, или resourceId вместо uri в resources/read. MCP‑SDK обычно такие вещи валидирует и выбрасывает понятное исключение, но если вы работаете ближе к протоколу, можно долго искать, почему «сервер меня не понимает». Хороший приём — рядом с обработчиком держать пример корректного JSON‑запроса из спецификации или из логов работающего клиента и сравнивать с тем, что вы отправляете.

Ошибка №5: попытка использовать notifications как «второй канал ответов».
Иногда разработчики, увидев notifications, начинают отправлять результаты операций через уведомления вместо обычных replies: «ну мы же уже в MCP и у нас есть SSE, давайте пушить всё нотификациями». Проблема в том, что JSON‑RPC‑уведомления по определению не привязаны к конкретному id и не воспринимаются клиентом как ответ на запрос. В результате сложнее отлаживать и невозможно понять, к какому вызову инструментов относится конкретное сообщение. Нотификации отлично подходят для событий (изменился список tools/resources/prompts, появился новый прогресс, пришёл лог), но не для обычных ответов на tools/call и им подобные.

Ошибка №6: не смотреть в MCP‑логи и инспекторы.
Самая человеческая ошибка — пытаться отлаживать интеграцию только через ChatGPT UI: «нажал кнопку, что‑то не пришло, разберусь когда‑нибудь». Пока вы не видите сырые MCP‑сообщения (requests, replies, notifications), сложно понимать, на каком уровне проблема: модель не вызвала tool, Apps SDK не дошёл до MCP‑сервера, сервер вернул не тот JSON, или всё сломалось уже при отрисовке виджета. MCP Inspector / Jam и структурированное логирование MCP‑сообщений — ваш лучший друг. После того как вы один раз увидите живой tools/call и tools/list в логах, формат сообщений MCP окончательно перестанет быть «магией» и станет обычной инженерной рутиной.

1
Задача
ChatGPT Apps, 6 уровень, 1 лекция
Недоступна
Генератор MCP-сообщений (request vs notification)
Генератор MCP-сообщений (request vs notification)
1
Задача
ChatGPT Apps, 6 уровень, 1 лекция
Недоступна
Мини-обработчик JSON-RPC для MCP методов (result vs error)
Мини-обработчик JSON-RPC для MCP методов (result vs error)
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ