mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
Integrate MCPProvider and enhance Telegram MCP tools
- Added MCPProvider to manage the initialization and updates of MCP servers within the SocketProvider context. - Introduced utility functions for handling typed values from MCP tool arguments, improving input validation across various tools. - Updated TelegramMCPServer to include enhanced logging and type safety for tool handling. - Refactored multiple Telegram tools to utilize the new argument handling functions, ensuring consistent input processing and error management. These changes enhance the overall structure and functionality of the Telegram MCP integration, providing a more robust framework for managing Telegram interactions.
This commit is contained in:
+8
-5
@@ -3,6 +3,7 @@ import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { store, persistor } from './store';
|
||||
import SocketProvider from './providers/SocketProvider';
|
||||
import MCPProvider from './providers/MCPProvider';
|
||||
import TelegramProvider from './providers/TelegramProvider';
|
||||
import AppRoutes from './AppRoutes';
|
||||
|
||||
@@ -11,11 +12,13 @@ function App() {
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<SocketProvider>
|
||||
<TelegramProvider>
|
||||
<Router>
|
||||
<AppRoutes />
|
||||
</Router>
|
||||
</TelegramProvider>
|
||||
<MCPProvider>
|
||||
<TelegramProvider>
|
||||
<Router>
|
||||
<AppRoutes />
|
||||
</Router>
|
||||
</TelegramProvider>
|
||||
</MCPProvider>
|
||||
</SocketProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Helpers for reading typed values from MCP tool args (Record<string, unknown>)
|
||||
*/
|
||||
|
||||
export function optNumber(args: Record<string, unknown>, key: string, fallback: number): number {
|
||||
const v = args[key];
|
||||
return typeof v === 'number' && Number.isFinite(v) ? v : fallback;
|
||||
}
|
||||
|
||||
export function optString(args: Record<string, unknown>, key: string): string | undefined {
|
||||
const v = args[key];
|
||||
return typeof v === 'string' ? v : undefined;
|
||||
}
|
||||
@@ -17,7 +17,11 @@ import { ErrorCategory, logAndFormatError } from "../errorHandler";
|
||||
import { ValidationError } from "../validation";
|
||||
import { SocketIOMCPTransportImpl } from "../transport";
|
||||
import { mcpLog } from "../logger";
|
||||
import type { TelegramMCPToolHandler } from "./types";
|
||||
import {
|
||||
type TelegramMCPToolHandler,
|
||||
type TelegramMCPToolName,
|
||||
TELEGRAM_MCP_TOOL_NAMES,
|
||||
} from "./types";
|
||||
import * as tools from "./tools";
|
||||
|
||||
export class TelegramMCPServer {
|
||||
@@ -27,6 +31,7 @@ export class TelegramMCPServer {
|
||||
constructor(socket: Socket | null | undefined) {
|
||||
this.transport = new SocketIOMCPTransportImpl(socket ?? undefined);
|
||||
this.config = { name: "telegram-mcp", version: "1.0.0" };
|
||||
mcpLog(`Telegram MCP ${this.config.name} v${this.config.version} ready`);
|
||||
this.setupHandlers();
|
||||
}
|
||||
|
||||
@@ -35,15 +40,14 @@ export class TelegramMCPServer {
|
||||
}
|
||||
|
||||
private setupHandlers(): void {
|
||||
this.transport.on(
|
||||
"toolCall",
|
||||
(data: { requestId: string; toolCall: MCPToolCall }) => {
|
||||
void this.handleToolCallRequest(data);
|
||||
},
|
||||
);
|
||||
this.transport.on("toolCall", (data: unknown) => {
|
||||
void this.handleToolCallRequest(
|
||||
data as { requestId: string; toolCall: MCPToolCall },
|
||||
);
|
||||
});
|
||||
|
||||
this.transport.on("listTools", (data: { requestId: string }) => {
|
||||
const { requestId } = data;
|
||||
this.transport.on("listTools", (data: unknown) => {
|
||||
const { requestId } = data as { requestId: string };
|
||||
try {
|
||||
const toolsList = this.listTools();
|
||||
this.transport.emit("listToolsResponse", {
|
||||
@@ -119,7 +123,12 @@ export class TelegramMCPServer {
|
||||
}
|
||||
|
||||
private findToolHandler(name: string): TelegramMCPToolHandler | undefined {
|
||||
const toolMap: Record<string, TelegramMCPToolHandler> = {
|
||||
const isToolName = (n: string): n is TelegramMCPToolName =>
|
||||
(TELEGRAM_MCP_TOOL_NAMES as readonly string[]).includes(n);
|
||||
|
||||
if (!isToolName(name)) return undefined;
|
||||
|
||||
const toolMap: Record<TelegramMCPToolName, TelegramMCPToolHandler> = {
|
||||
get_chats: tools.getChats,
|
||||
list_chats: tools.listChats,
|
||||
get_chat: tools.getChat,
|
||||
@@ -200,6 +209,7 @@ export class TelegramMCPServer {
|
||||
get_contact_chats: tools.getContactChats,
|
||||
get_last_interaction: tools.getLastInteraction,
|
||||
};
|
||||
|
||||
return toolMap[name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { store } from '../../../store';
|
||||
import { mtprotoService } from '../../../services/mtprotoService';
|
||||
import {
|
||||
selectOrderedChats,
|
||||
selectChatMessages,
|
||||
selectCurrentUser,
|
||||
} from '../../../store/telegramSelectors';
|
||||
import type { TelegramChat, TelegramUser, TelegramMessage } from '../../../store/telegram/types';
|
||||
|
||||
@@ -25,8 +25,8 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function getChat(
|
||||
args: { chat_id: string | number },
|
||||
context: TelegramMCPContext,
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { optNumber } from '../args';
|
||||
import { formatEntity, getChats as getChatsApi } from '../telegramApi';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
@@ -21,12 +22,12 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function getChats(
|
||||
args: { page?: number; page_size?: number },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const page = args.page ?? 1;
|
||||
const pageSize = args.page_size ?? 20;
|
||||
const page = optNumber(args, 'page', 1);
|
||||
const pageSize = optNumber(args, 'page_size', 20);
|
||||
const start = (page - 1) * pageSize;
|
||||
|
||||
const chats = await getChatsApi(pageSize + start);
|
||||
|
||||
@@ -18,7 +18,7 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function getMe(
|
||||
_args: Record<string, never>,
|
||||
_args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
|
||||
@@ -2,66 +2,86 @@
|
||||
* Get Messages tool - Get paginated messages from a specific chat
|
||||
*/
|
||||
|
||||
import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
import type { MCPTool, MCPToolResult } from "../../types";
|
||||
import type { TelegramMCPContext } from "../types";
|
||||
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { formatMessage, getChatById, getMessages as getMessagesApi } from '../telegramApi';
|
||||
import { ErrorCategory, logAndFormatError } from "../../errorHandler";
|
||||
import {
|
||||
formatMessage,
|
||||
getChatById,
|
||||
getMessages as getMessagesApi,
|
||||
} from "../telegramApi";
|
||||
import { validateId } from '../../validation';
|
||||
import type { TelegramMessage } from '../../../store/telegram/types';
|
||||
import type { TelegramMessage } from '../../../../store/telegram/types';
|
||||
import { optNumber } from '../args';
|
||||
|
||||
export const tool: MCPTool = {
|
||||
name: 'get_messages',
|
||||
description: 'Get paginated messages from a specific chat',
|
||||
name: "get_messages",
|
||||
description: "Get paginated messages from a specific chat",
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
type: "object",
|
||||
properties: {
|
||||
chat_id: { type: 'string', description: 'The ID or username of the chat' },
|
||||
page: { type: 'number', description: 'Page number (1-indexed)', default: 1 },
|
||||
page_size: { type: 'number', description: 'Number of messages per page', default: 20 },
|
||||
chat_id: {
|
||||
type: "string",
|
||||
description: "The ID or username of the chat",
|
||||
},
|
||||
page: {
|
||||
type: "number",
|
||||
description: "Page number (1-indexed)",
|
||||
default: 1,
|
||||
},
|
||||
page_size: {
|
||||
type: "number",
|
||||
description: "Number of messages per page",
|
||||
default: 20,
|
||||
},
|
||||
},
|
||||
required: ['chat_id'],
|
||||
required: ["chat_id"],
|
||||
},
|
||||
};
|
||||
|
||||
function senderDisplay(msg: TelegramMessage): string {
|
||||
return msg.fromName ?? msg.fromId ?? 'Unknown';
|
||||
return msg.fromName ?? msg.fromId ?? "Unknown";
|
||||
}
|
||||
|
||||
export async function getMessages(
|
||||
args: { chat_id: string | number; page?: number; page_size?: number },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const page = args.page ?? 1;
|
||||
const pageSize = args.page_size ?? 20;
|
||||
const page = optNumber(args, 'page', 1);
|
||||
const pageSize = optNumber(args, 'page_size', 20);
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) {
|
||||
return {
|
||||
content: [{ type: 'text', text: `Chat not found: ${chatId}` }],
|
||||
content: [{ type: "text", text: `Chat not found: ${chatId}` }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const messages = await getMessagesApi(chatId, pageSize, offset);
|
||||
if (!messages || messages.length === 0) {
|
||||
return { content: [{ type: 'text', text: 'No messages found for this page.' }] };
|
||||
return {
|
||||
content: [{ type: "text", text: "No messages found for this page." }],
|
||||
};
|
||||
}
|
||||
|
||||
const lines = messages.map((msg) => {
|
||||
const formatted = formatMessage(msg);
|
||||
const replyStr = msg.replyToMessageId ? ` | reply to ${msg.replyToMessageId}` : '';
|
||||
const msgText = formatted.text || '[Media/No text]';
|
||||
const replyStr = msg.replyToMessageId
|
||||
? ` | reply to ${msg.replyToMessageId}`
|
||||
: "";
|
||||
const msgText = formatted.text || "[Media/No text]";
|
||||
return `ID: ${formatted.id} | ${senderDisplay(msg)} | Date: ${formatted.date}${replyStr} | Message: ${msgText}`;
|
||||
});
|
||||
|
||||
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
||||
return { content: [{ type: "text", text: lines.join("\n") }] };
|
||||
} catch (error) {
|
||||
return logAndFormatError(
|
||||
'get_messages',
|
||||
"get_messages",
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
ErrorCategory.MSG,
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { optNumber, optString } from '../args';
|
||||
import { formatEntity, getChats as getChatsApi } from '../telegramApi';
|
||||
import { toHumanReadableAction } from '../toolActionParser';
|
||||
|
||||
@@ -27,12 +28,12 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function listChats(
|
||||
args: { chat_type?: string; limit?: number },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const limit = args.limit ?? 20;
|
||||
const chatType = args.chat_type?.toLowerCase();
|
||||
const limit = optNumber(args, 'limit', 20);
|
||||
const chatType = optString(args, 'chat_type')?.toLowerCase();
|
||||
|
||||
const chats = await getChatsApi(limit);
|
||||
const contentItems: Array<{ type: 'text'; text: string }> = [];
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { optNumber, optString } from '../args';
|
||||
import { formatMessage, getChatById, getMessages as getMessagesApi } from '../telegramApi';
|
||||
import { validateId } from '../../validation';
|
||||
|
||||
@@ -26,18 +27,15 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function listMessages(
|
||||
args: {
|
||||
chat_id: string | number;
|
||||
limit?: number;
|
||||
search_query?: string;
|
||||
from_date?: string;
|
||||
to_date?: string;
|
||||
},
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const limit = args.limit ?? 20;
|
||||
const limit = optNumber(args, 'limit', 20);
|
||||
const searchQuery = optString(args, 'search_query');
|
||||
const fromDate = optString(args, 'from_date');
|
||||
const toDate = optString(args, 'to_date');
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) {
|
||||
@@ -52,17 +50,17 @@ export async function listMessages(
|
||||
return { content: [{ type: 'text', text: 'No messages found matching the criteria.' }] };
|
||||
}
|
||||
|
||||
if (args.search_query) {
|
||||
const q = args.search_query.toLowerCase();
|
||||
if (searchQuery) {
|
||||
const q = searchQuery.toLowerCase();
|
||||
messages = messages.filter((m) => (m.message ?? '').toLowerCase().includes(q));
|
||||
}
|
||||
|
||||
if (args.from_date || args.to_date) {
|
||||
if (fromDate || toDate) {
|
||||
messages = messages.filter((m) => {
|
||||
const d = new Date(m.date * 1000);
|
||||
if (args.from_date && d < new Date(args.from_date)) return false;
|
||||
if (args.to_date) {
|
||||
const to = new Date(args.to_date);
|
||||
if (fromDate && d < new Date(fromDate)) return false;
|
||||
if (toDate) {
|
||||
const to = new Date(toDate);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
if (d > to) return false;
|
||||
}
|
||||
|
||||
@@ -30,12 +30,28 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function replyToMessage(
|
||||
args: { chat_id: string | number; message_id: number; text: string },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, "chat_id");
|
||||
const { message_id, text } = args;
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const messageId = typeof args.message_id === 'number' && Number.isInteger(args.message_id)
|
||||
? args.message_id
|
||||
: undefined;
|
||||
const text = typeof args.text === 'string' ? args.text : '';
|
||||
|
||||
if (messageId === undefined) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'message_id must be an integer' }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
if (!text) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'text is required' }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) {
|
||||
@@ -45,13 +61,13 @@ export async function replyToMessage(
|
||||
};
|
||||
}
|
||||
|
||||
const result = await sendMessage(chatId, text, message_id);
|
||||
const result = await sendMessage(chatId, text, messageId);
|
||||
if (!result) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Failed to reply to message ${message_id} in chat ${chatId}.`,
|
||||
type: 'text',
|
||||
text: `Failed to reply to message ${messageId} in chat ${chatId}.`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
@@ -61,8 +77,8 @@ export async function replyToMessage(
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Replied to message ${message_id} in chat ${chatId}.`,
|
||||
type: 'text',
|
||||
text: `Replied to message ${messageId} in chat ${chatId}.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -21,11 +21,17 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function resolveUsername(
|
||||
args: { username: string },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const raw = args.username;
|
||||
const raw = typeof args.username === 'string' ? args.username : '';
|
||||
if (!raw) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'username is required' }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
const username = raw.startsWith('@') ? raw : `@${raw}`;
|
||||
const chat = getChatById(username);
|
||||
if (!chat) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { MCPTool, MCPToolResult } from '../../types';
|
||||
import type { TelegramMCPContext } from '../types';
|
||||
|
||||
import { ErrorCategory, logAndFormatError } from '../../errorHandler';
|
||||
import { optNumber } from '../args';
|
||||
import { formatMessage, getChatById, getMessages } from '../telegramApi';
|
||||
import { validateId } from '../../validation';
|
||||
|
||||
@@ -24,12 +25,20 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function searchMessages(
|
||||
args: { chat_id: string | number; query: string; limit?: number },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const { query, limit = 20 } = args;
|
||||
const query = typeof args.query === 'string' ? args.query : '';
|
||||
const limit = optNumber(args, 'limit', 20);
|
||||
|
||||
if (!query) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'query is required' }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
const chat = getChatById(chatId);
|
||||
if (!chat) {
|
||||
@@ -45,7 +54,9 @@ export async function searchMessages(
|
||||
}
|
||||
|
||||
const q = query.toLowerCase();
|
||||
const filtered = messages.filter((m) => (m.message ?? '').toLowerCase().includes(q)).slice(0, limit);
|
||||
const filtered = messages
|
||||
.filter((m) => (m.message ?? '').toLowerCase().includes(q))
|
||||
.slice(0, limit);
|
||||
|
||||
const lines = filtered.map((m) => {
|
||||
const f = formatMessage(m);
|
||||
|
||||
@@ -21,11 +21,18 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function searchPublicChats(
|
||||
args: { query: string },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chats = await searchChats(args.query);
|
||||
const query = typeof args.query === 'string' ? args.query : '';
|
||||
if (!query) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'query is required' }],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
const chats = await searchChats(query);
|
||||
const results = chats.map(formatEntity);
|
||||
return {
|
||||
content: [{ type: 'text', text: JSON.stringify(results, undefined, 2) }],
|
||||
|
||||
@@ -25,13 +25,13 @@ export const tool: MCPTool = {
|
||||
};
|
||||
|
||||
export async function sendMessage(
|
||||
args: { chat_id: string | number; message: string },
|
||||
args: Record<string, unknown>,
|
||||
_context: TelegramMCPContext,
|
||||
): Promise<MCPToolResult> {
|
||||
try {
|
||||
const chatId = validateId(args.chat_id, 'chat_id');
|
||||
const { message } = args;
|
||||
if (!message || typeof message !== 'string') {
|
||||
const message = typeof args.message === 'string' ? args.message : '';
|
||||
if (!message) {
|
||||
return {
|
||||
content: [{ type: 'text', text: 'Message content is required' }],
|
||||
isError: true,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import type { SocketIOMCPTransportImpl } from '../transport';
|
||||
import type { MCPToolResult } from '../../mcp/types';
|
||||
import type { MCPToolResult } from '../types';
|
||||
import type { TelegramState } from '../../../store/telegram/types';
|
||||
|
||||
export interface TelegramMCPContext {
|
||||
@@ -15,3 +15,87 @@ export type TelegramMCPToolHandler = (
|
||||
args: Record<string, unknown>,
|
||||
context: TelegramMCPContext,
|
||||
) => Promise<MCPToolResult>;
|
||||
|
||||
export const TELEGRAM_MCP_TOOL_NAMES = [
|
||||
'get_chats',
|
||||
'list_chats',
|
||||
'get_chat',
|
||||
'create_group',
|
||||
'invite_to_group',
|
||||
'create_channel',
|
||||
'edit_chat_title',
|
||||
'delete_chat_photo',
|
||||
'leave_chat',
|
||||
'get_participants',
|
||||
'get_admins',
|
||||
'get_banned_users',
|
||||
'promote_admin',
|
||||
'demote_admin',
|
||||
'ban_user',
|
||||
'unban_user',
|
||||
'get_invite_link',
|
||||
'export_chat_invite',
|
||||
'import_chat_invite',
|
||||
'join_chat_by_link',
|
||||
'subscribe_public_channel',
|
||||
'get_messages',
|
||||
'list_messages',
|
||||
'list_topics',
|
||||
'send_message',
|
||||
'reply_to_message',
|
||||
'edit_message',
|
||||
'delete_message',
|
||||
'forward_message',
|
||||
'pin_message',
|
||||
'unpin_message',
|
||||
'mark_as_read',
|
||||
'get_message_context',
|
||||
'get_history',
|
||||
'get_pinned_messages',
|
||||
'send_reaction',
|
||||
'remove_reaction',
|
||||
'get_message_reactions',
|
||||
'list_contacts',
|
||||
'search_contacts',
|
||||
'add_contact',
|
||||
'delete_contact',
|
||||
'block_user',
|
||||
'unblock_user',
|
||||
'get_blocked_users',
|
||||
'get_me',
|
||||
'update_profile',
|
||||
'get_user_photos',
|
||||
'get_user_status',
|
||||
'mute_chat',
|
||||
'unmute_chat',
|
||||
'archive_chat',
|
||||
'unarchive_chat',
|
||||
'get_privacy_settings',
|
||||
'set_privacy_settings',
|
||||
'list_inline_buttons',
|
||||
'press_inline_button',
|
||||
'save_draft',
|
||||
'get_drafts',
|
||||
'clear_draft',
|
||||
'search_public_chats',
|
||||
'search_messages',
|
||||
'resolve_username',
|
||||
'get_media_info',
|
||||
'get_recent_actions',
|
||||
'create_poll',
|
||||
'get_bot_info',
|
||||
'set_bot_commands',
|
||||
'set_profile_photo',
|
||||
'delete_profile_photo',
|
||||
'edit_chat_photo',
|
||||
'get_sticker_sets',
|
||||
'get_gif_search',
|
||||
'get_contact_ids',
|
||||
'import_contacts',
|
||||
'export_contacts',
|
||||
'get_direct_chat_by_contact',
|
||||
'get_contact_chats',
|
||||
'get_last_interaction',
|
||||
] as const;
|
||||
|
||||
export type TelegramMCPToolName = (typeof TELEGRAM_MCP_TOOL_NAMES)[number];
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useAppSelector } from '../store/hooks';
|
||||
import { socketService } from '../services/socketService';
|
||||
import {
|
||||
initTelegramMCPServer,
|
||||
getTelegramMCPServer,
|
||||
updateTelegramMCPServerSocket,
|
||||
} from '../lib/mcp/telegram';
|
||||
|
||||
/**
|
||||
* MCPProvider initializes and updates MCP servers when the socket connects.
|
||||
* Place inside SocketProvider so the socket is available.
|
||||
*/
|
||||
const MCPProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const socketStatus = useAppSelector((state) => state.socket.status);
|
||||
|
||||
useEffect(() => {
|
||||
if (socketStatus !== 'connected') return;
|
||||
|
||||
const socket = socketService.getSocket();
|
||||
const server = getTelegramMCPServer();
|
||||
|
||||
if (server) {
|
||||
updateTelegramMCPServerSocket(socket);
|
||||
} else {
|
||||
initTelegramMCPServer(socket);
|
||||
}
|
||||
}, [socketStatus]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export default MCPProvider;
|
||||
Reference in New Issue
Block a user