# Протокол контекста модели

Source: https://docs.sisk-framework.org/ru/docs/extensions/mcp.html

Можно создавать приложения, которые предоставляют контекст моделям‑агентам, использующим большие языковые модели (LLM), с помощью пакета [Sisk.ModelContextProtocol](https://www.nuget.org/packages/Sisk.ModelContextProtocol/) :

    dotnet add package Sisk.ModelContextProtocol --prerelease

Этот пакет предоставляет полезные классы и методы для построения MCP‑серверов, работающих по протоколу [Streamable HTTP](https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http), поддерживая одиночные запрос‑ответ сообщения. Начиная с версии 1.7.0‑rc1 он поддерживает безсостояние запросов версии протокола `2026-07-28` и сохраняет устаревший процесс инициализации для клиентов, использующих версии `2025-11-25`, `2025-06-18` и `2025-03-26`. Поддерживаются инструменты и навыки.

> [!NOTE]
>
> Прежде чем начать, обратите внимание, что этот пакет находится в разработке и может вести себя не в соответствии со спецификацией. Прочитайте [подробности пакета](https://github.com/sisk-http/core/tree/main/extensions/Sisk.ModelContextProtocol), чтобы узнать, что уже реализовано, а что ещё не работает.

## Начало работы с MCP

Класс [McpProvider](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpProvider.md) является точкой входа для определения MCP‑сервера. Это запечатлённый объект‑провайдер, который можно настроить при запуске. Ваше приложение Sisk может иметь один или несколько MCP‑провайдеров.

```csharp
McpProvider mcp = new McpProvider(
    serverName: "math-server",
    serverTitle: "Mathematics server",
    serverVersion: new Version(1, 0));

mcp.Tools.Add(new McpTool(
    name: "math_sum",
    description: "Sums one or more numbers.",
    schema: JsonSchema.CreateObjectSchema(
        properties: new Dictionary<string, JsonSchema>()
        {
            { "numbers",
                JsonSchema.CreateArraySchema(
                    itemsSchema: JsonSchema.CreateNumberSchema(),
                    minItems: 1,
                    description: "The numbers to sum.")
            }
        },
        requiredProperties: ["numbers"]),
    executionHandler: async (McpToolContext context) =>
    {
        var numbers = context.Arguments["numbers"].GetJsonArray().ToArray<double>();
        var sum = numbers.Sum();
        return await Task.FromResult(McpToolResult.CreateText($"Sum result: {sum:N4}"));
    }));
```

Если ваше приложение будет предоставлять только один MCP‑провайдер, можно воспользоваться синглтоном билдера:

```csharp
static void Main(string[] args)
{
    using var host = HttpServer.CreateBuilder()
        .UseMcp(mcp =>
        {
            mcp.ServerName = "math-server";
            mcp.ServerTitle = "Mathematics server";

            mcp.Tools.Add(new McpTool(
                name: "math_sum",
                description: "Sums one or more numbers.",
                schema: JsonSchema.CreateObjectSchema(
                    properties: new Dictionary<string, JsonSchema>()
                    {
                        { "numbers",
                            JsonSchema.CreateArraySchema(
                                itemsSchema: JsonSchema.CreateNumberSchema(),
                                minItems: 1,
                                description: "The numbers to sum.")
                        }
                    },
                    requiredProperties: ["numbers"]),
                executionHandler: async (McpToolContext context) =>
                {
                    var numbers = context.Arguments["numbers"].GetJsonArray().ToArray<double>();
                    var sum = numbers.Sum();
                    return await Task.FromResult(McpToolResult.CreateText($"Sum result: {sum:N4}"));
                }));
        })
        .UseRouter(router =>
        {
            router.MapAny("/mcp", async (HttpRequest req) =>
            {
                return await req.HandleMcpRequestAsync();
            });
        })
        .Build();

    host.Start();
}
```

Точка доступа должна принимать как `GET`, так и `POST` запросы, поэтому `MapAny` — самое простое сопоставление маршрута. `HandleMcpRequestAsync` возвращает [HttpResponse](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpResponse.md), и ваш маршрут должен вернуть его. Если нужны несколько провайдеров в одном приложении, пропустите синглтон и вызывайте [McpProvider.HandleRequestAsync](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpProvider.HandleRequestAsync.md) напрямую из каждого маршрута:

```csharp
var mathProvider = new McpProvider("math-server", "Mathematics server", new Version(1, 0));

router.MapAny("/mcp/math", async request =>
{
    return await mathProvider.HandleRequestAsync(request);
});
```

## Создание JSON‑схем для функций

Библиотека [Sisk.ModelContextProtocol] использует форк [LightJson](https://github.com/CypherPotato/LightJson) для работы с JSON и JSON‑схемами. Эта реализация предоставляет удобный построитель JSON‑Schema для различных объектов:

- JsonSchema.CreateObjectSchema
- JsonSchema.CreateArraySchema
- JsonSchema.CreateBooleanSchema
- JsonSchema.CreateNumberSchema
- JsonSchema.CreateStringSchema
- JsonSchema.Empty

Пример:

```csharp
JsonSchema.CreateObjectSchema(
    properties: new Dictionary<string, JsonSchema>()
    {
        { "numbers",
            JsonSchema.CreateArraySchema(
                itemsSchema: JsonSchema.CreateNumberSchema(),
                minItems: 1,
                description: "The numbers to sum.")
        }
    },
    requiredProperties: ["numbers"]);
```

Создаёт следующую схему:

```json
{
  "type": "object",
  "properties": {
    "numbers": {
      "type": "array",
      "items": {
        "type": "number"
      },
      "minItems": 1,
      "description": "The numbers to sum."
    }
  },
  "required": ["numbers"]
}
```

## Обработка вызовов функций

Функция, определённая в параметре `executionHandler` класса [McpTool](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpTool.md), предоставляет JsonObject, содержащий аргументы вызова, которые можно удобно читать:

```csharp
mcp.Tools.Add(new McpTool(
    name: "browser_do_action",
    description: "Run an browser action, such as scrolling, refreshing or navigating.",
    schema: JsonSchema.CreateObjectSchema(
        properties: new Dictionary<string, JsonSchema>()
        {
            { "action_name",
                JsonSchema.CreateStringSchema(
                    enums: ["go_back", "refresh", "scroll_bottom", "scroll_top"],
                    description: "The action name.")
            },
            { "action_data",
                JsonSchema.CreateStringSchema(
                    description: "Action parameter."
                ) }
        },
        requiredProperties: ["action_name"]),
    executionHandler: async (McpToolContext context) =>
    {
        // читаем имя действия. будет выброшено исключение, если значение null или не является строкой
        string actionName = context.Arguments["action_name"].GetString();
        
        // action_data объявлен как необязательный, поэтому здесь может быть null
        string? actionData = context.Arguments["action_data"].MaybeNull()?.GetString();
        
        // Обрабатываем действие браузера на основе actionName
        return await Task.FromResult(
            McpToolResult.CreateText($"Performed browser action: {actionName}"));
    }));
```

Аргументы инструмента проверяются по схеме до выполнения вашего обработчика. Если проверка не проходит, провайдер возвращает клиенту MCP ошибку и не вызывает обработчик инструмента.

## Результаты функций

Объект [McpToolResult](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpToolResult.md) предоставляет три метода для создания содержимого ответа инструмента:

- [CreateAudio(ReadOnlySpan<byte>, string)](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpToolResult.CreateAudio.md): создаёт аудио‑ответ для клиента MCP.
- [CreateImage(ReadOnlySpan<byte>, string)](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpToolResult.CreateImage.md): создаёт изображение‑ответ для клиента MCP.
- [CreateText(string)](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpToolResult.CreateText.md): создаёт текстовый ответ (по умолчанию) для клиента MCP.

Кроме того, можно объединить несколько разных типов содержимого в один JSON‑ответ инструмента:

```csharp
mcp.Tools.Add(new McpTool(
    ...
    executionHandler: async (McpToolContext context) =>
    {
        // simulate real work

        byte[] browserScreenshot = await browser.ScreenshotAsync();
        
        return McpToolResult.Combine(
            McpToolResult.CreateText("Heres the screenshot of the browser:"),
            McpToolResult.CreateImage(browserScreenshot, "image/png")
        )
    }));
```

В данный момент провайдер обрабатывает методы `server/discover`, устаревшие `initialize` и `ping`, `tools/list`, `tools/call`, `skills/list`, `skills/get`, `resources/list`, `resources/read` и уведомления. Неподдерживаемые методы JSON‑RPC возвращают ошибку JSON‑RPC.

## Публикация навыков

MCP‑провайдер также может публиковать [Agent Skills](https://github.com/modelcontextprotocol/ext-skills) через расширение `io.modelcontextprotocol/skills`. Добавьте объекты [McpSkill](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpSkill.md) в [McpProvider.Skills](https://docs.sisk-framework.org/api/Sisk.ModelContextProtocol.McpProvider.Skills.md) до того, как провайдер начнёт обрабатывать запросы:

```csharp
mcp.Skills.Add(new McpSkill(
    path: "acme/refunds",
    frontmatter: new JsonObject
    {
        ["name"] = "refunds",
        ["description"] = "Follow the refund workflow."
    },
    body: "# Refunds\nRead references/policy.md before using the refund tools.",
    files: new Dictionary<string, byte[]>
    {
        ["references/policy.md"] = File.ReadAllBytes("skills/refunds/references/policy.md")
    }));
```

`McpSkill` генерирует файл `SKILL.md` из frontmatter и тела Markdown. Последний сегмент `path` должен совпадать с полем `name` в frontmatter. Файлы навыка копируются при его создании и больше не читаются с диска провайдером. Клиенты получают список и читают навыки через `skills/list`, `skills/get` и `resources/read`, используя версию протокола `2026-07-28`. См. [подробности пакета](https://github.com/sisk-http/core/tree/main/extensions/Sisk.ModelContextProtocol) для ограничений и правил валидации.

## Дальнейшая работа

Протокол контекста модели — это коммуникационный протокол для моделей‑агентов и приложений, предоставляющих им контент. Это новый протокол, поэтому его спецификация часто обновляется: появляются устаревания, новые возможности и несовместимые изменения.

Важно понять, какие задачи решает [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro), прежде чем начинать создавать агентные приложения.

Также ознакомьтесь со спецификацией пакета [Sisk.ModelContextProtocol](https://github.com/sisk-http/core/tree/main/extensions/Sisk.ModelContextProtocol), чтобы понять его текущее состояние, прогресс и возможности использования.
