Installing MCP Bundle
Requirements for version 1.x: PHP >= 8.4, Symfony ^7.2 or ^8.0, ufo-tech/json-rpc-bundle ^10.4.3, mcp/sdk ^0.8.1. The JsonRpcBundle server must already be installed.
Step 1: Installation
composer require ufo-tech/mcp-bundle
Step 2: Registering the bundle
Add the bundle to config/bundles.php next to JsonRpcBundle:
return [
// ...
Ufo\JsonRpcBundle\UfoJsonRpcBundle::class => ['all' => true],
Ufo\McpBundle\UfoMcpBundle::class => ['all' => true],
];
Step 3: Configuration
There is no Symfony Flex recipe for the bundle yet: the configuration files are copied by hand.
Copy vendor/ufo-tech/mcp-bundle/install/packages/ufo_mcp.yaml to config/packages/. Minimal configuration:
# config/packages/ufo_mcp.yaml
ufo_mcp:
enabled: true
mcp_server_info:
name: 'io.github.your-org/your-app'
title: 'Your App MCP Server'
Without the file the bundle runs on defaults. All parameters are on the Configuration page.
Step 4: Registering routes
Copy vendor/ufo-tech/mcp-bundle/install/routes/ufo_mcp.yaml to config/routes/:
# config/routes/ufo_mcp.yaml
ufo_mcp:
resource: ../../vendor/ufo-tech/mcp-bundle/config/router.yaml
prefix: /mcp
defaults: { api_group: mcp }
One import is one MCP endpoint serving one API group (api_group). To add another endpoint, import the same router.yaml again with a different prefix and api_group; see Routes.
Step 5: Methods for MCP
Add RPC methods to the endpoint's group with the #[RPC\APIGroups] attribute:
use Ufo\JsonRpcBundle\ApiMethod\Interfaces\IRpcService;
use Ufo\McpBundle\Attribute\McpTool;
use Ufo\RpcObject\RPC;
#[RPC\Info('Catalog')]
#[RPC\APIGroups(['mcp'], withDefault: false)] // only /mcp, not /rpc
final class CatalogProcedure implements IRpcService
{
/** Search products by name. */
#[McpTool(readOnlyHint: true)]
public function search(string $query, int $limit = 10): array
{
// ...
}
}
withDefault: false removes the methods from the default group (/rpc); without it the methods are available both on /rpc and on /mcp.
#[McpTool] is optional: it only adds hints for the agent. Every method of the group becomes a tool; see Tools.
Verification
| Request | What it returns |
|---|---|
POST /mcp |
The MCP endpoint (Streamable HTTP) |
GET /mcp/manifest/claude.json |
A connection config for Claude Code (also cursor, gemini, openai) |
GET /mcp/server.json |
The document for the MCP Registry |
The Claude Code manifest:
Request:
curl https://example.com/mcp/manifest/claude.json
Response:
{
"mcpServers": {
"mcp": {
"type": "http",
"url": "https://example.com/mcp",
"headers": {"Ufo-RPC-Token": "${UFO_MCP_TOKEN}"}
}
}
}
If protected_doc is enabled in JsonRpcBundle, add the token header to the request. Save the response as the project's .mcp.json and set the UFO_MCP_TOKEN environment variable: Claude Code will see the group's tools.
To list the tools without an AI client, use MCP Inspector:
npx -y @modelcontextprotocol/inspector --cli https://example.com/mcp \
--transport http --header "Ufo-RPC-Token: change-me" --method tools/list
What next?
- Configuration: several endpoints, versions, sessions.
- Tools: how schemas, texts and results are built.
- HTTP and security: tokens, CORS, DNS rebinding.