@deadragdoll/tellymcp 0.0.13 → 0.0.14
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.
- package/.env.example.client +5 -1
- package/.env.example.gateway +16 -1
- package/CHANGELOG.md +30 -0
- package/README-ru.md +14 -0
- package/README.md +14 -0
- package/TOOLS.md +182 -10
- package/config/templates/env.both.template +15 -2
- package/config/templates/env.client.template +5 -2
- package/config/templates/env.gateway.template +15 -2
- package/dist/cli.js +28 -1
- package/dist/services/features/telegram-mcp/browser.service.js +18 -0
- package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
- package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
- package/dist/services/features/telegram-mcp/mcp-server.service.js +18 -0
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +9 -1
- package/dist/services/features/telegram-mcp/src/app/config/env.js +119 -2
- package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
- package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +62 -19
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +410 -27
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +37 -3
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +7 -6
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +214 -37
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +186 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +4 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -35
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +5 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +3 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
- package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
- package/docs/CHAT_CONNECTOR.md +134 -0
- package/docs/STANDALONE-ru.md +13 -0
- package/docs/STANDALONE.md +13 -0
- package/package.json +5 -3
- package/packages/chrome-attach-extension/dist/background.js +572 -163
- package/packages/chrome-attach-extension/dist/manifest.json +2 -1
- package/packages/chrome-attach-extension/dist/options.js +15 -2
- package/packages/chrome-attach-extension/dist/recorder-content.js +14 -1
- package/packages/chrome-attach-extension/dist/recorder-page.js +34 -18
- package/packages/firefox-attach-extension/dist/background.js +413 -33
- package/packages/firefox-attach-extension/dist/manifest.json +0 -12
- package/packages/firefox-attach-extension/dist/options.js +14 -1
- package/packages/firefox-attach-extension/dist/recorder-content.js +14 -1
- package/packages/firefox-attach-extension/dist/recorder-page.js +34 -18
|
@@ -3,15 +3,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveMcpHttpAuthorization = resolveMcpHttpAuthorization;
|
|
7
|
+
exports.isMcpHttpRequestAuthorized = isMcpHttpRequestAuthorized;
|
|
6
8
|
exports.createMcpHttpHandler = createMcpHttpHandler;
|
|
7
9
|
const node_crypto_1 = require("node:crypto");
|
|
8
10
|
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
9
11
|
const ws_1 = __importDefault(require("ws"));
|
|
12
|
+
const oauthFacade_1 = require("./oauthFacade");
|
|
10
13
|
const auth_1 = require("./webapp/auth");
|
|
11
14
|
const relay_1 = require("./webapp/relay");
|
|
12
15
|
const assets_1 = require("./webapp/assets");
|
|
13
16
|
const client_1 = require("../shared/integrations/terminal/client");
|
|
14
17
|
const terminal_1 = require("./webapp/terminal");
|
|
18
|
+
const bodyLimits_1 = require("../shared/lib/bodyLimits");
|
|
15
19
|
function formatTerminalHttpError(error, fallback) {
|
|
16
20
|
if ((0, terminal_1.isTerminalUnavailableError)(error)) {
|
|
17
21
|
return "terminal runtime is unavailable";
|
|
@@ -32,6 +36,13 @@ function isInitializeRequest(body) {
|
|
|
32
36
|
const method = Reflect.get(body, "method");
|
|
33
37
|
return method === "initialize";
|
|
34
38
|
}
|
|
39
|
+
function readMcpRpcMethod(body) {
|
|
40
|
+
if (!body || typeof body !== "object") {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const method = Reflect.get(body, "method");
|
|
44
|
+
return typeof method === "string" ? method : undefined;
|
|
45
|
+
}
|
|
35
46
|
function readHeader(req, headerName) {
|
|
36
47
|
const value = req.headers[headerName];
|
|
37
48
|
if (Array.isArray(value)) {
|
|
@@ -44,24 +55,21 @@ async function readJsonBody(req) {
|
|
|
44
55
|
if (knownParams &&
|
|
45
56
|
typeof knownParams === "object" &&
|
|
46
57
|
Object.keys(knownParams).length > 0) {
|
|
58
|
+
(0, bodyLimits_1.assertSerializedBodySize)(knownParams);
|
|
47
59
|
return knownParams;
|
|
48
60
|
}
|
|
49
61
|
const knownBody = req.body;
|
|
50
62
|
if (knownBody !== undefined) {
|
|
63
|
+
(0, bodyLimits_1.assertSerializedBodySize)(knownBody);
|
|
51
64
|
return knownBody;
|
|
52
65
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
61
|
-
if (!raw) {
|
|
62
|
-
return undefined;
|
|
66
|
+
return (0, bodyLimits_1.readLimitedJsonBody)(req);
|
|
67
|
+
}
|
|
68
|
+
function ignoreInvalidJsonBody(error) {
|
|
69
|
+
if ((0, bodyLimits_1.isBodySizeLimitError)(error)) {
|
|
70
|
+
throw error;
|
|
63
71
|
}
|
|
64
|
-
return
|
|
72
|
+
return undefined;
|
|
65
73
|
}
|
|
66
74
|
function writeJson(res, statusCode, payload) {
|
|
67
75
|
const body = JSON.stringify(payload);
|
|
@@ -103,6 +111,19 @@ function readBearerToken(req) {
|
|
|
103
111
|
}
|
|
104
112
|
return header.slice("Bearer ".length).trim() || null;
|
|
105
113
|
}
|
|
114
|
+
function resolveMcpHttpAuthorization(req, internalBearerToken, oauthFacade) {
|
|
115
|
+
if (!internalBearerToken && !oauthFacade) {
|
|
116
|
+
return "public";
|
|
117
|
+
}
|
|
118
|
+
if (internalBearerToken && isAuthorized(req, internalBearerToken)) {
|
|
119
|
+
return "internal_bearer";
|
|
120
|
+
}
|
|
121
|
+
const token = readBearerToken(req);
|
|
122
|
+
return token && oauthFacade?.verifyAccessToken(token) ? "oauth" : null;
|
|
123
|
+
}
|
|
124
|
+
function isMcpHttpRequestAuthorized(req, internalBearerToken, oauthFacade) {
|
|
125
|
+
return resolveMcpHttpAuthorization(req, internalBearerToken, oauthFacade) !== null;
|
|
126
|
+
}
|
|
106
127
|
function isDuplicateSseStreamError(error) {
|
|
107
128
|
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
108
129
|
return message.includes("Only one SSE stream is allowed per session");
|
|
@@ -143,7 +164,10 @@ function isRelayTerminalUnavailableMessage(error) {
|
|
|
143
164
|
function createMcpHttpHandler(runtime, input) {
|
|
144
165
|
const transports = new Map();
|
|
145
166
|
const webAppSessions = new auth_1.WebAppSessionRegistry();
|
|
146
|
-
const liveWsServer = new WebSocketServer({
|
|
167
|
+
const liveWsServer = new WebSocketServer({
|
|
168
|
+
noServer: true,
|
|
169
|
+
maxPayload: bodyLimits_1.MAX_BODY_SIZE_BYTES,
|
|
170
|
+
});
|
|
147
171
|
const webAppBasePath = runtime.config.webapp.basePath.replace(/\/+$/u, "") || "/webapp";
|
|
148
172
|
const rootPrefix = normalizeBasePath(process.env.ROOT_PREFIX || "/api");
|
|
149
173
|
const publicWebAppBasePath = rootPrefix === "/"
|
|
@@ -153,6 +177,13 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
153
177
|
const webAppLivePrefix = `${webAppBasePath}/live/`;
|
|
154
178
|
const telegramWebhookPath = runtime.config.telegram.webhook.path.replace(/\/+$/u, "") ||
|
|
155
179
|
"/telegram/webhook";
|
|
180
|
+
const mcpHttpPath = runtime.config.mcp.httpPath.replace(/\/+$/u, "") || "/mcp";
|
|
181
|
+
const oauthFacade = runtime.config.oauth
|
|
182
|
+
? (0, oauthFacade_1.createOAuthFacade)(runtime.config.oauth, mcpHttpPath, runtime.logger)
|
|
183
|
+
: null;
|
|
184
|
+
if (oauthFacade?.ephemeralSigningKey) {
|
|
185
|
+
runtime.logger.warn("OAuth connector is using an ephemeral signing key; access tokens will stop working after restart");
|
|
186
|
+
}
|
|
156
187
|
const closeSessionEntry = async (entry) => {
|
|
157
188
|
await entry.close();
|
|
158
189
|
};
|
|
@@ -220,6 +251,11 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
220
251
|
socket.on("close", () => {
|
|
221
252
|
void cleanup();
|
|
222
253
|
});
|
|
254
|
+
socket.on("error", (error) => {
|
|
255
|
+
runtime.logger.warn("Telegram WebApp live WS socket error", {
|
|
256
|
+
error: error instanceof Error ? error.message : String(error),
|
|
257
|
+
});
|
|
258
|
+
});
|
|
223
259
|
socket.on("message", (raw) => {
|
|
224
260
|
void (async () => {
|
|
225
261
|
const parsed = raw && typeof raw === "string"
|
|
@@ -288,6 +324,15 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
288
324
|
}
|
|
289
325
|
if (resolved.session?.terminalTarget && (0, client_1.isStreamableTerminalTarget)(resolved.session.terminalTarget)) {
|
|
290
326
|
(0, client_1.resizeForegroundTerminal)(resolved.session.terminalTarget, Math.max(20, Math.min(400, Math.round(cols))), Math.max(5, Math.min(200, Math.round(rows))));
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (resolved.relayTarget) {
|
|
330
|
+
await input.getGatewaySocketService?.()?.requestLiveRelay?.({
|
|
331
|
+
clientUuid: resolved.relayTarget.clientUuid,
|
|
332
|
+
localSessionId: resolved.relayTarget.localSessionId,
|
|
333
|
+
requestType: "resize",
|
|
334
|
+
payload: { cols, rows },
|
|
335
|
+
});
|
|
291
336
|
}
|
|
292
337
|
}
|
|
293
338
|
})().catch((error) => {
|
|
@@ -356,11 +401,39 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
356
401
|
socket.close(1011, "Live stream bootstrap failed");
|
|
357
402
|
});
|
|
358
403
|
});
|
|
359
|
-
const
|
|
404
|
+
const handleRequestInternal = async (req, res, pathname) => {
|
|
360
405
|
const method = req.method ?? "GET";
|
|
361
406
|
const normalizedPathname = normalizePrefixedPathname(pathname);
|
|
362
407
|
const requestUrl = new URL(req.url ?? normalizedPathname, `http://gateway.local`);
|
|
363
408
|
requestUrl.pathname = normalizedPathname;
|
|
409
|
+
if (runtime.temporaryFileLinkStore &&
|
|
410
|
+
(await runtime.temporaryFileLinkStore.handleRequest(req, res, requestUrl.pathname))) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
if (oauthFacade) {
|
|
414
|
+
const authorizationHeader = readHeader(req, "authorization");
|
|
415
|
+
const connectorRequestContext = {
|
|
416
|
+
method,
|
|
417
|
+
rawPath: pathname,
|
|
418
|
+
path: requestUrl.pathname,
|
|
419
|
+
queryParameters: [...requestUrl.searchParams.keys()],
|
|
420
|
+
userAgent: readHeader(req, "user-agent"),
|
|
421
|
+
accept: readHeader(req, "accept"),
|
|
422
|
+
contentType: readHeader(req, "content-type"),
|
|
423
|
+
authorizationHeaderPresent: Boolean(authorizationHeader),
|
|
424
|
+
authorizationScheme: authorizationHeader?.split(/\s+/u, 1)[0],
|
|
425
|
+
mcpSessionId: readHeader(req, "mcp-session-id"),
|
|
426
|
+
remoteAddress: req.socket.remoteAddress,
|
|
427
|
+
};
|
|
428
|
+
runtime.logger.info("OAuth connector HTTP request received", connectorRequestContext);
|
|
429
|
+
res.once("finish", () => {
|
|
430
|
+
runtime.logger.info("OAuth connector HTTP response completed", {
|
|
431
|
+
...connectorRequestContext,
|
|
432
|
+
statusCode: res.statusCode,
|
|
433
|
+
responseContentType: String(res.getHeader("content-type") ?? ""),
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
364
437
|
if (requestUrl.pathname === "/healthz") {
|
|
365
438
|
writeJson(res, 200, {
|
|
366
439
|
ok: true,
|
|
@@ -369,6 +442,10 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
369
442
|
});
|
|
370
443
|
return;
|
|
371
444
|
}
|
|
445
|
+
if (oauthFacade &&
|
|
446
|
+
(await oauthFacade.handleRequest(req, res, requestUrl.pathname))) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
372
449
|
if (runtime.config.telegram.webhook.enabled &&
|
|
373
450
|
requestUrl.pathname === telegramWebhookPath) {
|
|
374
451
|
if (method !== "POST") {
|
|
@@ -457,7 +534,7 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
457
534
|
method,
|
|
458
535
|
path: requestUrl.pathname,
|
|
459
536
|
});
|
|
460
|
-
const body = await readJsonBody(req).catch(
|
|
537
|
+
const body = await readJsonBody(req).catch(ignoreInvalidJsonBody);
|
|
461
538
|
let sessionId = body &&
|
|
462
539
|
typeof body === "object" &&
|
|
463
540
|
typeof Reflect.get(body, "sessionId") === "string"
|
|
@@ -529,7 +606,6 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
529
606
|
session_id: relayBootstrap.session_id,
|
|
530
607
|
session_label: relayBootstrap.session_label,
|
|
531
608
|
terminal_target: relayBootstrap.terminal_target,
|
|
532
|
-
poll_interval_ms: relayBootstrap.poll_interval_ms,
|
|
533
609
|
expires_at: new Date(record.expiresAtMs).toISOString(),
|
|
534
610
|
});
|
|
535
611
|
if (trustedTelegramUserId !== null) {
|
|
@@ -569,17 +645,6 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
569
645
|
}
|
|
570
646
|
try {
|
|
571
647
|
const validated = (0, auth_1.validateTelegramWebAppInitData)(initDataRaw, initDataUnsafe, requireTelegramBotToken(runtime, "validate Telegram WebApp bootstrap"), runtime.config.webapp.initDataTtlSeconds);
|
|
572
|
-
runtime.logger.info("Telegram WebApp initData validation debug", {
|
|
573
|
-
sessionId: sessionId || null,
|
|
574
|
-
telegramUserId: validated.user.id,
|
|
575
|
-
providedHash: validated.validationDebug.providedHash,
|
|
576
|
-
officialRawMatches: validated.validationDebug.officialRaw.matches,
|
|
577
|
-
officialRawCheckString: validated.validationDebug.officialRaw.checkString,
|
|
578
|
-
officialRawComputedHash: validated.validationDebug.officialRaw.computedHash,
|
|
579
|
-
userFieldsMatches: validated.validationDebug.userFields?.matches ?? null,
|
|
580
|
-
userFieldsCheckString: validated.validationDebug.userFields?.checkString ?? null,
|
|
581
|
-
userFieldsComputedHash: validated.validationDebug.userFields?.computedHash ?? null,
|
|
582
|
-
});
|
|
583
648
|
if (!sessionId) {
|
|
584
649
|
sessionId =
|
|
585
650
|
(await runtime.bindingStore.getActiveSessionIdForTelegramUser(validated.user.id)) ?? "";
|
|
@@ -615,7 +680,6 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
615
680
|
session_id: sessionId,
|
|
616
681
|
session_label: session?.label ?? null,
|
|
617
682
|
terminal_target: Boolean(session?.terminalTarget),
|
|
618
|
-
poll_interval_ms: runtime.config.webapp.pollIntervalMs,
|
|
619
683
|
expires_at: new Date(record.expiresAtMs).toISOString(),
|
|
620
684
|
});
|
|
621
685
|
if (launchRecord?.telegramChatId !== undefined &&
|
|
@@ -640,7 +704,6 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
640
704
|
runtime.logger.warn("Telegram WebApp bootstrap rejected", {
|
|
641
705
|
sessionId,
|
|
642
706
|
initDataLength: initDataRaw.length,
|
|
643
|
-
initDataPreview: initDataRaw.slice(0, 160),
|
|
644
707
|
hasUnsafeUser: Boolean(initDataUnsafe?.user) &&
|
|
645
708
|
typeof initDataUnsafe?.user?.id === "number",
|
|
646
709
|
error: error instanceof Error
|
|
@@ -651,74 +714,6 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
651
714
|
}
|
|
652
715
|
return;
|
|
653
716
|
}
|
|
654
|
-
if (requestUrl.pathname === `${webAppBasePath}/api/view`) {
|
|
655
|
-
if (method !== "GET") {
|
|
656
|
-
writeText(res, 405, "Method not allowed");
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
const token = readBearerToken(req);
|
|
660
|
-
const webAppSession = token ? webAppSessions.get(token) : null;
|
|
661
|
-
if (!webAppSession) {
|
|
662
|
-
writeText(res, 401, "Unauthorized");
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
const relayTarget = (0, relay_1.parseLiveRelaySessionId)(webAppSession.sessionId);
|
|
666
|
-
if (relayTarget) {
|
|
667
|
-
try {
|
|
668
|
-
const result = await runtime.gatewayHttpService.requestLiveRelayView({
|
|
669
|
-
clientUuid: relayTarget.clientUuid,
|
|
670
|
-
localSessionId: relayTarget.localSessionId,
|
|
671
|
-
});
|
|
672
|
-
writeJson(res, 200, result);
|
|
673
|
-
}
|
|
674
|
-
catch (error) {
|
|
675
|
-
runtime.logger.error("Telegram WebApp relay visible buffer capture failed", {
|
|
676
|
-
sessionId: webAppSession.sessionId,
|
|
677
|
-
clientUuid: relayTarget.clientUuid,
|
|
678
|
-
localSessionId: relayTarget.localSessionId,
|
|
679
|
-
error: error instanceof Error
|
|
680
|
-
? (error.stack ?? error.message)
|
|
681
|
-
: String(error),
|
|
682
|
-
});
|
|
683
|
-
writeText(res, isRelayTerminalUnavailableMessage(error) ? 503 : 500, error instanceof Error ? error.message : "Failed to capture relay terminal buffer");
|
|
684
|
-
}
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
const binding = await runtime.bindingStore.getBinding(webAppSession.sessionId);
|
|
688
|
-
if (!binding ||
|
|
689
|
-
binding.telegramUserId !== webAppSession.telegramUserId) {
|
|
690
|
-
writeText(res, 403, "Session binding is no longer valid");
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
const session = await runtime.sessionStore.getSession(webAppSession.sessionId);
|
|
694
|
-
if (!session?.terminalTarget) {
|
|
695
|
-
writeText(res, 409, "terminal target is not configured for this session");
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
try {
|
|
699
|
-
const terminalSize = await (0, terminal_1.getTerminalWindowSize)(runtime.config.terminal, session.terminalTarget);
|
|
700
|
-
const content = await (0, terminal_1.captureVisibleTerminal)(runtime.config.terminal, session.terminalTarget, runtime.config.terminal.captureLines, runtime.config.webapp.visibleScreens);
|
|
701
|
-
const ansi = await (0, terminal_1.captureVisibleTerminalAnsi)(runtime.config.terminal, session.terminalTarget, runtime.config.terminal.captureLines, runtime.config.webapp.visibleScreens);
|
|
702
|
-
writeJson(res, 200, {
|
|
703
|
-
session_id: session.sessionId,
|
|
704
|
-
session_label: session.label ?? null,
|
|
705
|
-
captured_at: new Date().toISOString(),
|
|
706
|
-
content,
|
|
707
|
-
ansi,
|
|
708
|
-
...(terminalSize ? terminalSize : {}),
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
catch (error) {
|
|
712
|
-
runtime.logger.error("Telegram WebApp visible buffer capture failed", {
|
|
713
|
-
sessionId: webAppSession.sessionId,
|
|
714
|
-
error: error instanceof Error
|
|
715
|
-
? (error.stack ?? error.message)
|
|
716
|
-
: String(error),
|
|
717
|
-
});
|
|
718
|
-
writeText(res, (0, terminal_1.isTerminalUnavailableError)(error) ? 503 : 500, formatTerminalHttpError(error, "Failed to capture visible terminal buffer"));
|
|
719
|
-
}
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
717
|
if (requestUrl.pathname === `${webAppBasePath}/api/action`) {
|
|
723
718
|
if (method !== "POST") {
|
|
724
719
|
writeText(res, 405, "Method not allowed");
|
|
@@ -730,7 +725,7 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
730
725
|
writeText(res, 401, "Unauthorized");
|
|
731
726
|
return;
|
|
732
727
|
}
|
|
733
|
-
const body = await readJsonBody(req).catch(
|
|
728
|
+
const body = await readJsonBody(req).catch(ignoreInvalidJsonBody);
|
|
734
729
|
const action = body &&
|
|
735
730
|
typeof body === "object" &&
|
|
736
731
|
typeof Reflect.get(body, "action") === "string"
|
|
@@ -921,16 +916,22 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
921
916
|
});
|
|
922
917
|
return;
|
|
923
918
|
}
|
|
924
|
-
if (requestUrl.pathname !==
|
|
919
|
+
if (requestUrl.pathname !== mcpHttpPath &&
|
|
920
|
+
requestUrl.pathname !== `${mcpHttpPath}/`) {
|
|
925
921
|
writeText(res, 404, "Not found");
|
|
926
922
|
return;
|
|
927
923
|
}
|
|
928
|
-
|
|
924
|
+
const authorizationMode = resolveMcpHttpAuthorization(req, runtime.config.mcp.bearerToken, oauthFacade);
|
|
925
|
+
if (!authorizationMode) {
|
|
926
|
+
const authorizationHeader = readHeader(req, "authorization");
|
|
929
927
|
runtime.logger.warn("Unauthorized MCP HTTP request rejected", {
|
|
930
928
|
method,
|
|
931
929
|
path: requestUrl.pathname,
|
|
932
930
|
remoteAddress: req.socket.remoteAddress,
|
|
931
|
+
authorizationHeaderPresent: Boolean(authorizationHeader),
|
|
932
|
+
authorizationScheme: authorizationHeader?.split(/\s+/u, 1)[0],
|
|
933
933
|
});
|
|
934
|
+
oauthFacade?.writeMcpChallenge(res);
|
|
934
935
|
writeText(res, 401, "Unauthorized");
|
|
935
936
|
return;
|
|
936
937
|
}
|
|
@@ -938,12 +939,33 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
938
939
|
const parsedBody = method === "POST" || method === "DELETE"
|
|
939
940
|
? await readJsonBody(req)
|
|
940
941
|
: undefined;
|
|
942
|
+
const rpcMethod = readMcpRpcMethod(parsedBody);
|
|
941
943
|
runtime.logger.debug("MCP HTTP request received", {
|
|
942
944
|
method,
|
|
943
945
|
path: requestUrl.pathname,
|
|
944
946
|
sessionId,
|
|
947
|
+
authorizationMode,
|
|
948
|
+
rpcMethod,
|
|
945
949
|
hasBody: parsedBody !== undefined,
|
|
946
950
|
});
|
|
951
|
+
if (rpcMethod === "initialize" || rpcMethod === "tools/list") {
|
|
952
|
+
runtime.logger.info("Chat connector MCP handshake request received", {
|
|
953
|
+
rpcMethod,
|
|
954
|
+
authorizationMode,
|
|
955
|
+
sessionId,
|
|
956
|
+
userAgent: readHeader(req, "user-agent"),
|
|
957
|
+
});
|
|
958
|
+
res.once("finish", () => {
|
|
959
|
+
runtime.logger.info("Chat connector MCP handshake response completed", {
|
|
960
|
+
rpcMethod,
|
|
961
|
+
authorizationMode,
|
|
962
|
+
statusCode: res.statusCode,
|
|
963
|
+
sessionId: readHeader(req, "mcp-session-id") ??
|
|
964
|
+
(String(res.getHeader("mcp-session-id") ?? "") || undefined),
|
|
965
|
+
contentType: String(res.getHeader("content-type") ?? ""),
|
|
966
|
+
});
|
|
967
|
+
});
|
|
968
|
+
}
|
|
947
969
|
try {
|
|
948
970
|
if (method === "POST") {
|
|
949
971
|
if (sessionId) {
|
|
@@ -1090,6 +1112,24 @@ function createMcpHttpHandler(runtime, input) {
|
|
|
1090
1112
|
}
|
|
1091
1113
|
}
|
|
1092
1114
|
};
|
|
1115
|
+
const handleRequest = async (req, res, pathname) => {
|
|
1116
|
+
try {
|
|
1117
|
+
await handleRequestInternal(req, res, pathname);
|
|
1118
|
+
}
|
|
1119
|
+
catch (error) {
|
|
1120
|
+
if (!(0, bodyLimits_1.isBodySizeLimitError)(error)) {
|
|
1121
|
+
throw error;
|
|
1122
|
+
}
|
|
1123
|
+
if (!res.headersSent) {
|
|
1124
|
+
writeJson(res, 413, {
|
|
1125
|
+
error: `Request body exceeds the ${bodyLimits_1.MAX_BODY_SIZE} MiB limit`,
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
else if (!res.writableEnded) {
|
|
1129
|
+
res.end();
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1093
1133
|
let shuttingDown = false;
|
|
1094
1134
|
const handleUpgrade = async (req, socket, head, pathname) => {
|
|
1095
1135
|
const normalizedPathname = normalizePrefixedPathname(pathname);
|