monkeyscloud/monkeyslegion-mcp

Production-grade MCP (Model Context Protocol) server and client for PHP 8.4 with attribute-based tool registration, stdio + Streamable HTTP transports, and JSON-RPC 2.0 engine.

Maintainers

Package info

github.com/MonkeysCloud/MonkeysLegion-MCP

pkg:composer/monkeyscloud/monkeyslegion-mcp

Statistics

Installs: 1

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-05-23 03:20 UTC

This package is auto-updated.

Last update: 2026-05-23 05:29:52 UTC


README

PHP 8.4+ License: MIT

Production-grade Model Context Protocol (MCP) server and client for PHP 8.4+.
MonkeysLegion ships native MCP support — the first PHP framework to do so.

Features

  • JSON-RPC 2.0 engine with full batch support
  • Three MCP primitives: Tools, Resources, and Prompts
  • Attribute-based discovery: #[McpTool], #[McpResource], #[McpPrompt], #[McpParam]
  • Two transports: Stdio (local AI tools) and Streamable HTTP (remote)
  • PSR-15 Middleware: Drop-in HTTP integration for any PSR-15 framework
  • DI Provider: Bootstrap the full MCP stack from MLC configuration
  • MCP Client: HTTP and Stdio clients for connecting to external MCP servers
  • Schema generation from PHP method signatures via reflection
  • Schema validation for tool arguments (types, enums, ranges)
  • Protocol negotiation: 2024-11-05 and 2025-03-26
  • Session management via MCP-Session-Id header
  • PSR-3/PSR-7/PSR-15 integration

Installation

composer require monkeyscloud/monkeyslegion-mcp

Requirements

  • PHP 8.4+
  • psr/log ^3.0
  • psr/http-message ^2.0
  • psr/http-server-handler ^1.0
  • psr/http-server-middleware ^1.0
  • ext-curl (for HTTP client transport, optional)

Quick Start

1. Create an MCP Server

<?php
use MonkeysLegion\Mcp\Server\McpServer;
use MonkeysLegion\Mcp\Protocol\ServerInfo;

$server = new McpServer(new ServerInfo('MyApp', '1.0.0'));

2. Register Tools

$server->addTool(
    name: 'calculate',
    description: 'Perform arithmetic calculations',
    inputSchema: [
        'type'       => 'object',
        'properties' => [
            'operation' => ['type' => 'string', 'enum' => ['add', 'sub', 'mul', 'div']],
            'a'         => ['type' => 'number'],
            'b'         => ['type' => 'number'],
        ],
        'required' => ['operation', 'a', 'b'],
    ],
    handler: function (array $args): float {
        return match ($args['operation']) {
            'add' => $args['a'] + $args['b'],
            'sub' => $args['a'] - $args['b'],
            'mul' => $args['a'] * $args['b'],
            'div' => $args['b'] != 0 ? $args['a'] / $args['b'] : throw new \DivisionByZeroError(),
        };
    },
);

3. Attribute-Based Registration (Recommended)

<?php
use MonkeysLegion\Mcp\Attributes\McpTool;
use MonkeysLegion\Mcp\Attributes\McpParam;
use MonkeysLegion\Mcp\Attributes\McpResource;
use MonkeysLegion\Mcp\Attributes\McpPrompt;
use MonkeysLegion\Mcp\Prompt\PromptDefinition;

class AppTools
{
    /**
     * Search the database for records matching a query.
     */
    #[McpTool(name: 'search', description: 'Search records')]
    public function search(
        #[McpParam(description: 'Search query string')]
        string $query,
        #[McpParam(description: 'Max results to return', required: false)]
        int $limit = 10,
    ): array {
        // Your search logic here
        return ['results' => [], 'total' => 0];
    }

    #[McpResource(uri: 'app://config', name: 'config', mimeType: 'application/json')]
    public function getConfig(array $params = []): string
    {
        return json_encode(['env' => 'production', 'debug' => false]);
    }

    #[McpPrompt(name: 'code_review', description: 'Review code for best practices')]
    public function codeReview(array $params = []): PromptDefinition
    {
        return new PromptDefinition(
            name: 'code_review',
            description: 'Review code for best practices',
            arguments: [
                'language' => ['description' => 'Programming language', 'required' => true],
            ],
            messages: [
                ['role' => 'system', 'content' => ['type' => 'text', 'text' => 'You are a {language} expert.']],
                ['role' => 'user', 'content' => ['type' => 'text', 'text' => 'Review this code for best practices.']],
            ],
        );
    }
}

Scan and register:

use MonkeysLegion\Mcp\Discovery\AttributeScanner;

AttributeScanner::scan($server, [AppTools::class]);
// or scan an entire directory:
AttributeScanner::scanDirectory($server, __DIR__ . '/app/Mcp', 'App\\Mcp');

4. Choose a Transport

