Skip to content

Conversation

@soyuka
Copy link
Member

@soyuka soyuka commented Dec 9, 2025

Q A
Branch? main
Tickets na
License MIT
Doc PR needed

Rework of #7458

Now with a simpler approach, this gives the ability to add McpTool (upcoming McpResource) to any API Platform environment giving access to:

  • automatic input JSON Schema's (waiting for feat: Add output schema support to MCP tools modelcontextprotocol/php-sdk#153 for output schema compatiblity)
  • automatic structured content
  • reuses the Symfony serializer under the hood (with all the capabilities of API Platform including optional iris using item_uri_template)
  • allows to return a custom CallToolResult (from the php-sdk itself for images, sound etc.)
  • ability to reuse Symfony validation constraints as usual
  • works with doctrine entities if needed (calls the provider and the processor on demand)
  • integrates with the symfony security features

TODO:

  • test with #[McpTool] attribute
  • test using an entity
  • test custom CallToolResult content
  • test validation
  • test #[McpResource]

Because MCP uses JSON-RPC under the hood with some particular response objects (from php-sdk) we can not change returned headers yet (we'll work on an extension point).

@soyuka soyuka force-pushed the mcp-bundle-integration branch 3 times, most recently from d136520 to b904a0d Compare January 12, 2026 08:37
Copy link
Contributor

@rvanlaak rvanlaak left a comment

Choose a reason for hiding this comment

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

Exciting that the mcp repo's schema PR is merged now, and you're able to spend time on API Platform integration.

Tried reviewing this PR without checking it out and testing it, so please feel free to ignore all my comments / suggestions / nitpicks 🙌 Just trying to provide you with another pov at some classes.

@soyuka
Copy link
Member Author

soyuka commented Jan 12, 2026

Exciting that the mcp repo's schema PR is merged now, and you're able to spend time on API Platform integration.

Yeah it was the missing part + now I can also test out to use only the json-schema component's of API Platform to produce MCP SDK's schemas!

OskarStark added a commit to symfony/ai that referenced this pull request Jan 13, 2026
This PR was merged into the main branch.

Discussion
----------

[MCP Bundle] bump php-sdk to 0.3

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Docs?         | no
| Issues        | api-platform/core#7595
| License       | MIT

Commits
-------

ce5758f bump php-sdk to 0.3
symfony-splitter pushed a commit to symfony/mcp-bundle that referenced this pull request Jan 13, 2026
This PR was merged into the main branch.

Discussion
----------

[MCP Bundle] bump php-sdk to 0.3

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Docs?         | no
| Issues        | api-platform/core#7595
| License       | MIT

Commits
-------

ce5758fc bump php-sdk to 0.3
@soyuka soyuka force-pushed the mcp-bundle-integration branch 5 times, most recently from 192498f to 7ff8ba8 Compare January 14, 2026 10:55
@soyuka soyuka force-pushed the mcp-bundle-integration branch 3 times, most recently from 13f91e9 to cc5f55c Compare January 15, 2026 12:18
@soyuka soyuka requested a review from Copilot January 15, 2026 20:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR integrates MCP (Model Context Protocol) tool and resource capabilities into API Platform, enabling developers to expose their API resources as MCP tools and resources with automatic JSON Schema generation, structured content support, and seamless integration with Symfony's serializer and validation systems.

Changes:

  • Added McpTool and McpResource metadata classes that extend HttpOperation for defining MCP capabilities
  • Implemented MCP-specific state providers and processors for handling tool calls and resource reads
  • Added comprehensive test coverage for MCP functionality including validation, custom results, and markdown content

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php Added test assertion for new mcp.enabled configuration option
tests/Functional/McpTest.php Added comprehensive functional tests covering MCP tools, resources, validation, and content handling
tests/Fixtures/app/config/routing_test.php Added MCP route imports and removed outdated comments
tests/Fixtures/app/config/config_common.yml Added MCP bundle configuration with HTTP transport and session settings
tests/Fixtures/app/AppKernel.php Registered McpBundle and MonologBundle when MCP is available
tests/Fixtures/TestBundle/Entity/McpBook.php Created test entity demonstrating MCP tool integration with Doctrine entities
tests/Fixtures/TestBundle/ApiResource/McpWithMarkdown.php Created test resource for markdown content generation
tests/Fixtures/TestBundle/ApiResource/McpTools.php Created test resource demonstrating custom results and validation
tests/Fixtures/TestBundle/ApiResource/McpToolAttribute.php Created test resource using #[McpTool] attribute
tests/Fixtures/TestBundle/ApiResource/McpResourceExample.php Created test resource using #[McpResource] attribute
src/Symfony/Bundle/Resources/config/mcp/state.php Configured MCP state processors and handler service
src/Symfony/Bundle/Resources/config/mcp/mcp.php Configured core MCP services including loader and IRI converter
src/Symfony/Bundle/Resources/config/mcp/events.php Configured MCP event-based state processing
src/Symfony/Bundle/DependencyInjection/Configuration.php Added mcp configuration section
src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php Registered MCP metadata classes and loaded MCP service configuration
src/Metadata/Tests/Extractor/Adapter/resources.yaml Added mcp field to test resources
src/Metadata/Tests/Extractor/Adapter/XmlResourceAdapter.php Added support for building MCP operations in XML
src/Metadata/Resource/Factory/MetadataCollectionFactoryTrait.php Added logic to process MCP operations during resource metadata building
src/Metadata/McpTool.php Created metadata class for defining MCP tools
src/Metadata/McpResource.php Created metadata class for defining MCP resources
src/Metadata/ApiResource.php Added mcp property to store MCP operations
src/Mcp/State/ToolProvider.php Implemented state provider for MCP tool requests
src/Mcp/State/StructuredContentProcessor.php Implemented processor for generating structured MCP responses
src/Mcp/Server/Handler.php Implemented MCP request handler
src/Mcp/Routing/IriConverter.php Added IRI converter decorator for MCP operations
src/Mcp/Metadata/Operation/Factory/OperationMetadataFactory.php Implemented factory for retrieving MCP operation metadata
src/Mcp/Capability/Registry/Loader.php Implemented loader for registering MCP tools and resources
composer.json Added MCP SDK and bundle dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@soyuka soyuka force-pushed the mcp-bundle-integration branch from 320f687 to f750e9c Compare January 15, 2026 20:57
"symfony/intl": "^6.4 || ^7.0 || ^8.0",
"symfony/json-streamer": "^7.4 || ^8.0",
"symfony/maker-bundle": "^1.24",
"symfony/mcp-bundle": "dev-main",
Copy link
Contributor

Choose a reason for hiding this comment

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

There is available tags now
https://github.com/symfony/mcp-bundle/tags

/**
* @throws RuntimeException
*
* @return HttpOperation
Copy link
Contributor

Choose a reason for hiding this comment

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

Then you could use it as native typehint no ?

* @param mixed|null $messenger
* @param mixed|null $input
* @param mixed|null $output
* @param array<int, HttpOperation>|array<string, HttpOperation>|Operations|null $operations Operations is a list of HttpOperation
Copy link
Contributor

Choose a reason for hiding this comment

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

array<int, HttpOperation>|array<string, HttpOperation>

could basically be resume to HttpOperation[]

}
}

public function getMcp(): ?array
Copy link
Contributor

Choose a reason for hiding this comment

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

@return @param array<string, McpResource|McpTool>|null

return $this->mcp;
}

public function withMcp(array $mcp): static
Copy link
Contributor

Choose a reason for hiding this comment

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

@param array<string, McpResource|McpTool> $mcp

return $this->meta;
}

public function withMeta(?array $meta): static
Copy link
Contributor

Choose a reason for hiding this comment

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

array<string, mixed>|null

return $self;
}

public function getMeta(): ?array
Copy link
Contributor

Choose a reason for hiding this comment

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

array<string, mixed>|null

return $this->meta;
}

public function withMeta(?array $meta): static
Copy link
Contributor

Choose a reason for hiding this comment

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

array<string, mixed>|null

foreach ($values as $value) {
$child = $node->addChild('mcpOperation');
foreach ($value as $index => $data) {
if (method_exists($this, 'build'.ucfirst($index))) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if $index is numeric ? Is it possible ?
Since it's an array key, it could be casted to int and then ucfirst($int) will crash.

$child = $node->addChild('mcpOperation');
foreach ($value as $index => $data) {
if (method_exists($this, 'build'.ucfirst($index))) {
$this->{'build'.ucfirst($index)}($child, $data);
Copy link
Contributor

Choose a reason for hiding this comment

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

you could put 'build'.ucfirst($index) into a variable eventually

Copy link
Member

@dunglas dunglas left a comment

Choose a reason for hiding this comment

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

Partial review

meta: $mcp->getMeta()
),
self::HANDLER,
true
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
true
true,

return $operation;
}

if ($operation instanceof McpResource && $operation->getUri() === $operationName) {
Copy link
Member

Choose a reason for hiding this comment

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

Nit: can be merged with the previous condition.

"symfony/mcp-bundle": "dev-main",
"symfony/mercure-bundle": "*",
"symfony/messenger": "^6.4 || ^7.0 || ^8.0",
"symfony/monolog-bundle": "^4.0",
Copy link
Member

Choose a reason for hiding this comment

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

Why?

outputSchema: $outputSchema->getDefinitions()[$outputSchema->getRootDefinitionKey()]->getArrayCopy(),
),
self::HANDLER,
true
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
true
true,


declare(strict_types=1);

/*
Copy link
Member

Choose a reason for hiding this comment

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

To adapt or remove?


declare(strict_types=1);

/*
Copy link
Member

Choose a reason for hiding this comment

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

To adapt or remove?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants