#!/usr/bin/env python3
"""
MCP Server для Beget Hosting API
Управление сайтами, доменами, MySQL, FTP, Cron, DNS, бэкапами через Claude Code
GitHub: https://github.com/yasg1988/mcp-beget
Документация Beget API: https://beget.com/ru/kb/api/beget-api
"""
import json
import os
import urllib.parse
from typing import Any, Optional
import requests
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
# Конфигурация из переменных окружения (НЕ хранить в коде!)
BEGET_LOGIN = os.environ.get('BEGET_LOGIN', '')
BEGET_PASSWORD = os.environ.get('BEGET_PASSWORD', '')
BEGET_API_URL = 'https://api.beget.com/api'
def beget_request(section: str, method: str, params: Optional[dict] = None) -> dict:
"""
Выполняет запрос к Beget API
Args:
section: Раздел API (site, domain, mysql, ftp, cron, dns, backup, mail, stat, user)
method: Метод API (getList, add, delete и т.д.)
params: Дополнительные параметры запроса
Returns:
Ответ API в формате dict
"""
if not BEGET_LOGIN or not BEGET_PASSWORD:
return {
'success': False,
'error': 'BEGET_LOGIN и BEGET_PASSWORD не настроены. Установите переменные окружения.'
}
url = f"{BEGET_API_URL}/{section}/{method}"
query_params = {
'login': BEGET_LOGIN,
'passwd': BEGET_PASSWORD,
'output_format': 'json'
}
if params:
query_params['input_format'] = 'json'
query_params['input_data'] = json.dumps(params)
try:
response = requests.get(url, params=query_params, timeout=30)
response.raise_for_status()
data = response.json()
# Beget возвращает {status: "success/error", answer: ...}
if data.get('status') == 'success':
return {'success': True, 'data': data.get('answer')}
else:
return {'success': False, 'error': data.get('answer', 'Unknown error')}
except requests.exceptions.RequestException as e:
return {'success': False, 'error': str(e)}
except json.JSONDecodeError as e:
return {'success': False, 'error': f'JSON decode error: {e}'}
# Создаём MCP сервер
server = Server("beget-api")
@server.list_tools()
async def list_tools():
"""Список доступных инструментов"""
return [
# === SITE (Сайты) ===
Tool(
name="beget_site_list",
description="Получить список всех сайтов на хостинге Beget с привязанными доменами",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_site_add",
description="Создать новый сайт на хостинге Beget",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Имя сайта (будет создана папка name/public_html)"
}
},
"required": ["name"]
}
),
Tool(
name="beget_site_delete",
description="Удалить сайт с хостинга Beget",
inputSchema={
"type": "object",
"properties": {
"site_id": {
"type": "integer",
"description": "ID сайта (получить через beget_site_list)"
}
},
"required": ["site_id"]
}
),
Tool(
name="beget_site_link_domain",
description="Привязать домен к сайту на Beget",
inputSchema={
"type": "object",
"properties": {
"site_id": {"type": "integer", "description": "ID сайта"},
"domain_id": {"type": "integer", "description": "ID домена"}
},
"required": ["site_id", "domain_id"]
}
),
Tool(
name="beget_site_unlink_domain",
description="Отвязать домен от сайта на Beget",
inputSchema={
"type": "object",
"properties": {
"domain_id": {"type": "integer", "description": "ID домена"}
},
"required": ["domain_id"]
}
),
# === DOMAIN (Домены) ===
Tool(
name="beget_domain_list",
description="Получить список всех доменов на аккаунте Beget",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_domain_add",
description="Добавить виртуальный домен (без регистрации) на Beget",
inputSchema={
"type": "object",
"properties": {
"hostname": {
"type": "string",
"description": "Имя домена (например: example.com)"
},
"zone_id": {
"type": "integer",
"description": "ID зоны домена (получить через beget_domain_zones). По умолчанию: 1 (.ru)"
}
},
"required": ["hostname"]
}
),
Tool(
name="beget_domain_delete",
description="Удалить домен с аккаунта Beget",
inputSchema={
"type": "object",
"properties": {
"domain_id": {"type": "integer", "description": "ID домена"}
},
"required": ["domain_id"]
}
),
Tool(
name="beget_domain_zones",
description="Получить список доступных доменных зон на Beget",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_domain_subdomains",
description="Получить список поддоменов для домена",
inputSchema={
"type": "object",
"properties": {
"domain_id": {"type": "integer", "description": "ID домена"}
},
"required": ["domain_id"]
}
),
Tool(
name="beget_domain_php_version",
description="Получить версию PHP для домена",
inputSchema={
"type": "object",
"properties": {
"full_fqdn": {"type": "string", "description": "Полное имя домена (например: site.ru)"}
},
"required": ["full_fqdn"]
}
),
Tool(
name="beget_domain_change_php",
description="Изменить версию PHP для домена",
inputSchema={
"type": "object",
"properties": {
"full_fqdn": {"type": "string", "description": "Полное имя домена"},
"php_version": {"type": "string", "description": "Версия PHP (например: 8.1, 8.2, 8.3)"}
},
"required": ["full_fqdn", "php_version"]
}
),
# === MYSQL ===
Tool(
name="beget_mysql_list",
description="Получить список баз данных MySQL на Beget",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_mysql_add",
description="Создать новую базу данных MySQL на Beget",
inputSchema={
"type": "object",
"properties": {
"suffix": {
"type": "string",
"description": "Суффикс имени БД (будет login_suffix)"
},
"password": {
"type": "string",
"description": "Пароль для доступа к БД"
}
},
"required": ["suffix", "password"]
}
),
Tool(
name="beget_mysql_delete",
description="Удалить базу данных MySQL",
inputSchema={
"type": "object",
"properties": {
"suffix": {"type": "string", "description": "Суффикс имени БД"}
},
"required": ["suffix"]
}
),
Tool(
name="beget_mysql_change_password",
description="Изменить пароль доступа к базе MySQL",
inputSchema={
"type": "object",
"properties": {
"suffix": {"type": "string", "description": "Суффикс имени БД"},
"password": {"type": "string", "description": "Новый пароль"}
},
"required": ["suffix", "password"]
}
),
# === FTP ===
Tool(
name="beget_ftp_list",
description="Получить список FTP-аккаунтов на Beget",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_ftp_add",
description="Создать дополнительный FTP-аккаунт",
inputSchema={
"type": "object",
"properties": {
"suffix": {"type": "string", "description": "Суффикс логина (будет login_suffix)"},
"homedir": {"type": "string", "description": "Домашняя директория (например: site.ru/public_html)"},
"password": {"type": "string", "description": "Пароль FTP"}
},
"required": ["suffix", "homedir", "password"]
}
),
Tool(
name="beget_ftp_delete",
description="Удалить FTP-аккаунт",
inputSchema={
"type": "object",
"properties": {
"suffix": {"type": "string", "description": "Суффикс логина FTP"}
},
"required": ["suffix"]
}
),
Tool(
name="beget_ftp_change_password",
description="Изменить пароль FTP-аккаунта",
inputSchema={
"type": "object",
"properties": {
"suffix": {"type": "string", "description": "Суффикс логина FTP"},
"password": {"type": "string", "description": "Новый пароль"}
},
"required": ["suffix", "password"]
}
),
# === CRON ===
Tool(
name="beget_cron_list",
description="Получить список Cron-задач на Beget",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_cron_add",
description="Добавить Cron-задачу",
inputSchema={
"type": "object",
"properties": {
"minutes": {"type": "string", "description": "Минуты (0-59, * или */N)"},
"hours": {"type": "string", "description": "Часы (0-23, * или */N)"},
"days": {"type": "string", "description": "Дни месяца (1-31, * или */N)"},
"months": {"type": "string", "description": "Месяцы (1-12, * или */N)"},
"weekdays": {"type": "string", "description": "Дни недели (0-7, 0 и 7 = воскресенье)"},
"command": {"type": "string", "description": "Команда для выполнения"}
},
"required": ["minutes", "hours", "days", "months", "weekdays", "command"]
}
),
Tool(
name="beget_cron_delete",
description="Удалить Cron-задачу",
inputSchema={
"type": "object",
"properties": {
"task_id": {"type": "integer", "description": "ID задачи"}
},
"required": ["task_id"]
}
),
# === DNS ===
Tool(
name="beget_dns_get",
description="Получить DNS-записи для домена",
inputSchema={
"type": "object",
"properties": {
"fqdn": {"type": "string", "description": "Имя домена (например: site.ru)"}
},
"required": ["fqdn"]
}
),
Tool(
name="beget_dns_set",
description="Установить DNS-запись для домена",
inputSchema={
"type": "object",
"properties": {
"fqdn": {"type": "string", "description": "Имя домена"},
"record_type": {"type": "string", "description": "Тип записи (A, AAAA, MX, TXT, CNAME, NS, SRV)"},
"record_data": {"type": "string", "description": "Данные записи"}
},
"required": ["fqdn", "record_type", "record_data"]
}
),
# === BACKUP ===
Tool(
name="beget_backup_files_list",
description="Получить список доступных файловых бэкапов",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_backup_mysql_list",
description="Получить список доступных бэкапов MySQL",
inputSchema={"type": "object", "properties": {}}
),
Tool(
name="beget_backup_restore_file",
description="Восстановить файлы из бэкапа",
inputSchema={
"type": "object",
"properties": {
"backup_id": {"type": "integer", "description": "ID бэкапа"},
"paths": {
"type": "array",
"items": {"type": "string"},
"description": "Список путей для восстановления"
}
},
"required": ["backup_id", "paths"]
}
),
Tool(
name="beget_backup_restore_mysql",
description="Восстановить базу MySQL из бэкапа",
inputSchema={
"type": "object",
"properties": {
"backup_id": {"type": "integer", "description": "ID бэкапа"},
"databases": {
"type": "array",
"items": {"type": "string"},
"description": "Список баз для восстановления"
}
},
"required": ["backup_id", "databases"]
}
),
# === MAIL ===
Tool(
name="beget_mail_list",
description="Получить список почтовых ящиков на домене",
inputSchema={
"type": "object",
"properties": {
"domain": {"type": "string", "description": "Домен (например: site.ru)"}
},
"required": ["domain"]
}
),
Tool(
name="beget_mail_create",
description="Создать почтовый ящик",
inputSchema={
"type": "object",
"properties": {
"domain": {"type": "string", "description": "Домен"},
"mailbox": {"type": "string", "description": "Имя ящика (часть до @)"},
"password": {"type": "string", "description": "Пароль"}
},
"required": ["domain", "mailbox", "password"]
}
),
# === STAT ===
Tool(
name="beget_stat_load",
description="Получить статистику нагрузки сайтов за последний месяц",
inputSchema={"type": "object", "properties": {}}
),
# === USER ===
Tool(
name="beget_account_info",
description="Получить информацию об аккаунте Beget (баланс, тариф, и т.д.)",
inputSchema={"type": "object", "properties": {}}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""Обработчик вызовов инструментов"""
result = None
# === SITE ===
if name == "beget_site_list":
result = beget_request('site', 'getList')
elif name == "beget_site_add":
result = beget_request('site', 'add', {'name': arguments['name']})
elif name == "beget_site_delete":
result = beget_request('site', 'delete', {'id': arguments['site_id']})
elif name == "beget_site_link_domain":
result = beget_request('site', 'linkDomain', {
'domain_id': arguments['domain_id'],
'site_id': arguments['site_id']
})
elif name == "beget_site_unlink_domain":
result = beget_request('site', 'unlinkDomain', {'domain_id': arguments['domain_id']})
# === DOMAIN ===
elif name == "beget_domain_list":
result = beget_request('domain', 'getList')
elif name == "beget_domain_add":
params = {'hostname': arguments['hostname']}
if 'zone_id' in arguments:
params['zone_id'] = arguments['zone_id']
result = beget_request('domain', 'addVirtual', params)
elif name == "beget_domain_delete":
result = beget_request('domain', 'delete', {'id': arguments['domain_id']})
elif name == "beget_domain_zones":
result = beget_request('domain', 'getZoneList')
elif name == "beget_domain_subdomains":
result = beget_request('domain', 'getSubdomainList', {'domain_id': arguments['domain_id']})
elif name == "beget_domain_php_version":
result = beget_request('domain', 'getPhpVersion', {'full_fqdn': arguments['full_fqdn']})
elif name == "beget_domain_change_php":
result = beget_request('domain', 'changePhpVersion', {
'full_fqdn': arguments['full_fqdn'],
'php_version': arguments['php_version']
})
# === MYSQL ===
elif name == "beget_mysql_list":
result = beget_request('mysql', 'getList')
elif name == "beget_mysql_add":
result = beget_request('mysql', 'addDb', {
'suffix': arguments['suffix'],
'password': arguments['password']
})
elif name == "beget_mysql_delete":
result = beget_request('mysql', 'dropDb', {'suffix': arguments['suffix']})
elif name == "beget_mysql_change_password":
result = beget_request('mysql', 'changeAccessPassword', {
'suffix': arguments['suffix'],
'password': arguments['password']
})
# === FTP ===
elif name == "beget_ftp_list":
result = beget_request('ftp', 'getList')
elif name == "beget_ftp_add":
result = beget_request('ftp', 'add', {
'suffix': arguments['suffix'],
'homedir': arguments['homedir'],
'password': arguments['password']
})
elif name == "beget_ftp_delete":
result = beget_request('ftp', 'delete', {'suffix': arguments['suffix']})
elif name == "beget_ftp_change_password":
result = beget_request('ftp', 'changePassword', {
'suffix': arguments['suffix'],
'password': arguments['password']
})
# === CRON ===
elif name == "beget_cron_list":
result = beget_request('cron', 'getList')
elif name == "beget_cron_add":
result = beget_request('cron', 'add', {
'minutes': arguments['minutes'],
'hours': arguments['hours'],
'days': arguments['days'],
'months': arguments['months'],
'weekdays': arguments['weekdays'],
'command': arguments['command']
})
elif name == "beget_cron_delete":
result = beget_request('cron', 'delete', {'id': arguments['task_id']})
# === DNS ===
elif name == "beget_dns_get":
result = beget_request('dns', 'getData', {'fqdn': arguments['fqdn']})
elif name == "beget_dns_set":
result = beget_request('dns', 'changeRecords', {
'fqdn': arguments['fqdn'],
'records': {arguments['record_type']: arguments['record_data']}
})
# === BACKUP ===
elif name == "beget_backup_files_list":
result = beget_request('backup', 'getFileBackupList')
elif name == "beget_backup_mysql_list":
result = beget_request('backup', 'getMysqlBackupList')
elif name == "beget_backup_restore_file":
result = beget_request('backup', 'restoreFile', {
'backup_id': arguments['backup_id'],
'paths': arguments['paths']
})
elif name == "beget_backup_restore_mysql":
result = beget_request('backup', 'restoreMysql', {
'backup_id': arguments['backup_id'],
'bases': arguments['databases']
})
# === MAIL ===
elif name == "beget_mail_list":
result = beget_request('mail', 'getMailboxList', {'domain': arguments['domain']})
elif name == "beget_mail_create":
result = beget_request('mail', 'createMailbox', {
'domain': arguments['domain'],
'mailbox': arguments['mailbox'],
'mailbox_password': arguments['password']
})
# === STAT ===
elif name == "beget_stat_load":
result = beget_request('stat', 'getSiteListLoad')
# === USER ===
elif name == "beget_account_info":
result = beget_request('user', 'getAccountInfo')
else:
result = {'success': False, 'error': f'Unknown tool: {name}'}
return [TextContent(
type="text",
text=json.dumps(result, ensure_ascii=False, indent=2)
)]
async def main():
"""Запуск сервера"""
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
import asyncio
asyncio.run(main())