/news spawns claude-code as a local subprocess (single pod, no cross-container exec on K3s); Claude delivers the digest itself via the send_to_telegram MCP tool. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
83 lines
2.8 KiB
Dart
83 lines
2.8 KiB
Dart
import 'dart:convert';
|
||
import 'dart:io';
|
||
import 'package:mcp_dart/mcp_dart.dart';
|
||
|
||
final _token = Platform.environment['TELEGRAM_BOT_TOKEN'] ?? '';
|
||
final _http = HttpClient();
|
||
|
||
CallToolResult _ok(String text) => CallToolResult(content: [TextContent(text: text)]);
|
||
|
||
CallToolResult _err(String text) =>
|
||
CallToolResult(content: [TextContent(text: 'Ошибка: $text')], isError: true);
|
||
|
||
List<String> _splitMessage(String text) {
|
||
const maxLen = 4000;
|
||
if (text.length <= maxLen) return [text];
|
||
final chunks = <String>[];
|
||
var start = 0;
|
||
while (start < text.length) {
|
||
final end = (start + maxLen).clamp(0, text.length);
|
||
chunks.add(text.substring(start, end));
|
||
start = end;
|
||
}
|
||
return chunks;
|
||
}
|
||
|
||
Future<void> _sendMessage(int chatId, String text) async {
|
||
for (final chunk in _splitMessage(text)) {
|
||
final req = await _http.postUrl(
|
||
Uri.parse('https://api.telegram.org/bot$_token/sendMessage'),
|
||
);
|
||
req.headers.contentType = ContentType.json;
|
||
req.write(jsonEncode({
|
||
'chat_id': chatId,
|
||
'text': chunk,
|
||
'parse_mode': 'Markdown',
|
||
}));
|
||
final res = await req.close();
|
||
final body = await res.transform(utf8.decoder).join();
|
||
if (res.statusCode >= 400) {
|
||
throw Exception('Telegram sendMessage → ${res.statusCode}: $body');
|
||
}
|
||
}
|
||
}
|
||
|
||
void main() {
|
||
final server = McpServer(
|
||
const Implementation(name: 'agent-news', version: '0.1.0'),
|
||
options: const McpServerOptions(
|
||
capabilities: ServerCapabilities(tools: ServerCapabilitiesTools()),
|
||
),
|
||
);
|
||
|
||
server.registerTool(
|
||
'send_to_telegram',
|
||
description:
|
||
'Отправляет готовый текст (Markdown) в указанный Telegram-чат. '
|
||
'Единственный способ доставить результат ресёрча пользователю — '
|
||
'вызывай этот тул с финальным оформленным текстом.',
|
||
inputSchema: JsonSchema.object(
|
||
properties: {
|
||
'chat_id': JsonSchema.integer(description: 'ID чата Telegram, куда отправить сообщение'),
|
||
'text': JsonSchema.string(description: 'Готовый текст в Markdown-разметке Telegram'),
|
||
},
|
||
required: ['chat_id', 'text'],
|
||
),
|
||
callback: (args, _) async {
|
||
if (_token.isEmpty) {
|
||
return _err('TELEGRAM_BOT_TOKEN не задан в окружении MCP-сервера');
|
||
}
|
||
try {
|
||
final chatId = args['chat_id'] as int;
|
||
final text = args['text'].toString();
|
||
await _sendMessage(chatId, text);
|
||
return _ok('Отправлено в чат $chatId (${text.length} символов).');
|
||
} catch (e) {
|
||
return _err('$e');
|
||
}
|
||
},
|
||
);
|
||
|
||
server.connect(StdioServerTransport());
|
||
}
|