docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api
uk/docs/api/mcp-bundle/http_security.md
новий файл +71
−0
1
+
# HTTP і безпека
2
+
3
+
## Версії протоколу
4
+
5
+
Транспорт — Streamable HTTP з офіційного `mcp/sdk`. Він обслуговує обидві епохи протоколу MCP на тому самому URL:
6
+
7
+
* **Із сесіями** (2024-11-05 … 2025-11-25): `initialize` → заголовок `Mcp-Session-Id` → запити з цим заголовком; `DELETE` завершує сесію. Сесії зберігаються в пулі кешу ядра (див. [Налаштування](/docs/api/mcp-bundle/config)). Запит без сесії отримує 400, невідома чи прострочена сесія — 404 (клієнт повторює `initialize`).
8
+
* **Stateless 2026-07-28**: без handshake і без сесії. Кожен запит містить `params._meta["io.modelcontextprotocol/protocolVersion"]` і заголовки `MCP-Protocol-Version`, `Mcp-Method` (для `tools/call` — ще `Mcp-Name`); результат містить `_meta["io.modelcontextprotocol/serverInfo"]`.
9
+
10
+
Перевірено наскрізними тестами офіційним клієнтом SDK через HTTP на версіях 2025-06-18, 2025-11-25 і 2026-07-28, а також через MCP Inspector.
11
+
12
+
`GET` (окремий SSE-потік від сервера до клієнта) відповідає 405: сервер нічого не надсилає з власної ініціативи. JSON-RPC batch приймаються.
13
+
14
+
## Порядок перевірок
15
+
16
+
`McpRequestListener` виконується перед кожною дією контролера бандла (ендпоінт, `server.json`, маніфести):
17
+
18
+
1. **404** з порожнім тілом — група вимкнена (`McpAvailabilityCheckerInterface`, типово — параметр `enabled`) або в групи немає методів у запитаній версії `{ver}`. Ця перевірка йде до перевірки токена, тож недоступний ендпоінт виглядає однаково з токеном і без нього.
19
+
2. **OPTIONS** (CORS preflight) проходить без токена; на нього відповідає транспорт.
20
+
3. **Токен** — `IRpcSecurity` ядра:
21
+
* MCP-ендпоінт: `isValidApiRequest()` — діють `protected_api`, валідатори токенів і `security.providers` групи;
22
+
* `server.json` і маніфести: `isValidDocRequest()` — захищені лише коли ввімкнено `protected_doc`;
23
+
* немає токена → **401** `{"error": "Unauthorized. Token not found"}`; недійсний → **403** `{"error": "Forbidden. Invalid token"}`.
24
+
25
+
Дія визначається за методом контролера, а не за назвою маршруту, тож `name_prefix` в імпорті не дозволяє обійти перевірку.
26
+
27
+
## Заголовок із токеном
28
+
29
+
Використовується заголовок токена ядра (типово `Ufo-RPC-Token`, параметр `ufo_json_rpc.security.token_name` — див. [Налаштування JsonRpcBundle](/docs/api/json-rpc-bundle/config)). CORS-заголовок `Access-Control-Allow-Headers` містить його разом із заголовками MCP. Клієнти, що підтримують власні заголовки (Claude Code, Cursor, Gemini CLI, OpenAI Responses API), надсилають його; [маніфести клієнтів](/docs/api/mcp-bundle/discovery) прописують його на місці.
30
+
31
+
:::warning subtitle="OAuth не реалізовано"
32
+
Немає `/.well-known/oauth-protected-resource` і заголовка `WWW-Authenticate` у відповіді 401. Специфікація MCP робить авторизацію необов'язковою, але клієнти, які підтримують лише OAuth, підключитися не зможуть (наприклад, MCP Inspector без заголовка з токеном запускає свій OAuth-flow).
33
+
:::
34
+
35
+
## CORS і DNS rebinding
36
+
37
+
`McpAccessPolicyInterface` (типово `DefaultMcpAccessPolicy`) керує двома шарами транспорту SDK:
38
+
39
+
* `getAllowedOrigins()` — CORS. Типово `['*']`; `[]` — CORS-заголовки не надсилаються.
40
+
* `getAllowedHosts()` — захист від DNS rebinding. Типово `[]` — вимкнено. Якщо список задано, запит, чий `Host` у ньому відсутній, отримує 403. **Коли запит має заголовок `Origin`, SDK перевіряє і хост з Origin за тим самим списком**, тож хост браузерного клієнта теж має бути в списку.
41
+
42
+
```php
43
+
use Ufo\McpBundle\Security\McpAccessPolicyInterface;
44
+
45
+
final class AppMcpAccessPolicy implements McpAccessPolicyInterface
46
+
{
47
+
public function getAllowedHosts(): array
48
+
{
49
+
return ['api.example.com', 'app.example.com'];
50
+
}
51
+
52
+
public function getAllowedOrigins(): array
53
+
{
54
+
return ['https://app.example.com'];
55
+
}
56
+
}
57
+
```
58
+
59
+
```yaml
60
+
# config/services.yaml
61
+
services:
62
+
Ufo\McpBundle\Security\McpAccessPolicyInterface: '@App\Mcp\AppMcpAccessPolicy'
63
+
```
64
+
65
+
:::info
66
+
Політика діє для всіх груп. Головний захист — токен; CORS і перевірка хоста — додаткові шари.
67
+
:::
68
+
69
+
## Ліміт тіла запиту
70
+
71
+
`max_body_bytes` (загальний або для групи): на більше тіло запиту ендпоінт відповідає 413 з помилкою JSON-RPC.