Skip to content

Commit 55d1842

Browse files
committed
feat: setup MCP bundle
0 parents  commit 55d1842

File tree

12 files changed

+266
-0
lines changed

12 files changed

+266
-0
lines changed

.github/workflows/pipeline.yml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: pipeline
2+
on: pull_request
3+
4+
permissions:
5+
contents: read
6+
pull-requests: write
7+
8+
jobs:
9+
qa:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Conventional Commit
16+
uses: ytanikin/[email protected]
17+
with:
18+
task_types: '["feat", "fix", "docs", "test", "ci", "style", "refactor", "perf", "chore", "revert"]'
19+
add_label: 'true'
20+
custom_labels: '{"feat": "feature", "fix": "bug", "docs": "documentation", "test": "test", "ci": "CI/CD", "style": "codestyle", "refactor": "refactor", "perf": "performance", "chore": "chore", "revert": "revert"}'
21+
22+
- name: Setup PHP
23+
uses: shivammathur/setup-php@v2
24+
with:
25+
php-version: '8.2'
26+
27+
- name: Install Composer
28+
uses: "ramsey/composer-install@v3"
29+
30+
- name: Composer Validation
31+
run: composer validate --strict
32+
33+
- name: Install PHP Dependencies
34+
run: composer install --no-scripts
35+
36+
- name: Code Style PHP
37+
run: vendor/bin/php-cs-fixer fix --dry-run
38+
39+
- name: Rector
40+
run: vendor/bin/rector --dry-run
41+
42+
- name: PHPStan
43+
run: vendor/bin/phpstan analyse

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor
2+
composer.lock
3+
.php-cs-fixer.cache

.php-cs-fixer.dist.php

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
$finder = (new PhpCsFixer\Finder())
4+
->in(__DIR__)
5+
;
6+
7+
return (new PhpCsFixer\Config())
8+
->setRules([
9+
'@Symfony' => true,
10+
])
11+
->setFinder($finder)
12+
;

