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
- Server Initialization: The server is initialized with configuration
- Transport Creation: A transport is created based on the configuration
- Connection Establishment: The server connects to the transport
- Message Exchange: Messages are exchanged between the client and server via the transport
- 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
-
Connection Establishment:
- Client connects to
/sseendpoint via GET request - Server creates an SSEServerTransport instance with the
/messageendpoint - Server sends an "endpoint" event with the session ID
- The endpoint event format is:
/message?sessionId={id}
- Client connects to
-
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
- Client sends requests to
-
Key Implementation Notes:
- The
/messageendpoint 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
- The
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:
- Authentication: Implement authentication to prevent unauthorized access
- CORS: Configure CORS to restrict which domains can access the server
- HTTPS: Use HTTPS to encrypt communication
- Rate Limiting: Implement rate limiting to prevent abuse
- 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
/messageendpoint 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:
- Session ID Generation: The SSEServerTransport generates a UUID for the session ID using
randomUUID()from Node.js crypto module. - Session ID Communication: The server sends the session ID to the client in the "endpoint" event.
- 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
-
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.sessionIdrather than generating your own. -
Body Parsing Issues: If the
/messageendpoint uses body-parser middleware, it can consume the request stream, causing "stream is not readable" errors.Solution: Exclude the
/messageendpoint from body parsing:app.use((req, res, next) => { if (req.path !== '/message') { bodyParser.json({ strict: false })(req, res, next); } else { next(); } }); -
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); -
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:
- Start your server with the SSE transport
- Use the test-sse-client.js script to verify basic functionality
- Configure Cline to use your server
- Test basic operations through Cline
If you encounter issues, enable verbose logging and check the server logs for error messages.