@deadragdoll/tellymcp 0.0.12 → 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.
Files changed (83) hide show
  1. package/.env.example.client +5 -1
  2. package/.env.example.gateway +16 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README-ru.md +33 -0
  5. package/README.md +33 -0
  6. package/TOOLS.md +384 -9
  7. package/config/templates/env.both.template +15 -2
  8. package/config/templates/env.client.template +5 -2
  9. package/config/templates/env.gateway.template +15 -2
  10. package/dist/cli.js +137 -2
  11. package/dist/services/features/telegram-mcp/browser.service.js +56 -1
  12. package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
  13. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
  14. package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
  15. package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
  16. package/dist/services/features/telegram-mcp/mcp-server.service.js +30 -0
  17. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +23 -1
  18. package/dist/services/features/telegram-mcp/src/app/config/env.js +132 -2
  19. package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
  20. package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
  21. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
  22. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
  23. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +205 -17
  24. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
  25. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
  26. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +1 -1
  27. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
  28. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +1 -1
  29. package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +1 -1
  30. package/dist/services/features/telegram-mcp/src/features/browser/model/browserInjectScriptTool.js +28 -0
  31. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +33 -0
  32. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +33 -0
  33. package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +1 -1
  34. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStartTool.js +28 -0
  35. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStatusTool.js +28 -0
  36. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStopTool.js +28 -0
  37. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
  38. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +890 -23
  39. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +536 -0
  40. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +80 -0
  41. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +736 -0
  42. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +188 -0
  43. package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
  44. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
  45. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +4 -1
  46. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -35
  47. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
  48. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
  49. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
  50. package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
  51. package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
  52. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +5 -1
  53. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +28 -0
  54. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +3 -0
  55. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
  56. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
  57. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
  58. package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
  59. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
  60. package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
  61. package/docs/CHAT_CONNECTOR.md +134 -0
  62. package/docs/STANDALONE-ru.md +55 -6
  63. package/docs/STANDALONE.md +55 -6
  64. package/package.json +10 -5
  65. package/packages/chrome-attach-extension/dist/background.js +1735 -0
  66. package/packages/chrome-attach-extension/dist/icon.svg +6 -0
  67. package/packages/chrome-attach-extension/dist/manifest.json +37 -0
  68. package/packages/chrome-attach-extension/dist/options.html +312 -0
  69. package/packages/chrome-attach-extension/dist/options.js +606 -0
  70. package/packages/chrome-attach-extension/dist/popup.html +93 -0
  71. package/packages/chrome-attach-extension/dist/popup.js +79 -0
  72. package/packages/chrome-attach-extension/dist/recorder-content.js +96 -0
  73. package/packages/chrome-attach-extension/dist/recorder-page.js +282 -0
  74. package/packages/firefox-attach-extension/README.md +13 -0
  75. package/packages/firefox-attach-extension/dist/background.js +1622 -0
  76. package/packages/firefox-attach-extension/dist/icon.svg +6 -0
  77. package/packages/firefox-attach-extension/dist/manifest.json +44 -0
  78. package/packages/firefox-attach-extension/dist/options.html +312 -0
  79. package/packages/firefox-attach-extension/dist/options.js +540 -0
  80. package/packages/firefox-attach-extension/dist/popup.html +93 -0
  81. package/packages/firefox-attach-extension/dist/popup.js +64 -0
  82. package/packages/firefox-attach-extension/dist/recorder-content.js +90 -0
  83. package/packages/firefox-attach-extension/dist/recorder-page.js +318 -0
@@ -54,6 +54,8 @@ exports.resizePtyTarget = resizePtyTarget;
54
54
  exports.subscribePtyTarget = subscribePtyTarget;
55
55
  exports.stopPtyTarget = stopPtyTarget;
56
56
  exports.stopAllPtyTargets = stopAllPtyTargets;
57
+ const node_fs_1 = __importDefault(require("node:fs"));
58
+ const node_os_1 = __importDefault(require("node:os"));
57
59
  const node_path_1 = __importDefault(require("node:path"));
58
60
  const node_pty_1 = require("node-pty");
59
61
  const XtermHeadless = __importStar(require("@xterm/headless"));
@@ -73,6 +75,97 @@ function buildExitLine(record) {
73
75
  }
74
76
  return `${parts.join(" ")}]`;
75
77
  }
