Le Model Context Protocol (MCP) est devenu le standard pour exposer des données et des actions à des LLM. La spec est élégante mais la doc officielle plonge vite dans les SDK TypeScript et Python. Ce post déballe le minimum côté PHP, sans dépendance.

Le protocole en 30 secondes

MCP est un wrapper JSON-RPC 2.0 sur un transport. Le client (Claude Desktop, un agent custom, etc.) envoie des requêtes du genre tools/list ou tools/call. Le serveur répond en JSON. Trois transports existent : stdio (subprocess), HTTP+SSE, et HTTP streamable plus récent. On part sur stdio, le plus simple.

Client (Claude) tools/call stdin Serveur PHP JSON-RPC handler stdout Réponse JSON
Le client écrit du JSON sur stdin du serveur, le serveur répond sur stdout. Une requête par ligne.

Le code

Voici un serveur complet qui expose un tool get_time. Sauvegarde dans mcp-server.php :

<?php
$tools = [
    'get_time' => [
        'description' => 'Retourne l\'heure courante en ISO 8601',
        'inputSchema' => ['type' => 'object', 'properties' => (object) []],
        'handler' => fn() => date('c'),
    ],
];

while (($line = fgets(STDIN)) !== false) {
    $req = json_decode(trim($line), true);
    $id = $req['id'] ?? null;
    $method = $req['method'] ?? '';
    $params = $req['params'] ?? [];

    $result = match ($method) {
        'initialize' => ['protocolVersion' => '2024-11-05',
                          'capabilities' => ['tools' => (object) []],
                          'serverInfo' => ['name' => 'php-demo', 'version' => '0.1']],
        'tools/list' => ['tools' => array_map(
            fn($name, $t) => ['name' => $name, 'description' => $t['description'],
                              'inputSchema' => $t['inputSchema']],
            array_keys($tools), $tools)],
        'tools/call' => (function () use ($tools, $params) {
            $name = $params['name'];
            $args = $params['arguments'] ?? [];
            $out = $tools[$name]['handler']($args);
            return ['content' => [['type' => 'text', 'text' => (string) $out]]];
        })(),
        default => null,
    };

    if ($id !== null) {
        echo json_encode(['jsonrpc' => '2.0', 'id' => $id, 'result' => $result]) . "\n";
    }
}

Le tester

Configure Claude Desktop ou ton client MCP avec ce serveur (commande : php /chemin/mcp-server.php). Il devrait lister un tool get_time et l'appeler répond l'heure courante.

Ce qui manque pour un vrai serveur

  • Gestion d'erreurs JSON-RPC propres (codes -32700, -32601, etc.)
  • Validation du schéma d'entrée avant d'appeler le handler
  • Capabilities supplémentaires : resources, prompts, sampling
  • Transport HTTP streamable pour les déploiements serverless
  • Logs séparés (vers stderr, jamais stdout qui sert au protocole)

Mais le squelette ci-dessus suffit pour comprendre la mécanique. À partir de là, tu peux exposer une vraie DB, une API métier, ou un tool de calcul. Le format reste le même.