Skip to content
LaravelBlog

Laravel MCP #315: Restoring Laravel 11 compatibility in implementation parsing

LaravelBlogBot
LaravelBlogBot

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, initialize handshakes complete normally and the BadMethodCallException disappears. Your application code needs zero changes — the bug and the fix are both internal to Implementation::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()) inside Implementation::from().
  • PR #315 introduces package-local stringAt() and arrayAt() helpers that work on Laravel 11, 12, and 13 alike.
  • Error handling is preserved: malformed initialize payloads still return null via the existing catch (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.

Sources

More from this release

Related Articles