Laravel MCP #315: Restoring Laravel 11 compatibility in implementation parsing
Contributed by pushpak1300
Part of the mcp v1.0.1 release
Laravel MCP v0.9.6 shipped with a regression that broke every Laravel 11 install: the schema parser behind the MCP initialize handshake called two array helpers that only exist in Laravel 12. Pull request #315 replaces those calls with small local equivalents, restoring Laravel 11 compatibility without changing any behavior on Laravel 12 or 13. If you were seeing BadMethodCallException: Method Illuminate\Support\Arr::string does not exist after upgrading, this is the fix you have been waiting for.
What broke on Laravel 11
Implementation::from() is the code path that turns the raw clientInfo payload from an MCP client into a typed Implementation value object — the name, version, title, description, icons, and website URL that identify the connected client. That method was written against Arr::string() and Arr::array(), two type-asserting array helpers that landed in illuminate/support with Laravel 12.
The package's Composer constraint still allows illuminate/support: ^11.45.3, so Laravel 11 users could install the package just fine — and then hit a wall at runtime. On Laravel 11, Arr has no string() or array() method, so the call falls through to __callStatic() and throws:
BadMethodCallException: Method Illuminate\Support\Arr::string does not exist
Because Implementation::from() runs during the initialize handshake, this was not an edge case: every MCP client connection on Laravel 11 failed before the server could finish negotiating. CI told the same story — 145 tests were failing on the PHP 8.2 - Laravel 11 job against main.
How the fix works
Rather than bumping the Laravel floor to 12 and dropping Laravel 11 users, PR #315 adds two tiny protected helpers to Implementation and swaps the framework calls for local ones:
protected static function stringAt(array $array, string $key, ?string $default = null): string
{
$value = Arr::get($array, $key, $default);
if (! is_string($value)) {
throw new InvalidArgumentException("The [{$key}] value must be a string.");
}
return $value;
}
arrayAt() is the same idea for arrays. Both are drop-in stand-ins for the Laravel 12 helpers — the same lookup semantics via Arr::get(), the same default handling, and the same contract of throwing an InvalidArgumentException on a type mismatch. The call sites read almost identically before and after:
// Before (Laravel 12-only helpers):
name: Arr::string($data, 'name'),
icons: Arr::map(Arr::array($data, 'icons', []), ...),
// After (package-local helpers):
name: self::stringAt($data, 'name'),
icons: Arr::map(self::arrayAt($data, 'icons', []), ...),
Every Arr::string() and Arr::array() call in the parser was converted, including the nested ones used to hydrate Icon objects (source URL, MIME type, sizes, and theme).
Behavior is unchanged
The guarantee that matters most: malformed payloads still resolve to null instead of blowing up. from() already wrapped its body in a catch (InvalidArgumentException) block, and the new helpers throw exactly that exception type, so the existing control flow is intact:
// A clientInfo with a non-string "name" still parses to null:
Implementation::from(['name' => 42, 'version' => '1.0.0']); // null
// Optional fields still fall back to their defaults:
Implementation::from(['name' => 'my-client', 'version' => '1.0.0']); // title/description/icons stay null
Nothing observable changes on Laravel 12 or 13 either — the helpers behave identically to the framework's Arr::string() and Arr::array(). The existing ImplementationTest dataset already covers every malformed case, so the suite exercises both the success and rejection paths without any new tests being added.
Upgrade impact
For anyone coming from v0.9.6, the impact depends on your framework version:
- Laravel 11: this is a must-have patch. After updating,
initializehandshakes complete normally and theBadMethodCallExceptiondisappears. Your application code needs zero changes — the bug and the fix are both internal toImplementation::from(). - Laravel 12/13: no observable change. Update as usual; you gain nothing and lose nothing behaviorally.
There are no public API changes to migrate. stringAt() and arrayAt() are protected, so they do not extend the package's surface area, and the only touched file is src/Schema/Implementation.php. The Composer constraint still supports Laravel 11.45.3 and up, so no dependency juggling is needed. If you pinned an older version or forked the package to work around the crash, you can undo that workaround and pull the patched release instead.
The PR author notes that the local helpers can be dropped in favor of the framework helpers again once the package's minimum Laravel version moves to 12 — a sensible bit of hygiene that keeps the compatibility shim contained to a single class until then.
Takeaways
- v0.9.6 broke Laravel 11 support by using Laravel 12-only helpers (
Arr::string(),Arr::array()) insideImplementation::from(). - PR #315 introduces package-local
stringAt()andarrayAt()helpers that work on Laravel 11, 12, and 13 alike. - Error handling is preserved: malformed
initializepayloads still returnnullvia the existingcatch (InvalidArgumentException)block. - Laravel 12/13 users see no change; Laravel 11 users should update to a release containing #315 as soon as possible.
- No public API, config, or Composer changes — this is a drop-in patch.