Stdio (Local AI Tools — Claude Desktop, etc.)

use MonkeysLegion\Mcp\Transport\StdioTransport;

$transport = new StdioTransport();
$transport->start($server);

Streamable HTTP (Remote)

use MonkeysLegion\Mcp\Transport\StreamableHttpTransport;

$transport = new StreamableHttpTransport();
// In a route handler or controller:
$transport->handleRequest($server);

Or use it directly in a controller:

// POST /mcp
$body    = file_get_contents('php://input');
$headers = ['MCP-Protocol-Version' => $_SERVER['HTTP_MCP_PROTOCOL_VERSION'] ?? ''];

$response = $server->handleJson($body, $headers);

foreach ($server->responseHeaders() as $name => $value) {
    header("{$name}: {$value}");
}
echo $response;

PSR-15 Middleware (Recommended for Frameworks)

use MonkeysLegion\Mcp\Middleware\McpMiddleware;

// Mount on your middleware stack — handles POST, GET, DELETE on /mcp
$middleware = new McpMiddleware($server, '/mcp');

// Or use the Provider for zero-config setup:
use MonkeysLegion\Mcp\Provider\McpProvider;

$provider   = new McpProvider($config['mcp'] ?? []);
$middleware = $provider->middleware();  // Auto-wired with server + discovery
$server     = $provider->server();     // Singleton, pre-scanned
$transport  = $provider->transport();  // stdio or http based on config

5. Resources & Templates

use MonkeysLegion\Mcp\Resource\ResourceTemplate;

// Static resource
$server->addResource(
    name: 'readme',
    uri: 'file:///README.md',
    content: file_get_contents('README.md'),
    mimeType: 'text/markdown',
);

// Dynamic resource template
$server->addResourceTemplate(
    new ResourceTemplate('db://users/{id}', 'user', 'application/json', 'Fetch a user'),
    fn(array $params) => json_encode(User::find($params['id'])),
);

MCP Client

HTTP Client

use MonkeysLegion\Mcp\Client\McpClient;
use MonkeysLegion\Mcp\Client\ClientConfig;

$client = new McpClient(new ClientConfig(
    serverUrl: 'https://example.com/mcp',
    timeout: 30,
));

// Initialize session
$client->initialize();

// List and call tools
$tools  = $client->listTools();
$result = $client->callTool('search', ['query' => 'PHP 8.4']);

// Resources and Prompts
$resources = $client->listResources();
$content   = $client->readResource('file:///README.md');
$prompts   = $client->listPrompts();
$prompt    = $client->getPrompt('code_review', ['language' => 'PHP']);

Stdio Client (Subprocess)

use MonkeysLegion\Mcp\Client\StdioClient;

$client = new StdioClient('php mcp-server.php');
$client->start();
$client->initialize();

$tools  = $client->listTools();
$result = $client->callTool('calculate', ['operation' => 'add', 'a' => 3, 'b' => 5]);

$client->stop();

MLC Configuration

# config/mcp.mlc

mcp:
  server:
    name: "MyApp-MCP"
    version: "1.0.0"

  transport: "stdio"           # stdio | streamable-http
  endpoint: "/mcp"             # HTTP endpoint (streamable-http only)
  protocol_version: "2025-03-26"

  scan:
    directories:
      - path: "app/Mcp"
        namespace: "App\\Mcp"

  logging:
    enabled: true
    level: "debug"

Claude Desktop Integration

Add to your Claude Desktop config (claude_desktop_config.json):

{
  "mcpServers": {
    "my-app": {
      "command": "php",
      "args": ["/path/to/your/mcp-server.php"]
    }
  }
}

Create mcp-server.php:

<?php
require __DIR__ . '/vendor/autoload.php';

use MonkeysLegion\Mcp\Server\McpServer;
use MonkeysLegion\Mcp\Protocol\ServerInfo;
use MonkeysLegion\Mcp\Discovery\AttributeScanner;
use MonkeysLegion\Mcp\Transport\StdioTransport;

$server = new McpServer(new ServerInfo('MyApp', '1.0.0'));

// Register your tools
AttributeScanner::scanDirectory($server, __DIR__ . '/app/Mcp', 'App\\Mcp');

// Start stdio transport
(new StdioTransport())->start($server);

Protocol Support

Feature 2024-11-05 2025-03-26
Tools
Resources
Prompts
Streamable HTTP
Session management
Stdio transport

Testing

composer test

196 tests, 354 assertions covering:

  • JSON-RPC 2.0 engine (messages, responses, errors)
  • All MCP handlers (initialize, ping, tools, resources, prompts)
  • Schema generation and validation
  • Attribute scanning and discovery
  • PSR-15 middleware (routing, headers, POST/GET/DELETE)
  • DI provider (factories, singletons, config, discovery)
  • Stdio transport with in-memory streams
  • Full server integration

License

MIT — see LICENSE.