Skip to content

Developer Guide

Welcome to the MCP Client developer documentation. This guide will help you understand the module's architecture and how to work with it programmatically.

Introduction

The MCP Client module provides a clean, type-safe API for working with Model Context Protocol (MCP) tools in Drupal. The module is built around immutable Value Objects that represent MCP protocol entities.

Quick Start for Developers

Basic Tool Access

use Drupal\mcp_client\Entity\McpServer;

// Load an MCP server
$mcpServer = McpServer::load('my_server');

// Get all tools
$tools = $mcpServer->getTools();

// Get a specific tool
$tool = $mcpServer->getTool('search_files');

// Check if tool exists
if ($mcpServer->hasTool('search_files')) {
  // Work with the tool
}

Working with Tool Properties

// Access tool information
echo $tool->name();
echo $tool->description();
echo $tool->enabled() ? 'Enabled' : 'Disabled';

// Get input schema
$schema = $tool->getInputSchema();
$properties = $schema->getProperties();

// Check if property is required
if ($schema->isRequired('query')) {
  // Handle required field
}

Modifying Tools (Builder Pattern)

Value Objects are immutable. Use the Builder pattern to create modified copies:

use Drupal\mcp_client\ValueObject\ToolBuilder;
use Drupal\tool\Tool\ToolOperation;

// Enable a tool
$enabledTool = $tool->toBuilder()
  ->setEnabled(TRUE)
  ->build();

// Lock a tool
$lockedTool = $tool->toBuilder()
  ->setLocked(TRUE)
  ->setLockedDefinition($lockedDefinition)
  ->build();

// Set operation type
$tool = $tool->toBuilder()
  ->setOperation(ToolOperation::READ)
  ->build();

// Chain multiple modifications (creates only ONE object)
$modifiedTool = $tool->toBuilder()
  ->setEnabled(TRUE)
  ->setOperation(ToolOperation::READ)
  ->build();

// The original $tool remains unchanged!

Updating Tools in Storage

use Drupal\mcp_client\ValueObject\ToolCollection;

// Get current tools
$tools = $mcpServer->getTools();

// Modify a tool using builder
$tool = $tools->get('search_files');
$enabledTool = $tool->toBuilder()
  ->setEnabled(TRUE)
  ->build();

// Create new collection with modified tool
$newCollection = new ToolCollection(...$tools->all());
$newCollection->remove('search_files');
$newCollection->add($enabledTool);

// Update and save
$mcpServer->setTools($newCollection);
$mcpServer->save();

Key Concepts

1. Value Objects

All MCP entities are represented as immutable Value Objects:

  • Tool: Represents a single MCP tool
  • ToolCollection: A collection of tools
  • InputSchema: JSON Schema for tool inputs
  • LockedDefinition: Snapshot of a locked tool's definition

2. Immutability

Value Objects cannot be modified after creation. Instead, use the Builder pattern that returns new instances:

// ❌ This won't work (properties are readonly)
$tool->enabled = TRUE;

// ✅ This is the correct way
$tool = $tool->toBuilder()
  ->setEnabled(TRUE)
  ->build();

3. Type Safety

The module uses strict typing throughout:

public function getTools(): ToolCollectionInterface;
public function getTool(string $name): ?ToolInterface;

4. Protocol Compliance

Value Objects follow the MCP protocol specification, using camelCase for property names while supporting snake_case for backward compatibility.

Common Tasks

List All Enabled Tools

$tools = $mcpServer->getTools();
$enabledTools = array_filter(
  $tools->all(),
  fn($tool) => $tool->enabled()
);

Enable All Tools

$tools = $mcpServer->getTools();
$enabledTools = array_map(
  fn($tool) => $tool->toBuilder()->setEnabled(TRUE)->build(),
  $tools->all()
);

$mcpServer->setTools(new ToolCollection(...$enabledTools));
$mcpServer->save();

Export Tool Definitions

// Single tool
$toolArray = $tool->toArray();

// All tools
$toolsArray = $mcpServer->getTools()->toArray();

Import Tool Definitions

use Drupal\mcp_client\ValueObject\Tool;
use Drupal\mcp_client\ValueObject\ToolCollection;

$tools = array_map(
  fn($data) => Tool::fromArray($data),
  $externalData
);

$collection = new ToolCollection(...$tools);
$mcpServer->setTools($collection);
$mcpServer->save();

Documentation Structure

Best Practices

  1. Type hint with interfaces for flexibility: php function processTools(ToolCollectionInterface $tools): void

  2. Always save after modifications: php $mcpServer->setTools($newCollection); $mcpServer->save(); // Don't forget!

  3. Check existence before access: php if ($mcpServer->hasTool('my_tool')) { $tool = $mcpServer->getTool('my_tool'); }

  4. Batch operations efficiently (save once, not in loops): ```php // ✅ Good: Single save $tools = array_map( fn($t) => $t->toBuilder()->setEnabled(TRUE)->build(), $tools->all() ); $mcpServer->setTools(new ToolCollection(...$tools)); $mcpServer->save();

// ❌ Bad: Multiple saves foreach ($tools as $tool) { // ... save in loop } ```

  1. Use builder pattern for modifications to improve memory efficiency

  2. Trust the type system - if it compiles, it's valid

Need Help?

Contributing

When extending or contributing to the module:

  • Follow Drupal coding standards
  • Maintain immutability of Value Objects
  • Add comprehensive PHPDoc comments
  • Write unit and kernel tests
  • Update documentation for new features