One placeholder-less resource template makes the whole server unserviceable
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- php
- Domain
- backend-api-design
Research direction
Start with Builder::addResourceTemplate(), then read ResourceTemplate.php and ReflectedElementLoader.php to understand where the invalid URI is currently rejected. Trace Registry::load() and the lazy-loading path to confirm the failure timing. Done means a placeholder-less template is rejected during registration rather than on the first request, without preventing valid elements from being registered.
Written by the indexing model from the issue text.
Description
Reported by Mariano Damian Ferro Villanueva via email, opening it here on their behalf.
Problem
A #[McpResourceTemplate] whose uriTemplate contains no placeholder takes the whole server down, not just that one template.
#[McpResourceTemplate(
uriTemplate: 'data://tags',
name: 'all_tags',
title: 'All Tags',
description: 'All Tags',
mimeType: 'application/json'
)]
public function tag_all(?int $paged = 1): array
{
// ...
}
The handshake still succeeds, and then every request fails, including ones that have nothing to do with resources:
tools/list -> {"jsonrpc":"2.0","id":2,"error":{"code":-32602,"message":"Error registering manual resource template 'data://tags': Invalid URI template : \"data://tags\" must be a valid URI template with at least one placeholder."}}
tools/call -> {"jsonrpc":"2.0","id":3,"error":{"code":-32602,"message":"Error registering manual resource template 'data://tags': Invalid URI template : \"data://tags\" must be a valid URI template with at least one placeholder."}}
Reproduced on main against Server::builder() with the Streamable HTTP transport, on both the handshake and the 2026-07-28 lifecycle.
Cause
ResourceTemplate::__construct()requires at least one placeholder and throws (src/Schema/ResourceTemplate.php#L59-L61).ReflectedElementLoaderwraps that into aConfigurationException(ReflectedElementLoader.php#L214-L219), which abortsRegistry::load()before any element is registered.Builder::$lazyLoadingdefaults totrue, so that load runs on the first read during request handling.Registry::load()setsloadedonly on success, so every subsequent request retries and fails the same way.
So one misconfigured element is enough to make a server serve nothing, and the client is told -32602 (Invalid params) for what is a server-side configuration error it cannot do anything about.
The original report saw it as a 500 with Cannot modify header information - headers already sent (output started at /vendor/symfony/http-foundation/Response.php:393), which is how it surfaces once the response is already being written. I could not reproduce that part on main, so treat it as a symptom of the surrounding stack rather than part of this issue.
Secondary issue: inconsistent handling
The same mistake behaves differently depending on the registration path:
ReflectedElementLoaderthrowsConfigurationException, a hard failure taking down the registry.Discoverer::processFile()catches\Throwable, logs it and continues, so an attribute-discovered template with the same mistake is silently dropped.
Suggested fix
Validate the URI template in Builder::addResourceTemplate(), where the developer wrote it, instead of at the first read of the registry. PR follows.
Worth considering separately: a ConfigurationException reaching a client as -32602 is misleading (it is caught by catch (\InvalidArgumentException) in Protocol), and a single failing element aborting the whole registry load is a large blast radius for any other configuration mistake.
Line references are against main.
- Dominant language
- PHP
- Stars
- 1.6k
- Forks
- 173
- Avg merge
- 2d 49m
- Merged PRs (30d)
- 23
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from modelcontextprotocol/php-sdk
-
[Server] Handler type uses bare Closure, hard to decorate RegistryInterface under strict PHPStan OpenServer
Difficulty 1/5 Under an hour Newbie friendliness 78/100
modelcontextprotocol/php-sdk#468 · 2 comments ·
-
needs confirmation needs maintainer action Server
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
modelcontextprotocol/php-sdk#398 · 1 reaction ·
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
modelcontextprotocol/php-sdk#370 ·
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 55/100
modelcontextprotocol/php-sdk#510 · 1 comment ·
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 45/100
modelcontextprotocol/php-sdk#504 ·
All issues in modelcontextprotocol/php-sdk
Similar issues
-
a11y admissions.uiowa.edu needs grooming SiteImprove best practice
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Save States Menu Open
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
pluginsGLPI/datainjection#673 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
octobercms/october#6130 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
getgrav/grav-plugin-form#656 ·