78
+ function sanitizeFileNameSegment(value) {
79
+ const sanitized = value.trim().replace(/[^a-zA-Z0-9._-]+/gu, "-");
80
+ return sanitized || "session";
81
+ }
82
+ function shellSingleQuote(value) {
83
+ return `'${value.replace(/'/gu, `'\\''`)}'`;
84
+ }
85
+ function buildContextPromptPrefix(record) {
86
+ return `[TELLY ${record.sessionId}]`;
87
+ }
88
+ function buildContextEnv(record) {
89
+ return {
90
+ ...process.env,
91
+ TELLYMCP_TERMINAL_CONTEXT: "1",
92
+ TELLYMCP_SESSION_ID: record.sessionId,
93
+ TELLYMCP_TERMINAL_TARGET: record.target,
94
+ TELLYMCP_TERMINAL_CWD: record.cwd,
95
+ };
96
+ }
97
+ function prepareBashLaunch(record) {
98
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "tellymcp-pty-bash-"));
99
+ const rcPath = node_path_1.default.join(tempDir, `${sanitizeFileNameSegment(record.sessionId)}.bashrc`);
100
+ const promptPrefix = buildContextPromptPrefix(record);
101
+ const script = [
102
+ 'if [ -r "$HOME/.profile" ]; then . "$HOME/.profile"; fi',
103
+ 'if [ -r "$HOME/.bashrc" ]; then . "$HOME/.bashrc"; fi',
104
+ `export TELLYMCP_TERMINAL_CONTEXT=${shellSingleQuote("1")}`,
105
+ `export TELLYMCP_SESSION_ID=${shellSingleQuote(record.sessionId)}`,
106
+ `export TELLYMCP_TERMINAL_TARGET=${shellSingleQuote(record.target)}`,
107
+ `export TELLYMCP_TERMINAL_CWD=${shellSingleQuote(record.cwd)}`,
108
+ `PS1=${shellSingleQuote(`${promptPrefix} \\u@\\h:\\w\\$ `)}`,
109
+ ].join("\n");
110
+ node_fs_1.default.writeFileSync(rcPath, `${script}\n`, "utf8");
111
+ return {
112
+ args: ["--rcfile", rcPath, "-i"],
113
+ env: buildContextEnv(record),
114
+ tempPaths: [tempDir],
115
+ };
116
+ }
117
+ function prepareZshLaunch(record) {
118
+ const tempDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), "tellymcp-pty-zsh-"));
119
+ const promptPrefix = buildContextPromptPrefix(record);
120
+ node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, ".zshenv"), [
121
+ 'if [[ -r "$HOME/.zshenv" ]]; then source "$HOME/.zshenv"; fi',
122
+ `export TELLYMCP_TERMINAL_CONTEXT=${shellSingleQuote("1")}`,
123
+ `export TELLYMCP_SESSION_ID=${shellSingleQuote(record.sessionId)}`,
124
+ `export TELLYMCP_TERMINAL_TARGET=${shellSingleQuote(record.target)}`,
125
+ `export TELLYMCP_TERMINAL_CWD=${shellSingleQuote(record.cwd)}`,
126
+ "",
127
+ ].join("\n"), "utf8");
128
+ node_fs_1.default.writeFileSync(node_path_1.default.join(tempDir, ".zshrc"), [
129
+ 'if [[ -r "$HOME/.zprofile" ]]; then source "$HOME/.zprofile"; fi',
130
+ 'if [[ -r "$HOME/.zshrc" ]]; then source "$HOME/.zshrc"; fi',
131
+ `PROMPT=${shellSingleQuote(`${promptPrefix} %n@%m:%~%# `)}`,
132
+ "RPROMPT=''",
133
+ "",
134
+ ].join("\n"), "utf8");
135
+ return {
136
+ args: ["-i"],
137
+ env: {
138
+ ...buildContextEnv(record),
139
+ ZDOTDIR: tempDir,
140
+ },
141
+ tempPaths: [tempDir],
142
+ };
143
+ }
144
+ function prepareShellLaunch(record) {
145
+ const shellName = getShellDisplayName(record.shell).toLowerCase();
146
+ if (shellName === "bash") {
147
+ return prepareBashLaunch(record);
148
+ }
149
+ if (shellName === "zsh") {
150
+ return prepareZshLaunch(record);
151
+ }
152
+ return {
153
+ args: [],
154
+ env: buildContextEnv(record),
155
+ tempPaths: [],
156
+ };
157
+ }
158
+ function cleanupRecordTempPaths(record) {
159
+ for (const tempPath of record.tempPaths) {
160
+ try {
161
+ node_fs_1.default.rmSync(tempPath, { recursive: true, force: true });
162
+ }
163
+ catch {
164
+ // ignore cleanup failures
165
+ }
166
+ }
167
+ record.tempPaths = [];
168
+ }
76
169
  function createHeadlessTerminal(config) {
77
170
  return new XtermHeadless.Terminal({
78
171
  cols: config.cols,
@@ -97,13 +190,16 @@ function createSessionRecord(config, input) {
97
190
  pendingWrite: Promise.resolve(),
98
191
  pty: null,
99
192
  exited: false,
193
+ tempPaths: [],
100
194
  };
101
- const pty = (0, node_pty_1.spawn)(record.shell, [], {
195
+ const launch = prepareShellLaunch(record);
196
+ record.tempPaths = [...launch.tempPaths];
197
+ const pty = (0, node_pty_1.spawn)(record.shell, launch.args, {
102
198
  name: "xterm-color",
103
199
  cols: record.cols,
104
200
  rows: record.rows,
105
201
  cwd: record.cwd,
106
- env: process.env,
202
+ env: launch.env,
107
203
  });
108
204
  pty.onData((data) => {
109
205
  record.pendingWrite = record.pendingWrite.then(() => new Promise((resolve) => {
@@ -115,6 +211,7 @@ function createSessionRecord(config, input) {
115
211
  record.exited = true;
116
212
  record.exitCode = exitCode;
117
213
  record.signal = signal;
214
+ cleanupRecordTempPaths(record);
118
215
  record.pendingWrite = record.pendingWrite.then(() => new Promise((resolve) => {
119
216
  record.terminal.writeln(buildExitLine(record), resolve);
120
217
  }));
@@ -533,6 +630,7 @@ function stopPtyTarget(target) {
533
630
  record.pty?.kill();
534
631
  record.pty = null;
535
632
  record.exited = true;
633
+ cleanupRecordTempPaths(record);
536
634
  record.terminal.dispose();
537
635
  return true;
538
636
  }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readLimitedJsonBody = exports.assertSerializedBodySize = exports.assertStringBodySize = exports.assertBodySize = exports.isBodySizeLimitError = exports.BodySizeLimitError = exports.MAX_BASE64_SOURCE_SIZE_BYTES = exports.MAX_BODY_SIZE_BYTES = exports.MAX_BODY_SIZE = void 0;
4
+ exports.MAX_BODY_SIZE = 16;
5
+ exports.MAX_BODY_SIZE_BYTES = exports.MAX_BODY_SIZE * 1024 * 1024;
6
+ exports.MAX_BASE64_SOURCE_SIZE_BYTES = Math.floor((exports.MAX_BODY_SIZE_BYTES * 3) / 4);
7
+ class BodySizeLimitError extends Error {
8
+ actualBytes;
9
+ maxBytes;
10
+ statusCode = 413;
11
+ constructor(actualBytes, maxBytes = exports.MAX_BODY_SIZE_BYTES) {
12
+ const maxMiB = maxBytes / (1024 * 1024);
13
+ super(`Body size ${actualBytes} bytes exceeds the ${maxMiB} MiB limit.`);
14
+ this.actualBytes = actualBytes;
15
+ this.maxBytes = maxBytes;
16
+ this.name = "BodySizeLimitError";
17
+ }
18
+ }
19
+ exports.BodySizeLimitError = BodySizeLimitError;
20
+ const isBodySizeLimitError = (error) => error instanceof BodySizeLimitError;
21
+ exports.isBodySizeLimitError = isBodySizeLimitError;
22
+ const assertBodySize = (actualBytes, maxBytes = exports.MAX_BODY_SIZE_BYTES) => {
23
+ if (actualBytes > maxBytes) {
24
+ throw new BodySizeLimitError(actualBytes, maxBytes);
25
+ }
26
+ };
27
+ exports.assertBodySize = assertBodySize;
28
+ const assertStringBodySize = (value, maxBytes = exports.MAX_BODY_SIZE_BYTES) => {
29
+ (0, exports.assertBodySize)(Buffer.byteLength(value, "utf8"), maxBytes);
30
+ };
31
+ exports.assertStringBodySize = assertStringBodySize;
32
+ const assertSerializedBodySize = (value) => {
33
+ if (value === undefined) {
34
+ return;
35
+ }
36
+ (0, exports.assertStringBodySize)(JSON.stringify(value));
37
+ };
38
+ exports.assertSerializedBodySize = assertSerializedBodySize;
39
+ const readLimitedJsonBody = async (req) => {
40
+ const contentLength = req.headers["content-length"];
41
+ if (typeof contentLength === "string" && /^\d+$/u.test(contentLength)) {
42
+ (0, exports.assertBodySize)(Number(contentLength));
43
+ }
44
+ const chunks = [];
45
+ let totalBytes = 0;
46
+ for await (const chunk of req) {
47
+ const buffer = Buffer.isBuffer(chunk)
48
+ ? chunk
49
+ : Buffer.from(chunk);
50
+ totalBytes += buffer.byteLength;
51
+ (0, exports.assertBodySize)(totalBytes);
52
+ chunks.push(buffer);
53
+ }
54
+ if (chunks.length === 0) {
55
+ return undefined;
56
+ }
57
+ const raw = Buffer.concat(chunks, totalBytes).toString("utf8").trim();
58
+ if (!raw) {
59
+ return undefined;
60
+ }
61
+ return JSON.parse(raw);
62
+ };
63
+ exports.readLimitedJsonBody = readLimitedJsonBody;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isGatewayAuthorizationValid = isGatewayAuthorizationValid;
4
+ const node_crypto_1 = require("node:crypto");
5
+ function digest(value) {
6
+ return (0, node_crypto_1.createHash)("sha256").update(value, "utf8").digest();
7
+ }
8
+ function isGatewayAuthorizationValid(authorization, authToken) {
9
+ if (typeof authorization !== "string" || !authToken) {
10
+ return false;
11
+ }
12
+ return (0, node_crypto_1.timingSafeEqual)(digest(authorization), digest(`Bearer ${authToken}`));
13
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatLocalTimestamp = formatLocalTimestamp;
4
+ exports.formatLocalDateSegment = formatLocalDateSegment;
5
+ exports.formatLocalTimeSegment = formatLocalTimeSegment;
6
+ exports.formatLocalTimestampForFileName = formatLocalTimestampForFileName;
7
+ function padNumber(value, length = 2) {
8
+ return String(value).padStart(length, "0");
9
+ }
10
+ function formatLocalTimestamp(date = new Date()) {
11
+ return `${date.getFullYear()}-${padNumber(date.getMonth() + 1)}-${padNumber(date.getDate())}T${padNumber(date.getHours())}:${padNumber(date.getMinutes())}:${padNumber(date.getSeconds())}.${padNumber(date.getMilliseconds(), 3)}`;
12
+ }
13
+ function formatLocalDateSegment(date = new Date()) {
14
+ return `${date.getFullYear()}-${padNumber(date.getMonth() + 1)}-${padNumber(date.getDate())}`;
15
+ }
16
+ function formatLocalTimeSegment(date = new Date()) {
17
+ return `${padNumber(date.getHours())}-${padNumber(date.getMinutes())}-${padNumber(date.getSeconds())}`;
18
+ }
19
+ function formatLocalTimestampForFileName(date = new Date()) {
20
+ return formatLocalTimestamp(date).replace(/[:.]/gu, "-");
21
+ }
@@ -0,0 +1,134 @@
1
+ # ChatGPT and Claude connector
2
+
3
+ TellyMCP can expose its Streamable HTTP MCP endpoint to ChatGPT and Claude
4
+ through an OAuth 2.0 authorization-code facade with PKCE. The facade coexists
5
+ with `MCP_HTTP_BEARER_TOKEN`; existing internal clients can continue to use the
6
+ static bearer token.
7
+
8
+ ## Configure the gateway
9
+
10
+ Add the following to the gateway environment file:
11
+
12
+ ```bash
13
+ TELLYMCP_PUBLIC_URL=https://mcp.example.com/api
14
+ TELLYMCP_OAUTH_ISSUER=https://mcp.example.com/api
15
+ TELLYMCP_OAUTH_AUDIENCE=https://mcp.example.com/api
16
+ TELLYMCP_MAGIC_TOKEN=replace_with_a_private_human_login_token
17
+ TELLYMCP_OAUTH_PRIVATE_KEY_PEM="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
18
+ ```
19
+
20
+ Generate the stable signing key with:
21
+
22
+ ```bash
23
+ tellymcp oauth key
24
+ ```
25
+
26
+ The command prints a dotenv-ready `TELLYMCP_OAUTH_PRIVATE_KEY_PEM` assignment.
27
+ If the private key is omitted, TellyMCP generates an ephemeral key and existing
28
+ OAuth access tokens stop working after a restart.
29
+
30
+ For a hashed magic token, configure
31
+ `TELLYMCP_MAGIC_TOKEN_HASH=sha256:<64-hex-digest>` instead of the raw token.
32
+ Never configure both forms.
33
+
34
+ Optional restrictions:
35
+
36
+ ```bash
37
+ TELLYMCP_OAUTH_CLIENT_ID=tellymcp
38
+ TELLYMCP_OAUTH_CLIENT_SECRET=replace_with_client_secret
39
+ TELLYMCP_ALLOWED_REDIRECT_URIS=https://chatgpt.com/connector/oauth/...,https://claude.ai/api/mcp/auth_callback
40
+ TELLYMCP_AUTH_CODE_TTL_SECONDS=300
41
+ TELLYMCP_OAUTH_SCOPES=tellymcp:read tellymcp:write
42
+ TELLYMCP_OAUTH_KEY_ID=tellymcp-oauth
43
+ ```
44
+
45
+ OAuth is enabled when a connector identity, credential, redirect, or signing-key
46
+ setting is present. `TELLYMCP_PUBLIC_URL` and one magic-token form are then
47
+ required. Redirect URIs are matched exactly when the allowlist is configured.
48
+
49
+ ## Reverse proxy
50
+
51
+ Proxy all `/api/` routes to TellyMCP and disable response buffering on
52
+ `/api/mcp`. Claude also needs root path-insertion discovery routes such as:
53
+
54
+ ```text
55
+ /.well-known/oauth-authorization-server/api
56
+ /.well-known/openid-configuration/api
57
+ /.well-known/oauth-protected-resource/api/mcp
58
+ ```
59
+
60
+ The complete Nginx example is in `docs/tellymcp.gw.conf`.
61
+
62
+ ## Connect the chat host
63
+
64
+ Use the concrete MCP endpoint in the host's `Connection` field:
65
+
66
+ ```text
67
+ https://mcp.example.com/api/mcp
68
+ ```
69
+
70
+ Do not enter `https://mcp.example.com/api` as the connection URL. That URL is
71
+ the OAuth issuer/audience base, not an MCP endpoint. With the correct
72
+ connection URL, discovery may legitimately show two related resources:
73
+
74
+ ```text
75
+ OAuth issuer/audience: https://mcp.example.com/api
76
+ MCP resource: https://mcp.example.com/api/mcp
77
+ ```
78
+
79
+ For the production deployment used while implementing this feature, the
80
+ working ChatGPT values are:
81
+
82
+ ```text
83
+ Connection: https://drd.undoo.ru/api/mcp
84
+ Authorization server base: https://drd.undoo.ru/api
85
+ Resource: https://drd.undoo.ru/api/mcp
86
+ OIDC configuration URL: https://drd.undoo.ru/.well-known/openid-configuration/api
87
+ ```
88
+
89
+ The `/api` suffix in the root discovery URL is expected RFC 8414
90
+ path-insertion behavior for an issuer whose path is `/api`.
91
+
92
+ Do not give the host `MCP_HTTP_BEARER_TOKEN` or the magic token. The magic token
93
+ is entered only into the authorization page opened by the host. If a client ID
94
+ or client secret is configured in the host, it must match the gateway values.
95
+
96
+ TellyMCP tool names already use Claude-compatible letters, digits, and
97
+ underscores, and the tools publish output schemas.
98
+
99
+ ## Verify before connecting
100
+
101
+ ```bash
102
+ curl -fsS https://mcp.example.com/api/.well-known/oauth-protected-resource/mcp
103
+ curl -fsS https://mcp.example.com/api/.well-known/oauth-authorization-server
104
+ curl -fsS https://mcp.example.com/.well-known/oauth-authorization-server/api
105
+ curl -fsS https://mcp.example.com/api/.well-known/jwks.json
106
+ curl -i https://mcp.example.com/api/mcp
107
+ ```
108
+
109
+ The last response must be `401` and include a `WWW-Authenticate` header whose
110
+ `resource_metadata` value points to the protected-resource metadata endpoint.
111
+
112
+ Access tokens are RS256 JWTs without `exp`, matching the trusted internal
113
+ connector model. Authorization codes remain short-lived, PKCE-bound, and
114
+ one-time-use. Failed magic-token attempts are rate-limited per source address.
115
+
116
+ ## Diagnostics
117
+
118
+ OAuth discovery, authorization, token exchange, MCP `initialize`, and
119
+ `tools/list` milestones are logged at `info`. Rejections are logged at `warn`
120
+ without credentials, authorization codes, PKCE verifiers, or access tokens.
121
+ When OAuth is enabled, every HTTP request and completed response is also logged
122
+ with its normalized path, status, user agent, content types, MCP session id,
123
+ and whether an Authorization header was present. Header values and tokens are
124
+ never logged.
125
+
126
+ The production Nginx sample writes connector traffic to:
127
+
128
+ ```text
129
+ /var/log/nginx/tellymcp.access.log
130
+ /var/log/nginx/tellymcp.error.log
131
+ ```
132
+
133
+ If refresh stops after OAuth metadata and never logs an MCP `initialize`, first
134
+ verify that the host's `Connection` value ends in `/api/mcp`.
@@ -31,6 +31,8 @@ npm install -g @deadragdoll/tellymcp
31
31
 
32
32
  ```bash
33
33
  tellymcp browser install
34
+ tellymcp extension firefox
35
+ tellymcp extension chrome
34
36
  tellymcp codex-plugin install
35
37
  ```
36
38
 
@@ -63,6 +65,7 @@ DB_NAME=
63
65
  GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
64
66
  GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
65
67
  GATEWAY_TOKEN=change_me_gateway_token
68
+ GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
66
69
  ROOT_PREFIX=/api
67
70
  PORT=8080
68
71
  DISTRIBUTED_MODE=gateway
@@ -81,9 +84,15 @@ DISTRIBUTED_MODE=client
81
84
  GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
82
85
  GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
83
86
  GATEWAY_TOKEN=change_me_gateway_token
87
+ GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
84
88
  GATEWAY_USER_UUID=put_owner_uuid_here
85
89
  ```
86
90
 
91
+ Используйте один и тот же стойкий `GATEWAY_AUTH_TOKEN` на gateway и всех клиентах.
92
+ Не смешивайте его с `GATEWAY_TOKEN`: последний разделяет данные gateway по scope,
93
+ но не аутентифицирует HTTP- или WebSocket-транспорт. Сгенерируйте токен один раз,
94
+ например командой `openssl rand -hex 32`, и не используйте пример значения выше.
95
+
87
96
  Для первого запуска также желательно задать:
88
97
 
89
98
  ```env
@@ -91,7 +100,41 @@ TELLYMCP_SESSION_ID=NEW
91
100
  TELLYMCP_SESSION_LABEL=NEW
92
101
  ```
93
102
 
94
- ## 5. Запуск
103
+ ## 5. Attach extensions для браузера
104
+
105
+ Если нужно attach'иться к уже открытому Firefox или Chrome на агентской машине, включи локальный attach bridge в env этого агента:
106
+
107
+ ```env
108
+ BROWSER_ATTACH_ENABLED=true
109
+ BROWSER_ATTACH_WS_HOST=127.0.0.1
110
+ BROWSER_ATTACH_WS_PORT=9999
111
+ BROWSER_ATTACH_WS_PATH=/browser-attach/ws
112
+ ```
113
+
114
+ Выгрузи unpacked extension bundle из установленного пакета:
115
+
116
+ ```bash
117
+ tellymcp extension firefox
118
+ tellymcp extension chrome
119
+ ```
120
+
121
+ Команда создаст один из каталогов:
122
+
123
+ - `./tellymcp-firefox-attach`
124
+ - `./tellymcp-chrome-attach`
125
+
126
+ Дальше загрузи его в локальный браузер:
127
+
128
+ - Firefox: `about:debugging#/runtime/this-firefox` -> `Load Temporary Add-on` -> выбрать `manifest.json`
129
+ - Chrome: `chrome://extensions` -> включить Developer mode -> `Load unpacked` -> выбрать выгруженный каталог
130
+
131
+ После этого browser control panel умеет:
132
+
133
+ - attach'ить текущую agent-сессию к живой вкладке
134
+ - запускать и останавливать structured recording bundles в `.mcp-xchange/web/...`
135
+ - инжектить helper scripts в attached tab
136
+
137
+ ## 6. Запуск
95
138
 
96
139
  Gateway:
97
140
 
@@ -111,7 +154,7 @@ tellymcp run --env .env -s NEW
111
154
  tellymcp run
112
155
  ```
113
156
 
114
- ## 6. Webhook
157
+ ## 7. Webhook
115
158
 
116
159
  Gateway поддерживает polling и webhook.
117
160
 
@@ -128,10 +171,16 @@ TELEGRAM_WEBHOOK_SECRET=change_me_webhook_secret
128
171
 
129
172
  - `/api/telegram/webhook`
130
173
  - `/api/gateway`
174
+ - `/api/files` для короткоживущих upload/download запросов `get_file(type="url")`
131
175
  - `/api/webapp`
132
176
  - `/api/healthz`
133
177
 
134
- ## 7. MCP
178
+ Отдельный `location ^~ /api/files/` рекомендуется, хотя для самой маршрутизации
179
+ он не обязателен. В нём следует отключить access log, потому что путь содержит
180
+ токен, задать `client_max_body_size 32m` и отключить buffering запросов и
181
+ ответов. Канонический блок приведён в `nginx/tellymcp.gw.conf`.
182
+
183
+ ## 8. MCP
135
184
 
136
185
  Локальный client-mode MCP endpoint:
137
186
 
@@ -141,7 +190,7 @@ http://127.0.0.1:8787/mcp
141
190
 
142
191
  Используй MCP HTTP endpoint, который поднимает `tellymcp run`.
143
192
 
144
- ## 8. Проверки
193
+ ## 9. Проверки
145
194
 
146
195
  ```bash
147
196
  tellymcp doctor --env .env
@@ -153,7 +202,7 @@ tellymcp doctor --env .env
153
202
  tellymcp system-prune --env .env --yes
154
203
  ```
155
204
 
156
- ## 9. Операционные заметки
205
+ ## 10. Операционные заметки
157
206
 
158
207
  - gateway-бот — это основной user-facing control plane
159
208
  - консоли видны из gateway live registry
@@ -162,7 +211,7 @@ tellymcp system-prune --env .env --yes
162
211
  - browser screenshot для человека лучше отправлять через `browser_screenshot(send_to_telegram=true)`
163
212
  - файлы между консолями лучше отправлять через `send_partner_file`
164
213
 
165
- ## 10. Чего не делать в новых setup
214
+ ## 11. Чего не делать в новых setup
166
215
 
167
216
  Не строй новые setup вокруг:
168
217
 
@@ -31,6 +31,8 @@ Optional:
31
31
 
32
32
  ```bash
33
33
  tellymcp browser install
34
+ tellymcp extension firefox
35
+ tellymcp extension chrome
34
36
  tellymcp codex-plugin install
35
37
  ```
36
38
 
@@ -63,6 +65,7 @@ DB_NAME=
63
65
  GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
64
66
  GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
65
67
  GATEWAY_TOKEN=change_me_gateway_token
68
+ GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
66
69
  ROOT_PREFIX=/api
67
70
  PORT=8080
68
71
  DISTRIBUTED_MODE=gateway
@@ -81,9 +84,15 @@ DISTRIBUTED_MODE=client
81
84
  GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
82
85
  GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
83
86
  GATEWAY_TOKEN=change_me_gateway_token
87
+ GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
84
88
  GATEWAY_USER_UUID=put_owner_uuid_here
85
89
  ```
86
90
 
91
+ Use the same strong `GATEWAY_AUTH_TOKEN` on the gateway and every client. Keep it
92
+ separate from `GATEWAY_TOKEN`, which scopes gateway data but does not authenticate
93
+ the HTTP or WebSocket transport. Generate it once, for example with
94
+ `openssl rand -hex 32`, and do not use the illustrative value above.
95
+
87
96
  For the first run, also set:
88
97
 
89
98
  ```env
@@ -91,7 +100,41 @@ TELLYMCP_SESSION_ID=NEW
91
100
  TELLYMCP_SESSION_LABEL=NEW
92
101
  ```
93
102
 
94
- ## 5. Run
103
+ ## 5. Attached Browser Extensions
104
+
105
+ If you want to attach TellyMCP to an already running Firefox or Chrome tab on an agent machine, enable the local attach bridge in that agent env:
106
+
107
+ ```env
108
+ BROWSER_ATTACH_ENABLED=true
109
+ BROWSER_ATTACH_WS_HOST=127.0.0.1
110
+ BROWSER_ATTACH_WS_PORT=9999
111
+ BROWSER_ATTACH_WS_PATH=/browser-attach/ws
112
+ ```
113
+
114
+ Export the unpacked extension bundle from the installed package:
115
+
116
+ ```bash
117
+ tellymcp extension firefox
118
+ tellymcp extension chrome
119
+ ```
120
+
121
+ This creates one of:
122
+
123
+ - `./tellymcp-firefox-attach`
124
+ - `./tellymcp-chrome-attach`
125
+
126
+ Load it into the local browser:
127
+
128
+ - Firefox: `about:debugging#/runtime/this-firefox` -> `Load Temporary Add-on` -> choose `manifest.json`
129
+ - Chrome: `chrome://extensions` -> enable Developer mode -> `Load unpacked` -> choose the exported directory
130
+
131
+ After that the browser control panel can:
132
+
133
+ - attach the current agent session to a live browser tab
134
+ - start and stop structured recording bundles in `.mcp-xchange/web/...`
135
+ - inject helper scripts into the attached tab
136
+
137
+ ## 6. Run
95
138
 
96
139
  Gateway:
97
140
 
@@ -111,7 +154,7 @@ After `.mcpsession.json` is created in the workspace, later runs can usually use
111
154
  tellymcp run
112
155
  ```
113
156
 
114
- ## 6. Webhook
157
+ ## 7. Webhook
115
158
 
116
159
  Gateway supports polling and webhook.
117
160
 
@@ -128,10 +171,16 @@ If nginx already proxies `location /api/ { ... }` to the standalone listener, th
128
171
 
129
172
  - `/api/telegram/webhook`
130
173
  - `/api/gateway`
174
+ - `/api/files` for short-lived `get_file(type="url")` uploads and downloads
131
175
  - `/api/webapp`
132
176
  - `/api/healthz`
133
177
 
134
- ## 7. MCP
178
+ A dedicated `location ^~ /api/files/` is recommended, though not required for
179
+ routing. It should disable access logs because the path contains a token, set
180
+ `client_max_body_size 32m`, and disable proxy request/response buffering. See
181
+ `nginx/tellymcp.gw.conf` for the canonical block.
182
+
183
+ ## 8. MCP
135
184
 
136
185
  Local client-mode MCP:
137
186
 
@@ -141,7 +190,7 @@ http://127.0.0.1:8787/mcp
141
190
 
142
191
  Use the MCP HTTP endpoint exposed by `tellymcp run`.
143
192
 
144
- ## 8. Health Checks
193
+ ## 9. Health Checks
145
194
 
146
195
  ```bash
147
196
  tellymcp doctor --env .env
@@ -153,7 +202,7 @@ Destructive cleanup:
153
202
  tellymcp system-prune --env .env --yes
154
203
  ```
155
204
 
156
- ## 9. Operational Notes
205
+ ## 10. Operational Notes
157
206
 
158
207
  - the gateway bot is the user-facing control plane
159
208
  - consoles are discovered from the gateway live registry
@@ -162,7 +211,7 @@ tellymcp system-prune --env .env --yes
162
211
  - browser screenshot replies to humans should use `browser_screenshot(send_to_telegram=true)`
163
212
  - file results between consoles should use `send_partner_file`
164
213
 
165
- ## 10. Legacy Concepts To Avoid
214
+ ## 11. Legacy Concepts To Avoid
166
215
 
167
216
  Do not build new setups around:
168
217
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deadragdoll/tellymcp",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "TellyMCP - Telegram control plane for MCP-connected coding agents",
5
5
  "main": "dist/services/features/telegram-mcp/runtime.service.js",
6
6
  "bin": {
@@ -8,6 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
+ "packages/firefox-attach-extension/dist",
12
+ "packages/chrome-attach-extension/dist",
11
13
  "scripts",
12
14
  "config/templates",
13
15
  "config/codex",
@@ -15,6 +17,7 @@
15
17
  "README-ru.md",
16
18
  "docs/STANDALONE.md",
17
19
  "docs/STANDALONE-ru.md",
20
+ "docs/CHAT_CONNECTOR.md",
18
21
  "VERSION.md",
19
22
  "CHANGELOG.md",
20
23
  "TOOLS.md",
@@ -36,8 +39,9 @@
36
39
  },
37
40
  "scripts": {
38
41
  "postinstall": "node ./scripts/postinstall.js",
39
- "prepack": "yarn build",
40
- "prepublishOnly": "yarn build && yarn lint && yarn test",
42
+ "build:extensions": "node ./packages/firefox-attach-extension/scripts/build.mjs && node ./packages/chrome-attach-extension/scripts/build.mjs",
43
+ "prepack": "yarn build && yarn build:extensions",
44
+ "prepublishOnly": "yarn build && yarn build:extensions && yarn lint && yarn test",
41
45
  "mcp:local": "codex mcp add telegramHuman --url http://127.0.0.1:8787/mcp",
42
46
  "dev:gw": "TS_NODE_TRANSPILE_ONLY=1 TS_NODE_EXPERIMENTAL_RESOLVER=1 NODE_OPTIONS=--openssl-legacy-provider ts-node ./node_modules/moleculer/bin/moleculer-runner.js src/services --config src/moleculer.config.ts --envfile /opt/tellymcp-gateway/.env-gateway --hot --mask \"**/*.service.ts\"",
43
47
  "dev:builder": "TS_NODE_TRANSPILE_ONLY=1 TS_NODE_EXPERIMENTAL_RESOLVER=1 NODE_OPTIONS=--openssl-legacy-provider ts-node ./node_modules/moleculer/bin/moleculer-runner.js src/services --config src/moleculer.config.ts --envfile ./.env-builder --hot --mask \"**/*.service.ts\"",
@@ -73,7 +77,8 @@
73
77
  "dependencies": {
74
78
  "@grammyjs/menu": "^1.3.1",
75
79
  "@grammyjs/storage-redis": "^2.5.1",
76
- "@modelcontextprotocol/sdk": "^1.18.1",
80
+ "@modelcontextprotocol/sdk": "^1.29.0",
81
+ "@xterm/addon-fit": "0.10.0",
77
82
  "@xterm/headless": "5.5.0",
78
83
  "@xterm/xterm": "5.5.0",
79
84
  "amqplib": "0.10.9",
@@ -93,7 +98,7 @@
93
98
  "pino-pretty": "^13.1.3",
94
99
  "playwright": "^1.60.0",
95
100
  "socks-proxy-agent": "^8.0.5",
96
- "ws": "8.20.1",
101
+ "ws": "8.21.0",
97
102
  "zod": "4.4.3"
98
103
  },
99
104
  "devDependencies": {