mcp-telegram-ask-claude 1.0.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/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # alpha-claude-telegram-ask-user
2
+
3
+ > **Alpha / WIP** -- bugs expected. MCP bridge that lets Claude agents ask Telegram users questions via inline keyboards.
4
+
5
+ Extracted from [ClaudeClaw](https://github.com/Febnyy/claudeclaw). No other open source project does this bridge: Claude Agent SDK `ask_user` tool -> Telegram inline keyboard -> promise resolves with user's answer.
6
+
7
+ ## How it works
8
+
9
+ ```
10
+ Claude agent calls ask_user tool
11
+ -> MCP server sends Telegram message with inline keyboard
12
+ -> User taps a button (or types free text via ForceReply)
13
+ -> Bot callback_query handler resolves the promise
14
+ -> Claude agent receives the answer and continues
15
+ ```
16
+
17
+ 5-minute timeout built in. Double-tap protection. Multi-select support.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install alpha-claude-telegram-ask-user
23
+ # peer dep -- pick one:
24
+ npm install grammy # grammY adapter
25
+ npm install telegraf # Telegraf adapter
26
+ ```
27
+
28
+ ## Usage (grammY)
29
+
30
+ ```ts
31
+ import { Bot } from 'grammy'
32
+ import { query } from '@anthropic-ai/claude-agent-sdk'
33
+ import {
34
+ createTelegramUiServer,
35
+ createGrammyAdapter,
36
+ } from 'alpha-claude-telegram-ask-user'
37
+
38
+ const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!)
39
+ const chatId = process.env.TELEGRAM_CHAT_ID!
40
+
41
+ const adapter = createGrammyAdapter(bot)
42
+ const ui = createTelegramUiServer(adapter, chatId, { timeoutMs: 5 * 60_000 })
43
+
44
+ // Hook into callback queries (button taps)
45
+ bot.on('callback_query:data', async (ctx) => {
46
+ await ui.handleCallbackQuery(ctx.callbackQuery.data, ctx.callbackQuery.id)
47
+ })
48
+
49
+ // Hook into ForceReply (free text via "Autre")
50
+ bot.on('message:text', async (ctx) => {
51
+ if (ctx.message.reply_to_message) {
52
+ await ui.handleForceReplyMessage(ctx.message.reply_to_message.message_id, ctx.message.text)
53
+ }
54
+ })
55
+
56
+ bot.start()
57
+
58
+ // Attach MCP server to Claude queries
59
+ const response = await query({
60
+ prompt: 'Ask the user what they prefer.',
61
+ options: {
62
+ model: 'claude-opus-4-5',
63
+ mcpServers: { 'telegram-ui': ui.server },
64
+ },
65
+ })
66
+
67
+ // Cleanup on exit
68
+ process.on('SIGTERM', () => ui.destroy())
69
+ ```
70
+
71
+ ## Usage (Telegraf)
72
+
73
+ ```ts
74
+ import { Telegraf } from 'telegraf'
75
+ import { createTelegramUiServer, createTelegrafAdapter } from 'alpha-claude-telegram-ask-user'
76
+
77
+ const bot = new Telegraf(process.env.TELEGRAM_BOT_TOKEN!)
78
+ const adapter = createTelegrafAdapter(bot)
79
+ const ui = createTelegramUiServer(adapter, chatId)
80
+
81
+ bot.on('callback_query', async (ctx) => {
82
+ if ('data' in ctx.callbackQuery) {
83
+ await ui.handleCallbackQuery(ctx.callbackQuery.data, ctx.callbackQuery.id)
84
+ }
85
+ })
86
+ ```
87
+
88
+ ## Custom store (Redis, SQLite, etc.)
89
+
90
+ Inject your own store via the `store` option:
91
+
92
+ ```ts
93
+ import type { QuestionStore, PendingQuestion } from 'alpha-claude-telegram-ask-user'
94
+
95
+ class MyStore implements QuestionStore {
96
+ async set(id: string, q: PendingQuestion) { /* ... */ }
97
+ async get(id: string) { /* ... */ }
98
+ async delete(id: string) { /* ... */ }
99
+ async getExpired(now: number) { /* ... */ }
100
+ }
101
+
102
+ const ui = createTelegramUiServer(adapter, chatId, { store: new MyStore() })
103
+ ```
104
+
105
+ See `examples/custom-store-example.ts` for a full SQLite implementation.
106
+
107
+ ## API
108
+
109
+ ### `createTelegramUiServer(adapter, chatId, options?)`
110
+
111
+ Returns `{ server, handleCallbackQuery, handleForceReplyMessage, destroy }`.
112
+
113
+ - `server` -- MCP server to pass to `query({ options: { mcpServers } })`
114
+ - `handleCallbackQuery(data, callbackQueryId)` -- call from your bot's callback_query handler
115
+ - `handleForceReplyMessage(replyToMessageId, text)` -- call from your bot's message handler
116
+ - `destroy()` -- clears timers and in-flight state on shutdown
117
+
118
+ ### `createGrammyAdapter(bot)` / `createTelegrafAdapter(bot)`
119
+
120
+ Wrap your bot instance into the framework-agnostic `TelegramAdapter`.
121
+
122
+ ### `InMemoryStore`
123
+
124
+ Default store. No external dependencies.
125
+
126
+ ## Known issues (alpha)
127
+
128
+ - One `createTelegramUiServer` instance per process (module-level resolver maps)
129
+ - `ask_user` blocks the agent turn until the user responds (or timeout)
130
+ - No built-in retry on Telegram API errors
131
+ - ForceReply ("Autre") requires the user to explicitly reply to the bot message
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,4 @@
1
+ import type { Bot, Context } from 'grammy';
2
+ import type { TelegramAdapter } from '../types.js';
3
+ export declare function createGrammyAdapter(bot: Bot<Context>): TelegramAdapter;
4
+ //# sourceMappingURL=grammy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grammy.d.ts","sourceRoot":"","sources":["../../src/adapters/grammy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAwB,MAAM,aAAa,CAAA;AAExE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,eAAe,CA6BtE"}
@@ -0,0 +1,28 @@
1
+ export function createGrammyAdapter(bot) {
2
+ return {
3
+ async sendMessage(chatId, text, keyboard) {
4
+ const reply_markup = keyboard ? { inline_keyboard: keyboard } : undefined;
5
+ const msg = await bot.api.sendMessage(chatId, text, { reply_markup });
6
+ return { messageId: msg.message_id };
7
+ },
8
+ async editMessage(chatId, messageId, text, keyboard) {
9
+ // Pass `{ inline_keyboard: [] }` (not `undefined`) to actively remove the keyboard.
10
+ // Passing `reply_markup: undefined` is dropped by JSON.stringify and Telegram
11
+ // preserves the existing keyboard -- which is NOT the desired behavior on resolve/timeout.
12
+ const reply_markup = keyboard
13
+ ? { inline_keyboard: keyboard }
14
+ : { inline_keyboard: [] };
15
+ await bot.api.editMessageText(chatId, messageId, text, { reply_markup });
16
+ },
17
+ async answerCallbackQuery(callbackQueryId, text) {
18
+ await bot.api.answerCallbackQuery(callbackQueryId, { text });
19
+ },
20
+ async sendForceReply(chatId, promptText) {
21
+ const msg = await bot.api.sendMessage(chatId, promptText, {
22
+ reply_markup: { force_reply: true, selective: true },
23
+ });
24
+ return { messageId: msg.message_id };
25
+ },
26
+ };
27
+ }
28
+ //# sourceMappingURL=grammy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grammy.js","sourceRoot":"","sources":["../../src/adapters/grammy.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,mBAAmB,CAAC,GAAiB;IACnD,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACtC,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;YACzE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;YACrE,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,CAAA;QACtC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ;YACjD,oFAAoF;YACpF,8EAA8E;YAC9E,2FAA2F;YAC3F,MAAM,YAAY,GAAG,QAAQ;gBAC3B,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE;gBAC/B,CAAC,CAAC,EAAE,eAAe,EAAE,EAA0B,EAAE,CAAA;YACnD,MAAM,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;QAC1E,CAAC;QAED,KAAK,CAAC,mBAAmB,CAAC,eAAe,EAAE,IAAI;YAC7C,MAAM,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;QAC9D,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU;YACrC,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE;gBACxD,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;aACrD,CAAC,CAAA;YACF,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Telegraf, Context } from 'telegraf';
2
+ import type { TelegramAdapter } from '../types.js';
3
+ export declare function createTelegrafAdapter(bot: Telegraf<Context>): TelegramAdapter;
4
+ //# sourceMappingURL=telegraf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegraf.d.ts","sourceRoot":"","sources":["../../src/adapters/telegraf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,KAAK,EAAE,eAAe,EAAwB,MAAM,aAAa,CAAA;AAExE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,eAAe,CA4B7E"}
@@ -0,0 +1,27 @@
1
+ export function createTelegrafAdapter(bot) {
2
+ return {
3
+ async sendMessage(chatId, text, keyboard) {
4
+ const reply_markup = keyboard ? { inline_keyboard: keyboard } : undefined;
5
+ const msg = await bot.telegram.sendMessage(chatId, text, { reply_markup });
6
+ return { messageId: msg.message_id };
7
+ },
8
+ async editMessage(chatId, messageId, text, keyboard) {
9
+ // Same fix as grammY adapter: use `{ inline_keyboard: [] }` to remove keyboard.
10
+ // Telegraf v4 editMessageText signature: (chat_id, message_id, inline_message_id, text, extra?)
11
+ const reply_markup = keyboard
12
+ ? { inline_keyboard: keyboard }
13
+ : { inline_keyboard: [] };
14
+ await bot.telegram.editMessageText(chatId, messageId, undefined, text, { reply_markup });
15
+ },
16
+ async answerCallbackQuery(callbackQueryId, text) {
17
+ await bot.telegram.answerCbQuery(callbackQueryId, text);
18
+ },
19
+ async sendForceReply(chatId, promptText) {
20
+ const msg = await bot.telegram.sendMessage(chatId, promptText, {
21
+ reply_markup: { force_reply: true, selective: true },
22
+ });
23
+ return { messageId: msg.message_id };
24
+ },
25
+ };
26
+ }
27
+ //# sourceMappingURL=telegraf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegraf.js","sourceRoot":"","sources":["../../src/adapters/telegraf.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,qBAAqB,CAAC,GAAsB;IAC1D,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ;YACtC,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;YACzE,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;YAC1E,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,CAAA;QACtC,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ;YACjD,gFAAgF;YAChF,gGAAgG;YAChG,MAAM,YAAY,GAAG,QAAQ;gBAC3B,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE;gBAC/B,CAAC,CAAC,EAAE,eAAe,EAAE,EAA0B,EAAE,CAAA;YACnD,MAAM,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;QAC1F,CAAC;QAED,KAAK,CAAC,mBAAmB,CAAC,eAAe,EAAE,IAAI;YAC7C,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,eAAe,EAAE,IAAI,CAAC,CAAA;QACzD,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU;YACrC,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE;gBAC7D,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE;aACrD,CAAC,CAAA;YACF,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,CAAA;QACtC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { createTelegramUiServer } from './server.js';
2
+ export { InMemoryStore } from './store.js';
3
+ export { createGrammyAdapter } from './adapters/grammy.js';
4
+ export { createTelegrafAdapter } from './adapters/telegraf.js';
5
+ export { parseCallbackData, buildInlineKeyboard, formatQuestionText, generateShortId } from './keyboard.js';
6
+ export type { TelegramAdapter, TelegramUiOptions, QuestionStore, QuestionOption, PendingQuestion, InlineButton, InlineKeyboardMarkup, CallbackAction, } from './types.js';
7
+ export type { TelegramUiServer } from './server.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAG3G,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,cAAc,EACd,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,cAAc,GACf,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Public API
2
+ export { createTelegramUiServer } from './server.js';
3
+ export { InMemoryStore } from './store.js';
4
+ export { createGrammyAdapter } from './adapters/grammy.js';
5
+ export { createTelegrafAdapter } from './adapters/telegraf.js';
6
+ export { parseCallbackData, buildInlineKeyboard, formatQuestionText, generateShortId } from './keyboard.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA"}
@@ -0,0 +1,6 @@
1
+ import type { CallbackAction, InlineKeyboardMarkup, QuestionOption } from './types.js';
2
+ export declare function generateShortId(): string;
3
+ export declare function parseCallbackData(data: string): CallbackAction | null;
4
+ export declare function buildInlineKeyboard(questionId: string, options: QuestionOption[], multiSelect: boolean, selected?: number[]): InlineKeyboardMarkup;
5
+ export declare function formatQuestionText(question: string, header: string | undefined, options: QuestionOption[]): string;
6
+ //# sourceMappingURL=keyboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAItF,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAmBrE;AAID,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EAAE,EACzB,WAAW,EAAE,OAAO,EACpB,QAAQ,GAAE,MAAM,EAAO,GACtB,oBAAoB,CA6BtB;AAID,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,cAAc,EAAE,GACxB,MAAM,CAMR"}
@@ -0,0 +1,65 @@
1
+ import { randomBytes } from 'crypto';
2
+ // -- Short ID generator ---------------------------------------------------
3
+ export function generateShortId() {
4
+ return randomBytes(4).toString('hex'); // 8 hex chars
5
+ }
6
+ // -- Callback data parser -------------------------------------------------
7
+ export function parseCallbackData(data) {
8
+ if (!data.startsWith('q:'))
9
+ return null;
10
+ const parts = data.split(':');
11
+ if (parts.length < 3)
12
+ return null;
13
+ const questionId = parts[1];
14
+ if (!questionId)
15
+ return null;
16
+ if (parts[2] === 'done')
17
+ return { questionId, action: 'done' };
18
+ if (parts[2] === 'other')
19
+ return { questionId, action: 'other' };
20
+ if (parts[2] === 't' && parts.length === 4) {
21
+ const index = parseInt(parts[3], 10);
22
+ if (isNaN(index))
23
+ return null;
24
+ return { questionId, action: 'toggle', index };
25
+ }
26
+ const index = parseInt(parts[2], 10);
27
+ if (isNaN(index))
28
+ return null;
29
+ return { questionId, action: 'select', index };
30
+ }
31
+ // -- Keyboard builder ----------------------------------------------------
32
+ export function buildInlineKeyboard(questionId, options, multiSelect, selected = []) {
33
+ const rows = [];
34
+ let currentRow = [];
35
+ for (let i = 0; i < options.length; i++) {
36
+ const callbackData = multiSelect
37
+ ? `q:${questionId}:t:${i}`
38
+ : `q:${questionId}:${i}`;
39
+ const prefix = multiSelect && selected.includes(i) ? '-> ' : '';
40
+ currentRow.push({ text: `${prefix}${options[i].label}`, callback_data: callbackData });
41
+ // 2 buttons per row
42
+ if (currentRow.length === 2 || i === options.length - 1) {
43
+ rows.push(currentRow);
44
+ currentRow = [];
45
+ }
46
+ }
47
+ // Action row
48
+ const actionRow = [
49
+ { text: 'Autre', callback_data: `q:${questionId}:other` },
50
+ ];
51
+ if (multiSelect) {
52
+ actionRow.push({ text: 'Valider', callback_data: `q:${questionId}:done` });
53
+ }
54
+ rows.push(actionRow);
55
+ return rows;
56
+ }
57
+ // -- Question text formatter --------------------------------------------
58
+ export function formatQuestionText(question, header, options) {
59
+ const headerLine = header ? `[${header}] ` : '';
60
+ const optionLines = options
61
+ .map((o, i) => `${i + 1}. ${o.label} -- ${o.description}`)
62
+ .join('\n');
63
+ return `${headerLine}${question}\n\n${optionLines}`;
64
+ }
65
+ //# sourceMappingURL=keyboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.js","sourceRoot":"","sources":["../src/keyboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { createSdkMcpServer } from '@anthropic-ai/claude-agent-sdk';
2
+ import type { TelegramAdapter, TelegramUiOptions } from './types.js';
3
+ type McpServer = ReturnType<typeof createSdkMcpServer>;
4
+ export interface TelegramUiServer {
5
+ server: McpServer;
6
+ handleCallbackQuery(callbackData: string, callbackQueryId: string): Promise<void>;
7
+ handleForceReplyMessage(replyToMessageId: number, answerText: string): Promise<boolean>;
8
+ destroy(): void;
9
+ }
10
+ export declare const pendingResolvers: Map<string, (answer: string) => void>;
11
+ export declare const pendingForceReplies: Map<number, string>;
12
+ export declare function createTelegramUiServer(adapter: TelegramAdapter, chatId: string, options?: TelegramUiOptions): TelegramUiServer;
13
+ export {};
14
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAQ,MAAM,gCAAgC,CAAA;AAIzE,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAGlB,MAAM,YAAY,CAAA;AAGnB,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAGtD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,SAAS,CAAA;IACjB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjF,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvF,OAAO,IAAI,IAAI,CAAA;CAChB;AAID,eAAO,MAAM,gBAAgB,uBAA4B,MAAM,KAAK,IAAI,CAAG,CAAA;AAC3E,eAAO,MAAM,mBAAmB,qBAA4B,CAAA;AAI5D,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAgQlB"}
package/dist/server.js ADDED
@@ -0,0 +1,212 @@
1
+ import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
2
+ import { z } from 'zod';
3
+ import { InMemoryStore } from './store.js';
4
+ import { generateShortId, buildInlineKeyboard, formatQuestionText, parseCallbackData } from './keyboard.js';
5
+ // -- Module-level singletons (one instance per process) -------------------
6
+ export const pendingResolvers = new Map();
7
+ export const pendingForceReplies = new Map(); // messageId -> questionId
8
+ // -- Factory --------------------------------------------------------------
9
+ export function createTelegramUiServer(adapter, chatId, options = {}) {
10
+ const timeoutMs = options.timeoutMs ?? 300_000;
11
+ const store = options.store ?? new InMemoryStore();
12
+ // -- ask_user tool handler -------------------------------------------
13
+ async function handleAskUser(args) {
14
+ if (args.options.length < 2 || args.options.length > 4) {
15
+ return 'ask_user requires between 2 and 4 options.';
16
+ }
17
+ const questionId = generateShortId();
18
+ const multiSelect = args.multi_select ?? false;
19
+ const now = Math.floor(Date.now() / 1000);
20
+ const question = {
21
+ id: questionId,
22
+ chatId,
23
+ messageId: null,
24
+ question: args.question,
25
+ header: args.header ?? null,
26
+ options: args.options,
27
+ multiSelect,
28
+ selected: [],
29
+ answered: false,
30
+ createdAt: now,
31
+ expiresAt: now + Math.floor(timeoutMs / 1000),
32
+ };
33
+ await store.set(questionId, question);
34
+ const kb = buildInlineKeyboard(questionId, args.options, multiSelect);
35
+ const text = formatQuestionText(args.question, args.header, args.options);
36
+ let sentMessageId;
37
+ try {
38
+ const sent = await adapter.sendMessage(chatId, text, kb);
39
+ sentMessageId = sent.messageId;
40
+ const updated = { ...question, messageId: sentMessageId };
41
+ await store.set(questionId, updated);
42
+ }
43
+ catch {
44
+ await store.delete(questionId);
45
+ return 'Failed to reach user. Proceed with best judgment.';
46
+ }
47
+ // Wait for answer
48
+ const answer = await new Promise((resolve) => {
49
+ pendingResolvers.set(questionId, resolve);
50
+ setTimeout(async () => {
51
+ if (pendingResolvers.has(questionId)) {
52
+ pendingResolvers.delete(questionId);
53
+ const q = await store.get(questionId);
54
+ if (q?.messageId) {
55
+ adapter.editMessage(chatId, q.messageId, `${text}\n\n-- Question expiree`, { inline_keyboard: [] }).catch(() => { });
56
+ }
57
+ await store.delete(questionId);
58
+ resolve('__timeout__');
59
+ }
60
+ }, timeoutMs);
61
+ });
62
+ pendingResolvers.delete(questionId);
63
+ if (answer === '__timeout__') {
64
+ const minutes = Math.round(timeoutMs / 60_000);
65
+ return `User did not respond within ${minutes} minutes. Proceed with best judgment.`;
66
+ }
67
+ return answer;
68
+ }
69
+ // -- MCP server -------------------------------------------------------
70
+ const server = createSdkMcpServer({
71
+ name: 'telegram-ui',
72
+ version: '1.0.0',
73
+ tools: [
74
+ tool('ask_user', 'Ask the Telegram user a question with clickable buttons. Returns their chosen option or custom text answer.', {
75
+ question: z.string().describe('The question to ask'),
76
+ options: z
77
+ .array(z.object({ label: z.string(), description: z.string() }))
78
+ .min(2)
79
+ .max(4)
80
+ .describe('2-4 options to choose from'),
81
+ header: z.string().optional().describe('Short label (max 12 chars)'),
82
+ multi_select: z.boolean().optional().default(false),
83
+ }, async (args) => {
84
+ const answer = await handleAskUser(args);
85
+ return { content: [{ type: 'text', text: answer }] };
86
+ }),
87
+ ],
88
+ });
89
+ // -- handleCallbackQuery ----------------------------------------------
90
+ async function handleCallbackQuery(callbackData, callbackQueryId) {
91
+ const action = parseCallbackData(callbackData);
92
+ if (!action)
93
+ return;
94
+ const q = await store.get(action.questionId);
95
+ if (!q) {
96
+ await adapter.answerCallbackQuery(callbackQueryId).catch(() => { });
97
+ return;
98
+ }
99
+ if (q.answered) {
100
+ await adapter.answerCallbackQuery(callbackQueryId).catch(() => { });
101
+ return;
102
+ }
103
+ const text = formatQuestionText(q.question, q.header ?? undefined, q.options);
104
+ if (action.action === 'select') {
105
+ const chosen = q.options[action.index];
106
+ if (!chosen) {
107
+ await adapter.answerCallbackQuery(callbackQueryId).catch(() => { });
108
+ return;
109
+ }
110
+ const updated = { ...q, answered: true };
111
+ await store.set(q.id, updated);
112
+ if (q.messageId) {
113
+ await adapter.editMessage(q.chatId, q.messageId, `${text}\n\n-> ${chosen.label}`).catch(() => { });
114
+ }
115
+ const resolver = pendingResolvers.get(q.id);
116
+ if (resolver) {
117
+ resolver(chosen.label);
118
+ pendingResolvers.delete(q.id);
119
+ }
120
+ }
121
+ else if (action.action === 'toggle') {
122
+ let selected = [...q.selected];
123
+ if (selected.includes(action.index)) {
124
+ selected = selected.filter(i => i !== action.index);
125
+ }
126
+ else {
127
+ selected.push(action.index);
128
+ }
129
+ const updated = { ...q, selected };
130
+ await store.set(q.id, updated);
131
+ const kb = buildInlineKeyboard(q.id, q.options, true, selected);
132
+ if (q.messageId) {
133
+ await adapter.editMessage(q.chatId, q.messageId, text, kb).catch(() => { });
134
+ }
135
+ }
136
+ else if (action.action === 'done') {
137
+ if (q.selected.length === 0) {
138
+ await adapter.answerCallbackQuery(callbackQueryId, 'Select at least one option').catch(() => { });
139
+ return;
140
+ }
141
+ const chosenLabels = q.selected.map(i => q.options[i]?.label).filter(Boolean);
142
+ const answer = chosenLabels.join(', ');
143
+ const updated = { ...q, answered: true };
144
+ await store.set(q.id, updated);
145
+ if (q.messageId) {
146
+ await adapter.editMessage(q.chatId, q.messageId, `${text}\n\n-> ${answer}`).catch(() => { });
147
+ }
148
+ const resolver = pendingResolvers.get(q.id);
149
+ if (resolver) {
150
+ resolver(answer);
151
+ pendingResolvers.delete(q.id);
152
+ }
153
+ }
154
+ else if (action.action === 'other') {
155
+ try {
156
+ const sent = await adapter.sendForceReply(q.chatId, 'Ecris ta reponse :');
157
+ pendingForceReplies.set(sent.messageId, q.id);
158
+ }
159
+ catch {
160
+ // ignore
161
+ }
162
+ }
163
+ await adapter.answerCallbackQuery(callbackQueryId).catch(() => { });
164
+ }
165
+ // -- handleForceReplyMessage ------------------------------------------
166
+ async function handleForceReplyMessage(replyToMessageId, answerText) {
167
+ const questionId = pendingForceReplies.get(replyToMessageId);
168
+ if (!questionId)
169
+ return false;
170
+ pendingForceReplies.delete(replyToMessageId);
171
+ const q = await store.get(questionId);
172
+ if (!q || q.answered)
173
+ return false;
174
+ const updated = { ...q, answered: true };
175
+ await store.set(q.id, updated);
176
+ if (q.messageId) {
177
+ const text = formatQuestionText(q.question, q.header ?? undefined, q.options);
178
+ await adapter.editMessage(q.chatId, q.messageId, `${text}\n\n-> ${answerText}`).catch(() => { });
179
+ }
180
+ const resolver = pendingResolvers.get(q.id);
181
+ if (resolver) {
182
+ resolver(answerText);
183
+ pendingResolvers.delete(q.id);
184
+ }
185
+ return true;
186
+ }
187
+ // -- Cleanup interval (safety net) ------------------------------------
188
+ const cleanupInterval = setInterval(async () => {
189
+ const now = Math.floor(Date.now() / 1000);
190
+ const expired = await store.getExpired(now);
191
+ for (const q of expired) {
192
+ const text = formatQuestionText(q.question, q.header ?? undefined, q.options);
193
+ if (q.messageId) {
194
+ await adapter.editMessage(q.chatId, q.messageId, `${text}\n\n-- Question expiree`).catch(() => { });
195
+ }
196
+ const resolver = pendingResolvers.get(q.id);
197
+ if (resolver) {
198
+ resolver('__timeout__');
199
+ pendingResolvers.delete(q.id);
200
+ }
201
+ await store.delete(q.id);
202
+ }
203
+ }, 60_000);
204
+ // -- destroy ----------------------------------------------------------
205
+ function destroy() {
206
+ clearInterval(cleanupInterval);
207
+ pendingResolvers.clear();
208
+ pendingForceReplies.clear();
209
+ }
210
+ return { server, handleCallbackQuery, handleForceReplyMessage, destroy };
211
+ }
212
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { PendingQuestion, QuestionStore } from './types.js';
2
+ export declare class InMemoryStore implements QuestionStore {
3
+ private readonly map;
4
+ set(id: string, question: PendingQuestion): Promise<void>;
5
+ get(id: string): Promise<PendingQuestion | undefined>;
6
+ delete(id: string): Promise<void>;
7
+ getExpired(now: number): Promise<PendingQuestion[]>;
8
+ }
9
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEhE,qBAAa,aAAc,YAAW,aAAa;IACjD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IAEnD,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAIrD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAS1D"}
package/dist/store.js ADDED
@@ -0,0 +1,22 @@
1
+ export class InMemoryStore {
2
+ map = new Map();
3
+ async set(id, question) {
4
+ this.map.set(id, question);
5
+ }
6
+ async get(id) {
7
+ return this.map.get(id);
8
+ }
9
+ async delete(id) {
10
+ this.map.delete(id);
11
+ }
12
+ async getExpired(now) {
13
+ const result = [];
14
+ for (const q of this.map.values()) {
15
+ if (q.expiresAt < now && !q.answered) {
16
+ result.push(q);
17
+ }
18
+ }
19
+ return result;
20
+ }
21
+ }
22
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,aAAa;IACP,GAAG,GAAG,IAAI,GAAG,EAA2B,CAAA;IAEzD,KAAK,CAAC,GAAG,CAAC,EAAU,EAAE,QAAyB;QAC7C,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAU;QAClB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACrB,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,GAAW;QAC1B,MAAM,MAAM,GAAsB,EAAE,CAAA;QACpC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAChB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;CACF"}
@@ -0,0 +1,59 @@
1
+ export interface InlineButton {
2
+ text: string;
3
+ callback_data: string;
4
+ }
5
+ /** Rows of buttons. Each row is an array of buttons. */
6
+ export type InlineKeyboardMarkup = InlineButton[][];
7
+ export interface TelegramAdapter {
8
+ sendMessage(chatId: string, text: string, keyboard?: InlineKeyboardMarkup): Promise<{
9
+ messageId: number;
10
+ }>;
11
+ editMessage(chatId: string, messageId: number, text: string, keyboard?: InlineKeyboardMarkup): Promise<void>;
12
+ answerCallbackQuery(callbackQueryId: string, text?: string): Promise<void>;
13
+ sendForceReply(chatId: string, promptText: string): Promise<{
14
+ messageId: number;
15
+ }>;
16
+ }
17
+ export interface QuestionOption {
18
+ label: string;
19
+ description: string;
20
+ }
21
+ export interface PendingQuestion {
22
+ id: string;
23
+ chatId: string;
24
+ messageId: number | null;
25
+ question: string;
26
+ header: string | null;
27
+ options: QuestionOption[];
28
+ multiSelect: boolean;
29
+ selected: number[];
30
+ answered: boolean;
31
+ createdAt: number;
32
+ expiresAt: number;
33
+ }
34
+ export type CallbackAction = {
35
+ questionId: string;
36
+ action: 'select';
37
+ index: number;
38
+ } | {
39
+ questionId: string;
40
+ action: 'toggle';
41
+ index: number;
42
+ } | {
43
+ questionId: string;
44
+ action: 'done';
45
+ } | {
46
+ questionId: string;
47
+ action: 'other';
48
+ };
49
+ export interface QuestionStore {
50
+ set(id: string, question: PendingQuestion): Promise<void>;
51
+ get(id: string): Promise<PendingQuestion | undefined>;
52
+ delete(id: string): Promise<void>;
53
+ getExpired(now: number): Promise<PendingQuestion[]>;
54
+ }
55
+ export interface TelegramUiOptions {
56
+ timeoutMs?: number;
57
+ store?: QuestionStore;
58
+ }
59
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,wDAAwD;AACxD,MAAM,MAAM,oBAAoB,GAAG,YAAY,EAAE,EAAE,CAAA;AAInD,MAAM,WAAW,eAAe;IAC9B,WAAW,CACT,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAEjC,WAAW,CACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAAA;IAEhB,mBAAmB,CACjB,eAAe,EAAE,MAAM,EACvB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAAA;IAEhB,cAAc,CACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClC;AAID,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAID,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,WAAW,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAID,MAAM,MAAM,cAAc,GACtB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAA;AAI3C,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAA;IACrD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;CACpD;AAID,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ // -- Framework-agnostic keyboard types ------------------------------------
2
+ export {};
3
+ // NOTE: TelegramUiServer is NOT defined here to avoid coupling types.ts to the SDK.
4
+ // It is derived in server.ts via:
5
+ // export type TelegramUiServer = ReturnType<typeof createTelegramUiServer>
6
+ // index.ts re-exports it from server.ts, not from types.ts.
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,4EAA4E;;AAoF5E,oFAAoF;AACpF,kCAAkC;AAClC,6EAA6E;AAC7E,4DAA4D"}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "mcp-telegram-ask-claude",
3
+ "version": "1.0.0",
4
+ "description": "MCP bridge: Claude Agent SDK ask_user tool -> Telegram inline keyboards",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "dev": "tsc --watch"
19
+ },
20
+ "keywords": [
21
+ "claude",
22
+ "mcp",
23
+ "telegram",
24
+ "ask-user",
25
+ "inline-keyboard",
26
+ "grammy",
27
+ "telegraf"
28
+ ],
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "@anthropic-ai/claude-agent-sdk": "^0.2.78",
36
+ "zod": "^4.0.0"
37
+ },
38
+ "peerDependencies": {
39
+ "grammy": ">=1.0.0",
40
+ "telegraf": ">=4.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "grammy": {
44
+ "optional": true
45
+ },
46
+ "telegraf": {
47
+ "optional": true
48
+ }
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^22.0.0",
52
+ "grammy": "^1.41.1",
53
+ "telegraf": "^4.16.3",
54
+ "typescript": "^5.9.3",
55
+ "vitest": "^2.0.0"
56
+ }
57
+ }