Metadata-Version: 2.4
Name: voicelayer-agents
Version: 0.1.0
Summary: VoiceLayer channels for agent frameworks: the channel MCP tools for the Claude Agent SDK, OpenAI Agents and LangChain/LangGraph, and direct mode.
Project-URL: Homepage, https://docs.vlayers.ai/developers/frameworks/claude
Author-email: VoiceLayer <customer@vlayers.ai>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,claude-agent-sdk,langgraph,mcp,openai-agents,sms,voicelayer,whatsapp
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.10
Requires-Dist: voicelayer>=0.1.0
Provides-Extra: claude
Requires-Dist: claude-agent-sdk==0.2.163; extra == 'claude'
Provides-Extra: langgraph
Requires-Dist: langchain-mcp-adapters==0.3.2; extra == 'langgraph'
Requires-Dist: langgraph==1.2.12; extra == 'langgraph'
Provides-Extra: openai
Requires-Dist: openai-agents==0.23.1; extra == 'openai'
Description-Content-Type: text/markdown

# voicelayer-agents

VoiceLayer channels for the Python agent framework you already use: SMS, WhatsApp, Telegram, Discord and email, for the
Claude Agent SDK, the OpenAI Agents SDK and LangChain/LangGraph.

- **Tool packs** — VoiceLayer's channel tools (`message_send`, `message_reply`, `conversations_list`, `conversation_read`,
  `conversation_set_controller`, `messages_wait`, `channels_list`, `whatsapp_templates_list`) in each framework's own MCP
  hook. The VoiceLayer MCP server lists them; nothing is re-implemented here.
- **`serve_direct`** — direct mode, with any framework: your process answers the conversations handed to code
  (controller `api`). Your runner gets the message and the conversation's history and returns the reply; it is sent once,
  through every gate of the pipeline (the Output Guard included — a refusal goes to `on_refused`, never retried around).

```bash
pip install voicelayer-agents              # serve_direct and the test-mode helper
pip install "voicelayer-agents[openai]"    # + the OpenAI Agents tool pack; also [claude], [langgraph]
```

Each framework is an extra, pinned to the version the tests run. Direct mode is built on the generated API client,
[`voicelayer`](https://pypi.org/project/voicelayer/), installed with it.

## Keys

Set `VOICELAYER_API_KEY` to a workspace API key (dashboard -> API keys) scoped to what the agent does: `messages:send`,
`messages:read`, `conversations:control`, `events:read`, `channels:read`. Every helper reads it, and `VOICELAYER_API_URL`
(default `https://api.vlayers.ai`), so the examples below pass nothing. `api_key=` and `base_url=` override both. A test
key (`vl_test_...`) runs everything in test mode: every send goes through the loopback and costs nothing, and a workspace
with no number gets a free test sender. A worker key (`vlw_...`) is refused.

## Tool packs

```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:  # connected; closed afterwards
        agent = Agent(name="support", instructions="You answer customers in one short sentence.", mcp_servers=[voicelayer])
        print((await Runner.run(agent, 'Text "Hello from VoiceLayer" to +15550101 over sms.')).final_output)


asyncio.run(main())
```

```python
from claude_agent_sdk import ClaudeAgentOptions
from voicelayer_agents.claude import voicelayer_mcp

options = ClaudeAgentOptions(**voicelayer_mcp(), tools=[], system_prompt="You answer customers in one short sentence.")
```

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

agent = create_react_agent("openai:gpt-4.1-mini", await voicelayer_tools())  # inside an async function
```

## Direct mode

```python
from voicelayer_agents import DirectAnswer, serve_direct


def answer(turn):
    # turn.message: the inbound (id, channel, conversation_id, text); turn.history: the conversation, oldest first
    if "person" in turn.message.text.lower():
        return DirectAnswer(text="Passing you to a colleague.", handoff="asked for a person")
    return f"You said: {turn.message.text}"


serve_direct(answer).wait()  # answers until Ctrl+C
```

`serve_direct` returns once its place in the event stream is fixed: every message that arrives after it returns is
answered. `.wait()` blocks (a program that only calls `serve_direct` would exit at once); `.stop()` or a `with` block
ends it. Pass `after=` to resume from a saved cursor, `channels=` to narrow, `history=False` (or your own callable) to
skip the history read.

## Test mode

```python
from voicelayer_agents import simulate_inbound

# a person writing to the workspace, simulated (test keys only): sms, from +15550101 unless you pass channel= / from_=
simulate_inbound("Are you open?")
```

Guides: [vlayers.ai/docs/agents](https://vlayers.ai/docs/agents). The TypeScript twin is
[`@voicelayer/agents`](https://www.npmjs.com/package/@voicelayer/agents). Licence: Apache-2.0.
