Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Mcp Sdk Laravel Package

php-llm/mcp-sdk

A PHP SDK for the Model Context Protocol (MCP), helping developers build MCP servers and clients with tools, resources, prompts, and transports for connecting LLM applications to external data and workflows. Framework-agnostic and suitable for Laravel or standalone PHP projects.

Deep Wiki
Context7

Getting Started

This SDK is archived (last release May 2025). Treat it as a reference implementation or a base to fork, not a dependency you roll out blindly. Pin the exact version in composer.json and audit before shipping.

Minimal setup:

composer require php-llm/mcp-sdk

Explore the two top-level namespaces first — that tells you the whole shape of the package:

  • Client\ — connect to an MCP server, negotiate capabilities, list and call tools, read resources, fetch prompts.
  • Server\ — expose your own tools/resources/prompts over a transport (stdio for local, SSE/HTTP for remote).

Pick your lane based on what you're building:

  • Building a consumer (e.g., an AI CLI, an agent loop, an artisan command that drives Claude): start with the Client transport classes and the initialize → tools/list → tools/call roundtrip.
  • Building a provider (e.g., exposing your Laravel app's domain actions to an AI client): start with the Server classes, register one tool, run it over stdio, and point the MCP Inspector at it.

The fastest first win: create a Laravel artisan command that boots an MCP Server over stdio, registers a single tool (e.g., lookup_order), and validate it against the official Inspector UI. That single end-to-end loop surfaces 90% of the protocol and transport quirks before you commit to a design.

Implementation Patterns

Server as an artisan command (stdio transport). The idiomatic PHP/Laravel shape is a long-running artisan command that boots the server and blocks on stdio:

// app/Console/Commands/McpServe.php
public function handle(Server $server): int
{
    $server->tool('lookup_order', 'Fetch an order by ID', [
        'order_id' => ['type' => 'string', 'required' => true],
    ], function (array $args) {
        $order = Order::findOrFail($args['order_id']);
        return ['content' => [['type' => 'text', 'text' => $order->toJson()]]];
    });

    return $server->run(new StdioTransport());
}

Then wire it into an MCP host config (claude_desktop_config.json or equivalent) as php artisan mcp:serve.

Tool registry via a service provider. Don't hand-register tools inside the command. Resolve them from a tagged container collection so tools are discoverable, testable, and reusable across transports:

$this->app->tag([LookupOrderTool::class, SearchProductsTool::class], 'mcp.tools');
// in the server bootstrap:
foreach ($this->app->tagged('mcp.tools') as $tool) { $server->tool(...$tool->definition()); }

This keeps your tool classes as plain services you can unit-test without spinning up a transport.

Client usage for multi-step flows. The client is a request/response loop, not an agent. You own the orchestration:

  1. initialize → capture server capabilities.
  2. tools/list → build a tool schema to hand to your LLM (usually as JSON Schema passed straight into the model's tools parameter).
  3. Get tool calls back from the model, translate them to tools/call, execute, append results, loop.

Persist the initialize result and tool list for the session; don't re-list on every turn — tool catalogs on real servers get expensive.

Multiple transports, one interface. Keep your application code transport-agnostic. The same registered server should run over stdio in dev (fast, no ports) and over SSE/HTTP in a container. If your business logic leaks transport assumptions (e.g., writing to STDOUT directly instead of through the transport), you'll discover it when you try to run this behind nginx.

Resource vs tool split. Tools are for actions with side effects; resources are for read-only URI-addressed data (files, records). Model resources as yourdomain://... URIs so clients can subscribe to changes and cache. Don't funnel reads through tools just because it's easier — you lose the client's ability to reason about what's fetchable.

Gotchas and Tips

Archived = no security updates. The MCP spec is still churning (protocol version negotiation, auth on remote transports, annotation semantics). If you use this in production, budget for a fork and track upstream spec changes yourself. The initialize handshake pins a protocol version — mismatches with newer clients will drop connections silently.

Stdio is a footgun in web contexts. Never invoke the stdio transport from a web request. It reads/writes STDIN/STDOUT, and in FPM that's a fast route to corrupted responses or a hung worker. Use a separate long-running process (supervisor + artisan command) and a socket/SSE transport for anything request-driven.

Don't echo from tool handlers. Anything written to STDOUT corrupts the JSON-RPC framing on stdio. Route all logging to STDERR or a channel (Log::channel('stderr')), and wrap any library that prints (some DB drivers and var_dump debuggers will bite you here).

Schema validation is not automatic. The SDK passes your declared input schema to the client, but enforcing it inside the handler is your job. Validate with Laravel's Validator (or opis/json-schema) before touching domain logic — an LLM will absolutely send you strings where you declared integers.

Errors have two channels. Throwing from a handler typically becomes a tool result with isError: true; a malformed JSON-RPC request becomes a protocol error. Clients behave very differently for these — the former is fed back to the model for recovery, the latter kills the session. Surface expected failures as tool errors, not exceptions.

Result shape is strict. Tool results are content arrays of typed blocks (text, image, resource). For structured output, return JSON as a text block and document the shape; don't invent fields at the top level.

Debug with the Inspector, not curl. The handshake ordering (initialize → initialized notification → tools/list) is easy to get wrong by hand. Point the MCP Inspector at your server and use it as your regression harness.

Capability negotiation matters. Only advertise capabilities your server actually implements. Over-advertising causes clients to call handlers you never registered, and you'll get cryptic "method not found" errors far from the cause.

Extension point worth knowing: transports and (in this SDK) the dispatcher/handler resolution are the seams. If you need auth, rate limiting, or request tracing on remote transports, wrap them at the transport layer or via middleware rather than editing tool handlers — that keeps your tool classes portable if you swap SDKs later.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle