Skip to main content
Glama
mitetenov

Bedolaga MCP Server

by mitetenov

Bedolaga MCP Server

Servidor MCP para obtener datos de usuario de Bedolaga Bot por Telegram ID o por user_id interno.

El servidor es de solo lectura: a través de Bedolaga MCP no se puede cambiar el saldo, crear o renovar suscripciones, aplicar códigos promocionales, procesar reembolsos, retirar fondos de referidos ni realizar otras acciones en nombre del usuario.

Migración importante (1.0.0)

A partir de la versión 1.0.0, el contrato público de las herramientas ha cambiado y los nombres antiguos se han eliminado. Actualice la configuración del cliente:

Herramienta antigua

Qué la reemplaza

bedolaga_balance

reemplazada por bedolaga_user_get

bedolaga_transactions

reemplazada por bedolaga_billing_get

bedolaga_subscription

no tiene equivalente en Bedolaga MCP. El estado real de la suscripción y el estado del panel VPN se verifican a través de un mcp-remnawave independiente, no a través de este servidor

También en 1.0.0 se eliminó la ruta HTTP obsoleta /mcp: el Streamable HTTP con sesión ahora se sirve en el endpoint raíz /, como en mcp-remnawave. Cada publicación de imagen recibe tres etiquetas: :latest, :{version} y :{sha}.

La versión 1.0.0 es el primer contrato con rutas de API correctas, resultados estructurados y un límite de responsabilidad explícito con Remnawave.

Related MCP server: Monobank MCP Server

Herramientas (Tools)

El servidor proporciona exactamente ocho herramientas disponibles a través del protocolo MCP. Todas las herramientas son de solo lectura: los datos no se modifican.

Contrato de identidad

Cada herramienta acepta exactamente uno de dos campos:

  • telegram_id — número entero, Telegram ID del usuario (positivo);

  • user_id — número entero, ID interno del usuario en Bedolaga (positivo), utilizado para tickets de oficina solo por correo electrónico.

Si no se envía ningún campo o se envían ambos, la herramienta devuelve un error invalid_input. La identidad nunca se toma del modelo: supportBot siempre fija al remitente real: un telegram_id positivo del update de Telegram autenticado, o el user_id interno de la oficina para un ticket solo por correo electrónico.

bedolaga_user_get

Obtener la cuenta y el saldo del usuario actual de Bedolaga.

Parámetros:

Parámetro

Tipo

Obligatorio

Descripción

telegram_id

int

exactamente uno de los dos

Telegram ID del usuario

user_id

int

exactamente uno de los dos

ID interno del usuario de Bedolaga (ticket solo por correo electrónico)

Campos JSON de la respuesta (data):

Campo

Tipo

Descripción

found

bool

Indica si el usuario fue encontrado

telegram_id

int | null

Telegram ID del usuario

display_name

string | null

Nombre para mostrar seguro

status

string | null

Estado de la cuenta Bedolaga

balance_kopeks

int | null

Saldo en kopeks

balance_rubles

float | null

Saldo en rublos (siempre kopeks / 100)

has_made_first_topup

bool | null

Indica si hubo un primer depósito en el pasado

has_had_paid_subscription

bool | null

Indica si hubo una compra de pago en el pasado

referral_code

string | null

Código de referido

was_referred

bool | null

El usuario llegó por invitación

promo_group

object | null

Nombre del grupo promocional y porcentajes de descuento

created_at / last_activity

string | null

Fechas de creación y última actividad

El campo promo_group contiene solo name, server_discount_percent, traffic_discount_percent, device_discount_percent.

Ejemplo de interpretación (sintético): balance_kopeks: 350000 y balance_rubles: 3500.0 significan un saldo de 3 500 rublos. has_had_paid_subscription: false significa que aún no ha habido compras de pago.

bedolaga_billing_get

Mostrar de una sola llamada el saldo, los eventos financieros recientes y los registros internos de compras de Bedolaga, para distinguir un depósito de una compra.

Parámetros:

Parámetro

