refactor: put formatters into dedicated classes (#224)

Split formatting logic from Reference classes into dedicated formatter classes:
- ToolResultFormatter: Formats tool execution results into Content items
- PromptResultFormatter: Formats prompt results into PromptMessage items
- ResourceResultFormatter: Formats resource results into ResourceContents items

This refactor:
- Eliminates 122 lines of code duplication between ResourceReference and
  ResourceTemplateReference (identical formatting logic)
- Improves code organization by separating formatting concerns from
  capability reference management
- Makes formatters independently testable and reusable
- Reduces Reference classes by ~450 lines while maintaining all functionality

Changes:
- Create src/Capability/Formatter/* with three formatter classes
- Update Reference classes to delegate to formatters instead of inline logic
- Add comprehensive test coverage for all formatter classes
- All existing tests pass; no behavioral changes
This commit is contained in:
Mateu Aguiló Bosch 2026-01-18 22:17:44 +01:00 • committed by GitHub
commit ab4f686e18
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,8 +11,8 @@
namespace Mcp\Capability\Registry;
use Mcp\Capability\Formatter\ToolResultFormatter;
use Mcp\Schema\Content\Content;
use Mcp\Schema\Content\TextContent;
use Mcp\Schema\Tool;
/**
@ -54,62 +54,7 @@ class ToolReference extends ElementReference
*/
public function formatResult(mixed $toolExecutionResult): array
{
if ($toolExecutionResult instanceof Content) {
return [$toolExecutionResult];
}
if (\is_array($toolExecutionResult)) {
if (empty($toolExecutionResult)) {
return [new TextContent('[]')];
}
$allAreContent = true;
$hasContent = false;
foreach ($toolExecutionResult as $item) {
if ($item instanceof Content) {
$hasContent = true;
} else {
$allAreContent = false;
}
}
if ($allAreContent && $hasContent) {
return $toolExecutionResult;
}
if ($hasContent) {
$result = [];
foreach ($toolExecutionResult as $item) {
if ($item instanceof Content) {
$result[] = $item;
} else {
$result = array_merge($result, $this->formatResult($item));
}
}
return $result;
}
}
if (null === $toolExecutionResult) {
return [new TextContent('(null)')];
}
if (\is_bool($toolExecutionResult)) {
return [new TextContent($toolExecutionResult ? 'true' : 'false')];
}
if (\is_scalar($toolExecutionResult)) {
return [new TextContent($toolExecutionResult)];
}
$jsonResult = json_encode(
$toolExecutionResult,
\JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR | \JSON_INVALID_UTF8_SUBSTITUTE
);
return [new TextContent($jsonResult)];
return (new ToolResultFormatter())->format($toolExecutionResult);
}
/**