Add API documentation generation with phpDocumentor (#184)

* Add API documentation generation with phpDocumentor

- Add phpDocumentor configuration and `docs` Makefile target
- Generate docs during CI to catch errors before releases
- Set up GitHub Pages with Jekyll for hosting generated docs
- Add GitHub Actions workflow to deploy docs to GitHub Pages on releases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix phpDocumentor errors in type annotations

Change class-string union types to use phpDocumentor-compatible syntax
and reorder docblock tags so class descriptions precede `@author` tags.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix CI: pin phar-io/composer-distributor ^1.0.2

phar-io/composer-distributor 1.0.0 has a bug where it uses the wrong
package version when determining the phpDocumentor download URL,
causing CI to fail with 404 errors when using --prefer-lowest.
Version 1.0.2 fixes this issue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Integrate markdown guides into phpDocumentor output

Configure phpDocumentor to generate both API documentation and user
guides in a single unified site. This replaces the previous Jekyll-based
approach where API docs were copied into the docs folder.

- Add `<guide>` configuration to `phpdoc.dist.xml`
- Create custom template with "Guides" navigation link
- Replace `docs/index.html` redirect with `docs/index.md` guide index
- Update workflow to publish `build/docs` directly without Jekyll
- Track `.phpdoc/template/` while still ignoring `.phpdoc/cache/`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fine-tuning for path, config, and layout

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
This commit is contained in:
Jonathan Hefner 2026-02-01 09:22:40 -06:00 • committed by GitHub
commit 940eb90872
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 131 additions and 7 deletions

35
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Deploy Documentation
on:
release:
types: [published]
workflow_dispatch:
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: "none"
- name: Install Composer
uses: "ramsey/composer-install@v3"
- name: Generate Documentation
run: make docs
- name: Deploy to gh-pages branch
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./.phpdoc/build
enable_jekyll: false

View file

@ -142,3 +142,6 @@ jobs:
- name: PHPStan
run: vendor/bin/phpstan analyse
- name: Documentation
run: make docs

4
.gitignore vendored
View file

@ -9,3 +9,7 @@ examples/**/sessions
tests/Conformance/results
tests/Conformance/sessions
tests/Conformance/logs/*.log
# phpDocumentor
.phpdoc/build/
.phpdoc/cache/

View file

@ -0,0 +1,13 @@
{% extends 'layout.html.twig' %}
{% set topMenu = {
"menu": [
{ "name": "Guides", "url": "docs/index.html"},
{ "name": "Specification", "url": "https://modelcontextprotocol.io/" }
],
"social": [
{ "iconClass": "fab fa-github", "url": "https://github.com/modelcontextprotocol/php-sdk"},
{ "iconClass": "fab fa-discord", "url": "https://discord.gg/6CSzBmMkjX"}
]
}
%}

View file

@ -0,0 +1,10 @@
<h1 class="phpdocumentor-title">
<a href="{{ path('/docs/index.html') }}" class="phpdocumentor-title__link">
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 195 195" fill="none">
<path d="M25 97.8528L92.8823 29.9706C102.255 20.598 117.451 20.598 126.823 29.9706V29.9706C136.196 39.3431 136.196 54.5391 126.823 63.9117L75.5581 115.177" stroke="black" stroke-width="12" stroke-linecap="round"/>
<path d="M76.2653 114.47L126.823 63.9117C136.196 54.5391 151.392 54.5391 160.765 63.9117L161.118 64.2652C170.491 73.6378 170.491 88.8338 161.118 98.2063L99.7248 159.6C96.6006 162.724 96.6006 167.789 99.7248 170.913L112.331 183.52" stroke="black" stroke-width="12" stroke-linecap="round"/>
<path d="M109.853 46.9411L59.6482 97.1457C50.2757 106.518 50.2757 121.714 59.6482 131.087V131.087C69.0208 140.459 84.2168 140.459 93.5894 131.087L143.794 80.8822" stroke="black" stroke-width="12" stroke-linecap="round"/>
</svg>
<span style="margin-left: 0.25em;">{{ project.name }}</span>
</a>
</h1>

View file

@ -1,4 +1,4 @@
.PHONY: deps-stable deps-low cs phpstan tests unit-tests inspector-tests coverage ci ci-stable ci-lowest conformance-tests
.PHONY: deps-stable deps-low cs phpstan tests unit-tests inspector-tests coverage ci ci-stable ci-lowest conformance-tests docs
deps-stable:
composer update --prefer-stable
@ -36,3 +36,8 @@ ci: ci-stable
ci-stable: deps-stable cs phpstan tests
ci-lowest: deps-low cs phpstan tests
docs:
vendor/bin/phpdoc
@grep -q 'No errors have been found' .phpdoc/build/reports/errors.html || \
(echo "Documentation errors found. See build/docs/reports/errors.html" && exit 1)

View file

@ -36,7 +36,9 @@
"laminas/laminas-httphandlerrunner": "^2.12",
"nyholm/psr7": "^1.8",
"nyholm/psr7-server": "^1.1",
"phar-io/composer-distributor": "^1.0.2",
"php-cs-fixer/shim": "^3.91",
"phpdocumentor/shim": "^3",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^10.5",
"psr/simple-cache": "^2.0 || ^3.0",
@ -69,7 +71,8 @@
},
"config": {
"allow-plugins": {
"php-http/discovery": false
"php-http/discovery": false,
"phpdocumentor/shim": true
},
"sort-packages": true
}

7
docs/index.md Normal file
View file

@ -0,0 +1,7 @@
# MCP PHP SDK Guides
- [MCP Elements](mcp-elements.md) — Core capabilities (Tools, Resources, Resource Templates, and Prompts) with registration methods.
- [Server Builder](server-builder.md) — Fluent builder class for creating and configuring MCP server instances.
- [Transports](transports.md) — STDIO and HTTP transport implementations with guidance on choosing between them.
- [Server-Client Communication](server-client-communication.md) — Methods for servers to communicate back to clients: sampling, logging, progress, and notifications.
- [Examples](examples.md) — Example projects demonstrating attribute-based discovery, dependency injection, HTTP transport, and more.

View file

@ -0,0 +1,78 @@
# Client Communication
MCP supports various ways a server can communicate back to a server on top of the main request-response flow.
## Table of Contents
- [ClientGateway](#client-gateway)
- [Sampling](#sampling)
- [Logging](#logging)
- [Notification](#notification)
- [Progress](#progress)
## ClientGateway
Every communication back to client is handled using the `Mcp\Server\ClientGateway` and its dedicated methods per
operation. To use the `ClientGateway` in your code, you need to use method argument injection for `RequestContext`.
Every reference of a MCP element, that translates to an actual method call, can just add an type-hinted argument for the
`RequestContext` and the SDK will take care to include the gateway in the arguments of the method call:
```php
use Mcp\Capability\Attribute\McpTool;
use Mcp\Server\RequestContext;
class MyService
{
#[McpTool('my_tool', 'My Tool Description')]
public function myTool(RequestContext $context): string
{
$context->getClientGateway()->log(...);
```
## Sampling
With [sampling](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling) servers can request clients to
execute "completions" or "generations" with a language model for them:
```php
$result = $clientGateway->sample('Roses are red, violets are', 350, 90, ['temperature' => 0.5]);
```
The `sample` method accepts four arguments:
1. `message`, which is **required** and accepts a string, an instance of `Content` or an array of `SampleMessage` instances.
2. `maxTokens`, which defaults to `1000`
3. `timeout` in seconds, which defaults to `120`
4. `options` which might include `system_prompt`, `preferences` for model choice, `includeContext`, `temperature`, `stopSequences` and `metadata`
[Find more details to sampling payload in the specification.](https://modelcontextprotocol.io/specification/2025-06-18/client/sampling#protocol-messages)
## Logging
The [Logging](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging) utility enables servers
to send structured log messages as notifcation to clients:
```php
use Mcp\Schema\Enum\LoggingLevel;
$clientGateway->log(LoggingLevel::Warning, 'The end is near.');
```
## Progress
With a [Progress](https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/progress#progress)
notification a server can update a client while an operation is ongoing:
```php
$clientGateway->progress(4.2, 10, 'Downloading needed images.');
```
## Notification
Lastly, the server can push all kind of notifications, that implement the `Mcp\Schema\JsonRpc\Notification` interface
to the client to:
```php
$clientGateway->notify($yourNotification);
```

44
phpdoc.dist.xml Normal file
View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdocumentor
configVersion="3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://www.phpdoc.org"
xsi:noNamespaceSchemaLocation="https://docs.phpdoc.org/latest/phpdoc.xsd"
>
<title>MCP PHP SDK</title>
<paths>
<output>.phpdoc/build</output>
</paths>
<version number="latest">
<folder>latest</folder>
<api ignore-packages="true">
<source dsn=".">
<path>src</path>
</source>
<output>api</output>
<ignore>
<path>vendor/**/*</path>
<path>tests/**/*</path>
</ignore>
<ignore-tags>
<ignore-tag>phpstan-type</ignore-tag>
<ignore-tag>phpstan-type-import</ignore-tag>
<ignore-tag>template</ignore-tag>
<ignore-tag>template-covariant</ignore-tag>
<ignore-tag>template-extends</ignore-tag>
<ignore-tag>template-implements</ignore-tag>
<ignore-tag>extends</ignore-tag>
<ignore-tag>implements</ignore-tag>
</ignore-tags>
</api>
<guide format="md">
<source dsn=".">
<path>docs</path>
</source>
<output>/</output>
</guide>
</version>
<setting name="guides.enabled" value="true"/>
<setting name="graphs.enabled" value="false"/>
<setting name="template.color" value="black"/>
</phpdocumentor>

View file

@ -37,7 +37,7 @@ final class MessageFactory
/**
* Registry of all known message classes that have methods.
*
* @var array<int, class-string<Request|Notification>>
* @var list<class-string<Request>|class-string<Notification>>
*/
private const REGISTERED_MESSAGES = [
Schema\Notification\CancelledNotification::class,
@ -68,7 +68,7 @@ final class MessageFactory
];
/**
* @param array<int, class-string<Request|Notification>> $registeredMessages
* @param list<class-string<Request>|class-string<Notification>> $registeredMessages
*/
public function __construct(
private readonly array $registeredMessages,
@ -151,7 +151,7 @@ final class MessageFactory
/**
* Finds the registered message class for a given method name.
*
* @return class-string<Request|Notification>
* @return class-string<Request>|class-string<Notification>
*
* @throws InvalidInputMessageException
*/

View file

@ -17,12 +17,12 @@ use Psr\SimpleCache\CacheInterface;
use Symfony\Component\Uid\Uuid;
/**
* @author luoyue <1569097443@qq.com>
*
* PSR-16 compliant cache-based session store.
*
* This implementation uses any PSR-16 compliant cache as the storage backend
* for session data. Each session is stored with a prefixed key using the session ID.
*
* @author luoyue <1569097443@qq.com>
*/
class Psr16StoreSession implements SessionStoreInterface
{