Files
wehub-resource-sync e115934061
Publish `@librechat/data-schemas` to NPM / pack (push) Failing after 8s
Publish `@librechat/client` to NPM / pack (push) Failing after 0s
GitNexus Index / index (push) Failing after 1s
Sync Locize Translations & Create Translation PR / Create Translation PR on Version Published (push) Has been skipped
Sync Helm Chart Tags / Sync chart tags (push) Failing after 2s
Publish `librechat-data-provider` to NPM / pack (push) Failing after 1s
Docker Dev Images Build / build (Dockerfile, librechat-dev, node) (push) Failing after 1s
Docker Dev Images Build / build (Dockerfile.multi, librechat-dev-api, api-build) (push) Failing after 0s
Sync Helm Chart Tags / Ignore non-main push (push) Has been skipped
Sync Locize Translations & Create Translation PR / Sync Translation Keys with Locize (push) Failing after 1s
Publish `@librechat/client` to NPM / publish-npm (push) Has been cancelled
Publish `librechat-data-provider` to NPM / publish-npm (push) Has been cancelled
GitNexus Index / post-index (push) Has been cancelled
Publish `@librechat/data-schemas` to NPM / publish-npm (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:08:12 +08:00

79 lines
2.5 KiB
JavaScript

#!/usr/bin/env node
/**
* Streamable-HTTP MCP fixture for the allowlist-override e2e spec.
*
* Unlike the stdio `fake-mcp-server.js`, this one is reachable over a URL so it
* exercises the `mcpSettings.allowedDomains` check (stdio transports skip it). The
* e2e config deliberately omits this server's origin from `allowedDomains`, so it
* boots as `inspectionFailed`; `mcp-allowlist-override.spec.ts` then adds the origin
* via an admin-panel config override and asserts the server reinitializes.
*
* Mirrors the stateful streamable-HTTP pattern in
* packages/api/src/mcp/__tests__/helpers/oauthTestServer.ts (without OAuth).
*/
const http = require('http');
const { randomUUID } = require('crypto');
const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
const {
StreamableHTTPServerTransport,
} = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
const PORT = parseInt(process.env.E2E_MCP_HTTP_PORT || '8765', 10);
const HOST = '127.0.0.1';
function createMcpServer() {
const server = new McpServer({ name: 'e2e-http', version: '1.0.0' });
server.registerTool(
'http_ping',
{
description: 'Returns a deterministic value for LibreChat allowlist-override e2e tests.',
inputSchema: {},
},
async () => ({
content: [{ type: 'text', text: 'E2E HTTP MCP pong' }],
}),
);
return server;
}
/** @type {Map<string, InstanceType<typeof StreamableHTTPServerTransport>>} */
const sessions = new Map();
const httpServer = http.createServer(async (req, res) => {
const url = new URL(req.url, `http://${req.headers.host}`);
// Health endpoint so Playwright's `webServer` can detect readiness.
if (url.pathname === '/' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
return;
}
if (url.pathname !== '/mcp') {
res.writeHead(404);
res.end();
return;
}
const sid = req.headers['mcp-session-id'];
let transport = typeof sid === 'string' ? sessions.get(sid) : undefined;
if (!transport) {
transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
const mcp = createMcpServer();
await mcp.connect(transport);
}
await transport.handleRequest(req, res);
if (transport.sessionId && !sessions.has(transport.sessionId)) {
sessions.set(transport.sessionId, transport);
transport.onclose = () => sessions.delete(transport.sessionId);
}
});
httpServer.listen(PORT, HOST, () => {
console.log(`[e2e] fake HTTP MCP server listening on http://${HOST}:${PORT}/mcp`);
});