Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Agent-SIP Ideal for Hermes and n8n workflows

Agent-SIP — a self-hosted SIP voice bridge that can be controlled with AI agents via MCP.

Bring an AI agent to an ordinary phone extension. Agent-SIP registers with your PBX, sends and receives G.711 audio over RTP, and gives you a web dashboard, automation webhooks, and MCP tools for controlling calls from an assistant.

OpenAI's official Realtime SIP integration is designed around Twilio's cloud telephony, but Agent-SIP works with any local SIP server—including Asterisk, FreePBX, 3CX, Kamailio, or a carrier trunk. It speaks SIP directly using UDP signaling and RTP audio, so it doesn't rely on a cloud telephony provider. In our own setup, it runs as extension 500 on a FreePBX box.

Agent-SIP call dashboard
Call dashboard
Agent-SIP settings
Settings
Agent-SIP agent prompts
Agent prompts

Select any screenshot to open the full-size image.

🏗️ Architecture

Agent-SIP architecture

✨ Features

  • Real phone calls through a SIP extension using UDP signaling and RTP audio (PCMU or PCMA).
  • OpenAI Realtime speech with natural multilingual conversations, French defaults, configurable language and the marin voice.
  • CALL BRIEF objectives that tell the agent what to accomplish on each outbound call.
  • MCP control to make calls, steer the agent, speak, hang up, retrieve transcripts, inspect status, and save messages.
  • Automation webhooks for call and transcript events—ideal for n8n, Hermes, or your own service.
  • Background office ambience with selectable bundled sounds and adjustable volume.
  • Authenticated web UI for calls, live transcripts, logs, configuration, and prompts.
  • Simple deployment with Docker and a published GHCR image.
  • Call safeguards including configurable ring limits, ring timeouts, maximum agent turns, and automatic hangup after goodbyes.

🚀 Quick start with Docker

Agent-SIP works best with Docker host networking because SIP/SDP embeds network addresses and RTP uses a UDP port range.

make setup # creates .env from the template (first time)# edit .env: VOICE_API_KEY + your PBX details
make up # builds and starts

Without Make, use the published image directly. Edit .env and, at minimum, provide your SIP server, extension credentials, reachable SIP_ADVERTISE_HOST, and VOICE_API_KEY:

docker pull ghcr.io/ai-redcode/agent-sip:latest
cp .env.docker.example .env
docker run -d --name agent-sip --network host --env-file .env \
-v ./var:/app/var \
ghcr.io/ai-redcode/agent-sip:latest

Open http://localhost:8090 and sign in with admin / admin.

Warning

Change the default web password immediately, especially before exposing the UI beyond a trusted local network.

Host networking is required for the normal Docker setup: the PBX must be able to reach the SIP address and RTP ports advertised inside SDP. By default Agent-SIP uses TCP 8090 for the UI, TCP 8765 for MCP, UDP 5062 for SIP, and UDP 40000–40100 for RTP.

The Makefile prefers Docker Compose when available and falls back to the docker run command above when it is not.

🖥️ Web UI

The dashboard is organized into three tabs:

TabWhat it does
CallPlace and end calls, enter a CALL BRIEF, inject speech, follow the live transcript, and inspect recent logs.
SettingsConfigure the SIP endpoint, voice provider and speaking speed, background noise, MCP, webhooks, and UI credentials.
AgentSet the agent name, caller ID, default language, inbound context, inbound/outbound prompts, ring limits, and automatic-hangup behavior.

The status bar keeps the essentials visible at a glance: SIP Registration, Voice Provider, Call State, Active Call, and Agent. Settings are grouped into focused boxes for SIP endpoint, Voice provider (including speaking speed), Background noise, MCP, Webhook, and UI Password. Saving configuration persists it to var/config.json; API responses mask stored secrets.

🔧 MCP usage

The MCP control API listens at http://127.0.0.1:8765 by default. Set MCP_AUTH_TOKEN and send it as a Bearer token. Standard MCP clients can launch the included agent-sip-mcp stdio bridge, which proxies tools to the running HTTP service.

ToolArgumentsPurpose
get_statusReturn SIP registration, current call state, and recent call details.
make_callnumber, call_brief (optional)Start an outbound call with a per-call objective.
hangup_callEnd the active call.
saytextSpeak text into the active call.
steerinstructions and/or speedChange instructions, tone, or speaking speed (0.25–4.0) mid-call.
get_transcriptReturn recent transcript messages.
save_messagerecipient, caller_name, message, callback_number, language, confirmed_by_callerSave a caller-confirmed message.

