mcp-1.x-dev/src/Plugin/Mcp/AiAgentCalling.php

src/Plugin/Mcp/AiAgentCalling.php
<?php

namespace Drupal\mcp\Plugin\Mcp;

use Drupal\ai_agents\PluginInterfaces\AiAgentInterface;
use Drupal\ai_agents\Task\Task;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\mcp\Attribute\Mcp;
use Drupal\mcp\Plugin\McpPluginBase;
use Drupal\mcp\ServerFeatures\Tool;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Plugin implementation of the mcp.
 */
#[Mcp(
  id: 'aia',
  name: new TranslatableMarkup('AI Agent Calling'),
  description: new TranslatableMarkup(
    'Provides ai agent calling functionality.'
  ),
)]
class AiAgentCalling extends McpPluginBase implements ContainerFactoryPluginInterface {

  /**
   * The pluginManagerAiAgents service.
   *
   * @var \Drupal\ai_agents\PluginManager\AiAgentManager
   */
  protected $agentManager;

  /**
   * The pluginManagerAiProvider service.
   *
   * @var \Drupal\ai\AiProviderPluginManager
   */
  protected $providerPlugin;

  /**
   * {@inheritDoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ) {
    $instance = parent::create(
      $container,
      $configuration,
      $plugin_id,
      $plugin_definition,
    );

    $instance->agentManager = $container->get(
      'plugin.manager.ai_agents',
      ContainerInterface::NULL_ON_INVALID_REFERENCE
    );
    $instance->providerPlugin = $container->get(
      'ai.provider',
      ContainerInterface::NULL_ON_INVALID_REFERENCE
    );

    return $instance;
  }

  /**
   * {@inheritDoc}
   */
  public function checkRequirements(): bool {
    return $this->agentManager !== NULL && $this->providerPlugin !== NULL;
  }

  /**
   * {@inheritDoc}
   */
  public function getRequirementsDescription(): string {
    if (!$this->checkRequirements()) {
      $missing = [];
      if ($this->agentManager === NULL) {
        $missing[] = $this->t('AI Agents module');
      }
      if ($this->providerPlugin === NULL) {
        $missing[] = $this->t('AI module with provider configuration');
      }
      return $this->t('The following must be installed and configured: @missing', [
        '@missing' => implode(', ', $missing),
      ]);
    }
    return '';
  }

  /**
   * {@inheritdoc}
   */
  public function getTools(): array {
    $tools = [];

    foreach (
      $this->agentManager->getDefinitions() as $agent_id => $agent_definition
    ) {
      /** @var \Drupal\ai_agents\PluginInterfaces\AiAgentInterface $instance */
      $instance = $this->agentManager->createInstance($agent_id);

      if (!$this->checkAgentAccess($instance)) {
        continue;
      }

      if (!$instance->isAvailable()) {
        continue;
      }

      $capabilities = $instance->agentsCapabilities();
      foreach ($capabilities as $capability_name => $capability) {
        $tools[] = new Tool(
          name: "{$agent_id}__{$capability_name}",
          description: $capability['description'],
          inputSchema: [
            'type'       => 'object',
            'properties' => [
              'prompt' => [
                'type'        => 'string',
                'description' => 'The prompt to enable drupal modules or a question.',
              ],
            ],
            'required'   => ['prompt'],
          ],
        );
      }
    }

    return $tools;
  }

  /**
   * {@inheritdoc}
   */
  public function executeTool(string $toolId, mixed $arguments): array {
    $prompt = $arguments['prompt'];
    if (empty($prompt)) {
      throw new \InvalidArgumentException(
        'Prompt is required.'
      );
    }

    $agent_definitions = $this->agentManager->getDefinitions();

    $desired_agent = NULL;
    foreach ($agent_definitions as $agent_id => $agent_definition) {
      $agent = $this->agentManager->createInstance($agent_id);

      if (!$this->checkAgentAccess($agent) || !$agent->isAvailable()) {
        continue;
      }

      $capabilities = $agent->agentsCapabilities();
      foreach ($capabilities as $capability_name => $capability) {
        $toolName = "{$agent_id}__{$capability_name}";
        $sanitizedName = $this->sanitizeToolName($toolName);
        $oldToolName = "$agent_id:$capability_name";
        if ($sanitizedName === $toolId ||
            md5($oldToolName) === $toolId ||
            md5($toolName) === $toolId) {
          $desired_agent = $agent;
          break;
        }
      }
    }

    if (!$desired_agent) {
      throw new \InvalidArgumentException(
        'Agent or Capability not found.'
      );
    }

    $defaults = $this->providerPlugin->getDefaultProviderForOperationType(
      'chat_with_complex_json'
    );

    if (!$defaults) {
      throw new \InvalidArgumentException(
        'No default provider found for operation type.'
      );
    }
    $task = new Task($prompt);
    $task->setComments($this->messages ?? []);
    $desired_agent->setTask($task);
    $desired_agent->setAiProvider(
      $this->providerPlugin->createInstance($defaults['provider_id'])
    );
    $desired_agent->setModelName($defaults['model_id']);
    $desired_agent->setAiConfiguration([]);
    $desired_agent->setCreateDirectly(TRUE);

    $solvability = $desired_agent->determineSolvability();
    switch ($solvability) {
      case AiAgentInterface::JOB_NEEDS_ANSWERS:
        $questions = $desired_agent->askQuestion();

        return [
          [
            "type" => "text",
            "text" => implode("\n", $questions),
          ],
        ];

      case AiAgentInterface::JOB_NOT_SOLVABLE:
        return [
          [
            "type" => "text",
            "text" => 'Task is not solvable by this agent. Please rephrase.',
          ],
        ];

      case AiAgentInterface::JOB_SHOULD_ANSWER_QUESTION:
        return [
          [
            "type" => "text",
            "text" => $desired_agent->answerQuestion(),
          ],
        ];

      case AiAgentInterface::JOB_INFORMS:
        return [
          [
            "type" => "text",
            "text" => $desired_agent->inform(),
          ],
        ];

      case AiAgentInterface::JOB_SOLVABLE:
        $response = $desired_agent->solve();
        if ($response instanceof TranslatableMarkup) {
          try {
            $response = $response->render();
          }
          // For some cases the response can't render because of missing vars.
          // In this case we just return the json serialized response.
          catch (\Exception $e) {
            // Ideally this should not happen. But for some cases it does.
            // As there is missing arguments in the translatable markup.
            $response = $response->getUntranslatedString();
          }
        }

        return [
          [
            "type" => "text",
            "text" => $response,
          ],
        ];

      default:
        return [
          [
            "type" => "text",
            "text" => 'Unknown solvability status',
          ],
        ];
    }
  }

  /**
   * Check agent access handling both boolean and AccessResult return types.
   *
   * @param \Drupal\ai_agents\PluginInterfaces\AiAgentInterface $agent
   *   The agent instance.
   *
   * @return bool
   *   TRUE if access is allowed, FALSE otherwise.
   */
  protected function checkAgentAccess(AiAgentInterface $agent): bool {
    $access = $agent->hasAccess();

    // Handle boolean return type (for compatibility with some agents)
    if (is_bool($access)) {
      return $access;
    }

    // Handle AccessResult return type (standard)
    if (method_exists($access, 'isAllowed')) {
      return $access->isAllowed();
    }

    // Fallback: deny access if unexpected type.
    return FALSE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc