# LangChain, CrewAI and OpenAI Agents

> Give LangChain, CrewAI, the OpenAI Agents SDK or LlamaIndex agents a captcha tool, either as ready tool code or through the Solvecha MCP server.

Every framework below can use Solvecha in two ways: through the [MCP server](https://solvecha.com/docs/mcp-server.md), or with a small tool that calls the REST API. The MCP way needs no tool code.

## Through MCP


**OpenAI Agents SDK**

```python
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

async with MCPServerStreamableHttp(
    name="solvecha",
    params={
        "url": "https://api.solvecha.com/mcp",
        "headers": {"Authorization": f"Bearer {os.environ['SOLVECHA_API_KEY']}"},
        "timeout": 100,
    },
    client_session_timeout_seconds=100,
) as server:
    agent = Agent(name="Browser agent", instructions="Use solvecha tools for captcha images.", mcp_servers=[server])
    result = await Runner.run(agent, "Solve the captcha in this screenshot: ...")
```

**LangChain**

```python
import os
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "solvecha": {
        "transport": "streamable_http",
        "url": "https://api.solvecha.com/mcp",
        "headers": {"Authorization": f"Bearer {os.environ['SOLVECHA_API_KEY']}"},
    }
})
tools = await client.get_tools()  # pass these tools to your agent
```

**Google ADK**

```python
import os
from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams

agent = LlmAgent(
    model="gemini-flash-latest",
    name="browser_agent",
    instruction="Use the solvecha tools for captcha images.",
    tools=[McpToolset(connection_params=StreamableHTTPConnectionParams(
        url="https://api.solvecha.com/mcp",
        headers={"Authorization": f"Bearer {os.environ['SOLVECHA_API_KEY']}"},
    ))],
)
```


## As a plain tool

This function solves a reCAPTCHA grid from a PNG file or base64 text. It uses only `requests`. Wrap it with the decorator of your framework.

```python
import base64
import os
import time

import requests

API = "https://api.solvecha.com"


def solve_recaptcha_grid(task: str, grid: str, image_base64: str) -> list[int]:
    """Solve a reCAPTCHA v2 image challenge.

    Args:
        task: The instruction text, for example 'Select all images with buses'.
        grid: '3x3' or '4x4'.
        image_base64: One base64 picture of the whole grid.

    Returns the tile numbers to click, counted from 1, left to right, top to bottom.
    """
    headers = {"Authorization": f"Bearer {os.environ['SOLVECHA_API_KEY']}"}
    job = requests.post(f"{API}/v1/recognition/recaptcha", headers=headers, timeout=30,
                        json={"task": task, "grid": grid, "image_data": [image_base64]}).json()
    if "data" not in job:
        raise RuntimeError(f"Solvecha error {job.get('error')}: {job.get('message')}")
    for _ in range(40):
        time.sleep(1)
        answer = requests.get(f"{API}/v1/recognition/recaptcha", headers=headers, timeout=30,
                              params={"id": job["data"]}).json()
        if answer.get("error") == 14:  # still working
            continue
        if "data" in answer:
            return [i + 1 for i, picked in enumerate(answer["data"]) if picked]
        raise RuntimeError(f"Solvecha error {answer.get('error')}: {answer.get('message')}")
    raise TimeoutError("No answer in 40 seconds")
```


**LangChain**

```python
from langchain_core.tools import tool

solve_tool = tool(solve_recaptcha_grid)  # the docstring becomes the tool description
```

**CrewAI**

```python
from crewai.tools import tool

@tool("Solve reCAPTCHA grid")
def solve_tool(task: str, grid: str, image_base64: str) -> str:
    """Solve a reCAPTCHA v2 image challenge. Returns the tile numbers to click."""
    return str(solve_recaptcha_grid(task, grid, image_base64))
```

**OpenAI Agents SDK**

```python
from agents import function_tool

solve_tool = function_tool(solve_recaptcha_grid)
```

**LlamaIndex**

```python
from llama_index.core.tools import FunctionTool

solve_tool = FunctionTool.from_defaults(fn=solve_recaptcha_grid)
```


A model cannot pass a large picture reliably as a tool argument in every framework. If your agent has trouble with long base64 text, give the tool a file path instead and read the file inside the function.

See [reCAPTCHA image grids](https://solvecha.com/docs/captcha-types/recaptcha.md) for the request and answer format, and [Errors](https://solvecha.com/docs/errors.md) for the codes.
