Skip to content

Acceso MCP ​

源码版本v2026.7.20

Responsabilidad ​

MCP (Model Context Protocol) es el protocolo estándar para conectar herramientas (tool)/fuentes de datos externas. Hermes actúa tanto como client MCP, consumiendo herramientas expuestas por servidores MCP externos (puenteadas a ToolRegistry vía mcp_tool), como como server MCP (mcp_serve.py), exponiendo sus propias herramientas a otros clientes agent. Esta capa permite a Hermes integrar capacidades de terceros de forma ilimitada.

Motivo de diseño ​

¿Por qué MCP en lugar de reinventar un protocolo de herramientas propio? MCP es un estándar abierto impulsado por Anthropic; Claude Code, Cursor y Codex ya lo han adoptado, así que el ecosistema está listo. Hermes como client puede reutilizar decenas de servers MCP comunitarios; como server puede ser accionado por cualquier cliente MCP — interop bidireccional gratis. Inventar un protocolo propio pegaría mejor con el ToolRegistry interno, pero se perdería todo un ecosistema.

Archivos clave ​

Flujo de datos ​

  1. En el arranque (o en carga caliente durante la ejecución) se lee la configuración MCP; mcp_tool se conecta al server MCP externo.
  2. Obtiene el listado de herramientas expuestas por el server y registra cada una vía registry.register:365 como un ToolEntry (con aislamiento de namespace, para no chocar con las herramientas internas).
  3. La admisión de namespace la controla register_plugin_override_policy:316.
  4. El bucle principal recoge el schema, despacha el tool_call y devuelve el resultado igual que con las herramientas internas.
  5. Sentido inverso: Hermes puede, vía mcp_serve.py, exponer sus propias herramientas a otros clientes MCP, para que las invoquen editores u otros agentes.

Código clave ​

Puenteo de herramientas MCP a ToolRegistry ​

Tras conectar, _register_server_tools convierte cada MCP tool en un schema y la registra; el handler es una clausura que ata el nombre del server y el timeout:

python
for mcp_tool in server._tools:
    if not _should_register(mcp_tool.name):
        continue
    schema = _convert_mcp_schema(name, mcp_tool)
    tool_name_prefixed = schema["name"]
    existing_toolset = registry.get_toolset_for_tool(tool_name_prefixed)
    if existing_toolset and not existing_toolset.startswith("mcp-"):
        logger.warning("MCP '%s': tool '%s' collides with built-in — skipping",
                       name, mcp_tool.name)
        continue
    registry.register(name=tool_name_prefixed, toolset=toolset_name, schema=schema,
                      handler=_make_tool_handler(name, mcp_tool.name, server.tool_timeout),
                      check_fn=_make_check_fn(name), is_async=False,
                      description=schema["description"])

El nombre se prefija para que varios servers no colisionen entre sí; y el conflicto con herramientas internas se trata con honestidad: si el prefijo choca con un toolset no mcp-, se salta en lugar de reemplazar silenciosamente.

Puenteo de la llamada a herramienta ​

Cuando el bucle principal invoca una herramienta MCP, el handler del registro reenvía a esta clausura. Primero mira si el circuit breaker está abierto; luego toma la conexión (reconecta stdio si hace falta); después lanza la llamada síncrona al event loop en segundo plano y espera la respuesta MCP:

python
def _handler(args: dict, **kwargs) -> str:
    if _server_error_counts.get(server_name, 0) >= _CIRCUIT_BREAKER_THRESHOLD:
        age = time.monotonic() - _server_breaker_opened_at.get(server_name, 0.0)
        if age < _CIRCUIT_BREAKER_COOLDOWN_SEC:
            remaining = max(1, int(_CIRCUIT_BREAKER_COOLDOWN_SEC - age))
            return json.dumps({"error": (
                f"MCP server '{server_name}' unreachable after "
                f"{_CIRCUIT_BREAKER_THRESHOLD} failures. Retry in ~{remaining}s. "
                f"Do NOT retry this tool yet — use alternative approaches.")},
                ensure_ascii=False)
    server = _get_connected_server_for_call(server_name)
    if not server:
        _bump_server_error(server_name)
        return json.dumps({"error": f"MCP server '{server_name}' is not connected"},
                          ensure_ascii=False)

El mensaje de error incluye expresamente «Do NOT retry this tool yet» — va dirigido al modelo, para que no siga quemando iteraciones en un breaker abierto.

Inverso: exponer a Hermes como server MCP ​

mcp_serve.py levanta un server stdio con FastMCP y registra las herramientas del puente de mensajería. Cualquier cliente MCP puede hacer list/read/send de mensajes:

python
mcp = FastMCP("hermes", instructions=(
    "Hermes Agent messaging bridge. Use these tools to interact with "
    "conversations across Telegram, Discord, Slack, WhatsApp, Signal, Matrix."))

@mcp.tool()
def conversations_list(platform=None, limit=50, search=None) -> str:
    """List active messaging conversations across connected platforms."""
    ...

El decorador @mcp.tool() registra solo con eso; no hace falta escribir el schema a mano — FastMCP lo infiere de las anotaciones de tipo.

Límites y fallos ​

  • El server MCP se cae: si el subproceso muere o el endpoint HTTP cae, MCPServerTask reconecta solo; cuando los fallos acumulados superan el umbral, salta el circuit breaker y los siguientes tool_call devuelven error directamente. Tras un cooldown, el breaker semiabre y deja pasar una sonda.
  • Recogida de stdio server: un proceso ocioso demasiado tiempo se recoge para ahorrar recursos; la próxima llamada pasa por _request_lazy_reconnect (despierta y reenvía), con una latencia de decenas de ms a segundos.
  • Colisión de nombres: las herramientas MCP se prefijan y, si chocan con un toolset interno, se saltan; dos servers MCP con el mismo nombre se permiten (rama both_mcp) con un log a debug.

Resumen ​

MCP es el «bus de capacidades externas» de Hermes. mcp_tool hace que las herramientas externas entren al pool unificado de forma transparente; mcp_serve.py permite a su vez que Hermes sea invocado desde fuera. Al pasar todo por ToolRegistry, el bucle principal no necesita distinguir si una herramienta es interna o viene de MCP.

Sitio de aprendizaje comunitario no oficial. Basado en el código fuente de NousResearch/hermes-agent (licencia MIT). · Privacidad · Términos · Acerca de