LICENSE

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2025 Christopher Hertel
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# MCP Bundle
2+
3+
Symfony integration bundle for [Model Context Protocol](https://modelcontextprotocol.io/) using the unofficial
4+
PHP SDK [php-llm/mcp-sdk](https://github.com/php-llm/mcp-sdk) library.
5+
6+
## Installation
7+
8+
```bash
9+
composer require php-llm/mcp-bundle
10+
```
11+
12+
## Usage
13+
14+
At first, you need to decide whether your application should act as a MCP server or client. Both can be configured
15+
in the `mcp` section of your `config/packages/mcp.yaml` file.
16+
17+
### Act as Client
18+
19+
To use your application as a MCP client, integrating other MCP servers, you need to configure the `servers` you want to
20+
connect to. You can use either STDIO or Server-Sent Events (SSE) as transport methods.
21+
22+
You can find a list of example Servers in the [MCP Server List](https://modelcontextprotocol.io/examples).
23+
24+
Tools of those servers are available in your [LLM Chain Bundle](https://github.com/php-llm/llm-chain-bundle)
25+
configuration and usable in your chains.
26+
27+
### Act as Server
28+
29+
To use your application as a MCP server, exposing tools to clients like [Claude Desktop](https://claude.ai/download),
30+
you need to configure in the `client` section the transports you want to use. You can use either STDIO or SSE.
31+
32+
## Configuration
33+
34+
```yaml
35+
mcp:
36+
app: 'app' # Application name to be exposed to clients
37+
version: '1.0.0' # Application version to be exposed to clients
38+
39+
# Configure MCP servers to be used by this application
40+
servers:
41+
name:
42+
transport: 'stdio' # Transport method to use, either 'stdio' or 'sse'
43+
stdio:
44+
command: 'php /path/bin/console mcp' # Command to execute to start the client
45+
arguments: [] # Arguments to pass to the command
46+
sse:
47+
url: 'http://localhost:8000/sse' # URL to SSE endpoint of MCP server
48+
49+
# Configure this application to act as a MCP server
50+
client:
51+
transports:
52+
stdio: true # Enable STDIO via command
53+
sse: true # Enable Server-Sent Event via controller
54+
```

composer.json

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "php-llm/mcp-bundle",
3+
"type": "symfony-bundle",
4+
"description": "Symfony integration bundle for Model Context Protocol (via php-llm/mcp-sdk)",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Christopher Hertel",
9+
"email": "[email protected]"
10+
}
11+
],
12+
"require": {
13+
"php-llm/mcp-sdk": "dev-main",
14+
"symfony/config": "^6.4 || ^7.0",
15+
"symfony/dependency-injection": "^6.4 || ^7.0",
16+
"symfony/framework-bundle": "^6.4 || ^7.0"
17+
},
18+
"require-dev": {
19+
"php-cs-fixer/shim": "dev-master",
20+
"phpstan/phpstan": "2.1.x-dev",
21+
"rector/rector": "dev-main"
22+
},
23+
"config": {
24+
"sort-packages": true
25+
},
26+
"autoload": {
27+
"psr-4": {
28+
"PhpLlm\\McpBundle\\": "src/"
29+
}
30+
},
31+
"minimum-stability": "dev",
32+
"prefer-stable": true
33+
}

phpstan.dist.neon

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
parameters:
2+
level: 6
3+
paths:
4+
- src/
5+
excludePaths:
6+
analyse:
7+
- src/DependencyInjection/Configuration.php

rector.php

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector;
7+
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitSelfCallRector;
8+
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector;
9+
use Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertCountWithZeroToAssertEmptyRector;
10+
use Rector\PHPUnit\Set\PHPUnitSetList;
11+
12+
return RectorConfig::configure()
13+
->withPaths([
14+
__DIR__.'/src',
15+
])
16+
->withPhpSets(php82: true)
17+
->withSets([
18+
PHPUnitSetList::PHPUNIT_110,
19+
PHPUnitSetList::ANNOTATIONS_TO_ATTRIBUTES,
20+
PHPUnitSetList::PHPUNIT_CODE_QUALITY,
21+
])
22+
->withRules([
23+
PreferPHPUnitSelfCallRector::class,
24+
])
25+
->withImportNames(importShortClasses: false)
26+
->withSkip([
27+
AssertCountWithZeroToAssertEmptyRector::class,
28+
ClosureToArrowFunctionRector::class,
29+
PreferPHPUnitThisCallRector::class,
30+
])
31+
->withTypeCoverageLevel(0);
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\McpBundle\DependencyInjection;
6+
7+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
8+
use Symfony\Component\Config\Definition\ConfigurationInterface;
9+
10+
final class Configuration implements ConfigurationInterface
11+
{
12+
public function getConfigTreeBuilder(): TreeBuilder
13+
{
14+
$treeBuilder = new TreeBuilder('mcp');
15+
$rootNode = $treeBuilder->getRootNode();
16+
17+
//$rootNode
18+
19+
return $treeBuilder;
20+
}
21+
}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\McpBundle\DependencyInjection;
6+
7+
use Symfony\Component\Config\FileLocator;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
use Symfony\Component\DependencyInjection\Extension\Extension;
10+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
11+
12+
final class McpExtension extends Extension
13+
{
14+
public function load(array $configs, ContainerBuilder $container): void
15+
{
16+
$loader = new PhpFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources/config'));
17+
$loader->load('services.php');
18+
19+
$configuration = new Configuration();
20+
$config = $this->processConfiguration($configuration, $configs);
21+
22+
}
23+
}

src/McpBundle.php

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\McpBundle;
6+
7+
use Symfony\Component\HttpKernel\Bundle\Bundle;
8+
9+
final class McpBundle extends Bundle
10+
{
11+
}

src/Resources/services.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
6+
7+
return static function (ContainerConfigurator $container): void {
8+
9+
}

0 commit comments

Comments
 (0)