Thanks for considering a contribution to the PHP SDK for the Model Context Protocol. This is a collaboration between the PHP Foundation and the Symfony project, and it follows Symfony's conventions throughout.
- Report a bug or propose a feature by opening an issue.
Check existing issues first — many spec-driven changes are already tracked under the relevant
2026-07-28-style release label. - Send a pull request for a fix, a new capability, or a docs improvement.
- Improve the guides under
docs/— see Documentation below for how they're built. - Help close a conformance gap —
make conformance-testsruns the official MCP conformance suite against this SDK; a failing scenario there is a concrete, well-scoped contribution.
Requires PHP 8.1+.
composer installRun the full CI suite locally:
make ciThis runs, in order: make cs (PHP CS Fixer, auto-fixes style), make phpstan (static analysis),
and make tests (all PHPUnit suites; the interop suite needs Node). All three must pass. If your
change touches protocol-observable behavior, also run:
make conformance-tests # requires DockerThis project follows Symfony's coding standards and backward compatibility promise. In short:
See CLAUDE.md for a fuller tour of the codebase's architecture and layout.
New capabilities need unit tests (tests/Unit/) covering the core logic, and — for anything
reachable over the wire — end-to-end coverage. The suites are split by what they prove:
- Integration (
tests/Integration/,make integration-tests): our client and our server agree with each other. Fixture servers cover single features;ClientExamples/runs every script inexamples/client/, one test class per example, piping "user input" to STDIN where an example asks for it, and snapshots what it prints. - Interop (
tests/Interop/,make interop-tests, needs Node): each half works with an implementation we did not write.Server/drives the example servers with the MCP Inspector, one test class per example;Client/runs our client against the TypeScript SDK's reference server,@modelcontextprotocol/server-everything, over stdio and HTTP. Both are installed fromtests/Interop/package-lock.json(npm ci --prefix tests/Interop), so a release of one of their dependencies cannot change what the snapshots see. - Conformance (
tests/Conformance/,make conformance-tests): each half passes the specification's scripted scenarios.
Snapshots are written on the first run; delete one to re-record it. If you're adding a documented
pattern, consider adding or updating an example under examples/ along with its test class.
The guides under docs/ are built with Zensical in --strict mode,
which fails the build on a broken internal link:
make docs-guidesLinks between guide pages must be relative paths that resolve within docs/ (e.g.
protocol-versions.md, ../CLAUDE.md will not resolve — Zensical only follows the docs/ tree).
For anything at the repo root (CLAUDE.md, ROADMAP.md, CHANGELOG.md), link to it by its GitHub
URL instead, matching the pattern already used across docs/. The class-level API reference is
generated separately by phpDocumentor (make docs-api) and isn't hand-written.
The SDK follows Semantic Versioning — see SDK tier target for what that means pre- and post-1.0, and how it lines up with Symfony's backward compatibility promise.
New contributions are licensed under Apache License, Version 2.0. Existing code predating this policy remains under the MIT License — see LICENSE for the details. By opening a pull request, you agree your contribution is provided under those terms.
If something is unclear or you want early feedback on an approach before writing code, open an issue or a draft pull request — that's the right place to ask, rather than guessing at scope.