Глава 11

Проектируйте tools как безопасные операции

Инструмент в MCP - это model-controlled capability: клиент решает, когда предложить его модели, а модель может сама сформировать аргументы. Отсюда неочевидное следствие: schema, описание, annotations, authorization, идемпотентность и размер ответа - это не удобства, а часть границы безопасности. Плохо спроектированный инструмент даёт модели власть, которую потом нечем ограничить.

Хороший инструмент начинается со строгой схемы и честных annotations.

TypeScript
const searchOutputSchema = z.object({
  hits: z.array(z.object({
    id: z.string(),
    title: z.string(),
    excerpt: z.string(),
    score: z.number()
  }))
});

const reviewOutputSchema = z.object({
  service: z.string(),
  version: z.string(),
  decision: z.enum(["ready", "blocked"]),
  checks: z.array(z.object({
    id: z.enum(["ci", "tests", "security", "migration", "rollback"]),
    title: z.string(),
    passed: z.boolean(),
    evidence: z.string()
  })),
  summary: z.string()
});

  server.registerTool(
    "search_runbooks",
    {
      title: "Поиск по runbook",
      description: "Ищет только в утверждённых инженерных runbook и возвращает короткие выдержки.",
      inputSchema: z.object({ query: z.string().min(2).max(120) }),
      outputSchema: searchOutputSchema,
      annotations: {
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: false
      }
    },
    async ({ query }) => {
      const output = { hits: searchRunbooks(query) };
      return {
        content: [{ type: "text", text: JSON.stringify(output) }],
        structuredContent: output
      };
    }
  );

Важно понимать, что именно каждое поле обещает - и, главное, чего оно не гарантирует.

Поле · Что сообщает · Чего не гарантирует

  • inputSchema - Допустимая wire-форма аргументов; Право пользователя и бизнес-инварианты
  • outputSchema - Ожидаемая structure результата; Истинность данных
  • readOnlyHint - Операция не должна менять состояние; Безопасность внешнего API
  • destructiveHint - Подсказка о разрушительном эффекте; Human approval
  • idempotentHint - Повтор с теми же args не добавляет эффект; Корректную retry policy
  • openWorldHint - Есть ли взаимодействие с внешним миром; Изоляцию сети

Эта таблица - защита от опасной иллюзии, будто schema и annotations уже делают инструмент безопасным. inputSchema задаёт допустимую форму аргументов, но не право пользователя и не бизнес-инвариант. readOnlyHint говорит о намерении, но не о безопасности внешнего API. Annotations - подсказки для UI и approval-политики host, не защита сервера.

Чтобы почувствовать, какую поверхность выбрать под конкретное намерение, прогоните его через конструктор - он подскажет, что уместнее: инструмент, ресурс или prompt.

Интерактивная лаборатория 3

Выберите поверхность MCP

Tool: узкое имя, Zod input/output, authorization, timeout и annotations.

Разница между наивным и профессиональным инструментом почти всегда в ширине семантики. run_shell(command) или query_database(sql) дают модели язык с огромной властью и никакой allowlist. review_release(service, version) даёт узкую операцию с чёткой схемой, авторизацией и предсказуемым ответом. Второе скучнее, но именно оно доживает до продакшена.

Annotations являются hints. Клиент может использовать их для UI и approval-политики, но сервер не должен считать их защитой. Реальные ограничения живут в handler и в downstream-системе.

Инструменты выполняют операции - но часто модели нужен не эффект, а факт. Для этого есть отдельная поверхность: resources.

Проверка знаний

У tool стоит readOnlyHint: true. Что это гарантирует?

Ссылки