Skip to content

Repository files navigation

PHP MCP Schema

A dependency-free PHP 7.4+ runtime for the canonical Model Context Protocol schemas.

The package validates and hydrates exact MCP revisions into immutable shared records. It is a schema package, not an MCP client, server, or transport SDK.

Supported revisions:

  • 2025-11-25
  • 2026-07-28

Unknown identifiers are rejected. The runtime never selects a revision by a range or nearest-version rule.

Older revisions

Revisions before 2025-11-25 (2024-10-07, 2024-11-05, 2025-03-26, and 2025-06-18) are not accepted as identifiers and have no schema of their own. Most schema changes up to 2025-11-25 were additive, so consumers that negotiate one of those versions validate their messages through the 2025-11-25 schema. This is the same approach the official TypeScript SDK takes: it ships wire schemas only for 2025-11-25 and 2026-07-28 and parses every earlier revision with the 2025-11-25 schema. The mapping from a negotiated version to a schema revision is the consumer's decision, not this package's.

Installation

composer require wordpress/php-mcp-schema

The runtime described below ships in 0.2.0 and later. Releases before 0.2.0 expose the removed DTO API; see the migration guide.

Select and use a schema

Choose the exact revision before constructing any protocol value:

use WP\McpSchema\Record\Tool;
use WP\McpSchema\Schemas;

$schema = Schemas::create()->forVersion(Schemas::V2026_07_28);

$tool = $schema->fromArray(Tool::class, array(
    'name' => 'get_weather',
    'description' => 'Get current weather for a location',
    'inputSchema' => array(
        'type' => 'object',
        'properties' => array(
            'location' => array('type' => 'string'),
        ),
        'required' => array('location'),
    ),
));

echo $tool->getName();
echo json_encode($tool, JSON_THROW_ON_ERROR);

fromArray() is for PHP associative-array construction. fromValue() accepts decoded stdClass/list graphs and existing immutable records. fromJson() accepts raw JSON and preserves object/list identity and numeric-string object keys:

use WP\McpSchema\Contract\ClientRequest;
use WP\McpSchema\Record\CallToolRequest;
use WP\McpSchema\Schemas;

$schema = Schemas::create()->forVersion(Schemas::V2025_11_25);
$request = $schema->fromJson(
    ClientRequest::class,
    '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_weather","arguments":{"location":"Paris"}}}'
);

if ($request instanceof CallToolRequest) {
    echo $request->getParams()->getName();
    $arguments = $request->getParams()->getArguments();
    if ($arguments !== null) {
        echo $arguments->location;
    }
}

Useful union construction roots live under WP\McpSchema\Contract. Official JSON Schema anyOf validity is preserved. When more than one object member is valid, hydration chooses the member declaring the most keys present in the input; canonical order breaks a tie. Scalar unions keep canonical first-match behavior.

Records and wire identity

Records are immutable. Named getters read fields declared by the selected revision. Generic access preserves open-schema extension data:

$hasDescription = $tool->has('description');
$description = $tool->get('description');
$wireObject = $tool->jsonSerialize();

has() distinguishes omission from an explicit null. get() reads declared fields and present extension keys, and rejects unknown absent keys. jsonSerialize() returns a defensive stdClass and is the complete wire-output API.

Sequential PHP arrays are JSON lists and associative arrays are JSON objects. PHP converts numeric-string array keys such as "0" to integers, so use fromJson() or stdClass for objects made only of sequential numeric keys. An empty array() becomes an object only where the selected schema requires an object; in an unconstrained position it remains []. Use new stdClass() when an unconstrained empty JSON object must be unambiguous.

Exact message availability

The selected schema exposes generated directional checks:

$schema->allowsClientRequest('ping');
$schema->allowsClientNotification('notifications/initialized');
$schema->allowsServerRequest('sampling/createMessage');
$schema->allowsServerNotification('notifications/tools/list_changed');
$schema->allowsEmbeddedInput('elicitation/create');

For example, ping is valid under 2025-11-25 and absent under 2026-07-28; server/discover is valid under 2026-07-28 and absent under 2025-11-25. The method to record map lists every method with its request, result, or notification record and its revision availability.

Public namespaces

  • WP\McpSchema\Schemas and WP\McpSchema\Schema — exact revision selection and construction.
  • WP\McpSchema\Record — immutable named objects shared where compatible.
  • WP\McpSchema\Contract — useful union construction roots.
  • WP\McpSchema\Value — string constants for canonical enum-like values.
  • WP\McpSchema\Exception — stable selection, validation, JSON, availability, and field-access failures.

See the migration guide when moving from the removed DTO API.

Development

Canonical schemas are pinned under resources/schema/. The development-only plain Node generator stages and replaces only src/Record/, src/Contract/, src/Value/, src/Internal/Catalog/, and src/Internal/TypeRegistry.php. Handwritten runtime files remain separate, including src/Record.php and the other files under src/Internal/.

composer install
composer test
composer analyse
composer validate --strict
composer autoload:verify

cd generator
npm install
npm run generate
npm run verify

Never edit generated PHP directly. See the generator guide and the architecture.

License

GPL-2.0-or-later. See LICENSE.md.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Stars

14 stars

Watchers

4 watching

Forks

Releases

Packages

Contributors

Languages