Главная / Статьи / Практические заметки: Инструменты MCP в корпоративных приложениях: подходит для начинающих

Практические заметки: Инструменты MCP в корпоративных приложениях: подходит для начинающих

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

2925 слов

В следующих заметках описывается практический подход к изучению темы «MCP Tools Inside Enterprise Applications: A Beginner-Friendly Deep Dive». Основное внимание уделяется контрактам, проверкам и местам для вставки кода, а не мотивирующим аспектам. На этапе обзора сначала запишите условия контракта: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой список поможет сохранять честность при последующих изменениях кода. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия элементам, определите критерии успеха и не допускайте безответственного частичного выполнения задач.

1. Проблема: почему компаниям сначала понадобились MCP

1. Проблема: этап работает наилучшим образом, когда рассматривается как измеримая поверхность. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объема работ. Записывайте временные показатели, а также стоимость токенов или запросов рядом с функциональными результатами. Отображение стоимости на раннем этапе предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Используйте инструменты с узкими схемами и четкими метками о побочных эффектах. Администраторам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят их.

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.
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.

2. Основные концепции, объясненные просто

Этап «Объяснение двух основных концепций» наилучшим образом работает, если рассматривать его как измеримую поверхность. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работ. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Обеспечьте доступ к инструментам с узкими схемами и чёткими метками побочных эффектов. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят их.

Три примитива, которые может предоставлять сервер

Три основных элемента этого этапа работают наилучшим образом, когда их рассматривают как измеримую поверхность. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объема работ. Документируйте как успешный, так и восстановительный пути выполнения. Повторные попытки, проверки со стороны человека и обработка неработоспособных сообщений являются частью продукта, а не последующими доработками. Используйте инструменты с узкими схемами и четкими метками побочных эффектов. У операторов должна быть возможность узнать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят операцию. Три основных элемента этого этапа работают наилучшим образом, когда их рассматривают как измеримую поверхность. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объема работ. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия соответствующим элементам, определите критерии успеха и не допускайте безответственного частичного выполнения задач.

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) │
   └──────────────────┘     └──────────────────┘     └─────────────────┘

4. Создание вашего первого сервера MCP (Node.js / TypeScript)

На этапе «Создание первого проекта» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код проекта. Аутентификация должна осуществляться на шлюзе, а повторная авторизация — на уровне обработки данных. Один только токен не является границей между тенантами.

4.1 Настройка проекта

На этапе настройки проекта 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

На этапе настройки проекта 4 1 необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Считайте этот этап контрактом между входными данными и проверенными результатами. Дайте названия элементам, определите критерии успеха и не допускайте безответственного частичного выполнения задач.

4.2 Код сервера

При работе над этапом 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 Что на самом деле происходит здесь (теория по строкам)

При работе над этапом 4.3 «Что делать?» сначала запишите условия контракта: необходимые входные данные, сигнал успешного выполнения и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Фиксируйте название инструмента, хеш аргументов, время задержки и результат каждого вызова. Без такой записи отладка агента занимает часы.

4.4 Запуск

При работе над этапом «Запуск» запишите сначала условия работы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Документируйте одновременно успешный и восстановительный сценарии работы. Повторные попытки, проверки со стороны оператора и обработка некорректных сообщений являются частью продукта, а не элементами последующей доработки. Фиксируйте название инструмента, хеш аргументов, время задержки и результат каждого вызова. Без такой записи отладка занимает много времени.

npx tsx src/server.ts

При работе над этапом «Запуск» запишите сначала условия работы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Рассматривайте этот этап как договор между входными данными и проверенными результатами. Дайте названия соответствующим элементам, определите критерии успеха и не допускайте молчаливого частичного завершения работы.

5. Создание клиента MCP внутри корпоративного приложения

Этап создания клиента MCP работает наилучшим образом, если рассматривать его как измеримую единицу. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ. Записывайте время выполнения операций, а также стоимость токенов или запросов вместе с функциональными результатами. Отслеживание затрат на раннем этапе помогает избежать неожиданных счетов при переходе от демо-среды к общедоступным средам. Обеспечьте доступ к инструментам с узкими схемами и четкими метками о побочных эффектах. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят их.

// 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();

Почему это важно с концептуальной точки зрения

