php-sdk/docs/examples.md at main · modelcontextprotocol/php-sdk · GitHub
Skip to content

Latest commit

 

History

History
95 lines (76 loc) · 8.18 KB

examples.md

File metadata and controls

95 lines (76 loc) · 8.18 KB

Examples

Every example lives in examples/ and runs against the dependencies installed in the repository root. Each server example is a server.php whose bootstrap picks the transport from the SAPI it runs under:

# STDIO transport
php examples/server/discovery-calculator/server.php

# Streamable HTTP transport
php -S 127.0.0.1:8000 examples/server/discovery-userprofile/server.php

# Interactive testing with the MCP Inspector
npx @modelcontextprotocol/inspector php examples/server/discovery-calculator/server.php

Server examples

Example What it shows Docs
discovery-calculator Attribute discovery of tools and a resource; ToolCallException for tool errors Tools
discovery-userprofile Resource templates and prompts with completion providers; ResourceLink content Resource templates, Prompts, Completions
explicit-registration Manual addTool()/addResource()/addResourceTemplate()/addPrompt() without discovery Registering elements
combined-registration Discovery and manual registration combined, and which wins on conflict Registering elements
cached-discovery Caching the discovery scan in a PSR-16 cache Server builder
custom-dependencies Handlers pulling services from a PSR-11 container Server builder
complex-tool-schema Rich input schemas from typed parameters, PHP enums and defaults Schema generation
schema-showcase #[Schema] constraint attributes: formats, patterns, ranges Schema generation
env-variables Configuring a server through environment variables Server builder
client-communication Sampling, roots, progress and log messages from inside a handler Talking back to the client
client-logging Structured log notifications through the ClientLogger Logging
elicitation Asking the user for input mid-call with ClientGateway::elicit() and typed elicitation schemas, on either protocol era Asking for input
custom-method-handlers Replacing the built-in tools/list and tools/call request handlers Custom message handlers
mcp-apps The MCP Apps extension: a tool that ships an interactive HTML view Protocol extensions
stateless-lifecycle Revision 2026-07-28: cache policy, request state, notification bus Serving both eras, Caching, Subscriptions
oauth-keycloak OAuth authorization against a Keycloak instance (own README) Authorization
oauth-microsoft OAuth authorization against Microsoft Entra ID (own README) Authorization

Client examples

Example What it shows Docs
stdio_discovery_calculator.php Connecting over STDIO, listing and calling tools, reading resources Connecting to a server
http_discovery_calculator.php The same conversation over the Streamable HTTP transport Transports
stdio_client_communication.php Answering server-initiated sampling, log and progress messages Server-initiated requests
http_client_communication.php The same handlers over the Streamable HTTP transport Server-initiated requests
stdio_elicitation.php Answering elicitation requests from an interactive prompt Server-initiated requests
stdio_roots.php Exposing workspace roots and signalling roots/list_changed Server-initiated requests
stateless_lifecycle_client.php A client that only connects on revision 2026-07-28, see Modern-era client Connecting to a server

Client examples run directly:

php examples/client/stdio_discovery_calculator.php

The 2026-07-28 lifecycle

stateless-lifecycle/server.php answers both eras on the same URL. On revision 2026-07-28 every request carries its own protocol version and client capabilities, so a call is a single POST with no handshake before it:

php -S 127.0.0.1:8000 examples/server/stateless-lifecycle/server.php
curl -sS http://127.0.0.1:8000/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
        "io.modelcontextprotocol/protocolVersion":"2026-07-28",
        "io.modelcontextprotocol/clientCapabilities":{}}}}'

The example's header comments document the equivalent handshake-era conversation against the same endpoint. Serving both eras explains how the routing works.

Modern-era client

stateless_lifecycle_client.php drives the server above from PHP. By default, a client probes the server with server/discover and falls back to the initialize handshake when the server doesn't speak 2026-07-28. The example turns that fallback off with setFallbackProtocolVersion(null), so it only connects on 2026-07-28. Then it lists and calls tools without a session:

php -S 127.0.0.1:8000 examples/server/stateless-lifecycle/server.php &
php examples/client/stateless_lifecycle_client.php

See Protocol version for the API it uses.