Skip to main content
Glama
paralelum

ipbx-mcp

by paralelum

ipbx-mcp

MCP-Server des IPBX in TypeScript. Streamable-HTTP-Transport im zustandslosen Modus, Authentifizierung per statischem Bearer und/oder OAuth 2.1 + Google Workspace, lokale Persistenz in SQLite (OAuth-Clients, Refresh-Tokens, Audit-Log). Vom Scaffold base-mcp übernommen, stellt die PABX-Daten (MySQL) als typisierte Tools bereit.

Öffentliche URL in Produktion: https://mcp.ipbx.vivavox.com.br.

Anforderungen

  • Node.js >= 22 (better-sqlite3 v12 benötigt das)

  • Für OAuth: OAuth-Client in der Google Cloud Console im Modus Intern

Related MCP server: utel-mcp

Installation

npm install
cp .env.example .env   # depois preencha os valores reais
npm run build

Konfiguration

Laden Sie die .env in den Prozess (systemd EnvironmentFile=, docker env_file:, oder node --env-file=.env beim Start).

Pflichtfelder

Mindestens einer der Auth-Pfade:

Variable

Wann verwenden

MCP_AUTH_TOKEN

Statischer Bearer — Claude Desktop, CLI, API, Skripte, Cron

OAUTH_JWT_SECRET + OAUTH_ISSUER

OAuth — Clients über claude.ai (Web/Mobil)

OAuth (optional, aber für claude.ai erforderlich)

Variable

Beschreibung

OAUTH_ISSUER

Kanonische URL des Servers (z. B. https://mcp.ipbx.vivavox.com.br)

OAUTH_JWT_SECRET

HS256-Schlüssel der JWTs (32 Bytes hex)

GOOGLE_CLIENT_ID

Vom OAuth-Client in der Google Cloud Console

GOOGLE_CLIENT_SECRET

Vom OAuth-Client in der Google Cloud Console

ALLOWED_GOOGLE_HD

Erlaubte Workspace-Domain (Standard: vivavox.com.br)

Wenn alle vorhanden sind, werden die Routen /authorize, /oauth/google/callback, /token und /register (DCR) gemountet. Ohne sie funktioniert nur der statische Bearer.

Weitere

Variable

Standard

Beschreibung

PORT

3000

HTTP-Port

HOST

0.0.0.0

Schnittstelle (lokal in der Entwicklung 127.0.0.1 verwenden)

MCP_ALLOWED_HOSTS

CSV-Liste der im Host-Header akzeptierten Hosts

SQLITE_PATH

./data/app.db

Pfad der SQLite-Datei

IPBX_RECORD_BASE_URL

Basis der IPBX-API, die die Aufzeichnungen ausliefert (z. B. https://ipbx.vivavox.com.br/api). Ohne sie schlägt ipbx_recording_get fehl

MySQL (Datenquelle des IPBX)

Variable

Standard

Beschreibung

MYSQL_HOST

MySQL-Host

MYSQL_PORT

3306

MYSQL_USER

Dedizierten Benutzer mit ausschließlich GRANT SELECT verwenden

MYSQL_PASSWORD

MYSQL_DATABASE

MYSQL_POOL_LIMIT

5

Pool-Größe (mysql2)

MYSQL_SSL

leer

Jeder Wert aktiviert TLS mit Zertifikatsprüfung

IPBX_ID

Tenant, den diese Instanz bedient (siehe unten)

Die Datenbank ist mandantenfähig — eine Asterisk-Instanz pro Kunde, Tabelle ipbx — aber jede MCP-Instanz bedient genau einen Tenant. Alle Abfragen filtern nach IPBX_ID, und keine Tool akzeptiert diese ID als Parameter: So hängt die Isolierung zwischen Kunden nicht davon ab, was das Modell im Aufruf übergibt. Ein Container und eine Subdomain pro Tenant.

Generieren Sie zufällige Tokens mit:

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Endpunkte

Methode

Pfad

Auth

Beschreibung

POST

/mcp

bearer

JSON-RPC des MCP über Streamable HTTP

GET

/mcp

bearer

405

DELETE

/mcp

bearer

405

GET

/health

öffentlich

{"status":"ok"}

GET

/.well-known/oauth-authorization-server

öffentlich

RFC-8414-Metadaten

GET

/.well-known/oauth-protected-resource

öffentlich

RFC-9728-Metadaten

POST

/register

öffentlich

Dynamic Client Registration (RFC 7591)

GET

/authorize

öffentlich

Leitet zu Google weiter

GET

/oauth/google/callback

öffentlich

Empfängt den Redirect von Google

POST

/token

öffentlich

authorization_code / refresh_token

401 bei /mcp enthält WWW-Authenticate: Bearer realm=..., resource_metadata=... — ohne das entdeckt claude.ai den AS beim ersten Kontakt nicht.

Verfügbare Tools

Die Tool-Namen folgen dem Schema ipbx_<model>_<action>, wobei <action> aus dem Vokabular list / get / search / count stammt.

ipbx_instance_get

Stammdaten der IPBX-Instanz, die dieser Server bedient — Name, IP und SIP/AMI-Ports.

Parameter: keine. Die Instanz ist fest, definiert durch IPBX_ID in der Umgebung.

Rückgabe:

{
  "id": 1,
  "shortname": "vivavox",
  "fullname": "Vivavox Telecom",
  "ipaddr": "138.94.55.155",
  "sipport": 5601,
  "amiport": 6501,
  "created": "2024-06-17T16:37:59.000Z",
  "updated": "2024-06-17T16:37:59.000Z"
}

Gibt isError zurück, wenn die konfigurierte IPBX_ID in der Tabelle ipbx nicht existiert.

ipbx_branch_list

Listet die Nebenstellen der Instanz.

Parameter:

  • search (string, optional): Teilsuche nach Nebenstelle-Nummer oder Name

  • limit (number, optional): 1–500, Standard 100

Rückgabe:

{
  "total": 27,
  "truncated": false,
  "branches": [
    {
      "id": 2,
      "exten": "23",
      "name": "Ricardo Landim",
      "group": "Suporte",
      "record": true,
      "webrtc": false,
      "dtmf": "rfc4733",
      "forward_busy": "035988023317",
      "forward_noanswer": "035988023317",
      "forward_noanswer_wait": 5
    }
  ]
}

Gibt die SIP-Zugangsdaten nicht zurück. Die Spalten password (Passwort im Klartext) und username (Authentifizierungskennung, anders als die Nebenstelle-Nummer) bleiben bewusst außen vor — zusammen erlauben sie, ein Softphone zu registrieren und Anrufe auf dem Kundenkonto zu tätigen. Die Spaltenliste im SELECT ist genau deshalb explizit, damit keine davon versehentlich hineingerät.

ipbx_user_list

Listet die Panel-Benutzer der Instanz.

Parameter:

  • search (string, optional): Teilsuche nach Name oder E-Mail

  • limit (number, optional): 1–500, Standard 100

Rückgabe:

{
  "total": 6,
  "truncated": false,
  "users": [
    {
      "id": 11,
      "name": "Suporte",
      "email": "suporte@vivavox.com.br",
      "created": "2024-07-10T13:56:41.000Z",
      "updated": "2024-07-10T13:56:41.000Z"
    }
  ]
}

Gibt das Zugangspasswort nicht zurück. Die Spalte secret bleibt außen vor: Es ist das Panel-Login-Passwort, im Klartext in der Datenbank gespeichert (ohne Hash). Das offenzulegen würde administrativen Zugriff auf den PABX gewähren.

ipbx_group_list

Listet die Nebenstelle-Gruppen der Instanz, jeweils mit der Anzahl der Nebenstellen.

Parameter:

  • search (string, optional): Teilsuche nach Name oder Beschreibung

  • limit (number, optional): 1–500, Standard 100

Rückgabe:

{
  "total": 6,
  "truncated": false,
  "groups": [
    {
      "id": 1,
      "name": "Suporte",
      "description": "Grupo do suporte",
      "branches": 11
    }
  ]
}

Die Tabelle groups speichert keine Zugangsdaten — anders als bei branch und users werden hier alle Spalten offengelegt.

ipbx_trunk_list

Listet die Trunks der Instanz.

Parameter:

  • search (string, optional): Teilsuche nach Name oder Host

  • limit (number, optional): 1–500, Standard 100

Rückgabe:

{
  "total": 2,
  "truncated": false,
  "trunks": [
    {
      "id": 1,
      "name": "Vivavox",
      "host": "sip.vivavox.com.br",
      "port": "5060",
      "register": true,
      "record": true,
      "auth": "credentials"
    }
  ]
}

Gibt die Zugangsdaten des Anbieters nicht zurück. username und password bleiben außen vor — sie sind die wertvollste Zugangsdaten der Datenbank, da sie es erlauben, Anrufe direkt über den Anbieter zu tätigen, abgerechnet auf dem Konto. Stattdessen kommt auth, das nur angibt, wie der Trunk authentifiziert: "credentials" (Benutzer/Passwort) oder "ip" (IP-Allowlist, ohne Passwort).

ipbx_queue_list

Listet die Warteschlangen mit der Verteilungsstrategie und der Anzahl der Mitglieder.

Parameter: search (string, optional), limit (1–500, Standard 100)

{
  "total": 5,
  "queues": [
    { "id": 1, "name": "Suporte", "strategy": "ringall", "members": 8 },
    { "id": 5, "name": "Teste", "strategy": "leastrecent", "members": 1 }
  ]
}

ipbx_queue_member_list

Listet die Mitglieder der Warteschlangen in der Rufreihenfolge.

Parameter:

  • queue_id (number, optional): filtert eine Warteschlange; weglassen, um alle zu holen

  • limit (number, optional): 1–500, Standard 200

Rückgabe:

{
  "total": 8,
  "members": [
    {
      "queue_id": 1,
      "queue": "Suporte",
      "position": 1,
      "type": "branch",
      "exten": "29",
      "name": "Mateus Damaceno",
      "ref": "branch-10"
    }
  ]
}

Die Spalte queue_member.member speichert eine Referenz im Format <Typ>-<ID>branch-10 zeigt auf branch.id 10, also Nebenstelle 29. Es ist nicht die Nebenstelle-Nummer. Die Tool löst das zu exten + name auf, wenn das Mitglied eine Nebenstelle ist. Nicht jedes Mitglied ist eine: Es gibt Einträge redirect-N, die mit type: "redirect" und exten/name null zurückkommen.

ipbx_ivr_list

Listet die IVRs mit der zugehörigen Audiodatei und der Transkription dessen, was dem Anrufer angesagt wird.

Parameter: search (string, optional — matcht auf Name oder Transkriptionstext), limit (1–500, Standard 100)

{
  "total": 1,
  "ivrs": [
    {
      "id": 5,
      "name": "URA Rompimento",
      "audio": "URA Rompimento",
      "transcription": "Olá, se você está com falta de conexão e o LED Loss do seu modem óptico...",
      "options": 1
    }
  ]
}

Die Transkription ist das nützlichste Feld: Sie erlaubt es, einen IVR über das zu finden, was er ansagt, nicht nur über den Namen.

ipbx_ivr_option_list

Listet die Optionen der IVRs — welche Taste zu welchem Ziel führt.

Parameter:

  • ivr_id (number, optional): filtert einen IVR; weglassen, um alle zu holen

  • limit (number, optional): 1–500, Standard 200

Rückgabe:

{
  "total": 7,
  "options": [
    {
      "ivr_id": 1,
      "ivr": "URA Principal - Horario comercial",
      "digit": "1",
      "goto": { "type": "queue", "name": "Financeiro", "exten": null, "ref": "queue-3" }
    },
    {
      "ivr_id": 1,
      "ivr": "URA Principal - Horario comercial",
      "digit": "7X",
      "goto": { "type": "internal", "name": null, "exten": null, "ref": "internal" }
    }
  ]
}

ivr_option.goto ist polymorph: Es zeigt auf 5 verschiedene Tabellen (branch, queue, ivr, redirect, app) im Format <Typ>-<ID> und akzeptiert außerdem Literale ohne ID (internal). Die Tool löst den Zielnamen in allen Fällen auf; Literale kommen mit name null und erhaltenem ref zurück.

Das Feld digit ist nicht immer eine Ziffer: t ist Timeout, und Muster wie 7X matchen Nebenstelle-Bereiche.

ipbx_redirect_list

Listet die Redirects — kurze Nebenstellen, die zu einer externen Nummer über einen Trunk weiterleiten. Es sind dieselben redirect-<ID>, die als Ziel in Warteschlangen, IVRs und Routing-Regeln erscheinen.

Parameter: search (string, optional — matcht Nebenstelle, Name oder Nummer), limit (1–500, Standard 100)

{
  "total": 12,
  "redirects": [
    {
      "id": 2,
      "exten": "73",
      "name": "Ricardo Landim",
      "forward": "5535988023317",
      "trunk": "Vivavox",
      "ref": "redirect-2"
    }
  ]
}

⚠️ Personenbezogene Daten. forward ist in 100 % der Zeilen eine private Mobilnummer — keine Zugangsdaten, aber personenbezogene Daten im Sinne der LGPD. Die Tool gibt sie zurück, weil das der Existenzgrund der Tabelle ist, aber sie geht nicht in das audit_log.

ipbx_routing_list

Listet die Routing-Pläne, jeweils mit der Anzahl der Regeln und Zeitfenster.

Parameter: search (string, optional), limit (1–500, Standard 100)

{
  "total": 2,
  "routings": [
    { "id": 1, "name": "Entrada - Padrão", "rules": 6, "time_windows": 3 },
    { "id": 2, "name": "Saida - Padrão", "rules": 8, "time_windows": 1 }
  ]
}

ipbx_routing_time_list

Listet die Zeitfenster der Pläne.

Parameter: routing_id (number, optional), limit (1–500, Standard 100)

{
  "id": 1,
  "routing": "Entrada - Padrão",
  "name": "Horario comercial",
  "ranges": ["08:00-18:00,mon", "08:00-18:00,tue", "08:00-12:00,sat"]
}

Das pattern wird im Asterisk-Format gespeichert, ein Bereich pro Zeile; die Tool gibt es als Liste zurück.

ipbx_routing_rule_list

Listet die Routing-Regeln — den Dialplan. Jede Regel matcht ein Nummern-Muster innerhalb eines Zeitfensters, unterdrückt Ziffern, fügt ein Präfix hinzu und sendet an das Ziel.

Parameter: routing_id (number, optional), limit (1–500, Standard 200)

{
  "id": 4,
  "routing": "Saida - Padrão",
  "name": "LDN",
  "time_window": "Geral",
  "match": "0ZZ.",
  "suppress": 1,
  "prefix": "55",
  "goto": { "type": "trunk", "name": "Vivavox", "exten": null, "ref": "trunk-1" }
}

goto1 ist polymorph wie bei der IVR, zusätzlich mit dem Typ trunk (bei ausgehenden Regeln verwendet) — insgesamt sechs mögliche Ziele.

Zwei Schema-Details werden hier behandelt: Die Datenbankspalte heißt supress (mit einem „p"), exponiert als suppress; und goto2/goto3 existieren, sind aber in allen Zeilen leer — sie erscheinen als goto_extra nur, falls sie jemals befüllt werden.

ipbx_cdr_list

Anrufverlauf. Der Zeitraum ist Pflicht und auf 31 Tage begrenzt: Die cdr hat außer der PK keinen Index, daher ist jeder Filter ein Full Scan (~268k Zeilen heute).

Parameter: date_from und date_to (YYYY-MM-DD, Pflichtfelder), scope (call | leg, Standard call), src und dst (teilweise), branch_id, trunk_id, answered (bool), call_id, limit (1–500, Standard 25)

{
  "call_id": "sip1-1787578699.251937",
  "started": "2026-08-24 10:38:19",
  "ended": "2026-08-24 10:42:06",
  "direction": "inbound",
  "from": { "type": "trunk", "id": 1, "name": "Vivavox" },
  "caller": "35997609940",
  "dialed": null,
  "context": "queue-3",
  "answered": true,
  "talk_seconds": 265,
  "ring_attempts": 6,
  "targets": [{ "type": "branch", "id": 16, "exten": "35", "name": "Ester Vilela" }],
  "answered_by": [{ "type": "branch", "id": 16, "exten": "35", "name": "Ester Vilela" }],
  "dispositions": ["ANSWERED", "NO ANSWER"],
  "has_recording": true,
  "legs": 10
}

Die cdr ist die einzige Tabelle der PABX ohne ipbx_id. Die Verbindung zum Tenant ist der systemname von Asterisk, den die ipbx-api als sip<ipbx_id> schreibt und Asterisk in uniqueid/linkedid jeder Zeile stempelt — der Filter ist uniqueid LIKE 'sip<id>-%', mit Bindestrich (ohne ihn würde sip1 auch sip10- matchen).

Ein Anruf besteht aus vielen Zeilen: uniqueid identifiziert den Kanal, linkedid den Anruf, und jeder Dial-Versuch erzeugt eine Zeile — ein eingehender Queue-Anruf kommt auf 22. scope=call gruppiert nach linkedid; die Beine, deren Ziel ein Local/-Kanal ist, sind das Klingeln der Queue bei jedem Mitglied (werden zu ring_attempts), und die übrigen sind Gespräch (summieren talk_seconds). scope=leg liefert die rohen Beine — verwende es mit call_id, um einen Anruf zu debuggen.

has_recording erfordert ANSWERED zusätzlich zu gefülltem rec — dieselbe Regel wie recAvailable im Panel. Die Spalte rec wird vor dem Dial geschrieben (das Dialplan richtet MixMonitor im prerouting ein), also markiert sie „Aufnahme scharfgeschaltet" und nicht „Audio vorhanden": allein würde sie bei 99,96 % der Anrufe eine Aufnahme ergeben.

Keine Kanal-Spalte kommt roh heraus: channel, dstchannel und lastdata tragen den username des Endpoints, der die Hälfte der SIP-Anmeldedaten ist, und src bringt denselben Username bei internen Anrufen. Alles läuft durch src/channel.ts und kommt als Nebenstelle/Trunk/Queue heraus. rec bleibt ebenfalls außen vor — es wird zu has_recording.

Das Filtern nach branch_id/trunk_id wählt die Anrufe per Semi-Join aus, nicht per Zeile: Die Aggregate beschreiben weiterhin den gesamten Anruf, nicht nur die Beine dieser Nebenstelle.

ipbx_recording_get

URL des Audios eines Anrufs, ausgehend von der call_id, die ipbx_cdr_list zurückgibt.

Parameter: call_id (string, Pflichtfeld)

{
  "call_id": "sip1-1787577145.251772",
  "started": "2026-08-24 10:12:25",
  "has_recording": true,
  "url": "https://ipbx.vivavox.com.br/api/call/record/sip1-8f0e5161….wav",
  "note": "URL publica e sem expiracao: o nome do arquivo e a unica credencial. …"
}

Es ist aus einem Grund ein separates Tool statt eines Felds von ipbx_cdr_list: Die Route /call/record der ipbx-api erfordert keine Authentifizierung und die URL läuft nicht ab — der Dateiname (SHA1) ist die Anmeldedaten. Als Listungsfeld würde jeder CDR-Anruf 25 dauerhafte Zugriffe auf Gespräche im Kontext ausgeben, fast alle nie genutzt, und das Audit müsste 25 Anmeldedaten protokollieren oder nichts registrieren. Ein Tool pro Aufnahme ergibt eine Audit-Zeile mit der Identität des Anforderers. Das has_recording des CDR ist das Entdeckungssignal; dieses Tool ist der Zugriff.

Ohne Audio nennt die Antwort den Grund, statt nur abzulehnen — Chamada nao atendida (die Aufnahme wird vor dem Dial scharfgeschaltet) oder Aufnahme an der Nebenstelle deaktiviert. Eine call_id eines anderen Tenants liefert isError: Der Filter nach IPBX_ID wird in der Query angewendet, und das sip<id> der URL stammt aus der Umgebung, nie aus der empfangenen call_id.

Hängt von IPBX_RECORD_BASE_URL ab. Ohne sie startet der Server normal und nur dieses Tool schlägt fehl, mit expliziter Meldung — so, als würde man das Tool auf einem Server deaktivieren.

Jeder Anruf erzeugt eine Zeile in audit_log mit der Identität des Aufrufers: Google-E-Mail bei JWT, service:static bei statischem Bearer. src/dst von ipbx_cdr_list sind Telefonnummern und gehen nicht ins Audit — es bleibt nur number_filter: true.

Befehle

npm run build      # tsc
npm run check      # tsc --noEmit (sem emitir)
npm run dev        # tsc --watch
npm start          # node dist/index.js
npm run inspect    # MCP Inspector

Lokaler Smoke-Test:

curl -s http://localhost:3000/health
curl -s http://localhost:3000/.well-known/oauth-authorization-server
curl -s -X POST http://localhost:3000/mcp \
  -H "Authorization: Bearer $MCP_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Deploy

Docker (empfohlen)

Multi-Stage-Dockerfile (node:22-slim), Runtime als Nicht-Root-Benutzer mcp, exponiert /data als Volume für SQLite, Healthcheck über /health. In Produktion erfolgt das Deploy automatisch über .github/workflows/deploy.yml (Push des Tags vX.Y.Z → Build in GHCR → docker run auf der VPS). Manuell:

docker image build . -t ipbx-mcp:1.0

docker container run -d --env-file .env -p 50020:3000 \
  -v ipbx_data:/data --restart unless-stopped --name ipbx-mcp ipbx-mcp:1.0

docker stop ipbx-mcp && docker rm ipbx-mcp
docker logs -f ipbx-mcp

SQLite-Backup:

docker run --rm \
  -v ipbx_data:/data \
  -v $PWD:/backup \
  alpine tar czf /backup/sqlite-bkp.tgz -C /data .

systemd

[Unit]
Description=ipbx-mcp
After=network.target

[Service]
Type=simple
WorkingDirectory=/var/local/ipbx-mcp
ExecStart=/usr/bin/node dist/index.js
EnvironmentFile=/var/local/ipbx-mcp/.env
User=mcp
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

EnvironmentFile= ist das native systemd-Äquivalent zu .env. Verwende einen dedizierten Benutzer (mcp) statt root.

Konfiguration in einem MCP-Client

Claude Desktop / CLI (statischer Bearer)

{
  "mcpServers": {
    "ipbx": {
      "type": "http",
      "url": "https://mcp.ipbx.vivavox.com.br/mcp",
      "headers": {
        "Authorization": "Bearer SEU_MCP_AUTH_TOKEN"
      }
    }
  }
}

claude.ai (OAuth)

Als Custom Connector über https://mcp.ipbx.vivavox.com.br/mcp hinzufügen. Der OAuth-Flow startet automatisch — claude.ai entdeckt den AS über WWW-Authenticate, registriert einen Client per DCR, leitet zu Google weiter, empfängt den Code und tauscht ihn gegen ein Access Token.

Struktur

src/
  index.ts            # bootstrap HTTP, leitura de env, registro de rotas
  server.ts           # createServer() registra as tools (ipbx_*)
  mysql.ts            # pool mysql2 + queries do IPBX (tenant fixo)
  channel.ts          # nome de canal do Asterisk -> ramal/tronco/fila
  sqlite.ts           # better-sqlite3 + apply schemas
  audit.ts            # logToolCall() -> audit_log
  auth/
    jwt.ts            # sign/verify HS256 (jose)
    middleware.ts     # requireAuth: JWT -> fallback bearer estático
  oauth/
    routes.ts         # registerOAuthRoutes()
    store.ts          # DCR clients, codes, refresh, authorize-tx
    google.ts         # OAuth do Google (authorize URL + token exchange)
    pkce.ts           # verificação S256 em tempo constante
sql/
  001_oauth_schema.sql           # oauth_clients, oauth_codes, oauth_refresh_tokens, audit_log
  002_oauth_authorize_tx.sql     # oauth_authorize_tx (state Google <-> params)
Dockerfile
.github/workflows/deploy.yml     # build GHCR + deploy SSH na VPS

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    B
    maintenance
    An MCP server that wraps the UTEL IP-telephony REST API as MCP tools, enabling LLM agents to make authenticated HTTP requests to the UTEL API via a simple tool interface.
    1
    1
  • F
    license
    Not graded
    quality
    C
    maintenance
    MCP server for Bvoip / 1Stream that exposes call-reporting, phone-status, and CRM-extension-mapping endpoints as MCP tools.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/paralelum/ipbx-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server