php-sdk/examples/server
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Alex Rothberg fc456125e2
[Schema] Add resource_link content block (#400)
* Add resource_link content block

The spec's `resource_link` content type (protocol revision 2025-06-18,
still present in 2025-11-25) had no corresponding schema class: it lets a
tool result or prompt message reference a resource by URI/name without
embedding its full contents, the way `EmbeddedResource` does. Without it,
tools returning many or large resource references (e.g. search results
over a DAM) are stuck smuggling URIs through text/structured content.

Add `Mcp\Schema\Content\ResourceLink`, mirroring the spec's `Resource`
shape (uri, name, title, description, mimeType, annotations, size, icons,
_meta) with a fixed `type: "resource_link"`, following the conventions of
the sibling `EmbeddedResource`/`ResourceDefinition` classes.

Register it everywhere `resource` content blocks are (de)serialized by
type per the spec's `ContentBlock` union:
- `CallToolResult::fromArray()`
- `PromptMessage` (constructor type, `fromArray()`, phpstan types)
- `PromptResultFormatter` (typed-array and direct-instance paths)

`SamplingMessage`/`CreateSamplingMessageResult` are intentionally left
unchanged: the spec's `SamplingMessageContentBlock` does not include
`ResourceLink`.

Fixes #399

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Add resource_link example coverage to discovery-userprofile

Add a lookup_user tool to the discovery-userprofile example: given a
userId, it returns a short text summary plus a resource_link pointing
at the existing user://{userId}/profile resource template, instead of
embedding the profile. This mirrors the PR's motivating use case (a
tool referencing many/large resources by URI rather than inlining
them) and exercises resource_link through the example's own inspector
snapshot tests (tools_call-lookup_user[_not_found], tools_list).

Wiring this up surfaced a real bug: ToolReference::extractStructuredContent()
passed any raw array return value straight through as structuredContent,
even when it held Content instances (e.g. the TextContent/ResourceLink
pair above). That serializes to a JSON list, which fails the spec's
requirement that structuredContent be an object - the inspector's MCP
client rejected the tool call outright until fixed. Skip structuredContent
extraction whenever the raw result contains a Content item, since those
are already represented in `content` and were never meant to double as
structured data.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Alex Rothberg <51059+cancan101@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 23:20:56 +02:00
..
cached-discovery CS update after release of v3.95 (#282) 2026-04-26 12:39:27 +02:00
client-communication [Client][Server] Add Roots support (#395) 2026-08-14 23:04:25 +02:00
client-logging Move examples to server directory (#198) 2025-12-27 15:39:05 +01:00
combined-registration [Examples] Demo pre-built instance handlers + inspector coverage (#390) 2026-07-15 00:23:20 +02: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 [Schema] Add resource_link content block (#400) 2026-08-14 23:20:56 +02: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
mcp-apps [Schema][Server] Add MCP Apps extension (io.modelcontextprotocol/ui) support (#281) 2026-06-01 09:35:54 +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