Call a tool directly over HTTP:

curl -X POST http://localhost:8765/call \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_MCP_TOKEN" \
-d '{"name":"make_call","arguments":{"number":"201","call_brief":"Bonjour, ..."}}'

What is a CALL BRIEF?

A CALL BRIEF is objective text injected into the Realtime agent's session instructions for that call—not merely an opening sentence. It defines the task throughout the conversation: who to call, what to ask, what may be disclosed, and what result to collect. It can also set the language or tone, for example: Speak in Armenian, introduce yourself warmly, and ask whether Tuesday at 14:00 is available.

📡 Webhooks

Ideal for n8n workflows: configure a primary WEBHOOK_URL and, optionally, WEBHOOK_URL2. The second destination is useful when the same events should also flow to an n8n workflow. Delivery is best effort and never blocks call signaling or audio.

EventWhen it is sent
call.startedA call begins.
transcript.partialPartial speech is available, if enabled for that call direction.
transcript.finalA finalized transcript item is available.
call.endedThe call ends; includes outcome, rings, and duration.

Every JSON payload includes event and type (with the same value), plus call_id, caller_number, called_number, agent_name, transcript, timestamp, and mcp_url. A typical ended-call payload looks like this:

{
"event": "call.ended",
"type": "call.ended",
"call_id": "abc123",
"caller_number": "200",
"called_number": "201",
"agent_name": "Reception",
"transcript": [{"role": "agent", "text": "Bonjour."}],
"outcome": "completed",
"rings": 2,
"duration": 47.3,
"timestamp": "2026-08-05T12:00:47+00:00",
"mcp_url": "http://127.0.0.1:8765"
}

To replay a representative event against your receiver while developing:

curl -X POST http://localhost:5678/webhook/agent-sip \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_WEBHOOK_TOKEN" \
-d '{"event":"call.started","type":"call.started","call_id":"test-1","caller_number":"200","called_number":"201","agent_name":"Agent","transcript":[],"timestamp":"2026-08-05T12:00:00+00:00","mcp_url":"http://localhost:8765"}'

When WEBHOOK_AUTH_TOKEN is set, Agent-SIP sends both Authorization: Bearer … and X-Hub-Signature-256: sha256=…. The HMAC-SHA256 signature is calculated over the exact JSON body using the same token as the secret. (X-Hub-Signature with HMAC-SHA1 is also provided for compatibility.) Use WEBHOOK_NOTIFY_PARTIALS_INCOMING and WEBHOOK_NOTIFY_PARTIALS_OUTGOING to control noisy partial events independently.

n8n integration: create a Webhook node, place its production URL in WEBHOOK_URL2, verify the signature in the first workflow step, and route on the type field. A call.ended branch can summarize the transcript, update a CRM, or notify a home channel.

🤖 Hermes Agent integration (PersonalAssistant)

Ideal for Hermes workflows: Hermes can use Agent-SIP as both a callable tool server and an event source.

  1. Create an executable bridge wrapper named agent-sip-mcp-bridge. For a remote Agent-SIP host, its core command can be:

    #!/usr/bin/env bashexec ssh voice-host "MCP_AUTH_TOKEN=YOUR_MCP_TOKEN /tmp/agent-sip/.venv/bin/agent-sip-mcp"

    ssh does not automatically forward locally exported environment variables. Pass MCP_AUTH_TOKEN inline in the remote command as shown. For a non-default control URL on the remote host, pass AGENT_SIP_API_URL=… inline too.

  2. Register the stdio bridge with Hermes:

    hermes mcp add agent-sip --command /path/to/agent-sip-mcp-bridge
  3. In the Hermes webhook platform, subscribe to call.started, transcript.final, and call.ended (and transcript.partial only if needed). Use the same HMAC secret as WEBHOOK_AUTH_TOKEN, then route deliveries to your home channel.

  4. Ask your assistant: “Call X and ask about Y.” Hermes builds the CALL BRIEF, invokes make_call, follows the resulting event/transcript flow, and summarizes the outcome.

🧪 Development

Requires Python 3.11 or newer:

python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/agent-sip

Run the test suite:

.venv/bin/python -m pytest tests/ -v

Repository layout:

