UNPKG

redis-mcp-transport

Version:

Redis-based transport layer for Model Context Protocol with SSE support

118 lines (113 loc) 4.62 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var redis = require('redis'); var node_crypto = require('node:crypto'); var contentType = require('content-type'); var getRawBody = require('raw-body'); var types_js = require('@modelcontextprotocol/sdk/types.js'); // 订阅端实现 class RedisMcpTransport { constructor(endpoint, resOrSessionId, redisURL) { this._endpoint = endpoint; this._redisClient = redis.createClient({ url: redisURL }); this._redisClient.on('error', (err) => console.error('Redis Client Error', err)); this._isStarted = false; if (typeof resOrSessionId === 'string') { this._sessionId = resOrSessionId; } else { this._sseResponse = resOrSessionId; this._sessionId = node_crypto.randomUUID(); } } async start() { if (!this._isStarted) { await this._redisClient.connect(); this._isStarted = true; } if (this._sseResponse) { // 创建单独的订阅客户端 // 订阅客户端直接操作this._ssrResponse不走onmessage逻辑 this._subscriberClient = this._redisClient.duplicate(); await this._subscriberClient.connect(); const channel = this.getChannelName(); console.debug('Subscribing to channel:', channel); await this._subscriberClient.subscribe(channel, (message) => { var _a; try { const data = JSON.parse(message); (_a = this._sseResponse) === null || _a === void 0 ? void 0 : _a.write(`event: message\ndata: ${JSON.stringify(data)}\n\n`); } catch (error) { console.error('Error handling SSE message:', error); } }); this._sseResponse.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }); this._sseResponse.write(`event: endpoint\ndata: ${encodeURI(this._endpoint)}?sessionId=${this._sessionId}\n\n`); } } /** * 实现与 SSEServerTransport 兼容的 handlePostMessage 方法 * @param {Object} req - HTTP 请求对象(不使用,保持接口一致) * @param {Object} res - HTTP 响应对象(不使用,保持接口一致) * @returns {Promise<void>} */ async handlePostMessage(req, res) { var _a; // 发布客户端处理handlePostMessage,走onmessage逻辑会触发transport的send方法,始终往redis channel发布消息 try { // @ts-ignore let message = req.body; if (!message) { const contentTypeHeader = req.headers['content-type']; const ct = contentType.parse(contentTypeHeader); if (ct.type !== 'application/json') { throw new Error('Unsupported content type'); } let body = await getRawBody(req, { encoding: ct.parameters.charset || 'utf-8' }); message = JSON.parse(body); } // const message: any = JSON.parse(body); const parsedMessage = types_js.JSONRPCMessageSchema.parse(message); (_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, parsedMessage); } catch (error) { console.error('Error in handlePostMessage:', error); throw error; } } // 走一遍server的onmessage逻辑会触发transport的send方法 async send(message) { if (!this._isStarted) { await this._redisClient.connect(); this._isStarted = true; } const channel = this.getChannelName(); await this._redisClient.publish(channel, JSON.stringify(message)); await this.close(); } async close() { if (this._subscriberClient) { const channel = this.getChannelName(); await this._subscriberClient.unsubscribe(channel); await this._subscriberClient.disconnect(); } if (this._isStarted) { await this._redisClient.disconnect(); this._isStarted = false; } } getSessionId() { return this._sessionId; } getChannelName() { return `sse:channel:${this._sessionId}`; } } exports.RedisMcpTransport = RedisMcpTransport; exports.default = RedisMcpTransport; //# sourceMappingURL=index.js.map