Skip to content

[Schema][Server] Add the extensions framework SEP-2133 defines - #443

Open
chr-hertel wants to merge 2 commits into
modelcontextprotocol:mainfrom
chr-hertel:pr/extensions-framework
Open

[Schema][Server] Add the extensions framework SEP-2133 defines#443
chr-hertel wants to merge 2 commits into
modelcontextprotocol:mainfrom
chr-hertel:pr/extensions-framework

Conversation

@chr-hertel

Copy link
Copy Markdown
Member

enableExtension() took any string and advertised it, with no way for an extension to add a method: MessageFactory could not decode one, so nothing downstream ever saw it.

Identifiers are validated. Builder::enableExtension() checks the identifier against the _meta key naming rules through the new Mcp\Schema\Extension\ExtensionIdentifier — a prefix is mandatory, since an unprefixed name has no owner — which also recognises the modelcontextprotocol / mcp second labels the specification reserves.

An extension can contribute a method. An extension implementing the new MethodProvidingExtensionInterface contributes both the message classes its methods decode into (registered with MessageFactory, without which the methods cannot be decoded at all) and the handlers serving them.

Supporting changes: MessageFactory::make() takes an $additional list of message classes, and RequestHandlerInterface's result template is now covariant, so a handler declaring a concrete result satisfies a collection typed by the interface.

This is the framework SEP-2133 defines — what MCP Apps already sits on, and what the Tasks extension (#428) needs.

enableExtension() took any string and advertised it, with no way for an
extension to add a method: MessageFactory could not decode one, so nothing
downstream ever saw it.

Identifiers are now checked against the _meta key naming rules through
ExtensionIdentifier - a prefix is mandatory, since an unprefixed name has no
owner, and the modelcontextprotocol/mcp second labels the specification
reserves are recognised. An extension implementing
MethodProvidingExtensionInterface contributes both its message classes and
the handlers serving them.

MessageFactory::make() takes an $additional list of message classes, and
RequestHandlerInterface's result template is covariant so a handler
declaring a concrete result satisfies a collection typed by the interface.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the server-side “extensions framework” described by SEP-2133: extension identifiers are validated, and extensions can now contribute method message classes (so requests can be decoded) and request handlers (so requests can be served).

Changes:

  • Add ExtensionIdentifier validation and apply it in Server\Builder::enableExtension().
  • Introduce MethodProvidingExtensionInterface so extensions can register message classes and handlers; wire these into MessageFactory::make() via an additional list.
  • Update typing docs for covariant result generics and add unit tests/fixtures covering identifier validation and method-providing extensions.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Server/Builder.php Validates extension identifiers; collects extension message classes and handlers; passes extension messages into MessageFactory::make().
src/Schema/Extension/ExtensionIdentifier.php Defines SEP-2133 identifier validation and reserved-label detection.
src/Schema/Extension/MethodProvidingExtensionInterface.php New interface for extensions that add methods, exposing message classes and handlers.
src/JsonRpc/MessageFactory.php Extends make() to accept additional request/notification message classes.
src/Server/Handler/Request/RequestHandlerInterface.php Marks handler result template covariant (PHPDoc) and documents rationale.
src/Schema/JsonRpc/Response.php Marks response result template covariant (PHPDoc) and documents rationale.
tests/Unit/Server/BuilderTest.php Adds unit tests verifying identifier validation and extension-contributed messages/handlers.
tests/Unit/Schema/Extension/ExtensionIdentifierTest.php Adds unit tests for identifier validation and reserved-label recognition.
tests/Unit/Server/Extension/ThingExtension.php Minimal method-providing extension fixture for unit tests.
tests/Unit/Server/Extension/ThingListHandler.php Fixture handler serving the extension method.
tests/Unit/Server/Extension/ThingListRequest.php Fixture request class defining the extension method.
tests/Unit/Server/Extension/ThingListResult.php Fixture result class for the extension method.
CHANGELOG.md Documents the new extensions framework and related typing changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +22 to +26
* The two halves are declared separately because they answer different
* questions: the handlers say how a claimed method is served, and
* {@see self::getMethods()} says which methods exist at all — which is what
* lets a server distinguish "this extension is not enabled here" from "no such
* method", instead of answering `-32601` to both.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in de8934e.

public function testEnableExtensionRejectsUnprefixedIdentifier(): void
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage('Invalid extension identifier');

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bug — expectExceptionMessage() does substring matching (ExceptionMessageIsOrContains), not exact-match, so this partial-string assertion against the full "Invalid extension identifier: ..." message is correct as written.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants