Search by

adachsoft / ai-tool-mcp-bundle

Arkadiusz Adach

Symfony bundle exposing selected adachsoft/ai-tool-call tools as an MCP (Model Context Protocol) server over HTTP and stdio.

Package info

gitlab.com/a.adach/ai-tool-mcp-bundle

Issues

Type:symfony-bundle

pkg:composer/adachsoft/ai-tool-mcp-bundle

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

v0.1.0 2026-11-09 08:02 UTC

This package is auto-updated.

Last update: 2026-11-09 07:10:49 UTC


README

adachsoft/ai-tool-mcp-bundle is a Symfony bundle that turns any Symfony application into an MCP (Model Context Protocol) server exposing selected adachsoft/ai-tool-call tools.

Any ToolInterface implementation with a ToolFactoryInterface can be exposed by adding a few lines of configuration. Tools are created by factories from the host container, so they can use the host's repositories and services directly.

Features

  • MCP methods: initialize (with protocol version negotiation), ping, tools/list, tools/call.
  • Protocol versions: 2025-06-18, 2025-03-26, 2024-11-05.
  • Two transports sharing one core:
    • Streamable HTTP: a single POST endpoint returning JSON responses.
    • stdio: a console command for local clients (Claude Code, Claude Desktop, IDEs).
  • Explicit allow-list: only configured tools are listed or callable.
  • Tool failures (InvalidToolCallException, ToolExecutionException) are returned as results with isError: true, so the model can read the message and correct its call. Unexpected exceptions are logged and reported without internal details.
  • Lazy tool creation: a factory is invoked on the first call of its tool, never during tools/list.
  • No authentication logic of its own. The HTTP endpoint is protected by the host application's firewall.

Requirements

  • PHP 8.3+
  • Symfony 7.3+ or 8.x
  • adachsoft/ai-tool-call ^2.0

Installation

composer require adachsoft/ai-tool-mcp-bundle

Register the bundle (Symfony Flex does it automatically):

// config/bundles.php
return [
    // ...
    AdachSoft\AiToolMcpBundle\AiToolMcpBundle::class => ['all' => true],
];

Configuration

# config/packages/ai_tool_mcp.yaml
ai_tool_mcp:
    server:
        name: task-api                    # reported in initialize/serverInfo
        version: 1.0.0
        instructions: 'Manage tasks: create, read, tree, statuses and comments.'   # optional
    http:
        allowed_origins: []               # browser origins allowed to call the endpoint
    tools:
        # Key = exact tool name returned by ToolInterface::getDefinition()
        task:
            factory: AdachSoft\TaskTool\Tool\TaskToolFactory   # service id of a ToolFactoryInterface
            config:                                            # passed to the factory as ConfigMap
                max_title_length: 120
                base_path: '%kernel.project_dir%'              # container parameters and env vars are resolved
KeyDescription
server.name, server.versionServer identity returned by initialize.
server.instructionsOptional usage instructions for the model.
http.allowed_originsRequests with an Origin header not on this list get 403 (DNS rebinding protection). Requests without Origin (non-browser clients) are always accepted.
tools.<name>.factoryService id of an AdachSoft\AiToolCall\SPI\Factory\ToolFactoryInterface. The service must exist in the host container.
tools.<name>.configMap passed to ToolFactoryInterface::create() as ConfigMap. Keys must follow ConfigMap rules (lowercase, digits, _, optional . segments).

The configuration key must equal the tool name from getDefinition(); a mismatch raises McpToolConfigurationException. Tools whose definition has enabled: false are neither listed nor callable.

Registering the tool factory

The factory is resolved from the host container, so register it as a service if autowiring does not already cover it:

# config/services.yaml
services:
    AdachSoft\TaskTool\Tool\TaskToolFactory:
        arguments:
            $clock: null

HTTP transport

Import the bundle routes. The endpoint path is /mcp; use a prefix to move it, e.g. under a firewall:

# config/routes/ai_tool_mcp.yaml
ai_tool_mcp:
    resource: '@AiToolMcpBundle/config/routes.php'
    prefix: /api            # endpoint: POST /api/mcp

Behaviour:

  • POST with one JSON-RPC message: 200 + JSON response, or 202 with an empty body for notifications.
  • GET and other methods: 405 (no server-initiated SSE streams).
  • JSON-RPC batches are not supported (removed in MCP 2025-06-18).

Authentication is the host's responsibility. Place the route under an existing firewall, for example:

# config/packages/security.yaml
security:
    firewalls:
        api:
            pattern: ^/api/
            stateless: true
            custom_authenticators: [App\Security\ApiKeyAuthenticator]

Connecting Claude Code to an HTTP server:

claude mcp add --transport http tasks https://example.com/api/mcp --header "X-API-Key: <key>"

stdio transport

php bin/console ai-tool-mcp:serve-stdio

The command reads newline-delimited JSON-RPC messages from stdin and writes exactly one line per request to stdout. Stdout carries protocol messages only. Make sure the application does not log to stdout in the environment you run the command in (log to stderr or a file instead).

Connecting Claude Code to a local project:

claude mcp add tasks -- php /path/to/project/bin/console ai-tool-mcp:serve-stdio --env=prod

Running with --env=prod and a warmed cache keeps startup fast; the process stays alive for the whole session.

Tool results

  • Success: {"content": [{"type": "text", "text": "<result as JSON>"}], "isError": false}. The tool's ToolCallResultDto::$result map is encoded as JSON (Unicode and slashes unescaped).
  • Tool error: {"content": [{"type": "text", "text": "<exception message>"}], "isError": true}.
  • Unknown tool or invalid tools/call parameters: JSON-RPC error -32602.
  • Unknown method: JSON-RPC error -32601.

Development

composer test     # PHPUnit (unit + HTTP and stdio integration tests on a test kernel)
composer stan     # PHPStan
composer cs:fix   # PHP CS Fixer
composer rector   # Rector

License

MIT