- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Documented (README)
- !No description
git clone https://github.com/ljcl/gaggiuino-mcp{
"mcpServers": {
"gaggiuino-mcp": {
"command": "node",
"args": ["/path/to/gaggiuino-mcp/dist/index.js"],
"env": {
"GAGGIUINO_URL": "<gaggiuino_url>",
"MCP_PUBLIC_URL": "<mcp_public_url>"
}
}
}
}GAGGIUINO_URLMCP_PUBLIC_URLMCP Servers overview
# Gaggiuino MCP Server
A Remote [MCP](https://modelcontextprotocol.io) server for integrating a [Gaggiuino](https://gaggiuino.github.io/) 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, weight, water level)
- `get_latest_shot_id` - Most recent shot, id and headline numbers in one call
- `list_recent_shots` - The last few shots summarised, for trends over a session
- `get_shot_data` - Structured shot summary with metrics
- `get_shot_raw_data` - Complete time-series data
- `view_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 documentation
- `get_profile_info` - Everything known about one profile
- `get_machine_settings` - Boiler, steam, and scale configuration as the machine reports it
- `get_maintenance_status` - Descale and backflush history the machine tracks itself, with shots since each
- `get_dial_in_guidance` - Expert guidance for analyzing espresso shots
- `select_profile` - Switch the active profile (changes the machine; requires an authenticated server)
- `upload_profile` - Save a new brew profile to the machine (changes the machine; requires an authenticated server). Creates only — it never updates, and the machine assigns a fresh id every time, so uploading twice leaves two profiles
- `delete_profile` - Delete a profile from the machine (destructive and irreversible; requires an authenticated server). It demands the profile's exact name back as confirmation, refuses to delete the currently selected profile, and always prompts for approval in the host — even under a stored "always allow"
**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 as `get_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`](https://github.com/ljcl/gaggiuino-mcp/pkgs/container/gaggiuino-mcp),
so there is nothing to clone or build. It is also listed in the
[MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.ljcl/gaggiuino-mcp)
as `io.github.ljcl/gaggiuino-mcp`.
### 1. Download and Configure
```bash
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:
```bash
# 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
```bash
docker compose up -d
```
### 3. Verify
```bash
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`:
```bash
GAGGIUINO_MCP_TAG=3.2 # latest 3.2.x patch
GAGGIUINO_MCP_TAG=3.2.0 # exact release
```
Upgrade with:
```bash
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_PUBLIC_URL` | _(unset)_ | Public `https` origin clients reach this server on, with no path (e.g. `https://box.tailnet.ts.net`). Set together with `MCP_OAUTH_SECRET` to enable OAuth. It is advertised as the OAuth `resource`, so it must match the URL you enter in the client exactly. |
| `MCP_OAUTH_SECRET` | _(unset)_ | Signing key for self-issued OAuth tokens, at least 32 characters (`openssl rand -hex 32`). Keep it stable across restarts so clients stay signed in. Setting only one of these two fails at startup. |
| `MCP_OAUTH_PASSPHRASE_HASH` | _(unset)_ | scrypt hash of the passphrase you type on the consent page when connecting a client. Required whenever the built-in authorization server is on — without it the consent page would grant a token to anyone who reached it, so the server refuses to start. Generate with `cd apps/server && bun run hash-passphrase`; never store the passphrase itself. |
| `MCP_OAUTH_ISSUER` | _(unset)_ | Delegate token issuing to an identity provider you already run (Authentik, Keycloak, Authelia, Zitadel, Kanidm, `tsidp`). Set it and this server serves no OAuth endpoints of its own, verifying RS256/ES256 tokens against the issuer's JWKS instead. Requires `MCP_PUBLIC_URL`; refuses to start alongside `MCP_OAUTH_SECRET` or `MCP_OAUTH_PASSPHRASE_HASH`. See [Using an external identity provider](#using-an-external-identity-provider). |
| `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:
```json
{
"status": "ok",
"version": "3.2.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?",
"versions": {
"coreVersion": "a06f97fd",
"frontVersion": "a06f97fd",
"staticVersion": "a06f97fd"
}
}
}
```
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. `machine.versions` is
observed the same way — remembered the first time anything reads the machine's
settings (`get_machine_settings`), never fetched by `/health` itself — so `null`
there means "this server has not read the settings yet", not "the machine
reports no version". Only those three documented fields are published: `/health`
is unauthenticated, so anything a future firmware adds under `versions` stays
out of it until someone decides otherwise.
Logs are one JSON object per line, so you can pick out what you need:
```bash
docker compose logs -f | jq -c 'select(.event == "tool.call" and .outcome != "ok")'
```
### Securing the endpoint
**Turn on OAuth before exposing this server beyond your LAN.** Every tunnel
option below puts `/mcp` on the public internet, and without authentication
anyone who learns the URL gets the full tool surface against a machine in your
kitchen. The server prints a warning at startup while nothing is configured.
Three variables, all required together:
```bash
# 1. The public origin clients will reach the server on — no path, https only.
# This is advertised as the OAuth `resource`, so it must match the URL you
# type into Claude exactly.
MCP_PUBLIC_URL=https://your-machine.tail-scale.ts.net
# 2. The key your tokens are signed with. Keep it stable across restarts, or
# every restart signs you out of your phone.
openssl rand -hex 32
# 3. The passphrase you will type on the consent page. Store the hash, never
# the passphrase — this prompts and prints the whole line to paste in.
cd apps/server && bun run hash-passphrase >> ../../.env
```
**Then recreate the container — a plain restart is not enough:**
```bash
docker compose up -d --force-recreate
```
Compose tracks the *list* of `env_file` paths, not their contents, so editing
`.env` and running `docker compose up -d` (or `restart`) can reuse the existing
container along with the environment it was created with. The new variables
never reach the process and the server comes up unauthenticated exactly as if
you had not set them — with no error, because from its point of view nothing is
configured. Check what actually arrived:
```bash
docker inspect gaggiuino-mcp --format '{{range .Config.Env}}{{println .}}{{end}}' | grep MCP_
```
Setting only some of them fails at startup and names the missing one. That is
deliberate: silently falling back to an open endpoint is how somebody exposes a
tunnel believing it is protected. **The corollary is worth knowing when
diagnosing:** a server that is *running* and unauthenticated has seen none of
the three — if you believe you set them, the container is stale, not the config
wrong.
#### Why OAuth and not a shared token
OAuth is the only credential this server accepts, because **it is the only one
a Claude connector can present.** A connector is added at the account level so
one entry has to work on claude.ai, Claude Desktop and iOS, and on a personal
plan the "Add custom connector" dialog offers an OAuth Client ID and Secret and
no request-header field. A local stdio bridge isWhat people ask about gaggiuino-mcp
What is ljcl/gaggiuino-mcp?
+
ljcl/gaggiuino-mcp is mcp servers for the Claude AI ecosystem with 1 GitHub stars.
How do I install gaggiuino-mcp?
+
You can install gaggiuino-mcp by cloning the repository (https://github.com/ljcl/gaggiuino-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is ljcl/gaggiuino-mcp safe to use?
+
Our security agent has analyzed ljcl/gaggiuino-mcp and assigned a Trust Score of 77/100 (tier: Trusted). See the full breakdown of passed checks and flags on this page.
Who maintains ljcl/gaggiuino-mcp?
+
ljcl/gaggiuino-mcp is maintained by ljcl. The last recorded GitHub activity is dated 2026-09-26, with 79 open issues.
Are there alternatives to gaggiuino-mcp?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy gaggiuino-mcp to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/ljcl-gaggiuino-mcp)<a href="https://claudewave.com/repo/ljcl-gaggiuino-mcp"><img src="https://claudewave.com/api/badge/ljcl-gaggiuino-mcp" alt="Featured on ClaudeWave: ljcl/gaggiuino-mcp" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
The fastest path to AI-powered full stack observability, even for lean teams.