Library / SDK
modelcontextprotocol/php-sdk avatar
modelcontextprotocol/php-sdk

mcp/sdk: the official PHP SDK for Model Context Protocol servers and clients

The official PHP SDK for Model Context Protocol servers and clients. Maintained in collaboration with The PHP Foundation.

1,609 stars173 forksPHPNOASSERTION

At a glance

What is it?
A framework-agnostic PHP implementation of MCP, maintained with the PHP Foundation and the Symfony project. It is the right starting point for PHP applications that need to expose tools and resources to an agent, but it is still pre-1.0 and the README calls it experimental.
Who is it for?
Adopt mcp/sdk if you already run PHP and want to expose existing application code as MCP tools without writing a protocol layer by hand; the attribute-based discovery means most of the work is annotation. Do not adopt it as a stable dependency for a long-lived product surface, because the README states the SDK is experimental until the first major release and the BC promise applies to Symfony practices rather than to a frozen API.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap mcp/sdk fills for PHP applications

MCP is a protocol, not a library. Anything that wants to speak it needs message framing, a capability handshake, transport handling, and a way to describe tools and resources in the shape the specification expects. In PHP that meant either writing the wire format yourself or reaching for a community implementation of uncertain provenance. This repository is the official SDK, published on Packagist as mcp/sdk and maintained in collaboration with the PHP Foundation and the Symfony project. The README states that it provides a framework-agnostic API for implementing MCP servers and clients in PHP, covering tools, resources, prompts, STDIO and HTTP transports, sessions, authorization, and both protocol eras. The audience is narrow and identifiable: PHP developers who have an existing codebase, usually a framework application or a CMS plugin, and who want an agent to call into it. The list of libraries already using the SDK in the README is telling. api-platform/mcp, drupal/mcp_server, josbeir/cakephp-synapse, nette/mcp-inspector, and symfony/mcp-bundle are all integrations that expose an existing PHP application's entities or configuration over MCP. That is the pattern the SDK is built for.

Attribute discovery is the central design decision

The mechanism that distinguishes this SDK from a thin protocol wrapper is discovery. In the README's server example, a plain PHP class carries #[McpTool] on a method and #[McpResource(uri: 'config://calculator/settings')] on another. The method signature supplies the input schema: add(int $a, int $b): int becomes a tool taking two integers and returning one. The server is then assembled with Server::builder()->setServerInfo('Calculator', '1.0.0')->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor'])->build()->run(new StdioTransport()). setDiscovery takes a directory, a list of suffixes to scan, and directories to skip, so the tool surface is derived from the code rather than registered by hand. That is a real architectural commitment. It means adding a capability is a matter of writing a method and an attribute, and it means the SDK has to reflect over your classes at startup. The excludeDirs parameter in the example exists precisely because scanning a directory tree in PHP is not free; vendor is excluded because there is nothing to expose there. Where discovery sits in the startup path, and whether the result is cached between requests, is not something the README states, so if you are running under a long-lived worker you should read docs/run/index.md rather than assume.

Two protocol eras in one codebase

The SDK supports what the README calls both protocol eras: the initialize handshake and the stateless 2026-07-28 revision. That is a larger surface than it first appears. A server that speaks the handshake era negotiates capabilities and holds session state; the stateless revision removes that. Supporting both in one SDK means the transport and session code carries branches for each. The conformance table in the README is the honest way to read this. It is measured weekly against the MCP Conformance Test Framework and reported as passing checks out of total, for server and client separately, across two revisions. The README adds a caveat that matters: the 2026-07-28 scores run against the framework's alpha releases, so they move as upstream publishes new scenarios. A badge that shifts because the denominator changed is not the same signal as a badge that shifts because the implementation regressed. If you are choosing a revision to target, look at the badge for that revision rather than at the project as a whole, and read docs/protocol-versions.md for what 2026-07-28 actually changed.

Installing it and wiring a first server

Installation is one command: composer require mcp/sdk. The README's server example is complete enough to run as written, assuming the Calculator class is in a file under the scanned directory. The client side is symmetric. Client::builder()->setClientInfo('My Application', '1.0.0')->build() produces a client, connect(new StdioTransport(command: 'php', args: ['/path/to/server.php'])) starts a subprocess and speaks to it over stdio, listTools() returns the available tools, callTool('add', ['a' => 5, 'b' => 3]) invokes one by name with an associative array of arguments, and disconnect() tears it down. The named argument command on StdioTransport is worth noting: the SDK launches the server process itself rather than expecting you to manage it. The README points at docs/client/connecting.md for transports, timeouts, and the handlers that answer server-initiated requests. That last item is the part people underestimate. A client is not only a caller; the server can send requests back, and something has to answer them. The documentation covers it, the README example does not show it, and a client that ignores those requests will look broken in ways that are hard to diagnose from the calling side.

Pre-1.0, experimental, and what that means in practice

The README is direct about this: until the first major release, the SDK is considered experimental, with a link to the Symfony definition of that term and a pointer to ROADMAP.md for planned next steps. The project adopts Symfony's Coding Standards and its Backward Compatibility Promise, but the BC promise is a statement about how the maintainers treat public API between releases, not a guarantee that the API is settled. The release history supports a cautious reading. v0.7.1, v0.8.0, and v0.8.1 landed within roughly a month of each other, and the jump from 0.7 to 0.8 is the kind of minor version where Symfony-style projects are still permitted to break things. The practical consequence is that pinning a version and reading the changelog before upgrading is not optional here. The other limitation is structural rather than temporal. Attribute discovery assumes your capabilities live in classes the scanner can reach. If your tools are generated at runtime, defined in configuration, or come from a plugin system that registers them dynamically, the attribute path does not fit and you are back to whatever manual registration the SDK exposes. The README does not demonstrate that path, so verify it exists before designing around it.

How it compares to writing a server in TypeScript

The obvious alternative is the TypeScript SDK, which is the reference implementation most MCP documentation is written against. The difference is not quality, it is where the code lives. A TypeScript MCP server is a Node process that you deploy alongside your application and that reaches your application over HTTP or a database connection. The PHP SDK lets the server be the application. For a Drupal site, a Kirby site, or a Symfony app, that removes a deployment unit and a network hop, and it means the tool handler runs in the same process with the same service container and the same ORM session as the rest of the code. The cost is that you inherit PHP's execution model. Under a traditional request-per-process setup, anything stateful has to be rebuilt on each request, which is why the discovery step and the session handling deserve scrutiny. Under a long-lived worker the calculus changes again. The second alternative is a community PHP MCP implementation. The distinguishing feature there is governance rather than features: this one is the official SDK, maintained with the PHP Foundation and the Symfony project, and it carries a weekly conformance measurement against the shared test framework. If protocol correctness across revisions is something you need to demonstrate, that measurement is the reason to pick this over a smaller package.

Maintenance cost and the licence question

The day-to-day cost of using mcp/sdk is low if you stay on the attribute path. You add methods, annotate them, and the tool surface follows. The upgrade cost is concentrated in the pre-1.0 window. Between v0.7.1 and v0.8.1 there were three releases in about three weeks, two of them minor bumps, and the README's experimental notice means the maintainers have reserved the right to change things. Budget for reading release notes on each minor version rather than assuming a patch-level upgrade is safe. The licence situation needs a caveat rather than a conclusion. The repository metadata supplied here reports the licence as NOASSERTION, which is what GitHub records when it cannot match the LICENSE file to a known identifier. The README's badge points at Packagist, which does report a licence for the package. Those two signals disagree, and the disagreement is in the metadata, not necessarily in the licence itself. Check the LICENSE file in the repository and the licence field on the Packagist page before you rely on either. Nothing here is legal advice, and if the licence terms matter to your distribution model, that is a question for someone qualified to answer it.

Editorial conclusion

Adopt mcp/sdk if you already run PHP and want to expose existing application code as MCP tools without writing a protocol layer by hand; the attribute-based discovery means most of the work is annotation. Do not adopt it as a stable dependency for a long-lived product surface, because the README states the SDK is experimental until the first major release and the BC promise applies to Symfony practices rather than to a frozen API. Before committing, check the conformance badges for the revision you intend to speak, read ROADMAP.md for what is still planned, and confirm whether the NOASSERTION license field on the repository matches the LICENSE file that Packagist reports.

Official sources

  1. Issues
  2. modelcontextprotocol/php-sdk on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes