# Python frameworks

> Give a Python agent built with the Claude Agent SDK, OpenAI Agents or LangGraph your messaging channels in about ten minutes.

Source: https://docs.vlayers.ai/developers/frameworks/python

Kind: Tutorial

## What you'll build

A Python agent that texts a reserved test number with VoiceLayer's `message_send` tool. You use `voicelayer-agents`, which gives each framework the VoiceLayer [channel tools](https://docs.vlayers.ai/developers/mcp) over MCP. It adds no tools of its own, so they are the same in every framework and language. Each framework is an extra: `[claude]`, `[openai]` or `[langgraph]`.

## Prerequisites

- Python 3.10 or later.
- A test key in `VOICELAYER_API_KEY` (`vl_test_…`): see [Authentication and API keys](https://docs.vlayers.ai/get-started/authentication). With a test key every send goes to a simulated carrier and costs nothing.
- The model provider key your framework needs (`ANTHROPIC_API_KEY` or `OPENAI_API_KEY`).
- No phone number needed: with a test key, your workspace's free [test sender](https://docs.vlayers.ai/developers/test-mode#your-free-test-sender) stands in for one.

## Install

Pick the extra for your framework. For the OpenAI Agents SDK:

```bash
pip install "voicelayer-agents[openai]"
```

> Publishing soon: voicelayer-agents is not on PyPI yet. Until it is, install it from a checkout of the VoiceLayer repository (`sdks/python/voicelayer-agents`), as shown.

```bash
pip install "./voicelayer/sdks/python/voicelayer-agents[openai]"
```

Use the extra `claude` for the Claude Agent SDK, or `langgraph` for LangGraph. The LangGraph example names its model as a string (`"openai:gpt-4.1-mini"`), which needs `langchain[openai]` too.

## Give your agent VoiceLayer's tools

VoiceLayer's helpers read `VOICELAYER_API_KEY` themselves, so no example passes a key. Save one of these as `agent.py`.

OpenAI Agents:
`voicelayer_mcp_server()` returns the Agents SDK's MCP server, pointed at VoiceLayer. `async with` connects it and closes it afterwards.

```python
import asyncio

from agents import Agent, Runner
from voicelayer_agents.openai import voicelayer_mcp_server

async def main():
    async with voicelayer_mcp_server() as voicelayer:
        agent = Agent(name="support", instructions="You answer customers in one short sentence.", mcp_servers=[voicelayer])
        result = await Runner.run(agent, 'Text "Hello from VoiceLayer" to +15550101 over sms.')
        print(result.final_output)

asyncio.run(main())
```

Claude Agent SDK:
`voicelayer_mcp()` returns options to splat into `ClaudeAgentOptions`: the MCP server and the tools Claude may use without a permission prompt. `tools=[]` removes Claude Code's own file, shell and web tools: this agent only talks.

```python
import asyncio

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query
from voicelayer_agents.claude import voicelayer_mcp

async def main():
    options = ClaudeAgentOptions(**voicelayer_mcp(), tools=[], system_prompt="You answer customers in one short sentence.")
    async for message in query(prompt='Text "Hello from VoiceLayer" to +15550101 over sms.', options=options):
        if isinstance(message, ResultMessage):
            print(message.result)

asyncio.run(main())
```

LangGraph:
`voicelayer_tools()` loads the VoiceLayer tools as LangChain tools, for `create_react_agent` or a `ToolNode`.

```python
import asyncio

from langgraph.prebuilt import create_react_agent
from voicelayer_agents.langgraph import voicelayer_tools

async def main():
    agent = create_react_agent("openai:gpt-4.1-mini", await voicelayer_tools())
    result = await agent.ainvoke({"messages": [("user", 'Text "Hello from VoiceLayer" to +15550101 over sms.')]})
    print(result["messages"][-1].content)

asyncio.run(main())
```

```bash
export VOICELAYER_API_KEY=vl_test_...
export OPENAI_API_KEY=...
python agent.py
```

The agent calls `message_send` and prints its final answer, for example that the message was sent.

## Answer incoming messages

Direct mode works with any framework: your function gets the message and the conversation so far, and returns the reply. Save this as `serve.py`.

```python
from voicelayer_agents import serve_direct

serve_direct(lambda turn: f"You said: {turn.message.text}").wait()
```

The lambda stands in for your agent. With the OpenAI Agents SDK, `serve_direct(lambda turn: Runner.run_sync(agent, turn.message.text).final_output).wait()` answers with `agent` from the first example. `wait()` keeps the process running until you press Ctrl+C.

## Test it without a person

With `serve.py` running, simulate a person writing in from a second terminal. `simulate_inbound` works with test keys only:

```python
from voicelayer_agents import simulate_inbound

print(simulate_inbound("What are your opening hours?")["conversationId"])
```

The first terminal prints `voicelayer: replied in conversation …`. Open **Conversations** in the dashboard and turn on test conversations to see both messages.

## Common errors

| Error | Status | How to fix |
|---|---|---|
| [`unauthorized`](https://docs.vlayers.ai/errors/unauthorized) | 401 | Send `Authorization: Bearer <key>` with a secret key from the dashboard (Developers → API keys). Test keys start with `vl_test_`. |
| [`insufficient_scope`](https://docs.vlayers.ai/errors/insufficient_scope) | 403 | Use a key with the scope named in `details.required`, or one whose restriction covers this channel or Binding. |
| [`test_address_required`](https://docs.vlayers.ai/errors/test_address_required) | 422 | Use a reserved test address, or a live key to reach a real person. |
| [`test_mode_only`](https://docs.vlayers.ai/errors/test_mode_only) | 403 | Use a test key (`vl_test_…`) from the dashboard. |
| [`test_limit_reached`](https://docs.vlayers.ai/errors/test_limit_reached) | 429 | Try again after midnight UTC, or ask for a higher test limit. |

## Next steps

- [The MCP tools](https://docs.vlayers.ai/developers/mcp): what each tool does and which scope it needs.
- [Python SDK](https://docs.vlayers.ai/developers/sdks/python): call the API directly.
- [Test mode](https://docs.vlayers.ai/developers/test-mode): more ways to simulate people.
