Skip to main content
Glama

mcp-wordpress-instaWP

server.ts12.2 kB
#!/usr/bin/env node // src/server.ts import * as dotenv from 'dotenv'; dotenv.config(); // Load environment variables from .env first import express from 'express'; import type { Request, Response } from 'express'; import cors from 'cors'; import { randomUUID } from 'node:crypto'; import type { Server as HttpServer } from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js'; import { allTools, toolHandlers } from './tools/index.js'; import { z } from 'zod'; function createMcpServer() { const server = new McpServer({ name: 'wordpress', version: '0.0.1' }, { capabilities: { tools: allTools.reduce((acc, tool) => { acc[tool.name] = tool; return acc; }, {} as Record<string, unknown>) } }); for (const tool of allTools) { const handler = toolHandlers[tool.name as keyof typeof toolHandlers]; if (!handler) { continue; } const wrappedHandler = async (args: any) => { const result = await handler(args); return { content: result.toolResult.content.map((item: { type: string; text: string }) => ({ ...item, type: 'text' as const })), isError: result.toolResult.isError }; }; const zodSchema = z.object(tool.inputSchema.properties as z.ZodRawShape); server.tool(tool.name, zodSchema.shape, wrappedHandler); } return server; } type SessionEntry = { server: McpServer; transport: StreamableHTTPServerTransport; }; function getSessionId(value: string | string[] | undefined): string | undefined { if (!value) { return undefined; } return Array.isArray(value) ? value[0] : value; } async function startStdioServer(logToFile: (message: string) => void) { const server = createMcpServer(); const transport = new StdioServerTransport(); await server.connect(transport); logToFile('WordPress MCP server running on stdio'); return async () => { try { await transport.close(); } catch (error) { logToFile(`Error closing stdio transport: ${error instanceof Error ? error.message : String(error)}`); } try { await server.close(); } catch (error) { logToFile(`Error closing MCP server: ${error instanceof Error ? error.message : String(error)}`); } }; } async function startHttpServer(logToFile: (message: string) => void) { const port = Number(process.env.PORT ?? process.env.MCP_PORT ?? 8080); const app = express(); app.use(express.json({ limit: '4mb' })); app.use(cors({ origin: '*', exposedHeaders: ['Mcp-Session-Id'] })); const requiredToken = process.env.MCP_API_TOKEN; if (requiredToken) { app.use((req: Request, res: Response, next) => { const authHeader = req.headers.authorization; if (authHeader === `Bearer ${requiredToken}`) { next(); return; } res.status(401).json({ jsonrpc: '2.0', error: { code: -32001, message: 'Unauthorized' }, id: null }); }); } else { logToFile('Warning: MCP_API_TOKEN is not set; HTTP transport is running without authentication.'); } const sessions = new Map<string, SessionEntry>(); const createSession = () => { const server = createMcpServer(); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: sessionId => { if (sessionId) { sessions.set(sessionId, { server, transport }); logToFile(`Initialized MCP session ${sessionId}`); } }, onsessionclosed: sessionId => { if (sessionId) { sessions.delete(sessionId); logToFile(`Closed MCP session ${sessionId}`); } } }); transport.onclose = async () => { const sessionId = transport.sessionId; if (sessionId) { sessions.delete(sessionId); } try { await server.close(); } catch (error) { logToFile(`Error closing MCP server for session ${sessionId ?? 'unknown'}: ${error instanceof Error ? error.message : String(error)}`); } }; return { server, transport }; }; const ensureSession = (sessionId: string) => { const session = sessions.get(sessionId); if (!session) { return null; } return session; }; app.get('/healthz', (_req: Request, res: Response) => { res.status(200).send('ok'); }); app.get('/', (_req: Request, res: Response) => { res.status(200).send('ok'); }); app.post('/mcp', async (req: Request, res: Response) => { const sessionId = getSessionId(req.headers['mcp-session-id']); try { if (sessionId) { const session = ensureSession(sessionId); if (!session) { res.status(404).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Unknown session ID' }, id: null }); return; } await session.transport.handleRequest(req, res, req.body); return; } if (!isInitializeRequest(req.body)) { res.status(400).json({ jsonrpc: '2.0', error: { code: -32600, message: 'Initialization request required before other operations' }, id: null }); return; } const session = createSession(); await session.server.connect(session.transport); await session.transport.handleRequest(req, res, req.body); } catch (error) { logToFile(`Error handling MCP POST request: ${error instanceof Error ? error.message : String(error)}`); if (!res.headersSent) { res.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error' }, id: null }); } } }); app.get('/mcp', async (req: Request, res: Response) => { const sessionId = getSessionId(req.headers['mcp-session-id']); if (!sessionId) { res.status(400).send('Missing session ID'); return; } const session = ensureSession(sessionId); if (!session) { res.status(404).send('Unknown session ID'); return; } try { await session.transport.handleRequest(req, res); } catch (error) { logToFile(`Error handling MCP GET request for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`); if (!res.headersSent) { res.status(500).send('Internal server error'); } } }); app.delete('/mcp', async (req: Request, res: Response) => { const sessionId = getSessionId(req.headers['mcp-session-id']); if (!sessionId) { res.status(400).send('Missing session ID'); return; } const session = ensureSession(sessionId); if (!session) { res.status(404).send('Unknown session ID'); return; } try { await session.transport.handleRequest(req, res); } catch (error) { logToFile(`Error handling MCP DELETE request for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`); if (!res.headersSent) { res.status(500).send('Internal server error'); } } }); const httpServer: HttpServer = await new Promise((resolve, reject) => { const serverInstance = app.listen(port, '0.0.0.0', () => { logToFile(`WordPress MCP HTTP server listening on port ${port}`); resolve(serverInstance); }); serverInstance.once('error', reject); }); return async () => { logToFile('Shutting down HTTP server...'); for (const [sessionId, session] of sessions) { try { await session.transport.close(); } catch (error) { logToFile(`Error closing transport for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`); } try { await session.server.close(); } catch (error) { logToFile(`Error closing MCP server for session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`); } sessions.delete(sessionId); } await new Promise<void>((resolve, reject) => { httpServer.close(error => { if (error) { reject(error); } else { resolve(); } }); }); }; } function setupProcessHandlers(cleanup: () => Promise<void>) { let shuttingDown = false; const shutdown = async (exitCode: number) => { if (shuttingDown) { process.exit(exitCode); } shuttingDown = true; try { await cleanup(); } catch (error) { console.error('Error during shutdown cleanup:', error); } finally { process.exit(exitCode); } }; process.on('SIGTERM', () => { console.log('Received SIGTERM signal, shutting down...'); void shutdown(0); }); process.on('SIGINT', () => { console.log('Received SIGINT signal, shutting down...'); void shutdown(0); }); process.on('uncaughtException', error => { console.error('Uncaught exception:', error); void shutdown(1); }); process.on('unhandledRejection', error => { console.error('Unhandled rejection:', error); void shutdown(1); }); } async function main() { const { logToFile, initWordPress } = await import('./wordpress.js'); logToFile('Starting WordPress MCP server...'); if (!process.env.WORDPRESS_API_URL) { logToFile('Missing required environment variables. Please check your .env file.'); process.exit(1); } try { logToFile('Initializing WordPress client...'); await initWordPress(); logToFile('WordPress client initialized successfully.'); let cleanup: () => Promise<void>; if (process.env.PORT) { logToFile(`Detected PORT environment variable (${process.env.PORT}); starting HTTP transport.`); cleanup = await startHttpServer(logToFile); } else { logToFile('No PORT environment variable detected; starting stdio transport.'); cleanup = await startStdioServer(logToFile); } setupProcessHandlers(cleanup); } catch (error) { logToFile(`Failed to initialize server: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } } void main().catch(error => { console.error('Startup error:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pace8/mcp-wordpress-instaWP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server