Tipo

Obligatorio

Descripción

telegram_id

int

exactamente uno de los dos

Telegram ID del usuario

user_id

int

exactamente uno de los dos

ID interno del usuario de Bedolaga (ticket solo por correo electrónico)

limit

int

No

Límite de operaciones en la lista (por defecto 20, máximo 50)

Campos JSON de la respuesta (data):

Campo

Tipo

Descripción

balance_kopeks / balance_rubles

int / float | null

Saldo actual

transactions

array

Operaciones de más recientes a más antiguas, no más de limit

latest_completed_deposit

object | null

Resumen del último depósito completado

latest_completed_subscription_purchase

object | null

Resumen de la última compra de suscripción completada

purchased_after_latest_deposit

bool | null

Compra completada después del último depósito completado

bot_subscriptions

array

Registros internos de suscripciones de Bedolaga

meta

string

Explicación fija «deposit ≠ purchase»

Cada operación en transactions:

Campo

Tipo

Descripción

id

number | null

ID interno de la transacción

category

string

Categoría normalizada: deposit, subscription_purchase, gift_purchase, withdrawal, refund, failed_refund, referral_reward, poll_reward, unknown

direction

string

credit / debit / unknown

raw_type

string | null

Nombre de tipo original seguro

amount_kopeks / amount_rubles

int / float | null

Importe absoluto

payment_method

string | null

Método de pago

is_completed

bool | null

Si la operación está completada

description

string | null

Descripción

created_at / completed_at

string | null

Hora de creación y de finalización

Cada registro en bot_subscriptions contiene id, bot_record_status, bot_record_effective_status, is_trial, tariff_id, tariff_name, start_date, end_date, autopay_enabled, autopay_days_before y una note fija. El servidor prefiere la lista upstream completa subscriptions, elimina los registros duplicados por id y conserva un fallback al campo legacy único subscription. El campo se llama bot_record_status a propósito: es un registro interno de Bedolaga, no el estado del panel VPN. bot_record_effective_status también es un estado efectivo del lado del bot (calculado por el bot a partir de status y end_date), no el estado del panel.

Ejemplo de interpretación (sintético): latest_completed_deposit: {amount_kopeks: 350000} y purchased_after_latest_deposit: false — el dinero se ha acreditado en el saldo, pero no se ha completado una compra separada después del depósito.

bedolaga_referrals_get

Obtener el resumen de referidos del usuario actual.

Parámetros:

Parámetro

Tipo

Obligatorio

Descripción

telegram_id

int

exactamente uno de los dos

Telegram ID del usuario

user_id

int

exactamente uno de los dos

ID interno del usuario de Bedolaga (ticket solo por correo electrónico)

Campos JSON de la respuesta (data):

Поле

Тип

Описание

referral_code

string | null

Código de referencia del propietario de la cuenta

was_referred

bool | null

El propietario fue referido

effective_referral_commission_percent

number | null

Comisión efectiva

invited_count

int | null

Total invitados

active_referrals

int | null

Invitados activos

total_earned_kopeks / total_earned_rubles

int / float | null

Ganancias de todo el tiempo

month_earned_kopeks / month_earned_rubles

int / float | null

Ganancias del mes actual

recent_referral_rewards

array

Últimas recompensas por referidos del propietario

meta

string

Explicación fija

Se devuelven estadísticas solo del propietario de la cuenta. Telegram ID, IDs internos, username, nombres, saldo y actividad de los usuarios invitados nunca se devuelven.

bedolaga_subscription_get

Obtener los registros de suscripciones del lado del bot y las fechas del ciclo de vida (created_at, start_date, end_date, is_trial, autopay_enabled).

Parámetros: telegram_id o user_id (exactamente uno).

Devuelve has_subscription_records, active_record_count, la lista subscriptions y un meta fijo. El campo bot_record_status es un registro interno del bot, no el estado del panel VPN (el estado real se comprueba a través de Remnawave MCP).

bedolaga_tickets_get