Концепция «Почему это важно» наиболее эффективна, когда рассматривается как измеримая структура. Соберите один идеальный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работ. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли проводить аудит без необходимости просмотра всей структуры. Используйте инструменты с узкими схемами и чёткими метками о побочных эффектах. Хостам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически согласятся на их выполнение.

6. От локального прототипа к корпоративному развертыванию

Этап «6 From Local Prototype» работает наилучшим образом, когда его рассматривают как измеримую среду. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работ. Документируйте как успешный, так и восстановительный пути выполнения. Повторные попытки, проверки человеком и обработка неработающих сообщений являются частью продукта, а не этапом последующей доработки. Используйте инструменты с узкими схемами и чёткими метками побочных эффектов. Администраторам необходимо знать, какие вызовы изменяют состояние, прежде чем они автоматически одобрят операцию. Этап «6 From Local Prototype» работает наилучшим образом, когда его рассматривают как измеримую среду. Соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма работ. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия элементам, определите критерии успеха и не допускайте молчаливого частичного завершения работы.

// 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"));

7. Чек-лист критериев корпоративного уровня

На этапе 7 «Чек-лист критериев класса Enterprise» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывать скрытое состояние. Рядом с функциональными результатами следует записывать время выполнения и стоимость токенов или запросов. Отображение затрат на раннем этапе предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды. Аутентификация должна происходить на шлюзе, а повторная авторизация — на уровне обработки данных. Один только токен-носитель не является границей между тенантами.

8. Где это применяется в реальных корпоративных кейсах

Для этапа «8. Место отображения» необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь кодовый граф. Аутентифицируйтесь на шлюзе и повторно авторизуйтесь на уровне обработки данных. Один только токен-носитель не является границей между тенантами.

9. Распространённые ошибки, с которыми сталкиваются новички

На этапе «9 распространённых ошибок для начинающих» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо задокументировать как успешный, так и восстановительный пути работы. Повторные попытки, проверки со стороны человека и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки. Аутентификация происходит на шлюзе, а повторная авторизация — на уровне данных. Один только токен-носитель не является границей между тенантами. На этапе «9 распространённых ошибок для начинающих» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия элементам, определите критерии успеха и не допускайте безответственного частичного завершения работы.

10. Заключение

При работе над этапом «Заключение» сначала запишите условия контракта: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Рядом с функциональными результатами записывайте время выполнения, стоимость токенов или запросов. Очевидность затрат с самого начала предотвращает неожиданные счета при переходе от демо-среды к общедоступным средам. Для каждого вызова фиксируйте название инструмента, хеш аргументов, время задержки и результат. Без такой записи отладка циклов занимает часы.

Чек-лист операционной работы

На этапе чек-листа операционной работы перед изменением кода определите входные данные, ответственного за шаг и критерии завершения. Операторы должны иметь возможность перезапустить шаг, исходя из известной точки контроля, без необходимости угадывать скрытое состояние.

Лучше использовать небольшие, тестируемые единицы вместо обширных скриптов. Когда какой-то шаг терпит неудачу, ошибка должна указывать на конкретную ответственность, а не на запутанную цепочку операций.

Аутентифицируйтесь у шлюза и повторно авторизуйтесь на уровне обработки данных. Один только токен-носитель не является границей между тенантами.

Напишите краткий руководство: как обновлять ключи, как опустошать очередь, как откатывать последнюю загрузку данных.

Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Укажите названия элементов, определите критерии успеха и не допускайте безответственного частичного выполнения задачи.

Аутентифицируйтесь у шлюза и повторно авторизуйтесь на уровне обработки данных. Один только токен-носитель не является границей между тенантами.

Перед внедрением данной стек-технологии необходимо заморозить версии, сгенерировать эталонный отчет для критической цепочки операций и уточнить шаги возврата к предыдущему состоянию. В совместных средах требуются ограничения на частоту запросов, проверки принадлежности пользователя и четко определенный ответственный за обновление секретов. Лучше выбирать надежность, даже если она кажется менее привлекательной, чем красивые одноразовые демонстрации.

Примечание для задачи 100916d5ed60: не храните ключи поставщика в репозитории, установите лимит токенов на одну сессию и сохраняйте отчеты рядом с фикстчерами для оценки, чтобы последующие замены моделей можно было сравнивать.