PHP code example of milpa / mcp-server

1. Go to this page and download the library: Download milpa/mcp-server library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

milpa / mcp-server example snippets


use Milpa\McpServer\JsonRpcService;
use Milpa\ToolRuntime\Contracts\ToolContext;
use Milpa\ToolRuntime\ToolRegistry;
use Psr\Log\NullLogger;

$registry = new ToolRegistry(new NullLogger());
// ... register tools on $registry, e.g. via ToolScanner ...

$rpc = new JsonRpcService($registry);

$ctx = new ToolContext(principal: 'user:42', channel: 'mcp', scopes: ['notes:read']);

$response = $rpc->handle([
    'jsonrpc' => '2.0',
    'method' => 'tools/call',
    'params' => ['name' => 'list_notes', 'arguments' => ['page' => 1]],
    'id' => 1,
], $ctx);

// $response is a JSON-RPC 2.0 response array, or null. json_encode() an array response onto
// whatever transport your host terminates (an HTTP POST body, a stdio pipe, ...); a null
// response means the request was a notification and your transport MUST write nothing at all
// — see "Recipes" below.

use Milpa\Events\InterceptionSlot;
use Milpa\McpServer\Events\McpRequestEvent;
use Milpa\McpServer\Events\McpRespondedEvent;
use Milpa\McpServer\JsonRpcService;

// A cache plugin: short-circuit tools/call for a known-idempotent tool + args pair.
$dispatcher->subscribe('mcp.request', function (string $eventName, array $payload): void {
    $event = $payload['event']; // McpRequestEvent
    /** @var InterceptionSlot $slot */
    $slot = $payload['slot'];

    if ($event->getMethod() === 'tools/call') {
        $cached = $cache->get($event->getParams());
        if ($cached !== null) {
            $slot->shortCircuit($cached); // the tool never actually runs
        }
    }
});

// An audit plugin: log the outcome of every dispatched method, cache hit or not.
$dispatcher->subscribe('mcp.responded', function (string $eventName, array $payload): void {
    /** @var McpRespondedEvent $event */
    $event = $payload['event'];
    $logger->info('mcp method responded', [
        'method' => $event->getMethod(),
        'ok' => !isset($event->getResponse()['error']),
    ]);
});

$rpc = new JsonRpcService($registry, $dispatcher);

use Milpa\McpServer\Auth\Principal;
use Milpa\McpServer\Auth\TokenValidatorInterface;

final class DatabaseTokenValidator implements TokenValidatorInterface
{
    public function validate(string $token, ?string $ip = null): ?Principal
    {
        $record = $this->tokens->findValidByToken($token); // your own store, your own rules
        if ($record === null) {
            return null;
        }

        $record->recordUsage($ip); // optional side effect, entirely up to the implementation

        return new Principal(
            id: "user:{$record->ownerId()}",
            displayName: $record->ownerName(),
            scopes: $record->scopes(),
            extra: ['token_prefix' => $record->prefix()],
        );
    }
}

use Milpa\ToolRuntime\Contracts\ToolContext;

$ctx = ToolContext::mcp(
    requestId: (string) ($request['id'] ?? uniqid('mcp-', true)),
    principal: 'stdio',   // any non-empty string satisfies 

while (($line = fgets(STDIN)) !== false) {
    $request = json_decode(trim($line), true);
    // ... decode/validate $request as JSON, handle parse errors, etc ...

    $response = $rpc->handle($request, $ctx);

    if ($response === null) {
        continue; // notification (or a batch/notification hybrid edge case) — write nothing
    }

    fwrite(STDOUT, json_encode($response) . "\n");
    fflush(STDOUT);
}