Output Definitions
When to Use
Use this when declaring what a tool returns. Only declared keys become typed outputs, undeclared keys still reach callers raw, and a successful run that misses a required output becomes a failure.
Version: applies to
drupal/tool1.0.0-beta11 (beta; no security advisory coverage). Paths are undermodules/contrib/tool/.
Classes
OutputDefinition, EntityOutputDefinition, ListOutputDefinition and MapOutputDefinition, all in Drupal\tool\TypedData and implementing OutputDefinitionInterface. Constructors mirror the input classes without locked:
// tool 1.0.0-beta11 src/TypedData/OutputDefinition.php
public function __construct($data_type, string|TranslatableMarkup $label, string|TranslatableMarkup $description, $required = TRUE, $multiple = FALSE, $default_value = NULL, ?array $constraints = [], array $examples = [])
required defaults to TRUE for outputs too.
How Outputs Are Collected
After doExecute() returns a success, execute() copies only the result values whose key matches a declared output, then validates the outputs:
// tool 1.0.0-beta11 src/Tool/ToolBase.php, execute()
if ($this->result->isSuccess()) {
if ($provided_definitions = $this->getOutputDefinitions()) {
$this->outputs = [];
foreach ($this->result->getContextValues() as $context_name => $value) {
if (isset($provided_definitions[$context_name])) {
$this->setOutputValue($context_name, $value);
}
}
}
$this->failOnOutputViolations();
}
failOnOutputViolations() replaces the success with a Runtime failure when a required output was not provided ("The @name output is required but was not provided.") or a value fails its definition. It logs the detail to the tool logger channel. Output checks are by shape: an entity output must be an entity of the declared type; its fields are not validated (tool 1.0.0-beta11 src/TypedOutputsTrait.php).
What Reaches the Caller
Callers read getFormattedResult(), not the typed outputs. getFormattedResult() walks every result value, declared or not (tool 1.0.0-beta11 src/Tool/ToolBase.php):
| Result key | Typed output (getOutputValue(), validation) |
Output transforms (handles, wire coercion) | Sent by Drush, AI connector, MCP bridge |
|---|---|---|---|
| Declared | Yes | Yes | Yes |
| Undeclared | No | No, passed through raw | Yes, raw |
Decision
| If the output... | Declare... |
|---|---|
| Is always set on success | required left at TRUE |
| Is set only on some paths | required: FALSE |
| Is a list | ListOutputDefinition with an item_definition |
| Is a structured object | MapOutputDefinition with OutputDefinitionInterface properties |
| Is a content entity | EntityOutputDefinition. Handle-capable callers receive a handle (a handle:<uuid> string standing in for the entity; see Entity Inputs and Handles). A config entity passes through as an object |
Common Mistakes
- Returning a key with no output definition and assuming it stays private → it reaches Drush, AI and MCP callers raw, with no handle conversion; an entity under an undeclared key leaves as an object
- Omitting a conditional output without
required: FALSE→ a correct run reports "Tool execution failed" with categoryRuntime - Returning a value the definition cannot store (wrong structure) →
setOutputValue()throws outside thetryinexecute(); Drush reportsexecution_failed(tool 1.0.0-beta11src/Drush/RunErrorType.php) - Declaring outputs with core
ContextDefinitionor an input class → deprecated in beta9, removed in rc1
See Also
- doExecute and ExecutableResult → how results are built
- Calling a Tool from PHP →
getResult()vsgetFormattedResult() - Input Definitions → the input side
- Reference:
modules/contrib/tool/src/TypedOutputsTrait.php,modules/contrib/tool/src/Tool/ToolBase.php