The official PHP SDK for Model Context Protocol servers and clients. Maintained in collaboration with The PHP Foundation.
by modelcontextprotocolPHP
Last 12 weeks · 31 commits
5 of 6 standards met
Summary Implements the MCP Skills extension (SEP-2640): a way for servers to ship skills — multi-step workflow instructions that tell an agent how to orchestrate tools to reach a goal. Skills ride on the existing Resources primitive, mirroring the MCP Apps extension (). This PR adds no new protocol methods (the SEP's one optional method, , is a planned follow-up — see below). A server can expose a whole directory of skills in one line: This registers each (and its supporting files) as resources, derives the resource metadata from the YAML frontmatter, and serves a discovery index whose entries mirror each skill's frontmatter verbatim and carry a SHA-256 of the . Optionally, skills can also be served as one-shot archives: What's included — extension marker (, , , , , ). DTOs — , ( + optional / + optional ), , — readonly, , omit-null . — splits into YAML frontmatter + body (handles BOM/CRLF; rejects non-mapping frontmatter). — walks a directory, registers each skill and its supporting files as resources, enforces the spec's name↔final-path-segment rule, sanitizes schema-valid resource names (URIs keep real paths), guesses MIME types, guards against path traversal, mirrors frontmatter + digest into the index, and (when requested) serves packed archives. — deterministic ustar + gzip writer (no temp files, zeroed mtime/owner; at the archive root) for . — convenience that auto-enables the extension; takes . Notable fix only cast the outer map, so an extension with an empty payload (like Skills) serialized to instead of . The Apps extension never hit this because its payload is non-empty. Empty inner payloads are now coerced to . Dependency Adds () for frontmatter parsing — the feature is non-functional without it. Tests & docs Unit tests for the extension, DTOs, , , and (PharData round-trip, determinism, the digest-matches-served-bytes invariant), plus a serialization regression test for the fix. Inspector stdio snapshot test (, of a , a supporting file, and the discovery index). Example server at ; docs in . Verified: (level 6) clean, clean, full unit suite green (812 tests), inspector skills test green. Updates tracking the SEP The SEP redesigned the discovery index after this PR opened; it has been brought in line: Index format — entries now mirror frontmatter verbatim and carry a ; dropped the field and the / enum (the type was removed from the index in the redesign). Archives — added optional archive serving (), listed under each entry's . Deferred / follow-up — the SEP's one optional method (scoped directory listing) plus the capability flag. ZIP archives () — the SEP requires at least one of gzip-tar or zip; gzip-tar is implemented.
Summary runs all loaders eagerly () and then snapshots server capabilities from that registry (, etc.). Both happen once, when the server is built. Under a persistent runtime (e.g. FrankenPHP worker mode) the server is built a single time and reused across requests. If a loader's data source is not yet ready at that moment — a cold cache, an application metadata layer that gets warmed later — the registry captures an empty state and never recovers for the lifetime of the process. (which reads the registry) returns , while can still work if the consumer resolves handlers independently of the registry. This was reported downstream in api-platform/core#8370 (FrankenPHP worker mode). Change Add , a registry decorator that runs the loaders on the first read instead of at build time. Loading moves to request time, when the application is fully initialized; it runs once per process, and registrations made before the first read are preserved (loader writes are additive/idempotent by name). The deferred load is retried on the next read if it throws: the loaded flag is set only after a successful load, so a transient failure at the first read (data source still not ready) does not permanently freeze an empty registry for the whole process. In , wrap the registry in instead of calling eagerly. Advertise capabilities from the configured element sources (, , discovery/custom loaders, …) rather than from the now-lazy registry, so the handshake does not force an eager load. Custom loaders and discovery are opaque about which element kinds they yield, so their presence advertises tools/resources/prompts; over-advertising is harmless (a client lists and gets an empty result). A registry supplied via is treated as an opaque source too, so a pre-populated custom registry still advertises its capabilities (it previously advertised after the switch away from inspecting the registry, which would make a spec-compliant client skip entirely). This also removes the need for the userland workarounds consumers currently apply (request-time list handlers, restoring-registry decorators) to keep discovery working under worker runtimes. Tests New : loader not run until first read, runs on first read and populates, runs exactly once across many reads, runtime registrations survive the deferred load, and (a throwing first load is retried on the next read). New : a registry pre-populated via advertises in the handshake. Existing and the full suite pass. PHP-CS-Fixer and PHPStan clean. Notes Behavioral change worth flagging for review: capabilities are now derived from configured sources, so a server with a discovery path or custom loader that ultimately yields nothing will advertise // where it previously advertised . This is intentional (the whole point is not to inspect the registry at build), and harmless per MCP semantics, but it is a visible difference in the response.
Fixes #387. crashes for a pointing at a plain resource: returns a , but the handler reads , which only and declare. Providers now come from the prompt or the matched resource template; a resource with no template yields an empty completion result. Added a handler test for both the plain-resource regression and the resource-template path.
Summary The HTTP server transport already supports being an OAuth resource server (validating tokens) and an OAuth proxy to an upstream IdP. This adds the missing third piece: a self-contained OAuth 2.1 authorization server, so an MCP server can register clients, authorize users, and issue + validate its own RS256 JWT access tokens — without an external IdP or a third-party OAuth library (e.g. ). It is dependency-free apart from , which the existing already uses for validation; signing reuses it. No new hard dependency. What's included () Engine behind seams — / with implementations: authorization code grant with mandatory PKCE (S256), refresh-token rotation, one-time short-TTL codes, exact matching, constant-time client auth. Tokens & keys — (RS256) producing tokens that validate unchanged through the existing ; , , and for in-process self-validation (no network round-trip). Endpoints — , , (RFC 8414); DCR (RFC 7591) via + the existing . Storage — , , , optional , each with in-memory and PSR-16 implementations. Host seams — (the SDK can't authenticate users) and (+ default ). for RFC 6749 §5.2 error responses. A runnable example at . The engine is transport-agnostic (the middlewares are thin PSR-7 shells over it), so it can be driven directly from a framework controller too. Tests 144 new unit tests (PKCE RFC 7636 vector, self-issued round-trip through , granter behaviours, registrar, metadata, middlewares). phpstan level 6 clean. Status Draft. This is the SDK half of a larger effort to let Symfony MCP servers act as their own OAuth AS. Companion work, built on these primitives and validated end-to-end against a real Sulu MCP server: Tracking issue: symfony/ai#2134 — https://github.com/symfony/ai/issues/2134 MCP Bundle implementation (draft): symfony/ai#2135 — https://github.com/symfony/ai/pull/2135 Feedback on the public API/seams welcome.
Repository: modelcontextprotocol/php-sdk. Description: The official PHP SDK for Model Context Protocol servers and clients. Maintained in collaboration with The PHP Foundation. Stars: 1547, Forks: 150. Primary language: PHP. Languages: PHP (99.8%), Makefile (0.1%), Twig (0.1%). Homepage: https://php.sdk.modelcontextprotocol.io Latest release: v0.6.0 (4w ago). Open PRs: 13, open issues: 79. Last activity: 1w ago. Community health: 87%. Top contributors: chr-hertel, CodeWithKyrian, Nyholm, OskarStark, soyuka, bigdevlarry, dependabot[bot], luoyue712, bb-c24, e0ipso and others.