Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions config/packages/mcp.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ mcp:
http: true
http:
path: /mcp
# Hostnames the Streamable-HTTP transport's DNS-rebinding guard accepts in
# the Host/Origin header (no port; IPv6 bracketed). The SDK default is
# localhost-only, which 403s a real domain — so every deployment must list
# its public domain in MCP_ALLOWED_HOSTS. Never `false` (that would disable
# the guard entirely).
allowed_hosts: '%env(csv:MCP_ALLOWED_HOSTS)%'
23 changes: 0 additions & 23 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ parameters:
ldap_usernamefield: '%env(LDAP_USERNAMEFIELD)%'
ldap_usessl: '%env(bool:LDAP_USESSL)%'
ldap_create_user: '%env(bool:LDAP_CREATE_USER)%'
# MCP server (ADR-021 Phase 5): hostnames the /mcp Streamable-HTTP transport's
# DNS-rebinding guard accepts in the Host/Origin header. Must include the public
# domain (the SDK default is localhost-only, which 403s a real host).
app.mcp_allowed_hosts: '%env(csv:MCP_ALLOWED_HOSTS)%'

imports:
- { resource: services/object_mapper.yaml }
Expand Down Expand Up @@ -56,31 +52,12 @@ services:
- '../src/Service/Integration/Jira/JiraTicketService.php'
- '../src/Service/Integration/Jira/JiraAuthenticationService.php'
- '../src/Service/Integration/Jira/JiraIntegrationService.php'
# Wired explicitly below (overrides the bundle's mcp.server.controller).
- '../src/Mcp/McpEndpointController.php'

# Controllers are tagged automatically
App\Controller\:
resource: '../src/Controller/'
tags: ['controller.service_arguments']

# ADR-021 Phase 5: override the bundle's mcp.server.controller (final, hardcodes
# localhost-only DNS-rebinding hosts) with ours, host-allowlisted. Same args as
# Symfony\AI\McpBundle McpBundle::loadExtension registers, plus the allowed hosts.
mcp.server.controller:
class: App\Mcp\McpEndpointController
public: true
arguments:
- '@mcp.server'
- '@mcp.psr_http_factory'
- '@mcp.http_foundation_factory'
- '@mcp.psr17_factory'
- '@mcp.psr17_factory'
- '%app.mcp_allowed_hosts%'
- '@logger'
tags:
- 'controller.service_arguments'

# Add this if you use repositories
App\Repository\:
resource: '../src/Repository/'
Expand Down
76 changes: 0 additions & 76 deletions src/Mcp/McpEndpointController.php

This file was deleted.

10 changes: 8 additions & 2 deletions src/Security/ApiToken/RequireScopeSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use App\ValueObject\ApiScope;
use ReflectionMethod;
use Symfony\AI\McpBundle\Controller\McpController;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
Expand Down Expand Up @@ -55,8 +56,13 @@ public function onKernelController(ControllerEvent $event): void

$controller = $event->getController();
$controllerObject = is_array($controller) ? $controller[0] : (is_object($controller) ? $controller : null);
if ($controllerObject instanceof SelfEnforcesScope) {
return; // controller enforces scopes per call (e.g. the MCP endpoint)
// The MCP endpoint multiplexes many tools, each requiring a different scope
// checked in its handler (App\Mcp\ScopeGuard), so a single controller-level
// #[RequireScope] cannot express its requirement. It is the one controller
// that takes responsibility for enforcing scopes on every path, so the
// fail-closed default below is bypassed for it — and only for it.
if ($controllerObject instanceof McpController) {
return;
}

$required = $this->requiredScope($controller);
Expand Down
26 changes: 0 additions & 26 deletions src/Security/ApiToken/SelfEnforcesScope.php

This file was deleted.

4 changes: 2 additions & 2 deletions tests/Mcp/McpHttpEndpointTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@

/**
* Functional tests for the /mcp HTTP endpoint (ADR-021 Phase 5), driving the full
* stack — the Bearer-PAT firewall, our McpEndpointController, and the SDK's
* stack — the Bearer-PAT firewall, the bundle's McpController, and the SDK's
* Streamable-HTTP transport middleware (incl. the DNS-rebinding Host guard the
* direct tool tests bypass). Regression cover for the localhost-only default that
* 403'd a real domain until McpEndpointController allowlisted the host.
* 403'd a real domain until mcp.http.allowed_hosts allowlisted the host.
*/
final class McpHttpEndpointTest extends AbstractWebTestCase
{
Expand Down
Loading