Skip to content

Conversation

camilleislasse
Copy link

@camilleislasse camilleislasse commented Sep 18, 2025

Q A
Bug fix? no
New feature? yes
Docs? yes
Issues Fix #526
License MIT

Summary

This PR migrates the MCP Bundle from the internal symfony/mcp-sdk to the official mcp/sdk package and completely removes the internal MCP SDK component.

Changes

Remove Internal MCP SDK

  • Deleted entire src/mcp-sdk/ directory
  • Removed dependency from symfony/mcp-sdk to mcp/sdk
  • Updated all references in documentation and configuration files

MCP Bundle Migration

  • Dependency change: symfony/mcp-sdkmcp/sdk in composer.json
  • Server creation: Refactored to use Server::make() builder pattern
  • Service configuration: Simplified using official SDK patterns

New MCP Capabilities Support

  • Attribute-based discovery: Added support for #[McpTool], #[McpPrompt], #[McpResource], #[McpResourceTemplate]
  • Compiler passes: Created McpToolPass, McpPromptPass, McpResourcePass, McpResourceTemplatePass
  • Autoconfiguration: Automatic service tagging and registration

Bundle Enhancements

  • EventDispatcher integration: Added Symfony EventDispatcher for MCP SDK events
  • Dedicated logger: Added monolog.logger.mcp service
  • Configuration options: Added pagination_limit and instructions parameters
  • Route loading: Fixed RouteLoader registration for SSE transport

Demo Application Updates

  • Added new MCP examples:
    • CurrentTimePrompt.php - Prompt capability
    • CurrentTimeResource.php - Resource capability
    • CurrentTimeResourceTemplate.php - Resource template capability
  • Updated tool implementation: Migrated CurrentTimeTool.php from interface to attribute
  • Enhanced configuration: Added pagination_limit, instructions

Documentation

  • rewrite of doc/index.rst with examples for all capability types
  • Event system documentation with listener examples
  • Logging configuration guide
  • Updated README and CHANGELOG to reflect official SDK usage

Testing

  • Added tests for all compiler passes
  • Updated bundle tests for new configuration options
  • Added EventDispatcher test coverage

Breaking Changes

  • Tool definition: Use #[McpTool] attribute instead of implementing interfaces
  • Dependency: Replace symfony/mcp-sdk with mcp/sdk in composer.json

Code Examples

Before (interface-based):

class Tool implements MetadataInterface, ToolExecutorInterface {
    public function call(ToolCall $request): ToolCallResult {
        return new ToolCallResult($result);
    }
    public function getName(): string { return 'tool-name'; }
}

After (attribute-based):
class Tool {
    #[McpTool(name: 'tool-name')]
    public function execute(string $param): string {
        return $result;
    }
}

New capabilities:
#[McpPrompt(name: 'analysis')]
public function getPrompt(): array { ... }

#[McpResource(uri: 'data://current', name: 'current-data')]
public function getResource(): array { ... }

#[McpResourceTemplate(uriTemplate: 'data://{id}', name: 'data-by-id')]
public function getTemplate(string $id): array { ... }

@carsonbot carsonbot added Feature New feature MCP Bundle Issues & PRs about the MCP SDK integration bundle Status: Needs Review labels Sep 18, 2025
Copy link
Member

@chr-hertel chr-hertel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thanks for working on this - minor comment for now - on top I wonder about registering the capabilities correctly to the registry via DIC - but I'm only on my phone - could be a follow up anyways 👍

@camilleislasse
Copy link
Author

camilleislasse commented Sep 18, 2025

@chr-hertel What do you think of this approach:

// Interface
interface McpToolInterface {}

// Autoconfiguration  
$container->registerForAutoconfiguration(McpToolInterface::class)->addTag('mcp.tool');

// CompilerPass
$taggedServices = $container->findTaggedServiceIds('mcp.tool');
foreach ($taggedServices as $id => $tags) {
    $container->getDefinition($id)->setPublic(true);
}

In SDK's ReferenceHandler::getClassInstance()

  private function getClassInstance(string $className): object
  {
      if (null !== $this->container && $this->container->has($className)) {
          return $this->container->get($className); 
      }

      return new $className();  
  }

Our CompilerPass makes services public so the SDK's ReferenceHandler can use Symfony's DIC.

@chr-hertel
Copy link
Member

Goal would be to use the attributes, that are already around in the SDK - and we can use a service locator to skip making the public

@camilleislasse
Copy link
Author

Goal would be to use the attributes, that are already around in the SDK - and we can use a service locator to skip making the public

I'm not entirely sure we need to go through the mcp.tool tag intermediate step.

Currently the flow is:

  1. #[McpTool] attribute → auto-tagged as mcp.tool
  2. Compiler pass finds tagged services → creates Service Locator

We could potentially scan for #[McpTool] attributes directly in the compiler pass instead of using tags.

Pros

  • Separation of concerns between autoconfiguration and service locator creation
  • Allows manual tagging if needed
  • Built-in debuggability with debug:container --tag=mcp.tool

Pros of direct attribute scanning:

  • One less intermediate step
  • More direct connection between attribute and service locator

@OskarStark
Copy link
Contributor

I started some work here #460 (mostly vibe coding, so maybe ignore the code changes), but check the docs and namespace changes, maybe it can help you.

@OskarStark OskarStark changed the title [MCP Bundle] Migrate from symfony/mcp-sdk to official mcp/sdk [MCP Bundle] Migrate from symfony/mcp-sdk to official mcp/sdk Sep 19, 2025
@OskarStark
Copy link
Contributor

You should also remove the symfony/mcp-sdk completely here in this PR

