Value Objects API Reference
This document provides detailed information about the Value Object classes used in the MCP Client module.
Overview
Value Objects are immutable data structures that represent MCP protocol entities. They provide type safety, validation, and a clean API for working with MCP tools.
Tool
Represents a single MCP tool with its metadata and configuration.
Class: Drupal\mcp_client\ValueObject\Tool
Implements: ToolInterface
Properties (readonly):
- string $name - Tool name
- string $description - Tool description
- InputSchemaInterface $inputSchema - JSON Schema for inputs
- bool $enabled - Whether tool is enabled (default: FALSE)
- bool $locked - Whether tool is locked (default: FALSE)
- ?ToolOperation $operation - Tool operation type (optional)
- ?LockedDefinitionInterface $lockedDefinition - Locked definition snapshot (optional)
Creating a Tool
use Drupal\mcp_client\ValueObject\Tool;
use Drupal\mcp_client\ValueObject\InputSchema;
// Create an input schema
$inputSchema = new InputSchema(
type: 'object',
properties: [
'query' => [
'type' => 'string',
'description' => 'Search query',
],
],
required: ['query']
);
// Create a tool
$tool = new Tool(
name: 'search_files',
description: 'Search for files in a directory',
inputSchema: $inputSchema,
enabled: TRUE
);
From Array
// MCP protocol format (camelCase)
$tool = Tool::fromArray([
'name' => 'search_files',
'description' => 'Search for files',
'inputSchema' => [
'type' => 'object',
'properties' => ['query' => ['type' => 'string']],
],
'enabled' => TRUE,
'locked' => FALSE,
]);
// Legacy format (snake_case) - also supported
$tool = Tool::fromArray([
'name' => 'search_files',
'description' => 'Search for files',
'input_schema' => [...], // snake_case still works
]);
Modifying Tools (Builder Pattern)
Tools are immutable. To modify a tool, use the Builder pattern:
use Drupal\mcp_client\ValueObject\ToolBuilder;
use Drupal\tool\Tool\ToolOperation;
// Get original tool
$tool = $mcpServer->getTool('search_files');
// Create modified version using builder (original unchanged)
$enabledTool = $tool->toBuilder()
->setEnabled(TRUE)
->build();
// Chain multiple modifications (creates only ONE new object)
$modifiedTool = $tool->toBuilder()
->setEnabled(TRUE)
->setOperation(ToolOperation::READ)
->setLocked(TRUE)
->build();
// Original $tool remains unchanged
Using ToolBuilder Directly
You can also create tools from scratch using the builder. Required fields (name, description, inputSchema) must be provided during creation:
use Drupal\mcp_client\ValueObject\ToolBuilder;
// Create with required fields
$tool = ToolBuilder::create('search_files', 'Search for files in a directory', $inputSchema)
->setEnabled(TRUE)
->setOperation(ToolOperation::READ)
->build();
// Or from an existing tool
$builder = ToolBuilder::fromTool($existingTool);
$newTool = $builder
->setEnabled(FALSE)
->build();
Methods
| Method | Return Type | Description |
|---|---|---|
name() |
string |
Gets tool name |
description() |
string |
Gets tool description |
getInputSchema() |
InputSchemaInterface |
Gets input schema |
enabled() |
bool |
Checks if enabled |
locked() |
bool |
Checks if locked |
getOperation() |
?ToolOperation |
Gets operation type |
getLockedDefinition() |
?LockedDefinitionInterface |
Gets locked definition |
toBuilder() |
ToolBuilder |
Creates a builder from this tool |
toArray() |
array |
Converts to array (MCP protocol format) |
ToolBuilder
Builder for creating and modifying Tool instances. Provides a mutable, fluent interface for constructing immutable Tool objects without creating intermediate objects (more memory-efficient).
Class: Drupal\mcp_client\ValueObject\ToolBuilder
Creating a Builder
use Drupal\mcp_client\ValueObject\ToolBuilder;
// Create new builder with required fields (name, description, inputSchema)
$builder = ToolBuilder::create('search_files', 'Search for files', $inputSchema);
// Create from existing tool
$builder = ToolBuilder::fromTool($tool);
$builder = $tool->toBuilder(); // Same as above
// Create from array (validates required fields)
$builder = ToolBuilder::fromArray([
'name' => 'search_files',
'description' => 'Search for files',
'inputSchema' => [...],
]);
Factory Methods
| Method | Returns | Description |
|---|---|---|
create(string $name, string $description, InputSchemaInterface $inputSchema) |
self |
Creates builder with required fields |
fromTool(Tool $tool) |
self |
Creates builder from existing tool |
fromArray(array $data) |
self |
Creates builder from array (validates required fields) |
Builder Methods
| Method | Return Type | Description |
|---|---|---|
setEnabled(bool) |
self |
Sets enabled state |
setLocked(bool) |
self |
Sets locked state |
setOperation(?ToolOperation) |
self |
Sets tool operation |
setLockedDefinition(?LockedDefinitionInterface) |
self |
Sets locked definition |
build() |
Tool |
Builds the immutable Tool |
Note: setName(), setDescription(), and setInputSchema() are still available for advanced use cases, but required fields must be provided during creation.
Why Builder Pattern?
Memory Efficiency: The builder pattern creates only one Tool object, even when chaining multiple modifications.
Fail-Fast Validation: Required fields are validated at creation time, not build time, catching errors earlier.
// BEFORE Builder Pattern (wither methods created multiple intermediate objects)
$tool->withEnabled(TRUE)->withOperation($op)->withLocked(TRUE); // Created 3 objects
// AFTER Builder Pattern (creates only 1 final object - memory efficient)
$tool->toBuilder()
->setEnabled(TRUE)
->setOperation($op)
->setLocked(TRUE)
->build();
// Builder always starts in valid state (required fields enforced at creation)
$builder = ToolBuilder::create($name, $description, $schema); // ✅ Valid from start
// vs
$builder = ToolBuilder::create(); // ❌ Old way - could forget required fields
ToolCollection
A collection of tools with iteration and counting capabilities.
Class: Drupal\mcp_client\ValueObject\ToolCollection
Implements: ToolCollectionInterface, IteratorAggregate, Countable
Creating a Collection
use Drupal\mcp_client\ValueObject\ToolCollection;
// From individual tools
$collection = new ToolCollection($tool1, $tool2, $tool3);
// From array
$collection = ToolCollection::fromArray([
['name' => 'tool1', 'description' => '...', 'inputSchema' => [...]],
['name' => 'tool2', 'description' => '...', 'inputSchema' => [...]],
]);
// Empty collection
$collection = new ToolCollection();
Working with Collections
// Add a tool
$collection->add($tool);
// Get a tool by name
$tool = $collection->get('search_files');
// Check if tool exists
if ($collection->has('search_files')) {
// ...
}
// Remove a tool
$collection->remove('search_files');
// Get all tools as array
$tools = $collection->all();
// Count tools
$count = count($collection); // or $collection->count()
// Iterate over tools
foreach ($collection as $name => $tool) {
echo "Tool: {$tool->name()}\n";
}
// Convert to array
$array = $collection->toArray();
Methods
| Method | Return Type | Description |
|---|---|---|
get(string) |
?ToolInterface |
Gets tool by name |
add(ToolInterface) |
void |
Adds tool to collection |
remove(string) |
void |
Removes tool by name |
has(string) |
bool |
Checks if tool exists |
all() |
ToolInterface[] |
Gets all tools as array |
count() |
int |
Counts tools in collection |
getIterator() |
Traversable |
Gets iterator for foreach |
toArray() |
array |
Converts to array representation |
InputSchema
Represents a JSON Schema for tool input validation following the MCP protocol.
Class: Drupal\mcp_client\ValueObject\InputSchema
Implements: InputSchemaInterface
Properties (readonly):
- string $type - Schema type (default: 'object')
- array $properties - Property definitions
- array $required - Required property names
- array $additionalProperties - Additional schema properties
Creating a Schema
use Drupal\mcp_client\ValueObject\InputSchema;
$schema = new InputSchema(
type: 'object',
properties: [
'path' => [
'type' => 'string',
'description' => 'File path',
],
'recursive' => [
'type' => 'boolean',
'description' => 'Search recursively',
'default' => FALSE,
],
],
required: ['path'],
additionalProperties: [
'$schema' => 'http://json-schema.org/draft-07/schema#',
]
);
// From array
$schema = InputSchema::fromArray([
'type' => 'object',
'properties' => [...],
'required' => ['path'],
]);
Working with Schemas
// Get all properties
$properties = $schema->getProperties();
// Get specific property
$pathProperty = $schema->getProperty('path');
if ($pathProperty !== NULL) {
$type = $pathProperty['type'];
}
// Check if property is required
if ($schema->isRequired('path')) {
// ...
}
// Convert to array
$array = $schema->toArray();
Methods
| Method | Return Type | Description |
|---|---|---|
getProperties() |
array |
Gets all property definitions |
getProperty(string) |
?array |
Gets specific property definition |
isRequired(string) |
bool |
Checks if property is required |
toArray() |
array |
Converts to array (JSON Schema format) |
LockedDefinition
A snapshot of a tool's definition at the time it was locked. Used to preserve tool definitions even if the MCP server changes them.
Class: Drupal\mcp_client\ValueObject\LockedDefinition
Implements: LockedDefinitionInterface
Properties (readonly):
- string $name - Tool name at lock time
- string $description - Tool description at lock time
- InputSchemaInterface $inputSchema - Input schema at lock time
Creating a Locked Definition
use Drupal\mcp_client\ValueObject\LockedDefinition;
$lockedDef = new LockedDefinition(
name: 'search_files',
description: 'Original description',
inputSchema: $inputSchema
);
// From array
$lockedDef = LockedDefinition::fromArray([
'name' => 'search_files',
'description' => 'Original description',
'inputSchema' => [...],
]);
Usage with Tools
// Lock a tool with its current definition
$tool = $mcpServer->getTool('search_files');
$lockedDef = new LockedDefinition(
$tool->name(),
$tool->description(),
$tool->getInputSchema()
);
$lockedTool = $tool->toBuilder()
->setLocked(TRUE)
->setLockedDefinition($lockedDef)
->build();
Methods
| Method | Return Type | Description |
|---|---|---|
toArray() |
array |
Converts to array representation |
Type Safety and Validation
All Value Objects enforce type safety and validation:
// This will throw InvalidArgumentException
$tool = new Tool(
name: 'test',
description: 'Test tool',
inputSchema: $schema,
locked: TRUE,
lockedDefinition: NULL // ERROR: locked tools must have definition
);
Best Practices
- Use the Builder pattern to create and modify Tool objects efficiently
- Type hint with interfaces for flexibility:
ToolInterface,ToolCollectionInterface - Use
fromArray()when deserializing from storage or external sources - Use
toArray()when serializing for storage or API responses - Chain builder methods for multiple modifications to avoid creating intermediate objects
- Trust the type system - if it compiles, it's valid
Related Documentation
- Developer Overview - Developer documentation home
- Architecture - System architecture overview
- Quick Reference - Quick reference guide