docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api
en/docs/api/mcp-bundle/config.md
new file +112
−0
MCP Bundle configuration
Configuration has two parts: routes define which endpoints exist and which API group and version each one serves; the ufo_mcp configuration only tunes the group's server. The configuration never decides which methods become tools or which version is served.
Routes: one import per endpoint
# config/routes/ufo_mcp.yaml
ufo_mcp:
resource: ../../vendor/ufo-tech/mcp-bundle/config/router.yaml
prefix: /mcp
defaults: { api_group: mcp }
ufo_mcp_internal:
resource: ../../vendor/ufo-tech/mcp-bundle/config/router.yaml
prefix: /mcp/internal
defaults: { api_group: internal }
api_groupis the core API group the endpoint serves. Without it, the core default groupgeneralis used.- Every import gets its own route names (
mcp_ufo_mcp_endpoint,internal_ufo_mcp_endpoint, …), soname_prefixis not needed. The core route loader does this for YAML imports; in PHP or XML route configuration setname_prefixby hand.
Every import serves:
| Route | Name | HTTP methods |
|---|---|---|
<prefix>, <prefix>/{ver} |
<group>_ufo_mcp_endpoint |
POST, DELETE, OPTIONS; GET answers 405 |
<prefix>/server.json, <prefix>/{ver}/server.json |
<group>_ufo_mcp_server_json, <group>_ufo_mcp_server_json_ver |
GET |
<prefix>/manifest/{aiName}.json, <prefix>/{ver}/manifest/{aiName}.json |
<group>_ufo_mcp_manifest, <group>_ufo_mcp_manifest_ver |
GET |
{ver} is v1, v2, …; without it, v1.
Configuration reference
File config/packages/ufo_mcp.yaml, root key ufo_mcp.
ufo_mcp:
enabled: true
instructions: ~
max_body_bytes: 4194304
session_ttl: 3600
mcp_server_info:
name: 'io.github.your-org/your-app'
title: 'Your App MCP Server'
description: 'Exposes Your App RPC services as MCP tools.'
icons:
- src: 'https://example.com/icon.png'
mime_type: 'image/png'
sizes: ['128x128']
theme: ~
groups:
internal:
instructions: 'Internal tools.'
mcp_server_info:
name: 'io.github.your-org/your-app-internal'
| Parameter | Default | Description |
|---|---|---|
enabled |
true |
Whether the endpoint answers; otherwise 404. Supports '%env(bool:MCP_ENABLED)%' |
instructions |
null |
The instructions field of the initialize response: a hint to the agent on how to use the server |
max_body_bytes |
4194304 |
Request body limit in bytes, >= 1; a larger body gets 413 |
session_ttl |
3600 |
How many seconds a session of the session-based protocol lives, >= 1 |
mcp_server_info.name |
null |
Server id in reverse-DNS format for the MCP Registry; serverInfo.name in initialize. If unset: ufo-rpc in initialize, and the field is absent from server.json |
mcp_server_info.title |
null |
serverInfo.title in initialize and title in server.json |
mcp_server_info.description |
null |
server.json only |
mcp_server_info.icons[] |
[] |
server.json only: src (required), mime_type, sizes, theme (light | dark) |
groups.<group> |
— | Optional per-group overrides: the same fields enabled, instructions, max_body_bytes, session_ttl, mcp_server_info |
groupsis optional. The route defines the group; a group without its own entry runs on the bundle-wide settings. An entry only overrides them field by field: an unset (or~) field inherits the bundle-wide value,mcp_server_infotoo (a group may set onlyname).- There is no version setting.
serverInfo.versionininitializeandversioninserver.jsonare always the request's{ver}. - Options of earlier builds (
session_directory,server_name,server_version,methods) were removed; with them the configuration fails with "Unrecognized option".
For a group, max_body_bytes and session_ttl are not validated by the configuration the way the bundle-wide values are. Set integers >= 1; see Open issues.
API groups
The core #[RPC\APIGroups] attribute on a class or method decides which methods become tools:
use Ufo\RpcObject\RPC;
#[RPC\Info('Catalog')]
#[RPC\APIGroups(['mcp', 'internal'], withDefault: false)]
final class CatalogProcedure implements IRpcService { /* /mcp and /mcp/internal, not /rpc */ }
#[RPC\Info('Admin')]
#[RPC\APIGroups(['internal'], withDefault: false)]
final class AdminProcedure implements IRpcService { /* only /mcp/internal */ }
The same group can be served by /rpc/... (JsonRpcBundle) and /mcp/... (this bundle) at the same time. Token checks for the group (ufo_json_rpc.security.providers) apply to MCP as well.
API versions
/mcp and /mcp/v1 serve version 1, /mcp/v2 serves version 2 of the group in the core (#[RPC\Info('Catalog', version: 2, removedMethods: [...])]; a version inherits the previous one, see API versions). A version the group does not have answers 404. A group without any method answers 404 too.
Sessions and a shared cache
Clients of the session-based protocol (before 2026-07-28) get an Mcp-Session-Id. The session is stored in the core cache pool under the key ufo_mcp_<group>_<session id> with the group's session_ttl. By default this is a local filesystem pool; when several hosts serve MCP, point it to a shared store, otherwise a session created on one host is unknown on another:
# config/packages/ufo_json_rpc.yaml
ufo_json_rpc:
cache:
pool: 'cache.app' # e.g. a shared Redis
Stateless clients of the 2026-07-28 protocol do not need session storage.