# Frameworks and SDKs (/docs/connect/frameworks)

Every framework on this page loads quarry's tools from one MCP server:

```text
https://quarry.market/api/mcp
```

Your agent gets `search` (free, across every dataset), `get_dataset`, `ask`, and
the account tools described in [MCP server](/docs/mcp). The examples send an
[API key](/docs/subscriptions#api-keys) so the agent can use your subscriptions.
They read it from the `QUARRY_API_KEY` environment variable:

```bash
export QUARRY_API_KEY=qk_...
```

Without a key, remove the `QUARRY_API_KEY` line and the header or `auth` argument:
the agent can still search quarry and read free datasets. To pay per question from
a wallet instead, see [Paying with x402](/docs/x402).

Each example also needs its model provider's key: `ANTHROPIC_API_KEY` for the
Claude models used here, `OPENAI_API_KEY` for the OpenAI examples and CrewAI,
`GOOGLE_API_KEY` for Google ADK, and `AI_GATEWAY_API_KEY` for the Vercel AI SDK.
The TypeScript examples use top-level `await`, so run them as ES modules, for
example with `npx tsx agent.mts`.

## Python [#python]

### LangChain and LangGraph [#langchain-and-langgraph]

LangChain's MCP support ships in the `mcp` extra of `langchain` 1.4 and later. It's
still in beta.

```bash
pip install "langchain[mcp,anthropic]>=1.4"
```

```python
import asyncio
import os

from fastmcp.client import Client
from langchain.agents import create_agent
from langchain.mcp import MCPAdapter

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

async def main():
    # auth= is sent as "Authorization: Bearer <key>".
    quarry = Client("https://quarry.market/api/mcp", auth=QUARRY_API_KEY)
    async with MCPAdapter(quarry) as adapter:
        tools = await adapter.list_tools()
        agent = create_agent("anthropic:claude-sonnet-5", tools)
        result = await agent.ainvoke(
            {"messages": [{"role": "user", "content": "Find sources on chip lead times and cite them."}]}
        )
        print(result["messages"][-1].content)

asyncio.run(main())
```

Still on the older `langchain-mcp-adapters` package? It connects the same way:

```python
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "quarry": {
        "transport": "http",
        "url": "https://quarry.market/api/mcp",
        "headers": {"Authorization": f"Bearer {QUARRY_API_KEY}"},
    }
})
tools = await client.get_tools()  # inside an async function, as above
```

### LlamaIndex [#llamaindex]

```bash
pip install llama-index-tools-mcp llama-index-llms-anthropic
```

```python
import asyncio
import os

from llama_index.core.agent.workflow import FunctionAgent
from llama_index.llms.anthropic import Anthropic
from llama_index.tools.mcp import BasicMCPClient, McpToolSpec

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

async def main():
    quarry = BasicMCPClient(
        "https://quarry.market/api/mcp",
        headers={"Authorization": f"Bearer {QUARRY_API_KEY}"},
    )
    tools = await McpToolSpec(client=quarry).to_tool_list_async()
    agent = FunctionAgent(
        tools=tools,
        llm=Anthropic(model="claude-sonnet-5"),
        system_prompt="Answer with quarry's sources and cite them.",
    )
    print(await agent.run("Find sources on chip lead times and cite them."))

asyncio.run(main())
```

### OpenAI Agents SDK [#openai-agents-sdk]

```bash
pip install openai-agents
```

```python
import asyncio
import os

from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

async def main():
    async with MCPServerStreamableHttp(
        name="quarry",
        params={
            "url": "https://quarry.market/api/mcp",
            "headers": {"Authorization": f"Bearer {QUARRY_API_KEY}"},
        },
        cache_tools_list=True,
        # The default is 5 seconds; a search can take longer.
        client_session_timeout_seconds=30,
    ) as quarry:
        agent = Agent(
            name="Researcher",
            instructions="Answer with quarry's sources and cite them.",
            mcp_servers=[quarry],
        )
        result = await Runner.run(agent, "Find sources on chip lead times and cite them.")
        print(result.final_output)

asyncio.run(main())
```

### Pydantic AI [#pydantic-ai]

```bash
pip install "pydantic-ai-slim[mcp,anthropic]"
```

```python
import os

from pydantic_ai import Agent
from pydantic_ai.mcp import MCPToolset

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

quarry = MCPToolset(
    "https://quarry.market/api/mcp",
    headers={"Authorization": f"Bearer {QUARRY_API_KEY}"},
)
agent = Agent("anthropic:claude-sonnet-5", toolsets=[quarry])

result = agent.run_sync("Find sources on chip lead times and cite them.")
print(result.output)
```

`MCPToolset` replaced `MCPServerStreamableHTTP` in Pydantic AI 2.

### Google ADK [#google-adk]

```bash
pip install google-adk mcp
```

```python
import os

from google.adk.agents import Agent
from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

root_agent = Agent(
    name="researcher",
    model="gemini-flash-latest",
    instruction="Answer with quarry's sources and cite them.",
    tools=[
        McpToolset(
            connection_params=StreamableHTTPConnectionParams(
                url="https://quarry.market/api/mcp",
                headers={"Authorization": f"Bearer {QUARRY_API_KEY}"},
            )
        )
    ],
)
```

ADK loads `root_agent` from your agent's `agent.py`. Start it with `adk run` or
`adk web`.

### CrewAI [#crewai]

```bash
pip install crewai mcp
```

```python
import os

from crewai import Agent
from crewai.mcp import MCPServerHTTP

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

researcher = Agent(
    role="Researcher",
    goal="Answer questions with quarry's sources, cited",
    backstory="You search quarry and cite the passages you use.",
    mcps=[
        MCPServerHTTP(
            url="https://quarry.market/api/mcp",
            headers={"Authorization": f"Bearer {QUARRY_API_KEY}"},
            streamable=True,
        )
    ],
)
print(researcher.kickoff("Find sources on chip lead times and cite them.").raw)
```

CrewAI uses OpenAI by default; pass `llm=` to the agent to change the model.

## TypeScript [#typescript]

### Vercel AI SDK [#vercel-ai-sdk]

For AI SDK 7. The model string goes through the Vercel AI Gateway; use a provider
package such as `@ai-sdk/anthropic` to call a provider directly.

```bash
npm install ai @ai-sdk/mcp
```

```ts
import { createMCPClient } from "@ai-sdk/mcp";
import { generateText, isStepCount } from "ai";

const quarry = await createMCPClient({
  transport: {
    type: "http",
    url: "https://quarry.market/api/mcp",
    headers: { Authorization: `Bearer ${process.env.QUARRY_API_KEY}` },
  },
});

try {
  const { text } = await generateText({
    model: "anthropic/claude-sonnet-5",
    tools: await quarry.tools(),
    stopWhen: isStepCount(5),
    prompt: "Find sources on chip lead times and cite them.",
  });
  console.log(text);
} finally {
  await quarry.close();
}
```

The AI SDK doesn't follow redirects to an MCP server, so use the address exactly as
written: no `www`, no trailing slash. With `streamText`, close the client in
`onEnd` instead.

### Mastra [#mastra]

```bash
npm install @mastra/core @mastra/mcp
```

```ts
import { Agent } from "@mastra/core/agent";
import { MCPClient } from "@mastra/mcp";

const quarry = new MCPClient({
  id: "quarry",
  servers: {
    quarry: {
      url: new URL("https://quarry.market/api/mcp"),
      requestInit: { headers: { Authorization: `Bearer ${process.env.QUARRY_API_KEY}` } },
    },
  },
});

export const researcher = new Agent({
  id: "researcher",
  name: "Researcher",
  instructions: "Answer with quarry's sources and cite them.",
  model: "anthropic/claude-sonnet-5",
  tools: await quarry.listTools(),
});

const result = await researcher.generate("Find sources on chip lead times and cite them.");
console.log(result.text);
await quarry.disconnect();
```

Mastra prefixes each tool with the server name: `quarry_search`, `quarry_ask`, and
so on.

## Model APIs [#model-apis]

OpenAI and Anthropic can call quarry for you during a request, from their own
servers. Two consequences:

* Always send an API key, even for free datasets. Without one, quarry limits
  requests by where they come from, and these providers send requests for many
  customers from the same addresses.
* These calls can't pay per question from your wallet. Paid datasets need a
  subscription.

### OpenAI Responses API [#openai-responses-api]

```python
import os

from openai import OpenAI

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

client = OpenAI()  # reads OPENAI_API_KEY
response = client.responses.create(
    model="gpt-6-astra",
    input="Find sources on chip lead times and cite them.",
    tools=[
        {
            "type": "mcp",
            "server_label": "quarry",
            "server_url": "https://quarry.market/api/mcp",
            "headers": {"Authorization": f"Bearer {QUARRY_API_KEY}"},
            "require_approval": "never",
        }
    ],
)
print(response.output_text)
```

By default, OpenAI asks for your approval before each call to quarry;
`"require_approval": "never"` skips that step.

### Anthropic Messages API [#anthropic-messages-api]

```python
import os

import anthropic

QUARRY_API_KEY = os.environ["QUARRY_API_KEY"]

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY
response = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=2048,
    messages=[{"role": "user", "content": "Find sources on chip lead times and cite them."}],
    mcp_servers=[
        {
            "type": "url",
            "url": "https://quarry.market/api/mcp",
            "name": "quarry",
            # Anthropic adds "Bearer " itself: pass the bare key.
            "authorization_token": QUARRY_API_KEY,
        }
    ],
    tools=[{"type": "mcp_toolset", "mcp_server_name": "quarry"}],
    betas=["mcp-client-2025-11-20"],
)
print("".join(block.text for block in response.content if block.type == "text"))
```

Every server in `mcp_servers` needs a matching `mcp_toolset` entry in `tools`.
