EuiccBridge
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@EuiccBridgelist my eSIM profiles"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
EuiccBridge
A CLI (and MCP server) for managing eSIM profiles, SIM SMS, and cellular modem diagnostics on Linux — local-first, scriptable, and safe to point an AI agent at.
eSIM lifecycle: list, download, enable, disable, delete, switch Profiles
SMS: send/receive over AT/PDU directly, no
ModemManagerdependencyDiagnostics: registration, APN/PDP, interface, and connectivity checks, plus a
doctorcommand for environment pre-flight checksSafety by default: confirmations, redaction, allowlists, audit logging
One core library, three fronts: CLI today, MCP server for AI tooling, Web API planned — all calling the same code, not shelling out to each other
Built on top of proven lower-level tools (lpac, libqmi/qmicli, standard
Linux networking and AT commands) rather than reimplementing GSMA RSP, QMI,
or AT protocol stacks from scratch.
Goals
Keep eSIM activation codes, Profile operations, SMS, and SIM identifiers under local, auditable control.
Use a Linux VM or small Linux host as the hardware controller.
Expose one shared core library through CLI first, then MCP, then optional Web API/WebUI.
Make every dangerous operation explicit, confirmed, logged safely, and reversible where possible.
Related MCP server: ESP RainMaker MCP Server
Non-Goals
Do not implement GSMA RSP, APDU, QMI, MBIM, IMS, ePDG, IPsec, or SIP stacks.
Do not promise VoWiFi calling or SMS over WiFi Calling.
Do not build a multi-tenant/commercial traffic pool, IP-rotation-for-hire service, or Bot platform. (A single-SIM, self-use SOCKS5/HTTP proxy bound to whichever cellular interface is currently active is a different, smaller thing — see Roadmap. The line being drawn is multi-tenant/pool vs. self-use, not "proxy vs. no proxy".)
Architecture
euiccbridge core library
|-- CLI first deliverable, machine-readable JSON
|-- MCP server AI/tooling entrypoint, same core library
`-- Web API later layer, same core libraryThe CLI is an entrypoint, not the business logic layer. Web API and MCP call the same Python core library directly rather than shelling out to the CLI.
Core modules:
core.device/core.device_paths: AT port / QMI device / cellular interface discovery, AT command execution, modem status parsing.core.qmi: QMI device discovery and status viaqmicli.core.esim:lpacwrapper for eUICC/Profile operations (info, list, download, enable, disable, delete, switch).core.gsm_pdu: self-contained GSM 03.40/03.38 SMS PDU codec (noModemManagerdependency — see "SMS backend" below).core.sms/core.sms_store: SMS sync/list/send, backed by a local SQLite store that's the decoupling point for downstream consumers.core.data: APN, registration, PDP, interface, and public-IP diagnostics.core.doctor: read-only environment pre-flight checks.core.config: config loading + SMS allowlist management.core.security: confirmation, masking, allowlist, rate limits, audit.
SMS backend: AT/PDU-direct, not ModemManager
SMS is implemented from scratch against AT+CMGF/AT+CPMS/AT+CMGL/AT+CMGS
in PDU mode, not via ModemManager/mmcli. This was a deliberate choice: a
ModemManager instance and this project's own direct QMI/AT usage can end up
contending for the same control channel, and comparable closed-source modem
managers in this space document the same conflict and instruct disabling
ModemManager rather than depending on it. Going AT/PDU-direct avoids adding
a persistent background service and keeps this project's "auditable,
local-first, no unnecessary daemons" posture consistent across eSIM, SMS, and
diagnostics.
Relationship with EasyLPAC
EasyLPAC is a mature open-source lpac GUI frontend. EuiccBridge uses it as
a reference and optional manual fallback, not as a runtime dependency.
Useful parts borrowed from EasyLPAC's design: lpac discovery behavior (same
directory, system PATH, then /usr/bin/lpac), eSIM operation flow and
user-facing error handling, eUICC manufacturer/certificate/SAS display ideas,
and profile-notification handling (process after operations by default, with
a setting to disable it).
EuiccBridge still calls lpac directly because it needs machine-readable
CLI/MCP/Web API output, SMS, modem status, APN/PDP diagnosis, allowlists,
audit, and redaction — those are service/controller concerns, not GUI
concerns.
v1 Scope
Device discovery: AT port, QMI device, cellular interface, IMEI, ICCID, IMSI, subscriber number (when the SIM has one written to it — many data-only/IoT SIMs don't).
Radio state: operator, PLMN, registration state, signal, LTE band, APN, PDP, interface IP.
eSIM management: EID, Profile list, download, enable, disable, delete, switch (one-step disable-current + enable-target).
SMS: sync/list/send, allowlist, daily limit, safe audit, incremental (
--since-id) reads for downstream consumers.Data diagnosis: distinguish no registration, empty APN, inactive PDP, no interface IP, and public connectivity failure.
doctor: read-only pre-flight checks (AT/QMI reachability,lpacpresence and backend health, device-node integrity, QMI raw-ip mode).CLI and MCP: both use the same core library and default to redacted output.
v1 does not attempt: WiFi Calling SMS, VoWiFi outbound calls, IMS/ePDG/ IPsec/SIP implementation, or compatibility with any specific closed-source modem manager's API.
v1 CLI
The CLI supports JSON output so MCP, Web API, and scripts can consume the same result shape.
python3 -m euiccbridge doctor --json
python3 -m euiccbridge device list --json
python3 -m euiccbridge device status --json
python3 -m euiccbridge esim info --json
python3 -m euiccbridge esim list --json
python3 -m euiccbridge esim download --activation-code-stdin --confirm --json
python3 -m euiccbridge esim enable --iccid <iccid> --confirm --json
python3 -m euiccbridge esim disable --iccid <iccid> --confirm --json
python3 -m euiccbridge esim switch --iccid <iccid> --confirm --json
python3 -m euiccbridge esim delete --iccid <iccid> --confirm --force --json
python3 -m euiccbridge sms sync --json
python3 -m euiccbridge sms list --json
python3 -m euiccbridge sms send --to <number> --text <text> --confirm --json
python3 -m euiccbridge sms allowlist add --number <number> --json
python3 -m euiccbridge data diagnose --apn <apn> --json
python3 -m euiccbridge data test --interface <iface> --jsonesim download prompts for the activation code on stdin (paste + Enter, no
Ctrl+D needed) rather than taking it as an argument; add -v/--verbose to
stream lpac's progress live instead of waiting for one final JSON blob.
Multiple devices
If more than one physical modem is attached, every command above needs a
top-level --device <label> (before the subcommand) to say which one to
target:
python3 -m euiccbridge device list --json
python3 -m euiccbridge --device 5-3 device status --json
python3 -m euiccbridge --device 5-3 esim list --json
python3 -m euiccbridge --device 5-3 sms send --to <number> --text <text> --confirm --jsondevice list shows each attached device's label (its USB serial number if
the hardware reports one, otherwise its USB bus-port path e.g. 5-3 --
some rebranded/OEM modules don't report a serial). Devices are identified
by correlating AT port + QMI device + network interface via Linux USB
topology (sysfs), not by enumeration order, since /dev/ttyUSB* numbering
isn't stable across reconnects.
Omitting --device works exactly as before when exactly one device is
attached (nothing changes for single-device setups). With more than one
attached and no --device given, every command fails explicitly with
"N devices found (...) but none selected" rather than silently guessing
-- auto-discovering AT port and QMI device independently, with no
guarantee they picked the same physical modem, previously produced a
device status response silently mixing fields from two different modems
together (see docs/TROUBLESHOOTING.md). Set default_device in config
to avoid passing --device on every call.
SMS messages are tagged with the device they were sent/received through,
and the daily send limit (sms_daily_limit) is tracked per device, not
globally -- two modems each get their own budget.
Safety Rules
eSIM activation codes are read from stdin/interactive input, never shell arguments, and are never written to logs, audit files, tracebacks, or JSON responses.
eSIM write operations refuse to run while a conflicting modem-manager service is active.
Profile deletion requires both
--confirmand--force.SMS sending requires explicit confirmation and an allowlisted recipient, plus a configurable daily send limit.
EID, ICCID, IMSI, IMEI, phone numbers, activation codes, and SMS bodies are redacted by default in every command's output; an explicit opt-out flag (audited when used) reveals it per-call —
--revealonesim/smscommands,--no-redactondevice status.MCP dangerous tools require
confirm: true.Services do not listen on public interfaces.
Compatibility
Validated against a Quectel EC25/EG25-class USB modem (AT + QMI) paired with an eUICC exposing a GSMA RSP2 Root CI1 certificate, on a Debian Linux controller. The design is not hardcoded to that one device.
Compatibility tiers:
Tier 1: Quectel EC25/EG25/EC20-like AT + QMI modems.
Tier 2: Other cellular modems exposing standard AT registration/SMS commands.
Tier 3:
lpac-supported eUICC readers, eSIM management only.
Tiers 1 and 2 need only a SIM (physical or eSIM) to work — device status,
signal, registration, data diagnosis, and SMS all operate at the AT/QMI
layer and don't care what kind of SIM is inserted. Tier 3 (the esim
command family) specifically needs an actual eUICC with a GSMA ISD-R
applet; a plain (non-eUICC) SIM makes every esim command fail, and
doctor's lpac_backend check reports this distinctly (warn, "no eUICC
detected") rather than treating it as a broken environment.
Not supported: phone built-in eSIM interfaces, native macOS/Windows USB modem management, generic VoWiFi calling/SMS.
Roadmap
v1: local CLI + MCP, device status, eSIM management, SMS, data diagnosis,
doctor, multi-device selection (--device, per-device SMS attribution and daily limits) — complete, every item unit-tested and real-hardware validated (including with two physical modems attached simultaneously) except eSIMdownload's success path, which is validated but depends on network conditions outside this project's control (seedocs/TROUBLESHOOTING.md).v1.1: scheduled keepalive SMS with allowlist, limits, retries, and reports.
v1.2: local Web API backed by the same core library.
v1.3: lightweight WebUI for status, eSIM, SMS, and diagnostics.
v1.x: self-use single-card SOCKS5/HTTP proxy bound to whichever cellular interface is currently active (explicitly not the multi-tenant/pool feature described in Non-Goals).
v2: human-friendly device labels/nicknames (beyond the raw serial/USB path), per-device queues, and cross-device aggregation views.
v3: VoWiFi/IMS status diagnostics and log collection only.
v4: advanced data routing and per-interface outbound testing.
Near-term, more granular candidates: +CMTI real-time SMS notification
(deferred — needs a persistent AT-port listener, a different execution model
than the rest of this project; a periodic sms sync + notification hook
covers most of the same need at much less complexity), optional GSM 7-bit
encode path for outgoing SMS (currently UCS2-only by design), install/
dependency guidance per distro, an uqmi backend for OpenWrt.
Status
All v1 Scope items are implemented, unit-tested, and validated against real
hardware (eSIM info/list/download/enable/disable/delete/switch; SMS sync/
list/send including multi-part concatenation and non-ASCII content; device
status; data diagnosis; doctor).
Two real bugs were found and fixed during hardware validation that are worth
knowing about if you're bringing up your own hardware — see
docs/TROUBLESHOOTING.md for the full symptom/cause/fix writeups:
USB re-enumeration can corrupt AT-port device nodes into plain regular files;
doctor'sdevice_node_typescheck catches this directly.QMI WWAN interfaces in Ethernet/802.3 mode can fail to route reliably on some hardware;
doctor'sqmi_raw_ipcheck flags this, andscripts/80-euiccbridge-qmi-rawip.rulesis a working fix you can adapt to your modem's VID:PID.
Current Runtime Notes
A conflicting modem-manager service (if one is installed) should remain stopped while running EuiccBridge — it will contend for the same QMI control channel and AT port.
lpacis not vendored in this repository; install it separately and pointlpac_pathat it.sms_allowed_numberscan be managed without hand-editing the config file:euiccbridge sms allowlist add/remove/list --number <number> --json.Scheduled SMS sync, single device:
scripts/euiccbridge-sms-sync.service+.timer(copy both to/etc/systemd/system/,systemctl daemon-reload,systemctl enable --now euiccbridge-sms-sync.timer). Runseuiccbridge sms syncevery 30s by default; adjustOnUnitActiveSecin the.timerfile to matchsms_poll_interval_secondsif you change that config value.Scheduled SMS sync, multiple devices: the plain service above has no
--device, so with more than one physical device attached it fails every run ("N devices found but none selected") instead of silently syncing the wrong one. Use the templated units instead --scripts/euiccbridge-sms-sync@.service+@.timer-- one instance per device, named after itsdevice listlabel:systemctl enable --now euiccbridge-sms-sync@5-3.timer systemctl enable --now euiccbridge-sms-sync@5-2.timerSince device labels can be a USB bus-port path (when the hardware reports no serial number), replugging a device into a different port changes its label -- re-check
device listand re-enable the timer under the new label if that happens.scripts/sms_notify.pyis a separate, optional consumer script (deliberately not part of theeuiccbridgepackage — it's policy/orchestration, reading the stable CLI JSON contract, not a capability) — seescripts/sms_notify.config.example.jsonfor its config shape. It runs with--reveal, so pointhook_command/webhook_urlat a target you trust with real numbers/text. Setdevicein its config if more than one physical device is attached (same label as--device) -- run onesms_notify.pyinstance, each with its owncursor_pathanddevice, per device you want notifications for.scripts/sms_email_notify.pyis a ready-madehook_commandtarget that emails each new message via SMTP (seescripts/sms_email_notify.config.example.json-- use an app-specific SMTP password, not your real account password, and keep that config file root-readable only). Wire it in fromsms_notify.config.json:"hook_command": "python3 scripts/sms_email_notify.py /path/to/email_notify.config.json". Runsms_notify.pyon its own schedule, separate fromeuiccbridge-sms-sync[@.]timer(which only pulls messages off the modem, it doesn't dispatch notifications) -- there's no packaged systemd unit for it yet, add one following theeuiccbridge-sms-sync.timerpattern.The cellular data interface commonly needs QMI raw-ip mode to route real traffic reliably — see
scripts/80-euiccbridge-qmi-rawip.rulesanddocs/TROUBLESHOOTING.md.
Example Config
Create euiccbridge.config.json in this directory, or /opt/euiccbridge/config.json.
Minimal single-modem config:
{
"lpac_path": "/opt/euiccbridge/bin/lpac",
"lpac_apdu": "qmi",
"lpac_http": "curl",
"auto_process_notifications": false
}Pinned config for multi-device or debugging:
{
"at_port": "/dev/ttyUSB2",
"qmi_device": "/dev/cdc-wdm0",
"interface": "wwp0s3u3i4",
"lpac_path": "/opt/euiccbridge/bin/lpac",
"lpac_apdu": "qmi",
"lpac_http": "curl",
"lpac_qmi_device": "/dev/cdc-wdm0",
"lpac_at_device": "/dev/ttyUSB2",
"sms_allowed_numbers": ["10086"],
"sms_daily_limit": 5,
"sms_storage": "ME",
"sms_db_path": "/var/lib/euiccbridge/sms.db",
"sms_poll_interval_seconds": 30,
"auto_process_notifications": true,
"lpac_commands": {
"info": ["chip", "info"],
"list": ["profile", "list"],
"download": ["profile", "download", "-a", "{activation_code}"],
"enable": ["profile", "enable", "{iccid}"],
"disable": ["profile", "disable", "{iccid}"],
"delete": ["profile", "delete", "{iccid}"],
"notifications": ["notification", "process"]
}
}The lpac_commands section exists because lpac command flags can vary by
build/distribution. Calibrate it on your target host with lpac --help and
lpac profile --help before using write operations.
Troubleshooting
See docs/TROUBLESHOOTING.md for real-hardware failure modes and fixes
(corrupted AT-port device nodes, QMI raw-ip mode, DNS injection affecting
specific domains on some networks).
License
MIT — see LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/Cat0513/euiccbridge'
If you have feedback or need assistance with the MCP directory API, please join our Discord server