# Use VieNeu from the OpenAI API

Source: https://docs.vieneu.io/docs/integrations/mcp/openai-api

If you build your own app on OpenAI models, you can hand the model the VieNeu
tools directly: the model decides when to search voices and synthesize, and
OpenAI calls `https://api.vieneu.io/mcp` for you.

**You need:** an OpenAI API key and a VieNeu [API key](../../cloud-api/overview.md#authentication)
(`vn_sk_...`, from the **Developer** page). Keep both in environment variables:

```bash
export OPENAI_API_KEY=sk-...
export VIENEU_API_KEY=vn_sk_...
```

:::tip Just want audio from code?
If your code already knows the text and the voice, you do not need MCP or an
LLM at all — call the [OpenAI-compatible endpoint](../../cloud-api/openai-compatible.md)
directly with the OpenAI SDK. MCP is for letting the *model* decide.
:::

## Responses API

The Responses API has a built-in remote MCP tool. Pass your VieNeu key in
`authorization`; OpenAI sends it to VieNeu as a Bearer token, which VieNeu
accepts.

```python
import os
from openai import OpenAI

client = OpenAI()
resp = client.responses.create(
    model="gpt-5",  # any current model that supports tools
    tools=[{
        "type": "mcp",
        "server_label": "vieneu",
        "server_url": "https://api.vieneu.io/mcp",
        "authorization": os.environ["VIENEU_API_KEY"],
        "allowed_tools": ["list_voices", "text_to_speech", "get_speech_status", "get_token_balance"],
        "require_approval": "never",
    }],
    input="Tìm một giọng nữ miền Bắc rồi đọc câu: Xin chào, đây là VieNeu. Trả về link tải.",
)
print(resp.output_text)
```

```js
import OpenAI from "openai";

const client = new OpenAI();
const resp = await client.responses.create({
  model: "gpt-5",
  tools: [{
    type: "mcp",
    server_label: "vieneu",
    server_url: "https://api.vieneu.io/mcp",
    authorization: process.env.VIENEU_API_KEY,
    allowed_tools: ["list_voices", "text_to_speech", "get_speech_status", "get_token_balance"],
    require_approval: "never",
  }],
  input: "Tìm một giọng nữ miền Bắc rồi đọc câu: Xin chào, đây là VieNeu. Trả về link tải.",
});
console.log(resp.output_text);
```

- **`require_approval`:** the default asks for approval before every tool call
  (the response then contains `mcp_approval_request` items you must answer).
  `"never"` lets the model spend your VieNeu tokens without asking — keep
  `allowed_tools` tight, or require approval for `text_to_speech` only:
  `"require_approval": {"always": {"tool_names": ["text_to_speech"]}}`.
- **The key is not stored by OpenAI**, so send it with every request.
- The audio link is in the tool result and usually in the model's answer; the
  `mcp_call` items in `resp.output` hold the raw tool results.

## OpenAI Agents SDK (Python)

Let OpenAI call VieNeu for you (hosted tool):

```python
import os
from agents import Agent, HostedMCPTool, Runner

agent = Agent(
    name="Narrator",
    instructions="You narrate Vietnamese text with VieNeu and return the audio link.",
    tools=[HostedMCPTool(tool_config={
        "type": "mcp",
        "server_label": "vieneu",
        "server_url": "https://api.vieneu.io/mcp",
        "authorization": os.environ["VIENEU_API_KEY"],
        "require_approval": "never",
    })],
)
result = Runner.run_sync(agent, "Đọc câu 'Chào buổi sáng' bằng giọng Thu Trang.")
print(result.final_output)
```

Or connect from your own process (your code calls VieNeu, with any header you
choose):

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

async def main():
    async with MCPServerStreamableHttp(
        name="vieneu",
        params={
            "url": "https://api.vieneu.io/mcp",
            "headers": {"X-API-Key": os.environ["VIENEU_API_KEY"]},
            "timeout": 60,
        },
        cache_tools_list=True,
    ) as vieneu:
        agent = Agent(name="Narrator", mcp_servers=[vieneu])
        result = await Runner.run(agent, "Đọc câu 'Chào buổi sáng' bằng giọng Thu Trang.")
        print(result.final_output)

asyncio.run(main())
```

Set `timeout` generously: `text_to_speech` waits for the audio (a few seconds
for short text, up to about 50 seconds for long text).

## Costs

Two bills: OpenAI charges for the model's tokens, VieNeu charges your plan for
each synthesis (per character, minimum 50). `list_voices`, `list_emotion_tags`,
`get_speech_status` and `get_token_balance` are free on the VieNeu side.
