UNPKG

@websolutespa/payload-plugin-bowl-llm

Version:

LLM plugin for Bowl PayloadCms plugin

75 lines (74 loc) 3 kB
import { addDataAndFileToRequest, AuthenticationError } from 'payload'; import { appHandler } from './app.handler'; import { errorHandler } from './error.handler'; import { getMCPHandler } from './mcp.handler'; /** * MCP Handler — authenticates via appKey/apiKey (same as messageHandler). * * Endpoint: POST/GET/DELETE /llm/mcp * Query params: appKey, apiKey, locale * * Example URL: * POST /bowl/api/llm/mcp?appKey=MY_KEY&apiKey=MY_SECRET&locale=en * * Creates a stateless MCP server exposing the app's active tools. * Uses HTTP Streamable transport with the low-level Server API * to pass raw JSON Schemas from Pydantic models. */ export const mcpHandler = async (req)=>{ try { // Authenticate and load app via appHandler (same as messageHandler). // appHandler reads appKey/apiKey from req.body and locale from req.query. // Since the MCP body is a JSON-RPC message, we inject the query params // into req.body, call appHandler, then restore the original body. const { appKey, apiKey } = req.query; await addDataAndFileToRequest(req); const subRequest = { ...req, data: { ...req.data, appKey, apiKey } }; const app = await appHandler(subRequest); // @modelcontextprotocol/sdk's StreamableHTTPServerTransport uses @hono/node-server's // getRequestListener, which replaces global.Request and global.Response with Hono // custom classes. Unfortunately, we cannot pass overrideGlobalObjects: false because the option is // consumed inside the SDK transport and is not exposed to callers. // Save originals here and restore after the handler resolves so that Next.js // instanceof Response checks on subsequent route handlers keep working. const globals = globalThis; const originalResponse = globals['Response']; const originalRequest = globals['Request']; const mcpHandler = await getMCPHandler(req, app); const request = createRequestFromPayloadRequest(req); try { return await mcpHandler(request); } finally{ if (globals['Response'] !== originalResponse) { Object.defineProperty(globalThis, 'Response', { value: originalResponse }); } if (globals['Request'] !== originalRequest) { Object.defineProperty(globalThis, 'Request', { value: originalRequest }); } } } catch (error) { return errorHandler(error); } }; export const createRequestFromPayloadRequest = (req)=>{ if (!req.url) { throw new AuthenticationError(); } return new Request(req.url, { body: req.body, duplex: 'half', headers: req.headers, method: req.method }); }; //# sourceMappingURL=mcp.server.js.map