Obtener un resumen de las propias solicitudes de soporte (id, title, status, priority, fechas de creación/actualización/cierre) sin los textos de los mensajes ni los medios.

Parámetros: telegram_id o user_id (exactamente uno), limit (por defecto 10, máximo 50).

bedolaga_payment_status_get

Obtener el historial de operaciones financieras y el estado de finalización en el sistema de contabilidad del bot (completed / not_completed / unknown).

Parámetros: telegram_id o user_id (exactamente uno), limit (por defecto 5, máximo 20).

El estado not_completed solo significa que la operación no se ha completado en la facturación del bot, no un fallo o una espera por parte de la pasarela de pago.

bedolaga_promocode_check

Comprobar la definición global del código promocional, la fecha de caducidad, la actividad, el bonus y el saldo de usos restantes.

Parámetros: code (obligatorio), telegram_id o user_id (exactamente uno para fijar la identidad).

Devuelve el código enmascarado (code_masked), el indicador globally_valid, reason_code (not_found, inactive, not_yet_valid, expired_or_exhausted, lookup_incomplete) y user_eligibility: "unknown".

bedolaga_gifts_get

Obtener el historial de compras de regalos del propietario de la cuenta.

Parámetros: telegram_id o user_id (exactamente uno), limit (por defecto 20, máximo 50).

Muestra solo el hecho de la compra de regalos (contabilidad); los tokens de los regalos, los destinatarios y el estado de activación no se revelan.

Decision table

Cómo debe usar el LLM (supportBot) los datos de Bedolaga y Remnawave según los escenarios:

Escenario

Qué se ve en Bedolaga MCP

Acción del LLM

Depósito sin compra

deposit existe, purchased_after_latest_deposit: false

Explicar que el dinero se ha abonado al saldo, pero la compra por separado no se ha completado; indicar que complete la compra desde el saldo. No afirmar que la suscripción está defectuosa

Compra con panel operativo

Existe subscription_payment completada

Comprobar el estado real del panel a través de Remnawave MCP

Compra sin registro en el panel

Existe subscription_payment completada

Escalar como una discrepancia confirmada con un breve resumen factual

Sin depósito

No hay deposit

No afirmar que el proveedor de pagos no ha cobrado el dinero (Bedolaga solo confirma la ausencia de abono en su sistema de contabilidad); escalar si el usuario informa de un cobro real

Consulta sobre referidos

bedolaga_referrals_get

Enrutar solo a Bedolaga MCP

Consulta sobre nodo / HWID

Enrutar solo a Remnawave MCP (Bedolaga no conoce el estado de los nodos ni de los dispositivos)

Formato del resultado

Cada herramienta devuelve JSON en el content de MCP en texto con una envoltura común:

  • éxito: ok: true, source: "bedolaga-mcp", tool, data, meta;

  • error: ok: false, source, tool, error.code, error.message seguro, error.retryable.

El cuerpo bruto de la respuesta de la API de Bedolaga y las excepciones de Python del modelo no se devuelven. Las herramientas no devuelven email, enlace de suscripción, crypto link, claves, IDs de pago externos, identificadores de receipt, identificadores de Remnawave ni datos personales de los referidos.

Error codes

Código

Reintentable

Cuándo ocurre

invalid_input

no

Se han pasado ambos campos de identidad o ninguno; valor no válido

not_configured

no

Falta o es incorrecta la configuración del entorno

identity_unavailable

no

La identidad no se puede asociar con un usuario de Bedolaga

user_not_found

no

Usuario no encontrado (upstream 404)

unauthorized

no

Credenciales de API incorrectas o ausentes (upstream 401/403)

rate_limited

Se ha alcanzado el rate limit (upstream 429)

upstream_timeout

Tiempo de espera agotado o fallo de red antes de la respuesta

upstream_unavailable

Upstream no disponible (5xx o error irrecuperable)

invalid_upstream_response

no

El cuerpo de la respuesta no es un JSON válido o no es un objeto

internal_error

no

Error interno inesperado

