WordPress MCP Server Transport Layer

April 6, 2025 ยท View on GitHub

This document provides detailed information about the transport layer in the WordPress MCP Server, including how the transport system works, differences between transport types, how to implement custom transports, and troubleshooting common issues.

Transport System Overview

The WordPress MCP Server uses a transport abstraction layer to communicate with clients. This allows the server to support multiple communication methods while maintaining a consistent API.

Key Components

  • Transport Interface: Defines the methods that all transports must implement
  • Transport Factory: Creates transport instances based on configuration
  • Server Connection: Manages the connection between the server and transport

Communication Flow

  1. Server Initialization: The server is initialized with configuration
  2. Transport Creation: A transport is created based on the configuration
  3. Connection Establishment: The server connects to the transport
  4. Message Exchange: Messages are exchanged between the client and server via the transport
  5. Connection Termination: The connection is closed when the server or client terminates

Supported Transports

Stdio Transport

The stdio transport uses standard input/output streams for communication. This is the default transport and is used for local communication.

Characteristics

  • Local Only: Only works for local communication
  • Simple: No network configuration required
  • Reliable: Direct communication with no network issues
  • Secure: No network exposure

Usage

// Create a stdio transport
const transport = new StdioServerTransport();

// Connect the server to the transport
await server.connect(transport);

SSE Transport

The SSE (Server-Sent Events) transport uses HTTP and SSE for communication. This allows for remote access to the server.

Characteristics

  • Remote Access: Allows clients to connect from remote machines
  • HTTP Based: Uses standard HTTP protocol
  • Streaming: Server can push messages to the client
  • Session Based: Uses session IDs to track connections

Usage

// Create an Express app
const app = express();

// Set up SSE endpoint
app.get('/sse', async (req, res) => {
  // Create SSE transport
  const transport = new SSEServerTransport('/message', res);
  
  // Connect the server to the transport
  await server.connect(transport);
});

// Set up message endpoint
app.post('/message', async (req, res) => {
  // Handle message
  await transport.handlePostMessage(req, res);
});

// Start the server
app.listen(3000);

Implementation Details

  1. Connection Establishment:

    • Client connects to /sse endpoint via GET request
    • Server creates an SSEServerTransport instance with the /message endpoint
    • Server sends an "endpoint" event with the session ID
    • The endpoint event format is: /message?sessionId={id}
  2. Message Exchange:

    • Client sends requests to /message?sessionId={id} endpoint via POST
    • Server responds with "Accepted" (status 202)
    • Server sends the actual response via the SSE connection
  3. Key Implementation Notes:

    • The /message endpoint must not use body-parser middleware
    • The correct method name for calling tools is tools/call
    • The session ID must be included in the request URL as a query parameter
    • The SSEServerTransport generates a UUID for the session ID

Implementing Custom Transports

To implement a custom transport, you need to create a class that implements the Transport interface:

class CustomTransport {
  constructor(options) {
    this.options = options;
    this.onmessage = null;
    this.onclose = null;
    this.onerror = null;
  }

  async connect() {
    // Initialize the transport
    // Set up event handlers
  }

  async send(message) {
    // Send a message to the client
  }

  async close() {
    // Close the connection
  }
}

Then, register your transport with the transport factory:

// In transport/index.js
export function createTransport(type, options = {}) {
  switch (type.toLowerCase()) {
    case 'stdio':
      return new StdioServerTransport();
    case 'sse':
      return new SSEServerTransport(options.messagePath || '/message', options.res);
    case 'custom':
      return new CustomTransport(options);
    default:
      console.error(`[Transport] Unknown transport type: ${type}, falling back to stdio`);
      return new StdioServerTransport();
  }
}

Security Considerations

Stdio Transport

The stdio transport is inherently secure as it only allows local communication. However, you should still be careful about what commands you execute and what data you process.

SSE Transport

The SSE transport exposes the server to the network, so you need to consider security:

  1. Authentication: Implement authentication to prevent unauthorized access
  2. CORS: Configure CORS to restrict which domains can access the server
  3. HTTPS: Use HTTPS to encrypt communication
  4. Rate Limiting: Implement rate limiting to prevent abuse
  5. Input Validation: Validate all input to prevent injection attacks

Example CORS configuration:

// In sse.js
import cors from 'cors';

// ...

// Configure CORS
app.use(cors({
  origin: ['https://trusted-domain.com'],
  methods: ['GET', 'POST'],
  credentials: true
}));

