Use VieNeu from the 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
(vn_sk_..., from the Developer page). Keep both in environment variables:
export OPENAI_API_KEY=sk-...
export VIENEU_API_KEY=vn_sk_...
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 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.
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)
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 containsmcp_approval_requestitems you must answer)."never"lets the model spend your VieNeu tokens without asking — keepallowed_toolstight, or require approval fortext_to_speechonly:"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_callitems inresp.outputhold the raw tool results.
OpenAI Agents SDK (Python)
Let OpenAI call VieNeu for you (hosted tool):
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):
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.