How to implement the server part

Created on 11 December 2024, 10 days ago

Problem/Motivation

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

🌱 Plan
Status

Active

Version

1.0

Component

Miscellaneous

Created by

🇬🇪Georgia jibla

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @jibla
  • 🇪🇸Spain ejb503

    Will follow the thread...

    My 2c and I have this working locally and am aiming to get a prototype up for feedback,

    1. Use client SDK (Typescript/Python)
    2. Use shared transport
    3. Build custom module for MCP in Drupal
    a. Get endpoint for streaming
    b. Post endpoint for http POST
    c. Use Drupal queue to stream responses from POST to SSE...

    There is some complexity as we have to manage multiple concurrent subscriptions and Authentication isn't well handled in the MCP yet.

    Initial thoughts are it seems achievable but would like to see feedback as there is significantly more complexity than simply "wrapping the SDKs" so would need to see if folks were keen to actually leverage the extra work for native integration.

    Will follow up with a demo.

  • 🇬🇪Georgia jibla

    Hi @ejb503

    Once again, thanks for your thoughtful input and for pushing the conversation forward. Here's how I propose we proceed with the module implementation: let’s build both approaches.

    Here is my thought process:

    Currently, we have two potential paths:

    1. Make Drupal itself the MCP server.
    2. Build a separate MCP server binary that works with Drupal.

    Option 1: Drupal as the MCP Server

    This approach is definitely the most elegant and user-friendly. It eliminates the need for an intermediate server, enabling users to simply install the module, set their MCP application’s settings to the Drupal URL, and start using it immediately. There’s no additional tech stack or hosting required, making it a natural and low-effort choice for users. Its no-brainier.

    That said, it's well-known, that PHP's limitations pose challenges, particularly with real-time communication. HTTP SSE require persistent processes, which PHP and Drupal aren’t designed for. Workarounds with while loops are functional but hacky (to me), and implementing a complete MCP server (initiation, negotiation, caching, etc.) in PHP/Drupal introduces complexity and potential stability issues.

    Despite these challenges, again, I think, the usability and simplicity of this solution make it worth pursuing for the community.

    Option 2: Separate Executable MCP Server

    This is the approach we’ve tested and published so far in our initial implementation. The idea is to create an independent server binary built on the official MCP SDK, fully supporting the protocol. Drupal becomes a source of data and actions (additional context for models), exposing the necessary information to the server.

    This method, relies on a stable, pre-built transport layer for real-time communication (SSE or stdio) and Drupal focuses on providing context, tools, resources, and prompts without needing to handle low-level protocol details.

    While this setup definitely adds complexity (e.g., requiring experienced users to host and configure the binary server), it aligns well with the way other MCP integrations work—for example, MCP servers built on TS or Python that interact with PostgreSQL or Google Drive without making those systems MCP servers themselves.

    Viewing Drupal as a "data source" rather than the MCP server isn’t inherently wrong—it’s simply a different perspective.

    On the same time, making Drupal the server itself is also a perfectly right view.

    --

    So, I think, both views are right :))

    Solution

    Rather than committing to a single approach, we can develop both options in a modular way. This ensures flexibility and allows us to adapt to future changes, such as new transport mechanisms (e.g., HTTP REST) proposed by Anthropic.

    We can adopt a clean, modular architecture to separate concerns and allow different server implementations to integrate.

    Here’s how we can structure it (its a draft things, we continue later on this deeply):

    First, we have the core part

    mcp_core module
    - Discovers and provides tools, resources, and prompts.
    - Handles authentication and user configuration.
    - Acts as the foundation for all server implementations.

    and then we have sub-modules for server implementations.

    mcp_server_ts module
    - Supports the TypeScript-based server binary.
    - Relies on `mcp_core` for data and actions.
    - Focuses on exposing endpoints for communication with the binary.

    mcp_server_native module
    - Implements a full-fledged MCP server directly in Drupal.
    - Handles all MCP communication while leveraging `mcp_core` for tools, resources, and prompts.

    Other developers can create their own server implementations that integrate with `mcp_core`. This design ensures server modules are like adapters, interchangeable and independent of the core module.

    Let me know your thoughts, and I’d be happy to iterate further. Here's a diagram to illustrate the architecture:

  • 🇪🇸Spain ejb503

    Demo client plugged into the drupal (1) approach can be downloaded and tried here https://github.com/Ejb503/open-source-mcp-client

  • 🇬🇪Georgia gagosha

    Hey, what do you think about the following suggestion, @ejb503 @jibla?

    As a baseline, we all agree with what @marcus_johansson posted in the Slack channel: “It should be hostable by a normal webserver/PHP stack, without any additional layers needed.” Based on this, we can proceed as follows:

    Requirement:

    To implement an MCP server that directly connects to MCP clients, we need the following (simplified):

    1. An SSE endpoint that allows clients to establish a connection and receive messages from the server. Method: GET.
    2. A regular HTTP POST endpoint that enables clients to send messages to the server. Method: POST.

    With this setup, the POST endpoint would run the operation and queue the result, which the GET request could then check and return.

    Proposal:

    What if we add a configuration form with just one checkbox (defaulted to true) that determines whether the POST request should queue the operation result or return it directly?

    This approach would allow us to maintain both implementations without any additional work. The core of MCP would return results compatible with the specification, creating a win-win situation. By default, no additional layer is needed, and everything would be standard, straightforward, and directly hostable by Drupal.

  • 🇪🇸Spain ejb503

    Not even sure you need a checkbox...

    The POST endpoint would act as a regular server, we just add logic to queue the response to the stream in addition to the response.

    If there is no active connection, nothing happens anyway... we just need to make sure the connection and queuing logic is solid.

Production build 0.71.5 2024