Read-only monitoring integration: stick with AMI, or move to ARI?

I wrote a small server that lets an AI assistant answer questions about a running Asterisk box, so instead of guessing you can ask it which endpoints are offline, or why a trunk isn’t registering, and it goes and looks. It talks AMI over TCP and exposes a handful of read operations: core status, live channels, PJSIP endpoints with device state (falling back to chan_sip peers on older installs), a dialplan dump, and a CLI passthrough that only accepts an allow list of show commands.

It’s read-only out of the box. Originate and hangup exist, but they aren’t even registered as available tools unless you turn write mode on, and the manager.conf user I document gets read = system,call,command and write = command, nothing else.

Two things I’d like an opinion on from people who have run this sort of thing for real.

First, for pure inspection, is AMI still the sensible interface, or would you build new work on ARI? I went with AMI because the Command action hands me every CLI verb for free and I couldn’t find an equivalent shortcut in ARI. But I’d rather not be building on the older interface if the direction of travel is obvious to everyone but me.

Second, parsing Command output is the fragile part. Something like pjsip show endpoints is formatted for a human to read, and the columns shift between versions. Is there a version-stable way to get that data, or is scraping the CLI just what everybody does and accepts?

One smaller thing while I’m here. I strip carriage returns and newlines out of every field that goes into an AMI action, so a caller ID string can’t smuggle in an extra header. Is that actually needed on current versions, or does the parser already reject it? I couldn’t find a straight answer and defaulted to being careful.

The repo, for context: GitHub - ictinnovations/pbx-mcp: MCP server for Asterisk and FreeSWITCH. Lets AI assistants inspect channels, SIP registrations, trunk status and dialplan on a live PBX. · GitHub

There has been some missing creep in ARI, but AMI is still the intended tool for such functions.

I suppose one good thing about commands like pjsip show endpoings which return multi-item responses is that AMI now tells you, in the EventList header line, that a multi-item response is starting or finishing. Earlier versions of AMI required you to guess.

I think ARI will give you this kind of data back in a more easily parsed JSON format.

In terms of newlines, yes, I make sure to strip them from AMI values in my Seaskirt wrapper. Note also the quoting rules that apply to AGI arguments.

Yeah, that matches how we ended up doing it. When we need to originate a leg or fire a management action we go straight to AMI — it’s the boring, stable surface and it does exactly one thing. We keep ARI out of the media path entirely and let AudioSocket carry that.

The mission-creep framing is fair though. The overlap makes it genuinely unclear to newcomers which tool owns which job, and you kind of have to learn the
intended split by osmosis.

Yeah those EventList start/complete headers quietly saved us. Before we leaned on them we were basically guessing where a list ended and getting it wrong the moment things got busy.

The one that really got us was command-style responses on 14+, where multiline CLI output collapses down to just the last Output: line. String-cast that and you lose everything above it without a single error in the logs, so you sit there staring at clean output wondering why half the data’s gone. We stopped flattening it and started treating the response as the array it actually is.

And yeah, stripping newlines once at the wrapper is the right call. Way better than hoping every caller remembers to. AGI quoting is its own little trap
too.

The whole “zoo” of Asterisk APIs seems to have grown over the years/decades in a, shall we say, less than disciplined fashion. Seems like, every time somebody identified a gap in the existing ways to interface to Asterisk, a new way was added, with little thought for how it would fit into the existing framework.

Example: after AMI and AGI were added, it was realized that some of the latter functionality would be useful in the former as well. And so the AsyncAGI (aka AGI-in-AMI) feature was added.

Example: the option to do AMI connections over HTTP was added, as an alternative to the bespoke AMI protocol. And then somebody changed their minds, and this became deprecated later. But the HTTP config was reused for ARI.

Example: the use of the term “application” by ARI. Did somebody forget that was also the term for those commands you use in dialplans? So now we have to be clear which usage of “application” we mean — on top of its more general meaning outside Asterisk itself (what happens when we talk about “applications” of Asterisk to solve business problems?).

Note also the similarity between the “stasis” state that a channel goes into with ARI, with what happens with AsyncAGI — in both cases, the channel goes into a state where further action is under the control of an external program. And there is some commonality in the actions you can perform in both cases; but the way you go about this is different, of course.

Example: the addition of WebSocket connections, entirely separate to the existing AudioSocket functionality.

By the way, did you know AMI supports both challenge-response authentication and TLS-encrypted connections? If the TLS encryption worked in both directions, that would do away with the need for usernames and passwords altogether. But ARI I think only does “basic” HTTP authentication.

Ido that was beautifully stated my friend. Good read!