php-sdk/examples/server
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Volodymyr Panivko 293a5880e4
[Server] Compose StreamableHttpTransport security middleware via defaultMiddleware() factory (#307)
* [Server] Add CORS, DNS rebinding and protocol version middleware

Introduce three PSR-15 middleware for `StreamableHttpTransport` exposed
through a public `StreamableHttpTransport::defaultMiddleware()` factory
composed automatically when no middleware is passed.

- `CorsMiddleware`: secure-by-default (no `Access-Control-Allow-Origin`),
  configurable allowlist, reflects matching origin with `Vary: Origin`
  to protect shared caches.
- `DnsRebindingProtectionMiddleware`: validates `Origin`/`Host` against
  a hostname allowlist (localhost-only by default).
- `ProtocolVersionMiddleware`: rejects requests carrying an unsupported
  `Mcp-Protocol-Version` header with `400 Bad Request`.

The transport no longer applies CORS via an `instanceof + array_unshift`
post-hook; the middleware parameter is nullable — `null` installs the
secure defaults, `[]` disables them, and users compose by spreading
`StreamableHttpTransport::defaultMiddleware()`. `SESSION_HEADER` and
`PROTOCOL_VERSION_HEADER` are promoted to public constants so middleware
can reuse them.

BC breaks:
- The `corsHeaders` constructor parameter is removed; the `middleware`
  parameter shifts one position. Positional callers passing the old
  `corsHeaders` argument must switch to named arguments or drop it.
- Default `Access-Control-Allow-Origin` is no longer `*`.

Addresses #260 (DNS rebinding), #277 (CORS extraction) and #306
(protocol version validation).

* [Server] Address review feedback on middleware defaults

- CorsMiddleware: emit Access-Control-Allow-Methods/Headers only on
  preflight per CORS spec; tokenize Vary header to avoid substring
  false-positives; add allowCredentials flag that emits
  Access-Control-Allow-Credentials and throws when combined with
  wildcard origin.
- DnsRebindingProtectionMiddleware: return plain-text 403 body
  (HTTP-level rejection, not JSON-RPC); use parse_url(..., PHP_URL_HOST)
  for origin parsing; drop redundant unbracketed '::1' from default
  allowlist.
- ProtocolVersionMiddleware: use Error::forInvalidParams instead of
  forInvalidRequest -- JSON-RPC -32602 fits header-value rejection
  better.
- JsonRpcErrorResponse::create now accepts a JsonRpc\Error so callers
  choose the error code.
- StreamableHttpTransport: emit a warning log when an explicit empty
  middleware list is passed, so operators can spot accidental bypass
  of the default security stack.

* [Server] Fix CI: switch warning-log tests to PHPUnit mocks

Replace the anonymous AbstractLogger fixture in
StreamableHttpTransportTest with `$this->createMock(LoggerInterface::class)`
so the test does not declare a `log()` signature at all. That sidesteps
both the psr/log v1 LSP incompatibility (untyped parameter) and the
phpstan `missingType.parameter` complaint in one move, and matches how
the rest of the suite (CallToolHandlerTest etc.) fakes loggers.

* Update src/Server/Transport/Http/Middleware/CorsMiddleware.php

Co-authored-by: Antoine Bluchet <soyuka@users.noreply.github.com>

---------

Co-authored-by: Antoine Bluchet <soyuka@users.noreply.github.com>
2026-05-25 13:49:43 +02:00
..
cached-discovery CS update after release of v3.95 (#282) 2026-04-26 12:39:27 +02:00
client-communication [Schema][Server] Add top-level title to Tool (#288) 2026-04-26 15:37:40 +02:00
client-logging Move examples to server directory (#198) 2025-12-27 15:39:05 +01:00
combined-registration Move examples to server directory (#198) 2025-12-27 15:39:05 +01:00
complex-tool-schema Use SchemaValidator to validate tool input (#203) 2026-01-11 21:46:53 +01:00
custom-dependencies Update codestyle based on updated Symfony rules (#229) 2026-02-01 11:54:31 +01:00
custom-method-handlers [Schema][Server] Add top-level title to Tool (#288) 2026-04-26 15:37:40 +02:00
discovery-calculator Move examples to server directory (#198) 2025-12-27 15:39:05 +01:00
discovery-userprofile Update codestyle based on updated Symfony rules (#229) 2026-02-01 11:54:31 +01:00
elicitation [Schema][Server] Add top-level title to Tool (#288) 2026-04-26 15:37:40 +02:00
env-variables Update codestyle based on updated Symfony rules (#229) 2026-02-01 11:54:31 +01:00
explicit-registration Move examples to server directory (#198) 2025-12-27 15:39:05 +01:00
oauth-keycloak [Server] Compose StreamableHttpTransport security middleware via defaultMiddleware() factory (#307) 2026-05-25 13:49:43 +02:00
oauth-microsoft [Server] Compose StreamableHttpTransport security middleware via defaultMiddleware() factory (#307) 2026-05-25 13:49:43 +02:00
schema-showcase Update codestyle based on updated Symfony rules (#229) 2026-02-01 11:54:31 +01:00
bootstrap.php [Client] Feat: Implement MCP client component (#192) 2026-03-10 17:22:40 +01:00
README.md [Client] Feat: Implement MCP client component (#192) 2026-03-10 17:22:40 +01:00

MCP SDK Examples

This directory contains various examples of how to use the PHP MCP SDK.

You can run the examples with the dependencies already installed in the root directory of the SDK. The bootstrapping of the example will choose the used transport based on the SAPI you use.

For running an example, you execute the server.php like this:

# For using the STDIO transport:
php examples/server/discovery-calculator/server.php

# For using the Streamable HTTP transport:
php -S localhost:8000 examples/server/discovery-userprofile/server.php

You will see debug outputs to help you understand what is happening.

Run with Inspector:

npx @modelcontextprotocol/inspector php examples/server/discovery-calculator/server.php

Debugging

You can enable debug output by setting the DEBUG environment variable to 1, and additionally log to a file by setting the FILE_LOG environment variable to 1 as well. A dev.log file gets written within the example's directory.

With the Inspector you can set the environment variables like this:

npx @modelcontextprotocol/inspector -e DEBUG=1 -e FILE_LOG=1 php examples/server/discovery-calculator/server.php