Architecture Overview
The MCP Client module uses a clean, object-oriented architecture centered around immutable Value Objects (DTOs) that represent MCP protocol entities.
Value Object Pattern
The module implements the Value Object pattern for representing MCP tools and their related data. Value Objects are:
- Immutable: Once created, their state cannot be modified
- Self-validating: Enforce business rules in their constructors
- Type-safe: Use PHP type hints throughout
- Protocol-compliant: Follow the MCP protocol specification
Core Components
Value Objects (src/ValueObject/)
The heart of the architecture consists of immutable Value Objects that represent MCP protocol entities:
ValueObject/
├── Tool.php # Represents a single MCP tool
├── ToolInterface.php # Tool contract
├── ToolBuilder.php # Builder for Tool creation/modification
├── ToolCollection.php # Collection of tools
├── ToolCollectionInterface.php # Collection contract
├── InputSchema.php # JSON Schema for tool inputs
├── InputSchemaInterface.php # Schema contract
├── LockedDefinition.php # Snapshot of locked tool state
└── LockedDefinitionInterface.php # Locked definition contract
Config Entity (src/Entity/)
The McpServer config entity stores MCP server configurations and uses Value Objects internally:
- Stores server connection details (endpoint, transport type, credentials)
- Manages tool collections via
ToolCollectionInterface - Provides getters/setters for tool access
Plugin System
- McpToolDeriver: Discovers tools from MCP servers and creates Tool API plugins
- McpToolBase: Base plugin class for executing MCP tools
Design Principles
1. Immutability with Builder Pattern
All Value Objects are immutable. To modify them, use the Builder pattern:
use Drupal\mcp_client\ValueObject\ToolBuilder;
$tool = new Tool('my_tool', 'Description', $inputSchema);
// Create modified version using builder
$enabledTool = $tool->toBuilder()
->setEnabled(TRUE)
->build();
// $tool remains unchanged, $enabledTool is a new instance
// Chain multiple modifications (creates only ONE object)
$modifiedTool = $tool->toBuilder()
->setEnabled(TRUE)
->setOperation(ToolOperation::READ)
->build();
// Create new tool with builder (required fields enforced)
$newTool = ToolBuilder::create('my_tool', 'Description', $inputSchema)
->setEnabled(TRUE)
->build();
Why Builder Pattern? - Memory efficient: Creates only one object vs multiple intermediate objects - Clear intent: Explicitly shows object construction - Fail-fast: Required fields validated at creation time, not build time - Easy to extend: Add properties without updating multiple methods - Type-safe validation: Enforced by constructor and type hints
2. Type Safety
Strong typing throughout prevents runtime errors:
public function getTools(): ToolCollectionInterface;
public function getTool(string $name): ?ToolInterface;
3. Protocol Compliance
Value Objects follow MCP protocol naming conventions (camelCase) while supporting Drupal conventions (snake_case) for backward compatibility:
// Both work for backward compatibility
$data = ['inputSchema' => [...]] // MCP standard
$data = ['input_schema' => [...]] // Legacy Drupal
4. Separation of Concerns
- Value Objects: Pure data representation
- Entities: Persistence and Drupal integration
- Plugins: Business logic and tool execution
- Services: Infrastructure and communication
Data Flow
MCP Server → McpClient Service → Value Objects → Config Entity → Tool Plugins → AI Agent
- MCP server returns tool definitions
- Service creates Value Objects from raw data
- Config entity stores Value Objects
- Tool deriver creates plugins from Value Objects
- AI agents call tools via plugins
Next Steps
- Architecture Overview - Module design and patterns
- Value Objects API - Detailed API reference
- Quick Reference - Cheat sheet for common operations