Volver al inicio

Proxy de API de Claude para OpenClaw: código y uso de herramientas

Servidor proxy restaura acceso a Claude en openclaw tras bloqueo de Anthropic. Emulación de navegador con curl_cffi, soporte para uso de herramientas vía prompts XML y corrección automática JSON. Código completo, configuración e integración para desarrolladores.

Restaura Claude en OpenClaw: proxy con uso de herramientas
Advertisement 728x90

# Proxy para API de Claude vía Sesión Web: Integración con OpenClaw y Uso de Herramientas

Anthropic ha bloqueado el uso de Claude en apps de terceros como OpenClaw. Los suscriptores Pro ($100/mes) recibieron $100 en créditos extra de uso, pero eso no soluciona la integración de agentes. ¿La solución? Un servidor proxy que imita una sesión de navegador de Claude.ai con una API compatible con OpenAI. Así puedes ejecutar Claude Sonnet/Opus en OpenClaw con soporte completo para herramientas.

Desarrollado en Python con curl_cffi para huella TLS de Chrome. Las cookies y el ID de dispositivo provienen de tu sesión activa. Cada solicitud inicia una conversación nueva, transmitiendo respuestas vía SSE.

Configuración Básica de Sesión y Encabezados

La sesión imita Chrome con cookies reales del navegador. Probado con el navegador antidetección Camoufox.

Google AdInline article slot
def make_session() -> cf_requests.Session:
    """Crea una nueva sesión curl_cffi con huella TLS de Chrome."""
    cookies = get_cookies()
    session = cf_requests.Session(impersonate=IMPERSONATE)
    session.cookies.update(cookies)
    return session

def get_headers(cookies: dict) -> dict:
    return {
        "User-Agent": (
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
            "(KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
        ),
        "Origin": "https://claude.ai",
        "Referer": "https://claude.ai/new",
        "anthropic-device-id": cookies.get("anthropic-device-id", ""),
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-origin",
    }

Lógica de Solicitudes de Completado

  • POST a /api/organizations/{org_id}/chat_conversations para crear una conversación.
  • POST a /chat_conversations/{conv_id}/completion con text/event-stream.
def claude_complete(
    prompt: str,
    internal_model: str,
) -> Generator[str, None, None]:
    """Generador síncrono que emite líneas SSE de completado de claude.ai."""
    cookies = get_cookies()
    org_id = cookies["lastActiveOrg"]
    headers = get_headers(cookies)

    session = make_session()

    # 1. Crear conversación
    r = session.post(
        f"https://claude.ai/api/organizations/{org_id}/chat_conversations",
        json={"uuid": None, "name": ""},
        headers=headers,
        timeout=15,
    )
    r.raise_for_status()
    conv_id = r.json()["uuid"]
    log.info(f"Conv creada: {conv_id} model={internal_model}")

    # 2. Transmitir completado
    payload = {
        "prompt": prompt,
        "model": internal_model,
        "timezone": "Europe/Moscow",
        "attachments": [],
        "files": [],
    }
    r2 = session.post(
        f"https://claude.ai/api/organizations/{org_id}/chat_conversations/{conv_id}/completion",
        json=payload,
        headers={**headers, "Accept": "text/event-stream"},
        stream=True,
        timeout=60,
    )
    r2.raise_for_status()
    for line in r2.iter_lines():
        yield line

El servidor API usa aiohttp con rutas para /v1/models, /v1/chat/completions, /health. Se ejecuta en 127.0.0.1:8787.

def make_app() -> web.Application:
    app = web.Application()
    app.router.add_get("/v1/models", handle_models)
    app.router.add_get("/health", handle_health)
    app.router.add_post("/v1/chat/completions", handle_chat_completions)
    return app

Integración con OpenClaw

Configuración del proveedor:

"claude-web": {
    "baseUrl": "http://127.0.0.1:8787/v1",
    "apiKey": "dummy",
    "api": "openai-completions",
    "models": [
      {
        "id": "claude-sonnet-4-6-web",
        "name": "Claude Sonnet 4.6 (Web)",
        "reasoning": false,
        "input": ["text"],
        "cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
        "contextWindow": 200000,
        "maxTokens": 32000
      }
    ]
}

La generación de texto funciona de inmediato. El uso de herramientas requiere ajustes.

Google AdInline article slot

Soporte para Herramientas: Inyección de Prompt

El prompt del sistema obliga a Claude a generar llamadas a herramientas en formato XML:

TOOL_SYSTEM_PROMPT = """Tienes acceso a herramientas. Cuando necesites llamar a una herramienta, emite SOLO el bloque de llamada — sin explicaciones, sin texto antes o después:

<tool_call>
{{
"name": "tool_name_here", "arguments": {{"param1": "value1", "param2": "value2"}}
}}
</tool_call>

REGLAS:
- El JSON dentro de <tool_call> debe ser válido — sin comas finales, sin comentarios
- Puedes emitir múltiples bloques <tool_call> en secuencia
- Tras recibir bloques <tool_result>, continúa tu respuesta normalmente
- Si no llamas a una herramienta, responde normalmente sin etiquetas <tool_call>

Herramientas disponibles:
{tools_json}"""

El análisis usa regex con correcciones automáticas para errores JSON (comas finales, comentarios).

Manejo Final de Llamadas y Resultados de Herramientas

  • El analizador extrae bloques <tool_call>, corrige JSON y convierte al formato OpenAI.
  • Los resultados de herramientas se inyectan como XML <tool_result id="...">contenido</tool_result> en mensajes de usuario.
def parse_tool_calls(text: str) -> tuple[list[dict], str]:
    tool_calls = []
    for match in TOOL_CALL_RE.finditer(text):
        raw_json = match.group(1).strip()
        obj = None
        try:
            obj = json.loads(raw_json)
        except json.JSONDecodeError:
            fixed = _try_fix_json(raw_json)
            obj = json.loads(fixed)
        if obj:
            tool_calls.append({
                "id": f"call_{uuid.uuid4().hex[:12]}",
                "type": "function",
                "function": {
                    "name": obj.get("name", ""),
                    "arguments": json.dumps(obj.get("arguments", {})),
                },
            })
    remaining = TOOL_CALL_RE.sub("", text).strip()
    return tool_calls, remaining

def inject_tool_results(messages: list[dict], tools: list[dict]) -> list[dict]:
    # Convierte tool_calls/resultados OpenAI a XML para Claude
    # ... (lógica de lotes y generación XML)

Características Principales

  • Compatibilidad Total: API de OpenAI con uso de herramientas para OpenClaw/Claude Sonnet 4.6 (contexto de 200k).
  • Modo Sigiloso: curl_cffi + cookies/ID de dispositivo reales, TLS de Chrome.
  • Corrección Automática: Maneja fallos comunes de JSON de Claude (comas finales, comentarios).
  • Transmisión: SSE desde API web, conversación nueva por solicitud.
  • Límites: Respeta la suscripción Pro, sin uso extra.

Limitaciones y Mejoras

Depende de la estabilidad de la API web de Claude.ai. Posibles problemas:

Google AdInline article slot
  • Captchas en el sitio.
  • Tiempo de espera de sesiones.
  • Cambios en endpoints.

Soluciones: Actualización automática de cookies, rotación de ID de dispositivo, manejo de errores.

— Editorial Team

Advertisement 728x90

Leer después