Gaggiuino MCP Server
A remote MCP server for integrating Gaggiuino espresso machines with AI tools. It enables checking machine status, analyzing shot data, managing profiles, and receiving dial-in guidance.
README
Gaggiuino MCP Server
A Remote MCP server for integrating a Gaggiuino espresso machine with AI tools. Ask your AI assistant to check machine status, analyze shot data, and get dial-in guidance.
Features
MCP Tools
Shot Analysis
get_status- Current machine status (temperature, pressure, flow, weight)get_latest_shot_id- Most recent shot, id and headline numbers in one calllist_recent_shots- The last few shots summarised, for trends over a sessionget_shot_data- Structured shot summary with metricsget_shot_raw_data- Complete time-series dataview_shot_graph- Interactive shot graph rendered in MCP-compatible hosts (pressure, flow, weight over time with target overlays and optional shot comparison)
Profiles and Settings
list_profiles- Profiles on the machine, merged with this server's documentationget_profile_info- Everything known about one profileget_machine_settings- Boiler, steam, and scale configuration as the machine reports itget_dial_in_guidance- Expert guidance for analyzing espresso shotsselect_profile- Switch the active profile (the only tool that changes the machine; requiresMCP_AUTH_TOKEN)
MCP Prompts - workflow templates your host surfaces as slash commands or menu items:
dial_in_new_bag- first shots on a coffee you have not pulled before (bean, and optionally roast level, dose, and what you want in the cup)diagnose_last_shot- read the shot you just pulled against how it tasted (what was wrong, and optionally what you changed)choose_profile- pick a profile the machine actually holds for a coffee (roast level, and optionally drink and notes)espresso_shot_analyst- the dial-in guidance as a system prompt (same content asget_dial_in_guidance)
Each workflow prompt lays out the tools to call in order, so the analysis starts from the machine's own data rather than a guess.
MCP Resources - gaggiuino://profiles and gaggiuino://profiles/{id} for profile data
Quick Start
The server is published as a multi-arch image (linux/amd64, linux/arm64) at
ghcr.io/ljcl/gaggiuino-mcp,
so there is nothing to clone or build. It is also listed in the
MCP Registry
as io.github.ljcl/gaggiuino-mcp.
1. Download and Configure
mkdir gaggiuino-mcp && cd gaggiuino-mcp
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/.env.example
Edit .env with your Gaggiuino machine's address:
# Use the IP directly (recommended)
GAGGIUINO_URL=http://192.168.1.100
# Or if mDNS works on your network
GAGGIUINO_URL=http://gaggiuino.local
2. Start the Server
docker compose up -d
3. Verify
curl http://localhost:8000/health
The server is available at http://<your-docker-host>:8000/mcp.
Choosing a Version
The compose file tracks latest. To pin a release, set GAGGIUINO_MCP_TAG in .env:
GAGGIUINO_MCP_TAG=1.0 # latest 1.0.x patch
GAGGIUINO_MCP_TAG=1.0.1 # exact release
Upgrade with:
docker compose pull && docker compose up -d
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
GAGGIUINO_URL |
http://gaggiuino.local |
URL of your Gaggiuino machine |
PORT |
8000 |
Port for the MCP server |
HOST |
0.0.0.0 |
Host to bind to |
MCP_AUTH_TOKEN |
(unset) | Shared secret required as Authorization: Bearer <token> on /mcp. Unset serves the endpoint unauthenticated and disables select_profile. |
MCP_ALLOWED_ORIGINS |
(empty) | Comma-separated browser origins allowed to call /mcp. * allows any (unsafe). |
MCP_ALLOWED_HOSTS |
(empty) | Comma-separated Host header values to accept. Empty disables the check. |
LOG_LEVEL |
info |
debug, info, warn, error, or silent. Logs are one JSON object per line on stderr. |
Health and logs
GET /health returns JSON:
{
"status": "ok",
"version": "1.1.0",
"uptimeSec": 3412,
"machine": {
"url": "http://gaggiuino.local",
"state": "unreachable",
"lastCheckedAt": "2026-07-27T21:11:15.274Z",
"lastError": "Unable to connect. Is the computer able to access the url?"
}
}
It answers 200 whenever the process is alive, including while the machine is
unreachable — your espresso machine is off most of the day, and the container
healthcheck reads the status code. machine.state is ok, unreachable, or
unknown, observed from the requests the server already makes rather than from
a probe, so /health puts no extra load on the machine.
Logs are one JSON object per line, so you can pick out what you need:
docker compose logs -f | jq -c 'select(.event == "tool.call" and .outcome != "ok")'
Securing the endpoint
Set MCP_AUTH_TOKEN before exposing this server beyond your LAN. Every tunnel
option below puts /mcp on the public internet, and without a token anyone who
learns the URL gets the full tool surface against a machine in your kitchen. The
server prints a warning at startup while no token is set.
# Generate one and put it in your .env
openssl rand -hex 32
/health is deliberately left unauthenticated so the container's healthcheck and
your reverse proxy can probe it.
select_profile — the one tool that changes the machine — refuses to run at all
while no token is set, and says so. Everything else here only reads, which is why
an open server is a defensible default for a LAN and a machine-control tool on one
is not.
Requests carrying an Origin header are rejected unless the origin is listed in
MCP_ALLOWED_ORIGINS. This is what stops any web page you happen to visit from
POSTing to a server running on your own network — a token does not help there,
because the browser sends it for you. Requests with no Origin (Claude Desktop,
curl, anything that is not a browser) are unaffected, so the default empty list
is the right setting for almost everyone.
Listing an origin also makes /mcp answer that origin's CORS preflight and
echo Access-Control-Allow-Origin (plus Access-Control-Expose-Headers: mcp-session-id, without which a browser client can read the handshake but not
the session it needs to continue with). Allowing an origin the browser then
blocks would be an allowlist that allows nothing.
scripts/test-auth.sh probes a running server for all of the above.
Customization
The server ships with generic profiles and prompts. Two local override files are merged on top of the defaults at startup:
-
prompts.local.yaml- equipment-specific dial-in guidance (your grinder model, basket, and other equipment details). -
profiles.local.yaml- your own profiles, or overrides/removals of the defaults (set a profile ID tonullto remove it).
Start from the examples:
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/apps/server/src/data/prompts.example-local.yaml
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/apps/server/src/data/profiles.example-local.yaml
These hold personal equipment configuration, so they are deliberately never baked into the published image. To use them, uncomment the volumes: block in docker-compose.yml:
volumes:
- ./profiles.local.yaml:/app/apps/server/src/data/profiles.local.yaml:ro
- ./prompts.local.yaml:/app/apps/server/src/data/prompts.local.yaml:ro
From a repo checkout, copy each *.example-local.yaml to *.local.yaml alongside it in apps/server/src/data/ instead - they are gitignored and picked up automatically.
Connecting to AI Tools
Many AI tools (like Claude Desktop) route MCP requests through their own servers, not from your local machine. This means your MCP server needs to be accessible via a public HTTPS URL.
Every option in this section publishes
/mcpto the internet. SetMCP_AUTH_TOKENfirst — see Securing the endpoint — and give the token to your AI tool as a bearer credential.
Tailscale Funnel (Recommended)
Tailscale Funnel exposes your server to the internet via a secure HTTPS URL:
tailscale funnel --bg 8000
# URL: https://your-machine.tail-scale.ts.net/mcp
Cloudflare Tunnel
Use cloudflared to create a persistent tunnel to your server.
ngrok
ngrok http 8000
Local Network Only
If your AI tool connects directly (e.g. local MCP server config), use the direct address:
http://<docker-host-ip>:8000/mcp
Adding to Claude Desktop
- Go to Settings > Integrations > Add More > Add Remote MCP Server
- Set the URL to your public HTTPS endpoint (e.g.
https://your-machine.tail-scale.ts.net/mcp) - Save and enable
Architecture
AI Tool (Claude Desktop, etc.)
|
| HTTPS
v
+-----------------------------+
| HTTPS Tunnel |
| (Tailscale / Cloudflare / |
| ngrok / reverse proxy) |
+-----------------------------+
|
| HTTP (localhost:8000)
v
+-----------------------------+
| Gaggiuino MCP Server |
| (Docker container) |
| Bun + Streamable HTTP |
+-----------------------------+
|
| HTTP (local network)
v
+-----------------------------+
| Gaggiuino |
+-----------------------------+
Development
git clone https://github.com/ljcl/gaggiuino-mcp.git
cd gaggiuino-mcp
bun install # Install all dependencies
bun run build # Build all packages (Turborepo)
bun run test # Run all tests
bun run lint # Lint all packages
bun run check # lint + test + typecheck + build + knip + boundaries
# Server
cd apps/server
bun run dev # Watch mode
bun run test # Server tests only
# Shot graph UI (run from the repo root)
bun run storybook # Storybook on port 6006
# Regenerate JSON schemas (after changing Zod schemas in loader.ts)
cd apps/server
bun run generate-schemas
The main branch Storybook is published to GitHub Pages at
ljcl.github.io/gaggiuino-mcp — a static build for
browsing the shot-graph and UI components without running anything locally.
Docker
docker-compose.yml pulls the published image. To build and run the image from your
checkout instead, layer the build override on top of it:
docker compose -f docker-compose.yml -f docker-compose.build.yml build
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d
docker compose logs -f
The override tags the result ghcr.io/ljcl/gaggiuino-mcp:dev so a local build is never
mistaken for a published release.
Troubleshooting
Can't connect to gaggiuino.local
mDNS (.local hostnames) may not work inside Docker containers. Use the IP address directly in GAGGIUINO_URL.
AI tool can't reach the server
Ensure your server is publicly accessible via HTTPS. Tools like Claude Desktop route requests through their own servers, so Tailnet-only access (e.g. tailscale serve) won't work - you need a public tunnel (e.g. tailscale funnel).
Server starts but can't reach Gaggiuino
The container uses network_mode: host to share the host's network stack. If your Gaggiuino is on a different network segment, adjust your Docker networking configuration.
License
MIT © Luke Clark
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.