Troubleshooting

Common Issues

Stdio Transport

  • Error: Not connected: Make sure the server is connected to the transport
  • Error: Connection closed: The connection was closed unexpectedly

SSE Transport

  • Error: stream is not readable: Make sure the /message endpoint doesn't use body-parser middleware
  • Error: Method not found: Make sure you're using the correct method name (tools/call)
  • Error: No active SSE connection: Make sure the client is connected to the SSE endpoint
  • Error: Session ID not found: Make sure the session ID is included in the request URL
  • Session ID mismatch: Ensure you're using the session ID generated by SSEServerTransport, not a custom one

Debugging

To enable detailed logging, set the DEBUG environment variable:

# Enable all logging
DEBUG=* npm start

# Enable specific logging
DEBUG=mcp:transport npm start

Cline Integration

The WordPress MCP Server can be integrated with Cline, allowing AI assistants to interact with WordPress sites. This section provides information on how to configure and troubleshoot Cline integration.

Configuration

To use the WordPress MCP Server with Cline, you need to add it to your MCP settings file:

{
  "mcpServers": {
    "wordpress": {
      "command": "node",
      "args": ["/path/to/wordpress-mcp/index.js"],
      "env": {},
      "disabled": false,
      "autoApprove": []
    }
  }
}

For SSE transport, use:

{
  "mcpServers": {
    "wordpress": {
      "autoApprove": [],
      "disabled": false,
      "timeout": 60,
      "url": "http://localhost:3000/sse",
      "transportType": "sse"
    }
  }
}

Session ID Handling

The SSE transport uses session IDs to track connections. It's important to understand how session IDs are handled:

  1. Session ID Generation: The SSEServerTransport generates a UUID for the session ID using randomUUID() from Node.js crypto module.
  2. Session ID Communication: The server sends the session ID to the client in the "endpoint" event.
  3. Session ID Usage: The client must include the session ID in all requests to the server.

When implementing the SSE transport, it's crucial to use the session ID generated by the SSEServerTransport rather than generating a custom one. This ensures compatibility with Cline and other MCP clients.

// Correct implementation
const transport = new SSEServerTransport('/message', res);
const sessionId = transport.sessionId; // Use this session ID for tracking connections

Common Cline Integration Issues

  1. Session ID Mismatch: If your server generates a different session ID than what SSEServerTransport generates, Cline won't be able to connect properly.

    Solution: Use the session ID from transport.sessionId rather than generating your own.

  2. Body Parsing Issues: If the /message endpoint uses body-parser middleware, it can consume the request stream, causing "stream is not readable" errors.

    Solution: Exclude the /message endpoint from body parsing:

    app.use((req, res, next) => {
      if (req.path !== '/message') {
        bodyParser.json({
          strict: false
        })(req, res, next);
      } else {
        next();
      }
    });
    
  3. Endpoint Format: Cline expects the endpoint to be in the format /message?sessionId={id}.

    Solution: Ensure your SSEServerTransport is initialized with the correct endpoint path:

    const transport = new SSEServerTransport('/message', res);
    
  4. Connection Handling: Properly handle client disconnections to clean up resources.

    Solution: Listen for the 'close' event on the request:

    req.on('close', () => {
      // Clean up the connection
      if (connections.has(sessionId)) {
        const connection = connections.get(sessionId);
        connection.server.server.close().catch(console.error);
        connections.delete(sessionId);
      }
    });
    

Performance Considerations

Stdio Transport

The stdio transport is very efficient for local communication, but it's limited to a single connection.

SSE Transport

The SSE transport can handle multiple connections, but it's more resource-intensive:

  • Connection Limit: Configure the maximum number of connections
  • Timeout: Set appropriate timeouts for connections
  • Memory Usage: Monitor memory usage, especially with many connections
  • CPU Usage: Monitor CPU usage, especially with many connections

Example configuration:

// In config.json
{
  "server": {
    "transport": "sse",
    "sse": {
      "port": 3000,
      "maxConnections": 100,
      "timeout": 30000
    }
  }
}

Testing Transport Compatibility

To test if your transport implementation is compatible with Cline:

  1. Start your server with the SSE transport
  2. Use the test-sse-client.js script to verify basic functionality
  3. Configure Cline to use your server
  4. Test basic operations through Cline

If you encounter issues, enable verbose logging and check the server logs for error messages.