@camilleislasse camilleislasse force-pushed the fix/migrate-mcp-bundle-to-official-sdk branch from caea9d8 to d916690 Compare September 19, 2025 07:14
namespace Symfony\AI\McpBundle\Routing;

use Symfony\AI\McpBundle\Exception\LogicException;
use Symfony\Component\Config\Loader\Loader;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the right class?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use maybe Symfony\Component\Routing\Exception\LogicException ?

In CLAUDE.MD :

Use project specific exceptions instead of global exception classes like \RuntimeException, \InvalidArgumentException etc.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@camilleislasse
Copy link
Author

Could you please do a complete review? I've added in addition to the tool :

Feels like I'm going in all directions 😅 I'm not really sure what the bundle should handle vs what the SDK should handle.

@chr-hertel
Copy link
Member

Two things, besides the rebase, make sense still:

@camilleislasse
Copy link
Author

a34e6c4

Copy link
Member

@chr-hertel chr-hertel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @camilleislasse for kicking this off - will merge it now and we can iterate for leveling up 👍

@chr-hertel chr-hertel force-pushed the fix/migrate-mcp-bundle-to-official-sdk branch 3 times, most recently from 8e8ede0 to 65402c5 Compare September 24, 2025 20:26
@chr-hertel
Copy link
Member

Okay, we'll need to wait for modelcontextprotocol/php-sdk#79, but let's work with new PRs on top of this - instead of making this bigger.

- Replace symfony/mcp-sdk dependency with official mcp/sdk
- Refactor Server creation to use Server::make() builder pattern
- Convert tool definitions from interfaces to #[McpTool] attributes
- Implement automatic tool discovery in src/ directory
- Update STDIO transport command to mcp:server
- Simplify service configuration using native SDK patterns
- Remove redundant ServerFactory and routes.php files
- Add custom LogicException for bundle-specific errors
- Update all documentation (README, CHANGELOG, index.rst)
- Fix composer.json description
- Add .idea/ to .gitignore

Resolves symfony#526

Fabbot + php-cs-fixer

DOCtor-RST

 Fix RouteLoader registration to always create when transports enabled

Remove .idea

  - Add autoconfiguration for #[McpTool] attribute with mcp.tool tag
  - Create McpToolPass compiler pass using ServiceLocatorTagPass::register()
  - Inject Service Locator into ServerBuilder via setContainer()

Fix PR review comments for MCP Bundle migration

- Remove prefer-stable from root composer.json
- Move CHANGELOG content to 0.1 section and remove BC BREAK labels
- Use ServerBuilder::class and Server::class in services configuration
- Fix "client vs server" comment in documentation
- Use ServerBuilder::class in test instead of string

Remove symfony/mcp-sdk

Remove symfony/mcp-sdk ref in claude reamdde et phpstan

Rewrite CHANGELOG.md

Use [] === $taggedServices instead of empty($taggedServices)

Use use Symfony\Component\Routing\Exception\LogicException; instead of custom Exception

Add MCP capabilities support to Symfony bundle

This commit implements Model Context Protocol (MCP) support by adding:

- Prompts: System instructions for AI context using #[McpPrompt]
- Resources: Static data access using #[McpResource]
- Resource Templates: Dynamic resources with parameters using #[McpResourceTemplate]

Implementation includes:
- Auto-configuration for MCP attributes with proper tagging
- Compiler passes extending AbstractMcpPass for service registration
- Documentation updates with examples and usage patterns
- Demo examples showcasing each capability type

Technical details:
- Refactored auto-configuration into registerMcpAttributes() method
- Created AbstractMcpPass to eliminate code duplication
- Added proper error handling for timezone validation
- Resource Templates ready but await MCP SDK handler implementation

Apply PHP CS Fixer to demo MCP examples

- Add Symfony license headers to demo files
- Fix code style and formatting
- Add trailing commas where appropriate

Add pagination_limit and instructions configuration options

- Add pagination_limit option to control MCP list responses (default: 50)
- Add instructions option for server description to help LLMs
- Update services.php to pass both options to ServerBuilder
- Add comprehensive tests for new configuration options
- Update documentation with examples and usage
- Update demo configuration with practical examples

Both options map directly to ServerBuilder methods:
- setPaginationLimit(int)
- setInstructions(string)

Add dedicated MCP logger with configurable Monolog integration

- Create monolog.logger.mcp service with dedicated channel
- Update ServerBuilder to use MCP-specific logger instead of generic logger
- Add comprehensive logging documentation with configuration examples
- Include examples for different environments (dev/prod) and handlers (file/Slack)
- Add test coverage for MCP logger service creation and configuration

The MCP logger uses Monolog's logger prototype pattern and can be customized
by users through standard Monolog configuration in their applications.

  Add EventDispatcher support and event system documentation

  - Configure Symfony EventDispatcher for MCP SDK event handling
  - Add comprehensive event system documentation with examples
  - Add test coverage for EventDispatcher configuration
  - Fix PHPStan type assertion for ChildDefinition

Migrate MCP Bundle from SSE to official StreamableHttpTransport

- Replace custom SSE implementation with MCP SDK's StreamableHttpTransport
- Change sse transport to http in configuration and code
- Simplify DependencyInjection compiler passes
- Add HTTP session management (file/memory store options)
- Update documentation and tests for HTTP transport
@chr-hertel chr-hertel force-pushed the fix/migrate-mcp-bundle-to-official-sdk branch from 65402c5 to 1382d4c Compare September 24, 2025 20:36
@chr-hertel
Copy link
Member

Thank you @camilleislasse.

@chr-hertel chr-hertel merged commit 313cfa5 into symfony:main Sep 24, 2025
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature New feature MCP Bundle Issues & PRs about the MCP SDK integration bundle Status: Reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MCP Bundle] Adopt official MCP SDK

4 participants