EntityHandleTransformer doubles the label prefix in handle descriptions
### Problem/Motivation
Entity handle definitions come out of schema normalization with the label prefixed twice:
```
Entity: Entity: A node entity to process. (pass a handle from a previous tool call)
```
`EntityHandleTransformer::transformInputDefinition()` composes `label: description` into the new definition's description and passes the original label as the label:
```php
$description = (string) $definition->getLabel();
if ((string) $definition->getDescription()) {
$description .= ': ' . (string) $definition->getDescription();
}
$description .= ' (pass a handle from a previous tool call)';
return new InputDefinition(
'string',
$definition->getLabel() ?: new TranslatableMarkup('Entity'),
$description,
...
);
```
`ContextDefinitionNormalizer::getDescription()` then prefixes the label again, as it does for every definition:
```php
protected function getDescription(ContextDefinitionInterface $definition) : ?string {
if ($description = (string) $definition->getLabel()) {
if ((string) $definition->getDescription()) {
$description .= ': ' . (string) $definition->getDescription();
}
}
...
}
```
`transformOutputDefinition()` has the same composition. Non-entity inputs on the same tool get a single prefix, so this is specific to the handle path. The text is what the model reads, on every entity input and output for every connector that uses the transformer.
### Steps to reproduce
1. Normalize any tool with an `EntityInputDefinition` through `ToolDefinitionSerializer::normalizeInputSchema()` with an invoker whose subscriber calls `transformInputDefinition()`. `tool_test`'s `complex_entity_handle_invoker` reproduces it.
2. Inspect `properties.*.description` for the entity input.
### Expected behavior
```
Entity: A node entity to process. (pass a handle from a previous tool call)
```
### Actual behavior
```
Entity: Entity: A node entity to process. (pass a handle from a previous tool call)
```
### Proposed resolution
Stop composing the label into the description in the transformer. Keep only the original description plus the handle suffix; the normalizer already owns label prefixing.
```php
$description = (string) $definition->getDescription();
$description .= ($description ? ' ' : '') . '(pass a handle from a previous tool call)';
```
Same change in `transformOutputDefinition()` with its suffix.
Tighten `EntityHandleTransformerTest` to assert the full description string end to end through the normalizer, not a substring. A substring assertion is what let this ship.
### Drupal version
11.4.5
### Tool module version
1.0.0-beta6; still present on `1.0.x` HEAD (`5af93e5c`).
### Related
Found while reviewing [mcp_server_tool_bridge#3613896](https://www.drupal.org/project/mcp_server_tool_bridge/issues/3613896). Its `EntityHandleIntegrationTest` uses `assertStringContainsString('handle', ...)` and so does not catch the doubled prefix.
### AI usage (if applicable)
- [x] **AI Assisted Issue:** This issue was generated with AI assistance, but was reviewed and refined by the creator.
- [ ] **AI Assisted Code:** This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.
- [ ] **AI Generated Code:** This code was mainly generated by an AI with human guidance, and reviewed, tested, and refined by a human.
- [ ] **Vibe Coded:** This code was generated by an AI and has only been functionally tested.
issue
GitLab AI Context
Project: project/tool
Instance: https://git.drupalcode.org
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://git.drupalcode.org/project/tool/-/raw/1.0.x/README.md — project overview and setup
Repository: https://git.drupalcode.org/project/tool
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD