klapaudius/symfony-mcp-server
Symfony MCP Server lets you expose your Symfony app's tools, resources, and prompts to AI clients via the Model Context Protocol. Register capabilities with attributes or config, add MCP endpoints, and connect Claude and other MCP-compatible assistants to your PHP backend.
Architecture fit. klapaudius/symfony-mcp-server is a Symfony-oriented implementation of the Model Context Protocol (server side): it exposes tools/resources to LLM clients over JSON-RPC. The architectural mismatch with Laravel is the primary concern — it ships as a Symfony bundle with bundle lifecycle, DI extension, and Symfony-native attribute/config discovery. Laravel has no bundle concept, so integration means writing a service provider that boots the bundle's container definitions manually, or running the MCP server as a separate Symfony process alongside the Laravel app. The 1.9.0 addition — oneOf/anyOf/allOf composition in tool schemas — is a schema-generation-layer feature, which sits on the boundary between our PHP type declarations and the JSON Schema sent to the model; that layer is exactly where we would otherwise be injecting our own adapter, so this release reduces custom glue code for union/intersection-shaped inputs.
Integration feasibility. Feasible but not turnkey. The realistic pattern is: Laravel app owns domain logic, a thin adapter maps Laravel service bindings into the Symfony container, and tool definitions are declared with the package's schema builder. Symfony components are Composer-installable and already transitively present in many Laravel installs, so dependency conflicts are manageable but must be verified (symfony/serializer, symfony/validator, symfony/http-foundation version pins are the usual collision points against Laravel's own pins).
Technical risk. The new composition support carries client-side risk that is worth flagging explicitly: model providers differ in how faithfully they honor oneOf/anyOf/allOf in tool parameter schemas. Some enforce strict-schema modes that reject or flatten composition; others accept it but degrade argument quality, producing payloads that validate against one branch but not the branch we intended. Composition also recursively expands schema size, which inflates prompt tokens per tool and can push us against tool-definition limits. There is no indication in the release note that the feature is flag-gated or that validation is performed server-side before dispatch, so we should assume we own that verification.
Key questions.
discriminator/$ref reuse or only inline expansion?Stack fit. PHP 8.x on Laravel 10/11 is the target. The package's natural deployment is a long-lived process (stdio or HTTP+SSE transport), which is a poor fit for PHP-FPM request/response semantics. This is the single largest design decision: either run MCP as a dedicated worker/container (Octane or a standalone Symfony console process) or front it with a stateless HTTP transport. Pick the transport before writing any tool code, because it constrains auth, session, and observability design.
Migration path. Greenfield — no existing MCP surface in the codebase — so migration cost is zero. That is an advantage: we can adopt 1.9.0 directly and avoid the 1.8.x → 1.9.x schema-builder transition entirely. If we discover composition is unusable against our target models, pinning back to 1.8.2 is a clean rollback with no data migration.
Compatibility. Verify Composer resolution against Laravel's pinned Symfony components before committing to a spike; a version conflict here is a hard blocker, not a refactor. Confirm the package's PHP floor matches our runtime, and confirm the emitted JSON Schema draft is one our MCP clients accept. Composition support raises the minimum bar on all of these — a client that tolerated flat schemas may not tolerate anyOf.
Sequencing.
allOf inheritance shape, anyOf polymorphic input, nested oneOf discriminator) and test against each target client/model. Gate the 1.9.0 feature on this result.Maintenance. This is a single-maintainer upstream dependency on a fast-moving spec. The release note for 1.9.0 is a one-line changelog with no detail on edge cases, which is a signal about the project's documentation maturity — we should expect to read the source to answer the questions above. Assign a named owner; this is not a package to adopt and forget.
Support. Community-only. No vendor SLA. The mitigation is containment: keep the MCP surface behind our own interface so a swap to an alternative PHP MCP implementation is a bounded change rather than a rewrite.
Scaling. Long-lived process transports need their own scaling story (process count, memory ceiling, graceful restart) that is separate from our normal FPM autoscaling. Composed schemas increase per-request prompt payload for every tool definition we register, so tool count × schema size becomes a real budget concern at scale — a reason to keep the composed schemas tight and to consider exposing fewer, broader tools.
Failure modes.
anyOf branch → downstream domain validation fails at the wrong layer with a confusing error. Mitigate with strict server-side schema validation before dispatch and explicit per-branch error messages.Ramp-up. Moderate. An engineer familiar with Laravel but not Symfony DI needs a day or two on the container bridge; the MCP protocol concepts (tools, transports, JSON-RPC lifecycle) are another half-day. The schema-composition semantics are the subtlest part and are worth a short internal write-up, since the difference between what we express in PHP and what the model actually honors is where most integration bugs will land.
How can I help you explore Laravel packages today?