@gonzih/meatbag-mcp 1.0.0 → 1.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.
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * meatbag-mcp — thin MCP client
4
+ *
5
+ * Exposes one MCP tool: request_human_input
6
+ * Delegates all work to meatbag-service running on localhost:7702.
7
+ * No Telegram connection — zero Telegram-related dependencies.
8
+ *
9
+ * Usage:
10
+ * MEATBAG_SERVICE_URL=http://localhost:7702 npx meatbag-mcp
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
package/dist/mcp.js ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * meatbag-mcp — thin MCP client
5
+ *
6
+ * Exposes one MCP tool: request_human_input
7
+ * Delegates all work to meatbag-service running on localhost:7702.
8
+ * No Telegram connection — zero Telegram-related dependencies.
9
+ *
10
+ * Usage:
11
+ * MEATBAG_SERVICE_URL=http://localhost:7702 npx meatbag-mcp
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
15
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
16
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
17
+ // ── Config ──────────────────────────────────────────────────────────────────
18
+ const SERVICE_URL = process.env.MEATBAG_SERVICE_URL ?? "http://localhost:7702";
19
+ const POLL_INTERVAL_MS = 2_000;
20
+ const MAX_WAIT_MS = 5 * 60 * 1_000; // 5 minutes total
21
+ // ── Service client ───────────────────────────────────────────────────────────
22
+ async function postRequest(question, image_path) {
23
+ let res;
24
+ try {
25
+ res = await fetch(`${SERVICE_URL}/request`, {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/json" },
28
+ body: JSON.stringify({ question, image_path }),
29
+ signal: AbortSignal.timeout(10_000),
30
+ });
31
+ }
32
+ catch (err) {
33
+ const msg = err instanceof Error ? err.message : String(err);
34
+ throw new Error(`meatbag-service is not running on ${SERVICE_URL}: ${msg}\n` +
35
+ `Start it with: MEATBAG_BOT_TOKEN=<token> MEATBAG_CHAT_ID=<id> meatbag-service`);
36
+ }
37
+ if (!res.ok) {
38
+ const body = await res.text();
39
+ throw new Error(`POST ${SERVICE_URL}/request failed (${res.status}): ${body}`);
40
+ }
41
+ const data = (await res.json());
42
+ if (!data.request_id) {
43
+ throw new Error(`Service returned no request_id: ${JSON.stringify(data)}`);
44
+ }
45
+ return data.request_id;
46
+ }
47
+ async function pollResponse(requestId) {
48
+ const deadline = Date.now() + MAX_WAIT_MS;
49
+ while (Date.now() < deadline) {
50
+ let res;
51
+ try {
52
+ // GET /response/:id long-polls for up to 30s on the server side
53
+ res = await fetch(`${SERVICE_URL}/response/${requestId}`, {
54
+ signal: AbortSignal.timeout(35_000), // 30s server long-poll + 5s buffer
55
+ });
56
+ }
57
+ catch (err) {
58
+ const msg = err instanceof Error ? err.message : String(err);
59
+ throw new Error(`GET ${SERVICE_URL}/response/${requestId} failed: ${msg}`);
60
+ }
61
+ if (!res.ok) {
62
+ const body = await res.text();
63
+ throw new Error(`GET /response/${requestId} returned ${res.status}: ${body}`);
64
+ }
65
+ const data = (await res.json());
66
+ if (data.answer !== undefined) {
67
+ return data.answer;
68
+ }
69
+ // Server timed out with no answer — wait briefly and retry
70
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
71
+ }
72
+ throw new Error("Timed out waiting for human response (5 minutes)");
73
+ }
74
+ async function requestHumanInput(question, image_path) {
75
+ const requestId = await postRequest(question, image_path);
76
+ return pollResponse(requestId);
77
+ }
78
+ // ── MCP Server ────────────────────────────────────────────────────────────────
79
+ const server = new index_js_1.Server({ name: "meatbag-mcp", version: "1.1.0" }, { capabilities: { tools: {} } });
80
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
81
+ tools: [
82
+ {
83
+ name: "request_human_input",
84
+ description: "Send a message to the human operator via Telegram and wait for their reply. " +
85
+ "Use this when you need a human decision, approval, captcha solution, or free-text input. " +
86
+ "Requires meatbag-service to be running on localhost:7702.",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ question: {
91
+ type: "string",
92
+ description: "The question or prompt to send to the human operator.",
93
+ },
94
+ image_path: {
95
+ type: "string",
96
+ description: "Optional absolute path to an image file to send along with the question.",
97
+ },
98
+ },
99
+ required: ["question"],
100
+ },
101
+ },
102
+ ],
103
+ }));
104
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
105
+ if (request.params.name !== "request_human_input") {
106
+ return {
107
+ content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
108
+ isError: true,
109
+ };
110
+ }
111
+ const args = request.params.arguments;
112
+ if (!args.question || typeof args.question !== "string") {
113
+ return {
114
+ content: [{ type: "text", text: "question (string) argument is required" }],
115
+ isError: true,
116
+ };
117
+ }
118
+ const image_path = typeof args.image_path === "string" ? args.image_path : undefined;
119
+ try {
120
+ const answer = await requestHumanInput(args.question, image_path);
121
+ return {
122
+ content: [{ type: "text", text: answer }],
123
+ };
124
+ }
125
+ catch (err) {
126
+ const msg = err instanceof Error ? err.message : String(err);
127
+ return {
128
+ content: [{ type: "text", text: `Error: ${msg}` }],
129
+ isError: true,
130
+ };
131
+ }
132
+ });
133
+ // ── Start ─────────────────────────────────────────────────────────────────────
134
+ async function main() {
135
+ const transport = new stdio_js_1.StdioServerTransport();
136
+ await server.connect(transport);
137
+ process.stderr.write(`[meatbag-mcp] Server running on stdio (service: ${SERVICE_URL})\n`);
138
+ }
139
+ main().catch((err) => {
140
+ process.stderr.write(`[meatbag-mcp] Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
141
+ process.exit(1);
142
+ });
143
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * meatbag-service — central Telegram bot + HTTP API
4
+ *
5
+ * Runs as a persistent daemon. Owns the single Telegram bot connection.
6
+ * All meatbag-mcp instances delegate to this service via HTTP on localhost:7702.
7
+ *
8
+ * API:
9
+ * POST /request { question, image_path?, context? } → { request_id }
10
+ * GET /response/:id long-polls (≤30s) until answer ready → { answer } or {}
11
+ * GET /health → { status: "ok" }
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * meatbag-service — central Telegram bot + HTTP API
5
+ *
6
+ * Runs as a persistent daemon. Owns the single Telegram bot connection.
7
+ * All meatbag-mcp instances delegate to this service via HTTP on localhost:7702.
8
+ *
9
+ * API:
10
+ * POST /request { question, image_path?, context? } → { request_id }
11
+ * GET /response/:id long-polls (≤30s) until answer ready → { answer } or {}
12
+ * GET /health → { status: "ok" }
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const http_1 = require("http");
16
+ const promises_1 = require("fs/promises");
17
+ const crypto_1 = require("crypto");
18
+ const path_1 = require("path");
19
+ // ── Config ──────────────────────────────────────────────────────────────────
20
+ const BOT_TOKEN = process.env.MEATBAG_BOT_TOKEN;
21
+ const CHAT_ID = process.env.MEATBAG_CHAT_ID;
22
+ const PORT = parseInt(process.env.MEATBAG_SERVICE_PORT ?? "7702", 10);
23
+ if (!BOT_TOKEN) {
24
+ process.stderr.write("[meatbag-service] MEATBAG_BOT_TOKEN env var is required\n");
25
+ process.exit(1);
26
+ }
27
+ if (!CHAT_ID) {
28
+ process.stderr.write("[meatbag-service] MEATBAG_CHAT_ID env var is required\n");
29
+ process.exit(1);
30
+ }
31
+ const TG_API = `https://api.telegram.org/bot${BOT_TOKEN}`;
32
+ // ── Telegram helpers (native fetch) ──────────────────────────────────────────
33
+ async function tgSendMessage(text) {
34
+ const res = await fetch(`${TG_API}/sendMessage`, {
35
+ method: "POST",
36
+ headers: { "Content-Type": "application/json" },
37
+ body: JSON.stringify({ chat_id: CHAT_ID, text }),
38
+ });
39
+ if (!res.ok) {
40
+ throw new Error(`sendMessage failed: ${res.status} ${await res.text()}`);
41
+ }
42
+ }
43
+ async function tgSendPhoto(imagePath, caption) {
44
+ const imageData = await (0, promises_1.readFile)(imagePath);
45
+ const ext = (0, path_1.extname)(imagePath).toLowerCase();
46
+ const mimeMap = {
47
+ ".jpg": "image/jpeg",
48
+ ".jpeg": "image/jpeg",
49
+ ".png": "image/png",
50
+ ".gif": "image/gif",
51
+ ".webp": "image/webp",
52
+ };
53
+ const mimeType = mimeMap[ext] ?? "image/jpeg";
54
+ const blob = new Blob([imageData], { type: mimeType });
55
+ const form = new FormData();
56
+ form.append("chat_id", CHAT_ID);
57
+ form.append("caption", caption);
58
+ form.append("photo", blob, (0, path_1.basename)(imagePath));
59
+ const res = await fetch(`${TG_API}/sendPhoto`, { method: "POST", body: form });
60
+ if (!res.ok) {
61
+ throw new Error(`sendPhoto failed: ${res.status} ${await res.text()}`);
62
+ }
63
+ }
64
+ async function tgGetUpdates(offset, timeoutSecs) {
65
+ const res = await fetch(`${TG_API}/getUpdates`, {
66
+ method: "POST",
67
+ headers: { "Content-Type": "application/json" },
68
+ body: JSON.stringify({ offset, timeout: timeoutSecs, allowed_updates: ["message"] }),
69
+ signal: AbortSignal.timeout((timeoutSecs + 5) * 1000),
70
+ });
71
+ if (!res.ok) {
72
+ throw new Error(`getUpdates failed: ${res.status} ${await res.text()}`);
73
+ }
74
+ const data = (await res.json());
75
+ return data.result ?? [];
76
+ }
77
+ /** All tracked requests */
78
+ const requests = new Map();
79
+ /** FIFO queue of request IDs that haven't been answered yet */
80
+ const pendingQueue = [];
81
+ // ── Long-poll Telegram loop ──────────────────────────────────────────────────
82
+ let pollingOffset = 0;
83
+ /**
84
+ * Continuous Telegram poll loop — runs forever as a daemon.
85
+ * Routes each incoming message to the oldest pending request (FIFO).
86
+ */
87
+ async function pollLoop() {
88
+ process.stderr.write("[meatbag-service] Telegram polling started\n");
89
+ while (true) {
90
+ try {
91
+ const updates = await tgGetUpdates(pollingOffset, 30);
92
+ for (const update of updates) {
93
+ pollingOffset = update.update_id + 1;
94
+ const msg = update.message;
95
+ if (!msg)
96
+ continue;
97
+ if (String(msg.chat.id) !== CHAT_ID)
98
+ continue;
99
+ const text = msg.text ?? msg.caption ?? "";
100
+ if (!text)
101
+ continue;
102
+ // Match to oldest pending request
103
+ const id = pendingQueue.shift();
104
+ if (!id) {
105
+ process.stderr.write("[meatbag-service] Received message but no pending requests\n");
106
+ continue;
107
+ }
108
+ const entry = requests.get(id);
109
+ if (!entry)
110
+ continue;
111
+ process.stderr.write(`[meatbag-service] Answer received for request ${id}\n`);
112
+ entry.answer = text;
113
+ for (const waiter of entry.waiters)
114
+ waiter(text);
115
+ entry.waiters = [];
116
+ }
117
+ }
118
+ catch (err) {
119
+ const msg = err instanceof Error ? err.message : String(err);
120
+ // AbortError / TimeoutError are expected from the long-poll timeout
121
+ if (!msg.includes("TimeoutError") && !msg.includes("AbortError")) {
122
+ process.stderr.write(`[meatbag-service] Polling error: ${msg}\n`);
123
+ await new Promise((r) => setTimeout(r, 1000));
124
+ }
125
+ }
126
+ }
127
+ }
128
+ // ── HTTP helpers ─────────────────────────────────────────────────────────────
129
+ function readBody(req) {
130
+ return new Promise((resolve, reject) => {
131
+ let body = "";
132
+ req.on("data", (chunk) => (body += chunk));
133
+ req.on("end", () => resolve(body));
134
+ req.on("error", reject);
135
+ });
136
+ }
137
+ function sendJson(res, status, data) {
138
+ if (res.destroyed || res.writableEnded)
139
+ return;
140
+ const body = JSON.stringify(data);
141
+ res.writeHead(status, { "Content-Type": "application/json" });
142
+ res.end(body);
143
+ }
144
+ // ── HTTP server ───────────────────────────────────────────────────────────────
145
+ const httpServer = (0, http_1.createServer)(async (req, res) => {
146
+ const url = req.url ?? "/";
147
+ const method = req.method ?? "GET";
148
+ try {
149
+ // GET /health
150
+ if (method === "GET" && url === "/health") {
151
+ sendJson(res, 200, { status: "ok", pending: pendingQueue.length });
152
+ return;
153
+ }
154
+ // POST /request
155
+ if (method === "POST" && url === "/request") {
156
+ const bodyStr = await readBody(req);
157
+ let parsed;
158
+ try {
159
+ parsed = JSON.parse(bodyStr);
160
+ }
161
+ catch {
162
+ sendJson(res, 400, { error: "invalid JSON body" });
163
+ return;
164
+ }
165
+ const { question, image_path, context } = parsed;
166
+ if (typeof question !== "string" || !question) {
167
+ sendJson(res, 400, { error: "question (string) is required" });
168
+ return;
169
+ }
170
+ const id = (0, crypto_1.randomUUID)();
171
+ const entry = {
172
+ id,
173
+ question,
174
+ image_path: typeof image_path === "string" ? image_path : undefined,
175
+ context: typeof context === "string" ? context : undefined,
176
+ waiters: [],
177
+ };
178
+ requests.set(id, entry);
179
+ pendingQueue.push(id);
180
+ // Build Telegram message text
181
+ let tgText = question;
182
+ if (entry.context)
183
+ tgText = `[Context: ${entry.context}]\n\n${question}`;
184
+ try {
185
+ if (entry.image_path) {
186
+ await tgSendPhoto(entry.image_path, tgText);
187
+ }
188
+ else {
189
+ await tgSendMessage(tgText);
190
+ }
191
+ }
192
+ catch (tgErr) {
193
+ // Remove from queue if Telegram send fails
194
+ const qi = pendingQueue.indexOf(id);
195
+ if (qi !== -1)
196
+ pendingQueue.splice(qi, 1);
197
+ requests.delete(id);
198
+ const msg = tgErr instanceof Error ? tgErr.message : String(tgErr);
199
+ sendJson(res, 502, { error: `Telegram error: ${msg}` });
200
+ return;
201
+ }
202
+ process.stderr.write(`[meatbag-service] Request queued: ${id}\n`);
203
+ sendJson(res, 200, { request_id: id });
204
+ return;
205
+ }
206
+ // GET /response/:id — long-poll up to 30s
207
+ const responseMatch = /^\/response\/([^/]+)$/.exec(url);
208
+ if (method === "GET" && responseMatch) {
209
+ const id = responseMatch[1];
210
+ const entry = requests.get(id);
211
+ if (!entry) {
212
+ sendJson(res, 404, { error: "request not found" });
213
+ return;
214
+ }
215
+ // Already answered — return immediately
216
+ if (entry.answer !== undefined) {
217
+ sendJson(res, 200, { answer: entry.answer });
218
+ return;
219
+ }
220
+ // Long-poll: hold connection until answer arrives or 30s elapses
221
+ await new Promise((resolve) => {
222
+ const timer = setTimeout(() => {
223
+ const idx = entry.waiters.indexOf(handler);
224
+ if (idx !== -1)
225
+ entry.waiters.splice(idx, 1);
226
+ sendJson(res, 200, {}); // empty → client should retry
227
+ resolve();
228
+ }, 30_000);
229
+ const handler = (answer) => {
230
+ clearTimeout(timer);
231
+ sendJson(res, 200, { answer });
232
+ resolve();
233
+ };
234
+ entry.waiters.push(handler);
235
+ });
236
+ return;
237
+ }
238
+ sendJson(res, 404, { error: "not found" });
239
+ }
240
+ catch (err) {
241
+ const msg = err instanceof Error ? err.message : String(err);
242
+ process.stderr.write(`[meatbag-service] Request handler error: ${msg}\n`);
243
+ sendJson(res, 500, { error: msg });
244
+ }
245
+ });
246
+ // ── Start ─────────────────────────────────────────────────────────────────────
247
+ httpServer.listen(PORT, "127.0.0.1", () => {
248
+ process.stderr.write(`[meatbag-service] Listening on http://127.0.0.1:${PORT}\n`);
249
+ void pollLoop();
250
+ });
251
+ httpServer.on("error", (err) => {
252
+ process.stderr.write(`[meatbag-service] HTTP server error: ${err.message}\n`);
253
+ process.exit(1);
254
+ });
255
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@gonzih/meatbag-mcp",
3
- "version": "1.0.0",
4
- "description": "Human-in-the-loop MCP server via Telegram lets Claude Code agents pause and ask a human for input",
5
- "main": "dist/index.js",
3
+ "version": "1.1.0",
4
+ "description": "Human-in-the-loop MCP for Claude Code: central Telegram service + thin MCP clients. Multiple agents share one human channel without routing conflicts.",
5
+ "main": "dist/mcp.js",
6
6
  "bin": {
7
- "meatbag-mcp": "./dist/index.js"
7
+ "meatbag-mcp": "dist/mcp.js",
8
+ "meatbag-service": "dist/service.js"
8
9
  },
9
10
  "scripts": {
10
11
  "build": "tsc",
11
- "start": "node dist/index.js",
12
- "dev": "ts-node src/index.ts"
12
+ "dev:mcp": "ts-node src/mcp.ts",
13
+ "dev:service": "ts-node src/service.ts"
13
14
  },
14
15
  "keywords": [
15
16
  "mcp",
package/dist/index.d.ts DELETED
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * meatbag-mcp — Human-in-the-loop MCP server via Telegram
4
- *
5
- * Exposes a single MCP tool: request_human_input
6
- * When called, sends a Telegram message and waits for the next reply.
7
- * Uses a FIFO queue: the oldest pending request gets the next reply.
8
- *
9
- * Uses Node 18+ built-in fetch to call the Telegram Bot API directly —
10
- * no third-party Telegram library needed, zero extra dependency attack surface.
11
- */
12
- export {};
13
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG"}
package/dist/index.js DELETED
@@ -1,200 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- /**
4
- * meatbag-mcp — Human-in-the-loop MCP server via Telegram
5
- *
6
- * Exposes a single MCP tool: request_human_input
7
- * When called, sends a Telegram message and waits for the next reply.
8
- * Uses a FIFO queue: the oldest pending request gets the next reply.
9
- *
10
- * Uses Node 18+ built-in fetch to call the Telegram Bot API directly —
11
- * no third-party Telegram library needed, zero extra dependency attack surface.
12
- */
13
- Object.defineProperty(exports, "__esModule", { value: true });
14
- const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
15
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
16
- const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
17
- // ── Config ──────────────────────────────────────────────────────────────────
18
- const BOT_TOKEN = process.env.MEATBAG_BOT_TOKEN;
19
- const CHAT_ID = process.env.MEATBAG_CHAT_ID;
20
- const DEFAULT_TIMEOUT = parseInt(process.env.MEATBAG_TIMEOUT_SECONDS ?? "120", 10);
21
- if (!BOT_TOKEN) {
22
- process.stderr.write("[meatbag-mcp] MEATBAG_BOT_TOKEN env var is required\n");
23
- process.exit(1);
24
- }
25
- if (!CHAT_ID) {
26
- process.stderr.write("[meatbag-mcp] MEATBAG_CHAT_ID env var is required\n");
27
- process.exit(1);
28
- }
29
- const TG_API = `https://api.telegram.org/bot${BOT_TOKEN}`;
30
- async function tgSendMessage(chatId, text) {
31
- const res = await fetch(`${TG_API}/sendMessage`, {
32
- method: "POST",
33
- headers: { "Content-Type": "application/json" },
34
- body: JSON.stringify({ chat_id: chatId, text }),
35
- });
36
- if (!res.ok) {
37
- const body = await res.text();
38
- throw new Error(`Telegram sendMessage failed: ${res.status} ${body}`);
39
- }
40
- }
41
- async function tgGetUpdates(offset, timeoutSecs) {
42
- const res = await fetch(`${TG_API}/getUpdates`, {
43
- method: "POST",
44
- headers: { "Content-Type": "application/json" },
45
- body: JSON.stringify({ offset, timeout: timeoutSecs, allowed_updates: ["message"] }),
46
- signal: AbortSignal.timeout((timeoutSecs + 5) * 1000),
47
- });
48
- if (!res.ok) {
49
- const body = await res.text();
50
- throw new Error(`Telegram getUpdates failed: ${res.status} ${body}`);
51
- }
52
- const data = (await res.json());
53
- return data.result ?? [];
54
- }
55
- const pendingQueue = [];
56
- // ── Long-poll loop ───────────────────────────────────────────────────────────
57
- let pollingOffset = 0;
58
- let pollingActive = false;
59
- /**
60
- * Start background long-polling loop.
61
- * Runs only when there are pending requests; stops itself when the queue empties.
62
- */
63
- function ensurePolling() {
64
- if (pollingActive)
65
- return;
66
- pollingActive = true;
67
- void pollLoop();
68
- }
69
- async function pollLoop() {
70
- while (pendingQueue.length > 0) {
71
- try {
72
- const updates = await tgGetUpdates(pollingOffset, 30);
73
- for (const update of updates) {
74
- pollingOffset = update.update_id + 1;
75
- const msg = update.message;
76
- if (!msg)
77
- continue;
78
- if (String(msg.chat.id) !== CHAT_ID)
79
- continue;
80
- const text = msg.text ?? msg.caption ?? "";
81
- if (!text)
82
- continue;
83
- const pending = pendingQueue.shift();
84
- if (!pending)
85
- continue;
86
- clearTimeout(pending.timeoutHandle);
87
- pending.resolve(text);
88
- }
89
- }
90
- catch (err) {
91
- const errMsg = err instanceof Error ? err.message : String(err);
92
- // Ignore timeout errors from AbortSignal — they're expected
93
- if (!errMsg.includes("TimeoutError") && !errMsg.includes("AbortError")) {
94
- process.stderr.write(`[meatbag-mcp] Polling error: ${errMsg}\n`);
95
- }
96
- // Brief back-off before retrying to avoid hammering the API
97
- await new Promise((r) => setTimeout(r, 1000));
98
- }
99
- }
100
- pollingActive = false;
101
- }
102
- // ── Core logic ────────────────────────────────────────────────────────────────
103
- async function requestHumanInput(message, options, timeoutSeconds = DEFAULT_TIMEOUT) {
104
- // Build the Telegram message text
105
- let text = message;
106
- if (options && options.length > 0) {
107
- text += "\n\nOptions:";
108
- options.forEach((opt, i) => {
109
- text += `\n${i + 1}. ${opt}`;
110
- });
111
- text += "\n\nReply with the option number or your own answer.";
112
- }
113
- await tgSendMessage(CHAT_ID, text);
114
- return new Promise((resolve) => {
115
- const timeoutHandle = setTimeout(() => {
116
- const idx = pendingQueue.findIndex((p) => p.timeoutHandle === timeoutHandle);
117
- if (idx !== -1)
118
- pendingQueue.splice(idx, 1);
119
- resolve({ answer: "", timed_out: true });
120
- }, timeoutSeconds * 1000);
121
- pendingQueue.push({
122
- resolve: (answer) => resolve({ answer, timed_out: false }),
123
- reject: (err) => {
124
- process.stderr.write(`[meatbag-mcp] Request rejected: ${err.message}\n`);
125
- resolve({ answer: "", timed_out: true });
126
- },
127
- timeoutHandle,
128
- });
129
- // Kick off polling if not already running
130
- ensurePolling();
131
- });
132
- }
133
- // ── MCP Server ────────────────────────────────────────────────────────────────
134
- const server = new index_js_1.Server({ name: "meatbag-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
135
- server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
136
- tools: [
137
- {
138
- name: "request_human_input",
139
- description: "Send a message to the human operator via Telegram and wait for their reply. Use this when you need a human decision, approval, captcha solution, or free-text input.",
140
- inputSchema: {
141
- type: "object",
142
- properties: {
143
- message: {
144
- type: "string",
145
- description: "The question or prompt to send to the human operator.",
146
- },
147
- options: {
148
- type: "array",
149
- items: { type: "string" },
150
- description: "Optional list of choices. If provided, they are shown as a numbered list and the user can reply with a number.",
151
- },
152
- timeout_seconds: {
153
- type: "number",
154
- description: `How long to wait for a reply in seconds (default: ${DEFAULT_TIMEOUT}).`,
155
- },
156
- },
157
- required: ["message"],
158
- },
159
- },
160
- ],
161
- }));
162
- server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
163
- if (request.params.name !== "request_human_input") {
164
- return {
165
- content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
166
- isError: true,
167
- };
168
- }
169
- const args = request.params.arguments;
170
- if (!args.message || typeof args.message !== "string") {
171
- return {
172
- content: [{ type: "text", text: "message argument is required" }],
173
- isError: true,
174
- };
175
- }
176
- try {
177
- const result = await requestHumanInput(args.message, args.options, args.timeout_seconds);
178
- return {
179
- content: [{ type: "text", text: JSON.stringify(result) }],
180
- };
181
- }
182
- catch (err) {
183
- const msg = err instanceof Error ? err.message : String(err);
184
- return {
185
- content: [{ type: "text", text: `Error: ${msg}` }],
186
- isError: true,
187
- };
188
- }
189
- });
190
- // ── Start ─────────────────────────────────────────────────────────────────────
191
- async function main() {
192
- const transport = new stdio_js_1.StdioServerTransport();
193
- await server.connect(transport);
194
- process.stderr.write("[meatbag-mcp] Server running on stdio\n");
195
- }
196
- main().catch((err) => {
197
- process.stderr.write(`[meatbag-mcp] Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
198
- process.exit(1);
199
- });
200
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}