valentin_vranic's avatar

Laravel external MCP tool listing

Hi all!

I'm facing an issue for a while. Firstly it was the lack of understanding of MCP-s, secondly the understanding of capabilities of Laravel and MCP-s.

So as I know, the MCP-s are not kind of REST endpoint(s) as I thought in the beginning, but an SSE (server-sent-events). Which is a new for me.

So my starting approach was to just send a tools-list method to the url, but later realised that it does need an open session connection to the MCP itself and then can only happen the query.

So my question is, is there any solutions to this in laravel? A lightweight approach for a quick MCP client which only lists the tools of a certain MCP server? I'm building a workflow app, where one of the features would be that users can add/pick MCP servers and list their tools to visually see them.

Thanks in advance, Val

0 likes
2 replies
LaryAI's avatar
Level 58

Certainly! Let’s break down your question and provide a clear solution for interfacing Laravel with an MCP (Machine Control Protocol) server using SSE (Server-Sent Events), with the goal to list the tools of a certain MCP server.

Understanding the Problem

  • MCP servers: Control machines via a protocol that may use long-lived connections, often using SSE (not just stateless HTTP REST APIs).
  • Your task: From a Laravel app, you want to connect to one or more MCP servers, open a session, and list the available tools.
  • Challenge: This is different from making a simple HTTP GET/POST request, as it requires maintaining a live connection and potentially parsing a stream of data/events.

Solution Overview

You need a PHP/Laravel-compatible MCP client. Since MCP uses SSE, you’ll need to:

  1. Establish a live HTTP connection to the MCP server that supports SSE.
  2. Parse incoming event streams to retrieve tool listings.
  3. Optionally: Wrap this logic as a Laravel service so controllers or jobs can use it.

Step-by-step Laravel Solution

1. Connecting via SSE in PHP

PHP isn’t ideal for long-lived connections, but for a lightweight query (fetching tool lists), you can make a simple request and read the response. If the event stream is short (ends with a list), this approach works.

You can use Guzzle or just plain file_get_contents with a socket context for a simple implementation.

Example SSE Client (Tool List Fetcher)

Create a new class, e.g., app/Services/McpClient.php:

Notes:

  • This is a simple stream reading method. If the SSE runs "forever," add timeouts or logic to break after you get the data you want.
  • Adjust the /tools-list endpoint as per your MCP server's documentation.
  • For more robust/fault-tolerant handling, or to read multiple types of events, consider using a package/library or building a more advanced parser.

2. Using the Service in a Controller

use App\Services\McpClient;

class ToolController extends Controller
{
    public function listTools(Request $request)
    {
        $serverUrl = $request->input('server_url'); // From user form or saved config
        $client = new McpClient($serverUrl);

        try {
            $tools = $client->getToolsList();
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }

        return response()->json($tools);
    }
}

3. Frontend: Display the List

Fetch from this endpoint as a regular API and display tools visually as desired.


Summary

  • MCP’s use of SSE means you need to read an HTTP stream, not just send a request/response.
  • PHP can do this with stream contexts (fopen), and you can wrap that in a Laravel service.
  • This is a simple, quick approach for just listing tools; for heavier use or multiple users, consider using queue jobs, WebSockets, or offloading MCP connections to a Node.js or Go microservice.

Further Reading/Packages:


If your MCP server is different or more complex, or you have authentication/session requirements, let me know for a more tailored answer!

valentin_vranic's avatar

So, finally, I was able to come up with a working solution. Hopefully someone facing some same kind of issues will find it helpful.

  private function checkSSEServer(McpServer $server, array $headers): array
    {
        try {
            $response = Http::timeout(10)
                ->withHeaders($headers)
                ->get($server->url);

            $body = $response->body();
            $endpoint = $this->extractSseEndpoint($body, $server->url);

            $sessionId = $this->extractSessionId($endpoint);
            $requestHeaders = $sessionId ? array_merge($headers, ['Mcp-Session-Id' => $sessionId]) : $headers;

            return $this->requestToolsList($endpoint, $requestHeaders);
}

So, it's not handling when it's unsuccessful, only to show what is my workflow for it. McpServer is only my model consisting some props like name, url, etc...

Please or to participate in this conversation.