unreal-mcp-proxy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,333 @@
1
+ import http, {} from "node:http";
2
+ import { randomUUID } from "node:crypto";
3
+ import { pipeline } from "node:stream/promises";
4
+ import { Transform } from "node:stream";
5
+ import { readFile } from "node:fs/promises";
6
+ import { StringDecoder } from "node:string_decoder";
7
+ import { redactHeaders, summarizeBody } from "./redaction.js";
8
+ import { buildSessionModel } from "./session-model.js";
9
+ function sendJson(response, status, body) {
10
+ const data = Buffer.from(JSON.stringify(body));
11
+ response.writeHead(status, { "content-type": "application/json; charset=utf-8", "content-length": data.length });
12
+ response.end(data);
13
+ }
14
+ async function readJson(request, maxBytes = 65536) {
15
+ const body = await collect(request);
16
+ if (body.length > maxBytes)
17
+ throw new Error("request_too_large");
18
+ return JSON.parse(body.toString("utf8"));
19
+ }
20
+ async function collect(stream) {
21
+ const chunks = [];
22
+ for await (const chunk of stream)
23
+ chunks.push(chunk);
24
+ return Buffer.concat(chunks);
25
+ }
26
+ class BodyCapture {
27
+ limit;
28
+ size = 0;
29
+ chunks = [];
30
+ constructor(limit) {
31
+ this.limit = limit;
32
+ }
33
+ add(chunk) {
34
+ this.size += chunk.length;
35
+ if (this.size <= this.limit)
36
+ this.chunks.push(Buffer.from(chunk));
37
+ }
38
+ summary(contentType, options) {
39
+ if (this.size > this.limit)
40
+ return { omitted: true, reason: "body_too_large", size: this.size };
41
+ return summarizeBody(Buffer.concat(this.chunks), contentType, options);
42
+ }
43
+ }
44
+ class PassThroughCapture extends Transform {
45
+ limit;
46
+ size = 0;
47
+ chunks = [];
48
+ constructor(limit) {
49
+ super();
50
+ this.limit = limit;
51
+ }
52
+ _transform(chunk, _encoding, callback) {
53
+ this.size += chunk.length;
54
+ if (this.size <= this.limit)
55
+ this.chunks.push(Buffer.from(chunk));
56
+ callback(null, chunk);
57
+ }
58
+ summary(contentType, options) {
59
+ if (this.size > this.limit)
60
+ return { omitted: true, reason: "body_too_large", size: this.size };
61
+ return summarizeBody(Buffer.concat(this.chunks), contentType, options);
62
+ }
63
+ }
64
+ /**
65
+ * Unreal's MCP HTTP server may keep a tools/call SSE connection open until its keep-alive
66
+ * expires, long after the final result event arrived. Without this, every call appears to
67
+ * take ~15s. We end the downstream response as soon as the final JSON-RPC result/error for
68
+ * the request id is observed.
69
+ */
70
+ function relayUntilFinalSse(upstreamResponse, response, requestId, limit) {
71
+ return new Promise((resolve, reject) => {
72
+ const capture = new BodyCapture(limit);
73
+ const decoder = new StringDecoder("utf8");
74
+ let pending = "";
75
+ let finished = false;
76
+ const finish = () => {
77
+ if (finished)
78
+ return;
79
+ finished = true;
80
+ response.end();
81
+ upstreamResponse.destroy();
82
+ resolve(capture);
83
+ };
84
+ const fail = (error) => {
85
+ if (finished)
86
+ return;
87
+ finished = true;
88
+ reject(error);
89
+ };
90
+ const inspectEvents = () => {
91
+ const blocks = pending.split(/\r?\n\r?\n/);
92
+ pending = blocks.pop() ?? "";
93
+ for (const block of blocks) {
94
+ const data = block.split(/\r?\n/)
95
+ .filter((line) => line.startsWith("data:"))
96
+ .map((line) => line.slice(5).trimStart())
97
+ .join("\n");
98
+ if (!data)
99
+ continue;
100
+ try {
101
+ const message = JSON.parse(data);
102
+ const sameRequest = String(message.id) === String(requestId);
103
+ if (sameRequest && (Object.hasOwn(message, "result") || Object.hasOwn(message, "error"))) {
104
+ finish();
105
+ return;
106
+ }
107
+ }
108
+ catch { /* partial frame */ }
109
+ }
110
+ };
111
+ upstreamResponse.on("data", (chunk) => {
112
+ if (finished)
113
+ return;
114
+ capture.add(chunk);
115
+ response.write(chunk);
116
+ pending += decoder.write(chunk);
117
+ inspectEvents();
118
+ });
119
+ upstreamResponse.on("end", () => {
120
+ if (finished)
121
+ return;
122
+ pending += decoder.end();
123
+ pending += "\n\n";
124
+ inspectEvents();
125
+ if (!finished)
126
+ fail(new Error("SSE stream ended without a final JSON-RPC result"));
127
+ });
128
+ upstreamResponse.on("error", fail);
129
+ response.on("close", () => {
130
+ if (!finished) {
131
+ upstreamResponse.destroy();
132
+ fail(new Error("Downstream connection closed before the final SSE result"));
133
+ }
134
+ });
135
+ });
136
+ }
137
+ export function createProxyServer(config, store, options = {}) {
138
+ const upstream = new URL(config.upstreamUrl);
139
+ let viewerHtml = null;
140
+ const serveViewer = async (response) => {
141
+ if (!viewerHtml && options.viewerPath) {
142
+ try {
143
+ viewerHtml = await readFile(options.viewerPath);
144
+ }
145
+ catch { /* not built */ }
146
+ }
147
+ if (!viewerHtml) {
148
+ return sendJson(response, 503, { error: "viewer_not_built", message: "run `npm run build` or download viewer.html from the release" });
149
+ }
150
+ response.writeHead(200, {
151
+ "content-type": "text/html; charset=utf-8",
152
+ "cache-control": "no-store",
153
+ "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; connect-src 'self'; img-src data:"
154
+ });
155
+ response.end(viewerHtml);
156
+ };
157
+ return http.createServer(async (request, response) => {
158
+ const requestUrl = new URL(request.url ?? "/", `http://${request.headers.host ?? "127.0.0.1"}`);
159
+ const { pathname } = requestUrl;
160
+ if (pathname === "/health" && request.method === "GET") {
161
+ return sendJson(response, 200, { ok: true, session: store.describe(), upstream: config.upstreamUrl });
162
+ }
163
+ if (pathname === "/api/session" && request.method === "GET") {
164
+ return sendJson(response, 200, store.describe());
165
+ }
166
+ if (pathname === "/api/session/clear" && request.method === "POST") {
167
+ return sendJson(response, 200, await store.clear());
168
+ }
169
+ {
170
+ const match = pathname.match(/^\/api\/sessions\/([0-9a-f-]+)\/stream$/i);
171
+ if (match && request.method === "GET") {
172
+ response.writeHead(200, {
173
+ "content-type": "text/event-stream; charset=utf-8",
174
+ "cache-control": "no-cache, no-transform",
175
+ "connection": "keep-alive",
176
+ "x-accel-buffering": "no"
177
+ });
178
+ response.write(`event: connected\ndata: ${JSON.stringify({ sessionId: match[1] })}\n\n`);
179
+ const unsubscribe = store.subscribe((event) => {
180
+ if (event.sessionId !== match[1])
181
+ return;
182
+ response.write(`event: changed\nid: ${event.sequence}\ndata: ${JSON.stringify({ sequence: event.sequence, type: event.type, callId: event.callId ?? null })}\n\n`);
183
+ });
184
+ const heartbeat = setInterval(() => response.write(": keepalive\n\n"), 15000);
185
+ heartbeat.unref();
186
+ request.on("close", () => { clearInterval(heartbeat); unsubscribe(); });
187
+ return;
188
+ }
189
+ }
190
+ {
191
+ const match = pathname.match(/^\/api\/sessions\/([0-9a-f-]+)$/i);
192
+ if (match && request.method === "GET") {
193
+ const events = await store.readEvents(match[1]);
194
+ return sendJson(response, 200, buildSessionModel(match[1], events, config.webBaseUrl));
195
+ }
196
+ }
197
+ {
198
+ const match = pathname.match(/^\/api\/sessions\/([0-9a-f-]+)\/events$/i);
199
+ if (match && request.method === "GET") {
200
+ return sendJson(response, 200, { sessionId: match[1], events: await store.readEvents(match[1]) });
201
+ }
202
+ }
203
+ {
204
+ const match = pathname.match(/^\/api\/sessions\/([0-9a-f-]+)\/annotations$/i);
205
+ if (match && request.method === "POST") {
206
+ try {
207
+ if (match[1] !== store.session?.id)
208
+ return sendJson(response, 409, { error: "session_not_active" });
209
+ const annotation = await readJson(request);
210
+ if (!annotation.callId || !annotation.title || !annotation.summary) {
211
+ return sendJson(response, 400, { error: "callId, title, summary are required" });
212
+ }
213
+ const event = await store.append("ai_annotation", {
214
+ callId: annotation.callId,
215
+ severity: annotation.severity ?? "info",
216
+ title: annotation.title,
217
+ summary: annotation.summary,
218
+ cause: annotation.cause,
219
+ suggestion: annotation.suggestion,
220
+ author: annotation.author ?? "agent"
221
+ });
222
+ return sendJson(response, 201, event);
223
+ }
224
+ catch (error) {
225
+ return sendJson(response, 400, { error: error.message });
226
+ }
227
+ }
228
+ }
229
+ if ((pathname === "/viewer" || /^\/sessions\/[0-9a-f-]+$/i.test(pathname)) && request.method === "GET") {
230
+ return serveViewer(response);
231
+ }
232
+ if (pathname !== "/mcp")
233
+ return sendJson(response, 404, { error: "not_found" });
234
+ // GET /mcp is the streamable-HTTP notification channel, not a call - pipe it without recording.
235
+ if (request.method === "GET" || request.method === "DELETE") {
236
+ const passthrough = http.request(upstream, {
237
+ method: request.method,
238
+ headers: { ...request.headers, host: upstream.host }
239
+ }, (upstreamResponse) => {
240
+ response.writeHead(upstreamResponse.statusCode ?? 502, upstreamResponse.headers);
241
+ upstreamResponse.pipe(response);
242
+ });
243
+ passthrough.on("error", (error) => {
244
+ if (!response.headersSent)
245
+ sendJson(response, 502, { error: "upstream_unavailable", message: error.message });
246
+ else
247
+ response.destroy(error);
248
+ });
249
+ request.pipe(passthrough);
250
+ return;
251
+ }
252
+ // ---- transparent MCP forwarding with recording ----
253
+ const callId = randomUUID();
254
+ const started = performance.now();
255
+ const requestBody = await collect(request);
256
+ const requestSummary = summarizeBody(requestBody, request.headers["content-type"], config.redaction);
257
+ await store.append("mcp_request_started", {
258
+ callId,
259
+ clientSource: request.headers["x-observability-source"] ?? "agent",
260
+ operationId: request.headers["x-observability-operation-id"],
261
+ operationTitle: typeof request.headers["x-observability-operation-title"] === "string"
262
+ ? decodeURIComponent(request.headers["x-observability-operation-title"])
263
+ : undefined,
264
+ operationStep: request.headers["x-observability-operation-step"],
265
+ method: request.method,
266
+ headers: redactHeaders(request.headers, config.redaction.headers),
267
+ body: requestSummary
268
+ });
269
+ const headers = { ...request.headers, host: upstream.host };
270
+ headers["x-observability-session-id"] = store.session.id;
271
+ headers["x-observability-call-id"] = callId;
272
+ headers["content-length"] = String(requestBody.length);
273
+ const upstreamRequest = http.request(upstream, { method: request.method, headers });
274
+ let settled = false;
275
+ upstreamRequest.on("response", async (upstreamResponse) => {
276
+ const responseHeadersMs = Math.round((performance.now() - started) * 100) / 100;
277
+ const contentType = upstreamResponse.headers["content-type"] ?? "";
278
+ const summaryRecord = requestSummary && typeof requestSummary === "object" && !Array.isArray(requestSummary)
279
+ ? requestSummary : null;
280
+ const finalizableSse = summaryRecord?.method === "tools/call" && contentType.includes("text/event-stream");
281
+ const downstreamHeaders = { ...upstreamResponse.headers };
282
+ if (finalizableSse) {
283
+ delete downstreamHeaders.connection;
284
+ delete downstreamHeaders["content-length"];
285
+ delete downstreamHeaders["transfer-encoding"];
286
+ }
287
+ response.writeHead(upstreamResponse.statusCode ?? 502, downstreamHeaders);
288
+ try {
289
+ let capture;
290
+ if (finalizableSse) {
291
+ capture = await relayUntilFinalSse(upstreamResponse, response, summaryRecord?.id, config.redaction.maxBodyBytes);
292
+ }
293
+ else {
294
+ const passThrough = new PassThroughCapture(config.redaction.maxBodyBytes);
295
+ await pipeline(upstreamResponse, passThrough, response);
296
+ capture = passThrough;
297
+ }
298
+ settled = true;
299
+ const resultEventMs = Math.round((performance.now() - started) * 100) / 100;
300
+ await store.append("mcp_request_completed", {
301
+ callId,
302
+ status: upstreamResponse.statusCode,
303
+ durationMs: resultEventMs,
304
+ responseHeadersMs,
305
+ resultEventMs,
306
+ completionReason: finalizableSse ? "final_sse_event" : "stream_end",
307
+ headers: redactHeaders(upstreamResponse.headers, config.redaction.headers),
308
+ body: capture.summary(contentType, config.redaction)
309
+ });
310
+ }
311
+ catch (error) {
312
+ settled = true;
313
+ await store.append("mcp_request_failed", { callId, error: error.message });
314
+ }
315
+ });
316
+ upstreamRequest.on("error", async (error) => {
317
+ if (settled)
318
+ return;
319
+ settled = true;
320
+ await store.append("mcp_request_failed", {
321
+ callId,
322
+ durationMs: Math.round((performance.now() - started) * 100) / 100,
323
+ error: error.message
324
+ });
325
+ if (!response.headersSent)
326
+ sendJson(response, 502, { error: "upstream_unavailable", message: error.message });
327
+ else
328
+ response.destroy(error);
329
+ });
330
+ upstreamRequest.end(requestBody);
331
+ });
332
+ }
333
+ //# sourceMappingURL=proxy-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-server.js","sourceRoot":"","sources":["../src/proxy-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { BodySummary, JsonValue } from "./types.js";
2
+ export interface RedactionConfig {
3
+ headers: string[];
4
+ jsonKeys: string[];
5
+ maxBodyBytes: number;
6
+ }
7
+ export declare function redactHeaders(headers: Record<string, string | string[] | undefined>, names: string[]): Record<string, string | string[] | undefined>;
8
+ export declare function redactValue(value: JsonValue, keys: string[]): JsonValue;
9
+ /** Summarize a request/response body for recording: parse JSON, unpack SSE frames, redact secrets. */
10
+ export declare function summarizeBody(buffer: Buffer, contentType: string | undefined, options: RedactionConfig): BodySummary | null;
@@ -0,0 +1,55 @@
1
+ const REDACTED = "[REDACTED]";
2
+ export function redactHeaders(headers, names) {
3
+ const blocked = new Set(names.map((name) => name.toLowerCase()));
4
+ return Object.fromEntries(Object.entries(headers).map(([key, value]) => [
5
+ key,
6
+ blocked.has(key.toLowerCase()) ? REDACTED : value
7
+ ]));
8
+ }
9
+ export function redactValue(value, keys) {
10
+ const blocked = new Set(keys.map((key) => key.toLowerCase()));
11
+ const visit = (item) => {
12
+ if (Array.isArray(item))
13
+ return item.map(visit);
14
+ if (!item || typeof item !== "object")
15
+ return item;
16
+ return Object.fromEntries(Object.entries(item).map(([key, child]) => [
17
+ key,
18
+ blocked.has(key.toLowerCase()) ? REDACTED : visit(child)
19
+ ]));
20
+ };
21
+ return visit(value);
22
+ }
23
+ /** Summarize a request/response body for recording: parse JSON, unpack SSE frames, redact secrets. */
24
+ export function summarizeBody(buffer, contentType, options) {
25
+ if (!buffer.length)
26
+ return null;
27
+ if (buffer.length > options.maxBodyBytes) {
28
+ return { omitted: true, reason: "body_too_large", size: buffer.length };
29
+ }
30
+ if (contentType?.includes("text/event-stream")) {
31
+ const events = buffer.toString("utf8").split(/\r?\n/)
32
+ .filter((line) => line.startsWith("data:"))
33
+ .map((line) => line.slice(5).trim())
34
+ .filter(Boolean)
35
+ .map((data) => {
36
+ try {
37
+ return redactValue(JSON.parse(data), options.jsonKeys);
38
+ }
39
+ catch {
40
+ return { omitted: true, reason: "invalid_sse_json" };
41
+ }
42
+ });
43
+ return { transport: "sse", events };
44
+ }
45
+ if (!contentType?.includes("application/json")) {
46
+ return { omitted: true, reason: "non_json", size: buffer.length };
47
+ }
48
+ try {
49
+ return redactValue(JSON.parse(buffer.toString("utf8")), options.jsonKeys);
50
+ }
51
+ catch {
52
+ return { omitted: true, reason: "invalid_json", size: buffer.length };
53
+ }
54
+ }
55
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { SessionEvent, SessionModel } from "./types.js";
2
+ export declare function buildSessionModel(sessionId: string, events: SessionEvent[], webBaseUrl: string, now?: number): SessionModel;
3
+ /** Normalize an error message for similarity matching: mask paths, UUIDs, hex, and numbers. */
4
+ export declare function normalizeError(text: string | null | undefined): string | null;