Документация Adapstory
Плагины

MCP-инструменты

Как LLM вызывают ваш плагин через Plugin Gateway.

MCP (Model Context Protocol) — это то, как LLM в Adapstory обнаруживают и вызывают capabilities вашего плагина. Plugin Gateway (BC-01) сидит между LLM и вашим кодом.

Анатомия вызова инструмента

LLM ──▶ Plugin Gateway (BC-01) ──▶ Ваш плагин (/mcp/<tool>)

           ├─ auth: service-account JWT со scope (tenant, plugin)
           ├─ guardrails: content filter, prompt-injection check
           ├─ budget:     token + per-call cost check
           └─ telemetry:  OpenTelemetry span стартует здесь

Плагин получает обычный HTTP-запрос с:

  • Authorization: Bearer <jwt> — идентичность вызывающего пользователя
  • X-Tenant-Id: <tenant> — контекст тенанта
  • X-Correlation-Id: <uuid> — связывает трейс
  • X-Llm-Session: <id> — conversation, в которой идёт вызов
  • JSON body соответствующий вашей schema

Написание handler'а

Держите handler'ы маленькими. Паттерн:

  1. Валидируйте аргументы по JSON-Schema (SDK делает это автоматически).
  2. Вызовите core-API или LLM Gateway для реальной работы.
  3. Верните JSON-результат соответствующий returnSchema (если объявлен).
# src/hello_learner/tools.py
from adapstory_plugin_sdk import mcp_tool, LlmGateway, Context

@mcp_tool(name="hello_learner")
async def hello_learner(
    learner_name: str,
    *,
    ctx: Context,
    gateway: LlmGateway,
) -> dict:
    ctx.logger.info("greeting", learner=learner_name)
    response = await gateway.chat(
        model=ctx.plugin.defaultModel,
        messages=[
            {"role": "system", "content": "Be brief and encouraging."},
            {"role": "user",   "content": f"Say hi to {learner_name}."},
        ],
    )
    return {"message": response.text}

Стриминг

Для инструментов, отдающих частичные результаты (summaries, генерации), поставьте streaming: true в манифесте и верните Server-Sent Events:

@mcp_tool(name="summarize_course", streaming=True)
async def summarize_course(course_id: str, *, gateway: LlmGateway):
    async for chunk in gateway.stream(
        model="claude-sonnet-4-6",
        messages=[...],
    ):
        yield { "delta": chunk.text }

Gateway пробрасывает SSE в LLM-клиент с соответствующей буферизацией.

Cancellation

Gateway шлёт заголовок X-Llm-Cancel: <correlation-id>, когда апстрим-LLM отваливается. Уважайте его:

async for chunk in gateway.stream(...):
    if ctx.cancelled:
        break
    yield { "delta": chunk.text }

SDK пробрасывает cancellation в in-flight Gateway-вызовы.

Бюджеты и back-pressure

Если BC-01 возвращает 429 Too Many Requests с X-LLM-Budget-Resetне retry. Верните деградированный результат (кэш, эвристика, пусто) и дайте клиенту принять решение.

Per-tool rate limits (из manifest.rateLimit) обеспечиваются Gateway. Вам не нужно их реализовывать.

Контракт ошибок

Возвращайте структурированные ошибки, чтобы LLM могла восстановиться:

from adapstory_plugin_sdk import ToolError

raise ToolError(
    code="learner_not_found",
    message="No learner with that ID in this tenant.",
    retryable=False,
    suggestion="Check that the learner has been enrolled.",
)

Gateway конвертирует это в JSON-ответ, о котором LLM может рассуждать.

Контракт observability

Каждый handler, декорированный @mcp_tool, автоматически:

  • Стартует OpenTelemetry span plugin.{name}.tool.{tool_name} с атрибутами tenant.id, user.id, llm.session.id.
  • Эмитит лог-строку INFO на entry + exit.
  • Репортит длительность + outcome в Prometheus через OTLP-коллектор.

Вам не нужно инструментировать это самостоятельно. Добавьте только ctx.logger.info(...) с бизнес-контекстом.

Далее: Жизненный цикл.

On this page