app/ SIP, RTP, Realtime, MCP, webhook, and web application code
assets/ambient/ Bundled background sound loops
docs/screenshots/ Web UI screenshots used in this README
scripts/ Diagnostic utilities
static/ Dashboard and login pages
tests/ Unit and integration tests
tools/ Development helpers
var/ Persisted runtime configuration and messages
Dockerfile Container image definition
docker-compose.yml Host-networked deployment

The FastAPI documentation is available at http://localhost:8090/docs while the service is running.

⚙️ Configuration reference

Environment variables override settings that are not already populated in persisted var/config.json. VOICE_API_KEY is required for live speech.

SIP

VariableDefaultDescription
SIP_SERVER_HOST127.0.0.1FreePBX/Asterisk host.
SIP_SERVER_PORT5060PBX SIP port.
SIP_TRANSPORTudpSIP transport; only UDP is supported.
SIP_USERNAME200SIP extension/username.
SIP_AUTH_USERNAMEemptyAuthentication username; falls back to the extension where applicable.
SIP_PASSWORDemptySIP password.
SIP_LOCAL_HOST0.0.0.0Local bind address.
SIP_LOCAL_PORT5062Local SIP UDP port.
SIP_ADVERTISE_HOST127.0.0.1Address advertised to the PBX in SIP/SDP.
SIP_RTP_PORT_START / SIP_RTP_PORT_END40000 / 40100RTP UDP port range.
SIP_CODECpcmuG.711 codec: pcmu or pcma.

Voice and agent

VariableDefaultDescription
VOICE_PROVIDERopenaiVoice provider; currently OpenAI only.
VOICE_API_KEYrequiredOpenAI API key.
VOICE_BASE_URLwss://api.openai.com/v1/realtimeRealtime WebSocket endpoint.
VOICE_MODELgpt-realtime-2.1Realtime model name.
VOICE_VOICEmarinOpenAI Realtime voice.
VOICE_SPEED1.0Speaking speed (0.25–4.0).
AGENT_NAMEAgentDisplayed agent name.
AGENT_DEFAULT_LANGUAGEfrSession language, such as fr, en, or hy.
AGENT_INBOUND_PROMPT / AGENT_OUTBOUND_PROMPTbuilt inDirection-specific system prompts.
AGENT_INBOUND_BRIEFemptyRecipients or context for incoming calls.
AGENT_CALLER_ID200Outbound caller identity.
AGENT_MAX_RINGS6Maximum 180 Ringing responses (1–20).
AGENT_MAX_RING_SECONDS30Outbound ring timeout (5–300 seconds).
AGENT_MAX_AGENT_TURNS3Maximum agent turns used by silence handling (1–20).
AGENT_END_GRACE_SECONDS4.0Grace period before auto-hangup after a goodbye.

MCP, webhooks, ambience, and UI

VariableDefaultDescription
MCP_ENABLEDtrueCompatibility setting; the MCP service is always enabled.
MCP_HOST127.0.0.1MCP HTTP bind address (.env.docker.example uses 0.0.0.0).
MCP_PORT8765MCP HTTP port.
MCP_AUTH_TOKENemptyOptional Bearer token.
WEBHOOK_ENABLEDtrueCompatibility setting; delivery occurs when a URL is configured.
WEBHOOK_URL / WEBHOOK_URL2emptyPrimary and secondary receiver URLs.
WEBHOOK_AUTH_TOKENemptyBearer token and HMAC secret.
WEBHOOK_NOTIFY_PARTIALS_INCOMINGfalseSend partial transcript events for inbound calls.
WEBHOOK_NOTIFY_PARTIALS_OUTGOINGtrueSend partial transcript events for outbound calls.
AMBIENT_ENABLEDfalseMix background audio into calls (.env.docker.example enables it).
AMBIENT_FILEoffice.wavFile from assets/ambient/.
AMBIENT_VOLUME0.12Mix level (0.0–0.5).
WEB_ENABLEDtrueEnable the web application.
WEB_USERNAMEadminWeb UI username.
WEB_PASSWORDadminWeb UI password—change it.

License and disclaimer

No license file is currently included in this repository; all rights remain with the copyright holder unless a license is added. Agent-SIP can place real telephone calls—follow local calling, recording, consent, privacy, and emergency-services laws, and secure all credentials before deployment.

About

Bridge between SIP server and OpenAI Realtime voice agent, controlled by Hermes via MCP

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages