### Run Keycloak OAuth example Source: https://php.sdk.modelcontextprotocol.io/run/authorization Commands to navigate to the Keycloak example directory and start the environment using Docker Compose. ```bash cd examples/server/oauth-keycloak docker-compose up -d # Test credentials: demo / demo123 ``` -------------------------------- ### Complete Server Builder Configuration Example Source: https://php.sdk.modelcontextprotocol.io/run/server-builder A comprehensive example demonstrating server identity, pagination, discovery caching, session management, and manual capability registration. ```php use Mcp\Server; use Mcp\Server\Session\FileSessionStore; use Mcp\Capability\Registry\Container; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Psr16Cache; use Monolog\Logger; use Monolog\Handler\StreamHandler; // Setup dependencies $logger = new Logger('mcp-server'); $logger->pushHandler(new StreamHandler('mcp.log', Logger::INFO)); $cache = new Psr16Cache(new FilesystemAdapter('mcp-discovery')); $sessionStore = new FileSessionStore(__DIR__ . '/sessions'); // Setup container with dependencies $container = new Container(); $container->set(\PDO::class, new \PDO('sqlite::memory:')); $container->set(DatabaseService::class, new DatabaseService($container->get(\PDO::class))); // Build server $server = Server::builder() // Server identity ->setServerInfo('Advanced Calculator', '2.1.0') // Performance and behavior ->setPaginationLimit(100) ->setInstructions('Use calculate tool for math operations. Check config resource for current settings.') // Discovery with caching ->setDiscovery(__DIR__, ['src'], ['vendor', 'tests'], $cache) // Session management ->setSession($sessionStore) // Services ->setLogger($logger) ->setContainer($container) // Manual capability registration ->addTool([Calculator::class, 'advancedCalculation'], 'advanced_calc') ->addResource([Config::class, 'getSettings'], 'config://app/settings', 'app_settings') // Build the server ->build(); ``` -------------------------------- ### Comprehensive Client Usage Example Source: https://php.sdk.modelcontextprotocol.io/client/errors A full example demonstrating client configuration, logging handlers, sampling callbacks, and server interaction. ```php level->value}] {$notification->data}\n"; } ); // Configure sampling callback $samplingCallback = new class implements SamplingCallbackInterface { public function __invoke(CreateSamplingMessageRequest $request): CreateSamplingMessageResult { echo "[SAMPLING] Processing request (max {$request->maxTokens} tokens)\n"; try { // Integration with your LLM provider $response = "This is a mock LLM response for: " . json_encode($request->messages); return new CreateSamplingMessageResult( role: Role::Assistant, content: new TextContent($response), model: 'mock-llm', stopReason: 'endTurn', ); } catch (\Throwable $e) { throw new SamplingException( "Sampling failed: {$e->getMessage()}", 0, $e ); } } }; // Build client $client = Client::builder() ->setClientInfo('Example Client', '1.0.0') ->setInitTimeout(30) ->setRequestTimeout(120) ->setCapabilities(new ClientCapabilities(sampling: true)) ->addNotificationHandler($loggingHandler) ->addRequestHandler(new SamplingRequestHandler($samplingCallback)) ->build(); // Create transport $transport = new StdioTransport( command: 'php', args: [__DIR__ . '/server.php'], ); // Connect and use server try { echo "Connecting to server...\n"; $client->connect($transport); // Get server info $serverInfo = $client->getServerInfo(); echo "Connected to: {$serverInfo->name} v{$serverInfo->version}\n\n"; // List capabilities echo "Available tools:\n"; $tools = $client->listTools(); foreach ($tools->tools as $tool) { echo " - {$tool->name}\n"; } echo "\nAvailable resources:\n"; $resources = $client->listResources(); foreach ($resources->resources as $resource) { echo " - {$resource->uri}\n"; } // Set logging level $client->setLoggingLevel(LoggingLevel::Debug); // Call tool with progress echo "\nCalling tool with progress...\n"; $result = $client->callTool( name: 'process_data', arguments: ['dataset' => 'large_file.csv'], onProgress: static function (float $progress, ?float $total, ?string $message) { $percent = $total > 0 ? round(($progress / $total) * 100) : 0; echo " Progress: {$percent}% - {$message}\n"; } ); echo "\nResult:\n"; foreach ($result->content as $content) { if ($content instanceof TextContent) { echo $content->text . "\n"; } } } catch (\Throwable $e) { echo "Error: {$e->getMessage()}\n"; echo $e->getTraceAsString() . "\n"; } finally { $client->disconnect(); echo "\nDisconnected.\n"; } ``` -------------------------------- ### Run Microsoft Entra ID OAuth example Source: https://php.sdk.modelcontextprotocol.io/run/authorization Commands to navigate to the Microsoft Entra ID example directory, prepare the environment file, and start the services. ```bash cd examples/server/oauth-microsoft cp env.example .env # Edit .env with your Azure credentials docker-compose up -d ``` -------------------------------- ### Run server examples with STDIO, HTTP, and MCP Inspector Source: https://php.sdk.modelcontextprotocol.io/examples Commands to run example servers from the repository root. The first uses STDIO transport, the second uses Streamable HTTP via PHP's built-in server, and the third launches the MCP Inspector for interactive testing. ```bash # STDIO transport php examples/server/discovery-calculator/server.php ``` ```bash # Streamable HTTP transport php -S 127.0.0.1:8000 examples/server/discovery-userprofile/server.php ``` ```bash # Interactive testing with the MCP Inspector npx @modelcontextprotocol/inspector php examples/server/discovery-calculator/server.php ``` -------------------------------- ### Production Setup with Caching Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Implements PSR-16 caching to improve performance by avoiding filesystem scanning on every server start. Always exclude vendor/ directories to prevent scan aborts. ```php use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\ Component\Cache\Psr16Cache; // Cache discovered elements to avoid filesystem scanning on every server start $cache = new Psr16Cache(new FilesystemAdapter('mcp-discovery')); $server = Server::builder() ->setDiscovery( basePath: __DIR__, scanDirs: ['src', 'lib'], // Scan these directories recursively excludeDirs: ['vendor', 'tests', 'temp'], // Skip these directory names within scanned dirs cache: $cache // Cache for performance ) ->build(); ``` -------------------------------- ### Basic Discovery Setup Source: https://php.sdk.modelcontextprotocol.io/run/server-builder A minimal configuration that scans the current directory and the src/ directory while excluding the vendor directory. ```php $server = Server::builder() ->setDiscovery(__DIR__, excludeDirs: ['vendor']) // Scans '.' and 'src' ->build(); ``` -------------------------------- ### Start HTTP server and launch Inspector Source: https://php.sdk.modelcontextprotocol.io/get-started/inspector Starts the PHP built-in server and then launches the MCP Inspector against the HTTP URL. The Inspector will connect to the server over HTTP. ```bash php -S localhost:8000 server.php npx @modelcontextprotocol/inspector http://localhost:8000 ``` -------------------------------- ### Configure and run a server Source: https://php.sdk.modelcontextprotocol.io/run Use Server::builder() to configure server settings and discovery, then call run() with a transport implementation to start the server. ```php $server = Server::builder() ->setServerInfo('My Server', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build(); $transport = new SomeTransport(); $result = $server->run($transport); // Blocks for STDIO, returns a response for HTTP ``` -------------------------------- ### Install MCP SDK via Composer Source: https://php.sdk.modelcontextprotocol.io/get-started/installation Run this command to install the core MCP SDK package. ```bash composer require mcp/sdk ``` -------------------------------- ### Start Stateless Lifecycle Server Source: https://php.sdk.modelcontextprotocol.io/examples Command to start the stateless-lifecycle server using PHP's built-in development server. This server answers both eras on the same URL. ```bash php -S 127.0.0.1:8000 examples/server/stateless-lifecycle/server.php ``` -------------------------------- ### Run stateless lifecycle client Source: https://php.sdk.modelcontextprotocol.io/examples Starts the server on localhost and executes the stateless client script to discover the server and call a tool. ```bash php -S 127.0.0.1:8000 examples/server/stateless-lifecycle/server.php & php examples/client/stateless_lifecycle_client.php ``` -------------------------------- ### Install symfony/finder dependency Source: https://php.sdk.modelcontextprotocol.io/get-started/first-server Installs the symfony/finder package required for server discovery. ```bash composer require symfony/finder ``` -------------------------------- ### Create an MCP Server in server.php Source: https://php.sdk.modelcontextprotocol.io/ This example demonstrates a complete MCP server using the Calculator class with McpTool and McpResource attributes. It requires symfony/finder for attribute discovery. ```php 2]; } } Server::builder() ->setServerInfo('Calculator', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build() ->run(new StdioTransport()); ``` -------------------------------- ### Run STDIO Discovery Calculator Client Source: https://php.sdk.modelcontextprotocol.io/examples Command to run the stdio_discovery_calculator.php client example directly. Connects over STDIO, lists and calls tools, and reads resources. ```bash php examples/client/stdio_discovery_calculator.php ``` -------------------------------- ### Server::builder() Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Static method to obtain a Server Builder instance. Recommended way to start building an MCP server. ```APIDOC ## Server::builder() ### Description Static method to obtain a Server Builder instance. Recommended way to start building an MCP server. ### Method Static ### Endpoint Server::builder() ### Parameters None ### Request Example ```php use Mcp\Server; $server = Server::builder() ->setServerInfo('My MCP Server', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build(); ``` ### Response #### Success Response - **Builder** (Builder) - A Builder instance for fluent configuration. ``` -------------------------------- ### Retrieve server information with getServerInfo() and getInstructions() Source: https://php.sdk.modelcontextprotocol.io/client/connecting After successful connection, get server implementation info (name and version) and optional instructions. The instructions block is conditional on a truthy value. ```php // Get server implementation info $serverInfo = $client->getServerInfo(); echo "Server: {$serverInfo->name} v{$serverInfo->version}\n"; // Get server instructions $instructions = $client->getInstructions(); if ($instructions) { echo "Instructions: {$instructions}\n"; } ``` -------------------------------- ### Test HTTP Transport with MCP Inspector Source: https://php.sdk.modelcontextprotocol.io/run/framework-integration Start the PHP server and connect using the MCP Inspector CLI tool. ```bash # Start your PHP server php -S localhost:8000 server.php # Connect with MCP Inspector npx @modelcontextprotocol/inspector http://localhost:8000 ``` -------------------------------- ### Install Optional PSR-17 Implementation Source: https://php.sdk.modelcontextprotocol.io/get-started/installation Install a PSR-17 implementation like nyholm/psr7 to enable HTTP support. The SDK uses php-http/discovery to automatically detect installed implementations. ```bash composer require nyholm/psr7 ``` -------------------------------- ### Configure Server with Server::builder() Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Demonstrates configuring a server instance with request state, input limits, cache policies, notification bus, and modern protocol versions. ```php use Mcp\Schema\Enum\CacheScope; use Mcp\Schema\Enum\ProtocolVersion; use Mcp\Server\Subscription\Psr16NotificationBus; use Mcp\Server\Wire\CachePolicy; $server = Server::builder() // Signs the state a multi round-trip request carries through the client. // The same key must reach every process that might serve the retry. ->setRequestState($_ENV['MCP_REQUEST_STATE_KEY'], ttl: 600) // Bounds the input-required shim, which fulfils an `InputRequiredResult` // over a handshake-era connection. `withoutInputRequiredShim()` turns it off. ->setInputRequiredLimits(maxRounds: 4, roundTimeout: 120) // Caching hints stamped on cacheable results. Defaults to `ttlMs: 0, private`. ->setCachePolicy(CachePolicy::default(30_000)->withMethod('tools/list', 3_600_000, CacheScope::Public)) // Delivery for `subscriptions/listen`, and how long such a stream is held. ->setNotificationBus(new Psr16NotificationBus($cache)) ->setSubscriptionLifetime(0) // Narrow the modern leg, or drop it entirely. ->setModernVersions([ProtocolVersion::V2026_07_28]) ->build(); ``` -------------------------------- ### Symfony: Install PSR Libraries Source: https://php.sdk.modelcontextprotocol.io/run/framework-integration Composer command to install the required PSR libraries for Symfony integration. ```bash composer require symfony/psr-http-message-bridge nyholm/psr7 ``` -------------------------------- ### build() Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Creates the server instance. ```APIDOC ## build() ### Description Creates the server instance. ### Method build() ### Parameters None ``` -------------------------------- ### Set Server Instructions Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Provide hints to help AI models understand how to use the server. ```php $server = Server::builder() ->setInstructions('This calculator supports basic arithmetic operations. Use the calculate tool for math operations and check the config resource for current settings.'); ``` -------------------------------- ### Create server.php Source: https://php.sdk.modelcontextprotocol.io/get-started/first-server Defines a Calculator class with MCP tools and resources, and initializes the server using StdioTransport. ```php #!/usr/bin/env php 2]; } } exit(Server::builder() ->setServerInfo('Calculator', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build() ->run(new StdioTransport())); ``` -------------------------------- ### Install PSR-18 client for auto-discovery Source: https://php.sdk.modelcontextprotocol.io/client/transports Composer command to install a PSR-18 HTTP client. The transport automatically discovers PSR-18 clients from packages like php-http/guzzle7-adapter, php-http/curl-client, and symfony/http-client. ```bash # Install any PSR-18 client - discovery works automatically composer require php-http/guzzle7-adapter ``` -------------------------------- ### OidcDiscovery Source: https://php.sdk.modelcontextprotocol.io/run/authorization Discovers OAuth/OIDC server metadata and provides methods to get specific endpoints. ```APIDOC ## OidcDiscovery ### Description Discovers OAuth/OIDC server metadata and provides methods to get specific endpoints. ### Constructor ```php $discovery = new OidcDiscovery( httpClient: null, // PSR-18 (auto-discovered) requestFactory: null, // PSR-17 (auto-discovered) cache: $cache, // PSR-16 cache (optional) cacheTtl: 3600, // Cache TTL ); ``` ### Methods - `discover($issuer)` - Discovers metadata for the given issuer. - `getJwksUri($issuer)` - Returns the JWKS URI for the issuer. - `getTokenEndpoint($issuer)` - Returns the token endpoint for the issuer. - `getAuthorizationEndpoint($issuer)` - Returns the authorization endpoint for the issuer. ``` -------------------------------- ### ApiKeyValidator Implementation Source: https://php.sdk.modelcontextprotocol.io/run/authorization Example implementation of AuthorizationTokenValidatorInterface that validates API keys and returns an AuthorizationResult with attributes. ```APIDOC ## ApiKeyValidator Implementation ### Description Implements the AuthorizationTokenValidatorInterface to validate access tokens against a set of valid API keys. Returns an AuthorizationResult that either allows access with attributes or denies with an error. ### Interface AuthorizationTokenValidatorInterface ### Method validate(string $accessToken): AuthorizationResult ### Parameters - **accessToken** (string) - Required - The access token to validate. ### Return Value AuthorizationResult - The result of the validation, either allowing access with attributes or denying with an error. ### Example Code ```php use Mcp\Server\Transport\Http\OAuth\AuthorizationTokenValidatorInterface; use Mcp\Server\Transport\Http\OAuth\AuthorizationResult; final class ApiKeyValidator implements AuthorizationTokenValidatorInterface { public function __construct( private array $validKeys, ) {} public function validate(string $accessToken): AuthorizationResult { if (!isset($this->validKeys[$accessToken])) { return AuthorizationResult::unauthorized( 'invalid_token', 'Unknown API key' ); } $keyInfo = $this->validKeys[$accessToken]; return AuthorizationResult::allow([ 'api_key.name' => $keyInfo['name'], 'api_key.scopes' => $keyInfo['scopes'], ]); } } // Usage $validator = new ApiKeyValidator([ 'sk_live_abc123' => ['name' => 'Production', 'scopes' => ['read', 'write']], ]); ``` ``` -------------------------------- ### Create Server with Direct Instantiation Source: https://php.sdk.modelcontextprotocol.io/run/server-builder An alternative approach to initialize a server builder by directly instantiating the Builder class. ```php use Mcp\Server\Builder; $server = (new Builder()) ->setServerInfo('My MCP Server', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build(); ``` -------------------------------- ### getPrompt Source: https://php.sdk.modelcontextprotocol.io/client/capabilities Gets a prompt by name with optional arguments. Returns a result object containing an array of messages. ```APIDOC ## getPrompt ### Description Gets a prompt by name with optional arguments. Returns a result object containing an array of messages. ### Method getPrompt ### Parameters - **name** (string) - Required - The name of the prompt to get. - **arguments** (array) - Optional - Associative array of arguments for the prompt. - **onProgress** (callable) - Optional - Callback function receiving progress, total, and message. ### Request Example ```php $promptResult = $client->getPrompt( name: 'code_review', arguments: ['language' => 'php', 'code' => '...'], ); foreach ($promptResult->messages as $message) { echo "{$message->role->value}: {$message->content->text}\n"; } ``` ### Response - **messages** (array) - Array of message objects, each with role and content. ``` -------------------------------- ### getPrompt with Progress Source: https://php.sdk.modelcontextprotocol.io/client/capabilities Gets a prompt with progress notifications. The onProgress callback is invoked if the server sends progress updates. ```APIDOC ## getPrompt with Progress ### Description Gets a prompt with progress notifications. The onProgress callback is invoked if the server sends progress updates. ### Method getPrompt ### Parameters - **name** (string) - Required - The name of the prompt to get. - **arguments** (array) - Optional - Associative array of arguments for the prompt. - **onProgress** (callable) - Required - Callback function receiving progress, total, and message. ### Request Example ```php $result = $client->getPrompt( name: 'generate_report', arguments: ['topic' => 'quarterly_analysis'], onProgress: static function (float $progress, ?float $total, ?string $message) { echo "Generating: {$message}\n"; } ); ``` ### Response - **messages** (array) - Array of message objects. ``` -------------------------------- ### Create Server with Static Builder Method Source: https://php.sdk.modelcontextprotocol.io/run/server-builder The recommended approach to initialize a server builder using the static Server::builder() method. ```php use Mcp\Server; $server = Server::builder() ->setServerInfo('My MCP Server', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->build(); ``` -------------------------------- ### Call sample() with message, maxTokens, timeout, and options Source: https://php.sdk.modelcontextprotocol.io/handlers/client-communication Calls the sample() method on the client gateway to request a language model completion. The message is required; maxTokens defaults to 1000, timeout to 120 seconds, and options may include systemPrompt, preferences, includeContext, temperature, stopSequences, metadata, tools, and toolChoice. ```php $result = $clientGateway->sample('Roses are red, violets are', 350, 90, ['temperature' => 0.5]); ``` -------------------------------- ### Configure CorsMiddleware with StreamableHttpTransport Source: https://php.sdk.modelcontextprotocol.io/run/http Examples of configuring CorsMiddleware for specific origins and wildcard development settings within the StreamableHttpTransport constructor. ```php use Mcp\Server\Transport\Http\Middleware\CorsMiddleware; use Mcp\Server\Transport\Http\Middleware\DnsRebindingProtectionMiddleware; use Mcp\Server\Transport\StreamableHttpTransport; // Reflect a specific origin $transport = new StreamableHttpTransport( $request, middleware: [ new CorsMiddleware(allowedOrigins: ['https://myapp.com']), new DnsRebindingProtectionMiddleware(), ], ); // Allow all origins (development only) $transport = new StreamableHttpTransport( $request, middleware: [ new CorsMiddleware(allowedOrigins: ['*']), new DnsRebindingProtectionMiddleware(), ], ); ``` -------------------------------- ### setInstructions() Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Provides hints to help AI models understand how to use your server. ```APIDOC ## setInstructions() ### Description Provides hints to help AI models understand how to use your server. ### Method Fluent ### Endpoint Builder::setInstructions() ### Parameters #### Request Body - **$instructions** (string) - Required - Instructions for AI models ### Request Example ```php $server = Server::builder() ->setInstructions('This calculator supports basic arithmetic operations. Use the calculate tool for math operations and check the config resource for current settings.'); ``` ### Response #### Success Response - **Builder** (Builder) - The Builder instance for chaining. ``` -------------------------------- ### WWW-Authenticate Header for 401 Responses Source: https://php.sdk.modelcontextprotocol.io/run/authorization The header included by the middleware on 401 responses to guide clients to the protected resource metadata. ```http WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource", scope="mcp:read mcp:write" ``` -------------------------------- ### Configure Server Information Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Set the server's identity including name, version, description, icons, and website URL. ```php use Mcp\Schema\Icon; use Mcp\Server; $server = Server::builder() ->setServerInfo( name: 'Calculator Server', version: '1.2.0', description: 'Advanced mathematical calculations', icons: [new Icon('https://example.com/icon.png', 'image/png', ['64x64'])], websiteUrl: 'https://example.com', ); ``` -------------------------------- ### Protected Resource Metadata JSON Configuration Source: https://php.sdk.modelcontextprotocol.io/run/authorization Example JSON structure served by ProtectedResourceMetadataMiddleware to enable client discovery of the authorization server. ```json { "authorization_servers": ["https://auth.example.com/realms/mcp"], "scopes_supported": ["mcp:read", "mcp:write"], "resource": "https://mcp.example.com/mcp" } ``` -------------------------------- ### Get a prompt with arguments Source: https://php.sdk.modelcontextprotocol.io/client/capabilities Retrieves a prompt by name with an associative array of arguments. The result contains messages; each message has a role and content with text. ```php $promptResult = $client->getPrompt( name: 'code_review', arguments: ['language' => 'php', 'code' => '...'], ); foreach ($promptResult->messages as $message) { echo "{$message->role->value}: {$message->content->text}\n"; } ``` -------------------------------- ### Hybrid server builder with discovery and manual tool registration Source: https://php.sdk.modelcontextprotocol.io/servers/registration Combines directory-based discovery with manual tool registration. Manual registrations take precedence over discovered elements with the same identifier (name for tools/prompts, uri for resources, uriTemplate for resource templates). ```php $server = Server::builder() ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) // Discover most capabilities ->addTool([ExternalService::class, 'process'], 'external') // Add specific ones ->build(); ``` -------------------------------- ### Configure MCP Apps extension with Server builder Source: https://php.sdk.modelcontextprotocol.io/advanced/extensions Enable the McpApps extension and define a resource and tool pair linked via _meta.ui. ```php use Mcp\Schema\Content\TextResourceContents; use Mcp\Schema\Extension\Apps\McpApps; use Mcp\Schema\Extension\Apps\ToolVisibility; use Mcp\Schema\Extension\Apps\UiResourceContentMeta; use Mcp\Schema\Extension\Apps\UiResourceCsp; use Mcp\Schema\Extension\Apps\UiResourcePermissions; use Mcp\Schema\Extension\Apps\UiToolMeta; $server = Server::builder() ->enableExtension(new McpApps()) ->addResource( fn () => new TextResourceContents( uri: 'ui://my-app', mimeType: McpApps::MIME_TYPE, text: file_get_contents(__DIR__.'/app.html'), meta: ['ui' => new UiResourceContentMeta( csp: new UiResourceCsp(connectDomains: ['https://api.example.com']), permissions: new UiResourcePermissions(geolocation: true), prefersBorder: true, )], ), 'ui://my-app', mimeType: McpApps::MIME_TYPE, meta: ['ui' => McpApps::resourceMarker()], ) ->addTool( $myToolHandler, 'my_tool', meta: ['ui' => new UiToolMeta( resourceUri: 'ui://my-app', visibility: [ToolVisibility::Model, ToolVisibility::App], )], ) ->build(); ``` -------------------------------- ### Manual Tool Registration Source: https://php.sdk.modelcontextprotocol.io/servers/registration Demonstrates registering tools using various handler types: closures, class method pairs, instance methods, and invokable classes. ```php $server = Server::builder() // Using closure ->addTool( handler: function(int $a, int $b): int { return $a + $b; }, name: 'add_numbers', description: 'Adds two numbers together' ) // Using class method pair ->addTool( handler: [Calculator::class, 'multiply'], name: 'multiply_numbers' // name and description are optional - derived from method name and docblock ) // Using instance method ->addTool( handler: [$calculatorInstance, 'divide'] ) // Using invokable class ->addTool( handler: InvokableCalculator::class ); ``` -------------------------------- ### Get prompt with progress notifications Source: https://php.sdk.modelcontextprotocol.io/client/capabilities Passes an onProgress callback to getPrompt to receive progress updates while generating the prompt. The callback receives progress, total, and message. ```php $result = $client->getPrompt( name: 'generate_report', arguments: ['topic' => 'quarterly_analysis'], onProgress: static function (float $progress, ?float $total, ?string $message) { echo "Generating: {$message}\n"; } ); ``` -------------------------------- ### Build and connect an MCP client with StdioTransport Source: https://php.sdk.modelcontextprotocol.io/client Shows the synchronous client API: build a client with Client::builder(), set client info and timeouts, create a StdioTransport to launch a local PHP server, then connect, list tools, and disconnect. Every method returns a result or throws. ```php use Mcp\Client; use Mcp\Client\Transport\StdioTransport; // Build and configure the client $client = Client::builder() ->setClientInfo('My Client', '1.0.0') ->setInitTimeout(30) ->setRequestTimeout(120) ->build(); // Create a transport $transport = new StdioTransport( command: 'php', args: ['/path/to/server.php'], ); // Connect and use the server $client->connect($transport); $tools = $client->listTools(); $client->disconnect(); ``` -------------------------------- ### Serve modern era only with StatelessHttpTransport Source: https://php.sdk.modelcontextprotocol.io/run/protocol-eras Builds a stateless dispatcher that serves only the modern era (version `V2026_07_28`) and mounts it on `StatelessHttpTransport` to handle the request. This is the opposite of the handshake-only example. ```php $protocol = Server::builder() ->setServerInfo('My Server', '1.0.0') ->buildStateless([ProtocolVersion::V2026_07_28]); (new SapiEmitter())->emit((new StatelessHttpTransport($protocol))->handle($request)); ``` -------------------------------- ### Configure Client for Tool-Enabled Sampling Source: https://php.sdk.modelcontextprotocol.io/client/server-requests Configures the client with samplingTools capability and demonstrates returning ToolUseContent blocks within the sampling callback. ```php use Mcp\Schema\ClientCapabilities; use Mcp\Schema\Content\ToolUseContent; use Mcp\Schema\Enum\Role; use Mcp\Schema\Result\CreateSamplingMessageResult; $client = Client::builder() ->setCapabilities(new ClientCapabilities( sampling: true, samplingContext: true, samplingTools: true, )) ->addRequestHandler(new SamplingRequestHandler($samplingCallback)) ->build(); // Inside the sampling callback, after invoking the LLM provider: return new CreateSamplingMessageResult( role: Role::Assistant, content: array_map( static fn ($call) => new ToolUseContent($call->id, $call->name, $call->input), $providerResponse->toolCalls, ), model: $providerResponse->model, stopReason: 'toolUse', ); ``` -------------------------------- ### Establish connection with $client->connect($transport) Source: https://php.sdk.modelcontextprotocol.io/client/connecting Opens the transport and performs the MCP initialization handshake: sends InitializeRequest, waits for InitializeResult, and sends InitializedNotification. On modern revisions, it asks server/discover for identity instead. Wrap in try/catch to handle ConnectionException. ```php $client->connect($transport); ``` -------------------------------- ### Launch Inspector for STDIO server Source: https://php.sdk.modelcontextprotocol.io/get-started/inspector Starts the MCP Inspector against a PHP server using the STDIO transport. The Inspector launches the server process itself and prints a URL to open in the browser. ```bash npx @modelcontextprotocol/inspector php server.php ``` -------------------------------- ### Basic Client Builder Configuration Source: https://php.sdk.modelcontextprotocol.io/client/connecting Configure a client with client info, timeouts, and retries. The init timeout is in seconds, request timeout in seconds, and max retries controls connection retry count. ```php use Mcp\Client; $client = Client::builder() ->setClientInfo('My Application', '1.0.0', 'Description of my client') ->setInitTimeout(30) // Seconds to wait for initialization ->setRequestTimeout(120) // Seconds to wait for request responses ->setMaxRetries(3) // Retries for failed connections ->build(); ``` -------------------------------- ### Standalone Usage with PSR-17 Discovery and Laminas Emitter Source: https://php.sdk.modelcontextprotocol.io/run/framework-integration Simplified example of using the HTTP transport standalone, with PSR-17 discovery and Laminas emitter. Requires persistent sessions via FileSessionStore. ```php use Http\Discovery\Psr17Factory; use Mcp\Server; use Mcp\Server\Transport\StreamableHttpTransport; use Mcp\Server\Session\FileSessionStore; use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; $psr17Factory = new Psr17Factory(); $request = $psr17Factory->createServerRequestFromGlobals(); $server = Server::builder() ->setServerInfo('HTTP Server', '1.0.0') ->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor']) ->setSession(new FileSessionStore(__DIR__ . '/sessions')) // HTTP needs persistent sessions ->build(); $transport = new StreamableHttpTransport($request); $response = $server->run($transport); (new SapiEmitter())->emit($response); ``` -------------------------------- ### Claude Desktop configuration for calculator server Source: https://php.sdk.modelcontextprotocol.io/get-started/inspector Adds an entry to Claude Desktop's configuration file to launch the PHP server. Use an absolute path to server.php because the host does not run the command from the project directory. ```json { "mcpServers": { "calculator": { "command": "php", "args": ["/absolute/path/to/server.php"] } } } ``` -------------------------------- ### PHP @deprecated tag example Source: https://php.sdk.modelcontextprotocol.io/deprecation-policy Shows the format of a PHP @deprecated tag used for deprecated elements, naming the protocol revision (or SDK version) and the earliest removal date, twelve months out. ```php /** * @deprecated since protocol revision 2026-07-28 (SEP-2577), earliest removal 2027-07-28. */ ``` -------------------------------- ### setInstructions() Source: https://php.sdk.modelcontextprotocol.io/run/server-builder Sets usage instructions for the server. ```APIDOC ## setInstructions() ### Description Sets usage instructions for the server. ### Method setInstructions() ### Parameters - **instructions** (string) - Required - Usage instructions ``` -------------------------------- ### Initialize ProtectedResourceMetadataMiddleware Source: https://php.sdk.modelcontextprotocol.io/run/authorization Instantiates the middleware that serves protected resource metadata at well-known paths. ```php $metadataMiddleware = new ProtectedResourceMetadataMiddleware( metadata: $metadata, // ProtectedResourceMetadata instance responseFactory: null, // PSR-17 (auto-discovered) streamFactory: null, // PSR-17 (auto-discovered) ); ``` -------------------------------- ### Throw PromptGetException for invalid prompt style Source: https://php.sdk.modelcontextprotocol.io/servers/prompts Demonstrates error handling in a prompt handler by throwing PromptGetException for invalid input. This exception is converted to a JSON-RPC error response with the actual message, while other exceptions get a generic error message. ```php use Mcp\Exception\PromptGetException; #[McpPrompt] public function generatePrompt(string $topic, string $style): array { $validStyles = ['casual', 'formal', 'technical']; if (!in_array($style, $validStyles)) { throw new PromptGetException( "Invalid style '{$style}'. Must be one of: " . implode(', ', $validStyles) ); } return [ ['role' => 'user', 'content' => "Write about {$topic} in a {$style} style"] ]; } ``` -------------------------------- ### Define a resource template with #[McpResourceTemplate] Source: https://php.sdk.modelcontextprotocol.io/servers/resource-templates Use the McpResourceTemplate attribute to define a dynamic resource. The uriTemplate must start with a scheme and contain at least one variable. Variables are bound by name to method parameters, all are required, and type hints work normally. ```php use Mcp\Capability\Attribute\McpResourceTemplate; class UserProvider { /** * Retrieves user profile information by ID. */ #[McpResourceTemplate( uriTemplate: 'user://{userId}/profile/{section}', name: 'user_profile', description: 'User profile data by section', mimeType: 'application/json' )] public function getUserProfile(string $userId, string $section): array { return $this->users[$userId][$section] ?? throw new \InvalidArgumentException("Profile section not found"); } } ``` -------------------------------- ### Build a server with StreamableHttpTransport Source: https://php.sdk.modelcontextprotocol.io/run/protocol-eras Initializes an MCP server that supports both protocol eras using the StreamableHttpTransport. ```php use Mcp\Server; use Mcp\Server\Transport\StreamableHttpTransport; $server = Server::builder() ->setServerInfo('My Server', '1.0.0') ->addTool(static fn (string $city): string => "17°C in {$city}", name: 'get_weather', description: '…') ->build(); (new SapiEmitter())->emit($server->run(new StreamableHttpTransport($request))); ```