Repository navigation
MCP Tools Relay
The relay bundle puts questions to the configured media relay and decodes
the relay control messages sipnab captured.
Load only these tools with --mcp-tools relay, or combine bundles as
Choosing which tools load describes.
Every tool on this page follows the rules every tool
follows, and each section states only
what its tool does differently. The MCP tool reference lists
the other bundles and finds any tool by name.
| Tool | Parameters | Returns |
|---|---|---|
decode_ng |
frame_ref |
Decodes the relay control message one pointer names: the command, the call, whether it carries SDP, and which delivery path carried it |
query_relay |
call_id?, max_calls?
|
Transmits. Asks the configured relay what it holds right now. Needs --mcp-allow-relay-query and a live source; the destination comes from operator configuration only |
relay_stats |
call_id?, names_only?
|
Transmits. Asks the configured relay for its own counters (global, per-call, or the names it knows), tiered relay_reported. Same opt-in and live-source gate as query_relay
|
relay_compare |
call_id |
Transmits. Sets the relay's per-call RTP count beside sipnab's measured count, both tiers named and a word verdict, never a sum. Same gate as query_relay
|
Every finding a tool reports points back at captured bytes. These retrieve those bytes, prove which frame they came from, and package them so someone else can check the work.
decode_evidence decodes a SIP frame. This decodes a relay control message,
and adds the question no other surface answers: which path carried it, and did
that path authenticate whoever sent it.
That distinction decides what the message is worth. Anything on the segment can put a control message on the HEP port, and landing there is the whole reason sipnab credits it -- whereupon it names a call and binds media to an address its sender chose. One delivered to an HMAC-authenticated listener is a different claim entirely. Both decode to the same bytes.
delivery is hep or sniffed-udp. delivery_trust uses the same scale as
explain_attribution, and here it is
always port-gated-only. decode_ng reads the frame back out of a capture file: no HEP
listener on this run admitted it, and decode_ng does not verify a shared
secret or HMAC token, so --hep-auth-mode does not raise it. on_believed_mirror_port stands
apart because it is the ONLY reason anything believes a sniffed message at all,
and a reader should see the whole of that reason rather than its conclusion.
Status is verified, unverified or unresolvable, as in
decode_evidence, and a pointer leading nowhere returns
a reason rather than failing the call.
The tools that change server state rather than read it. Every one stays off until you turn it on server-side.
This tool transmits, as relay_stats and relay_compare do. Every other
tool answers from bytes sipnab already holds. These three put a packet on the
network.
It closes a gap a passive decoder cannot. A call already in progress when sipnab started has no control exchange left to read, so the relay's own view is the only way to learn which ports belong to it -- and incident response usually begins mid-call, which is exactly when that gap is worst.
The tool needs three things, and says which one is missing:
| requirement | why |
|---|---|
--mcp-allow-relay-query |
transmitting is a larger act than reading, so an operator opts in |
| the relay control flag | names which relay to ask |
| a live source | a run reading a file can obtain no transmit permit |
There is no address parameter, deliberately. The destination comes from operator configuration and from nowhere else. A tool argument naming the destination would turn this surface into a way to make sipnab send packets to a host the caller chose, and an address sipnab could otherwise infer is one it learned from packets -- a host that served as a relay during the capture, and may be somebody's laptop now.
| Name | Type | Legal values | If omitted |
|---|---|---|---|
call_id |
string? | One Call-ID to ask the relay about. | Switches modes — the tool enumerates what the relay holds. |
max_calls |
u32? | Enumeration only. A cap on the Call-IDs the relay returns. | The relay's own default. |
max_calls takes its name from what it bounds rather than from limit,
because it is the relay's own list argument traveling to another process
rather than a page over data sipnab holds. rtpengine warns that raising it may push the answer past
one UDP datagram, and sipnab reports a truncated answer rather than padding it.
Omit call_id to enumerate. truncated reports that the relay held more than
it returned, because "the relay holds these 32 calls" and "the relay returned
the first 32 of an unknown number" are different statements.
// query_relay { "call_id": "call-2c9d47@192.0.2.10" }
{
"asked": "query",
"relay_address": "127.0.0.1:22222",
"outcome": "call",
"call_id": "call-2c9d47@192.0.2.10",
"tags": [
{
"tag": "from-tag-9a1c",
"in_dialogue_with": ["to-tag-4b7e"],
"codec": "PCMU",
"streams": [
{
"local_address": "192.0.2.10",
"local_port": 30000,
"endpoint": "198.51.100.4:16388",
"is_rtcp": false,
"ssrcs": [305419896]
}
]
}
],
"delivery_trust": "asked",
"delivery_note": "sipnab asked the relay over its control socket; no third party could answer",
"schema_version": 1
}Transmits, like query_relay. It asks the configured relay
for the statistics it keeps about ITSELF -- never a figure sipnab derived -- so
each value carries the tier relay_reported and never blends with what sipnab
measured. That comparison is relay_compare.
Three modes from two parameters:
| Name | Type | Legal values | If omitted |
|---|---|---|---|
call_id |
string? | One Call-ID, to scope the counters to that call. | The relay's own global counters. |
names_only |
bool? |
true returns the NAMES the relay knows, not their values -- "what can I even ask for", since the key set is version-specific. |
false. A call_id overrides it. |
It needs the same three things query_relay does, and refuses identically when
one is missing: --mcp-allow-relay-query, a configured relay control address,
and a live source.
A relay that answers but declines -- a Call-ID it does not hold -- is a
success carrying outcome: "refused" and the relay's own words, not an
error: the relay answered, it just said no. An unreachable relay is the error,
because nothing is then known.
// relay_stats { "call_id": "call-2c9d47@192.0.2.10" }
{
"relay_address": "127.0.0.1:22222",
"tier": "relay_reported",
"outcome": "ok",
"statistics": [
{ "name": "totals.RTP.packets", "value": "9000" },
{ "name": "totals.RTP.bytes", "value": "1440000" }
],
"delivery_trust": "asked",
"delivery_note": "sipnab asked the relay over its control socket; no third party could answer",
"schema_version": 1
}A names_only answer carries names, a names_source (listed when the relay
enumerated them, probed when they are the names that did not refuse) and a
names_note stating how sipnab determined the set -- so a probed set is never
read as a definitive enumeration.
Transmits, like query_relay. It sets the relay's own RTP
packet count for one call beside the count sipnab measured from the packets it
captured. A comparison, never a sum: both figures travel with their tiers
(relay_reported and sipnab_measured), the verdict is a word, and it never
computes relay - sipnab as a value -- the two count different sockets over
different windows, so an ordinary gap is not a relay fault.
| Name | Type | Legal values | If omitted |
|---|---|---|---|
call_id |
string | The Call-ID to compare. | Required -- the call fails. |
Same opt-in and live-source gate as relay_stats. It reports an absent side as
absent, never coercing it to zero: a call the relay does not hold carries sipnab's
count and no relay figure (outcome: "relay_does_not_hold_call"), and a call
sipnab never captured carries the relay's figure and no measured one
(outcome: "sipnab_has_no_rtp"). Only when both sides produced a count -- a
real zero included -- are they compared.
// relay_compare { "call_id": "call-2c9d47@192.0.2.10" }
{
"relay_address": "127.0.0.1:22222",
"call_id": "call-2c9d47@192.0.2.10",
"outcome": "compared",
"relay_reported": { "value": 9000, "name": "totals.RTP.packets" },
"sipnab_measured": 9000,
"verdict": "match",
"note": "the relay and sipnab agree at 9000 RTP packets",
"delivery_trust": "asked",
"delivery_note": "sipnab asked the relay over its control socket; no third party could answer",
"schema_version": 1
}Website · Repository · Issues · Generated from docs/ — edit there, not here.
Getting started
- Installation
- First CLI Triage
- Glossary
- Cookbook
- Runnable Examples
- Real World Captures
- Troubleshooting
- Tuning Capture
- Encapsulations
- Rtpengine Relays
- Backers
Using the TUI
CLI & automation
- CLI Reference
- Error Messages
- Filter DSL
- Output Formats
- SIP Response Codes
- SIP Methods
- SIP Header Fields
- SIP Parameters
- SIP Lint Rules
- MOS and Codecs
Configuration
Integrations (API & MCP)
- REST API
- Prometheus Metrics
- Authentication
- MCP
- MCP Deployment
- MCP Across an Estate
- MCP Tools
- MCP Tools Core
- MCP Tools Signaling
- MCP Tools Captures
- MCP Tools Security
- MCP Tools Media
- MCP Tools Relay
- MCP Tools TFPS
- MCP Tools Server
- MCP Tools vCon
- MCP Tools TLS
- MCP Protocol
- Uprobe Walkthrough
- Capture SIP over TLS
- WASM Plugins
- vCon Export
- vCon Capture Stack
- G729 Decoding
Your SIP server
- Use OpenSIPS as the SIP Server
- Run sipnab beside OpenSIPS
- Use Kamailio as the SIP Server
- Run sipnab beside Kamailio
- Use Asterisk as the PBX
- Run sipnab beside Asterisk
- Use FreeSWITCH in the Voice Stack
- Run sipnab beside FreeSWITCH
Add to your voice stack
- Add a vCon Server
- Send vCons to a vCon Server
- Send vCons to vcon store
- Add TFPS
- Control TFPS from sipnab
- Ban SIP Scanners with fail2ban
- Feed fail2ban from sipnab
- Add rtpengine
- Name rtpengine Media with sipnab
- Add rtpproxy
- Name rtpproxy Media with sipnab
- Add Homer
- Connect sipnab to Homer
- Add Prometheus and Grafana
- Add sipnab Metrics to Prometheus
Development & internals
- Internals Index
- Internals Subsystem Guide
- Internals Invariants
- Internals Testing
- Internals Walkthroughs
- Internals Build CI Release
- Internals Profiling
- Internals Domain Primer
- Library API
- Benchmarks
- Fault Model
- Threat Model
- Assurance Case
- Architecture
- Internals Threading
- Internals TUI Testing
- Internals Zero Copy Payloads
- Internals Uprobe Capture
- Internals Rtpengine Control Plane
- Internals Relay Control Decoding
- Internals vCon Exporter
- Internals Operator Guide Notes
- Internals Postmortems
- Internals Code Map
- Internals Lint Rules
- Internals Encapsulation Decoding
- Internals MCP Design Notes
- Internals Metrics And Output Notes