El mensaje de usuario se construye únicamente a partir del error.message seguro y nunca revela el body HTTP ni la URL interna.

Transportes

El servidor admite dos transportes en un mismo server factory y un mismo registro de herramientas:

Transporte

Launcher

Puerto

Protocolo

Streamable HTTP (principal)

http_server.py

3100 por defecto

MCP de doble era en / (ver más abajo), GET /health, DELETE / (solo sesiones legacy)

Stdio

bedolaga_server.py

Handshake MCP stdio (el mismo factory)

El endpoint / es único, pero atiende dos eras del protocolo a la vez; el SDK v2 determina por sí mismo a qué era pertenece cada solicitud mediante la cabecera MCP-Protocol-Version:

  • Protocolo moderno 2026-07-28 — stateless/sessionless. Cada POST a / es autosuficiente: el servidor nunca emite Mcp-Session-Id ni almacena estado entre solicitudes. Los clientes oficiales de MCP SDK v2 (ver «Cliente oficial de SDK v2» más abajo) usan este modo automáticamente.

  • Clientes legacy con initialize-handshake (protocolos hasta 2025-11-25, incluido 2024-11-05) reciben la cabecera Mcp-Session-Id en respuesta a initialize y deben enviarla en todas las solicitudes posteriores. DELETE / con esa cabecera finaliza exactamente esa sesión; no afecta a otras sesiones ni a los clientes modernos.

GET /health devuelve el liveness del proceso y la versión del servidor, sin revelar la configuración ni los secretos.

Compatibilidad de versiones

Componente

Versión

Bedolaga Bot API (upstream)

commit 49b05d5, aplicación 4.1.0

bedolaga-mcp

1.2.0

Python MCP SDK (mcp)

2.0.0

Protocolos MCP compatibles

2026-07-28 (moderno, stateless) + legacy initialize-handshake hasta 2025-11-25

supportBot

2.0.1

mcp-remnawave

v3.2.1

El contrato de las herramientas se ha verificado contra el commit upstream indicado y el estándar de mcp-remnawave v3.2.1.

Requisitos

  • Python 3.11+

  • Docker (opcional)

  • Bedolaga Bot desplegado con Web API

  • Clave de API de Bedolaga (se entrega en el panel de administración del bot)

Inicio rápido

1. Clonar

git clone https://github.com/mitetenov/bedolaga-mcp.git
cd bedolaga-mcp

2. Configurar

cp .env.example .env
# Заполнить BEDOLAGA_API_URL и BEDOLAGA_API_KEY

3. Ejecutar

Streamable HTTP (recomendado):

# Установить зависимости
pip install -r requirements.txt

# Запустить HTTP-сервер
BEDOLAGA_API_URL=https://your-bot.example.com \
BEDOLAGA_API_KEY=your-key \
python3 http_server.py

El servidor escuchará en http://0.0.0.0:3100, el endpoint MCP es la raíz /.

Stdio:

BEDOLAGA_API_URL=https://your-bot.example.com \
BEDOLAGA_API_KEY=your-key \
python3 bedolaga_server.py

Mediante Docker:

docker compose up -d

La imagen de Docker por defecto ejecuta el servidor Streamable HTTP en el puerto 3100.

Conexión como servidor MCP

Streamable HTTP

El servidor está disponible por HTTP en el puerto 3100, el endpoint es la raíz / (http://localhost:3100).

Hermes Agent

# ~/.hermes/config.yaml
mcp_servers:
  bedolaga:
    transport: streamable-http
    url: "http://localhost:3100"
    env:
      BEDOLAGA_API_URL: "https://your-bot.example.com"
      BEDOLAGA_API_KEY: "your-api-key"

Claude Desktop

{
  "mcpServers": {
    "bedolaga": {
      "type": "streamableHttp",
      "url": "http://localhost:3100"
    }
  }
}

Cursor / VS Code

{
  "mcpServers": {
    "bedolaga": {
      "transport": "streamable-http",
      "url": "http://localhost:3100"
    }
  }
}

Comprobación mediante curl (legacy compatibility check)

El JSON-RPC crudo mediante curl usa legacy initialize-handshake (protocolo 2024-11-05): es una comprobación manual de compatibilidad inversa, no la forma en que se comunican los clientes modernos. Los clientes modernos de MCP SDK v2 negocian el protocolo 2026-07-28 automáticamente y no reciben Mcp-Session-Id (ver «Cliente oficial de SDK v2 (protocolo moderno)» más abajo).

# Liveness
curl -s http://localhost:3100/health

# Legacy initialize handshake (получить session ID; работает для протоколов вплоть до 2025-11-25)
curl -s -X POST http://localhost:3100/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}},"id":1}' \
  -D - | grep -i mcp-session-id

# Список инструментов (с session ID)
curl -s -X POST http://localhost:3100/ \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: <SESSION_ID>" \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":2}'

# Вызов инструментов
# Пользователь и баланс
curl -s -X POST http://localhost:3100/ \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: <SESSION_ID>" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"bedolaga_user_get","arguments":{"telegram_id":123456789}},"id":3}'

# Биллинг (операции и внутренние записи покупок)
curl -s -X POST http://localhost:3100/ \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: <SESSION_ID>" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"bedolaga_billing_get","arguments":{"telegram_id":123456789,"limit":20}},"id":4}'

# Реферальная сводка
curl -s -X POST http://localhost:3100/ \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: <SESSION_ID>" \
  -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"bedolaga_referrals_get","arguments":{"telegram_id":123456789}},"id":5}'

# Завершение legacy-сессии (для современного протокола 2026-07-28 не требуется и не применяется)
curl -s -X DELETE http://localhost:3100/ \
  -H "Mcp-Session-Id: <SESSION_ID>"

Cliente oficial de SDK v2 (protocolo moderno)

El cliente oficial de Python MCP SDK v2 (mcp==2.0.0) negocia el protocolo por sí mismo — 2026-07-28 si el servidor lo soporta, de lo contrario legacy-handshake — sin construir manualmente _meta ni cabeceras:

import asyncio

from mcp.client.client import Client


async def main() -> None:
    async with Client("http://localhost:3100/", mode="auto") as client:
        print("negotiated protocol:", client.protocol_version)  # "2026-07-28" against this server

        tools = await client.list_tools()
        print([tool.name for tool in tools.tools])

        result = await client.call_tool(
            "bedolaga_user_get", {"telegram_id": 123456789}
        )
        print(result.content)


asyncio.run(main())

mode="auto" — es la misma negociación que utiliza supportBot: el cliente decide por sí mismo si tiene delante un servidor moderno o legacy, y no exige que el código llamante conozca la era del protocolo de antemano.

Transporte Stdio

Hermes Agent

# ~/.hermes/config.yaml
mcp_servers:
  bedolaga:
    command: "python3"
    args: ["/path/to/bedolaga-mcp/bedolaga_server.py"]
    env:
      BEDOLAGA_API_URL: "https://your-bot.example.com"
      BEDOLAGA_API_KEY: "your-api-key"

Claude Desktop

{
  "mcpServers": {
    "bedolaga": {
      "command": "python3",
      "args": ["/path/to/bedolaga-mcp/bedolaga_server.py"],
      "env": {
        "BEDOLAGA_API_URL": "https://your-bot.example.com",
        "BEDOLAGA_API_KEY": "your-api-key"
      }
    }
  }
}

Cursor / VS Code

Añadir a .cursor/mcp.json o settings.json:

{
  "mcpServers": {
    "bedolaga": {
      "command": "python3",
      "args": ["/path/to/bedolaga-mcp/bedolaga_server.py"],
      "env": {
        "BEDOLAGA_API_URL": "https://your-bot.example.com",
        "BEDOLAGA_API_KEY": "your-api-key"
      }
    }
  }
}

Gestión de sesiones

El transporte Streamable HTTP es dual-era, y las sesiones solo se aplican a una de las dos eras:

  • Legacy initialize-handshake (protocolos hasta 2025-11-25): después de initialize, el servidor devuelve la cabecera Mcp-Session-Id, que el cliente debe enviar en todas las solicitudes posteriores. DELETE / con esta cabecera finaliza solo la sesión indicada; un cliente no puede finalizar ni reutilizar la sesión de otro.

  • Protocolo moderno 2026-07-28: stateless/sessionless — el servidor nunca emite Mcp-Session-Id, y DELETE / no es necesario ni se aplica para tales clientes.

Variables de entorno

Variable

Descripción

BEDOLAGA_API_URL

URL de Bedolaga Web API

BEDOLAGA_API_KEY

Clave API de Bedolaga (se transmite upstream en X-API-Key)

MCP_HTTP_HOST

Dirección para bind (por defecto: 0.0.0.0)

MCP_HTTP_PORT

Puerto del servidor HTTP (por defecto: 3100)

BEDOLAGA_TIMEOUT_MS

Timeout upstream en milisegundos (por defecto: 10000)

Por compatibilidad, se aceptan las variables legacy HOST/PORT si MCP_HTTP_HOST/MCP_HTTP_PORT no están definidas.

API Upstream

Bedolaga Web API: X-API-Key en la cabecera. Rutas utilizadas:

  • GET /users/by-telegram-id/{telegram_id} — usuario por Telegram ID;

  • GET /users/{user_id} — usuario por ID interno (tickets email-only);

  • GET /transactions?user_id=... — operaciones con filtros y paginación;

  • GET /partners/referrers/{user_id} — tarjeta de referido.

Más información: https://docs.bedolagam.ru

Limitaciones de la primera versión

  • No hay intentos de pago provider-specific. Bedolaga solo devuelve operaciones que se han convertido en registros en la tabla común transactions. Los intentos brutos del proveedor de pagos que no se convirtieron en un registro no están disponibles.

  • No hay lectura de la cesta de Redis del usuario. El Web API actual no proporciona un endpoint read-only seguro para esto. El problema actual de «recargó, pero no hay compra» se diagnostica de manera fiable por la diferencia entre deposit y subscription_payment (ver decision table).

  • La búsqueda email-only es compatible. Para tickets de la cuenta sin Telegram ID, el servidor acepta el user_id interno (entero positivo) y lo resuelve mediante GET /users/{user_id}. supportBot fija el user_id interno de la cuenta (valor absoluto de la clave de conversación sintética negativa) — para estos tickets, los datos de Bedolaga están disponibles, y las herramientas de Remnawave devuelven identity_unavailable, porque ese usuario no tiene identidad de Telegram ni un registro probado en el panel.

Reversión (rollback)

Desactivar BEDOLAGA_MCP_ENABLED=false en supportBot lo devuelve al modo Remnawave-only: Bedolaga MCP no se conecta, sus herramientas desaparecen de la allowlist, y el procesamiento de tickets por webhook/poller (BEDOLAGA_ENABLED) sigue siendo independiente. La reversión no toca la base de usuarios ni los datos financieros — Bedolaga MCP es read-only y no almacena estado.

Reversión de la imagen bedolaga-mcp a la etiqueta 1.1.0 (la última versión antes de la migración al MCP SDK v2, solo la era legacy de Streamable HTTP) también es segura: el cliente supportBot en MCP SDK v2 cambia automáticamente (auto-fallback) al legacy initialize-handshake si el servidor no responde al protocolo moderno 2026-07-28, por lo que las herramientas de Bedolaga MCP siguen disponibles sin configuración adicional.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    Not graded
    maintenance
    Provides user balance information by connecting to a backend service through the users_balance tool. Built with TypeScript and Express for retrieving financial data.
    -
  • A
    license
    A
    quality
    A
    maintenance
    Enables integration with Monobank API to check currency exchange rates, view account balances, and retrieve transaction statements through natural language queries.
    3
    29 npm
    9
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interaction with Telegram Bot API for sending messages, photos, editing messages, answering callbacks, and fetching updates.
    MIT