mcp-1.x-dev/src/Controller/McpConnectionController.php

src/Controller/McpConnectionController.php
<?php

declare(strict_types=1);

namespace Drupal\mcp\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Controller for displaying MCP connection information.
 */
class McpConnectionController extends ControllerBase {

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected RequestStack $requestStack;

  /**
   * Constructs a new McpConnectionController object.
   *
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   */
  public function __construct(RequestStack $request_stack) {
    $this->requestStack = $request_stack;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get('request_stack')
    );
  }

  /**
   * Displays MCP connection information.
   *
   * @return array
   *   A render array containing the connection information.
   */
  public function connectionInfo(): array {
    $request = $this->requestStack->getCurrentRequest();
    $base_url = $request->getSchemeAndHttpHost();
    $mcp_endpoint = $base_url . '/mcp/post';

    $build = [
      '#type' => 'container',
      '#attributes' => ['class' => ['mcp-connection-info']],
    ];

    $build['client_config_section'] = [
      '#type' => 'details',
      '#title' => $this->t('Client Configuration Examples'),
      '#open' => TRUE,
    ];

    $build['client_config_section']['claude_desktop'] = [
      '#type' => 'details',
      '#title' => $this->t('Claude Desktop'),
      '#open' => FALSE,
    ];

    $build['client_config_section']['claude_desktop']['location'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('<strong>Configuration file location:</strong>'),
    ];

    $build['client_config_section']['claude_desktop']['paths'] = [
      '#theme' => 'item_list',
      '#list_type' => 'ul',
      '#items' => [
        $this->t('<strong>macOS:</strong> <code>~/Library/Application Support/Claude/claude_desktop_config.json</code>'),
        $this->t('<strong>Windows:</strong> <code>%APPDATA%/Claude/claude_desktop_config.json</code>'),
      ],
    ];

    $build['client_config_section']['claude_desktop']['stdio_config_label'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('<strong>STDIO Transport Configuration (Recommended):</strong>'),
    ];

    $claude_desktop_stdio_config = json_encode([
      'mcpServers' => [
        'mcp-server-drupal' => [
          'command' => 'docker',
          'args' => [
            'run',
            '-i',
            '--rm',
            '-e',
            'DRUPAL_AUTH_USER',
            '-e',
            'DRUPAL_AUTH_PASSWORD',
            '--network=host',
            'ghcr.io/omedia/mcp-server-drupal:latest',
            '--drupal-url=' . $base_url,
            '--unsafe-net',
          ],
          'env' => [
            'DRUPAL_AUTH_USER' => 'your-drupal-username',
            'DRUPAL_AUTH_PASSWORD' => 'your-drupal-password',
          ],
        ],
      ],
      'globalShortcut' => '',
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

    $build['client_config_section']['claude_desktop']['stdio_config'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['code-container']],
      'pre' => [
        '#type' => 'html_tag',
        '#tag' => 'pre',
        '#value' => $claude_desktop_stdio_config,
        '#attributes' => ['class' => ['language-json'], 'id' => 'claude-desktop-stdio-config'],
      ],
      'button' => [
        '#type' => 'html_tag',
        '#tag' => 'button',
        '#value' => $this->t('Copy'),
        '#attributes' => [
          'class' => ['copy-button'],
          'onclick' => "navigator.clipboard.writeText(document.getElementById('claude-desktop-stdio-config').textContent); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000);",
          'style' => 'position: absolute; top: 5px; right: 5px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;',
        ],
      ],
    ];

    $build['client_config_section']['claude_desktop']['http_config_label'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('<strong>HTTP Transport Configuration (if supported):</strong>'),
    ];

    $claude_desktop_http_config = json_encode([
      'mcpServers' => [
        'drupal-mcp' => [
          'url' => $mcp_endpoint,
          'auth' => [
            'type' => 'basic',
            'username' => 'your-drupal-username',
            'password' => 'your-drupal-password',
          ],
        ],
      ],
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

    $build['client_config_section']['claude_desktop']['http_config'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['code-container']],
      'pre' => [
        '#type' => 'html_tag',
        '#tag' => 'pre',
        '#value' => $claude_desktop_http_config,
        '#attributes' => ['class' => ['language-json'], 'id' => 'claude-desktop-http-config'],
      ],
      'button' => [
        '#type' => 'html_tag',
        '#tag' => 'button',
        '#value' => $this->t('Copy'),
        '#attributes' => [
          'class' => ['copy-button'],
          'onclick' => "navigator.clipboard.writeText(document.getElementById('claude-desktop-http-config').textContent); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000);",
          'style' => 'position: absolute; top: 5px; right: 5px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;',
        ],
      ],
    ];

    $build['client_config_section']['claude_desktop']['restart_note'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#attributes' => ['class' => ['description']],
      '#value' => $this->t('Note: Restart Claude Desktop after updating the configuration file.'),
    ];

    $build['client_config_section']['claude_code'] = [
      '#type' => 'details',
      '#title' => $this->t('Claude Code'),
      '#open' => FALSE,
    ];

    $build['client_config_section']['claude_code']['info'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('Add the MCP server using the following terminal command:'),
    ];

    $claude_code_command = sprintf(
      'claude code add-mcp-server mcp-server-drupal docker run -i --rm -e DRUPAL_AUTH_USER -e DRUPAL_AUTH_PASSWORD --network=host ghcr.io/omedia/mcp-server-drupal:latest --drupal-url=%s --unsafe-net',
      $base_url
    );

    $build['client_config_section']['claude_code']['command'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['code-container']],
      'pre' => [
        '#type' => 'html_tag',
        '#tag' => 'pre',
        '#value' => $claude_code_command,
        '#attributes' => ['class' => ['language-bash'], 'id' => 'claude-code-command'],
      ],
      'button' => [
        '#type' => 'html_tag',
        '#tag' => 'button',
        '#value' => $this->t('Copy'),
        '#attributes' => [
          'class' => ['copy-button'],
          'onclick' => "navigator.clipboard.writeText(document.getElementById('claude-code-command').textContent); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000);",
          'style' => 'position: absolute; top: 5px; right: 5px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;',
        ],
      ],
    ];

    $build['client_config_section']['claude_code']['env_note'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#attributes' => ['class' => ['description']],
      '#value' => $this->t('Set the DRUPAL_AUTH_USER and DRUPAL_AUTH_PASSWORD environment variables before running the command.'),
    ];

    $build['client_config_section']['cursor'] = [
      '#type' => 'details',
      '#title' => $this->t('Cursor'),
      '#open' => FALSE,
    ];

    $build['client_config_section']['cursor']['stdio_config_label'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('<strong>STDIO Transport Configuration (Recommended):</strong>'),
    ];

    $cursor_stdio_config = json_encode([
      'mcpServers' => [
        'mcp-server-drupal' => [
          'command' => 'docker',
          'args' => [
            'run',
            '-i',
            '--rm',
            '-e',
            'DRUPAL_AUTH_USER',
            '-e',
            'DRUPAL_AUTH_PASSWORD',
            '--network=host',
            'ghcr.io/omedia/mcp-server-drupal:latest',
            '--drupal-url=' . $base_url,
            '--unsafe-net',
          ],
          'env' => [
            'DRUPAL_AUTH_USER' => 'your-drupal-username',
            'DRUPAL_AUTH_PASSWORD' => 'your-drupal-password',
          ],
        ],
      ],
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

    $build['client_config_section']['cursor']['stdio_config'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['code-container']],
      'pre' => [
        '#type' => 'html_tag',
        '#tag' => 'pre',
        '#value' => $cursor_stdio_config,
        '#attributes' => ['class' => ['language-json'], 'id' => 'cursor-stdio-config'],
      ],
      'button' => [
        '#type' => 'html_tag',
        '#tag' => 'button',
        '#value' => $this->t('Copy'),
        '#attributes' => [
          'class' => ['copy-button'],
          'onclick' => "navigator.clipboard.writeText(document.getElementById('cursor-stdio-config').textContent); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000);",
          'style' => 'position: absolute; top: 5px; right: 5px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;',
        ],
      ],
    ];

    $build['client_config_section']['cursor']['http_config_label'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('<strong>HTTP Transport Configuration (if supported):</strong>'),
    ];

    $cursor_http_config = json_encode([
      'mcpServers' => [
        'drupal-mcp' => [
          'url' => $mcp_endpoint,
          'auth' => [
            'type' => 'basic',
            'username' => 'your-drupal-username',
            'password' => 'your-drupal-password',
          ],
        ],
      ],
    ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

    $build['client_config_section']['cursor']['http_config'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['code-container']],
      'pre' => [
        '#type' => 'html_tag',
        '#tag' => 'pre',
        '#value' => $cursor_http_config,
        '#attributes' => ['class' => ['language-json'], 'id' => 'cursor-http-config'],
      ],
      'button' => [
        '#type' => 'html_tag',
        '#tag' => 'button',
        '#value' => $this->t('Copy'),
        '#attributes' => [
          'class' => ['copy-button'],
          'onclick' => "navigator.clipboard.writeText(document.getElementById('cursor-http-config').textContent); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000);",
          'style' => 'position: absolute; top: 5px; right: 5px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;',
        ],
      ],
    ];

    $build['client_config_section']['cursor']['restart_note'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#attributes' => ['class' => ['description']],
      '#value' => $this->t('Note: Restart Cursor after updating the configuration file.'),
    ];

    $build['transport_methods'] = [
      '#type' => 'details',
      '#title' => $this->t('Connection Methods'),
      '#open' => FALSE,
    ];

    $build['transport_methods']['description'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('Drupal MCP supports two transport methods. Choose the one that best fits your client:'),
    ];

    $build['transport_methods']['http_transport'] = [
      '#type' => 'details',
      '#title' => $this->t('1. HTTP Transport (Direct Connection)'),
      '#open' => TRUE,
    ];

    $build['transport_methods']['http_transport']['endpoint'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['mcp-endpoint', 'code-container'], 'style' => 'margin: 10px 0;'],
      'label' => [
        '#type' => 'html_tag',
        '#tag' => 'strong',
        '#value' => $this->t('MCP Endpoint URL:'),
      ],
      'value' => [
        '#type' => 'html_tag',
        '#tag' => 'code',
        '#value' => $mcp_endpoint,
        '#prefix' => ' ',
        '#attributes' => ['id' => 'mcp-endpoint-url', 'style' => 'padding: 5px; background: #f4f4f4;'],
      ],
      'button' => [
        '#type' => 'html_tag',
        '#tag' => 'button',
        '#value' => $this->t('Copy'),
        '#attributes' => [
          'class' => ['copy-button'],
          'onclick' => "navigator.clipboard.writeText(document.getElementById('mcp-endpoint-url').textContent); this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy', 2000);",
          'style' => 'margin-left: 10px; padding: 5px 10px; background: #007cba; color: white; border: none; border-radius: 3px; cursor: pointer;',
        ],
      ],
    ];

    $build['transport_methods']['http_transport']['info'] = [
      '#theme' => 'item_list',
      '#list_type' => 'ul',
      '#items' => [
        $this->t('<strong>Transport:</strong> HTTP POST with JSON-RPC 2.0'),
        $this->t('<strong>Authentication:</strong> Basic Authentication'),
        $this->t('<strong>Best for:</strong> Remote Drupal sites, streamable HTTP clients'),
      ],
    ];

    $build['transport_methods']['stdio_transport'] = [
      '#type' => 'details',
      '#title' => $this->t('2. STDIO Transport (Recommended)'),
      '#open' => TRUE,
    ];

    $build['transport_methods']['stdio_transport']['info'] = [
      '#type' => 'html_tag',
      '#tag' => 'p',
      '#value' => $this->t('Uses a Docker container for secure communication. Recommended for Claude Desktop and Cursor.'),
    ];

    $build['transport_methods']['stdio_transport']['docker'] = [
      '#theme' => 'item_list',
      '#list_type' => 'ul',
      '#items' => [
        $this->t('<strong>Docker Image:</strong> <code>ghcr.io/omedia/mcp-server-drupal:latest</code>'),
        $this->t('<strong>Transport:</strong> STDIO with Docker'),
        $this->t('<strong>Best for:</strong> Claude Desktop, Claude Code, Cursor'),
      ],
    ];

    $build['protocol_info'] = [
      '#type' => 'details',
      '#title' => $this->t('Protocol Information'),
      '#open' => FALSE,
    ];

    $build['protocol_info']['content'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      'info' => [
        '#type' => 'html_tag',
        '#tag' => 'p',
        '#value' => $this->t('This server implements the Model Context Protocol (MCP) specification, enabling AI-powered interactions with your Drupal content and tools.'),
      ],
      'links' => [
        '#theme' => 'item_list',
        '#list_type' => 'ul',
        '#items' => [
          [
            '#markup' => $this->t('MCP Specification: <a href="@url" target="_blank">@url</a>', [
              '@url' => 'https://modelcontextprotocol.io',
            ]),
          ],
          [
            '#markup' => $this->t('Documentation: <a href="@url" target="_blank">@url</a>', [
              '@url' => 'https://drupalmcp.io',
            ]),
          ],
          [
            '#markup' => $this->t('STDIO Binary: <a href="@url" target="_blank">GitHub: omedia/mcp-server-drupal</a>', [
              '@url' => 'https://github.com/omedia/mcp-server-drupal',
            ]),
          ],
          [
            '#markup' => $this->t('Protocol Version: 2024-11-05'),
          ],
        ],
      ],
    ];

    $build['#attached']['library'][] = 'mcp/admin-styles';
    $build['#attached']['html_head'][] = [
      [
        '#type' => 'html_tag',
        '#tag' => 'style',
        '#value' => '.code-container { position: relative; } .code-container pre { padding-right: 80px; overflow-x: auto; background: #f4f4f4; padding: 15px; border-radius: 4px; } .copy-button:hover { opacity: 0.9; }',
      ],
      'mcp-connection-styles',
    ];

    return $build;
  }

}

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

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