LED Display Server
Allows publishing display commands (text, image, color, clear) to MQTT topics and retrieving device status, enabling control of LED display devices.
Allows controlling LED displays on Raspberry Pi devices via MQTT messages, including sending text, displaying images, setting colors, and clearing the display.
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., "@LED Display ServerDisplay 'Hello' on the kitchen LED"
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.
LED Display Server
Serveur central Laravel d'une plateforme domotique pilotable par IA.
Agent IA (Claude) → [MCP/HTTP + OAuth Passport] → ce serveur → [MQTT] → Mosquitto → Raspberry Pi (écran LED)Le serveur ne fait aucun rendu. Il authentifie, vérifie les permissions, publie des payloads MQTT stricts, et journalise.
Stack
Laravel 11 / PHP 8.3
laravel/mcp— tools MCPlaravel/passport— OAuth 2.1 pour les agentsphp-mqtt/laravel-client— publication MQTTPostgreSQL 16
Docker Compose (app PHP-FPM + nginx + Postgres + Mosquitto)
GitHub Actions → build/push
ghcr.io
Related MCP server: mcp2mqtt
Contrat MQTT (Raspberry Pi)
Topic : display/led/# (le mqtt_topic du device en base, ex. display/led)
Payload JSON exact :
{
"type": "text|image|color|clear",
"content": "...",
"duration": 10,
"priority": "normal|high"
}type | content |
| texte à afficher |
| URL |
| hex |
| ignoré |
duration et priority sont optionnels (priority défaut normal).
Tools MCP
Endpoint : POST /mcp/led-display (middleware auth:api)
Tool | Rôle |
| devices accessibles à l'utilisateur |
| publie |
| publie |
| publie |
| publie |
| dernier |
Chaque tool vérifie les permissions (device_user_permissions.allowed_actions), récupère mqtt_topic, publie, écrit un display_logs.
Note API laravel/mcp : structure basée sur la doc officielle (
Server+$tools,Tool::handle(Request): Response,schema(JsonSchema), enregistrement dansroutes/ai.phpviaMcp::web+Mcp::oauthRoutes). Si un attribut / namespace change dans une version future du package, vérifier https://laravel.com/docs/mcp.
Installation locale (Docker)
Prérequis VPS : réseau Docker partagé avec Caddy :
docker network create proxy # une seule fois si absentcp .env.example .env
# APP_URL=https://led.ton-domaine.fr
# Mot de passe Mosquitto
chmod +x docker/mosquitto/generate-passwd.sh
./docker/mosquitto/generate-passwd.sh ledserver changeme
# ou :
docker run --rm -v "$PWD/docker/mosquitto:/mosquitto/config" eclipse-mosquitto:2 \
mosquitto_passwd -b -c /mosquitto/config/passwd ledserver changeme
# Aligner MQTT_AUTH_* dans .env
# MQTT_AUTH_USERNAME=ledserver
# MQTT_AUTH_PASSWORD=changeme
docker compose build
docker compose up -d
docker compose exec app php artisan key:generate --force
docker compose exec app php artisan migrate --force
docker compose exec app php artisan passport:keys --force
docker compose exec app php artisan db:seed --forceReverse-proxy Caddy
Le service web (container_name: led-display-web) n'expose plus de port public : Caddy termine le TLS et reverse-proxifie sur le réseau proxy.
Ajoute le snippet
docker/caddy/Caddyfile.snippetà tonCaddyfile(adapte le domaine).Vérifie que le compose Caddy est bien sur
networks: proxy: external: true.Recharge Caddy :
docker exec caddy caddy reload --config /etc/caddy/Caddyfile
Internet → Caddy:443 → led-display-web:80 → app:9000
Pi LED ────────────── MQTT :1883 ─────────→ mqttMQTT reste exposé sur l'hôte (MQTT_PUBLIC_PORT, défaut 1883) : ce n'est pas routé via Caddy HTTP.
Assets CSS/JS : nginx sert
public/via le volume Dockerpublic_assets(sync au démarrage deapp). Sans ça, Filament apparaît sans styles. Vérifie aussiAPP_URL=https://…dans.env.Attention VPS / Portainer : si
compose.yamletdocker-compose.ymlcoexistent, Compose utilisecompose.yamlen priorité. Garde les deux identiques (le repo versionne les deux) ou n’en garde qu’un.
Back-office Filament (/admin)
Accessible uniquement aux users is_admin=true (compte seed : admin@led-display.local / password).
Section | Contenu |
Dashboard | Stats devices (online / offline / never seen) + tableau de statut |
Dispositifs | CRUD + capabilities JSON + commande générique MQTT + permissions |
Utilisateurs | CRUD + flag admin |
Tokens MCP | Créer / révoquer des personal access tokens Passport |
Logs | Historique des commandes |
Gateway MQTT générique
Chaque device a un catalogue capabilities.commands (params + template payload). L'agent MCP :
ListDevices→capabilities/commandsInvokeDeviceCommand(device_id, command, params)→ publish MQTT
Types préremplis : led_display (text/image/color/clear), relay (power/toggle). Les tools Display* restent disponibles pour les écrans.
Après déploiement image :
docker compose exec app php artisan migrate --force
# Se connecter sur https://domaine/adminSi la création de token échoue, créer un client Passport personal access :
docker compose exec app php artisan passport:client --personal --name="LED Personal" --no-interactionCompte démo (seeder) :
admin@led-display.local/password(admin)agent@led-display.local/password(permissions LED complètes)Device
#1— topicdisplay/led
Sans Docker (dev)
cp .env.example .env
# Adapter DB_* (pgsql ou sqlite)
composer install
php artisan key:generate
php artisan migrate
php artisan passport:install
php artisan db:seed
php artisan serveEnregistrer un device / permissions
php artisan device:create "LED Cuisine" display/led/cuisine
php artisan device:grant agent@led-display.local 1 --actions=text,image,color,clearAPI REST (Bearer Passport) :
CRUD /api/devicesCRUD /api/users(admin)POST /api/devices/{id}/permissions{ "user_id": 2, "allowed_actions": ["text","clear"] }GET /api/display-logs
Connecter un agent MCP
Créer un client OAuth Passport (ou personal access token pour tests) :
php artisan passport:client --name="Claude Agent" --no-interaction
# ou token personnel :
php artisan tinker
>>> $u = App\Models\User::where('email','agent@led-display.local')->first();
>>> $u->createToken('mcp')->accessToken;Configurer le client MCP (ex. Claude / Cursor) :
URL :
https://VOTRE_HOST/mcp/led-displayAuth : Bearer token Passport (
Authorization: Bearer …)OAuth discovery : routes enregistrées via
Mcp::oauthRoutes()(voir doc MCP OAuth)
L'agent appelle
ListDevicespuisDisplaySendText.
MQTTS (port 8883)
Le broker expose 1883 (clair, transition / Docker) et 8883 (TLS).
Laravel (app, mqtt-listener) reste en clair interne : MQTT_HOST=mqtt + MQTT_PORT=1883.
1. Certificats
chmod +x docker/mosquitto/generate-certs.sh
./docker/mosquitto/generate-certs.sh command.lha.run # hostname = SAN du certProduction : copier un fullchain/privkey Let’s Encrypt dans storage/mosquitto/certs/
(voir storage/mosquitto/certs/README.md).
2. Démarrer / recreeer Mosquitto
docker compose up -d --force-recreate mqtt
docker compose logs mqtt --tail=30
# firewall : ouvrir TCP 88833. Clients (Tasmota / Pi)
Réglage | Valeur |
Host | même CN/SAN que le cert (ex. |
Port |
|
TLS / SSL | activé |
User / pass |
|
Auto-signé : Tasmota peut exiger le fingerprint du cert ou une option « insecure ». Préférer Let’s Encrypt en prod.
4. Fermer 1883 sur Internet (après migration)
Firewall : n’autoriser plus 1883 depuis l’extérieur (garder 8883).
Optionnel : retirer le mapping "${MQTT_PUBLIC_PORT:-1883}:1883" du compose.
Listener de statut (optionnel)
Le listener met à jour devices.status / last_seen_at (dashboard online/offline) :
JSON sur
status_topicou{mqtt_topic}/status(écrans LED)Tasmota :
tele/{topic}/STATE|SENSOR(JSON),tele/{topic}/LWT(Online/Offline),stat/{topic}/POWER
Matching Tasmota : slug dans mqtt_topic (ex. cmnd/tasmota_XXXXXX/POWER) ou status_topic = tele/tasmota_XXXXXX/STATE.
Pour mettre à jour manuellement / debug :
docker compose --profile listeners up -d mqtt-listener
# ou
php artisan mqtt:listen-status --topic='display/led/+/status'Broker MQTT externe
Si Mosquitto tourne ailleurs (pas le service mqtt Compose), dans .env :
MQTT_HOST=mqtt.example.com
MQTT_PORT=8883
MQTT_TLS_ENABLED=true
MQTT_TLS_CA_FILE=/etc/ssl/certs/ca-certificates.crt
MQTT_AUTH_USERNAME=...
MQTT_AUTH_PASSWORD=...Puis retirez / ne démarrez pas le service mqtt du compose.
Déploiement GitHub Actions → GHCR → VPS
Workflow : .github/workflows/deploy.yml
Push
main(ou tagv*) → build image Docker → pushghcr.io/fabienhamot/mcp_mqtt_serverDéploiement SSH optionnel : définir la variable de dépôt
ENABLE_VPS_DEPLOY=trueet les secrets :VPS_HOST,VPS_USER,VPS_SSH_KEY,VPS_PORT,VPS_APP_DIR,GHCR_TOKEN
Sur le VPS :
docker network create proxy 2>/dev/null || true
git clone git@github.com:fabienhamot/mcp_mqtt_server.git ~/mcp_mqtt_server && cd ~/mcp_mqtt_server
cp .env.example .env # APP_URL=https://led.ton-domaine.fr, DB_*, MQTT_*
# générer passwd mosquitto + ajouter le snippet Caddy (docker/caddy/Caddyfile.snippet)
docker compose pull
docker compose up -d
docker compose exec app php artisan passport:keys --force
docker compose exec app php artisan db:seed --forceTests
php artisan test --filter=DisplayPayloadTestStructure utile
app/Mcp/Servers/LedDisplayServer.php
app/Mcp/Tools/*Tool.php
app/Services/{DisplayPayload,MqttPublisher,DisplayCommandService,DevicePermissionService}.php
app/Models/{Device,DeviceUserPermission,DisplayLog,User}.php
routes/ai.php # MCP + OAuth
routes/api.php # back-office REST
docker-compose.yml
Dockerfile
.github/workflows/deploy.ymlPoints à vérifier contre la doc officielle
laravel/mcp— attributs#[Name],#[Description], annotations tools (IsReadOnly,IsIdempotent), signature exacte deMcp::oauthRoutes()/ middleware Passport : https://laravel.com/docs/mcpPassport 13 — trait
HasApiTokens+ interfaceOAuthenticatable, guardapidriverpassportTLS Mosquitto 8883 —
./docker/mosquitto/generate-certs.shpuis clients en MQTTS ; fermer 1883 public après migration
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.
Related MCP Servers
- AlicenseBquality-maintenanceAn MCP server that bridges the physical world and AI models by enabling natural language control of IoT hardware via the MQTT protocol. It supports real-time device monitoring, command publishing, and response handling for seamless integration between AI clients and physical devices.Last updated3
- Flicense-qualityDmaintenanceThis server bridges the Model Context Protocol (MCP) to the MQTT protocol, allowing LLMs to interact with IoT devices and hardware systems through configurable message topics. It enables users to map natural language prompts to specific MQTT commands for controlling devices like Raspberry Pi fans or PWM controllers.Last updated1
- Alicense-qualityCmaintenanceEnables LLM agents to securely monitor and control MQTT devices for building automation, industrial control, and smart home systems through a standardized MCP interface.Last updated19MIT
- Alicense-qualityDmaintenanceMCP server that gives AI agents direct access to IoT devices via the jettyd platform, enabling control and monitoring of ESP32 devices through natural language.Last updated56MIT
Related MCP Connectors
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
MCP server for AI dialogue using various LLM models via AceDataCloud
MCP server for Gainium — manage trading bots, deals, and balances via AI assistants
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/fabienhamot/mcp_mqtt_server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server